#!/usr/bin/perl package log_viewer; use strict; use FindBin; use lib "$FindBin::Bin/../"; use lib "$FindBin::Bin/../DADA/perllib"; 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; } $ENV{PATH} = "/bin:/usr/bin"; delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' }; my $Plugin_Config = {}; # Usually, this doesn't need to be changed. # But, if you are having trouble saving settings # and are redirected to an # outside page, you may need to set this manually. $Plugin_Config->{Plugin_URL} = $DADA::Config::S_PROGRAM_URL . '?flavor=plugins;&plugin=log_viewer'; $Plugin_Config->{Plugin_Name} = 'Log Viewer'; # This refers to the, "tail" command: $Plugin_Config->{tail_command} = 'tail'; # use some of those Modules use DADA::Config 11.0.0; use DADA::Template::HTML; use DADA::App::Guts; use DADA::MailingList::Settings; use DADA::App::LogSearch; my $admin_list; my $root_login; my $list; my %Logs; my $logs; my $log_name; my $lines; my $Default_Log; sub reset_globals { $admin_list = undef; $root_login = undef; $list = undef; %Logs = (); $logs = undef; $log_name = undef; $lines = undef; $Default_Log = 'Usage Log'; } reset_globals(); init_vars(); run() unless caller(); sub run { my $q = shift; if ( !$ENV{GATEWAY_INTERFACE} ) { # croak "There's no CLI for this plugin."; } else { cgi_main($q); } } sub test_sub { return "Hello, World!"; } 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->{'log_viewer'}->{$key} ) ) { if ( defined( $DADA::Config::PLUGIN_CONFIGS->{'log_viewer'}->{$key} ) ) { $Plugin_Config->{$key} = $DADA::Config::PLUGIN_CONFIGS->{'log_viewer'}->{$key}; } } } } sub cgi_main { my $q = shift; my ( $admin_list, $t_root_login, $checksout, $error_msg ) = check_list_security( -cgi_obj => $q, -Function => 'log_viewer' ); if ( !$checksout ) { return ({}, $error_msg) } $root_login = $t_root_login; $list = $admin_list; $logs = {}; $lines = $q->param('lines') || 100; $log_name = $q->param('log_name') || $Default_Log; %Logs = get_logs($list); $logs = find_logs(); my $ls = DADA::MailingList::Settings->new( { -list => $list } ); my $li = $ls->get; my $prm = $q->param('prm') || 'cgi_view'; my %Mode = ( 'cgi_view' => \&cgi_view, 'ajax_view_logs_results' => \&ajax_view_logs_results, 'ajax_delete_log' => \&ajax_delete_log, 'search_logs' => \&search_logs, 'download' => \&download, ); if ( exists( $Mode{$prm} ) ) { return $Mode{$prm}->($q); #call the correct subroutine } else { return cgi_view($q); } } sub cgi_view { my $q = shift; # get the list information my $ls = DADA::MailingList::Settings->new( { -list => $list } ); my $li = $ls->get; my @log_name_strings = ( 'Usage Log', 'Error Log', 'Bounce Handler Log', 'Cronjob Output' ); my @order_of_names = (); for my $log_names (@log_name_strings) { if ( exists( $logs->{$log_names} ) ) { push( @order_of_names, $log_names, ); delete( $logs->{$log_names} ); } } push( @order_of_names, keys %$logs ); require HTML::Menu::Select; my $logs_popup_menu = HTML::Menu::Select::popup_menu( { name => 'log_name', id => 'log_name', values => [@order_of_names], default => $log_name, } ); my $log_lines = []; for ( 100, 1, 10, 20, 25, 50, 100, 200, 500, 1000, 10000, 100000, 1000000 ) { push( @$log_lines, { line_count => $_ } ); } $Default_Log = $log_name; my @log_names = @order_of_names; require DADA::Template::Widgets; my $scrn = DADA::Template::Widgets::wrap_screen( { -screen => 'plugins/log_viewer/default.tmpl', -with => 'admin', -wrapper_params => { -Root_Login => $root_login, -List => $list, }, -vars => { log_names => $#log_names, logs_popup_menu => $logs_popup_menu, log_lines => $log_lines, Plugin_URL => $Plugin_Config->{Plugin_URL}, }, } ); return ( {}, $scrn ); } sub ajax_view_logs_results { my $q = shift; my $scrn; $scrn .= '
'; $scrn .= show_log( $log_name, $lines ); $scrn .= '
'; return ( {}, $scrn ); } sub show_log { my ( $log_name, $lines ) = @_; my $loglines = log_lines( $Logs{$log_name}, $lines ); my $html; $html .= '
';
    for (@$loglines) {
        $_ =~ s/\t/    /g;
        $html .= encode_html_entities( $_, "\200-\377" ) . "\n";
    }
    $html .= '
'; ## $html .= '' . encode_html_entities( $_, "\200-\377" ) . '
' . "\n"; return $html; } sub ajax_delete_log { my @log_names = keys %$logs; if ( exists( $logs->{$log_name} ) ) { my $file = $Logs{$log_name}; make_safer($file); unlink($file); } return ( {}, undef ); } #---------------------------------------------------------------------# sub find_logs { my %found_logs; for ( keys %Logs ) { if ( file_check( $Logs{$_} ) == 1 ) { $found_logs{$_} = $Logs{$_}; } } return \%found_logs; } sub log_name { my $file_name = shift; for ( keys %Logs ) { if ( $file_name eq $Logs{$_} ) { return $_; } } } sub file_check { my $filename = shift; if ( -f $filename && -e $filename ) { return 1; } else { return 0; } } sub log_lines { my ( $filename, $lines ) = @_; my $tail = make_safer( $Plugin_Config->{tail_command} ); $filename = make_safer($filename); $lines = make_safer($lines); my $log = `$tail -n $lines $filename`; my @lines = split( "\n", $log ); if ( ( $log_name eq "Usage Log" ) || ( $log_name eq "Bounce Handler Log" ) ) { my @good_lines; for (@lines) { if ( ( $_ =~ m/\[(.*?)\]\t$list(.*)/ ) || ( $_ =~ m/\[(.*?)\]\t(.*?)\t$list(.*)/ ) ) { push( @good_lines, $_ ); } } @lines = @good_lines; undef(@good_lines); } my @d = (); for my $l(@lines){ push(@d, safely_decode($l)); } undef @lines; return \@d; } sub search_logs { my $q = shift; my $s_logs = shift || [ values(%$logs) ]; my $query = shift || $q->param('query'); my $test = shift || undef; $list ||= shift; require Data::Dumper; my $only_content = 0; my $ls = undef; my $li = undef; my $return = undef; if ( !$test ) { $only_content = xss_filter( scalar $q->param('only_content') ) || 0; # get the list information $ls = DADA::MailingList::Settings->new( { -list => $list, } ); $li = $ls->get; } else { $only_content = 1; } # header #$return .= $q->header( admin_header_params() ); if ( $only_content != 1 ) { $return .= admin_template_header( -Title => "Log Viewer - Search", -List => $li->{list}, -Root_Login => $root_login, -HTML_Header => 0, ); } if ( $only_content != 1 ) { $return .= "

{Plugin_URL} . "\">" . $Plugin_Config->{Plugin_Name} . " » Search Results

"; $return .= $q->h1( "Results for: " . $q->param('query') ); } my $searcher = DADA::App::LogSearch->new; my $results = $searcher->search( { -query => $query, -files => $s_logs, } ); my @terms = split( ' ', $q->param('query') ); for my $file_name ( keys %$results ) { if ( $results->{$file_name}->[0] ) { $return .= $q->h1( log_name($file_name) ); for my $l ( @{ $results->{$file_name} } ) { my @entries = split( "\t", $l ); $return .= '
'; } } } if ( $only_content != 1 ) { $return .= admin_template_footer( -List => $list, -Form => 0 ); } if ($test) { return $return; } else { return ( {}, $return ); } } sub download { use File::Basename; my $name = fileparse( $Logs{$log_name} ); my ( $file_name, $file_ending ) = split( /\./, $name, 2 ); open my $LOG, '<:encoding(' . $DADA::Config::HTML_CHARSET . ')', make_safer( $Logs{$log_name} ) or die $!; my $line; my $l = undef; while ( defined( $line = <$LOG> ) ) { $l .= $line; } close($LOG); return ( { "-Content-disposition" => 'attachment; filename=' . $file_name . '-' . time . '.' . $file_ending, -type => 'text/plain', }, $l ); } sub get_logs { my $l_list = shift; my %logs = ( 'Usage Log' => $DADA::Config::PROGRAM_USAGE_LOG, 'Error Log' => $DADA::Config::PROGRAM_ERROR_LOG, # 'Clickthrough Log (raw)' => $DADA::Config::LOGS . '/' . $l_list . '-clickthrough.log', 'Bounce Handler Log' => $DADA::Config::LOGS . '/' . 'bounces.txt', 'Cronjob Output' => $DADA::Config::LOGS . '/' . 'cronjob_output.txt', ); my $mass_mailing_logs = mass_mailing_logs($l_list); my $bridge_received_msgs = bridge_received_msgs($l_list); if ( keys %$mass_mailing_logs ) { %logs = ( %logs, %{$mass_mailing_logs} ); } if ( keys %$bridge_received_msgs ) { %logs = ( %logs, %{$bridge_received_msgs} ); } return %logs; } sub mass_mailing_logs { my $list = shift; my $dir = $DADA::Config::LOGS; my $file; my @files; $dir = DADA::App::Guts::make_safer($dir); opendir( DIR, $dir ) or die "$!"; while ( defined( $file = readdir DIR ) ) { next if $file =~ /^\.\.?$/; $file =~ s(^.*/)(); if ( -f $dir . '/' . $file ) { if ( $file =~ m/sendout\-$list/ ) { push( @files, $file ); } } } closedir(DIR); # sendout-example-list-20131222202213.73210509_at_dadamailproject.com-log my $mass_mailing_logs = {}; require DADA::MailingList::Archives; my $ma = DADA::MailingList::Archives->new( { -list => $list } ); foreach my $found_file (@files) { my ( $throway, $list_shortname, $mailing_type, $mid, $orig_fn ) = split( '-', $found_file, 5 ); $mid =~ s/\.(.*?)$//; my $subject = 'Mass Mailing: '; if ( $mailing_type eq 'list' ) { if ( $ma->check_if_entry_exists($mid) ) { $subject .= $ma->get_archive_subject($mid); } else { $subject .= 'Unarchived Message #' . $mid; } } else { $subject .= '(' . $mailing_type . ')' . ' Message # ' . $mid; } #my $date = scalar(localtime($mid)); my $date = date_this( -Packed_Date => $mid, -All => 1 ); $subject .= ', ' . $date; $mass_mailing_logs->{$subject} = $DADA::Config::LOGS . '/' . $found_file; } return $mass_mailing_logs; #return @files; } sub bridge_received_msgs { my $list = shift; my $dir = $DADA::Config::TMP; my $file; my @files; my $mbox = {}; $dir = DADA::App::Guts::make_safer($dir); my $file = $dir . '/' . 'bridge_received_msgs-' . $list . '.mbox'; if ( -f $file ) { $mbox->{'Bridge Received Messages (mbox)'} = $file; return $mbox; } } __END__ =pod =head1 Plugin: log_viewer.cgi - View Logs Created by Dada Mail This plugin allows you to view the Dada Mail, Error, Bounce and Clickthrough logs that Dada Mail creates in its activities through your web browser. =head1 Installation This plugin can be installed during a Dada Mail install/upgrade, using the included installer that comes with Dada Mail. The below installation instructions go through how to install the plugin manually. =head2 Change permissions of "log_viewer.cgi" to 755 The, C plugin will be located in your, I diretory. Change the script to, C<755> =head2 Configure your .dada_config file Now, edit your C<.dada_config> file, so that it shows the plugin in the left-hand menu, under the, B heading: First, see if the following lines are present in your C<.dada_config> file: # start cut for list control panel menu =cut =cut # end cut for list control panel menu If they are, remove them. Then, find these lines: # { # -Title => 'View Logs', # -Title_URL => $S_PROGRAM_URL."/log_viewer", # -Function => 'log_viewer', # -Activated => 1, # }, Uncomment the lines, by taking off the, "#"'s: { -Title => 'View Logs', -Title_URL => $S_PROGRAM_URL."/log_viewer", -Function => 'log_viewer', -Activated => 1, }, Save your C<.dada_config> file. =head1 COPYRIGHT Copyright (c) 1999 - 2020 Justin Simoni http://justinsimoni.com All rights reserved. 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