#!/usr/bin/perl

package bridge;

use FindBin qw($RealScript);

use lib "$FindBin::Bin/../";
use lib "$FindBin::Bin/../DADA/perllib";
use lib "$FindBin::Bin/../DADA/App/Support";


BEGIN {
    my $b__dir = ( getpwuid($>) )[7] . '/perl';
    push @INC, $b__dir . '5/lib/perl5', $b__dir . '5/lib/perl5/x86_64-linux-thread-multi', $b__dir . 'lib',
      map { $b__dir . $_ } @INC;
}

use strict;
$ENV{PATH} = "/bin:/usr/bin";
delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };

use Try::Tiny; 
use Carp qw(cluck); 

#---------------------------------------------------------------------#
# Bridge
# For instructions, see the pod of this file. try:
#  pod2text ./bridge | less
#
# Or try online:
#  https://dadamailproject.com/d/bridge.html
#
#---------------------------------------------------------------------#
# REQUIRED:
#
# It is only required that you read the documentation. All variables
# that are set here are *optional*
#---------------------------------------------------------------------#

use DADA::Config 11.0.0;

use Fcntl qw(
  O_CREAT
  O_RDWR
  LOCK_EX
  LOCK_NB
);
use Encode qw(encode decode);
use Try::Tiny;

my $Plugin_Config = {
	Plugin_Name                         => 'Bridge',
	MessagesAtOnce                      => 1,
	Room_For_One_More_Check             => 1,
	Enable_POP3_File_Locking            => 1,
	Check_List_Owner_Return_Path_Header => 0,
	Check_Multiple_Return_Path_Headers  => 0,
	Check_Multiple_From_Addresses       => 1,
	Plugin_URL                          => $DADA::Config::S_PROGRAM_URL . '?flavor=plugins&plugin=bridge',
	Plugin_Name                         => 'Bounce Handler',
}; 


#---------------------------------------------------------------------#

#---------------------------------------------------------------------#

$ENV{PATH} = "/bin:/usr/bin";
delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };

my $App_Version = $DADA::Config::VERSION;

# Phowaa - let's import *a few* things
use DADA::Template::HTML;
use DADA::App::Guts;
use DADA::Mail::Send;
use DADA::MailingList::Subscribers;
use DADA::MailingList::Settings;
use DADA::Security::Password;
use DADA::App::POP3Tools;
use Email::Address;
use Digest::MD5 qw(md5_hex);
use MIME::Parser;
use MIME::Entity;
use Getopt::Long; # well, that doesn't seem to be doing anything... 

my $parser;
my $test;
my $help;
my $inject;
my $verbose;
my $debug;
my $list;
my $run_list;
my $check_deletions;
my $root_login;
my $checksums;
my $tmp_msg_file;
my $rm_tmp_msg_file;

my $dont_test_age_of_messages = 0;
sub dont_test_age_of_messages { 
	$dont_test_age_of_messages = 1; 
}

init_vars();
sub reset_globals {
    $parser           = new MIME::Parser;
    $parser           = optimize_mime_parser($parser);
    $test             = undef;
    $help             = undef;
    $inject           = 0;
    $verbose          = 0;
    $list             = undef;
    $run_list         = undef;
    $check_deletions  = 0;
    $root_login       = 0;
    $checksums        = {};
	$tmp_msg_file     = undef; 
	$rm_tmp_msg_file  = 0; 
	
}
sub init_vars {

    # DEV: This NEEDS to be in its own  module - perhaps DADA::App::PluginHelper or something?
    while ( my $key = each %$Plugin_Config ) {
        if ( exists( $DADA::Config::PLUGIN_CONFIGS->{'Bridge'}->{$key} ) ) {
            if ( defined( $DADA::Config::PLUGIN_CONFIGS->{'Bridge'}->{$key} ) ) {
                $Plugin_Config->{$key} =
                  $DADA::Config::PLUGIN_CONFIGS->{'Bridge'}->{$key};
            }
        }
    }
}

run()
  unless caller();

sub test_sub { 
    return 'Hello, World!'; 
    
}   
sub run {
    
    my $q = shift;
    reset_globals();
#    if ( !$ENV{GATEWAY_INTERFACE} ) {
#        &cl_main();
#    }
#    else {
        &cgi_main($q);
#    }

}

sub cgi_main {
    
    my $q = shift;
    my $admin_list;

    ( $admin_list, $root_login ) = check_list_security(
        -cgi_obj  => $q,
        -Function => 'bridge',
    );

    $list = $admin_list;

    my $prm = $q->param('prm') || 'cgi_default';

    #die $prm;

    my %Mode = (
        'edit'                        => \&edit,
        'cgi_show_plugin_config'      => \&cgi_show_plugin_config,
        'test_pop3'                   => \&cgi_test_pop3,
        'awaiting_msgs'               => \&cgi_awaiting_msgs,
        'manual_start'                => \&admin_cgi_manual_start,
        'admin_cgi_manual_start_ajax' => \&admin_cgi_manual_start_ajax,
        'cgi_test_pop3_ajax'          => \&cgi_test_pop3_ajax,
		#'cgi_inject'                  => \&cgi_inject,
        'mod'                         => \&mod,
    );

    if ( exists( $Mode{$prm} ) ) {
        return $Mode{$prm}->($q);    #call the correct subroutine
    }
    else {
        return edit($q);
    }
}


sub cgi_test_pop3_ajax {

    my $q = shift;

    my $r = '';

    my $ls = DADA::MailingList::Settings->new( { -list => $list } );
    my $password = $q->param('password') || undef;
    if ( !defined($password) ) {
        if ( defined( $ls->param('cipher_key') ) ) {
            $password = DADA::Security::Password::cipher_decrypt( $ls->param('cipher_key'),
                $ls->param('discussion_pop_password') );
        }
        else {
            $password = undef;
        }
    }

    my ( $pop3_obj, $pop3_status, $pop3_log );

    try {
        require DADA::App::POP3Tools;
        ( $pop3_obj, $pop3_status, $pop3_log ) = DADA::App::POP3Tools::net_pop3_login(
            {
                server          => scalar $q->param('server'),
                username        => scalar $q->param('username'),
                password        => $password,
				port            => scalar $q->param('port'),
				AUTH_MODE       => scalar $q->param('auth_mode'),
                USESSL          => scalar $q->param('use_ssl'),
				starttls        => scalar $q->param('use_starttls'), 
				SSL_verify_mode => scalar $q->param('ssl_verify_mode'),		
				ping_test       => 1, 		
            }
        );
    } catch {
        $r .= $_;
    };
	
    if ( defined($pop3_obj) ) {
        $pop3_obj->quit();
    }
    if ( $pop3_status == 1 ) {
        $r .= '<p>Connection is Successful!</p>';
    }
    else {
        $r .= '<p>Connection is NOT Successful.</p>';
    }
    $r .= '<pre>' . $pop3_log . '</pre>';
	
    return ( {}, $r );
}

sub cgi_test_pop3 {
    my $q = shift;
    my $r = '';

    my $chrome = 1;
    if ( defined( $q->param('chrome') ) ) {
        $chrome = $q->param('chrome') || 0;
    }

    my %vars = (
        screen      => 'using_bridge',
        Plugin_Name => $Plugin_Config->{Plugin_Name},
    );

    require DADA::Template::Widgets;
    my $scrn;

    if ( $chrome == 1 ) {

        $scrn = DADA::Template::Widgets::wrap_screen(
            {
                -screen         => 'plugins/bridge/test_pop3.tmpl',
                -with           => 'admin',
                -wrapper_params => {
                    -Root_Login => $root_login,
                    -List       => $list,
                },
                -vars => {%vars},

            }
        );
    }
    else {
        $scrn = DADA::Template::Widgets::screen(
            {
                -screen => 'plugins/bridge/test_pop3.tmpl',
                -with   => 'admin',
                -vars   => {%vars},

            }
        );
    }
    return ( {}, $scrn );

}

sub cgi_awaiting_msgs {

    my $r = '';

    $run_list = $list;
    $verbose  = 1;
 
    my $mod = SimpleModeration->new( { -List => $list } );
    my $awaiting_msgs = $mod->awaiting_msgs();

	my $msg_entries = []; 
	  
    for my $message_data (@$awaiting_msgs) {
        
		my $parser = $parser;#!?
       

        # unescape URI encoded stuff:
		my $messagename = $message_data->{name};
        $messagename =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;

		my $entity = undef; 
        try { 
			$entity = $parser->parse_data( 
				safely_encode( 
					$mod->get_msg( 
						{ 
							-msg_id => $messagename 
						} 
					) 
				) 
			); 
		} catch { 
			# ... 
		};
        
		if ( !$entity ) {
           # $r .= "can't show message $messagename: $@\n";
        }
        else {
            my $subject = $entity->head->get( 'Subject', 0 );
            $subject =~ s/\n//g;
            my $from = $entity->head->get( 'From', 0 );
            $from =~ s/\n//g;
            my $date = $entity->head->get( 'Date', 0 );
            $date =~ s/\n//g;
            
			#my $messagehdr = "From: " . $from . "; Subj: " . $subject . " ; Date: " . $date;
             
  			my $resend_link = 
  				$DADA::Config::S_PROGRAM_URL
                . '?flavor=plugins&plugin=bridge&prm=mod&list='
                . DADA::App::Guts::uriescape($list)
                . '&process=resend&msg_id='
                . DADA::App::Guts::uriescape($messagename);
			
            my $confirmation_link =   
               $DADA::Config::S_PROGRAM_URL
              . '?flavor=plugins&plugin=bridge&prm=mod&list='
              . DADA::App::Guts::uriescape($list)
              . '&process=confirm&msg_id='
              . DADA::App::Guts::uriescape($messagename)
			  . '&acting_email_address='
			  . DADA::App::Guts::uriescape('(via list control panel)')
			  ;
			  
            my $deny_link =
                $DADA::Config::S_PROGRAM_URL
              . '?flavor=plugins&plugin=bridge&prm=mod&list='
              . DADA::App::Guts::uriescape($list)
              . '&process=deny&msg_id='
              . DADA::App::Guts::uriescape($messagename)
			  . '&acting_email_address='
			  . DADA::App::Guts::uriescape('(via list control panel)')
			  ;
			  
			my $delete_link = 
				$DADA::Config::S_PROGRAM_URL
              . '?flavor=plugins&plugin=bridge&prm=mod&list='
              . DADA::App::Guts::uriescape($list)
              . '&process=delete&msg_id='
              . DADA::App::Guts::uriescape($messagename)
			  . '&acting_email_address='
			  . DADA::App::Guts::uriescape('(via list control panel)')
			  ;
			  
			push(@$msg_entries, { 
				subject           => $subject, 
				from              => $from, 
				date              => $date, 
				
				confirmation_link => $confirmation_link, 
				deny_link         => $deny_link, 
				resend_link       => $resend_link, 
				delete_link       => $delete_link,
			});  
        }
    }
    
  
    my $scrn = DADA::Template::Widgets::wrap_screen(
        {
            -screen         => 'plugins/bridge/awaiting_msgs.tmpl',
            -with           => 'admin',
            -wrapper_params => {
                -Root_Login => $root_login,
                -List       => $list,
            },
            -vars => {
                Plugin_URL                     => $Plugin_Config->{Plugin_URL},
                Plugin_Name                    => $Plugin_Config->{Plugin_Name},
				
            	msg_entries                    => $msg_entries, 
            },
            -list_settings_vars_param => {
                -list                 => $list,
                -dot_it               => 1,
                -i_know_what_im_doing => 1,
            },
        }

    );
    return ( {}, $scrn );
}

sub admin_cgi_manual_start_ajax {

    my $q = shift; 
    my $r = '';
       $r .= '<pre>';     # DEV no like.
       $r .= start($list);
       $r .= '</pre>';    # DEV no like.
    return ( {}, $r );
}


sub mod { 
	my $q = shift; 
	
	if($q->request_method() =~ m/POST/i){
		return post_mod($q); 
	}
	else { 
	    my $scrn = DADA::Template::Widgets::screen(
	        {
	            -screen => 'plugins/bridge/postify_mod_get.tmpl',
				-vars => { 
					flavor               => $q->param('flavor'),
					plugin               => $q->param('plugin'),
					prm                  => $q->param('prm'),
					list                 => $q->param('list'),
					process              => $q->param('process'),
					msg_id               => $q->param('msg_id'),
					acting_email_address => $q->param('acting_email_address'),
					rand_string          => $q->param('rand_string'),
				}
	        }
	    );
	    return ( {}, $scrn );
	}
}

sub post_mod {

    my $q = shift; 
    
    my $r = '';
    my $checksout = 1;
	
	# $list is global, and is only set if the 
	# user has already logged in. (see: cgi_main)
    if ( $list ne $q->param('list') ) {
        $checksout = 0;
    }    
   
    # We'll use the list that's passed to us.
    $list = $q->param('list');

	my $acting_email_address = $q->param('acting_email_address') || '(via list control panel)'; 
    if ($checksout) {
        $r .= admin_template_header(
            -Title      => "Moderation",
            -List       => $list,
            -Root_Login => $root_login
        );
    }
    else {

        $r .= list_template(
            -Part  => "header",
            -Title => "Moderation",
        );
    }

    my $ls = DADA::MailingList::Settings->new( { -list => $list } );

    my $mod = SimpleModeration->new( { -List => $list } );
    my $msg_id = $q->param('msg_id');
    my ($valid_msg, $imm_r) = $mod->is_moderated_msg($msg_id);

	$r .= "<ul>";


    if ( $valid_msg == 1 ) {
        $r .= "<li>Message appears to be valid and exists</li>";

        if ( $q->param('process') eq 'confirm' ) {
            my $g_msg = $mod->get_msg( { -msg_id => $msg_id } );
            
            $r .= "<pre>\n";
			$r .= process(
                {
                    -ls  => $ls,
                    -msg => \$g_msg,
                }
            );
            $r .= "\n</pre>";

            $r .= "<li>Message has been sent!</li>";
            if ( $ls->param('send_moderation_accepted_msg') == 1 ) {
                $r .= "<li>Sending acceptance message</li>";
                $mod->send_accept_msg(
					 { 
						 -msg_id              => $msg_id, 
						 -parser              => $parser, 
 						-acting_email_address => $acting_email_address, 
					  } 
				);
            }

            $mod->remove_msg( { -msg_id => $msg_id } );
			
			warn "moderated message ($msg_id) acted upon (confirm) by, " . $acting_email_address; 
			

        }
        elsif ( $q->param('process') eq 'deny' ) {

            $r .= "<li>Message has been denied and being removed</li>";
            if ( $ls->param('send_moderation_rejection_msg') == 1 ) {
                $r .= "<li>Sending rejection message!</li>";
                
				$mod->send_reject_msg(
                    {
                        -msg_id => $msg_id,
                        -parser => $parser,
						-acting_email_address => $acting_email_address, 
                    }
                );

            }
            #gotta do this, after, since removing it will not make the send rejection message thing to work.
            $mod->remove_msg( { -msg_id => $msg_id } );
			
			warn "moderated message ($msg_id) acted upon (deny) by, " . $acting_email_address; 
			
			
        }
        elsif ( $q->param('process') eq 'delete' ) {
			
            $mod->remove_msg( { -msg_id => $msg_id } );
            $r .= "<li>Message has been removed</li>";
			
			warn "moderated message ($msg_id) acted upon (delete) by, " . $acting_email_address; 
			
       
	    }
        elsif ( $q->param('process') eq 'resend' ) {

            $r .= "<li>Resending Message to Moderations for Moderation....</li>";
 		   
		    # JEBUSS:
            my $full_msg = $mod->get_msg( { -msg_id => $msg_id } );
			my $entity = $parser->parse_data(safely_encode($full_msg)); 
	        my $from = $entity->head->get( 'From', 0 );
	           $from =~ s/\n//g;
			
   			warn "moderated message ($msg_id) acted upon (resend) by, " . $acting_email_address; 
			
			
           $mod->moderation_msg(
                {
                    -msg    => $full_msg,
                    -msg_id => $msg_id,
                    -from   => $from, # Really. 
                    -parser => $parser
                }
            );
		   	
        }
        else {
            $r .= "<li>Invalid action</li>";
			
   			warn "moderated message ($msg_id) acted upon (invalod_action) by, " . $acting_email_address; 
			warn 'invalid action: ' . $q->param('process'); 
			
			
        }

    }
    else {
        $r .= "<li>Moderated message doesn't exist - most likely it was already moderated.</li>";
    }

	$r .= "</ul>";
	
    if ($checksout) {
        $r .= '<p><a class="button" href="'
          . $DADA::Config::S_PROGRAM_URL
          . '?flavor=plugins&plugin=bridge&prm=awaiting_msgs">Awaiting Message Index...</a></p>';

        $r .= admin_template_footer(
            -Form => 0,
            -List => $list,
        );
    }
    else {
        $r .= list_template( -Part => "footer" );
    }

    return ( {}, $r );

}

sub validate_list_email {

    my ($args) = @_;

    my $list = $args->{-list};
    if ( !exists( $args->{-list_email} ) ) {
        return ( 1, {} );
    }

    my $list_email = $args->{-list_email};

    $list_email = DADA::App::Guts::strip($list_email);
    my $status = 1;

    my @list_types = qw(
      list
      authorized_senders
      moderators
    );

    # white_list
    # black_list

    my $errors = {
        list_email_set_to_list_owner_email => 0,
        list_email_set_to_list_admin_email => 0,
    };
    for (@list_types) {
        $errors->{ 'list_email_subscribed_to_' . $_ } = 0;
    }

    if ( $list_email eq '' ) {
        return ( 1, $errors );
    }

    require DADA::MailingList::Settings;
    require DADA::MailingList::Subscribers;

    for my $t_list ( available_lists() ) {

        my $ls = DADA::MailingList::Settings->new( { -list => $t_list } );
        if ( 
			cased( $ls->param('list_owner_email') ) eq 
			cased($list_email)
		) {
            if ( $t_list eq $list ) {
                $errors->{list_email_set_to_list_owner_email} = 1;
            }
            else {
                $errors->{list_email_set_to_another_list_owner_email} = 1;
            }
            $status = 0;
        }
        if ( 
			cased( $ls->param('admin_email') ) eq cased($list_email) 
		) {

            if ( $t_list eq $list ) {
                $errors->{list_email_set_to_list_admin_email} = 1;
            }
            else {
                $errors->{list_email_set_to_another_list_admin_email} = 1;
            }
            $status = 0;

        }
        my $lh = DADA::MailingList::Subscribers->new( { -list => $t_list } );

        for my $type (@list_types) {
            if (
                $lh->check_for_double_email(
                    -Email => $list_email,
                    -Type  => $type,
                ) == 1
              )
            {
                if ( $t_list eq $list_email ) {
                    $errors->{ 'list_email_subscribed_to_' . $type } = 1;
                }
                else {
                    $errors->{ 'list_email_subscribed_to_another_' . $type } = 1;
                }
                $status = 0;
            }
        }
    }

    #	use Data::Dumper;
    #	die Dumper([$status, $errors]);
    return ( $status, $errors );
}

sub edit {

    my $q = shift; 
    
    my $ls = DADA::MailingList::Settings->new( { -list => $list } );

    #  group_list_pp_mode                         => 0,
    
	
    my %bridge_settings_defaults = (
        disable_discussion_sending                                => 0,
        msg_soft_size_limit                                       => undef, 
        msg_hard_size_limit                                       => undef,
        group_list                                                => 0,
        group_list_pp_mode_from_phrase                            => undef,
        prefix_list_name_to_subject                               => 0,
        no_prefix_list_name_to_subject_in_archives                => 0,
        discussion_pop_email                                      => undef,
        bridge_list_email_type                                    => 'pop3_account',
        discussion_pop_server                                     => undef,
		discussion_pop_port                                       => 'AUTO',
        discussion_pop_username                                   => undef,
		discussion_pop_password                                   => undef, 
        discussion_pop_auth_mode                                  => undef,
        discussion_pop_use_ssl                                    => 0,
		discussion_pop_starttls                                   =>  0,
		discussion_pop_ssl_verify_mode                            => 0, 
        prefix_discussion_list_subjects_with                      => '',
		bridge_mention_original_sender                            => 0, 
        enable_moderation                                         => 0,
		bridge_use_moderation_for                                 => undef,
		bridge_recently_added_subscribers_timeframe               => undef, 
        moderate_discussion_lists_with                            => 'list_owner_email',
        send_moderation_msg                                       => 0,
        send_moderation_accepted_msg                              => 0,
        send_moderation_rejection_msg                             => 0,
		bridge_auto_reject_awaiting_moderation_messages           => 0, 
		bridge_auto_reject_awaiting_moderation_messages_timeframe => 0, 
        enable_authorized_sending                                 => 0,
        authorized_sending_no_moderation                          => 0,
        subscriber_sending_no_moderation                          => 0,
        send_received_msg                                         => 0,
        send_msgs_to_list                                         => 0,
        send_msg_copy_to                                          => 0,
        send_msg_copy_address                                     => '',
        send_not_allowed_to_post_msg                              => 0,
        send_invalid_msgs_to_owner                                => 0,
        mail_discussion_message_to_poster                         => 0,
        strip_file_attachments                                    => 0,
        file_attachments_to_strip                                 => '',
        ignore_spam_messages                                      => 0,
        ignore_spam_messages_with_status_of                       => 0,
        rejected_spam_messages                                    => 0,
        set_to_header_to_list_address                             => 0,
        find_spam_assassin_score_by                               => undef,
        open_discussion_list                                      => 0,
        rewrite_anounce_from_header                               => 0,
		announce_from_header_allowed_domains                      => '',
        discussion_template_defang                                => 0,
        digest_enable                                             => 0,
        digest_schedule                                           => 86400,
		delivery_prefs_set_default                                => undef, 
		delivery_prefs_default                                    => undef, 
        bridge_announce_reply_to                                  => 'none', 
		bridge_announce_reply_to_custom_email_address             => undef, 
		bridge_send_internal_problem_to_list_owner                => 0,
    );

    # Validation, basically.
    my $list_email_status = 1;
    my $list_email_errors = {};


    if ( $q->param('process') == 1 ) {

	    if ( ! defined( $q->param('discussion_pop_password') )
		 || length($q->param('discussion_pop_password')) <= 0
		 
		 ) {
			# warn 'no discussion_pop_password: '  . $q->param('discussion_pop_password');
			delete($bridge_settings_defaults{discussion_pop_password}); 
	    }
		else { 
			# warn 'yes discussion_pop_password: '  . $q->param('discussion_pop_password');
		}
		
		if(defined($q->param('announce_from_header_allowed_domains'))){ 
			my $tmp = $q->param('announce_from_header_allowed_domains');
			   $tmp =~ s/\r\n/\n/g;
			   $q->param('announce_from_header_allowed_domains', $tmp); 
		}

        $ls->save_w_params(
            {
                -associate => $q,
                -settings  => {%bridge_settings_defaults}
            }
        );
        return ( { -redirect_uri => $DADA::Config::S_PROGRAM_URL . '?f=plugins&plugin=bridge&prm=edit&done=1' },
            undef );
    }
    else {
        # Not editing!
    }

    ( $list_email_status, $list_email_errors ) = validate_list_email(
        {
            -list       => $list,
            -list_email => $ls->param('discussion_pop_email'),
        }
    );

    my $lh = DADA::MailingList::Subscribers->new( { -list => $list } );
    my $auth_senders_count = $lh->num_subscribers( { -type => 'authorized_senders' } );
    my $moderators_count   = $lh->num_subscribers( { -type => 'moderators' } );
    my $requires_moderation_count   = $lh->num_subscribers( { -type => 'requires_moderation' } );


    my $has_discussion_pop_password = 0;
    if ( defined( $ls->param('discussion_pop_password') ) ) {
        $has_discussion_pop_password = 1;
    }

    my $can_use_ssl = 1;
    try {
        require IO::Socket::SSL;
    }
    catch {
        $can_use_ssl = 0;
    };
	
	require HTML::Menu::Select;
    my $discussion_pop_auth_mode_popup = HTML::Menu::Select::popup_menu(
        { 
			id       => 'discussion_pop_auth_mode',
	        name     => 'discussion_pop_auth_mode',
	        default  => $ls->param('discussion_pop_auth_mode'),
	        values   => [qw(POP APOP)],
		}
    );
    my $spam_level_popup_menu = HTML::Menu::Select::popup_menu(
        { 
			values => [ 1 .. 50 ],
	        default  => $ls->param('ignore_spam_messages_with_status_of'),
	        name     => 'ignore_spam_messages_with_status_of',
			id       => 'ignore_spam_messages_with_status_of', 
		}
    );
    my $digest_labels = {
        3600   => 'Hour',
        10800  => '3 Hours',
        21600  => '6 Hours',
        43200  => '12 Hours',
        86400  => 'Day',
        259200 => '3 Days',
        604800 => 'Week',
    };
    my $digest_schedule_popup_menu = HTML::Menu::Select::popup_menu(
        { 
			values => [ sort { $a <=> $b } keys %$digest_labels ],
	        labels   => $digest_labels,
	        default  => scalar $ls->param('digest_schedule'),
	        name     => 'digest_schedule',
	        id       => 'digest_schedule',
		}
    );
	
	my $delivery_prefs_labels = { 
		digest     => 'Digest',
		individual => 'Individual',
		hold       => 'Hold Mailings (vacation, etc)',
	};
	
    my $delivery_prefs_default_popup_menu = HTML::Menu::Select::popup_menu(
        { 
			values => [ qw(individual digest hold) ],
	        labels   => $delivery_prefs_labels,
	        default  => scalar $ls->param('delivery_prefs_default'),
	        name     => 'delivery_prefs_default',
	        id       => 'delivery_prefs_default',
		}
    );
	
    my $msg_sizes = { 
        1048576  => '1 Megabyte',
        2097152  => '2 Megabytes', 
        2621440  => '2.5 Megabytes',
        3145728  => '3 Megabytes', 
        4194304  => '4 Megabytes', 
        5242880  => '5 Megabytes', 
        6291456  => '6 Megabytes', 
        7340032  => '7 Megabytes', 
        8388608  => '8 Megabytes', 
        9437184  => '9 Megabytes', 
        10485760 => '10 Megabytes',
        20971520 => '20 Megabytes',
    }; 
	
    my $msg_soft_size_limit_popup_menu= HTML::Menu::Select::popup_menu(
        { 
			values   => [sort  {$a <=> $b} keys %$msg_sizes],
	        labels   => $msg_sizes, 
	        default  => $ls->param('msg_soft_size_limit'),
	        name     => 'msg_soft_size_limit',
	        id       => 'msg_soft_size_limit',
		}
    );
    my $msg_hard_size_limit_popup_menu  = HTML::Menu::Select::popup_menu(
        { 
			values => [sort  {$a <=> $b} keys %$msg_sizes],
	        labels   => $msg_sizes, 
	        default  => $ls->param('msg_hard_size_limit'),
	        name     => 'msg_hard_size_limit',
	        id       => 'msg_hard_size_limit',
		}
    );
	
    my $done = $q->param('done') || 0;

    my $ses_params = {};
    require DADA::App::AmazonSES;
    my $ses = DADA::App::AmazonSES->new;
	
    if ( 
	
		(
			$ls->param('sending_method') eq 'amazon_ses'
		)
        || 
		( 
				$ls->param('sending_method') eq 'smtp' 
			 && $ls->param('smtp_server') =~ m/amazonaws\.com/ 
			 && $ses->has_ses_options_set == 1 
		 )
	){
        $ses_params->{using_ses} = 1;
       
        $ses_params->{list_owner_ses_verified}     = $ses->sender_verified( $ls->param('list_owner_email') );
        $ses_params->{list_admin_ses_verified}     = $ses->sender_verified( $ls->param('admin_email') );
        $ses_params->{discussion_pop_ses_verified} = $ses->sender_verified( $ls->param('discussion_pop_email') );
    }
    
    my $program_abs_path = $RealScript; 
    $program_abs_path    =~ s/$FindBin::Bin//; 
    $program_abs_path    = $FindBin::Bin . '/' . $RealScript; 
    	
    my $mod = SimpleModeration->new( { -List => $ls->param('list') } );
    my $num_awaiting_msgs = $mod->num_awaiting_msgs;
   
   
    my $scrn = DADA::Template::Widgets::wrap_screen(
        {
            -screen         => 'plugins/bridge/default.tmpl',
            -with           => 'admin',
            -wrapper_params => {
                -Root_Login => $root_login,
                -List       => $list,
            },
            -vars => {
                screen                         => 'using_bridge',
                Plugin_URL                     => $Plugin_Config->{Plugin_URL},
                Plugin_Name                    => $Plugin_Config->{Plugin_Name},
                Allow_Manual_Run               => $Plugin_Config->{Allow_Manual_Run},
                Manual_Run_Passcode            => $Plugin_Config->{Manual_Run_Passcode},
                can_use_ssl                    => $can_use_ssl,
                done                           => $done,
                authorized_senders_count       => $auth_senders_count,
                moderators_count               => $moderators_count,
				requires_moderation_count      => $requires_moderation_count, 
				num_awaiting_msgs              => $num_awaiting_msgs, 
                has_discussion_pop_password    => $has_discussion_pop_password,
                discussion_pop_auth_mode_popup => $discussion_pop_auth_mode_popup,
                msg_soft_size_limit_popup_menu => $msg_soft_size_limit_popup_menu, 
                msg_hard_size_limit_popup_menu => $msg_hard_size_limit_popup_menu, 
                can_use_spam_assassin          => &can_use_spam_assassin(),
                spam_level_popup_menu          => $spam_level_popup_menu,
                find_spam_assassin_score_by_calling_spamassassin_directly =>
                  ( $ls->param('find_spam_assassin_score_by') eq 'calling_spamassassin_directly' ) ? 1 : 0,
                find_spam_assassin_score_by_looking_for_embedded_headers =>
                  ( $ls->param('find_spam_assassin_score_by') eq 'looking_for_embedded_headers' ) ? 1 : 0,
                list_email_status                        => $list_email_status,
               # mailing_list_message_from                => $mailing_list_message_from,
                
				digest_schedule_popup_menu               => $digest_schedule_popup_menu,
				delivery_prefs_default_popup_menu        => $delivery_prefs_default_popup_menu, 
				
                error_list_email_set_to_list_owner_email => $list_email_errors->{list_email_set_to_list_owner_email},
                error_list_email_set_to_list_admin_email => $list_email_errors->{list_email_set_to_list_admin_email},
                error_list_email_subscribed_to_list      => $list_email_errors->{list_email_subscribed_to_list},
                error_list_email_subscribed_to_authorized_senders =>
                  $list_email_errors->{list_email_subscribed_to_authorized_senders},
                error_list_email_subscribed_to_moderators => $list_email_errors->{list_email_subscribed_to_moderators},
                error_list_email_set_to_another_list_owner_email =>
                  $list_email_errors->{list_email_set_to_another_list_owner_email},
                error_list_email_set_to_another_list_admin_email =>
                  $list_email_errors->{list_email_set_to_another_list_admin_email},
                error_list_email_subscribed_to_another_list =>
                  $list_email_errors->{list_email_subscribed_to_another_list},
                error_list_email_subscribed_to_another_authorized_senders =>
                  $list_email_errors->{list_email_subscribed_to_another_authorized_senders},
                error_list_email_subscribed_to_another_moderators =>
                  $list_email_errors->{list_email_subscribed_to_another_moderators},
                %$ses_params,
                program_abs_path               => $program_abs_path,
                
            },
            -list_settings_vars_param => {
                -list                 => $list,
                -dot_it               => 1,
                -i_know_what_im_doing => 1,
            },
        }

    );
    return ( {}, $scrn );

}



sub scheduled_task { 
    
    my $list = shift; 
    if($list eq '_all'){
        undef $list; 
    }
    my ($h, $b) = start($list);
    return $b;  
}




sub start {
    
    my $list_to_run = shift;
    
	reset_globals();
    
    my $r     = '';
    my @lists = ();
    
	if ( ! defined($list_to_run) ) {
        @lists = available_lists( -In_Random_Order => 1 );
    }
    else {
        push(@lists, $list_to_run);
    }

  my $messages_viewed = 0;
    
  LIST_QUEUE: for my $list (@lists) {
        
        if ( $messages_viewed >= $Plugin_Config->{MessagesAtOnce} ) {
            $r .= "\n\nThe limit has been reached of the amount of messages to be looked at for this execution\n\n";
            last;
        }

        my $ls = DADA::MailingList::Settings->new( { -list => $list } );

        $r .= "Mailing List: " . $ls->param('list_name') . ' (' . $list . ")\n";

        if ( $ls->param('disable_discussion_sending') == 1 ) {
            $r .= "\t* Bridge is not enabled for, $list \n";
			undef($ls);
            next LIST_QUEUE;
        }
        if ( $ls->param('bridge_list_email_type') eq "mail_forward_pipe" ) {
            $r .= "\t* List Email is set up as a Email Forward to Pipe to Bridge \n";
			undef($ls);
            next LIST_QUEUE;
        }
		
        if ( length($ls->param('discussion_pop_email')) <= 0
		|| 	check_for_valid_email($ls->param('discussion_pop_email')) == 1
		
		) {
            $r .= "\t* ***Warning!*** Misconfiguration of plugin! List Email has not been set to a valid email address!\n\t\tSkipping $list...";
			undef($ls);
            next LIST_QUEUE;
        }
		
        if ( 
			cased($ls->param('discussion_pop_email')) eq 
			cased($ls->param('list_owner_email'))
		) {
            $r .=
"\t\t***Warning!*** Misconfiguration of plugin! The List Owner email cannot be the same address as the list email address!\n\t\tSkipping $list...\n";
			undef($ls);
            next LIST_QUEUE;
        }

        if ( !valid_login_information($ls) ) {
            $r .=
"\t\tLogin information doesn't seem to be valid. Make sure you've supplied everything needed: List Email, POP3 Server, POP3 Username, POP3 Password";
            undef($ls);
			next LIST_QUEUE;
        }

        my $lock_file_fh = undef;
        if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
            $lock_file_fh = DADA::App::POP3Tools::_lock_pop3_check( { name => 'bridge-' . $list . '.lock' } );
			if(!defined($lock_file_fh)){ 
				$r .= "couldn't get a lock for $list, skipping.";
	            next LIST_QUEUE;
			}
        }
        my ( $pop3_obj, $pop3_status, $pop3_log ) = pop3_login($ls);

        $r .= $pop3_log;
        if ( $pop3_status == 0 ) {
            $r .= "\t* POP3 connection failed!\n";
			
	        if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
	            DADA::App::POP3Tools::_unlock_pop3_check(
	                {
	                    name => 'bridge-' . $list . '.lock',
	                    fh   => $lock_file_fh,
	                }
	            );
	        }
			
            next LIST_QUEUE;
        }


		my $pop3_msg_list = $pop3_obj->list; 

		my $msg_count = 0; 
           $msg_count = scalar keys %$pop3_msg_list;
		
		if($msg_count == 0){ 
			$r .= "\t* No messages awaiting for, $list \n";
			next LIST_QUEUE; 
		 }
		   
        my $local_msg_viewed = 0;

		MSG_QUEUE: for my $msgnum (sort { $a <=> $b } keys  %$pop3_msg_list) {

			my $msgsize = $pop3_msg_list->{$msgnum};

            if ( $messages_viewed >= $Plugin_Config->{MessagesAtOnce} ) {
                last;
            }
			
            $messages_viewed++;
            $local_msg_viewed++;

            $r .= "\n";
            $r .= "Message Size: " . human_readable_filesize($msgsize) . "\n";
            
            my ($mmt_status, $mmt_r) = max_msg_test( 
                { 
                    -size    => $msgsize,
                    -ls_obj => $ls,
                 } 
            );
            $r .= $mmt_r; 
            
            if ($mmt_status  == 0 ) {
                # We don't do anything else to this guy
                next MSG_QUEUE;
            }
			
		   my $msg_ar = $pop3_obj->get($msgnum);
		   # lazy, but... 
		   my $full_msg = join("", @$msg_ar); 
            
            push( @{ $checksums->{$list} }, create_checksum( \$full_msg ) );

            # $full_msg = safely_decode($full_msg);
            
            my ($smmt_status, $smmt_r) = soft_max_msg_test( 
                { 
                    -size   => $msgsize,
                    -ls_obj => $ls, 
					-msg_ref => \$full_msg,
                 } 
            );
            $r .= $smmt_r; 
            
            if ( $smmt_status == 0 ) {
                send_msg_too_big( $ls, \$full_msg, $msgsize );
                next MSG_QUEUE;
            }

            $r .= "\t* Message Size is below both Soft and Hard Max Sizes.\n\n";


#			warn 'going to try to change  Content-Transfer-Encoding (0)';
			
            try { 
               require DADA::App::FormatMessages;
               my $dfm = DADA::App::FormatMessages->new( -List => $ls->param('list') );
                my $entity = $parser->parse_data(safely_encode($full_msg)); 
                   $entity = $dfm->change_content_transfer_encoding({-entity => $entity}); 
                   $full_msg    = safely_decode($entity->as_string); 
                   undef $entity; 
                   # warn 'Hey Hey! $full_msg ' . safely_encode($full_msg); 
            } catch { 
                warn 'Couldn\'t change Content-Transfer-Encoding: ' . $_; 
            };
            
            
            try {
				
                my ( $status, $errors, $validate_r ) = validate_msg( $ls, \$full_msg );
                $r .= $validate_r; 
                
                if ($status) {

                    $r .= process(
                        {
                            -ls  => $ls,
                            -msg => \$full_msg,
                        }
                    );
                }
                else {
                    $r .= "\tMessage did not pass verification.\n";
                    $r .= handle_errors( $ls, $errors, $full_msg );
                }
                append_message_to_file( $ls, $full_msg );

            } catch { 
                cluck "bridge - irrecoverable error processing message. Skipping message: $_";
                # Then, we have to tell the og sender, something went wrong: 
                $r .= "bridge - irrecoverable error processing message. Skipping message: $_";
				
				if($ls->param('bridge_send_internal_problem_to_list_owner') == 1){
					$r .= "Sending a copy of the message to the List Owner\n";
					send_bridge_internal_problem_to_list_owner($ls, $_, $full_msg); 
				}
            };

        }    # MSG_QUEUE

        my $delete_msg_count = 0;
		
		for my $msgnum_d (sort { $a <=> $b } keys  %$pop3_msg_list) {

            $r .= "\t* Removing message from server:\n";
			# $r .= "\t* Message # $msgnum_d marked to be deleted\n";
            $pop3_obj->delete($msgnum_d);
            $delete_msg_count++;

            last
              if $delete_msg_count >= $local_msg_viewed;

        }
        $r .= "\t* Disconnecting from POP3 server\n";

        $pop3_obj->quit();

        if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
            DADA::App::POP3Tools::_unlock_pop3_check(
                {
                    name => 'bridge-' . $list . '.lock',
                    fh   => $lock_file_fh,
                }
            );
        }

        if ($check_deletions) {
            if ( keys  %$pop3_msg_list ) {
                $r .= message_was_deleted_check($ls);
            }
            else {
                $r .= "\t* No messages received, skipping deletion check.\n";
            }
        }
		
		
    }    # LIST_QUEUE?

	MODERATION_QUEUE: for my $list (@lists) {		
			
        my $ls = DADA::MailingList::Settings->new( { -list => $list } );
			
	    if ( 
			   $ls->param('group_list') == 1 
			&& $ls->param('enable_moderation') == 1
		) {
			$r .= "\nChecking for messages awaiting moderation:\n" . '-' x 72 . "\n";
		
	        my $mod = SimpleModeration->new( { -List => $list } );
		
	        my $awaiting_msgs = $mod->awaiting_msgs();


	        for (@$awaiting_msgs) {
	            $r .= "\t\t * " . $_->{name} . "\n";
				
				if($ls->param('bridge_auto_reject_awaiting_moderation_messages') == 1){ 
					
					my $epoch = $_->{date}; 
					my $padded = int($epoch) + (int($ls->param('bridge_auto_reject_awaiting_moderation_messages_timeframe')) * 86400); 
				
					if($padded <= time){ 
						$r .= "\t\t\tMessage is older than, " 
						. $ls->param('bridge_auto_reject_awaiting_moderation_messages_timeframe')
						. ' day(s); deleting. '; 
						
						my ($valid_msg, $imm_r) = $mod->is_moderated_msg($_->{name});
						if($valid_msg){
							$mod->send_reject_msg(
			                    {
			                        -msg_id => $_->{name},
			                        -parser => $parser,
			                    }
			                );
			           	 	$mod->remove_msg( { -msg_id =>  $_->{name} } );
						}
					}
				}
	        }
		}
	}
	


    $r .= "\nProcessing Digests:\n" . '-' x 72 . "\n";

  	DIGEST_QUEUE: for my $list (@lists) {
            
        my $ls = DADA::MailingList::Settings->new( { -list => $list } );

        $r .= "Mailing List: " . $ls->param('list_name') . ' (' . $list . ")\n";
        
        
        if ( $ls->param('disable_discussion_sending') == 1 ) {
            $r .= "\t* Bridge is not enabled for, $list \n";
            next DIGEST_QUEUE;
        }
        if ( $ls->param('digest_enable') != 1 ) {
            $r .= "\t* Digests are not enabled for, $list \n";
            next DIGEST_QUEUE;
        }

        require DADA::App::Digests;
        my    $digest = DADA::App::Digests->new( { -list => $list } );
        $r .= $digest->send_digest();

    }    # DIGEST_QUEUE




    return ( {}, $r );
}

sub max_msg_test {

    my ($args) = @_;

    my $ls = $args->{-ls_obj}; 

    my $r      = '';
    my $size   = $args->{-size};

    if ( $size > $ls->param('msg_hard_size_limit') ) {

        $r .=
            "\t* Warning! Message size ( "
          . $size
          . " ) is larger than the maximum size allowed ( "
          . $ls->param('msg_hard_size_limit') . " )\n";
        warn "bridge Warning! Message size ( "
          . $size
          . " ) is larger than the maximum size allowed ( "
          . $ls->param('msg_hard_size_limit') . ")";
        return ( 0, $r );
    }
    else {
        $r .= "\t Received message is below maximum size allowed.\n";
        return ( 1, $r );
    }

}

sub soft_max_msg_test {

    my ($args) = @_;

    my $ls = $args->{-ls_obj}; 
    
    my $r = '';

    my $size = $args->{-size};
    if ( $size > $ls->param('msg_soft_size_limit') ) {


		
		my $e_msg = "\t* Warning! Message size ( "
          . $size
          . " bytes) is larger than the soft maximum size allowed ( "
          . $ls->param('msg_soft_size_limit') . "bytes )\n";
		
		
	    if ( $size < $ls->param('msg_hard_size_limit') ) {
			my $msg_ref = $args->{-msg_ref};
			try { 
				
				
				$e_msg = "\t* Warning! Message size ( "
				          . human_readable_filesize($size) 
				          . " ) is larger than the soft maximum size allowed ( "
				          . human_readable_filesize($ls->param('msg_soft_size_limit'))   . " )\n";
				
	  			$e_msg .= "\t\tMailing List: " . $ls->param('list_name') .            "\n";
				
						  
				my $entity = $parser->parse_data($$msg_ref); 	
				
				if ( !$entity ) {
					warn "couldn't parse the entity?"; 
				}
				else{ 
					$e_msg .= "\t\tDate: "         . $entity->head->get( 'Date', 0 );
					$e_msg .= "\t\tFrom: "         . $entity->head->get( 'From', 0 );
					$e_msg .= "\t\tSubject: "      . $entity->head->get( 'Subject', 0 ) . "\n";				
				}
			} catch { 
				warn $_; 
			}
		}
		
        $r .= $e_msg; 
		
        return ( 0, $r );
    }
    else {
        $r .= "\t Received message is below maximum soft size allowed.\n";
		return ( 1, $r );
    }

}

sub inject_msg {
    
    reset_globals(); 
    
    my ($args) = @_; 
    my $r; 
    
    my $filename = $args->{-filename};
    
	$tmp_msg_file = $filename; 
		
    if(! -e $filename){ 
        $r .= "No file found at, $filename\n";  
        return $r; 
    }
        
    my $run_list = $args->{-list};
    my $list     = $run_list;
    

	# There should be more to this - we should send an email back to the og 
	# sender, telling them the email address is set up incorrectly. 
	#
    if ( check_if_list_exists( -List => $list) == 0 ) {
		die   'Passed non-existent list, ' . $list . ', bailing.';
		# return 'Passed non-existent list, ' . $list . ', bailing.';
    }
    my $ls = DADA::MailingList::Settings->new( { -list => $list } );
    my $r;

    my $size = ( stat($filename) )[7];
    my ( $status, $error_msg ) = max_msg_test( 
        { 
            -size   => $size,
            -ls_obj => $ls,
         } 
    );
    if ( $status == 0 ) {
        return $error_msg;
    }
    require File::Slurper;	 
	 my $msg = File::Slurper::read_text(
	 	$filename, 
		$DADA::Config::HTML_CHARSET
	);
	
    try { 
        $msg = safely_decode( $msg );
    } catch {
        warn 'extra decoding did not work: ' . $_; 
    };
   
    if ( $ls->param('bridge_list_email_type') ne "mail_forward_pipe" ) {

        $r .= "\t* Bridge is not enabled to receive mail this way, for this list. \n";

        warn "Bridge is not enabled to receive mail this way, for this list.";
    }
    else {
        my ( $status, $error_msg ) = soft_max_msg_test( 
            { 
                -size    => $size,
                -ls_obj  => $ls, 
				-msg_ref => \$msg, 
            } 
        );
        if ( $status == 0 ) {
            $r .= $error_msg;
            send_msg_too_big( $ls, \$msg, $size );
        }
        else {

            my ( $status, $errors, $msg ) = inject(
                {
                    -ls        => $ls,
                    -msg       => $msg,
                    -verbose   => $verbose,
                    -test_mail => $test,
                }
            );
            $r .= $msg;
        }
    } 
	
	$rm_tmp_msg_file = 1; 
	
    return $r;
}

sub message_was_deleted_check {

    my $r = '';

    # DEV: Nice for testing...
    #return;

    $r .= "\n\t* Waiting 5 seconds before removal check...\n";

    sleep(5);

    my $ls = shift;

    my $lock_file_fh = undef;
    if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
        $lock_file_fh = DADA::App::POP3Tools::_lock_pop3_check( { name => 'bridge-' . $ls->param('list') . '.lock', } );
		if(!defined($lock_file_fh)){ 
			$r .= "couldn't get a lock for " . $ls->param('list') . ", skipping.";
            return $r; 
		}
    	
	}

    my ( $pop3_obj, $pop3_status, $pop3_log ) = pop3_login($ls);

    if ( $pop3_status == 1 ) {

		my $pop3_msg_list = $pop3_obj->list; 
		
		my $msg_count = 0; 
           $msg_count = scalar keys %$pop3_msg_list;
		   
        if ( $msg_count < 1 ) {
            $r .= "\t\tNo messages to check.\n";
			return $r;
        }

		for my $msgnum (sort { $a <=> $b } keys  %$pop3_msg_list) {
			
			my $msg_size = $pop3_msg_list->{$msgnum};
   
 		   my $msg_ar = $pop3_obj->get($msgnum);
 		   my $msg    = join("", @$msg_ar); 

            my $cs = create_checksum( \$msg );

            $r .= "\t\tcs:             $cs\n";

            my @cs;
            if ( exists($checksums->{$list}) ) {
                @cs = @{ $checksums->{$list} };
            }

            for my $s_cs (@cs) {

                $r .= "\t\tsaved checksum: $s_cs\n";

                if ( $cs eq $s_cs ) {
                    $r .= "\t* Message was NOT deleted from POP server! Will attempt to do that now...\n";
                    $pop3_obj->delete($msgnum);
                }
                else {
                    $r .= "\t* Message checksum does not match saved checksum, keeping message for later delivery...\n";
                }
            }
        }

        $pop3_obj->quit();

    }
    else {
        $r .= "POP3 login failed.\n";
    }

    if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
        DADA::App::POP3Tools::_unlock_pop3_check(
            {
                name => 'bridge-' . $ls->param('list') . '.lock',
                fh   => $lock_file_fh,
            }
        );
    }

    return $r;

}

sub help {
    my $h = q{ 

arguments: 
-----------------------------------------------------------
--help                 		
--verbose
--test pop3
--inject

-----------------------------------------------------------
for a general overview and more instructions, try:

pod2text ./bridge | less

-----------------------------------------------------------

--help

Displays a help menu.

--list

Will allow you to work on one list at a time, instead of all the lists you 
have. 

--verbose 

Runs the script in verbose mode. 

--test pop3

Allows you to test the pop3 login information on the command line. 
Currently the only test available. 

Example: 

 prompt>bridge --test pop3 --list yourlistshortname

Will test the pop3 connection of a list with a shortname of, 
yourlistshortname

Another Example: 

 prompt>bridge --verbose --list yourlistshortname

Will check for messages to deliver for list, 
yourlistshortname> and outputting a lot of information on the command line. 

--inject

When this flag is passed, Bridge will then read a full email message from STDIN, and process the message it receives. You will need to also pass the, --list parameter. 

This flag will only work if you have set your mailing list to use a  Email Forward as its List Email, and not a POP3 Account. 

};

    return ( {}, $h );

}

sub test_pop3 {

    my $r = '';

    my @lists;

    if ( !$run_list ) {

        $r .=
          "Testing all lists - \nTo test an individual list, pass the list shortname in the '--list' parameter...\n\n";

        @lists = available_lists( -In_Random_Order => 1 );
    }
    else {
        push( @lists, $run_list );
    }

    for my $l (@lists) {

        $r .= "\n" . '-' x 72 . "\nTesting List: '" . $l . "'\n";

        unless ( check_if_list_exists( -List => $l, ) ) {
            $r .= "'$l' does not exist! - skipping\n";
            next;
        }

        my $ls = DADA::MailingList::Settings->new( { -list => $l } );

        my $lock_file_fh = undef;
        if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
            $lock_file_fh = DADA::App::POP3Tools::_lock_pop3_check( { name => 'bridge-' . $l . '.lock', } );
			if(!defined($lock_file_fh)){ 
				$r .= "couldn't get a lock for " . $l . ", skipping.";
	            next;
			}
			
        }

        my ( $pop3_obj, $pop3_status, $pop3_log ) = pop3_login($ls);
        $r .= $pop3_log;
        if ( $pop3_status == 1 ) {
            $pop3_obj->quit();
            $r .= "\tLogging off of the POP Server.\n";
        }
		
        if ( $Plugin_Config->{Enable_POP3_File_Locking} == 1 ) {
            DADA::App::POP3Tools::_unlock_pop3_check(
                {
                    name => 'bridge-' . $l . '.lock',
                    fh   => $lock_file_fh,
                }
            );
        }
		
    }
    $r .= "\n\nPOP3 Login Test Complete.\n\n";
    return $r;
}

sub pop3_login {
    my $ls = shift;

    my $r;
    my $password =
      DADA::Security::Password::cipher_decrypt( $ls->param('cipher_key'),
        $ls->param('discussion_pop_password') );
    if ( !valid_login_information($ls) ) {
        $r .=
"Some POP3 Login Information is missing - please double check! (aborting login attempt)\n";
        return ( undef, undef, $r );
    }
    else {

        my $pop;
        my $status;
        my $log;

        try {
            ( $pop, $status, $log ) = DADA::App::POP3Tools::net_pop3_login(
                {
                    server    => $ls->param('discussion_pop_server'),
                    username  => $ls->param('discussion_pop_username'),
                    password  => $password,
                    verbose   => $verbose,
                    USESSL    => $ls->param('discussion_pop_use_ssl'),
                    AUTH_MODE => $ls->param('discussion_pop_auth_mode'),
                }
            );
        }
        catch {
            $r .= "Problems Logging in:\n$_";
            warn $_;
            return ( undef, undef, $r );
        };

        return ( $pop, $status, $log );
    }

    return $r;
}

sub valid_login_information {

    my $ls = shift;
    return 0 if !defined( $ls->param('discussion_pop_server') );
    return 0 if !defined( !$ls->param('discussion_pop_username') );
    return 0 if !defined( !$ls->param('discussion_pop_email') );
    return 0 if !defined( !$ls->param('discussion_pop_password') );
    return 1;
}

sub validate_msg {


	# to ddress is list email, 
	# from ddress is From emial
	# Naw, need header.... 
	# X-From_App
    my $ls       = shift;
    my $test_msg = shift;           #ref
    my $msg      = ${$test_msg};    # copy of orig

    my $status = 1;
    my $notice = '';
    my $r      = '';

    # DEV:
    # This should *really* mention each and every test....

    my $errors = {
        multiple_from_addresses                  => 0,
        msg_from_list_address                    => 0,
        list_email_address_is_list_owner_address => 0,
		list_email_address_is_list_admin_address => 0,
        invalid_msg                              => 0,
        multiple_return_path_headers             => 0,
        x_been_there_header_found                => 0,
        msg_not_from_list_owner                  => 0,
        needs_moderation                         => 0,
        subscribed                               => 0,
        msg_not_from_subscriber                  => 0,
        msg_not_from_list_owner                  => 0,
        msg_not_from_an_authorized_sender        => 0,
        message_seen_as_spam                     => 0,
		message_too_old                          => 0,
    };

    my $lh =
      DADA::MailingList::Subscribers->new( { -list => $ls->param('list') } );

    if ( 
		   lc_email( $ls->param('discussion_pop_email') ) 
		eq lc_email( $ls->param('list_owner_email') ) 
	) {
        $r .= "\t\t***Warning!*** Misconfiguration of plugin! The List Owner email cannot be the same address as the List Email address!\n";
        $errors->{list_email_address_is_list_owner_address} = 1;
    }
	
    if ( lc_email( $ls->param('discussion_pop_email') ) eq lc_email( $ls->param('admin_email') ) ) {
        $r .=
"\t\t***Warning!*** Misconfiguration of plugin! The List Admin email cannot be the same address as the List Email address!\n";
        $errors->{list_email_address_is_list_admin_address} = 1;
    }
	
	

    my $message_is_blank = 0;

#	warn '$msg: ' . $msg; 
	
    if ( !defined($msg) ) {
	#	warn 'message is blank!';
		
        $message_is_blank = 1;
    }
    elsif ( $msg eq '' ) {
        $message_is_blank = 1;
    }
    elsif ( length($msg) == 0 ) {
        $message_is_blank = 1;
    }
    if ($message_is_blank) {
        $r .= "\t\t***Warning!*** Message is blank.\n";
        $errors->{blank_message} = 1;
        return ( 0, $errors, $r );
    }

    my $entity;
    $msg = safely_encode($msg);

#    use Data::Dumper; 
#    warn '$parser ' . Dumper($parser); 
    
	my $ntr = 0; 
    try {
        $entity = $parser->parse_data($msg); #safely encoded above 
        #warn 'still here...'; 
    } catch { 
        $r .= "\t\tProblems with parsing message: $_\n";
        cluck "Problems with parsing message: $_\n";
        cluck '$msg:' . $msg; 
        $errors->{invalid_msg} = 1;  
		$ntr = 1;   
    };
	if($ntr == 1){ 
		return ( 0, $errors, $r );        
	}
        
    # These checks make sure that multiple From: headers and addresses don't exist
    if ( $Plugin_Config->{Check_Multiple_From_Addresses} == 1 ) {
        try {
            if ( $entity->head->count('From') > 1 ) {
                $r .= "\t\tMessage has more than one 'From' header? Unsupported email message - will reject!\n";
                $errors->{multiple_from_addresses} = 1;
            }
            else {
                my @count = Email_Address_parse( $entity->head->get( 'From', 0 ) );
                if ( scalar(@count) > 1 ) {
                    $r .= "\t\tMessage has more than one 'From' header? Unsupported email message - will reject!\n";
                    $errors->{multiple_from_addresses} = 1;
                }
            }
        } catch { 
            $r .= "\t\tError with multiple from address check! Marking as a problem! - $_";
            $errors->{multiple_from_addresses} = 1;
        };
    }

    # /These checks make sure that multiple From: headers and addresses don't exist

    if ( $Plugin_Config->{Check_Multiple_Return_Path_Headers} == 1 ) {
        if ( $entity->head->count('Return-Path') > 1 ) {
            $r .= "\t\tMessage has more than one 'Return-Path' header? Malformed email message - will reject!\n";
            $errors->{multiple_return_path_headers} = 1;
        }
    }

	my $x_been_there_header = undef; 
	if ( $entity->head->count('X-BeenThere') ) {
		$x_been_there_header = $entity->head->get( 'X-BeenThere', 0 );
		chomp($x_been_there_header);
	}
	elsif ( $entity->head->count('X-Beenthere') ) {
			$x_been_there_header = $entity->head->get( 'X-Beenthere', 0 );
			chomp($x_been_there_header);
	}
			
    if ( defined $x_been_there_header ) {

       if ( lc_email($x_been_there_header) eq lc_email( $ls->param('discussion_pop_email') ) ) {
            $r .= "\t* Message is from myself (the, X-BeenThere header has been set), message should be ignored...\n";
            $errors->{x_been_there_header_found} = 1;
        }
		elsif($x_been_there_header eq $DADA::Config::PROGRAM_URL) { 
            $r .= "\t* Message is from myself (the, X-BeenThere header has been set to the app's URL), message should be ignored...\n";
            $errors->{x_been_there_header_found} = 1;
		}
        else {
            $errors->{x_been_there_header_found} = 0;
        }
		
		ALL_LIST_EMAILS: for my $tmp_list(available_lists(-Dont_Die   => 1)){ 
			
			my $tmp_ls = DADA::MailingList::Settings->new({-list => $tmp_list}); 
			
			next if lc_email($tmp_ls->param('discussion_pop_email')) eq lc_email( $ls->param('discussion_pop_email'));
			
	        if ( lc_email($x_been_there_header) eq lc_email( $tmp_ls->param('discussion_pop_email') ) ) {
	        
			     $r .= "\t* Message is from another mailing list's List Email: '" 
				 . $x_been_there_header 
				 . "' (the, X-BeenThere header has been set), message should be ignored...\n";
	        
			     $errors->{x_been_there_header_found} = 1;
			
				 undef $tmp_ls; 
				 last ALL_LIST_EMAILS;
	         }
			 
			undef $tmp_ls; 
		}
		

    }

    my $rough_from = $entity->head->get( 'From', 0 );
	
	# warn '$rough_from: ' . $rough_from; 
	
    my $from_address = '';

    if ( defined($rough_from) ) {
        try {
            # This correct ?
            $from_address = ( Email_Address_parse($rough_from) )[0]->address;
			# warn '$from_address: ' . $from_address; 
		#	warn 'Email_Address_parse($rough_from) )[0]->address: ' . (Email_Address_parse($rough_from) )[1]->address; 
			
	    } catch { 
	         $r .= '\t*Warning! Something\'s wrong with the From address - ' . $_;
			 warn '\t*Warning! Something\'s wrong with the From address - ' . $_;
	    };
	}
	
    $from_address = lc_email($from_address);
	
	$r .= "* Checking Recipient: $from_address\n";

    if ( lc_email($from_address) eq lc_email( $ls->param('list_owner_email') ) ) {
        $r .= "\t* From List Owner: Yes.\n";
        if ( $Plugin_Config->{Check_List_Owner_Return_Path_Header} ) {
            ( $errors, $notice ) = test_Check_List_Owner_Return_Path_Header( $ls, $entity, $errors );
            $r .= $notice;
        }

    }
    else {

        $r .= "\t* From List Owner: No.\n";

        $errors->{msg_not_from_list_owner} = 1;

        if ( $ls->param('group_list') == 1 ) {

            $r .= "\t* Discussion List Support is enabled.\n";

	        if ( $ls->param('enable_moderation') ) {
	            $r .= "\t* Moderation enabled.\n";
	          
				if($ls->param('bridge_use_moderation_for') eq 'everyone'){
					$errors->{needs_moderation} = 1;
	        	}
				elsif($ls->param('bridge_use_moderation_for') eq 'requires_moderation_sublist'){ 
					
					 $r .= "\t\t* Checking From: address with, 'Requires Moderation' sublist...\n";
		            if (
		                $lh->check_for_double_email(
		                    -Email => lc_email($from_address),
		                    -Type  => 'requires_moderation',
		                ) == 1
		              ) { 
						$r .= "\t\t* Moderation required for, " . lc_email($from_address) . "\n";
					  	$errors->{needs_moderation} = 1;
					  }
					  else { 
					  	$r .= "\t\t* No moderation required.\n";
					  }
					  
				}
				# This only works if the person is subscribed (think open discussion lists):
				elsif(
	                   $lh->check_for_double_email(-Email => lc_email($from_address), -Type  => 'list') == 1
					&& $ls->param('bridge_use_moderation_for') eq 'recently_added_subscribers'
				){
					$r .= "\t\t* Checking is subscriber has been subscribed long enough to not require moderation...\n";
					
					my $timestamp = $lh->get_subscriber_timestamp({-email => $from_address, -type => 'list'});
					
					require Time::Piece;
					require Time::Piece::MySQL;
					
					my $tp = Time::Piece->from_mysql_timestamp( $timestamp );
					my $epoch = $tp->epoch; 
					my $padded = int($epoch) + (int($ls->param('bridge_recently_added_subscribers_timeframe')) * 86400); 
					
					if($padded <= time){ 
						$r .= "\t\t* No moderation required.\n";
					}
					else { 
						$errors->{needs_moderation} = 1;
						$r .= "\t\t* Moderation required for, " . lc_email($from_address) . "\n";
					}
				}
				else { 
					$errors->{needs_moderation} = 1;
				}
			
			}
	        else {
	            $r .= "\t* Moderation disabled.\n";
	        }
			
            my ( $s_status, $s_errors ) =
              $lh->subscription_check( 
			  	{ 
					-email => $from_address, 
					-mode  => 'admin'
				} 
			);

            if ( $s_errors->{subscribed} == 1 ) {
                $r .= "\t\t* From Subscriber: Yes.\n";


                $errors->{msg_not_from_list_owner} = 0;
                
				# Open discussion list - bad idea. 
				if ( $ls->param('subscriber_sending_no_moderation') ) {
                    $errors->{needs_moderation} = 0;
                }
                elsif ( $errors->{needs_moderation} == 1 ) {
                    $r .= "\t\t* Moderation Required.\n";
                }
            }
            else {
                $r .= "\t\t* From Subscriber: No.\n";

                if (   $ls->param('open_discussion_list') == 1 )
                {
                    $r .= "\t\t* Postings from non-Subscribers is enabled.\n";
                    $errors->{msg_not_from_list_owner} = 0;
                }
                else {
                    $errors->{msg_not_from_subscriber} = 1;
                }
            }

        }
        else {
            $r .= "\t* Discussion Support is disabled.\n";
        }
    }

    if ( $ls->param('enable_authorized_sending') == 1 ) {

        # cancel out other errors???
        $r .= "\t* Authorized Senders is enabled.\n";
        my ( $m_status, $m_errors ) = $lh->subscription_check(
            {
                -email => $from_address,
                -type  => 'authorized_senders',
            }
        );
        if ( $m_errors->{subscribed} == 1 ) {
            $r .= "\t\t* From Authorized Sender: Yes.\n";
            $errors->{msg_not_from_list_owner} = 0;
            $errors->{msg_not_from_subscriber} = 0;
            if ( $ls->param('authorized_sending_no_moderation') ) {
                $errors->{needs_moderation} = 0;
                $r .= "\t\t* Moderation is not required for Authorized Senders.\n";
            }
            elsif ( $errors->{needs_moderation} == 1 ) {
                $r .= "\t\t* Moderation required.\n";
            }
        }
        else {
            $r .= "\t\t* From Authorized Sender: No.\n";
        }
    }
    else {
        $r .= "\t* Authorized Senders is disabled.\n";
    }

    if ( $ls->param('ignore_spam_messages') == 1 ) {
        $r .= "\t* SpamAssassin check is enabled.\n";

        if ( $ls->param('find_spam_assassin_score_by') eq 'calling_spamassassin_directly' ) {

            $r .= "\t* Loading SpamAssassin directly.\n";

            try {

                require Mail::SpamAssassin;

                if ( $Mail::SpamAssassin::VERSION <= 2.60 && $Mail::SpamAssassin::VERSION >= 2 ) {
                    require Mail::SpamAssassin::NoMailAudit;

                    # this needs to be optimized...
                    my $spam_check_message = $entity->as_string;
                    $spam_check_message = safely_decode($spam_check_message);

                    my @spam_check_message =
                      split( "\n", $spam_check_message );

                    my $mail = Mail::SpamAssassin::NoMailAudit->new( data => \@spam_check_message );

                    my $spamtest = Mail::SpamAssassin->new(
                        {local_tests_only => 1,
                            dont_copy_prefs  => 1,
                        }
                    );

                    my $score;
                    my $report;

                    if ($spamtest) {
                        my $spam_status;
                        $spam_status = $spamtest->check($mail);

                        if ($spam_status) {
                            $score  = $spam_status->get_hits();
                            $report = $spam_status->get_report();
                        }

                    }

                    if ( $score eq undef && $score != 0 ) {
                        $r .= "\t* Trouble parsing scoring information - letting message pass...\n";
                    }
                    else {

                        if ( $score >= $ls->param('ignore_spam_messages_with_status_of') ) {
                            $r .=
                                "\t*  Message has *failed* Spam Test (Score of: $score, "
                              . $ls->param('ignore_spam_messages_with_status_of')
                              . " needed.) - ignoring message.\n";
                            $errors->{message_seen_as_spam} = 1;

                            $r .= "\n" . $report;
                        }
                        else {
                            $errors->{message_seen_as_spam} = 0;

                            $r .=
                                "\t* Message passed! Spam Test (Score of: $score, "
                              . $ls->param('ignore_spam_messages_with_status_of')
                              . " needed.)\n";
                        }

                    }

                    undef $mail;
                    undef $spamtest;
                    undef $score;
                    undef $report;

                }
                elsif ( $Mail::SpamAssassin::VERSION >= 3 ) {

                    my $spam_check_message = $entity->as_string;
                    my $spamtest           = Mail::SpamAssassin->new(
                        {

                            local_tests_only => 1,
                            dont_copy_prefs  => 1,

                            # userstate_dir    => '/home/hhbc/private/',
                        }
                    );
                    my $mail        = $spamtest->parse($spam_check_message);
                    my $spam_status = $spamtest->check($mail);

                    my $score  = $spam_status->get_score();
                    my $report = $spam_status->get_report();

                    if ( $score eq undef && $score != 0 ) {
                        $r .= "\t* Trouble parsing scoring information - letting message pass...\n";
                    }
                    else {

                        if ( ( $score >= $ls->param('ignore_spam_messages_with_status_of') )
                            || $spam_status->is_spam() )
                        {
                            $r .=
                                "\t* Message has *failed* Spam Test (Score of: $score, "
                              . $ls->param('ignore_spam_messages_with_status_of')
                              . " needed.) - ignoring message.\n";

                            $errors->{message_seen_as_spam} = 1;

                            $r .= "\n" . $report;
                        }
                        else {
                            $errors->{message_seen_as_spam} = 0;

                            $r .=
                                "\t* Message passed! Spam Test (Score of: $score, "
                              . $ls->param('ignore_spam_messages_with_status_of')
                              . " needed.)\n";
                        }
                    }

                    $spam_status->finish;
                    $mail->finish;
                    $spamtest->finish;
                    undef $score;
                    undef $report;
                }
                else {
                    $r .=
"\t* SpamAssassin 2.x and 3.x are currently supported, you have version $Mail::SpamAssassin::VERSION, skipping test\n";
                }
            
			} catch { 
                $r .= "\t* SpamAssassin doesn't seem to be available. Skipping test - option should be disabled!\n";
			};
        }
        elsif ( $ls->param('find_spam_assassin_score_by') eq 'looking_for_embedded_headers' ) {

            $r .= "\t* Looking for embedding SpamAssassin Headers...\n";

            my $score = undef;
            if ( $entity->head->count('X-Spam-Status') ) {

                my @x_spam_status_fields =
                  split( ' ', $entity->head->get( 'X-Spam-Status', 0 ) );
                for (@x_spam_status_fields) {
                    if ( $_ =~ m/score\=/ ) {
                        $score = $_;
                        $score =~ s/score\=//;

                        $r .= "\t* Found them...\n";
                        last;

                    }
                }
            }

            if ( $score eq undef && $score != 0 ) {
                $r .= "\t* Trouble parsing scoring information - letting message pass...\n";
            }
            else {

                if ( $score >= $ls->param('ignore_spam_messages_with_status_of') ) {
                    $r .=
                        "\t*  Message has *failed* Spam Test (Score of: $score, "
                      . $ls->param('ignore_spam_messages_with_status_of')
                      . " needed.) - ignoring message.\n";

                    $errors->{message_seen_as_spam} = 1;

                    if ($verbose) {
                        my @x_spam_report = $entity->head->get('X-Spam-Report');
                        $r .= "\n\t";
                        $r .= "$_\n" for @x_spam_report;
                    }

                }
                else {
                    $errors->{message_seen_as_spam} = 0;

                    $r .=
                        "\t*  Message passed! Spam Test (Score of: $score, "
                      . $ls->param('ignore_spam_messages_with_status_of')
                      . " needed.)\n";
                }

            }
        }
        else {
            $r .= "\t* Don't know how to find the SpamAssassin score, sorry!\n";
        }

    }
    else {
        $r .= "\t* SpamAssassin check is disabled.\n";
    }
	
	if($dont_test_age_of_messages != 1) {
		# Message too old test
		my ($too_old_check, $too_old_r) = test_msg_is_too_old($entity);
		if($too_old_check == 1){ 
			$status = 0; 
			$errors->{message_too_old} = 1; 
			 $r .= "\t* Message is more than 7 days old, will not process/send\n";
		}	
		#/Message too old test 
	}
	
    $r .= "\n";

    # This below probably can't happen anymore...
    if ( lc_email( $ls->param('discussion_pop_email') ) eq lc_email($from_address) ) {
        $errors->{msg_from_list_address} = 1;
        $r .= "\t* *WARNING!* Message is from the List Address. That's bad.\n";
    }

    for ( keys %$errors ) {
        if ( $errors->{$_} == 1 ) {
            $status = 0;
            last;
        }
    }
 
    return ( $status, $errors, $r );
}

sub test_msg_is_too_old {
	 
    my $entity = shift;
	my $r = ''; 
	my $date = $entity->head->get( 'Date', 0);
	#chomp($date);
	
	$r .= "Date: '$date'\n";
	
    if ( $date ) { 
    	
		$r .= "Found a date!\n";
		
		require Date::Parse;
		my $t = Date::Parse::str2time($date);
		
		$r .= "t '" . $t . "'\n";
		
		# Is today's date - 7 days less than or equal to the date received?
		# If so, the message is too old.
		if((int(time) - 604_800) <= int($t)){ 
			$r .= "too young to fail\n";
			return (0, $r);
		}
		else { 
			$r .= "yup, Too old!";
			return (1, $r);
		}
    }
    else {
		# No Date found... WTF?
		# I wanna return "1" for this, but something tells me 
		# there's a weird edge case, and this will break 
		# for SOMEONE out there...
		$r .= "Couldnt find a date!";
        return (0, $r);
    }
	
}


sub test_Check_List_Owner_Return_Path_Header {

    my $ls     = shift;
    my $entity = shift;
    my $errors = shift;
    my $notice = '';

    require Email::Address;

    # This has been copied from the main thingy,
    my $rough_from = $entity->head->get( 'From', 0 );

    #$notice .= '$rough_from: ' . $rough_from;
    my $from_address = '';

    if ( defined($rough_from) ) {

	    try { 
			$from_address = ( Email_Address_parse($rough_from) )[0]->address;
		} catch { 
	        $notice .= '\t\tWarning! Something\'s wrong with the From address - ' . $_
	    };
    
		$from_address = lc_email($from_address);

		# $notice .= '$from_address:  ' . $from_address;

		# /This has been copied from the main thingy,
	}
	
    my $rough_return_path = undef;

    if ( $entity->head->get( 'Return-Path', 0 ) ) {

        # I haven't a clue what this is.
        # $notice .= q{$entity->head->get( 'Return-Path', 0 ) } . $entity->head->get( 'Return-Path', 0 );
        $rough_return_path = $entity->head->get( 'Return-Path', 0 );
    }
    else {

        # Strange, but there is no return-path... why?
        $notice .= "\t\t * No Return Path Found - Skipping Test\n";
        $errors->{list_owner_return_path_set_funny} = 0;
        return ( $errors, $notice );
    }

    my $return_path_address = '';

    if ( defined($rough_return_path) ) {

        try { 
            $return_path_address = ( Email_Address_parse($rough_return_path) )[0]->address; 
        }
        catch { 
            warn $_; 
        }
    }
    $return_path_address = lc_email($return_path_address);

    if ( lc_email($from_address) eq lc_email($return_path_address) ) {

        $errors->{list_owner_return_path_set_funny} = 0;

        $notice .=
          "\t\t * Address set in, From: header, ($from_address) matches, Return-Path address ($return_path_address)\n";

    }
    else {

        $notice .=
"\t\t * Address set in, From: header, ($from_address) doesn't match, Return-Path address ($return_path_address) ? Why?\n";
        warn
"\t\t * Address set in, From: header, ($from_address) doesn't match, Return-Path address ($return_path_address) ? Why?\n";

        if ( lc_email($return_path_address) eq lc_email( $ls->param('admin_email') ) ) {

            $notice .= "\t\tAh! Ok, The Return-Path is set to the list administrator - I guess that's ok....";
            warn "\t\tAh! Ok, The Return-Path is set to the list administrator - I guess that's ok....";

            $errors->{list_owner_return_path_set_funny} = 0;

        }
        else {

            $errors->{list_owner_return_path_set_funny} = 1;

        }
    }

    return ( $errors, $notice );

    #return ({list_owner_return_path_set_funny => 1}, "This here is my notice.");

}

sub send_msg_too_big {

    my $ls           = shift;
    my $full_msg_ref = shift;
    my $size         = shift;
    my $entity;

    try {
        $entity = $parser->parse_data(safely_encode($$full_msg_ref));
		
        if ( !$entity ) {
            warn "couldn't create a new entity in send_msg_too_big, passing.";
        }

        my $from_address = ( Email_Address_parse( $entity->head->get( 'From', 0 ) ) )[0]->address;
		my $size_of_original_message = human_readable_filesize($size);
		
		
        require DADA::App::FormatMessages;
        my $dfm = DADA::App::FormatMessages->new( -List => $ls->param('list') );

        my $subject = $entity->head->get( 'Subject', 0 );
           $subject =~ s/\n//g;
           $subject = $dfm->_decode_header($subject);
		
		require DADA::App::Messages;
	    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
		$dap->send_out_message(
			{ 
				-message => 'msg_too_big_msg', 
				-email   => $from_address,
		        -tmpl_params => {
		            -list_settings_vars_param => { -list => $ls->param('list'), -dot_it => 1, },
		            -subscriber_vars => { 'subscriber.email' => $from_address, },
		            -vars            => {
		                original_subject         => $subject,
		                size_of_original_message => $size_of_original_message,
		            }
		        }
			}
		);
	} catch {
        warn "Wasn't able to process message in send_msg_too_big: $_";
        return 0;
    };
	return 1; 
}

sub notify_of_delivery {

	# warn 'in notify_of_delivery'; 
    my ($args) = @_;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }

    my $test_mail = 0;
    if ( exists( $args->{-test_mail} ) ) {
        $test_mail = $args->{-test_mail};
    }

    my $ls = $args->{-ls};

    # $msg is a scalarref
    my $msg = $args->{-msg};

	my $nsr_msg = ${$msg}; 
    my $entity = $parser->parse_data(safely_encode($nsr_msg));

    my $rough_from = $entity->head->get( 'From', 0 );
    chomp($rough_from);
    my $from_address = undef;

    my $from_address = undef;
    if ( defined($rough_from) ) {
        try { 
			$from_address = ( Email_Address_parse($rough_from) )[0]->address; 
		} catch { 
			warn $_; 
		};
    }

	
    require DADA::App::FormatMessages;
    my $dfm = DADA::App::FormatMessages->new( -List => $ls->param('list') );

	my $original_subject = $entity->head->get( 'Subject', 0 );
	chomp($original_subject);
	$original_subject =~ s/\n//g;
	$original_subject = $dfm->_decode_header($original_subject);
	
	
	require DADA::App::EmailThemes; 
	my $em = DADA::App::EmailThemes->new(
		{ 
			-list      => $ls->param('list'),		
			
		}
	);
	my $etp = $em->fetch('msg_received_msg');
	
	require DADA::App::Messages;
    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
	
	$dap->send_multipart_email(
        {
            -headers => {
                To      => $rough_from,
                From    => $ls->param('list_owner_email'),
                Subject => $etp->{vars}->{subject},
            },
            -plaintext_body          => $etp->{plaintext},
            -html_body               => $etp->{html},

            -tmpl_params => {
                -list_settings_vars       => $ls->params,
                -list_settings_vars_param => { -dot_it => 1, },
                -subscriber_vars          => { 'subscriber.email' => $from_address, },
                -vars                     => {
                    original_subject => $original_subject,
                }
            },
        }
    );
}

sub process {

    my ($args) = @_;

    my $r;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }

    my $test_mail = 0;
    if ( exists( $args->{-test_mail} ) ) {
        $test_mail = $args->{-test_mail};
    }

    my $ls = $args->{-ls};

    # $msg is a scalarref
    my $msg = $args->{-msg};

    $r .= "\t* Processing Message...\n";

    my ( $n_msg, $dm_format_r ) = dm_format(
        {
            -ls  => $ls,
            -msg => $msg,    #scalarref
        }
    );
    $r .= $dm_format_r;
	
	
	my $msg_size_check = 1; 
	my $msg_size_r     = undef; 
	
	if($ls->param('email_limit_message_size') == 1) {
		$r .= "\t\t* Message Size check enabled. Limit: " . $ls->param('email_message_size_limit') . "M\n";
		my ($ms_status, $ms_filesize) = message_size_check(
			{ 
				-msg    => $n_msg, 
				-ls_obj => $ls, 
			}
		);
	
		if($ms_status == 0){ 
			$msg_size_check = 0; 
			my $diff = $ms_filesize - (int($ls->param('email_message_size_limit')) * 1_048_576); 		
			$msg_size_r = "\n"
				. "\t\t* "
				. 'Message size: ' . human_readable_filesize($ms_filesize)
				. ', is larger than the limit set of ' 
				. $ls->param('email_message_size_limit')
				. 'M'
				. ' by: '
				. human_readable_filesize($diff)
				. "\n"
			;		
		}
	}
	
	if($msg_size_check == 0){ 
		$r .= $msg_size_r; 
	    $r .= "\t* Message NOT being delivered.  \n";
	}
	else {	
		
        if ( $ls->param('send_received_msg') == 1 ) {
			# warn 'notify_of_delivery'; 
            notify_of_delivery(
                {
                    -ls        => $ls,
                    -msg       => $msg,
                    -test_mail => $test_mail,
                }
            );
        }
	
	    if ( $ls->param('send_msgs_to_list') == 1 ) {
	        $r .= "\t* Message being delivered! \n";

	        my ( $msg_id, $saved_message, $d_r ) = deliver(
	            {
	                -ls        => $ls,
	                -msg       => $n_msg,
	                -test_mail => $test_mail,
	            }
	        );
	        $r .= $d_r;
	        archive(
	            {
	                -ls        => $ls,
	                -msg       => $n_msg,
	                -msg_id    => $msg_id,
	                -saved_msg => $saved_message
	            }
	        );
	    }
		else { 
			 $r .= "\t* NOTE: Message is NOT being sent to Subscribers.\n";
		}
	
	    if (   $ls->param('send_msg_copy_to')
	        && $ls->param('send_msg_copy_address') )
	    {
	        $r .= "\t* Sending a copy of the message to: " . $ls->param('send_msg_copy_address') . "\n";

	        deliver_copy(
	            {
	                -ls  => $ls,
	                -msg => $msg,
	            }
	        );
	    }
	}

    $r .= "\t* Finished Processing Message.\n\n";

    return $r;

}

sub dm_format {

    my ($args) = @_;
    my $r;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }

    my $ls  = $args->{-ls};
    my $msg = $args->{-msg};    # scalarref

    if ( $ls->param('strip_file_attachments') == 1 ) {
        my ($sfa_r) = undef;
        ( $msg, $sfa_r ) = strip_file_attachments( $msg, $ls );
        $r .= $sfa_r;
    }

    require DADA::App::FormatMessages;

    my $fm = DADA::App::FormatMessages->new( -List => $ls->param('list') );
    $fm->mass_mailing(1);

    if (   $ls->param('group_list') == 0
        && $ls->param('rewrite_anounce_from_header') == 1 )
    {
        $fm->reset_from_header(1);
    }

#	warn '${$msg} ' .  ${$msg}; 

    my ( $header_str, $body_str ) = $fm->format_headers_and_body(
        {	
			-msg             => ${$msg},
	        -convert_charset => 1,
			-format_mlm      => 1, 
		}
	);
#	warn '$header_str' . $header_str; 
#	warn '$body_str'   . $body_str; 
  
    # not a scalarref (duh)
    my $all_together = $header_str . "\n\n" . $body_str;
    return ( $all_together, $r );

}

sub strip_file_attachments {

    my $msg = shift;    #ref
    my $ls  = shift;
    my $r;

    my $entity = undef;

    try { 
		$entity = $parser->parse_data(
			safely_encode( 
				${$msg} 
			) 
		);
	} catch { 
		warn $_; 
	};
    if ( !$entity ) {
        die "no entity found! die'ing!";
    }

    $r .= "\t* Stripping banned file attachments...\n\n";

    ( $entity, $ls ) = process_stripping_file_attachments( $entity, $ls );

    my $un = $entity->as_string;
    $un = safely_decode($un);
    return ( \$un, $r );
}



sub process_stripping_file_attachments {

    my $entity = shift;
    my $ls     = shift;

    my @att_bl = split( ' ', $ls->param('file_attachments_to_strip') );
    my $lt = {};

    for (@att_bl) {
        $lt->{ lc($_) } = 1;
    }

    my @parts = $entity->parts;

    if (@parts) {

        # multipart...
        my $i;
        for $i ( 0 .. $#parts ) {
            ( $parts[$i], $ls ) =
              process_stripping_file_attachments( $parts[$i], $ls );

        }

        my @new_parts;

        for $i ( 0 .. $#parts ) {
            if ( !$parts[$i] ) {

            }
            else {

                push( @new_parts, $parts[$i] );
            }
        }

        $entity->parts( \@new_parts );

        $entity->sync_headers(
            'Length'      => 'COMPUTE',
            'Nonstandard' => 'ERASE'
        );

        return ( $entity, $ls );

    }
    else {

        my $name = $entity->head->mime_attr("content-type.name")
          || $entity->head->mime_attr("content-disposition.filename");

        my $f_ending = $name;
        $f_ending =~ s/(.*)\.//g;

        if (
               $lt->{ lc( $entity->head->mime_type ) } == 1
            || $lt->{ lc($f_ending) } == 1
            || $lt->{ '.' . lc($f_ending) } == 1

          )
        {

            print "\t* Stripping attachment with:\n\t\t* name: $name \n\t\t* MIME-Type: "
              . $entity->head->mime_type . "\n";

            return ( undef, $ls );
        }

        $entity->sync_headers(
            'Length'      => 'COMPUTE',
            'Nonstandard' => 'ERASE'
        );
        return ( $entity, $ls );
    }

    return ( $entity, $ls );
}

sub deliver_copy {

    my $r;
    $r .= "\t* Delivering Copy...\n";

    my ($args) = @_;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }

    my $ls  = $args->{-ls};
    my $msg = $args->{-msg};

    my $test_mail = 0;
    if ( exists( $args->{-test_mail} ) ) {
        $test_mail = $args->{-test_mail};
    }

    my $mh = DADA::Mail::Send->new(
        {

            -list   => $ls->param('list'),
            -ls_obj => $ls,
        }
    );

    #warn "test_mail " . $test_mail;

    $mh->test($test_mail);

    my $entity = undef; 
    try { 
        
        $entity = $parser->parse_data(
            safely_encode(
                $msg
                )
            ) 
    } catch { 
		warn $_; 
	};

    if ( !$entity ) {
        $r .= "\t* Message sucks!\n";

    }
    else {

        my $headers = $entity->stringify_header;
           $headers = safely_decode($headers);

        my %headers = $mh->return_headers($headers);
          $headers{To} = $ls->param('send_msg_copy_address');

        $r .= "\t* Message Details:\n";
        $r .= "\t* Subject: " . $headers{Subject} . "\n";

        my $msg_id = $mh->send(

            %headers,

            # Trust me on these :)

            # These are here so the message doesn't cause an infinite loop BACK to the list -

            # These are *probably* optional,
            'Bcc' => '',
            'Cc'  => '',

            # This'll do the trick, all by itself.
            'X-BeenThere' => $ls->param('discussion_pop_email'),

            Body => safely_decode( $entity->stringify_body ),

        );

    }
    return $r;

}

sub deliver {

    my ($args) = @_;
    my $r;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }

    my $ls  = $args->{-ls};
    my $msg = $args->{-msg};

    my $test_mail = 0;
    if ( exists( $args->{-test_mail} ) ) {
        $test_mail = $args->{-test_mail};
    }

    my $mh = DADA::Mail::Send->new(
        {
            -list   => $ls->param('list'),
            -ls_obj => $ls,
        }
    );

    $mh->test($test_mail);
    $msg = safely_encode($msg);

    my $entity = undef;
    try { 
		$entity = $parser->parse_data(
			safely_encode(
				$msg
			)
		); 
	} catch { 
		warn $_; 
	};

    if ( !$entity ) {
        $r .= "\t* Message sucks!\n";
    }
    else {

        my $headers = $entity->stringify_header;
        $headers = safely_decode($headers);
        my %headers = $mh->return_headers($headers);
		
		# Fwaaaaah? 
        $headers{To} = $ls->param('list_owner_email');


		#
		#
		#
		
        require DADA::App::FormatMessages;
        my $dfm = DADA::App::FormatMessages->new( -List => $ls->param('list') );
		
		
		require DADA::App::EmailThemes;
	    my $em = DADA::App::EmailThemes->new(
	        {
	            -list  => $ls->param('list'),
				-cache => 1, 
	        }
	    );
			
		my $etp = $em->fetch(
			$dfm->layout_choice()
		);
		
		$headers{To} = $dfm->format_phrase_address(
                    $etp->{vars}->{to_phrase},
                    $ls->param('list_owner_email')
                );
		
		#
		#
		#
		

        $r .= "\t* Message Details: \n";
        $r .= "\t* Subject: " . $headers{Subject} . "\n";

        if (   $ls->param('group_list') == 1
            && $ls->param('mail_discussion_message_to_poster') != 1 )
        {

            my $f_a;

            if ( exists( $headers{From} ) ) {

                try { 
					$f_a = ( Email_Address_parse( $headers{From} ) )[0]->address; 
					
	                $r .= "\t* Going to skip sending original poster ($f_a) a copy of their own  message...\n";
	                $mh->do_not_send_to( [$f_a] );
					
				} catch { 
					$r .= "\t* Problems not sending copy to original sender: $_\n\n";
				};
            }
        }
		
        my $msg_id = $mh->mass_send(
            {            
                -msg => {
					%headers, 
					Body => safely_decode(
						$entity->stringify_body
					)
				},
            }
        );


		# warn '$r:' . $r; 

        return ( $msg_id, $mh->saved_message, $r );

    }

}

sub archive {

    my ($args) = @_;

    if ( !exists( $args->{-ls} ) ) {
        die "You must pass a -ls parameter!";
    }
    if ( !exists( $args->{-msg} ) ) {
        die "You must pass a -msg parameter!";
    }
    if ( !exists( $args->{-msg_id} ) ) {
        die "You must pass a -msg_id parameter!";
    }
    if ( !exists( $args->{-saved_msg} ) ) {
        die "You must pass a -saved_msg parameter!";
    }

    my $ls        = $args->{-ls};
    my $msg       = $args->{-msg};
    my $msg_id    = $args->{-msg_id};
    my $saved_msg = $args->{-saved_msg};

    if ( $ls->param('archive_messages') == 1 ) {

        require DADA::MailingList::Archives;

        # I'm having trouble with the db handle die'ing after we've forked a mailing.
        # I wonder if telling Mr. Archives here to create  new connection will help things...

        my $la =
          DADA::MailingList::Archives->new( { -list => $ls->param('list') } );

        my $entity = undef;

        try {
            $msg    = safely_encode($msg);
            $entity = $parser->parse_data(
				safely_encode(
					$msg
					)
			);
        } catch {
			warn $_; 
        };

        if ($entity) {

            my $Subject = $entity->head->get( 'Subject', 0 );
            if ( $ls->param('no_prefix_list_name_to_subject_in_archives') == 1 ) {
                $Subject = $la->strip_subjects_appended_list_name($Subject);
            }

            try {
                $la->set_archive_info( $msg_id, $Subject, undef, undef, $saved_msg, );
            } catch {
                warn "$DADA::Config::PROGRAM_NAME $DADA::Config::VER warning! message did not archive correctly!: $_";
            };
        }
        else {
            warn "Problem archiving message...";
        }

    }
}

sub send_msg_not_from_subscriber {

    my $ls  = shift;
    my $msg = shift;

    $msg = safely_encode($msg);
    my $entity = $parser->parse_data($msg); # done above
    my $original_subject = $entity->head->get( 'Subject', 0 );
   
   
    my $rough_from = $entity->head->get( 'From', 0 );
    my $from_address    = undef;
	my $is_a_subscriber = undef;
    if ( defined($rough_from) ) {
        require DADA::MailingList::Subscribers; 
		my $lh = DADA::MailingList::Subscribers->new( { -list => $ls->param('list') } );
		
		
        try { 
			
			$from_address    = ( Email_Address_parse($rough_from) )[0]->address; 
			my $member_of = $lh->member_of({
				-email => $from_address,
	            -types => ['list']
			}); 
			if(scalar(@$member_of) >= 1){ 
				if($member_of->[0] eq 'list'){ 
					$is_a_subscriber = 1;
				}
			}
		} catch { 
			warn $_; 
		};
    }

    if ( $from_address && $from_address ne '' ) {

        require DADA::MailingList::Settings;
        if ( $from_address eq $ls->param('discussion_pop_email') ) {
            warn
"Message is from List Email ($from_address)? Not sending, 'not_allowed_to_post_msg' so to not send message back to list!";
        }
        else {
			
			require DADA::App::Messages;
		    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
			$dap->send_out_message(
				{
					-message => 'not_allowed_to_post_msg',
					-email => $from_address, 
	            	-tmpl_params    => {
		                -list_settings_vars_param => { 
							-list => $ls->param('list'), 
						},
		                -subscriber_vars          => {
		                    'subscriber.email' => $from_address
		                },
		                -vars => {
		                     original_subject => $original_subject, 
							 is_a_subscriber  => $is_a_subscriber,
		                }
					}
	            }
			);
        }

    }
    else {
        warn "Problem with send_msg_not_from_subscriber! There's no address to send to?: " . $rough_from;
    }

}

sub send_spam_rejection_message {
    my $ls  = shift;
    my $msg = shift;

    $msg = safely_encode($msg);
    my $entity = $parser->parse_data($msg); #done above

    my $rough_from = $entity->head->get( 'From', 0 );
    my $from_address;
    if ( defined($rough_from) ) {
        ;
        try { 
			$from_address = ( Email_Address_parse($rough_from) )[0]->address; 
		} catch { 
			warn $_; 
		}; 
    }

    if ( $from_address && $from_address ne '' ) {

        require DADA::MailingList::Settings;
        my $ls =
          DADA::MailingList::Settings->new( { -list => $ls->param('list') } );


  
		require DADA::App::Messages;
	    my $dap = DADA::App::Messages->new(
			{
				-list => $ls->param('list'),
			}
		);
		$dap->send_out_message(
            {
				-message => 'msg_labeled_as_spam_msg',
				-email => $from_address, 
                -tmpl_params => {
                    -list_settings_vars       => $ls->params,
                    -list_settings_vars_param => { -dot_it => 1, },
                    -subscriber_vars          => { 'subscriber.email' => $from_address, },
                    -vars                     => {
                        original_subject => $entity->head->get( 'Subject', 0 ),
                    }
                },
            }
        );

    }
    else {
        warn "Problem with send_spam_rejection_message! There's no address to send to?: " . $rough_from;
    }
}

sub send_invalid_msgs_to_owner {

    my $ls  = shift;
    my $msg = shift;

    $msg = safely_encode($msg);
    my $entity = $parser->parse_data($msg); #done above
    my $rough_from = $entity->head->get( 'From', 0 );
    my $from_address;
    if ( defined($rough_from) ) {
        try { 
			$from_address = ( Email_Address_parse($rough_from) )[0]->address; 
		} catch { 
			warn $_; 
		};
    }

    if ( $from_address && $from_address ne '' ) {

		my $original_subject = $entity->head->get('Subject',0);
		my $is_a_subscriber = 0; 
		
        require DADA::MailingList::Subscribers; 
		my $lh = DADA::MailingList::Subscribers->new( { -list => $ls->param('list') } );
		my $member_of = $lh->member_of({
			-email => $from_address,
            -types => ['list']
		}); 
		if(scalar(@$member_of) >= 1){ 
			if($member_of->[0] eq 'list'){ 
				$is_a_subscriber = 1;
			}
		}
		
		require DADA::App::Messages;
	    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
		$dap->send_out_message(
			{ 
				-message => 'invalid_msgs_to_owner_msg', 
				-email   => $ls->param('list_owner_email'),
                -tmpl_params => {
                    -list_settings_vars       => $ls->params,
                    -list_settings_vars_param => { 
							-dot_it => 1,
					},
                    -subscriber_vars => { 
                        'subscriber.email' => $from_address, 
                    },
                    -vars => { 
                        original_subject => $original_subject, 
						is_a_subscriber  => $is_a_subscriber, 
                    },
                },
			}
		);
    }
    else {
        warn "Problem with send_invalid_msgs_to_owner!";
    }
}

sub handle_errors {

    my $ls       = shift;
    my $errors   = shift;
    my $full_msg = shift;
    my $r;
    my $entity;

    $full_msg = safely_encode($full_msg);

	unless(length($full_msg) > 0){ 
		$r = '$full_msg is blank - cannot handle_errors';
		warn $r; 
		return $r; 
	}
	
	
    try {
        $entity = $parser->parse_data(
            $full_msg
        ); # done above
    } catch { 
        $r .= "Problems Handling Error: $_\n"; 
#      warn 'length($full_msg):' . length($full_msg); 
#       warn '$full_msg' . $full_msg; 
#        warn 'reseting mime parser.'; 
        $parser          = new MIME::Parser;
        $parser          = optimize_mime_parser($parser);
        
#		warn $r; 
              
    };
    
    if ( !$entity ) {
        $r .= "No entity found!\n";
        return $r; 
    }
    else {  

        my $reasons = '';
        for ( keys %$errors ) {
            $reasons .= $_ . ', '
              if $errors->{$_} == 1;
        }
        my $subject = $entity->head->get( 'Subject', 0 );
        $subject =~ s/\n//g;
        my $from = $entity->head->get( 'From', 0 );
        $from =~ s/\n//g;

        # $from should probably be simply the email address, not the entire header...
        #
        try { 
			$from = ( Email_Address_parse($from) )[0]->address; 
		} catch {
            warn "this was a problem parsing the email address from the header? '$_'";
        };

        my $message_id = $entity->head->get( 'Message-Id', 0 );
        $message_id =~ s/\n//g;
        if ( !$message_id ) {

            require DADA::Security::Password;

            my ( $f_user, $f_domain ) = split( '@', $from );
            my $fake_message_id = '<'
              . DADA::App::Guts::message_id()
              . '.FAKE_MSG_ID'
              . DADA::Security::Password::generate_rand_string('1234567890') . '@'
              . $f_domain . '>';

            $message_id = $fake_message_id;
            $entity->head->replace( 'Message-ID', $fake_message_id );

            warn
    "bridge - message has no Message-Id header!...? Creating FAKE Message-Id ($fake_message_id) , to avoid any conflicts...";

        }

        warn "Bridge is rejecting sending out the received message:"
			. "\n\tList: " . $ls->param('list')
			. "\n\tFrom: $from"
			. "\n\tSubject: $subject"
			. "\n\tMessage-ID: $message_id"
			. "\n\tReasons: $reasons"
		;

        $r .= "\tReasons:\n";

        my %error_descriptions = (
            needs_moderation        => 'Message needs to be moderated',
            msg_not_from_subscriber => 'Message is not from a Subscriber',
            msg_not_from_list_owner => 'Message is not from the List Owner',
			message_too_old         => 'Message is too old to process',
        );

        for ( keys %$errors ) {
            if ( $errors->{$_} == 1 ) {
                if ( exists( $error_descriptions{$_} ) ) {
                    $r .= "\t\t* " . $error_descriptions{$_} . "\n";
                }
                else {
                    $r .= "\t\t" . $_ . "\n";
                }
            }
        }

        if ( $errors->{list_owner_return_path_set_funny} == 1 ) {
            $r .= "\t\t* list_owner_return_path_set_funny\n";
            # and I'm not going to do anything...
        }

        $r .= "\n";

        if ( $errors->{message_seen_as_spam} == 1 ) {

            if ( $ls->param('rejected_spam_messages') eq 'send_spam_rejection_message' ) {
                $r .= "\t\t* end_spam_rejection_message on its way!\n";
                send_spam_rejection_message( $ls, $full_msg );

            }
            elsif ( $ls->param('rejected_spam_messages') eq 'ignore_spam' ) {
                $r .= "\t\t *** Message seen as SPAM - ignoring. ***\n";
            }
            else {
                $r .= "\t\tlist_settings.rejected_spam_messages is setup impoperly - ignoring message!\n";
            }

        }
        elsif ( $errors->{multiple_return_path_headers} == 1 ) {

            $r .= "\t\t* Message has multiple 'Return-Path' headers. Ignoring. \n";
            warn "$DADA::Config::PROGRAM_NAME Error: Message has multiple 'Return-Path' headers. Ignoring.";

        }
        elsif ( $errors->{msg_from_list_address} ) {

            $r .= "\t\t* message was from the list address - will not process! - (ignoring) \n";
            warn "$DADA::Config::PROGRAM_NAME Error: message was from the list address - will not process! - (ignoring)";
        }
        elsif($errors->{message_too_old} == 1){
			
            $r .= "\t\t* Message is too old - will not process! - (ignoring)";
            warn "$DADA::Config::PROGRAM_NAME Message is too old - will not process! - (ignoring)";
        
		}
		elsif ($errors->{msg_not_from_subscriber} == 1
            || $errors->{msg_not_from_list_owner} == 1
            || $errors->{msg_not_from_an_authorized_sender} == 1 )
        {

            if ( $ls->param('send_not_allowed_to_post_msg') == 1 ) {

                $r .= "\t Sending out, 'Not Allowed to Post' email notification to poster\n";
                send_msg_not_from_subscriber( $ls, $full_msg );

            }

            if ( $ls->param('send_invalid_msgs_to_owner') == 1 ) {
                $r .= "\t Sending out, 'Not Allowed to Post' email notification to List Owner\n";
                send_invalid_msgs_to_owner( $ls, $full_msg );

            }

        }
        elsif ( $errors->{needs_moderation} ) {

            $r .= "Message being saved for moderation by List Owner.\n";

            my $mod = SimpleModeration->new( { -List => $ls->param('list') } );
			
			# why is $full_msg a scalar?
           my $ran_string = generate_rand_string_md5();
		   # I feel '-'s are mucking things up: 
			  $ran_string =~ s/\-|\%|\=/_/g;

		   $mod->save_msg( 
		   		{ 
					-msg    => $full_msg, 
					-msg_id => $ran_string 
				}
			);

            # This is only used once...
            $r .= $mod->moderation_msg(
                {
                    -msg    => $full_msg,
                    -msg_id => $ran_string,
                    -from   => $from,
                    -parser => $parser
                }
            );

            if ( $ls->param('send_moderation_msg') == 1 ) {
                $r .= "\t\t * Sending 'awaiting moderation' message!\n";
                $mod->send_moderation_msg(
                    {
                        -msg_id => $ran_string,
                        -parser => $parser,
                    }
                );
            }

            my $awaiting_msgs = $mod->awaiting_msgs();

            $r .= "\t* Other awaiting messages:\n";

            for (@$awaiting_msgs) {
                $r .= "\t\t * " . $_->{name} . ' => ' . $_->{date} ."\n";
            }
        }
		warn $r; 
        return $r;
    }
}

sub create_checksum {

    my $data = shift;

    if ( $] >= 5.008 ) {
        require Encode;
        my $cs = md5_hex( safely_encode($$data) );
        return $cs;
    }
    else {
        my $cs = md5_hex($$data);
        return $cs;
    }
}

sub can_use_spam_assassin {

    try { 
		require Mail::SpamAssassin; 
	} catch {
        return 0;
    }
	return 1; 

}

sub append_message_to_file {

    my $ls  = shift;
    my $msg = shift;
    my $rp  = find_return_path($msg);
    my $r;

    my $file = $DADA::Config::TMP . '/bridge_received_msgs-' . $ls->param('list') . '.mbox';

    $r .= "\n";
    $r .= "Saving message at: '$file' \n";

    $file = DADA::App::Guts::make_safer($file);

    open( APPENDLOG, '>>:encoding(' . $DADA::Config::HTML_CHARSET . ')', $file )
      or die $!;
    chmod( $DADA::Config::FILE_CHMOD, $file );
    print APPENDLOG 'From ' . $rp . "\n";
    print APPENDLOG $msg . "\n";
    close(APPENDLOG) or die $!;

    $r .= "Saved. \n";
    return ( 1, $r );

}


sub send_bridge_internal_problem_to_list_owner { 

    my $ls     = shift;
    my $errors = shift; 
	my $msg    = shift;
	
	try {
	    require DADA::App::FormatMessages;
	    my $dfm = DADA::App::FormatMessages->new( -List => $ls->param('list') );

		require DADA::App::EmailThemes; 
		my $em = DADA::App::EmailThemes->new(
			{ 
				-list      => $ls->param('list'),		
			
			}
		);
		my $etp = $em->fetch('bridge_internal_problem_to_list_owner');
	
		require DADA::App::Messages;
	    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
	
		my $tmp_dir = $DADA::Config::TMP . '/_mime_cache'; 
		create_dir($tmp_dir); 
		my $attachment_filename = 'recieved_message.eml';
		my $nifp = new_image_file_path($attachment_filename, $tmp_dir); 
		simple_printout_file($nifp, \$msg);
			
		$dap->send_multipart_email(
	        {
	            -headers => {
	                To      => $ls->param('list_owner_email'),
	                From    => $ls->param('list_owner_email'),
	                Subject => $etp->{vars}->{subject},
	            },
	            -plaintext_body          => $etp->{plaintext},
	            -html_body               => $etp->{html},

	            -tmpl_params => {
	                -list_settings_vars       => $ls->params,
	                -list_settings_vars_param => { -dot_it => 1, },
	                -vars                     => {
	                   errors => $errors, 
	                }
	            },
				-attachments => [{
					# We won't use path and filename for anything, as that trips 
					# a mail filter - .eml files seem to not be all that OK. 
					-type         => 'message/rfc822',
					-path         => $nifp, 
					-filename     => $attachment_filename,
				}],
	        }
	    );
	} catch { 
		warn $_; 
	};
}




sub find_return_path {

    my $msg = shift;
    my $rp;

    try {

        $msg = safely_encode($msg);
        my $entity = $parser->parse_data($msg); # done above
        $rp = $entity->head->get( 'Return-Path', 0 );
		$entity->purge; 
    } catch { 
		warn $_; 
		return undef;
	};
    chomp $rp;
    return $rp;
}

sub cgi_show_plugin_config {
    my $q = shift; 
    my $configs = [];
    for ( sort keys %$Plugin_Config ) {
        if ( $_ eq 'Password' ) {
            push( @$configs, { name => $_, value => '(Not Shown)' } );
        }
        else {
            push( @$configs, { name => $_, value => $Plugin_Config->{$_} } );
        }
    }

    require DADA::Template::Widgets;
    my $scrn = DADA::Template::Widgets::wrap_screen(
        {
            -screen         => 'plugins/shared/plugin_config.tmpl',
            -with           => 'admin',
            -wrapper_params => {
                -Root_Login => $root_login,
                -List       => $list,
            },
            -vars => {
                screen      => 'using_bridge',
                Plugin_Name => $Plugin_Config->{Plugin_Name},
                configs     => $configs,
            },
        },
    );
    return({}, $scrn);

}



sub inject {


	#
	# init args: 
    my ($args) = @_;
	
	my $msg       = undef; 
	my $send_test = 0;
    my $test_mail = 0;
	
	if ( exists($args->{-msg}) ) { 
		# Why are we, safely_decode()ing this? 
		$msg = safely_decode($args->{-msg});	
	}
	if( exists( $args->{-send_test} ) ) {
        $send_test = $args->{-send_test};
    }
    if ( exists( $args->{-verbose} ) ) {
        $verbose = $args->{-verbose};
    }
    if ( exists( $args->{-test_mail} ) ) {
        $test_mail = $args->{-test_mail};
    }
    my $ls;
    if ( exists( $args->{-ls} ) ) {
        $ls = $args->{-ls};
    }
    else {
        require DADA::MailingList::Settings;
        $ls = DADA::MailingList::Settings->new( { -list => $ls->param('list') } );
    }
	
	# /init args 
	#
	
	#
	# init return log string
	my $r   = '';
	
	# Sanity checks - should we even be looking at the message? 
    
	if ( $ls->param('disable_discussion_sending') == 1 ) {
        $r .= "\tThis sending method has been disabled for " . $ls->param('list') . ", ignoring message... \n";
        return ( 0, { disabled => 1 }, $r );
	}
	
	
	if(! defined($msg)){ 
		return ( 0, { blank_message => 1 }, $r );
	}
	if(length($msg) == 0){ 
		return ( 0, { blank_message => 1 }, $r );
	}
	

	
    if ( $ls->param('bridge_list_email_type') ne "mail_forward_pipe" ) {
        $r .= "\t* List Email is NOT set up as a Email Forward to Pipe to Bridge \n";
        return ( 0, { bridge_list_email_type_ne_mail_forward_pipe => 1 }, $r );
    }
	
    if ( 
		length($ls->param('discussion_pop_email')) <= 0
		|| 	check_for_valid_email($ls->param('discussion_pop_email')) == 1
	) {
        $r .= "\t* ***Warning!*** Misconfiguration of plugin! List Email has not been set to a valid email address!\n\t\tSkipping $list...";
		return ( 0, { list_email_invalid => 1 }, $r );
    }
	
    if ( 
		cased($ls->param('discussion_pop_email')) eq 
		cased($ls->param('list_owner_email'))
	) {
        $r .= "\t\t***Warning!*** Misconfiguration of plugin! The List Owner email cannot be the same address as the list email address!\n\t\tSkipping $list...\n";
		return ( 0, { list_owner_same_as_list_email => 1 }, $r );
    }
	#/ Sanity checks
	#

	#
	#
	# This is just to change the Content-Transfer-Encoding to 'quoted-printable' 
	# (or whatever it is set in the preferences)
	# Why is this done? 
	# Unicode strings and (especially) the MIME parser don't play well, and a wrongly 
	# encoded message - either received or because of something we did 
	# can cause the app to die unexpectantly - which means the message does not get delivered
	# Changing to quoted-printable means we're only dealing with ASCII strings (hopefully!)
	# which are easily digested by the MIME Parser, this plugin, and Perl. 
	# 
	#
	
	require DADA::App::FormatMessages;
	my $dfm    = DADA::App::FormatMessages->new(
		-List => $ls->param('list') 
	);
	my $entity = undef; 
   
	try { 
	    $entity = $parser->parse_data(
			safely_encode(
				$msg
			)
		); 
		if($entity){ 	
			
			#
			# This is very important - most likely we're changing to 
			# quoted-printable, which means any unicode strings in the 
			# message will be turned to ASCII, making things much easier for the 
			# plugin to deal with, without hitting encoding issues. 
			# 
			$entity = $dfm->change_content_transfer_encoding(
		   		{
					-entity => $entity
				}
			); 
			#
			# I hate that I then just turn this back into a string! 
			# Why not pass an HTML::Entity obj around? 
			#
	       $msg    = safely_decode(
		   		$entity->as_string
		 	); 
		   $entity->purge;
	       undef $entity; 
		}
		else { 
			die 'entity is undefined? Was a valid email message passed?';
		}
    } catch { 
        warn 'Couldn\'t change Content-Transfer-Encoding: ' . $_; 
    };
	#
	# end Content-Transfer-Encoding 
	# 
	
	#	  
	# Sanity checks are done, but we still have to validate the message itself: 
	#
    my ( $status, $errors );

    my $status;
    my $errors;
    my $report;
     
    try {
        ( $status, $errors, $report ) = validate_msg( $ls, \$msg );
    } catch { 
        warn 'couldn\'t validate_msg?!'; 
        return (0, {}, 'couldn\'t validate_msg?!'); 
    };
    
	try { 
		
        if ($status == 1) {
            $r .= process(
                {
                    -ls        => $ls,
                    -msg       => \$msg,
                    -test_mail => $test_mail,
                }
            );

            my ( $amtf_status, $amtf_r ) = append_message_to_file( $ls, $msg );
            $r .= $amtf_r;
            return ( $status, $errors, $r );

        }
        else {

            $r .= "\tMessage did not pass verification.\n";
            $r .= handle_errors( $ls, $errors, $msg );
            
            append_message_to_file( $ls, $msg );

            return ( $status, $errors, $r );

        }

    } catch { 

        cluck "bridge - irrecoverable error processing message. Skipping message: $_";
        # Then, we have to tell the og sender, something went wrong: 
        $r .= "bridge - irrecoverable error processing message. Skipping message: $_\n";
		
		if($ls->param('bridge_send_internal_problem_to_list_owner') == 1){
			$r .= "Sending a copy of the message to the List Owner\n";
			send_bridge_internal_problem_to_list_owner($ls, $_, $msg); 
		}
		
        return ( 0, { irrecoverable_error => 1 }, $r );

    }; 
    return ( $status, $errors, $r );

}

sub cgi_inject { 

 my $q = shift; 
 my $fn = $q->param('fn') || die "no filename passed";
 $verbose  = 1;
 my $r; 
 
 
$r = inject_msg(
 	
 { 
	 -filename => $fn, 
	 -list     => $list, 
 }); 
 
 
 return ( {}, $r );
  
}

sub message_size_check { 

	my ($args) = @_; 
	
	my $msg    = $args->{-msg};
	my $ls     = $args->{-ls_obj};
	 
	
	my $tmp_file = make_safer( 
		$DADA::Config::TMP 
		. '/'
		. 'message_size_check-' . generate_rand_string_md5()
		. '.tmp'
	);
		
    open( OUTFILE, '>', $tmp_file ) or die( "can't write to " . $tmp_file . ": $!" );
	print OUTFILE $msg; 
	close(OUTFILE) or die $!;
	
	my $size = (stat $tmp_file)[7];
	
	unlink($tmp_file); 
	
	if($size > (int($ls->param('email_message_size_limit')) * 1_048_576)) {
	    return (0, $size);
	}
	else { 
		return (1, $size); 
	}
}




END {

    if ( defined($parser) ) {
        $parser->filer->purge;
    }
		
	if(defined($tmp_msg_file)){
		if(-e $tmp_msg_file){ 
			if($rm_tmp_msg_file == 1){
				my $n   = unlink($tmp_msg_file);
				if ( $n != 1 ) {
				    warn "could not remove tmpfile at, $tmp_msg_file";
			 	}
				else { 
					warn 'removing tmp msg file, ' . $tmp_msg_file; 
				}
			}
			else { 
				warn 'Problems processing incoming message? tmp msg file saved at, ' . $tmp_msg_file;
			}
		}
		else { 
			# Forked process may be running END, so if that happens, it's OK. 
			warn 'tmp msg file already removed, ' . $tmp_msg_file; 
		}
	}
}

sub DESTROY {}
    
package SimpleModeration;

use strict;

use DADA::Config qw(!:DEFAULT);
use DADA::App::Guts;
use MIME::Entity;
use Try::Tiny; 

sub new {
    my $class = shift;
    my $self  = {@_};
    bless $self, $class;

    my ($args) = @_;

    if ( !$args->{-List} ) {
        warn "You need to supply a list ->new({-List => your_list}) in the constructor.";
        return undef;
    }
    else {

        $self->{list} = $args->{-List};
    }

    $self->init;

    return $self;

}

sub init {

    my $self = shift;
    $self->check_moderation_dir();

}

sub check_moderation_dir {

    # Basically, just makes the tmp directory that we need...

    my $self = shift;
    if ( -d $self->mod_dir ) {

        # Well, ok!
    }
    else {

        die "$DADA::Config::PROGRAM_NAME $DADA::Config::VER warning! Could not create, '" . $self->mod_dir . "'- $!"
          unless mkdir( $self->mod_dir, $DADA::Config::DIR_CHMOD );

        chmod( $DADA::Config::DIR_CHMOD, $self->mod_dir )
          if -d $self->mod_dir;
    }
}

sub num_awaiting_msgs { 
	my $self = shift; 
	return scalar @{$self->awaiting_msgs};
}

sub awaiting_msgs {

    my $self    = shift;
    my $pattern = quotemeta( $self->{list} . '-' );
    my @awaiting_msgs;
    my %allfiles;

    my $f;
    if ( opendir( MOD_MSGS, $self->mod_dir ) ) {
        while ( defined( $f = readdir MOD_MSGS ) ) {
            next if $f =~ /^\.\.?$/;
            $f =~ s(^.*/)();
            next unless $f =~ m/^$pattern/;
            my $name = $f;
            $f =~ s/^$pattern//;
            $allfiles{$f} = ( stat( $self->mod_dir . '/' . $name ) )[9];
        }

        closedir(MOD_MSGS)
          or warn "couldn't close: " . $self->mod_dir;

		  # we did this song and dance to sort keys based on the date of the file: 
        foreach my $key (sort { $allfiles{$b} <=> $allfiles{$a} } keys %allfiles) {
            push( @awaiting_msgs, {name => $key, date => $allfiles{$key}} );
        }

    }
    else {
        warn "could not open " . $self->mod_dir . " $!";
    }

    return [@awaiting_msgs];

}

sub save_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg} ) {
        die "You must supply a message!";
    }

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }

    my $file = $self->mod_msg_filename( $args->{-msg_id} );

    open my $MSG_FILE, '>:encoding(' . $DADA::Config::HTML_CHARSET . ')', $file
      or die "Cannot write saved raw message at: '" . $file . " because: $!";

    print $MSG_FILE $args->{-msg};

    close($MSG_FILE)
      or die "Coulnd't close: " . $file . "because: " . $!;

}

sub moderation_msg {

	# This needs to be rewritten to be a honest mass email, with a tmp list. 
    my $self = shift;
    my ($args) = @_;
	
    my $r; 

    if ( !$args->{-msg} ) {
        die "You must supply a message!";
    }

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }
    $args->{-msg_id} =~ s/\@/_at_/g;
    $args->{-msg_id} =~ s/\>|\<//g;
    $args->{-msg_id} = DADA::App::Guts::strip( $args->{-msg_id} );

    if ( !$args->{-from} ) {
        die "You must supply a from!";
    }

    if ( !$args->{-parser} ) {
        die "You must supply a parser!";
    }

    require DADA::MailingList::Settings;
    my $ls = DADA::MailingList::Settings->new( { -list => $self->{list} } );

    my $parser = $args->{-parser};
    my $entity =
      $parser->parse_data( DADA::App::Guts::safely_encode( $args->{-msg} ) );

    require DADA::App::FormatMessages;
    my $dfm = DADA::App::FormatMessages->new( -List => $self->{list} );

    my $subject = $entity->head->get( 'Subject', 0 );
    $subject =~ s/\n//g;
    $subject = $dfm->_decode_header($subject);

    #  create an array of recepients
    my @moderators;
    if ( $ls->param('moderate_discussion_lists_with') eq 'moderators' ) {
        my $lh =
          DADA::MailingList::Subscribers->new( { -list => $self->{list} } );
        my $moderators = [];
        $moderators = $lh->subscription_list( { -type => 'moderators' } );
        for my $moderator (@$moderators) {

            if ( $moderator->{email} eq $args->{-from} ) {

                # Well, we'll just pass that one right by...
                # I don't think we want an moderator to
                # be able to moderate their own message!
            }
            else {
                push( @moderators, $moderator->{email} );
            }
        }
        $r .= "\tMessage being sent to Moderators and List Owner for moderation.\n";
        
    }
    else {
        $r .= "\tMessage being sent to List Owner for moderation.\n";
    }
    push( @moderators, $ls->param('list_owner_email') );    # always addressed

	require DADA::App::EmailThemes; 
	my $em = DADA::App::EmailThemes->new(
		{ 
			-list      => $self->{list},
			-cache     => 1,
		}
	);
	my $etp = $em->fetch('moderation_msg');
	

	

	for my $to_address (@moderators) {                      # recepient loop
		
		sleep(1); 
		
		my $msg_entity = MIME::Entity->build(
	    	Type    => "multipart/alternative",
			Charset => $ls->param('charset_value'),
		); 
	    $msg_entity->attach(
	        Type     => 'text/plain',
	        Encoding => $ls->param('plaintext_encoding'),
	        Data     => $etp->{plaintext},
	    );
	    $msg_entity->attach(
	        Type     => 'text/html',
	        Encoding => $ls->param('html_encoding'),
	        Data     => $etp->{html},
	    );
		
		my $r_entity = undef;
		
        my $rand_string = generate_rand_string_md5();
		
		#$r .= '$rand_string: ' . $rand_string . "\n"; 
		#$r .= '$to_address here is1: ' . $to_address . "\n"; 
		
	    my $confirmation_link =
	        $DADA::Config::S_PROGRAM_URL
	      . '?flavor=plugins&plugin=bridge&prm=mod&list='
	      . DADA::App::Guts::uriescape( $self->{list} )
	      . '&process=confirm&msg_id='
	      . DADA::App::Guts::uriescape( $args->{-msg_id} )
		  . '&acting_email_address='
		  . uriescape($to_address)
		  . '&rand_string=' 
		  . uriescape($rand_string)
		; 

  		#$r .= '$to_address here is2: ' . $to_address . "\n"; 
	    my $deny_link =
	        $DADA::Config::S_PROGRAM_URL
	      . '?flavor=plugins&plugin=bridge&prm=mod&list='
	      . DADA::App::Guts::uriescape( $self->{list} )
	      . '&process=deny&msg_id='
	      . DADA::App::Guts::uriescape( $args->{-msg_id} )
		  . '&acting_email_address='
		  . uriescape($to_address)
		  . '&rand_string=' 
		  . uriescape($rand_string)
		;  
		  
	    $r_entity = MIME::Entity->build(
	        Type    => "multipart/mixed",
	        Subject => $etp->{vars}->{subject},
	        To      => $dfm->format_phrase_address(
							$etp->{vars}->{to_phrase},
							$to_address,
						),
	        From    => $dfm->format_phrase_address(
							$etp->{vars}->{from_phrase},
							$ls->param('list_owner_email'),
						),

	    );
		
		#$r .= '$to_address here is3: ' . $to_address . "\n"; 
		
		$r_entity->add_part($msg_entity);
	    $r_entity->attach(
	        Type        => 'message/rfc822',
	        Disposition => "inline",
	        Data        => DADA::App::Guts::safely_decode( DADA::App::Guts::safely_encode( $entity->as_string ) ),
	    );

  		#$r .= '$to_address here is4: ' . $to_address . "\n"; 
		#$r .= '$confirmation_link: ' . $confirmation_link . "\n";  
		#$r .= '$deny_link: ' . $deny_link . "\n";  	
		
	    require DADA::App::Messages;
		my $dap = DADA::App::Messages->new( { -list => $self->{list} } );
		
		
        $dap->send_generic_email(
            {
				-entity      => $r_entity, 
                -tmpl_params => {
                    -list_settings_vars       => $ls->get,
                    -list_settings_vars_param => { -dot_it => 1, },
                    -vars => {
                        moderation_confirmation_link => $confirmation_link,
                        moderation_deny_link         => $deny_link,
                        message_subject              => $subject,
                        msg_id                       => $args->{-msg_id},
                        'subscriber.email'           => $args->{-from},
                    },
                },
            },
        );
		
	    $r .= "\t* Sent moderation request to $to_address\n";
		
		
		undef($confirmation_link); 
		undef($deny_link);
		undef($to_address);
		$r_entity->purge;
		undef($r_entity);
		undef($dap);
		$msg_entity->purge; 
		undef($msg_entity); 
		
    }
	
	$entity->purge;

    return $r; 
}

sub send_moderation_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }
    $args->{-msg_id} =~ s/\@/_at_/g;
    $args->{-msg_id} =~ s/\>|\<//g;
    $args->{-msg_id} = DADA::App::Guts::strip( $args->{-msg_id} );

    if ( !$args->{-parser} ) {
        die "You must supply a parser!";
    }

    # DEV there are two instances of my $parser, and my $entity of them - which one is the correct one?

    my $parser = $args->{-parser};

    my $entity;
    try {
        $entity = $parser->parse_data(
            DADA::App::Guts::safely_encode(
                $self->get_msg( { -msg_id => $args->{-msg_id} } )
            )
        );
    } catch { 
		 warn "no entity found! $_";
		 return undef; 
	};
	
    require DADA::App::FormatMessages;
    my $dfm = DADA::App::FormatMessages->new( -List => $self->{list} );

    my $subject = $entity->head->get( 'Subject', 0 );
       $subject =~ s/\n//g;
       $subject = $dfm->_decode_header($subject);

    my $from = $entity->head->get( 'From', 0 );
    $from =~ s/\n//g;

    require DADA::MailingList::Settings;
    my $ls = DADA::MailingList::Settings->new( { -list => $self->{list} } );


	require DADA::App::Messages;
    my $dap = DADA::App::Messages->new({-list => $self->{list}});
	$dap->send_out_message(
		{ 
			-message => 'await_moderation_msg', 
			-email   => $from,
		    -tmpl_params => {
		        -list_settings_vars       => $ls->params,
		        -list_settings_vars_param => { -dot_it => 1, },
		        -subscriber_vars          => { 'subscriber.email' => $args->{-from}, },
		        -vars                     => {
		            message_subject => $subject,
		            message_from    => $args->{-from},
		            msg_id          => $args->{-msg_id},
		            Plugin_Name     => $Plugin_Config->{Plugin_Name},
		        }
		    }
		}
	);

	$entity->purge;
}

sub send_accept_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }
    $args->{-msg_id} =~ s/\@/_at_/g;
    $args->{-msg_id} =~ s/\>|\<//g;
    $args->{-msg_id} = DADA::App::Guts::strip( $args->{-msg_id} );

    if ( !$args->{-parser} ) {
        die "You must supply a parser!";
    }

    # DEV there are two instances of my $parser, and my $entity of them - which one is the correct one?

    my $parser = $args->{-parser};

    my $entity;
    try {
        $entity = $parser->parse_data( DADA::App::Guts::safely_encode( $self->get_msg( { -msg_id => $args->{-msg_id} } ) ) );
    } catch { 
    	warn "no entity found! $_";
		return undef
	};

    require DADA::App::FormatMessages;
    my $dfm = DADA::App::FormatMessages->new( -List => $self->{list} );

    my $subject = $entity->head->get( 'Subject', 0 );
    $subject =~ s/\n//g;
    $subject = $dfm->_decode_header($subject);

    my $from = $entity->head->get( 'From', 0 );
    $from =~ s/\n//g;

    require DADA::MailingList::Settings;
    my $ls = DADA::MailingList::Settings->new( { -list => $self->{list} } );
	
	require DADA::App::Messages;
    my $dap = DADA::App::Messages->new({-list => $ls->param('list')});
	
    warn "Bridge moderated message has been ACCEPTED:"
		. "\n\tList: "         . $self->{list}
		. "\n\tFrom: "         . $from
		. "\n\tSubject: "      . $subject
		. "\n\tMessage-ID: "   . $args->{-msg_id}
		. "\n\tBy Moderator: " . $args->{-acting_email_address}
		. "\n\tUser Agent:   " . $ENV{HTTP_USER_AGENT}
	;
	
	
	$dap->send_out_message(
		{
			-message => 'accept_message',
			-email   => $from, 
            -tmpl_params => {
                -list_settings_vars       => $ls->params,
                -list_settings_vars_param => { -dot_it => 1, },
                -subscriber_vars          => { 'subscriber.email' => $args->{-from}, },
                -vars                     => {
                    message_subject => $subject,
                    message_from    => $from,
                    msg_id          => $args->{-msg_id},
                    Plugin_Name     => $Plugin_Config->{Plugin_Name},
                }
            }
		}	
	);
}

sub send_reject_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }
    $args->{-msg_id} =~ s/\@/_at_/g;
    $args->{-msg_id} =~ s/\>|\<//g;
    $args->{-msg_id} = DADA::App::Guts::strip( $args->{-msg_id} );

    if ( !$args->{-parser} ) {
        die "You must supply a parser!";
    }

    # DEV there are two instances of my $parser, and my $entity of them - which one is the correct one?

    my $parser = $args->{-parser};

    my $entity;
    try {
        $entity =
          $parser->parse_data( DADA::App::Guts::safely_encode( $self->get_msg( { -msg_id => $args->{-msg_id} } ) ) );

    } catch {
        warn "no entity found! $_";
    };


	
    require DADA::App::FormatMessages;
    my $dfm = DADA::App::FormatMessages->new( -List => $self->{list} );
    my $subject = $entity->head->get( 'Subject', 0 );
    $subject =~ s/\n//g;
    $subject = $dfm->_decode_header($subject);

    my $from = $entity->head->get( 'From', 0 );
    $from =~ s/\n//g;

    require DADA::MailingList::Settings;
    my $ls = DADA::MailingList::Settings->new( { -list => $self->{list} } );
	
    warn "Bridge moderated message has been REJECTED:"
		. "\n\tList: "         . $self->{list}
		. "\n\tFrom: "         .  $from
		. "\n\tSubject: "      . $subject
		. "\n\tMessage-ID: "   . $args->{-msg_id}
		. "\n\tBy Moderator: " . $args->{-acting_email_address}
		. "\n\tUser Agent:   " . $ENV{HTTP_USER_AGENT}
	;

	require DADA::App::Messages;
    my $dap = DADA::App::Messages->new(
		{
			-list => $ls->param('list'),
		}
	);
	
	$dap->send_out_message(
        {
			-message => 'rejection_msg',
			-email => $from,
            -tmpl_params => {
                -list_settings_vars       => $ls->params,
                -list_settings_vars_param => { -dot_it => 1, },
                -subscriber_vars          => {
                    'subscriber.email' => $from,

                },
                -vars => {

                    message_subject => $subject,
                    message_from    => $args->{-from},
                    msg_id          => $args->{-msg_id},
                    Plugin_Name     => $Plugin_Config->{Plugin_Name},

                }
            },
        }
    );
	
}

sub is_moderated_msg {

    my $self   = shift;
    my $msg_id = shift;
	
	my $r =$self->mod_msg_filename($msg_id); 

    if ( -e $self->mod_msg_filename($msg_id) ) {
        return (1, $r);
    }
    else {
        return (0, $r);
    }

}

sub get_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }

    my $file = $self->mod_msg_filename( $args->{-msg_id} );

    if ( !-e $file ) {

        die "Message: $file doesn't exist?!";

    }
    else {

        open my $MSG_FILE, '<', $file
          or die "Cannot read saved raw message at: '" . $file . "' because: " . $!;

        my $msg = do { local $/; <$MSG_FILE> };

        close $MSG_FILE
          or die "Didn't close: '" . $file . "'properly because: " . $!;

        return $msg;

    }

}

sub remove_msg {

    my $self = shift;
    my ($args) = @_;

    if ( !$args->{-msg_id} ) {
        die "You must supply a message id!";
    }

    my $file = $self->mod_msg_filename( $args->{-msg_id} );

    if ( -e $file ) {

        my $count = unlink($file);
        if ( $count != 1 ) {
            warn "Weird file delete count is: $count - should be, '1'";
        }
    }
    else {
        warn "no file at: $file to delete!";
    }

    return 1;

}

sub mod_msg_filename {

    my $self       = shift;
    my $message_id = shift;

    $message_id =~ s/\@/_at_/g;
    $message_id =~ s/\>|\<//g;
    $message_id = DADA::App::Guts::strip($message_id);
    $message_id = DADA::App::Guts::uriescape($message_id);
    return $self->mod_dir . '/' . DADA::App::Guts::uriescape( $self->{list} ) . '-' . $message_id;

}

sub mod_dir {

    my $self = shift;

    return $DADA::Config::TMP . '/moderated_msgs';

}

=pod

=head1 Bridge

Bridge connects your mail reader with Dada Mail, allowing you to send both announce-only messages from your mail reader as well as enabling discussion mailing lists, where each member of your mailing list may send messages out which will then be sent to the entire mailing list. 

=begin html

<iframe width="640" height="360" src="https://www.youtube.com/embed/gaqVVOURtuI?rel=0" frameborder="0" allowfullscreen></iframe>

=end html

=head2 How Bridge Works

Messages are received by sending these messages to the B<List Email>, which you will have to set up separately to this plugin. 

The List Email can be either a regular B<POP3 email account>, which Bridge will log into on a schedule (cronjob), or an B<Email Forward>, which will forward the message directly to the Bridge. 

=head1 User Guide

The below documentation goes into detail on how to I<install> and I<configure> Bridge. A user guide for Bridge is available in the Dada Mail Manual chapter, B<Using Bridge>: 

L<https://dadamailproject.com/pro_dada/using_bridge.html>

For more information on Pro Dada/Dada Mail Manual: 

L<https://dadamailproject.com/purchase/pro.html>

=head1 Obtaining The Plugin

Bridge is located in the, I<dada/plugins> directory of the Dada Mail distribution, under the name: I<bridge>

=head1 Installation 

Bridge can be installed using the included installer that comes with Dada Mail. In the Installer look under, B<Plugins/Extensions > and check B<Bridge>.

=head1 Mailing List Configuration 

Once you've installed Bridge, you may access it via the List Control Panel. Bridge can be accessed via the admin menu under, B<Plugins/Extensions - Discussion Lists> 

Before you can start using Bridge for your mailing list, there's two things that you will need to do for each mailing list you would like to use Bridge for:

=over

=item * Enable Bridge

=item * Create and Configure the List Email 

=back 

=head2 Enable Bridge

In Bridge's control panel, under, B<General>, uncheck the option: B<Disable Bridge>. Save your changes. 

=head2 Create and Configure the List Email

The B<List Email> is the address that you will be sending your email messages to for them to be broadcasted to your entire mailing list.

There's a few constraints you want to keep in mind when creating the List Email. Most likely, the address is going to be on the same domain that you install Dada Mail and it's going to be an address that you're not already using; either somewhere else in Dada Mail, or as another general email address. 

The List Email can either be a normal B<POP3 Email Account>, or a B<Mail Forward>.

A B<POP3 account> is fairly easy to set up and Bridge basically acts as a mail reader: Bridge will log in via POP3, check any messages that may be available, process them, then delete them when they've been read. 

A B<Mail Forward> may be slightly trickier to set up, but messages will br processed much quicker, as it's not waiting for the schedule to run, as you do, with a POP3 setup. 

Before attempting, make sure that you can set up a mail forward that can B<Pipe to a Program>, and not simply forward to another email address.  

=head3 Setup As: POP3 Account 

Toggle the radio buttons labeled, B<Setup As> to, B<POP3 Account>.

Create a new POP3 Account. This email account will be the email address you will send messages to. Additional fields should be shown, where you may plug in the POP3 Login information for this email address ( POP3 Server,  POP3 Username,  POP3 Password, etc.). You may test that the login credentials are working by clicking the button labeled, B<Test POP3 Login Information...>. 

Once the login information works with Bridge, Save your changes. 

=head3 Setup As: Mail Forward

Toggle the radio buttons labeled, B<Setup As> to, B<Email Forward>. An I<example> of the command you'll need to work with Bridge will be shown. 

Create a new Mail Forward, and use the example shown as a starting point for the piped command. Here's an example, 

 |/home/youraccount/public_html/cgi-bin/dada/mail.cgi --inject --list yourlist

If you're setting the command in cPanel (or something similar) and it asks you to, "I<enter a path relative to your home directory>", you may need to simply remove the B<Pipe Character> (B<|>) and the path to your home directory I</home/youraccount>, and plugging in this instead: 

    public_html/cgi-bin/dada/mail.cgi --inject --list yourlist

=head2 Cronjob

Bridge runs in the background on a schedule. Make sure to set Dada Mail's cronjob: 

L<https://dadamailproject.com/d/features-scheduled_cronjobs.pod.html>

=head2 DMARC Policies and Bridge

Bridge has a very important option labeled, B<Send messages, "On Behalf Of" (p.p. mode) >. If you are running a discussion list, you will want this option checked, to make sure mail delivery works. 

This feature was introduced in v7.3.0 of Dada Mail and has been refined since. If you're running a version of Dada Mail below v7.3.0 and are running a discussion list, it's important to upgrade to make sure your discussion list is running correctly. 

=head2 Testing Bridge

Once you've enabled Bridge, and have set up the List Email, it's time to test the plugin. Simply send a message to your List Email. To make things easier, make sure to send the message from the List Owner's email address, which is allowed to send to both announce-only and discussion type mailing lists. If a message is sent out to your entire mailing list, congratulations: Bridge is working. 

=head2 Additional Mailing List Configuration

In Bridge's List Control Panel and below, B<List Email Configuration> section, there are additional settings you may customize, depending on how you'd like your mailing list to function. 

=head1 Advanced Topics

=head1 Plugin Configuration Settings

The below settings are available to you, if you wish to further configure Bridge. These settings can be configured inside your C<.dada_config> file. 

First, search and see if the following lines are present in your C<.dada_config> file: 

 # start cut for plugin configs
 =cut

 =cut
 # end cut for plugin configs

If they are present, remove them.

You can then configure the plugin variables on these lines: 

	Bridge => {
	
		Plugin_Name                         => undef,
		MessagesAtOnce                      => undef,
		Room_For_One_More_Check             => undef,
		Enable_POP3_File_Locking            => undef,
		Check_List_Owner_Return_Path_Header => undef,
		Check_Multiple_Return_Path_Headers  => undef,
	
	},

=head2 Plugin_Name

The name of the plugin. By default, B<Bridge>.

=head2 MessagesAtOnce

You can specify how many messages you want to have the program actually handle per execution of the script by changing the, C<MessagesAtOnce> variable. By default, it's set conservatively to, C<1>.

=head2 Room_For_One_More_Check

C<Room_For_One_More_Check> looks to see how many mass mailings are currently in the mass mailing queue. If its at or above the limit set in C<$MAILOUT_AT_ONCE_LIMIT>, Bridge will not attempt to look for and (possibly) create another mass mailing to join the queue. 

=head2 Enable_POP3_File_Locking

When set to, C<1>, Bridge will use a simple lockfile scheme to make sure that it does not check the same POP3 account at the same time another copy of the plugin is doing the exact same thing, saving you from potentially sending out multiple copies of the same message. 

Sometimes, the simple POPp3 lock in Dada Mail gets stale, and a deadlock happens. Setting this configuration to, C<0> disables lockfiles. Stale locks will be deleted by the app after a day of being stale.  

=head2 Check_List_Owner_Return_Path_Header

When testing the validity of a received message, Dada Mail will look to see if the, C<Return-Path> header matches what's set in the, C<From> header. If they do not match, this test fails and the message will be rejected. Setting, C<Check_List_Owner_Return_Path_Header> to, C<0> will disable this test. 

=head2 Check_Multiple_Return_Path_Headers

C<Check_Multiple_Return_Path_Headers> is another validity test for received messages. This time, the message is looked to see if it has more than one C<Return-Path> header. If it does, it is rejected. If you set, C<Check_Multiple_Return_Path_Headers> to, C<0>, this test will be disabled. 


=head1 Debugging 

=head2 Debugging your POP3 account information

The easiest way to debug your POP3 account info is to actually test it out. 

Try logging into your List Email, via the command line:

     prompt:]telnet pop3.example.com 110
     Trying 12.123.123.123...
     Connected to pop3.example.com.
     Escape character is '^]'.
     +OK <37892.1178250885@hedwig.summersault.com>
     user user%example.com
     +OK 
     pass sneaky
     +OK 
     list

In the above example, B<pop3.example.com> is your POP3 server. You'll be typing in: 

  user user%example.com

and: 

  pass sneaky

(changing them to their real values) when prompted. This is basically what bridge does itself. 

If you don't have command line access, try adding an account in a desktop mail reader. If these credentials work there, they'll most likely work for Bridge. 

If your account information is correct and also logs in when you test the pop3 login information through bridge yourself, check to see if there isn't an email filter attached the account that looks at messages before they're delivered to the POP3 Mailbox and outright deletes messages because it triggered a flag. 

This could be the cause of mysterious occurences of messages never reaching the POP3 Mailbox. 

=head1 COPYRIGHT

Copyright (c) 1999 - 2023 Justin Simoni All rights reserved. 

=head1 LICENSE

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.

=cut

