diff --git a/.env.template b/.env.template index f0a1595..94d8a7a 100644 --- a/.env.template +++ b/.env.template @@ -17,12 +17,13 @@ SOURCE_IMAP_SERVER=wp123456.mail.server-he.de SOURCE_IMAP_PORT=993 # SOURCE_EMAIL: Your source email address +# Required if SOURCE_USERNAME is empty, optional if username is provided # Example: user@yourdomain.de SOURCE_EMAIL=user@example-domain.de -# SOURCE_USERNAME: Login username (if different from email) +# SOURCE_USERNAME: Login username (alternative to email) +# Required if SOURCE_EMAIL is empty, optional if email is provided # Some providers use separate username for login (e.g. hosteurope123, mail_user) -# Leave empty if login uses email address # Host Europe: often uses email account name or specific username SOURCE_USERNAME= @@ -47,12 +48,13 @@ DEST_IMAP_SERVER=mail.securehost.de DEST_IMAP_PORT=993 # DEST_EMAIL: Your destination email address +# Required if DEST_USERNAME is empty, optional if username is provided # Example: user@securehost.de DEST_EMAIL=user@securehost.de -# DEST_USERNAME: Login username for destination (if different from email) +# DEST_USERNAME: Login username for destination (alternative to email) +# Required if DEST_EMAIL is empty, optional if email is provided # Some hosting providers use separate username (e.g. user123, cpanel_user) -# Leave empty if login uses email address # Securehost.de: typically uses email or specific username DEST_USERNAME= @@ -129,16 +131,26 @@ TEMP_DOWNLOAD_DIR=./temp_emails # AUTHENTICATION EXAMPLES # ============================================================================ -# Email-based login (most common): +# Option 1: Email-based login (most common): # SOURCE_EMAIL=user@domain.de # SOURCE_USERNAME= # → Script tries: user@domain.de -# Username-based login: +# Option 2: Username-based login: # SOURCE_EMAIL=user@domain.de # SOURCE_USERNAME=mail_user123 # → Script tries: mail_user123, fallback to user@domain.de +# Option 3: Username-only (email optional): +# SOURCE_EMAIL= +# SOURCE_USERNAME=mail_user123 +# → Script tries: mail_user123 only + +# Option 4: Email-only (username optional): +# SOURCE_EMAIL=user@domain.de +# SOURCE_USERNAME= +# → Script tries: user@domain.de only + # Host Europe specific: # SOURCE_USERNAME=email_account_name (from Host Europe KIS) # SOURCE_USERNAME= (if using Easy-Mail-Login) diff --git a/README.md b/README.md index a7bdab1..da4bb6e 100644 --- a/README.md +++ b/README.md @@ -27,23 +27,37 @@ A Python script to migrate emails from one IMAP account to another with flexible ## Authentication Methods -The script supports two authentication methods and automatically tries both: +The script supports flexible authentication with four possible configurations: -### Method 1: Email-based Login (Most Common) +### Option 1: Email-based Login (Most Common) ```env SOURCE_EMAIL=user@domain.de SOURCE_USERNAME= ``` **Used by**: Gmail, Yahoo, Outlook, most modern providers -### Method 2: Username-based Login +### Option 2: Username + Email (Dual Method) ```env SOURCE_EMAIL=user@domain.de SOURCE_USERNAME=mail_user123 ``` -**Used by**: Some hosting providers, older email systems, cPanel hosting +**Used by**: Some hosting providers - tries username first, falls back to email -The script automatically tries username first (if provided), then falls back to email authentication. +### Option 3: Username-only Login +```env +SOURCE_EMAIL= +SOURCE_USERNAME=mail_user123 +``` +**Used by**: Legacy systems, some cPanel hosting where email is unknown + +### Option 4: Email-only Login +```env +SOURCE_EMAIL=user@domain.de +SOURCE_USERNAME= +``` +**Used by**: When you're certain email authentication is the only method + +**Requirements**: You must provide either SOURCE_EMAIL or SOURCE_USERNAME (or both). Same applies to destination settings. ## Import Folder Options @@ -235,7 +249,10 @@ Errors encountered: 0 ### Common Issues **Authentication Failed** -- Check if username is required (try both with and without `SOURCE_USERNAME`) +- Try different authentication combinations: + - Email only: Set `SOURCE_USERNAME=` (empty) + - Username only: Set `SOURCE_EMAIL=` (empty) + - Both: Provide both values for fallback - Verify server address from your hosting provider - For Host Europe: Get exact server name from KIS panel - For Gmail/Yahoo: Use app passwords, not regular passwords @@ -248,7 +265,10 @@ Errors encountered: 0 - Verify server address and port **Username vs Email Login Issues** -- Try leaving `SOURCE_USERNAME` empty to use email login +- Try all combinations: + 1. Email only: `SOURCE_EMAIL=user@domain.de`, `SOURCE_USERNAME=` + 2. Username only: `SOURCE_EMAIL=`, `SOURCE_USERNAME=username123` + 3. Both (fallback): Provide both values - For Host Europe: Check if Easy-Mail-Login is enabled - Contact your hosting provider for authentication method - Check provider documentation for login format diff --git a/email_migration.py b/email_migration.py index d9621f5..5c27261 100755 --- a/email_migration.py +++ b/email_migration.py @@ -33,14 +33,18 @@ class IMAPConnection: def __init__(self, server, port, email_addr, username, password, use_ssl=True, timeout=60): self.server = server self.port = port - self.email = email_addr - self.username = username + self.email = email_addr or "" + self.username = username or "" self.password = password self.use_ssl = use_ssl self.timeout = timeout self.connection = None self.logger = logging.getLogger(__name__) + # Validate that we have at least one authentication method + if not self.email.strip() and not self.username.strip(): + raise ValueError("Either email or username must be provided for authentication") + def connect(self): try: if self.use_ssl: @@ -65,7 +69,7 @@ class IMAPConnection: self.logger.debug(f"Username login failed for {self.username}: {username_error}") # If username login failed or no username provided, try email - if not login_success: + if not login_success and self.email.strip(): try: self.connection.login(self.email, self.password) login_user = self.email @@ -75,7 +79,12 @@ class IMAPConnection: self.logger.error(f"Email login failed for {self.email}: {email_error}") if not login_success: - self.logger.error(f"Failed to authenticate with {self.server} using both username and email") + available_methods = [] + if self.username.strip(): + available_methods.append("username") + if self.email.strip(): + available_methods.append("email") + self.logger.error(f"Failed to authenticate with {self.server} using {' and '.join(available_methods)}") return False return True @@ -187,7 +196,7 @@ class EmailMigrator: self.source = IMAPConnection( config['SOURCE_IMAP_SERVER'], int(config['SOURCE_IMAP_PORT']), - config['SOURCE_EMAIL'], + config.get('SOURCE_EMAIL', ''), config.get('SOURCE_USERNAME', ''), config['SOURCE_PASSWORD'], config.get('SOURCE_IMAP_USE_SSL', 'True').lower() == 'true', @@ -197,7 +206,7 @@ class EmailMigrator: self.destination = IMAPConnection( config['DEST_IMAP_SERVER'], int(config['DEST_IMAP_PORT']), - config['DEST_EMAIL'], + config.get('DEST_EMAIL', ''), config.get('DEST_USERNAME', ''), config['DEST_PASSWORD'], config.get('DEST_IMAP_USE_SSL', 'True').lower() == 'true', @@ -358,8 +367,9 @@ def main(): print(f"Error loading .env file: {e}") exit(1) - required_vars = ['SOURCE_IMAP_SERVER', 'SOURCE_EMAIL', 'SOURCE_PASSWORD', - 'DEST_IMAP_SERVER', 'DEST_EMAIL', 'DEST_PASSWORD'] + # Basic required variables + required_vars = ['SOURCE_IMAP_SERVER', 'SOURCE_PASSWORD', + 'DEST_IMAP_SERVER', 'DEST_PASSWORD'] missing_vars = [var for var in required_vars if not config.get(var)] if missing_vars: @@ -367,6 +377,20 @@ def main(): print("Please check your .env file.") exit(1) + # Validate authentication methods + source_email = config.get('SOURCE_EMAIL', '').strip() + source_username = config.get('SOURCE_USERNAME', '').strip() + dest_email = config.get('DEST_EMAIL', '').strip() + dest_username = config.get('DEST_USERNAME', '').strip() + + if not source_email and not source_username: + print("Error: Either SOURCE_EMAIL or SOURCE_USERNAME must be provided for source account") + exit(1) + + if not dest_email and not dest_username: + print("Error: Either DEST_EMAIL or DEST_USERNAME must be provided for destination account") + exit(1) + migrator = EmailMigrator(config) try: