package main import ( "log" "strconv" "github.com/emersion/go-imap/v2/imapclient" "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" "github.com/zalando/go-keyring" "gorm.io/gorm" ) type MailAccountConnection struct { gorm.Model Email string `gorm:"uniqueIndex"` ImapServer string ImapPort int ImapUsername string SmtpServer string SmtpPort int SmtpUsername string Password string `gorm:"-:all"` ImapClient *imapclient.Client `gorm:"-:all"` SmtpClient *smtp.Client `gorm:"-:all"` SmtpSasl *sasl.Client `gorm:"-:all"` } var mailConnections = make(map[string]MailAccountConnection) func (app *App) GetMailConnections() ([]MailAccountConnection, error) { userContact := app.GetUserContact() connections := make([]MailAccountConnection, len(userContact.Emails)) for index, email := range userContact.Emails { if err := app.DB. Where("email = ?", email.Value). First(&connections[index]). Error; err != nil { connections[index].Email = email.Value connections[index].ImapUsername = email.Value connections[index].ImapPort = 993 connections[index].SmtpUsername = email.Value connections[index].SmtpPort = 465 continue } mailPassword, err := keyring.Get( app.GetMailPasswordKeyringService(), email.Value, ) if err != nil { continue } connections[index].Password = mailPassword if connections[index].Email != "" && connections[index].ImapServer != "" && connections[index].ImapPort != 0 && connections[index].ImapUsername != "" && connections[index].SmtpServer != "" && connections[index].SmtpPort != 0 && connections[index].SmtpUsername != "" && connections[index].Password != "" { mailConnections[email.Value] = connections[index] } } return connections, nil } func (app *App) SaveMailConnection(conn MailAccountConnection) error { keyring.Set( app.GetMailPasswordKeyringService(), conn.Email, conn.Password, ) return app.DB.Save(&conn).Error } func RemoveMailConnection(address string) { if conn, ok := mailConnections[address]; ok { if conn.ImapClient != nil { conn.ImapClient.Close() } if conn.SmtpClient != nil { conn.SmtpClient.Close() } delete(mailConnections, address) } } func (app *App) EstablishMailConnections() { _, _ = app.GetMailConnections() for key, conn := range mailConnections { log.Printf("Attempting to connect to %s...\n", conn.Email) var err error if conn.ImapClient != nil { conn.ImapClient.Close() } switch conn.ImapPort { case 143: conn.ImapClient, err = imapclient.DialStartTLS( conn.ImapServer+":"+strconv.Itoa(conn.ImapPort), nil, ) break case 993: conn.ImapClient, err = imapclient.DialTLS( conn.ImapServer+":"+strconv.Itoa(conn.ImapPort), nil, ) break default: conn.ImapClient, err = imapclient.DialTLS( conn.ImapServer+":"+strconv.Itoa(conn.ImapPort), nil, ) break } if err != nil { RemoveMailConnection(conn.Email) log.Print(err.Error()) continue } if err := conn.ImapClient.Login(conn.ImapUsername, conn.Password).Wait(); err != nil { RemoveMailConnection(conn.Email) log.Print(err.Error()) continue } if conn.SmtpClient != nil { conn.SmtpClient.Close() } switch conn.SmtpPort { case 25: conn.SmtpClient, err = smtp.Dial(conn.SmtpServer + ":" + strconv.Itoa(conn.SmtpPort)) break case 587: conn.SmtpClient, err = smtp.DialStartTLS( conn.SmtpServer+":"+strconv.Itoa(conn.SmtpPort), nil, ) break case 465: conn.SmtpClient, err = smtp.DialTLS( conn.SmtpServer+":"+strconv.Itoa(conn.SmtpPort), nil, ) break default: conn.SmtpClient, err = smtp.DialTLS( conn.SmtpServer+":"+strconv.Itoa(conn.SmtpPort), nil, ) break } if err != nil { RemoveMailConnection(conn.Email) log.Print(err.Error()) continue } auth := sasl.NewPlainClient("", conn.SmtpUsername, conn.Password) if err := conn.SmtpClient.Auth(auth); err != nil { RemoveMailConnection(conn.Email) log.Print(err.Error()) continue } conn.SmtpSasl = &auth mailConnections[key] = conn log.Printf("Successfully connected %s!\n", conn.Email) } } func CloseMailConnections() { for key, _ := range mailConnections { RemoveMailConnection(key) } }