diff --git a/.gitignore b/.gitignore index 5341a2e..30a2c65 100644 --- a/.gitignore +++ b/.gitignore @@ -2,14 +2,7 @@ *.log *spec-20* *.tgz -current.* *.xz -current1 -current2 *.html *.txt -accounts -configuration -domains -hosts *el8* diff --git a/root/etc/e-smith/db/configuration/defaults/mailstats/status b/root/etc/e-smith/db/configuration/defaults/mailstats/status new file mode 100644 index 0000000..86981e6 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/mailstats/status @@ -0,0 +1 @@ +enabled diff --git a/root/etc/e-smith/db/configuration/defaults/mailstats/type b/root/etc/e-smith/db/configuration/defaults/mailstats/type new file mode 100644 index 0000000..05c3192 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/mailstats/type @@ -0,0 +1 @@ +report diff --git a/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/100mailstats b/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/100mailstats index 6dcd0de..1bb42ce 100644 --- a/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/100mailstats +++ b/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/100mailstats @@ -1,6 +1,6 @@ { # mailstats - my $status = $mailstats{'Status'} || 'disabled'; + my $status = $mailstats{'status'} || 'disabled'; if ($status eq 'enabled') { diff --git a/root/usr/bin/mailstats.py b/root/usr/bin/mailstats.py index f55ad70..5f0614b 100644 --- a/root/usr/bin/mailstats.py +++ b/root/usr/bin/mailstats.py @@ -85,8 +85,8 @@ import argparse import tempfile #import mysql.connector import numpy as np -import plotly.graph_objects as go -import plotly.express as px +#import plotly.graph_objects as go +#import plotly.express as px import colorsys import pymysql import json @@ -98,7 +98,7 @@ log_dir_path = "/var/log/mailstats" # Check if the directory exists, and create it if it doesn't if not os.path.exists(log_dir_path): os.makedirs(log_dir_path) -logging.basicConfig(level=logging.INFO, # Default level of messages to log +logging.basicConfig(level=logging.DEBUG, # Default level of messages to log format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), # Log to console @@ -108,7 +108,7 @@ enable_graphs = True; #This could be a DB entry if required. try: import matplotlib.pyplot as plt except ImportError: - logging.debug("Matplotlib is not installed - no graphs") + logging.warning("Matplotlib is not installed - no graphs") enable_graphs = False; Mailstats_version = '1.2' @@ -150,6 +150,10 @@ PERCENT = TOTALS + 1 ColTotals = 24 ColPercent = 25 +def strip_ansi_codes(text): + ansi_escape = re.compile(r'\x1b\[[0-9;]*m') + return ansi_escape.sub('', text) + def replace_bracket_content(input_filename, output_filename): import re @@ -242,6 +246,8 @@ def get_logs_from_Journalctl(date='yesterday'): entry_timestamp = entry.get('__REALTIME_TIMESTAMP', None) entry_microseconds = int(entry_timestamp.timestamp() * 1_000_000) if entry_timestamp and since_microseconds <= entry_microseconds <= until_microseconds: + # takeout ASCII Escape sequences from the message + entry['MESSAGE'] = strip_ansi_codes(entry['MESSAGE']) logs.append(entry) # Sort logs by __REALTIME_TIMESTAMP in ascending order @@ -588,32 +594,49 @@ def parse_data(data): # and mapping: try: return_dict = { - 'sme': fields[0].strip() if len(fields) > 0 else None, - 'qpsmtpd': fields[1].strip() if len(fields) > 1 else None, - 'id': fields[2].strip() if len(fields) > 2 else None, - 'action': fields[3].strip() if len(fields) > 3 else None, #5 - 'logterse': fields[4].strip() if len(fields) > 4 else None, - 'ip': fields[5].strip() if len(fields) > 5 else None, - 'sendurl': fields[6].strip() if len(fields) > 6 else None, #1 - 'sendurl1': fields[7].strip() if len(fields) > 7 else None, #2 - 'from-email': fields[8].strip() if len(fields) > 8 else None, #3 - 'error-reason': fields[8].strip() if len(fields) > 9 else None, #3 - 'to-email': fields[9].strip() if len(fields) > 9 else None, #4 - 'error-plugin': fields[10].strip() if len(fields) > 10 else None, #5 - 'action1': fields[10].strip() if len(fields) > 10 else None, #5 - 'error-number' : fields[11].strip() if len(fields) > 11 else None, #6 - 'sender': fields[12].strip() if len(fields) > 12 else None, #7 - 'virus': fields[12].strip() if len(fields) > 12 else None, #7 - 'error-msg' :fields[13].strip() if len(fields) > 13 else None, #7 - 'spam-status': fields[13].strip() if len(fields) > 13 else None, #8 - 'error-result': fields[14].strip() if len(fields) > 14 else None,#8 + 'sme': fields[0].strip() if len(fields) > 0 else "", + 'qpsmtpd': fields[1].strip() if len(fields) > 1 else "", + 'id': fields[2].strip() if len(fields) > 2 else "", + 'action': fields[3].strip() if len(fields) > 3 else "", #5 + 'logterse': fields[4].strip() if len(fields) > 4 else "", + 'ip': fields[5].strip() if len(fields) > 5 else "", + 'sendurl': fields[6].strip() if len(fields) > 6 else "", #1 + 'sendurl1': fields[7].strip() if len(fields) > 7 else "", #2 + 'from-email': fields[8].strip() if len(fields) > 8 else "", #3 + 'error-reason': fields[8].strip() if len(fields) > 9 else "", #3 + 'to-email': fields[9].strip() if len(fields) > 9 else "", #4 + 'error-plugin': fields[10].strip() if len(fields) > 10 else "", #5 + 'action1': fields[10].strip() if len(fields) > 10 else "", #5 + 'error-number' : fields[11].strip() if len(fields) > 11 else "", #6 + 'sender': fields[12].strip() if len(fields) > 12 else "", #7 + 'virus': fields[12].strip() if len(fields) > 12 else "", #7 + 'error-msg' :fields[13].strip() if len(fields) > 13 else "", #7 + 'spam-status': fields[13].strip() if len(fields) > 13 else "", #8 + 'error-result': fields[14].strip() if len(fields) > 14 else "",#8 # Add more fields as necessary } except: logging.error(f"error:len:{len(fields)}") - return_dict = {} + return_dict = create_empty_return() return return_dict +def safe_strip(lst, index): + if 0 <= index < len(lst): + value = lst[index] + if value is not None: + return value.strip() + return "" + + +def create_empty_return(): + # Return dictionary with all keys, values None + keys = [ + 'sme', 'qpsmtpd', 'id', 'action', 'logterse', 'ip', 'sendurl', 'sendurl1', + 'from-email', 'error-reason', 'to-email', 'error-plugin', 'action1', 'error-number', + 'sender', 'virus', 'error-msg', 'spam-status', 'error-result' + ] + return {key: "" for key in keys} + # def count_entries_by_hour(log_entries): # hourly_counts = defaultdict(int) # for entry in log_entries: diff --git a/smeserver-mailstats.spec b/smeserver-mailstats.spec index 84e1dab..72ba727 100644 --- a/smeserver-mailstats.spec +++ b/smeserver-mailstats.spec @@ -6,7 +6,7 @@ Summary: Daily mail statistics for SME Server %define name smeserver-mailstats Name: %{name} %define version 11.1 -%define release 2 +%define release 3 Version: %{version} Release: %{release}%{?dist} License: GPL @@ -35,6 +35,10 @@ A script that via cron.d e-mails mail statistics to admin on a daily basis. See http://www.contribs.org/bugzilla/show_bug.cgi?id=819 %changelog +* Mon Sep 01 2025 Brian Read 11.1-3.sme +- Sort out ASCII escape codes in return from journalctl API [SME: 13117] +- Add in Status enabled t default for mailstats DB [SME: 13118] + * Sun Apr 06 2025 Brian Read 11.1-2.sme - First build on Koji - and Add in SM2 panel [SME: 13116]