2024-05-28 20:28:13 +02:00
|
|
|
#
|
|
|
|
# Mailstats.py
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# This script provides daily SpamFilter statistics.
|
|
|
|
#
|
|
|
|
# Re-written in python from Mailstats.pl (Perl) to conform to SME11 / Postfix / qpsmtpd log formats
|
|
|
|
# and html output added
|
|
|
|
#
|
|
|
|
import datetime
|
2024-05-29 11:15:23 +02:00
|
|
|
import sys
|
|
|
|
from chameleon import PageTemplateFile,PageTemplate
|
|
|
|
import pkg_resources
|
|
|
|
|
|
|
|
Mailstats_version = '1.2'
|
2024-05-28 20:28:13 +02:00
|
|
|
|
|
|
|
def truncate_microseconds(timestamp):
|
|
|
|
# Split timestamp into main part and microseconds
|
|
|
|
main_part, microseconds = timestamp.split('.')
|
|
|
|
# Truncate the last three digits of the microseconds
|
|
|
|
truncated_microseconds = microseconds[:-3]
|
|
|
|
# Combine the main part and truncated microseconds
|
|
|
|
truncated_timestamp = f"{main_part}.{truncated_microseconds}"
|
|
|
|
# Remove the microseconds completely if they exist
|
|
|
|
return truncated_timestamp.split('.')[0]
|
|
|
|
|
|
|
|
def filter_yesterdays_entries(log_entries):
|
|
|
|
# Determine yesterday's date
|
|
|
|
yesterday = (datetime.datetime.now() - datetime.timedelta(days=1)).date()
|
|
|
|
|
|
|
|
# Filter entries for yesterday's date
|
|
|
|
yesterday_entries = []
|
|
|
|
for timestamp, data in log_entries:
|
|
|
|
truncated_timestamp = truncate_microseconds(timestamp)
|
|
|
|
entry_date = datetime.datetime.strptime(truncated_timestamp, '%Y-%m-%d %H:%M:%S').date()
|
|
|
|
if entry_date == yesterday:
|
|
|
|
parsed_data = parse_data(data)
|
|
|
|
yesterday_entries.append((truncated_timestamp, parsed_data))
|
|
|
|
|
|
|
|
return yesterday_entries
|
|
|
|
|
|
|
|
def read_and_filter_yesterday_log(file_path):
|
|
|
|
# Read the file and split each line into a dictionary
|
|
|
|
log_entries = []
|
|
|
|
with open(file_path, 'r') as file:
|
|
|
|
for line in file:
|
2024-05-29 11:15:23 +02:00
|
|
|
if '`' in line:
|
|
|
|
parts = line.split(' ')
|
|
|
|
if parts:
|
|
|
|
# Combine parts to form the complete timestamp
|
|
|
|
timestamp = ' '.join(parts[:2])
|
|
|
|
data = ' '.join(parts[2:]) # The rest of the line after date and time
|
|
|
|
log_entries.append((timestamp, data))
|
2024-05-28 20:28:13 +02:00
|
|
|
|
|
|
|
# Filter the entries to keep only those from yesterday
|
|
|
|
filtered_entries = filter_yesterdays_entries(log_entries)
|
|
|
|
|
|
|
|
# Sort the filtered log entries based on the truncated timestamp
|
|
|
|
sorted_entries = sorted(filtered_entries, key=lambda x: datetime.datetime.strptime(x[0], '%Y-%m-%d %H:%M:%S'))
|
|
|
|
|
|
|
|
# Create a dictionary
|
|
|
|
sorted_dict = {entry[0]: entry[1] for entry in sorted_entries}
|
|
|
|
|
|
|
|
return sorted_dict
|
|
|
|
|
|
|
|
def parse_data(data):
|
|
|
|
# Split data string into parts and map to named fields.
|
|
|
|
# Adjust the field names and parsing logic according to your data format.
|
2024-05-29 11:15:23 +02:00
|
|
|
# Split at the backtick - before it fields split at space, after, fields split at tab
|
2024-05-28 20:28:13 +02:00
|
|
|
parts = data.split('`')
|
2024-05-29 11:15:23 +02:00
|
|
|
#print(parts[0],parts[1])
|
|
|
|
fields1 = parts[0].strip().split() if len(parts) > 0 else []
|
|
|
|
fields2 = parts[1].split('\t') if len(parts) > 1 else []
|
|
|
|
# then merge them
|
|
|
|
fields = fields1 + fields2
|
|
|
|
# if fields[8] != 'queued':
|
|
|
|
# i = 0
|
|
|
|
# print(f"len:{len(fields)}")
|
|
|
|
# for part in fields:
|
|
|
|
# print(f"{i}: {part}")
|
|
|
|
# i = i +1
|
|
|
|
# quit()
|
|
|
|
# and mapping:
|
|
|
|
try:
|
|
|
|
return_dict = {
|
|
|
|
'id': fields[0] if len(fields) > 0 else None,
|
|
|
|
'action': fields[1] if len(fields) > 1 else None,
|
|
|
|
'logterse': fields[2] if len(fields) > 2 else None,
|
|
|
|
'ip': fields[3] if len(fields) > 3 else None,
|
|
|
|
'sendurl': fields[4] if len(fields) > 4 else None,
|
|
|
|
'sendurl1': fields[5] if len(fields) > 5 else None,
|
|
|
|
'from-email': fields[6] if len(fields) > 6 else None,
|
|
|
|
'error-reason': fields[6] if len(fields) > 6 else None,
|
|
|
|
'to-email': fields[7] if len(fields) > 7 else None,
|
|
|
|
'error-plugin': fields[8] if len(fields) > 8 else None,
|
|
|
|
'action1': fields[8] if len(fields) > 8 else None,
|
|
|
|
'error-number' : fields[9] if len(fields) > 9 else None,
|
|
|
|
'sender': fields[10] if len(fields) > 10 else None,
|
|
|
|
'error-msg' :fields[10] if len(fields) > 10 else None,
|
|
|
|
'spam-status': fields[11] if len(fields) > 11 else None,
|
|
|
|
'error-result': fields[11] if len(fields) > 11 else None,
|
|
|
|
# Add more fields as necessary
|
|
|
|
}
|
|
|
|
except:
|
|
|
|
#print(f"error:len:{len(fields)}")
|
|
|
|
return_dict = {}
|
|
|
|
return return_dict
|
2024-05-28 20:28:13 +02:00
|
|
|
|
2024-05-29 11:15:23 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
chameleon_version = pkg_resources.get_distribution("Chameleon").version
|
|
|
|
except pkg_resources.DistributionNotFound:
|
|
|
|
chameleon_version = "Version information not available"
|
|
|
|
python_version = sys.version
|
|
|
|
python_version = python_version[:8]
|
|
|
|
current_datetime = datetime.datetime.now()
|
|
|
|
formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M")
|
|
|
|
hello_string = "Mailstats version:"+Mailstats_version+" Chameleon version:"+chameleon_version+" On Python:"+python_version+" at "+formatted_datetime
|
|
|
|
print(hello_string)
|
|
|
|
sorted_log_dict = read_and_filter_yesterday_log('/home/brianr/SME11Build/GITFiles/smecontribs/smeserver-mailstats/current.log')
|
|
|
|
#print(sorted_log_dict)
|
|
|
|
i = 1
|
|
|
|
for timestamp, data in sorted_log_dict.items():
|
|
|
|
if data['action'] == '(deny)':
|
|
|
|
error = data['error-plugin']
|
|
|
|
msg = data['error-msg']
|
|
|
|
else:
|
|
|
|
error = ""
|
|
|
|
msg = ""
|
|
|
|
print(f"{i}: {timestamp} IP = {data['ip']} Result:{data['action']} {error} {msg}" )
|
|
|
|
i = i + 1
|
2024-05-28 20:28:13 +02:00
|
|
|
|
|
|
|
|