Check in current version

This commit is contained in:
Kumi 2022-08-21 09:46:00 +00:00
commit dc15132da2
Signed by: kumi
GPG key ID: ECBCC9082395383F
8 changed files with 197 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.pyc
__pycache__/
settings.ini
venv/

0
classes/__init__.py Normal file
View file

54
classes/config.py Normal file
View file

@ -0,0 +1,54 @@
from configparser import ConfigParser
from json import loads
from pathlib import Path
import socket
from .vessel import Vessel
class Config:
@classmethod
def fromFile(cls, path):
parser = ConfigParser()
parser.read(path)
return cls(parser)
def __init__(self, config):
self._config = config
@property
def vessels(self):
out = list()
for section in filter(lambda x: x.startswith("Vessel "), self._config.sections()):
out.append(Vessel.fromConfig(self._config[section]))
return out
def getTempDir(self):
return Path(self._config["FILEMAILER"].get("TempDir", fallback="/tmp/filemailer/"))
def getMailServer(self):
return self._config["FILEMAILER"].get("Server", fallback="localhost")
def getMailPort(self):
return int(self._config["FILEMAILER"].get("Port", fallback=0))
def getMailSSL(self):
return bool(int(self._config["FILEMAILER"].get("SSL", fallback=0)))
def getMailUsername(self):
return self._config["FILEMAILER"].get("Username")
def getMailPassword(self):
return self._config["FILEMAILER"].get("Password")
def getMailSender(self):
return self._config["FILEMAILER"].get("Sender")
def getBCC(self):
return loads(self._config.get("FILEMAILER", "BCC", fallback="[]"))
def getHostname(self):
return self._config.get("FILEMAILER", "Hostname", fallback=socket.gethostname())

29
classes/smtp.py Normal file
View file

@ -0,0 +1,29 @@
import smtplib
class SMTP:
@classmethod
def fromConfig(cls, config):
return cls(config.getMailServer(), config.getMailUsername(), config.getMailPassword(), config.getMailSender(), config.getMailPort(), config.getMailSSL())
def __init__(self, host, username=None, password=None, sender=None, port=None, ssl=None):
port = 0 if port is None else port
ssl = bool(ssl)
smtpclass = smtplib.SMTP_SSL if ssl else smtplib.SMTP
self.connection = smtpclass(host, port)
self.connection.login(username, password)
self.sender = sender
def send_message(self, message, *args, **kwargs):
if not message.get("From"):
message["From"] = self.sender
elif message["From"] == "None":
message.replace_header("From", self.sender)
self.connection.send_message(message, *args, **kwargs)
def __del__(self):
self.connection.close()

52
classes/vessel.py Normal file
View file

@ -0,0 +1,52 @@
from configparser import SectionProxy
from typing import Optional, Union
from pathlib import Path
from paramiko.client import SSHClient
import time
class Vessel:
@classmethod
def fromConfig(cls, config: SectionProxy):
name = config.name
host = config["Host"]
username = config.get("Username")
sourcedir = config.get("SourceDir")
return cls(name, host, username, sourcedir)
def __init__(self, name: str, host: str, username: Optional[str] = None, sourcedir: Optional[Union[str, Path]] = None):
self.name = name
self.host = host
self.username = username or "filemailer"
self.sourcedir = str(sourcedir) if sourcedir else "/var/filemailer"
self._ssh = None
self._sftp = None
def connect(self):
self._ssh = SSHClient()
self._ssh.load_system_host_keys()
try:
self._ssh.connect(self.host, username=self.username)
self._sftp = self._ssh.open_sftp()
except Exception as e:
raise Exception(f"Could not connect to {self.name} ({self.host}): {e}")
def fetch(self, destination, retry=True):
try:
self._sftp.chdir(self.sourcedir)
files = self._sftp.listdir()
time.sleep(3) # Make sure write operations are complete
for f in files:
self._sftp.get(f, str(destination / f.split("/")[-1]))
self._sftp.remove(f)
except Exception as e:
if retry:
return self.fetch(destination, False)
raise

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
paramiko

13
settings.dist.ini Normal file
View file

@ -0,0 +1,13 @@
[FILEMAILER]
Hostname = FileMailerServer
TempDir = /tmp/FileMailer/
Sender = filemailer@example.com
Server = kumi.email
Username = filemailer@example.com
Password = Pa$$w0rd!
SSL = 1
Port = 465
BCC = ["stalker@example.com"]
[Vessel TestVessel]
Host = 10.11.12.13

44
worker.py Normal file
View file

@ -0,0 +1,44 @@
from json import loads
from email.parser import Parser as EmailParser
from email.utils import formatdate
from classes.config import Config
from classes.smtp import SMTP
config = Config.fromFile("settings.ini")
path = config.getTempDir()
path.mkdir(exist_ok=True)
smtp = SMTP.fromConfig(config)
for vessel in config.vessels:
try:
vessel.connect()
vessel.fetch(path)
except Exception as e:
print(f"SFTP operations failed on {vessel.host}: {e}")
for eml in sorted(filter(lambda x: x.with_suffix(".json").exists(), path.glob("*.eml")), key=lambda d: d.stat().st_mtime):
try:
with open(eml.resolve()) as contentfile:
content = contentfile.read()
with open(eml.with_suffix(".json").resolve()) as metafile:
meta = loads(metafile.read())
message = EmailParser().parsestr(content)
message.add_header("Received", f"by {config.getHostname()} (Kumi Systems FileMailer); {formatdate()}")
for bcc in config.getBCC():
if not bcc in meta["recipients"]:
meta["recipients"].append(bcc)
smtp.send_message(message, from_addr=meta["sender"], to_addrs=meta["recipients"])
eml.with_suffix(".json").unlink()
eml.unlink()
except Exception as e:
print(f"Could not process file {eml.resolve()}: {e}")