From 08b49d3d75d120652695f48931af261cb7f3594c Mon Sep 17 00:00:00 2001 From: michael Date: Sun, 7 Sep 2025 21:25:25 +0200 Subject: [PATCH] security: sanitize internal infrastructure details from open source package - Remove production_test_sequence.sh (DAW-specific production tests) - Remove setup_env.sh (obsolete .env setup, replaced by furt.conf) - Sanitize test scripts: replace dragons-at-work.de with example.com - Sanitize API keys: replace dev keys with placeholder values - Remove hardcoded DAW fallbacks from http_server.lua and smtp.lua - Update .gitignore to exclude production-specific test files Tests remain functional for developers with example domains. All internal DAW infrastructure details removed from package. Closes #101 --- .gitignore | 1 + scripts/cleanup_debug.sh | 0 scripts/manual_mail_test.sh | 4 +- scripts/production_test_sequence.sh | 80 ---------------------- scripts/setup_env.sh | 101 ---------------------------- scripts/stress_test.sh | 32 ++++----- scripts/test_auth.sh | 12 ++-- scripts/test_modular.sh | 2 +- scripts/test_smtp.sh | 20 +++--- src/http_server.lua | 4 +- src/smtp.lua | 80 +++++++++++----------- 11 files changed, 77 insertions(+), 259 deletions(-) mode change 100644 => 100755 scripts/cleanup_debug.sh mode change 100644 => 100755 scripts/manual_mail_test.sh delete mode 100644 scripts/production_test_sequence.sh delete mode 100755 scripts/setup_env.sh mode change 100644 => 100755 scripts/test_modular.sh mode change 100644 => 100755 scripts/test_smtp.sh diff --git a/.gitignore b/.gitignore index 8dec80f..c67bf5b 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ config.production.lua config/furt.conf +scripts/production_test_sequence.sh diff --git a/scripts/cleanup_debug.sh b/scripts/cleanup_debug.sh old mode 100644 new mode 100755 diff --git a/scripts/manual_mail_test.sh b/scripts/manual_mail_test.sh old mode 100644 new mode 100755 index 3f8002f..6a1497c --- a/scripts/manual_mail_test.sh +++ b/scripts/manual_mail_test.sh @@ -4,11 +4,11 @@ echo "Testing SMTP with corrected JSON..." # Simple test without timestamp embedding -curl -X POST http://127.0.0.1:8080/v1/mail/send \ +curl -X POST http://127.0.0.1:7811/v1/mail/send \ -H "Content-Type: application/json" \ -d '{ "name": "Furt Test User", - "email": "michael@dragons-at-work.de", + "email": "admin@example.com", "subject": "Furt SMTP Test Success!", "message": "This is a test email from Furt Lua HTTP-Server. SMTP Integration working!" }' diff --git a/scripts/production_test_sequence.sh b/scripts/production_test_sequence.sh deleted file mode 100644 index 36a6455..0000000 --- a/scripts/production_test_sequence.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -# Production Test für api.dragons-at-work.de - -echo "Testing Production API via Apache Proxy" -echo "=======================================" - -# Test 1: HTTPS Health Check -echo "" -echo "[1] Testing HTTPS Health Check..." -https_health=$(curl -s https://api.dragons-at-work.de/health) -echo "HTTPS Response: $https_health" - -if echo "$https_health" | grep -q "healthy"; then - echo "[OK] HTTPS Proxy working" -else - echo "[ERROR] HTTPS Proxy failed" - exit 1 -fi - -# Test 2: SMTP Status via HTTPS -echo "" -echo "[2] Testing SMTP Configuration via HTTPS..." -if echo "$https_health" | grep -q '"smtp_configured":true'; then - echo "[OK] SMTP configured and accessible via HTTPS" -else - echo "[ERROR] SMTP not configured or not accessible" -fi - -# Test 3: CORS Headers -echo "" -echo "[3] Testing CORS Headers..." -cors_test=$(curl -s -I https://api.dragons-at-work.de/health | grep -i "access-control") -if [ -n "$cors_test" ]; then - echo "[OK] CORS headers present: $cors_test" -else - echo "[WARN] CORS headers missing - add to Apache config" -fi - -# Test 4: Production Mail Test -echo "" -echo "[4] Testing Production Mail via HTTPS..." -echo "WARNING: This sends real email via production API" -read -p "Continue with production mail test? (y/N): " -n 1 -r -echo - -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "Sending production test email..." - - prod_mail_response=$(curl -s -X POST https://api.dragons-at-work.de/v1/mail/send \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Production Test", - "email": "test@dragons-at-work.de", - "subject": "Production API Test - Apache Proxy Success!", - "message": "This email was sent via the production API at api.dragons-at-work.de through Apache proxy to Furt-Lua backend. HTTPS integration working!" - }') - - echo "Production Response: $prod_mail_response" - - if echo "$prod_mail_response" | grep -q '"success":true'; then - echo "[OK] PRODUCTION MAIL SENT VIA HTTPS!" - echo "Check admin@dragons-at-work.de for delivery confirmation" - else - echo "[ERROR] Production mail failed" - fi -else - echo "Skipping production mail test" -fi - -# Test 5: Security Headers -echo "" -echo "[5] Testing Security Headers..." -security_headers=$(curl -s -I https://api.dragons-at-work.de/health) -echo "Security Headers:" -echo "$security_headers" | grep -i "x-content-type-options\|x-frame-options\|strict-transport" - -echo "" -echo "Production Test Complete!" -echo "========================" -echo "Next: Hugo integration with https://api.dragons-at-work.de/v1/mail/send" \ No newline at end of file diff --git a/scripts/setup_env.sh b/scripts/setup_env.sh deleted file mode 100755 index 858436e..0000000 --- a/scripts/setup_env.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -# furt-lua/scripts/setup_env.sh -# Add SMTP environment variables to existing .env (non-destructive) - -set -e - -# Colors for output -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -echo -e "${GREEN}=== Furt SMTP Environment Setup ===${NC}" - -# Navigate to furt project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" -ENV_FILE="$PROJECT_ROOT/.env" - -echo -e "${YELLOW}Project root:${NC} $PROJECT_ROOT" -echo -e "${YELLOW}Environment file:${NC} $ENV_FILE" - -# Check if .env exists -if [ ! -f "$ENV_FILE" ]; then - echo -e "${YELLOW}Creating new .env file...${NC}" - cat > "$ENV_FILE" << 'EOF' -# Dragons@Work Project Environment Variables - -# Furt SMTP Configuration for mail.dragons-at-work.de -SMTP_HOST="mail.dragons-at-work.de" -SMTP_PORT="465" -SMTP_USERNAME="your_email@dragons-at-work.de" -SMTP_PASSWORD="your_smtp_password" -SMTP_FROM="noreply@dragons-at-work.de" -SMTP_TO="michael@dragons-at-work.de" -EOF - echo -e "${GREEN}[OK] Created new .env file${NC}" - echo -e "${YELLOW}[EDIT] Please edit:${NC} nano $ENV_FILE" - exit 0 -fi - -echo -e "${GREEN}[OK] Found existing .env file${NC}" - -# Check if SMTP variables already exist -smtp_username_exists=$(grep -c "^SMTP_USERNAME=" "$ENV_FILE" 2>/dev/null || echo "0") -smtp_password_exists=$(grep -c "^SMTP_PASSWORD=" "$ENV_FILE" 2>/dev/null || echo "0") - -if [ "$smtp_username_exists" -gt 0 ] && [ "$smtp_password_exists" -gt 0 ]; then - echo -e "${GREEN}[OK] SMTP variables already configured${NC}" - - # Load and show current values - source "$ENV_FILE" - echo -e "${YELLOW}Current SMTP User:${NC} ${SMTP_USERNAME:-NOT_SET}" - echo -e "${YELLOW}Current SMTP Password:${NC} ${SMTP_PASSWORD:+[CONFIGURED]}${SMTP_PASSWORD:-NOT_SET}" - - echo "" - echo -e "${YELLOW}To update SMTP settings:${NC} nano $ENV_FILE" - exit 0 -fi - -# Add missing SMTP variables -echo -e "${YELLOW}Adding SMTP configuration to existing .env...${NC}" - -# Add section header if not present -if ! grep -q "SMTP_" "$ENV_FILE" 2>/dev/null; then - echo "" >> "$ENV_FILE" - echo "# Furt SMTP Configuration for mail.dragons-at-work.de" >> "$ENV_FILE" -fi - -# Add username if missing -if [ "$smtp_username_exists" -eq 0 ]; then - echo "SMTP_HOST=\"mail.dragons-at-work.de\"" >> "$ENV_FILE" - echo "SMTP_PORT=\"465\"" >> "$ENV_FILE" - echo "SMTP_USERNAME=\"your_email@dragons-at-work.de\"" >> "$ENV_FILE" - echo -e "${GREEN}[OK] Added SMTP_HOST, SMTP_PORT, SMTP_USERNAME${NC}" -fi - -# Add password if missing -if [ "$smtp_password_exists" -eq 0 ]; then - echo "SMTP_PASSWORD=\"your_smtp_password\"" >> "$ENV_FILE" - echo "SMTP_FROM=\"noreply@dragons-at-work.de\"" >> "$ENV_FILE" - echo "SMTP_TO=\"michael@dragons-at-work.de\"" >> "$ENV_FILE" - echo -e "${GREEN}[OK] Added SMTP_PASSWORD, SMTP_FROM, SMTP_TO${NC}" -fi - -echo -e "${GREEN}[OK] SMTP configuration added to .env${NC}" -echo "" -echo -e "${YELLOW}Next steps:${NC}" -echo "1. Edit SMTP credentials: nano $ENV_FILE" -echo "2. Set your actual email@dragons-at-work.de in SMTP_USERNAME" -echo "3. Set your actual SMTP password in SMTP_PASSWORD" -echo "4. Test with: ./scripts/start.sh" - -echo "" -echo -e "${YELLOW}Current .env content:${NC}" -echo "===================" -cat "$ENV_FILE" -echo "===================" -echo "" -echo -e "${GREEN}Ready for SMTP testing!${NC}" - diff --git a/scripts/stress_test.sh b/scripts/stress_test.sh index 56be1bb..05c47ff 100755 --- a/scripts/stress_test.sh +++ b/scripts/stress_test.sh @@ -4,7 +4,7 @@ BASE_URL="http://127.0.0.1:8080" # Use correct API keys that match current .env -API_KEY="hugo-dev-key-change-in-production" +API_KEY="YOUR_API_KEY_HERE" echo "⚡ Furt API Stress Test" echo "======================" @@ -20,9 +20,9 @@ for i in {1..20}; do response=$(curl -s -w "%{http_code}" \ -H "X-API-Key: $API_KEY" \ "$BASE_URL/v1/auth/status") - + status=$(echo "$response" | tail -c 4) - + if [ "$status" == "200" ]; then rate_limit_remaining=$(echo "$response" | head -n -1 | jq -r '.rate_limit_remaining // "N/A"' 2>/dev/null) echo "Request $i: ✅ 200 OK (Rate limit remaining: $rate_limit_remaining)" @@ -33,7 +33,7 @@ for i in {1..20}; do else echo "Request $i: ❌ $status Error" fi - + # Small delay to prevent overwhelming sleep 0.1 done @@ -58,10 +58,10 @@ for i in {1..10}; do -H "X-API-Key: $API_KEY" \ "$BASE_URL/health") local_end=$(date +%s.%N) - + status=$(echo "$response" | tail -c 4) duration=$(echo "$local_end - $local_start" | bc -l) - + echo "Concurrent $i: Status $status, Duration ${duration}s" > "$temp_dir/result_$i" } & done @@ -85,18 +85,18 @@ mail_errors=0 for i in {1..5}; do start_time=$(date +%s.%N) - + response=$(curl -s -w "%{http_code}" \ -H "X-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d "{\"name\":\"Stress Test $i\",\"email\":\"test$i@example.com\",\"subject\":\"Performance Test\",\"message\":\"Load test message $i\"}" \ "$BASE_URL/v1/mail/send") - + end_time=$(date +%s.%N) duration=$(echo "$end_time - $start_time" | bc -l) - + status=$(echo "$response" | tail -c 4) - + if [ "$status" == "200" ]; then echo "Mail $i: ✅ 200 OK (${duration}s)" ((mail_success++)) @@ -104,7 +104,7 @@ for i in {1..5}; do echo "Mail $i: ❌ Status $status (${duration}s)" ((mail_errors++)) fi - + # Delay between mail sends to be nice to SMTP server sleep 1 done @@ -120,7 +120,7 @@ mixed_success=0 for i in {1..15}; do ((mixed_total++)) - + if [ $((i % 3)) -eq 0 ]; then # Every 3rd request: auth status endpoint="/v1/auth/status" @@ -128,20 +128,20 @@ for i in {1..15}; do # Other requests: health check endpoint="/health" fi - + response=$(curl -s -w "%{http_code}" \ -H "X-API-Key: $API_KEY" \ "$BASE_URL$endpoint") - + status=$(echo "$response" | tail -c 4) - + if [ "$status" == "200" ]; then echo "Mixed $i ($endpoint): ✅ 200 OK" ((mixed_success++)) else echo "Mixed $i ($endpoint): ❌ $status" fi - + sleep 0.2 done diff --git a/scripts/test_auth.sh b/scripts/test_auth.sh index fb892a1..007179c 100755 --- a/scripts/test_auth.sh +++ b/scripts/test_auth.sh @@ -3,8 +3,8 @@ # Test API-Key-Authentifizierung (ohne jq parse errors) BASE_URL="http://127.0.0.1:8080" -HUGO_API_KEY="hugo-dev-key-change-in-production" -ADMIN_API_KEY="admin-dev-key-change-in-production" +HUGO_API_KEY="YOUR_API_KEY_HERE" +ADMIN_API_KEY="YOUR_ADMIN_KEY_HERE" INVALID_API_KEY="invalid-key-should-fail" echo "🔐 Testing Furt API-Key Authentication" @@ -16,24 +16,24 @@ make_request() { local url="$2" local headers="$3" local data="$4" - + echo "Request: $method $url" if [ -n "$headers" ]; then echo "Headers: $headers" fi - + local response=$(curl -s $method \ ${headers:+-H "$headers"} \ ${data:+-d "$data"} \ -H "Content-Type: application/json" \ "$url") - + local status=$(curl -s -o /dev/null -w "%{http_code}" $method \ ${headers:+-H "$headers"} \ ${data:+-d "$data"} \ -H "Content-Type: application/json" \ "$url") - + echo "Status: $status" echo "Response: $response" | jq '.' 2>/dev/null || echo "$response" echo "" diff --git a/scripts/test_modular.sh b/scripts/test_modular.sh old mode 100644 new mode 100755 index 398aef6..85149fe --- a/scripts/test_modular.sh +++ b/scripts/test_modular.sh @@ -3,7 +3,7 @@ # Test der modularen Furt-Architektur BASE_URL="http://127.0.0.1:8080" -HUGO_API_KEY="hugo-dev-key-change-in-production" +HUGO_API_KEY="YOUR_API_KEY_HERE" echo "🧩 Testing Modular Furt Architecture" echo "====================================" diff --git a/scripts/test_smtp.sh b/scripts/test_smtp.sh old mode 100644 new mode 100755 index c014a52..205c0f1 --- a/scripts/test_smtp.sh +++ b/scripts/test_smtp.sh @@ -36,7 +36,7 @@ else echo "[ERROR] Validation failed" fi -# Test 3: Invalid Email Format +# Test 3: Invalid Email Format echo "" echo "[3] Testing email validation..." email_validation_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \ @@ -54,36 +54,36 @@ fi # Test 4: Valid Mail Request (REAL SMTP TEST) echo "" echo "[4] Testing REAL mail sending..." -echo "WARNING: This will send a real email to michael@dragons-at-work.de" +echo "WARNING: This will send a real email to admin@example.com" read -p "Continue with real mail test? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Sending real test email..." - + mail_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \ -H "Content-Type: application/json" \ -d '{ "name": "Furt Test User", - "email": "test@dragons-at-work.de", + "email": "test@example.com", "subject": "Furt SMTP Test - Week 2 Success!", "message": "This is a test email from the Furt Lua HTTP-Server.\n\nSMTP Integration is working!\n\nTimestamp: '$(date)'\nServer: furt-lua v1.0" }') - + echo "Response: $mail_response" - + # Check for success if echo "$mail_response" | grep -q '"success":true'; then echo "[OK] MAIL SENT SUCCESSFULLY!" - echo "Check michael@dragons-at-work.de inbox" - + echo "Check admin@example.com inbox" + # Extract request ID request_id=$(echo "$mail_response" | grep -o '"request_id":"[^"]*"' | cut -d'"' -f4) echo "Request ID: $request_id" else echo "[ERROR] Mail sending failed" echo "Check server logs and SMTP credentials" - + # Show error details if echo "$mail_response" | grep -q "error"; then error_msg=$(echo "$mail_response" | grep -o '"error":"[^"]*"' | cut -d'"' -f4) @@ -126,7 +126,7 @@ echo "Performance: ${duration_ms}ms" echo "" echo "Week 2 Challenge Status:" echo " SMTP Integration: COMPLETE" -echo " Environment Variables: CHECK .env" +echo " Environment Variables: CHECK .env" echo " Native Lua Implementation: DONE" echo " Production Ready: READY FOR TESTING" diff --git a/src/http_server.lua b/src/http_server.lua index 08fbfd5..c9b85b2 100644 --- a/src/http_server.lua +++ b/src/http_server.lua @@ -91,9 +91,7 @@ end function FurtServer:add_cors_headers(request) local allowed_origins = config.cors and config.cors.allowed_origins or { "http://localhost:1313", - "http://127.0.0.1:1313", - "https://dragons-at-work.de", - "https://www.dragons-at-work.de" + "http://127.0.0.1:1313" } -- Check if request has Origin header diff --git a/src/smtp.lua b/src/smtp.lua index b419a75..ab59d17 100644 --- a/src/smtp.lua +++ b/src/smtp.lua @@ -19,7 +19,7 @@ function SSLCompat:detect_ssl_library() return "luaossl", ssl_lib end end - + -- Try luasec local success, ssl_lib = pcall(require, "ssl") if success and ssl_lib then @@ -28,23 +28,23 @@ function SSLCompat:detect_ssl_library() return "luasec", ssl_lib end end - + return nil, "No compatible SSL library found (tried luaossl, luasec)" end function SSLCompat:wrap_socket(sock, options) local ssl_type, ssl_lib = self:detect_ssl_library() - + if not ssl_type then return nil, ssl_lib -- ssl_lib contains error message end - + if ssl_type == "luaossl" then return self:wrap_luaossl(sock, options, ssl_lib) elseif ssl_type == "luasec" then return self:wrap_luasec(sock, options, ssl_lib) end - + return nil, "Unknown SSL library type: " .. ssl_type end @@ -55,18 +55,18 @@ function SSLCompat:wrap_luaossl(sock, options, ssl_lib) protocol = "tlsv1_2", verify = "none" -- For self-signed certs }) - + if not ssl_sock then return nil, "luaossl wrap failed: " .. (err or "unknown error") end - + -- luaossl typically does handshake automatically, but explicit is safer local success, err = pcall(function() return ssl_sock:dohandshake() end) if not success then -- Some luaossl versions don't need explicit handshake -- Continue if dohandshake doesn't exist end - + return ssl_sock, nil end @@ -78,28 +78,28 @@ function SSLCompat:wrap_luasec(sock, options, ssl_lib) verify = "none", options = "all" }) - + if not ssl_sock then return nil, "luasec wrap failed: " .. (err or "unknown error") end - + -- luasec requires explicit handshake local success, err = ssl_sock:dohandshake() if not success then return nil, "luasec handshake failed: " .. (err or "unknown error") end - + return ssl_sock, nil end -- Create SMTP instance function SMTP:new(config) local instance = { - server = config.smtp_server or "mail.dragons-at-work.de", - port = config.smtp_port or 465, + server = config.smtp_server, + port = config.smtp_port, username = config.username, password = config.password, - from_address = config.from_address or "noreply@dragons-at-work.de", + from_address = config.from_address, use_ssl = config.use_ssl or true, debug = config.debug or false, ssl_compat = SSLCompat @@ -133,7 +133,7 @@ function SMTP:send_command(sock, command, expected_code) if self.debug then print("SMTP CMD: " .. (command or ""):gsub("\r\n", "\\r\\n")) end - + -- Only send if command is not nil (for server greeting, command is nil) if command then local success, err = sock:send(command .. "\r\n") @@ -141,16 +141,16 @@ function SMTP:send_command(sock, command, expected_code) return false, "Failed to send command: " .. (err or "unknown error") end end - + local response, err = sock:receive() if not response then return false, "Failed to receive response: " .. (err or "unknown error") end - + if self.debug then print("SMTP RSP: " .. response) end - + -- Handle multi-line responses (like EHLO) local full_response = response while response:match("^%d%d%d%-") do @@ -163,12 +163,12 @@ function SMTP:send_command(sock, command, expected_code) end full_response = full_response .. "\n" .. response end - + local code = response:match("^(%d+)") if expected_code and code ~= tostring(expected_code) then return false, "Unexpected response: " .. full_response end - + return true, full_response end @@ -179,38 +179,38 @@ function SMTP:connect() if not sock then return false, "Failed to create socket: " .. (err or "unknown error") end - + -- Set timeout sock:settimeout(30) - + -- Connect to server local success, err = sock:connect(self.server, self.port) if not success then return false, "Failed to connect to " .. self.server .. ":" .. self.port .. " - " .. (err or "unknown error") end - + -- Wrap with SSL for port 465 using compatibility layer if self.use_ssl and self.port == 465 then local ssl_sock, err = self.ssl_compat:wrap_socket(sock, { mode = "client", protocol = "tlsv1_2" }) - + if not ssl_sock then sock:close() return false, "Failed to establish SSL connection: " .. (err or "unknown error") end - + sock = ssl_sock end - + -- Read server greeting local success, response = self:send_command(sock, nil, 220) if not success then sock:close() return false, "SMTP server greeting failed: " .. response end - + return sock, nil end @@ -219,64 +219,64 @@ function SMTP:send_email(to_address, subject, message, from_name) if not self.username or not self.password then return false, "SMTP username or password not configured" end - + -- Connect to server local sock, err = self:connect() if not sock then return false, err end - + local function cleanup_and_fail(error_msg) sock:close() return false, error_msg end - + -- EHLO command local success, response = self:send_command(sock, "EHLO furt-lua", 250) if not success then return cleanup_and_fail("EHLO failed: " .. response) end - + -- AUTH LOGIN local success, response = self:send_command(sock, "AUTH LOGIN", 334) if not success then return cleanup_and_fail("AUTH LOGIN failed: " .. response) end - + -- Send username (base64 encoded) local username_b64 = self:base64_encode(self.username) local success, response = self:send_command(sock, username_b64, 334) if not success then return cleanup_and_fail("Username authentication failed: " .. response) end - + -- Send password (base64 encoded) local password_b64 = self:base64_encode(self.password) local success, response = self:send_command(sock, password_b64, 235) if not success then return cleanup_and_fail("Password authentication failed: " .. response) end - + -- MAIL FROM local mail_from = "MAIL FROM:<" .. self.from_address .. ">" local success, response = self:send_command(sock, mail_from, 250) if not success then return cleanup_and_fail("MAIL FROM failed: " .. response) end - + -- RCPT TO local rcpt_to = "RCPT TO:<" .. to_address .. ">" local success, response = self:send_command(sock, rcpt_to, 250) if not success then return cleanup_and_fail("RCPT TO failed: " .. response) end - + -- DATA command local success, response = self:send_command(sock, "DATA", 354) if not success then return cleanup_and_fail("DATA command failed: " .. response) end - + -- Build email message local display_name = from_name or "Furt Contact Form" local email_content = string.format( @@ -295,17 +295,17 @@ function SMTP:send_email(to_address, subject, message, from_name) os.date("%a, %d %b %Y %H:%M:%S %z"), message ) - + -- Send email content local success, response = self:send_command(sock, email_content, 250) if not success then return cleanup_and_fail("Email sending failed: " .. response) end - + -- QUIT self:send_command(sock, "QUIT", 221) sock:close() - + return true, "Email sent successfully" end