Files
SMTP-Downloader/smtp-downloader.sh

190 lines
5.3 KiB
Bash

import imaplib
import email
from email.header import decode_header
import os
from pathlib import Path
import paramiko
# =========================
# EMAIL CONFIG
# =========================
IMAP_SERVER = "outlook.office365.com"
EMAIL_ACCOUNT = "mailbox@company.com"
PASSWORD = "your_password"
MAIL_FOLDERS = ["Inbox", "Orders"]
# =========================
# SFTP CONFIG
# =========================
SFTP_HOST = "sftp.company.com"
SFTP_PORT = 22
SFTP_USERNAME = "sftpuser"
SSH_KEY_PATH = "/home/youruser/.ssh/id_rsa"
SSH_KEY_PASSPHRASE = None
# =========================
# RULES
# =========================
RULES = [
{
"from_contains": "supplier1@company.com",
"to_contains": "orders@yourcompany.com",
"remote_path": "/incoming/orders"
},
{
"from_contains": "reports@vendor.com",
"to_contains": "reports@yourcompany.com",
"remote_path": "/incoming/reports"
}
]
TEMP_DOWNLOAD = "/tmp/mail_attachments"
# =========================
# HELPERS
# =========================
def ensure_folder(path):
Path(path).mkdir(parents=True, exist_ok=True)
def clean_subject(subject):
if not subject:
return "No Subject"
parts = decode_header(subject)
decoded_subject = ""
for part, encoding in parts:
if isinstance(part, bytes):
decoded_subject += part.decode(encoding or "utf-8", errors="ignore")
else:
decoded_subject += part
return decoded_subject
def decode_filename(filename):
if not filename:
return "unknown"
parts = decode_header(filename)
decoded_name = ""
for part, encoding in parts:
if isinstance(part, bytes):
decoded_name += part.decode(encoding or "utf-8", errors="ignore")
else:
decoded_name += part
return decoded_name
# =========================
# SFTP CONNECTION
# =========================
def create_sftp_connection():
try:
key = paramiko.RSAKey.from_private_key_file(
SSH_KEY_PATH,
password=SSH_KEY_PASSPHRASE
)
transport = paramiko.Transport((SFTP_HOST, SFTP_PORT))
transport.connect(username=SFTP_USERNAME, pkey=key)
sftp = paramiko.SFTPClient.from_transport(transport)
return sftp, transport
except Exception as e:
print(f"SFTP connection failed: {e}")
return None, None
# =========================
# UPLOAD FILE
# =========================
def upload_to_sftp(local_file, remote_folder):
sftp, transport = create_sftp_connection()
if not sftp:
print("Skipping upload due to SFTP connection error")
return
try:
filename = os.path.basename(local_file)
remote_path = f"{remote_folder}/{filename}"
print(f"Uploading {filename} -> {remote_path}")
sftp.put(local_file, remote_path)
print("Upload successful")
except Exception as e:
print(f"Failed to upload {filename}: {e}")
finally:
sftp.close()
transport.close()
# =========================
# PROCESS EMAIL
# =========================
def process_email(msg):
from_addr = msg.get("From", "").lower()
to_addr = msg.get("To", "").lower()
subject = clean_subject(msg.get("Subject"))
print("\n====================")
print(f"FROM: {from_addr}")
print(f"TO: {to_addr}")
print(f"SUBJECT: {subject}")
matching_rule = None
for rule in RULES:
if rule["from_contains"].lower() in from_addr and rule["to_contains"].lower() in to_addr:
matching_rule = rule
break
if not matching_rule:
print("No matching rule")
return
remote_folder = matching_rule["remote_path"]
for part in msg.walk():
content_disposition = str(part.get("Content-Disposition"))
if "attachment" not in content_disposition.lower():
continue
filename = decode_filename(part.get_filename())
ensure_folder(TEMP_DOWNLOAD)
local_path = os.path.join(TEMP_DOWNLOAD, filename)
with open(local_path, "wb") as f:
f.write(part.get_payload(decode=True))
print(f"Saved attachment locally: {local_path}")
upload_to_sftp(local_path, remote_folder)
# =========================
# MAIN
# =========================
def main():
try:
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
mail.login(EMAIL_ACCOUNT, PASSWORD)
print("Connected to mailbox")
except Exception as e:
print(f"Failed to connect/login: {e}")
return
for folder in MAIL_FOLDERS:
print(f"\nChecking folder: {folder}")
status, _ = mail.select(folder)
if status.decode() != "OK":
print(f"Cannot access folder: {folder}")
continue
status, messages = mail.search(None, "UNSEEN")
if status.decode() != "OK":
continue
email_ids = messages[0].split()
print(f"Found {len(email_ids)} unread emails")
for email_id in email_ids:
status, msg_data = mail.fetch(email_id, "(RFC822)")
if status.decode() != "OK":
continue
raw_email = msg_data[0][1]
msg = email.message_from_bytes(raw_email)
process_email(msg)
mail.store(email_id, '+FLAGS', '\\Seen')
mail.logout()
print("\nFinished")
if __name__ == "__main__":
main()