#!/usr/bin/perl use strict; #---------------------------------------------------------------------# # dada_bridge.pl (No Cute Name Yet) # For instructions, see the pod of this file. try: # pod2text ./dada_bridge.pl | less # # Or try online: # http://mojo.skazat.com/support/documentation/dada_bridge.pl.html # #---------------------------------------------------------------------# # REQUIRED: # # You must change (below) the: # # Absolute path of the, dada, dada/DADA and dada/DADA/perllib # directories. # # The path to your Perl Libraries must be changed and include: # Your Site-Wide Perl Libraries #---------------------------------------------------------------------# use lib qw( ../ ../DADA ../DADA/perllib /home/account/www/cgi-bin/dada /home/account/www/cgi-bin/dada/DADA /home/account/www/cgi-bin/dada/DADA/perllib /usr/local/lib/perl5/site_perl/5.8.0/mach /usr/local/lib/perl5/site_perl/5.8.0 /usr/local/lib/perl5/site_perl /usr/local/lib/perl5/5.8.0/BSDPAN /usr/local/lib/perl5/5.8.0/mach /usr/local/lib/perl5/5.8.0 ); # # There is nothing else to configure in this program. #---------------------------------------------------------------------# use DADA::Config; #---------------------------------------------------------------------# $ENV{PATH} = "/bin:/usr/bin"; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; my $App_Version = '0.0'; 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 Email::Address; use Digest::MD5 qw(md5_hex); use Net::POP3; use MIME::Parser; use MIME::Entity; use Getopt::Long; my %Global_Template_Options = ( #debug => 1, path => [$TEMPLATES], die_on_bad_params => 0, ); my $parser = new MIME::Parser; $parser = optimize_mime_parser($parser); use CGI; my $q = new CGI; my $test; my $help; my $verbose = 0; my $debug = 0; # not used? my $list; my $run_list; my $check_deletions = 0; my $root_login = 0; my $checksums = {}; GetOptions("help" => \$help, "test=s" => \$test, "verbose" => \$verbose, "list=s" => \$run_list, "check_deletions" => \$check_deletions, ); main(); sub main { if(!$ENV{GATEWAY_INTERFACE}){ &cl_main(); }else{ &cgi_main(); } } sub cgi_main { my $admin_list; ($admin_list, $root_login) = check_list_security(-cgi_obj => $q, -Function => 'dada_bridge'); $list = $admin_list; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get(); my $flavor = $q->param('flavor') || 'cgi_default'; my %Mode = ( 'cgi_default' => \&cgi_default, 'cgi_edit' => \&cgi_edit, 'test_pop3' => \&cgi_test_pop3, 'manual_start' => \&cgi_manual_start, ); if(exists($Mode{$flavor})) { $Mode{$flavor}->(); #call the correct subroutine }else{ &cgi_default; } } sub cgi_test_pop3 { print(admin_html_header(-Title => "POP3 Login Test", -List => $list, -Form => 0, -Root_Login => $root_login )); $run_list = $list; $verbose = 1; print '
'; test_pop3(); print ''; print ''; print admin_html_footer(-Form => 0, -List => $list, ); } sub cgi_manual_start { print(admin_html_header( -Title => "Manually Running Mailing...", -List => $list, -Form => 0, -Root_Login => $root_login )); $run_list = $list; $verbose = 1; $check_deletions = 1; print '
'; start(); print ''; print ''; print admin_html_footer(-Form => 0, -List => $list, ); } sub cgi_default { require HTML::Template::Expr; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get(); print(admin_html_header( -Title => "Discussion List Options", -List => $list, -Form => 0, -Root_Login => $root_login, )); my $tmpl = default_cgi_template(); my $template = HTML::Template::Expr->new(%Global_Template_Options, scalarref => \$tmpl, ); my $done = $q->param('done') || 0; $template->param( done => $done, GOOD_JOB_MESSAGE => $GOOD_JOB_MESSAGE, list => $list, list_name => $li->{list_name}, disable_discussion_sending => $li->{disable_discussion_sending}, group_list => $li->{group_list}, append_list_name_to_subject => $li->{append_list_name_to_subject}, add_reply_to => $li->{add_reply_to}, discussion_pop_email => $li->{discussion_pop_email}, discussion_pop_server => $li->{discussion_pop_server}, discussion_pop_username => $li->{discussion_pop_username}, discussion_pop_password => DADA::Security::Password::cipher_decrypt($li->{cipher_key}, $li->{discussion_pop_password}), append_discussion_lists_with => $li->{append_discussion_lists_with}, list_owner_email => $li->{list_owner_email}, admin_email => $li->{admin_email}, enable_moderation => $li->{enable_moderation}, send_msgs_to_list => $li->{send_msgs_to_list}, send_msg_copy_to => $li->{send_msg_copy_to}, send_msg_copy_address => $li->{send_msg_copy_address}, send_not_allowed_to_post_msg => $li->{send_not_allowed_to_post_msg}, send_invalid_msgs_to_owner => $li->{send_invalid_msgs_to_owner}, mail_discussion_message_to_poster => $li->{mail_discussion_message_to_poster}, PROGRAM_URL => $PROGRAM_URL, archive_messages => $li->{archive_messages}, ); print $template->output(); print admin_html_footer(-Form => 0, -List => $list, ); } sub cgi_edit { my $disable_discussion_sending = $q->param('disable_discussion_sending') || 0; my $group_list = $q->param('group_list') || 0; my $append_list_name_to_subject = $q->param('append_list_name_to_subject') || 0; my $add_reply_to = $q->param('add_reply_to') || 0; my $discussion_pop_email = $q->param('discussion_pop_email') || undef; my $discussion_pop_server = $q->param('discussion_pop_server') || undef; my $discussion_pop_username = $q->param('discussion_pop_username') || undef; my $discussion_pop_password = $q->param('discussion_pop_password') || undef; my $append_discussion_lists_with = $q->param('append_discussion_lists_with') || ''; my $enable_moderation = $q->param('enable_moderation') || 0; my $send_msgs_to_list = $q->param('send_msgs_to_list') || 0; my $send_msg_copy_to = $q->param('send_msg_copy_to') || 0; my $send_msg_copy_address = $q->param('send_msg_copy_address') || ''; my $send_not_allowed_to_post_msg = $q->param('send_not_allowed_to_post_msg') || 0; my $send_invalid_msgs_to_owner = $q->param('send_invalid_msgs_to_owner') || 0; my $mail_discussion_message_to_poster = $q->param('mail_discussion_message_to_poster') || 0; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get; $ls->save({ disable_discussion_sending => $disable_discussion_sending, group_list => $group_list, append_list_name_to_subject => $append_list_name_to_subject, add_reply_to => $add_reply_to, discussion_pop_email => $discussion_pop_email, discussion_pop_server => $discussion_pop_server, discussion_pop_username => $discussion_pop_username, append_discussion_lists_with => $append_discussion_lists_with, discussion_pop_password => DADA::Security::Password::cipher_encrypt($li->{cipher_key}, $discussion_pop_password), enable_moderation => $enable_moderation, send_msgs_to_list => $send_msgs_to_list, send_msg_copy_to => $send_msg_copy_to, send_msg_copy_address => $send_msg_copy_address, send_not_allowed_to_post_msg => $send_not_allowed_to_post_msg, send_invalid_msgs_to_owner => $send_invalid_msgs_to_owner, mail_discussion_message_to_poster => $mail_discussion_message_to_poster, }); print $q->redirect(-uri => $q->url.'?done=1'); } sub cl_main { init(); if($test){ $verbose = 1; if($test eq 'pop3'){ test_pop3(); }else{ print "I don't know what you want to test!\n\n"; help(); } }elsif($help){ help(); }else{ start(); } } sub init {}; sub start { my @lists; if(!$run_list){ print "Running all lists - \nTo test an individual list, pass the list shortname in the '--list' parameter...\n\n" if $verbose; @lists = available_lists(); }else{ $lists[0] = $run_list; } foreach my $list (@lists){ print "\n" . '-' x 72 . "\nList: " . $list . "\n" if $verbose; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get(); next if ! valid_login_information($list); my $pop = pop3_login( $list, $li->{discussion_pop_server}, $li->{discussion_pop_username}, $li->{discussion_pop_password}, ); if($pop){ my $msgnums = $pop->list; # hashref of msgnum => size foreach my $msgnum (keys %$msgnums) { if ($li->{disable_discussion_sending} != 1){ my $msg = $pop->get($msgnum); my $full_msg; $full_msg .= $_ foreach(@$msg); push(@{$checksums->{$list}}, create_checksum(\$full_msg)); my ($status, $errors) = validate_msg($list, $full_msg); if($status){ process($list, $li, $full_msg); }else{ print "\tMessage did not pass verification - handling that...real soon!\n"; handle_errors($list, $errors, $full_msg); } }else{ print "\tThis sending method has been disabled for $list, deleting message... \n" if $verbose; } } foreach my $msgnum (keys %$msgnums) { print "\tremoving message from server...\n" if $verbose; $pop->delete($msgnum); } print "\tdisconnecting from POP3 server\n" if $verbose; $pop->quit(); if($check_deletions){ if(keys %$msgnums){ message_was_deleted_check($list); }else{ print "\tNo messages received, skipping deletion check.\n" if $verbose; } } }else{ print "\tPOP3 connection failed!\n" if $verbose; } } } sub message_was_deleted_check() { print "\n\tWaiting 5 seconds before removal check...\n" if $verbose; sleep(5); my $list = shift; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get(); my $pop = pop3_login( $list, $li->{discussion_pop_server}, $li->{discussion_pop_username}, $li->{discussion_pop_password}, ); if($pop){ my $msgnums = $pop->list; # hashref of msgnum => size foreach my $msgnum (keys %$msgnums) { my $msg = $pop->get($msgnum); my $full_msg; $full_msg .= $_ foreach(@$msg); my $cs = create_checksum(\$full_msg); print "\t\tcs: $cs\n" if $verbose; foreach my $s_cs(@{$checksums->{$list}}){ if($cs eq $s_cs){ print "\t\tsaved checksum: $s_cs" if $verbose; print "\t\tMessage was NOT deleted from POP server! Will attempt to do that now...\n" if $verbose; $pop->delete($msgnum); } } } $pop->quit(); }else{ print "POP3 login failed.\n"; } } sub help { print "This is where the help should be, but it's not here.\n\n"; } sub test_pop3 { my @lists; if(!$run_list){ print "Testing all lists - \nTo test an individual list, pass the list shortname in the '--list' parameter...\n\n"; @lists = available_lists(); }else{ push(@lists, $run_list); } foreach my $l(@lists){ print "\n" . '-' x 72 . "\nTesting List: '" . $l . "'\n"; unless(check_if_list_exists(-List => $l)){ print "'$l' does not exist! - skipping\n"; next; } my $ls = DADA::MailingList::Settings->new(-List => $l); my $li = $ls->get(); if($li->{disable_discussion_sending} == 1){ print "'$l' has this feature disabled - skipping.\n"; }else{ my $pop = pop3_login($l, $li->{discussion_pop_server}, $li->{discussion_pop_username}, $li->{discussion_pop_password}, ); if($pop){ $pop->quit(); print "\tLogging off of the POP Server.\n"; } } } print "\n\nPOP3 Login Test Complete.\n\n"; } sub pop3_login { my ($l, $server, $username, $password) = @_; my $ls = DADA::MailingList::Settings->new(-List => $l); my $li = $ls->get(); $password = DADA::Security::Password::cipher_decrypt($li->{cipher_key}, $password); if(!valid_login_information($l)){ print "Some POP3 Login Information is missing - please double check! (aborting login attempt)\n" if $verbose; return undef; }else{ print "\tLogging into POP3 server: $server\n" if $verbose; my $pop = Net::POP3->new($server) or warn "\tConnection to '$server' wasn't successful: $!"; if(!$pop){ print "\tCouldn't estabilish a connection to $server!\n" if $verbose; }else{ my $messagecount; eval {require Digest::MD5}; if(!$@){ print "\tTrying secure login...\n" if $verbose; $messagecount = $pop->apop($username,$password); if(!$messagecount){ print "\tHmm, secure login failed, switching to regular login...\n" if $verbose; $pop = Net::POP3->new($server) or warn "\tConnection to '$server' wasn't successful: $!"; $messagecount = $pop->login($username,$password); } }else{ $messagecount = $pop->login($username,$password); } if(($messagecount ne '') && ($messagecount >= 0)){ print "\tPOP3 Login succeeded.\n" if $verbose; print "\tPOP3 Server says: " . $pop->banner if $verbose; print "\n\tMessage count: $messagecount\n" if $verbose; }else{ print "\tPOP3 login failed.\n" if $verbose; } } return undef if !$pop; return $pop; } } sub valid_login_information { my $list = shift; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get(); return 0 if ! $li->{discussion_pop_server}; return 0 if ! $li->{discussion_pop_username}; return 0 if ! $li->{discussion_pop_email}; return 0 if ! $li->{discussion_pop_password}; return 1; } sub validate_msg { my $list = shift; my $msg = shift; my $status = 1; my $errors = {msg_not_from_list_owner => 0, msg_from_list_address => 0}; my $ls = DADA::MailingList::Settings->new( -List => $list); my $lh = DADA::MailingList::Subscribers->new(-List => $list); my $li = $ls->get; my $entity; eval { $entity = $parser->parse_data($msg) }; if(!$entity){ print "\t\tMessage invalid! - no entity found.\n" if $verbose; $errors->{invalid_msg} = 1; return (0, $errors); } my $rough_from = $entity->head->get('From', 0); my $from_address = ''; if (defined($rough_from)){; eval { $from_address = (Email::Address->parse($rough_from))[0]->address; } } print '\t\tWarning! Something\'s wrong with the From address - ' . $@ if $@ && $verbose; $from_address = lc_email($from_address); print "\t\tMessage is from: '" . $from_address . "'\n"; if($from_address eq $li->{list_owner_email}){ print "\t\t * From: address is the list owner address ; (". $li->{list_owner_email} .')' . "\n" if $verbose; }else{ print "\t\t * From address is NOT from list owner address\n" if $verbose; $errors->{msg_not_from_list_owner} = 1; if($li->{enable_moderation}){ print "\t\tModeration enabled...\n" if $verbose; my ($m_status, $m_errors) = $lh->subscription_check(-Email => $from_address, -Type => 'moderators', ); if($m_errors->{subscribed} != 1){ $errors->{msg_not_from_moderator} = 1; print "\t\t*Message is NOT from a moderator.\n" if $verbose; }else{ print "\t\t*Message *is* from a moderator!\n" if $verbose; $errors->{msg_not_from_list_owner} = 0 if $errors->{msg_not_from_list_owner} == 1; } }else{ print "\t\tModeration disabled...\n"; } if($li->{group_list} == 1){ print "\t\tDiscussion Support enabled...\n" if $verbose; if($li->{enable_moderation} && $errors->{msg_not_from_moderator} == 0){ print "\t\tSubscription checked skipped - moderation enabled and address passed validation.\n"; }else{ my ($s_status, $s_errors) = $lh->subscription_check(-Email => $from_address); if ($s_errors->{subscribed} != 1){ $errors->{msg_not_from_subscriber} = 1; print "\t\t*Message is NOT from a subscriber.\n" if $verbose; }else{ print "\t\t*Message *is* from a current subscriber.\n" if $verbose; $errors->{msg_not_from_moderator} = 0; $errors->{msg_not_from_list_owner} = 0; } } }else{ print "\t\tDiscussion Support disabled...\n"; } } if ($li->{discussion_pop_email} eq $from_address){ $errors->{msg_from_list_address} = 1; print "\t\t *WARNING!* Message is from the List Address. That's bad.\n" if $verbose; } foreach(keys %$errors){ if($errors->{$_} == 1){ $status = 0 ; last; } } return ($status, $errors); } sub process { my $list = shift; my $li = shift; my $full_msg = shift; print "\n\t\tProcessing Message...\n" if $verbose; if($li->{send_msgs_to_list} == 1){ my $n_full_msg = dm_format($li->{list}, $full_msg); print "\t\tMessage being delivered! \n" if $verbose; my ($msg_id, $saved_message) = deliver($list, $n_full_msg); archive($list, $n_full_msg, $msg_id, $saved_message); } if($li->{send_msg_copy_to} && $li->{send_msg_copy_address}){ print "\t\t Sending a copy of the message to: " . $li->{send_msg_copy_address} . "\n" if $verbose; deliver_copy($li->{list}, $full_msg); } print "\t\tFinished Processing Message.\n\n" if $verbose; } sub dm_format { my $list = shift; my $msg = shift; require DADA::App::FormatMessages; my $fm = DADA::App::FormatMessages->new(-List => $list); $fm->treat_as_discussion_msg(1); my ($header_str, $body_str) = $fm->format_headers_and_body(-msg => $msg); return $header_str . "\n\n" . $body_str; } sub deliver_copy { print "Delivering Copy...\n" if $verbose; my $list = shift; my $msg = shift; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get; my $mh = DADA::Mail::Send->new($li); my $entity; eval { $entity = $parser->parse_data($msg) }; if(!$entity){ print "\t\tMessage sucks!\n" if $verbose; }else{ my %headers = $mh->return_headers($entity->stringify_header); $headers{To} = $li->{send_msg_copy_address}; if($verbose){ print "\tMessage Details: \n\t" . '-' x 50 . "\n"; print "\tSubject: " . $headers{Subject} . "\n"; } my $msg_id = $mh->send( %headers, # Trust me on these :) Body => $entity->stringify_body, ); } } sub deliver { my $list = shift; my $msg = shift; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get; my $mh = DADA::Mail::Send->new($li); my $entity; eval { $entity = $parser->parse_data($msg) }; if(!$entity){ print "\t\tMessage sucks!\n" if $verbose; }else{ my %headers = $mh->return_headers($entity->stringify_header); $headers{To} = $li->{list_owner_email}; if($verbose){ print "\tMessage Details: \n\t" . '-' x 50 . "\n"; print "\tSubject: " . $headers{Subject} . "\n"; } if($li->{group_list} == 1 && $li->{mail_discussion_message_to_poster} != 1){ my $f_a; if (defined($headers{From})){ eval { $f_a = (Email::Address->parse($headers{From}))[0]->address; } } if(!$@){ print "\tGoing to skip sending original poster ($f_a) a copy of their own message...\n" if $verbose; $mh->do_not_send_to([$f_a]); }else{ print "Problems not sending copy to original sender: $@\n\n" if $verbose; } } my $msg_id = $mh->bulk_send( %headers, # Trust me on these :) Body => $entity->stringify_body, ); return ($msg_id, $mh->saved_message); } } sub archive { my $list = shift; my $full_msg = shift; my $msg_id = shift; my $saved_msg = shift; my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get; if ($li->{archive_messages} == 1){ require DADA::MailingList::Archives; my $la = DADA::MailingList::Archives->new(-List => $li); my $entity; eval { $entity = $parser->parse_data($full_msg) }; if($entity){ $la->set_archive_info( $msg_id, $entity->head->get('Subject', 0), undef, undef, $saved_msg, ); }else{ warn "Problem archiving message..."; } } } sub send_msg_not_from_subscriber { my $list = shift; my $msg = shift; my $entity = $parser->parse_data($msg); my $rough_from = $entity->head->get('From', 0); my $from_address; if (defined($rough_from)){; eval { $from_address = (Email::Address->parse($rough_from))[0]->address; } } if($from_address && $from_address ne ''){ my $ls = DADA::MailingList::Settings->new(-List => $list); my $li = $ls->get; my $mh = DADA::Mail::Send->new($li); my $reply = MIME::Entity->build(Type => "multipart/mixed", From => $li->{list_owner_email}, To => $from_address, Subject => "$PROGRAM_NAME Error - Not Allowed to Post On " . $li->{list_name} . " (original message attached)", ); $reply->attach(Type => 'text/plain', Data => $li->{not_allowed_to_post_message} ); $reply->attach( Type => 'message/rfc822', Disposition => "attachment", Data => $entity->as_string, ); require DADA::App::FormatMessages; my $fm = DADA::App::FormatMessages->new(-List => $list); #$fm->treat_as_discussion_msg(1); $fm->use_email_templates(0); my ($header_str, $body_str) = $fm->format_headers_and_body(-msg => $reply->as_string); $mh->send( $mh->return_headers($header_str), Body => $body_str, ); }else{ warn "Problem with send_msg_not_from_subscriber!"; } } sub send_invalid_msgs_to_owner { my $li = shift; my $list = shift; my $msg = shift; my $entity = $parser->parse_data($msg); my $rough_from = $entity->head->get('From', 0); my $from_address; if (defined($rough_from)){; eval { $from_address = (Email::Address->parse($rough_from))[0]->address; } } if($from_address && $from_address ne ''){ my $mh = DADA::Mail::Send->new($li); my $reply = MIME::Entity->build(Type => "multipart/mixed", From => $li->{list_owner_email}, To => $li->{list_owner_email}, Subject => "$PROGRAM_NAME Error - $from_address Not Allowed to Post On " . $li->{list_name} . " (original message attached)", ); $reply->attach(Type => 'text/plain', Data => 'The attached message was not sent from one of the subscribers of ' . $li->{list_name}, ); $reply->attach( Type => 'message/rfc822', Disposition => "attachment", Data => $entity->as_string, ); $mh->send( $mh->return_headers($reply->stringify_header), Body => $reply->stringify_body ); }else{ warn "Problem with send_invalid_msgs_to_owner!"; } } sub handle_errors { my $list = shift; my $errors = shift; my $full_msg = shift; my $ls = DADA::MailingList::Settings->new( -List => $list); my $li = $ls->get; print "\t\tError delivering message! Reasons:\n\n"; foreach(keys %$errors){ print "\t\t\t" . $_ . "\n" if $errors->{$_} == 1; } if($errors->{msg_not_from_subscriber} == 1 || $errors->{msg_not_from_list_owner} == 1 || $errors->{msg_not_from_moderator} == 1){ if($li->{send_not_allowed_to_post_msg} == 1){ print "\t\tmsg_not_from_subscriber on its way! \n\n"; send_msg_not_from_subscriber($list, $full_msg); } if($li->{send_invalid_msgs_to_owner} == 1){ print "\t\tinvalid_msgs_to_owner on its way! \n\n"; send_invalid_msgs_to_owner($li, $list, $full_msg); } }elsif($errors->{msg_from_list_address}){ warn "$PROGRAM_NAME Error: message was from the list address - will not process! - (ignoring)"; } } sub create_checksum { my $data = shift; if($] >= 5.008){ require Encode; my $cs = md5_hex(Encode::encode_utf8($$data)); return $cs; }else{ my $cs = md5_hex($$data); return $cs; } } sub default_cgi_template { my $sf = submit_form(); return q{