From 410e297d0e429243e43d801c588bd489c37b7859 Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Sat, 16 May 2026 13:21:17 -0400 Subject: [PATCH 1/2] allow SMTP without credentials for anonymous relays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMTP delivery method was gated on both smtpUsername and smtpPassword being non-empty, which meant you couldn't use pop against an internal mail relay or a university SMTP server that accepts anonymous sends. The underlying go-simple-mail library already does the right thing when Username is empty (skips auth in the AUTH switch), so the gate was the only thing blocking this. Switch the trigger to "any SMTP setting is set" — host or user or password. The from-defaults-to-username step only runs when there is a username to fall back to. Closes #136 Signed-off-by: Charlie Tonneslan --- main.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index bbb3423..c3c7586 100644 --- a/main.go +++ b/main.go @@ -74,15 +74,20 @@ var rootCmd = &cobra.Command{ Short: "Send emails from your terminal", Long: `Pop is a tool for sending emails from your terminal.`, RunE: func(cmd *cobra.Command, _ []string) error { + // SMTP is "enabled" if the user pointed at a server, with or + // without credentials. Local/relay setups (universities, internal + // mailrelays) often run without auth, see #136. + smtpEnabled := smtpHost != "" || smtpUsername != "" || smtpPassword != "" + var deliveryMethod DeliveryMethod switch { - case resendAPIKey != "" && smtpUsername != "" && smtpPassword != "": + case resendAPIKey != "" && smtpEnabled: deliveryMethod = Unknown case resendAPIKey != "": deliveryMethod = Resend - case smtpUsername != "" && smtpPassword != "": + case smtpEnabled: deliveryMethod = SMTP - if from == "" { + if from == "" && smtpUsername != "" { from = smtpUsername } } From 90422721feb82c78344dae36f0bef9f3a15f2fe4 Mon Sep 17 00:00:00 2001 From: Amolith Date: Thu, 16 Jul 2026 21:39:54 -0600 Subject: [PATCH 2/2] smtp: validate creds Treat SMTP as configured only when a host or username is present. Return a config error when a password's supplied without a username because SMTP auth can't use a password on its own. --- main.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index b4cd44f..43028fd 100644 --- a/main.go +++ b/main.go @@ -107,10 +107,18 @@ non-interactively on the CLI by providing all required flags: See "pop skill" for a full skill definition for AI agents.`, RunE: func(cmd *cobra.Command, _ []string) error { - // SMTP is "enabled" if the user pointed at a server, with or - // without credentials. Local/relay setups (universities, internal - // mailrelays) often run without auth, see #136. - smtpEnabled := smtpHost != "" || smtpUsername != "" || smtpPassword != "" + // We'll use this to print to stderr and downsample colors on the way, + // if needed. + errWriter := colorprofile.NewWriter(os.Stderr, os.Environ()) + + if smtpPassword != "" && smtpUsername == "" { + err := errors.New("SMTP password provided without an SMTP username") + cmd.SilenceUsage = true + cmd.SilenceErrors = true + _, _ = fmt.Fprintf(errWriter, "\n %s %s\n\n", errorHeaderStyle.String(), err) + return err + } + smtpEnabled := smtpHost != "" || smtpUsername != "" var deliveryMethod DeliveryMethod switch { @@ -162,10 +170,6 @@ See "pop skill" for a full skill definition for AI agents.`, } } - // We'll use this to print to stderr and downsample colors on the way, - // if needed. - errWriter := colorprofile.NewWriter(os.Stderr, os.Environ()) - { const gap = " "