import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.1"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { // Verify auth const authHeader = req.headers.get("Authorization"); if (!authHeader) { return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY")!; const supabase = createClient(supabaseUrl, supabaseAnonKey, { global: { headers: { Authorization: authHeader } }, }); const { data: { user }, error: userError } = await supabase.auth.getUser(); if (userError || !user) { return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const body = await req.json(); const { action, smtp_host, smtp_port, smtp_username, smtp_password, test_recipient } = body; if (!smtp_host || !smtp_port || !smtp_username || !smtp_password) { return new Response(JSON.stringify({ success: false, error: "Missing SMTP credentials" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } if (action === "test_connection") { try { const useSSL = Number(smtp_port) === 465; let conn: Deno.Conn | Deno.TlsConn; if (useSSL) { conn = await Deno.connectTls({ hostname: smtp_host, port: Number(smtp_port), }); } else { conn = await Deno.connect({ hostname: smtp_host, port: Number(smtp_port), }); } const read = async (): Promise => { const buf = new Uint8Array(4096); const n = await conn.read(buf); return new TextDecoder().decode(buf.subarray(0, n || 0)); }; const write = async (cmd: string) => { await conn.write(new TextEncoder().encode(cmd + "\r\n")); }; const greeting = await read(); if (!greeting.startsWith("220")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `Bad greeting: ${greeting.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } await write("EHLO test.local"); let ehloResponse = await read(); if (!useSSL && ehloResponse.includes("STARTTLS")) { await write("STARTTLS"); const tlsResp = await read(); if (!tlsResp.startsWith("220")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `STARTTLS rejected: ${tlsResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } conn = await Deno.startTls(conn as Deno.TcpConn, { hostname: smtp_host }); await write("EHLO test.local"); ehloResponse = await read(); } await write("QUIT"); conn.close(); return new Response( JSON.stringify({ success: true, message: `Connection successful to ${smtp_host}:${smtp_port}`, details: { greeting: greeting.trim(), ehlo: ehloResponse.trim(), secure: useSSL ? "implicit_tls" : ehloResponse.includes("STARTTLS") ? "starttls_available" : "plain_smtp", }, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (connError) { return new Response( JSON.stringify({ success: false, error: `Connection failed: ${connError.message}`, message: `Could not connect to ${smtp_host}:${smtp_port}. Please verify the host/port and SSL/TLS mode.`, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } } if (action === "send_test_email") { const recipient = test_recipient || user.email; if (!recipient) { return new Response(JSON.stringify({ success: false, error: "No recipient email available" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } try { const useSSL = Number(smtp_port) === 465; let conn: Deno.Conn; if (useSSL) { conn = await Deno.connectTls({ hostname: smtp_host, port: Number(smtp_port), }); } else { conn = await Deno.connect({ hostname: smtp_host, port: Number(smtp_port), }); } const read = async (): Promise => { const buf = new Uint8Array(4096); const n = await conn.read(buf); return new TextDecoder().decode(buf.subarray(0, n || 0)); }; const write = async (cmd: string) => { await conn.write(new TextEncoder().encode(cmd + "\r\n")); }; // Read greeting const greeting = await read(); if (!greeting.startsWith("220")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `Bad greeting: ${greeting.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // EHLO await write(`EHLO test.local`); let ehloResp = await read(); // STARTTLS for non-SSL ports if (!useSSL && ehloResp.includes("STARTTLS")) { await write("STARTTLS"); const tlsResp = await read(); if (tlsResp.startsWith("220")) { conn = await Deno.startTls(conn as Deno.TcpConn, { hostname: smtp_host }); await write(`EHLO test.local`); ehloResp = await read(); } } // AUTH LOGIN await write("AUTH LOGIN"); const authResp = await read(); if (!authResp.startsWith("334")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `Auth not supported: ${authResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // Send username (base64) await write(btoa(smtp_username)); const userResp = await read(); if (!userResp.startsWith("334")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `Username rejected: ${userResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // Send password (base64) await write(btoa(smtp_password)); const passResp = await read(); if (!passResp.startsWith("235")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `Authentication failed. Please check your username and password.` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // MAIL FROM await write(`MAIL FROM:<${smtp_username}>`); const fromResp = await read(); if (!fromResp.startsWith("250")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `MAIL FROM rejected: ${fromResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // RCPT TO await write(`RCPT TO:<${recipient}>`); const rcptResp = await read(); if (!rcptResp.startsWith("250")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `RCPT TO rejected: ${rcptResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // DATA await write("DATA"); const dataResp = await read(); if (!dataResp.startsWith("354")) { conn.close(); return new Response(JSON.stringify({ success: false, error: `DATA rejected: ${dataResp.trim()}` }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const now = new Date().toUTCString(); const emailContent = [ `From: ${smtp_username}`, `To: ${recipient}`, `Subject: SMTP Test Email`, `Date: ${now}`, `Content-Type: text/html; charset=utf-8`, ``, ``, `

SMTP Test Successful

`, `

This is a test email sent from your email server settings.

`, `

Server: ${smtp_host}:${smtp_port}

`, `

Username: ${smtp_username}

`, `

Sent at: ${now}

`, `
`, `

This email confirms your SMTP configuration is working correctly.

`, ``, `.`, ].join("\r\n"); await conn.write(new TextEncoder().encode(emailContent + "\r\n")); const sendResp = await read(); await write("QUIT"); conn.close(); const emailSent = sendResp.startsWith("250"); return new Response( JSON.stringify({ success: emailSent, message: emailSent ? `Test email sent successfully to ${recipient}` : `Email delivery uncertain: ${sendResp.trim()}`, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (sendError) { return new Response( JSON.stringify({ success: false, error: `Failed to send test email: ${sendError.message}`, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } } return new Response( JSON.stringify({ success: false, error: "Invalid action. Use 'test_connection' or 'send_test_email'." }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error) { return new Response( JSON.stringify({ success: false, error: error.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } });