diff --git a/email_tunnel.py b/email_tunnel.py index 3e640dd..2727b0f 100755 --- a/email_tunnel.py +++ b/email_tunnel.py @@ -206,7 +206,12 @@ def save_last_uid(state_file: str, uid: int) -> None: def connect_imap(config: Config) -> imaplib.IMAP4: # Proton Bridge usually uses STARTTLS on 1143. If you configured SSL-only, switch to IMAP4_SSL. logger.info("Connecting to IMAP %s:%s ...", config.imap_host, config.imap_port) - imap = imaplib.IMAP4(config.imap_host, config.imap_port) + try: + imap = imaplib.IMAP4(config.imap_host, config.imap_port) + except (ConnectionRefusedError, OSError) as exc: + raise FatalConfigError( + f"IMAP connect failed to {config.imap_host}:{config.imap_port}: {exc}" + ) from exc # Upgrade to TLS (verification can be disabled for local Proton Bridge). ssl_context = ssl.create_default_context() @@ -216,7 +221,10 @@ def connect_imap(config: Config) -> imaplib.IMAP4: imap.starttls(ssl_context=ssl_context) logger.info("Logging in to IMAP ...") - imap.login(config.imap_user, config.imap_password) + try: + imap.login(config.imap_user, config.imap_password) + except imaplib.IMAP4.error as exc: + raise FatalConfigError(f"IMAP login failed: {exc}") from exc logger.debug("Listing available IMAP folders...") typ, folders = imap.list() diff --git a/email_tunnel.service b/email_tunnel.service new file mode 100644 index 0000000..4a66a82 --- /dev/null +++ b/email_tunnel.service @@ -0,0 +1,17 @@ +[Unit] +Description=Email Tunnel (Proton Bridge to SMTP) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=email-tunnel +Group=email-tunnel +WorkingDirectory=/opt/email-tunnel +ExecStart=/usr/bin/python3 email_tunnel.py +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/email-tunnel/.env + +[Install] +WantedBy=multi-user.target diff --git a/install_email_tunnel.sh b/install_email_tunnel.sh new file mode 100755 index 0000000..9ab6f8e --- /dev/null +++ b/install_email_tunnel.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# +# Installation script for email_tunnel systemd service +# +# This script will: +# 1. Verify prerequisites +# 2. Copy service file to systemd directory with correct paths/user +# 3. Enable and start the service +# 4. Show status and usage information + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Get script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "======================================" +echo "email_tunnel Service Installer" +echo "======================================" +echo "" + +# Check if running as root +if [ "$EUID" -eq 0 ]; then + echo -e "${RED}Error: Do not run this script as root or with sudo${NC}" + echo "The script will request sudo privileges when needed." + exit 1 +fi + +# Verify required files exist +echo "Checking required files..." +REQUIRED_FILES=( + "$SCRIPT_DIR/email_tunnel.py" + "$SCRIPT_DIR/.env" + "$SCRIPT_DIR/email_tunnel.service" +) + +for file in "${REQUIRED_FILES[@]}"; do + if [ ! -f "$file" ]; then + echo -e "${RED}Error: Required file not found: $file${NC}" + exit 1 + fi +done +echo -e "${GREEN}✓ All required files found${NC}" +echo "" + +# Check for required commands +echo "Checking dependencies..." +MISSING_DEPS=() +for cmd in python3 systemctl; do + if ! command -v "$cmd" &> /dev/null; then + MISSING_DEPS+=("$cmd") + fi +done + +if [ ${#MISSING_DEPS[@]} -gt 0 ]; then + echo -e "${RED}Error: Missing required dependencies: ${MISSING_DEPS[*]}${NC}" + echo "Please install them and try again." + exit 1 +fi +echo -e "${GREEN}✓ All dependencies installed${NC}" +echo "" + +# Verify .env file has required variables +echo "Validating .env configuration..." +REQUIRED_VARS=(IMAP_IP IMAP_PORT IMAP_USER IMAP_PASSWORD IMAP_FOLDER SMTP_PORT SMTP_IP) +MISSING_ENV=() +for var in "${REQUIRED_VARS[@]}"; do + if ! grep -q "^${var}=" "$SCRIPT_DIR/.env"; then + MISSING_ENV+=("$var") + fi +done + +if [ ${#MISSING_ENV[@]} -gt 0 ]; then + echo -e "${YELLOW}Warning: .env may be missing: ${MISSING_ENV[*]}${NC}" + read -p "Continue anyway? (y/N) " -n 1 -r + echo "" + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi +else + echo -e "${GREEN}✓ .env configuration looks good${NC}" +fi +echo "" + +# Test script syntax +echo "Testing email_tunnel.py byte-compile..." +if python3 -m py_compile "$SCRIPT_DIR/email_tunnel.py"; then + echo -e "${GREEN}✓ Byte-compile succeeded${NC}" +else + echo -e "${RED}Error: Byte-compile failed${NC}" + exit 1 +fi +echo "" + +# Check if service is already installed +SERVICE_NAME="email_tunnel.service" +if systemctl list-unit-files | grep -q "$SERVICE_NAME"; then + echo -e "${YELLOW}Warning: $SERVICE_NAME is already installed${NC}" + read -p "Reinstall? (y/N) " -n 1 -r + echo "" + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 0 + fi + echo "Stopping existing service..." + sudo systemctl stop "$SERVICE_NAME" 2>/dev/null || true + sudo systemctl disable "$SERVICE_NAME" 2>/dev/null || true +fi + +# Prepare service file with correct paths +echo "Preparing service file..." +TEMP_SERVICE=$(mktemp) +sed "s|/opt/email-tunnel|$SCRIPT_DIR|g" "$SCRIPT_DIR/email_tunnel.service" > "$TEMP_SERVICE" + +# Update user/group in service file +CURRENT_USER=$(whoami) +CURRENT_GROUP=$(id -gn) +sed -i "s|User=email-tunnel|User=$CURRENT_USER|g" "$TEMP_SERVICE" +sed -i "s|Group=email-tunnel|Group=$CURRENT_GROUP|g" "$TEMP_SERVICE" + +# Install service file +echo "Installing systemd service file..." +sudo cp "$TEMP_SERVICE" /etc/systemd/system/$SERVICE_NAME +rm "$TEMP_SERVICE" +sudo chmod 644 /etc/systemd/system/$SERVICE_NAME +echo -e "${GREEN}✓ Service file installed${NC}" +echo "" + +# Reload systemd +echo "Reloading systemd daemon..." +sudo systemctl daemon-reload +echo -e "${GREEN}✓ Systemd reloaded${NC}" +echo "" + +# Enable and start service +echo "Enabling and starting $SERVICE_NAME..." +sudo systemctl enable "$SERVICE_NAME" +sudo systemctl start "$SERVICE_NAME" +echo -e "${GREEN}✓ Service enabled and started${NC}" +echo "" + +# Show status +echo "======================================" +echo "Installation Complete!" +echo "======================================" +echo "" +echo "Service Status:" +sudo systemctl status "$SERVICE_NAME" --no-pager -l || true +echo "" + +echo -e "${GREEN}email_tunnel is now running!${NC}" +echo "" +echo "Useful commands:" +echo " View status: systemctl status $SERVICE_NAME" +echo " View logs: ./logs_email_tunnel.sh -f" +echo " Restart service: sudo systemctl restart $SERVICE_NAME" +echo " Stop service: sudo systemctl stop $SERVICE_NAME" +echo " Uninstall: ./uninstall_email_tunnel.sh" +echo "" diff --git a/logs_email_tunnel.sh b/logs_email_tunnel.sh new file mode 100644 index 0000000..74c4822 --- /dev/null +++ b/logs_email_tunnel.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# +# Log viewer for email_tunnel systemd service + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +SERVICE_NAME="email_tunnel.service" + +# Check if systemctl is available +if ! command -v systemctl &> /dev/null; then + echo -e "${RED}Error: systemctl not found${NC}" + echo "This script requires systemd." + exit 1 +fi + +# Check if journalctl is available +if ! command -v journalctl &> /dev/null; then + echo -e "${RED}Error: journalctl not found${NC}" + echo "This script requires journalctl." + exit 1 +fi + +# Function to show help +show_help() { + cat << EOF +${GREEN}email_tunnel Log Viewer${NC} + +Usage: $0 [OPTION] + +Options: + -f, --follow Follow logs in real-time (like tail -f) + -n, --lines NUMBER Show last N lines (default: 50) + -t, --today Show logs from today only + -h, --hour Show logs from the last hour + -b, --boot Show logs since last boot + -a, --all Show all logs (no limit) + -s, --status Show service status + -e, --errors Show only error messages + -v, --verbose Show verbose output (all log levels) + --help Show this help message + +Examples: + $0 Show last 50 log lines + $0 -f Follow logs in real-time + $0 -n 100 Show last 100 lines + $0 -t Show today's logs + $0 -e Show only errors + $0 -s Show service status + +EOF +} + +# Function to show status +show_status() { + echo -e "${BLUE}=== Service Status ===${NC}" + systemctl status "$SERVICE_NAME" --no-pager -l || true + echo "" + echo -e "${BLUE}=== Recent Runs ===${NC}" + journalctl -u "$SERVICE_NAME" -n 5 --no-pager -o short-precise +} + +# Parse command line arguments +FOLLOW=false +LINES=50 +SINCE="" +PRIORITY="" +SHOW_ALL=false +SHOW_STATUS=false + +if [ $# -eq 0 ]; then + : +else + while [[ $# -gt 0 ]]; do + case $1 in + -f|--follow) + FOLLOW=true + shift + ;; + -n|--lines) + LINES="$2" + shift 2 + ;; + -t|--today) + SINCE="today" + shift + ;; + -h|--hour) + SINCE="1 hour ago" + shift + ;; + -b|--boot) + SINCE="boot" + shift + ;; + -a|--all) + SHOW_ALL=true + shift + ;; + -s|--status) + SHOW_STATUS=true + shift + ;; + -e|--errors) + PRIORITY="err" + shift + ;; + -v|--verbose) + PRIORITY="" + shift + ;; + --help) + show_help + exit 0 + ;; + *) + echo -e "${RED}Error: Unknown option: $1${NC}" + echo "Use --help for usage information" + exit 1 + ;; + esac + done +fi + +# Check if service exists +if ! systemctl list-unit-files | grep -q "$SERVICE_NAME"; then + echo -e "${YELLOW}Warning: $SERVICE_NAME is not installed${NC}" + echo "Install the service first with: ./install_email_tunnel.sh" + exit 1 +fi + +# Show status if requested +if [ "$SHOW_STATUS" = true ]; then + show_status + exit 0 +fi + +# Build journalctl command +CMD="journalctl -u $SERVICE_NAME" + +# Add follow flag +if [ "$FOLLOW" = true ]; then + CMD="$CMD -f" +fi + +# Add line limit +if [ "$SHOW_ALL" = false ] && [ "$FOLLOW" = false ]; then + CMD="$CMD -n $LINES" +fi + +# Add time filter +if [ -n "$SINCE" ]; then + if [ "$SINCE" = "boot" ]; then + CMD="$CMD -b" + else + CMD="$CMD --since \"$SINCE\"" + fi +fi + +# Add priority filter +if [ -n "$PRIORITY" ]; then + CMD="$CMD -p $PRIORITY" +fi + +# Add no-pager for non-follow mode +if [ "$FOLLOW" = false ]; then + CMD="$CMD --no-pager" +fi + +# Show what we're doing +if [ "$FOLLOW" = true ]; then + echo -e "${GREEN}Following logs for $SERVICE_NAME...${NC}" + echo "Press Ctrl+C to stop" + echo "" +elif [ -n "$SINCE" ]; then + echo -e "${GREEN}Showing logs since $SINCE...${NC}" + echo "" +elif [ "$SHOW_ALL" = true ]; then + echo -e "${GREEN}Showing all logs for $SERVICE_NAME...${NC}" + echo "" +else + echo -e "${GREEN}Showing last $LINES log lines for $SERVICE_NAME...${NC}" + echo "" +fi + +# Execute the command +eval "$CMD" diff --git a/uninstall_email_tunnel.sh b/uninstall_email_tunnel.sh new file mode 100644 index 0000000..f4ae646 --- /dev/null +++ b/uninstall_email_tunnel.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# +# Uninstallation script for email_tunnel systemd service + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo "======================================" +echo "email_tunnel Service Uninstaller" +echo "======================================" +echo "" + +# Check if running as root +if [ "$EUID" -eq 0 ]; then + echo -e "${RED}Error: Do not run this script as root or with sudo${NC}" + echo "The script will request sudo privileges when needed." + exit 1 +fi + +SERVICE_NAME="email_tunnel.service" + +# Check if systemctl is available +if ! command -v systemctl &> /dev/null; then + echo -e "${RED}Error: systemctl not found${NC}" + echo "This script requires systemd." + exit 1 +fi + +# Check if service is installed +if ! systemctl list-unit-files | grep -q "$SERVICE_NAME"; then + echo -e "${YELLOW}$SERVICE_NAME is not installed${NC}" + echo "Nothing to uninstall." + exit 0 +fi + +# Confirm uninstallation +echo -e "${YELLOW}This will remove the $SERVICE_NAME systemd service.${NC}" +echo "Your script and configuration will NOT be deleted." +echo "" +read -p "Continue with uninstallation? (y/N) " -n 1 -r +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Uninstallation cancelled." + exit 0 +fi +echo "" + +# Stop the service if running +echo "Stopping $SERVICE_NAME..." +if systemctl is-active --quiet "$SERVICE_NAME"; then + sudo systemctl stop "$SERVICE_NAME" + echo -e "${GREEN}✓ Service stopped${NC}" +else + echo "Service is not running" +fi +echo "" + +# Disable the service +echo "Disabling $SERVICE_NAME..." +if systemctl is-enabled --quiet "$SERVICE_NAME"; then + sudo systemctl disable "$SERVICE_NAME" + echo -e "${GREEN}✓ Service disabled${NC}" +else + echo "Service was not enabled" +fi +echo "" + +# Remove service file +echo "Removing service file..." +if [ -f /etc/systemd/system/$SERVICE_NAME ]; then + sudo rm /etc/systemd/system/$SERVICE_NAME + echo -e "${GREEN}✓ Removed $SERVICE_NAME${NC}" +else + echo -e "${YELLOW}No service file found to remove${NC}" +fi +echo "" + +# Reload systemd +echo "Reloading systemd daemon..." +sudo systemctl daemon-reload +echo -e "${GREEN}✓ Systemd reloaded${NC}" +echo "" + +# Reset failed state if exists +sudo systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true + +# Verify uninstallation +echo "Verifying uninstallation..." +if systemctl list-unit-files | grep -q "email_tunnel"; then + echo -e "${RED}Warning: Some email_tunnel units still found${NC}" + systemctl list-unit-files | grep email_tunnel +else + echo -e "${GREEN}✓ Service unit removed${NC}" +fi +echo "" + +echo "======================================" +echo "Uninstallation Complete!" +echo "======================================" +echo "" +echo -e "${GREEN}The email_tunnel systemd service has been removed.${NC}" +echo "" +echo "Your files are still available:" +echo " - email_tunnel.py (relay script)" +echo " - .env (configuration)" +echo " - email_tunnel.service (service template)" +echo "" +echo "You can reinstall with: ./install_email_tunnel.sh"