diff --git a/README.md b/README.md index 1a57408..b5c16b4 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,33 @@ Does not support different accounts. ## Install +### Archiver + 1. Create a venv 2. `pip install -r requirements.txt` 3. `cp config.yml.sample config.yml` 4. Edit `config.yml` and configure your login info. -5. `python3 run.py` +5. `python3 archiver.py` -A sample systemd service file is included. +### Viewer + +1. `sudo mkdir /etc/secrets` +2. `sudo chown -R root:root /etc/secrets` +3. Generate your password: + ```shell + echo -n "" | base64 + ``` +4. Edit `/etc/secrets/imapviewer` with this content: + ```shell + SQLITE3_DB= + HTTP_AUTH_PASS= + ``` +5. `sudo chmod 600 /etc/secrets/imapviewer` +6. Reload and start the service. + +Sample systemd services file is included. ## To Do - [ ] Fix subject decoding. Some character sets aren't detected correctly. -- [ ] Sync Gmail categories as folders. - [ ] In the viewer, format the raw email to something viewable. diff --git a/requirements.txt b/requirements.txt index c6f410d..2732923 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ chardet==5.2.0 humanize==4.9.0 mmh3==4.1.0 flask==3.0.2 -python-magic==0.4.27 \ No newline at end of file +python-magic==0.4.27 +flask_httpauth==4.8.0 \ No newline at end of file diff --git a/server.py b/server.py index acab8cd..11af8de 100644 --- a/server.py +++ b/server.py @@ -1,3 +1,4 @@ +import base64 import json import os import sqlite3 @@ -6,11 +7,31 @@ from pathlib import Path import magic from flask import Flask, render_template, send_from_directory +from flask_httpauth import HTTPBasicAuth app = Flask(__name__) if not os.environ.get('SQLITE3_DB'): raise Exception('SQLITE3_DB not set.') +if not os.environ.get('HTTP_AUTH_PASS'): + raise Exception('HTTP_AUTH_PASS not set.') + +try: + user_password = base64.b64decode(os.environ.get('HTTP_AUTH_PASS')).decode().replace('\n', '') +except UnicodeDecodeError: + raise Exception('Cannot decode password. Is it base64 encoded?') + +auth = HTTPBasicAuth() +users = { + 'admin': user_password, +} + + +@auth.get_password +def get_pw(username): + if username in users: + return users.get(username) + return None def get_db_connection(): @@ -24,6 +45,7 @@ def dict_from_row(row): @app.route('/') +@auth.login_required def index(): conn = get_db_connection() folders = conn.execute('SELECT name, table_name FROM folders_mapping').fetchall() @@ -36,6 +58,7 @@ def index(): @app.route('/folder/') +@auth.login_required def folder(table_name): conn = get_db_connection() emails = conn.execute(f'SELECT * FROM {table_name} ORDER BY timestamp DESC').fetchall() @@ -47,6 +70,7 @@ def folder(table_name): @app.route('/email//') +@auth.login_required def email(table_name, id): conn = get_db_connection() email = conn.execute(f'SELECT * FROM {table_name} WHERE id = ?', (id,)).fetchone() @@ -58,6 +82,7 @@ def email(table_name, id): @app.route('/attachments/') +@auth.login_required def download_file(filename): mimetype = magic.from_file(str(Path('attachments', filename)), mime=True) return send_from_directory('attachments', filename, mimetype=mimetype) diff --git a/imaparchiver.service b/systemd/imaparchiver.service similarity index 100% rename from imaparchiver.service rename to systemd/imaparchiver.service diff --git a/systemd/imaparchiver.timer b/systemd/imaparchiver.timer new file mode 100644 index 0000000..2ac7389 --- /dev/null +++ b/systemd/imaparchiver.timer @@ -0,0 +1,9 @@ +[Unit] +Description=IMAP archiver service timer + +[Timer] +OnBootSec=15min +OnUnitActiveSec=12h + +[Install] +WantedBy=timers.target diff --git a/systemd/imapviewer.service b/systemd/imapviewer.service new file mode 100644 index 0000000..e2963a3 --- /dev/null +++ b/systemd/imapviewer.service @@ -0,0 +1,15 @@ +[Unit] +Description=IMAP archiver viewer +Wants=network-online.target +After=network-online.target + +[Service] +User=emailsync +Group=emailsync +EnvironmentFile=/etc/secrets/imapviewer +ExecStart=/srv/email/imap-archiver/venv/bin/python3 /srv/email/imap-archiver/server.py +SyslogIdentifier=imap-viewer +Restart=on-failure + +[Install] +WantedBy=multi-user.target