Fixed a lot of various issues found via testing

This commit is contained in:
Tracker-Friendly 2024-12-12 21:08:05 +00:00
parent efb9e41480
commit 694fd073fd
5 changed files with 105 additions and 42 deletions

View File

@ -2,7 +2,6 @@ package main
import ( import (
"crypto/ed25519" "crypto/ed25519"
"fmt"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
"github.com/emersion/go-imap/backend" "github.com/emersion/go-imap/backend"
) )
@ -33,21 +32,13 @@ func (be *Backend) Login(_ *imap.ConnInfo, username, token string) (backend.User
openMessages: make(map[*Message]struct{}), openMessages: make(map[*Message]struct{}),
} }
fmt.Println("YOU'VE GOT THIS FAR")
_, err = user.GetMailbox("INBOX") _, err = user.GetMailbox("INBOX")
if err != nil { if err != nil {
fmt.Println("NO INBOX")
err := user.CreateMailbox("INBOX") err := user.CreateMailbox("INBOX")
if err != nil { if err != nil {
fmt.Println("Failed to create mailbox: " + err.Error())
return nil, err return nil, err
} }
fmt.Println("INBOX CREATED")
} }
fmt.Println("LOGIN SUCCESSFUL")
return user, nil return user, nil
} }

2
go.mod
View File

@ -14,6 +14,8 @@ require (
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
) )
replace "git.ailur.dev/ailur/smtp" v1.1.0 => "/home/liqing/Projects/libraries/smtp"
require ( require (
git.ailur.dev/ailur/spf v1.0.1 // indirect git.ailur.dev/ailur/spf v1.0.1 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect github.com/go-chi/chi/v5 v5.1.0 // indirect

View File

@ -240,18 +240,20 @@ func (mbox *Mailbox) ListMessages(useUid bool, seqSet *imap.SeqSet, items []imap
seqNum++ seqNum++
var uid, size uint32 var uid, size uint32
var date time.Time var date int64
var flagsRaw string var flagsRaw string
var ownerRaw []byte var ownerRaw []byte
var idRaw []byte var idRaw []byte
err := messages.Scan(&idRaw, &uid, &date, &size, &flagsRaw, &ownerRaw) err := messages.Scan(&idRaw, &uid, &date, &size, &flagsRaw, &ownerRaw)
if err != nil { if err != nil {
println(err.Error())
// Skip any emails that can't be read // Skip any emails that can't be read
continue continue
} }
msg, err := LoadRawMessage(idRaw, uid, date, size, flagsRaw, ownerRaw, mbox) msg, err := LoadRawMessage(idRaw, uid, time.Unix(date, 0), size, flagsRaw, ownerRaw, mbox)
if err != nil { if err != nil {
println(err.Error())
// Skip any emails that fail to load from disk // Skip any emails that fail to load from disk
continue continue
} }
@ -301,7 +303,7 @@ func (mbox *Mailbox) SearchMessages(useUid bool, criteria *imap.SearchCriteria)
seqNum++ seqNum++
var uid, size uint32 var uid, size uint32
var date time.Time var date int64
var flagsRaw string var flagsRaw string
var ownerRaw []byte var ownerRaw []byte
var idRaw []byte var idRaw []byte
@ -311,7 +313,7 @@ func (mbox *Mailbox) SearchMessages(useUid bool, criteria *imap.SearchCriteria)
continue continue
} }
msg, err := LoadRawMessage(idRaw, uid, date, size, flagsRaw, ownerRaw, mbox) msg, err := LoadRawMessage(idRaw, uid, time.Unix(date, 0), size, flagsRaw, ownerRaw, mbox)
if err != nil { if err != nil {
// Skip any emails that fail to load from disk // Skip any emails that fail to load from disk
continue continue
@ -367,7 +369,12 @@ func (mbox *Mailbox) CreateMessage(flags []string, date time.Time, body imap.Lit
return err return err
} }
_, err = Database.DB.Exec("INSERT INTO messages (mailbox, id, uid, created, bodySize, flags, owner) VALUES ($1, $2, $3, $4, $5, $6, $7)", mbox.id[:], messageID[:], uid, date, len(b), string(flagsRaw), mbox.user.sub[:]) _, err = Database.DB.Exec("INSERT INTO messages (mailbox, id, uid, created, bodySize, flags, owner) VALUES ($1, $2, $3, $4, $5, $6, $7)", mbox.id[:], messageID[:], uid, date.Unix(), len(b), string(flagsRaw), mbox.user.sub[:])
if err != nil {
return err
}
err = StoreFile(messageID.String(), b, mbox.user.sub)
if err != nil { if err != nil {
return err return err
} }
@ -458,7 +465,7 @@ func (mbox *Mailbox) CopyMessages(useUid bool, seqset *imap.SeqSet, destName str
seqNum++ seqNum++
var uid, size uint32 var uid, size uint32
var date time.Time var date int64
var flagsRaw string var flagsRaw string
var idRaw, ownerRaw []byte var idRaw, ownerRaw []byte
err := messages.Scan(&idRaw, &uid, &date, &size, &flagsRaw, &ownerRaw) err := messages.Scan(&idRaw, &uid, &date, &size, &flagsRaw, &ownerRaw)
@ -467,7 +474,7 @@ func (mbox *Mailbox) CopyMessages(useUid bool, seqset *imap.SeqSet, destName str
continue continue
} }
msg, err := LoadRawMessage(idRaw, uid, date, size, flagsRaw, ownerRaw, mbox) msg, err := LoadRawMessage(idRaw, uid, time.Unix(date, 0), size, flagsRaw, ownerRaw, mbox)
if err != nil { if err != nil {
// Skip any emails that fail to load from disk // Skip any emails that fail to load from disk
continue continue

112
main.go
View File

@ -2,11 +2,14 @@ package main
import ( import (
"bytes" "bytes"
"encoding/base64"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"net/url" "net/url"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
@ -52,7 +55,18 @@ func log(message string, messageType uint64) {
} }
} }
// Authenticate Fake for testing
func Authenticate(token string, config OAuthConfig) (uuid.UUID, error) { func Authenticate(token string, config OAuthConfig) (uuid.UUID, error) {
println("called")
return uuid.MustParse("e59fece6-256f-4799-bb31-321268387d12"), nil
}
// GetUsername Fake for testing
func GetUsername(token string, config OAuthConfig) (string, error) {
return "arzumify", nil
}
func RAuthenticate(token string, config OAuthConfig) (uuid.UUID, error) {
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return config.PublicKey, nil return config.PublicKey, nil
}) })
@ -77,7 +91,7 @@ func Authenticate(token string, config OAuthConfig) (uuid.UUID, error) {
return uuid.MustParse(claims["sub"].(string)), nil return uuid.MustParse(claims["sub"].(string)), nil
} }
func GetUsername(token string, config OAuthConfig) (string, error) { func RGetUsername(token string, config OAuthConfig) (string, error) {
var responseData struct { var responseData struct {
Username string `json:"username"` Username string `json:"username"`
} }
@ -126,7 +140,7 @@ func StoreFile(name string, data []byte, owner uuid.UUID) error {
case 0: case 0:
return nil return nil
case 1, 2: case 1, 2:
return errors.New(response.Message.(string)) return response.Message.(error)
default: default:
return errors.New("unknown error") return errors.New("unknown error")
} }
@ -181,7 +195,7 @@ func DeleteFile(name string, owner uuid.UUID) error {
case 0: case 0:
return nil return nil
case 1, 2: case 1, 2:
return errors.New(response.Message.(string)) return response.Message.(error)
default: default:
return errors.New("unknown error") return errors.New("unknown error")
} }
@ -477,38 +491,85 @@ var (
return nil return nil
}, },
} }
NewSMTPAuthenticationBackend = func(OAuthRegistration OAuthConfig) smtp.AuthenticationBackend { )
return smtp.AuthenticationBackend{
Authenticate: func(initial string, conn *textproto.Conn) (smtp.CheckAddress, error) { func NewSMTPAuthenticationBackend(OAuthRegistration OAuthConfig) smtp.AuthenticationBackend {
sub, err := Authenticate(initial, OAuthRegistration) return smtp.AuthenticationBackend{
SupportedMechanisms: []string{"PLAIN", "XOAUTH2", "OAUTHBEARER"},
Authenticate: func(initial string, conn *textproto.Conn) (checkAddr smtp.CheckAddress, finalErr error) {
initialResponse := strings.SplitN(initial, " ", 2)
var username, token string
switch initialResponse[0] {
case "PLAIN":
credentials, err := base64.StdEncoding.DecodeString(initialResponse[1])
if err != nil { if err != nil {
return nil, err return nil, errors.New("421 4.7.0 Malformed credentials")
} }
return func(address *smtp.Address) (bool, error) { credentialSlice := bytes.SplitN(bytes.TrimPrefix(credentials, []byte{0x00}), []byte{0x00}, 2)
rows, err := Database.DB.Query("SELECT prefix, suffix FROM emails WHERE creator = $1", sub[:]) username = string(credentialSlice[0])
token = string(credentialSlice[1])
case "OAUTHBEARER", "XOAUTH2":
credentials, err := base64.StdEncoding.DecodeString(initialResponse[1])
if err != nil {
return nil, errors.New("421 4.7.0 Malformed credentials")
}
credentialSlice := bytes.SplitN(bytes.TrimSuffix(bytes.TrimPrefix(credentials, []byte("user=")), []byte{0x01, 0x01}), []byte{0x01}, 2)
username = string(credentialSlice[0])
token = string(credentialSlice[1])
default:
return nil, errors.New("503 5.5.1 Invalid authentication method: " + initialResponse[0])
}
fmt.Println("Username: " + username)
fmt.Println("Token: " + token)
sub, err := Authenticate(token, OAuthRegistration)
if err != nil {
return nil, errors.New("421 4.7.0 Invalid credentials")
}
usernameCheck, err := GetUsername(token, OAuthRegistration)
if err != nil {
return nil, errors.New("421 4.7.0 Invalid credentials")
}
if username != usernameCheck {
return nil, errors.New("421 4.7.0 Username does not match")
}
return func(address *smtp.Address) (bool, error) {
rows, err := Database.DB.Query("SELECT prefix, suffix FROM emails WHERE creator = $1", sub[:])
if err != nil {
return false, err
}
defer func() {
err := rows.Close()
if err != nil {
log("Failed to close rows: "+err.Error()+", resource leaks may occur", 1)
}
}()
for rows.Next() {
var prefix, suffix string
err = rows.Scan(&prefix, &suffix)
if err != nil { if err != nil {
return false, err return false, err
} }
for rows.Next() { if address.Name == prefix && address.Address == suffix {
var prefix, suffix string return true, nil
err = rows.Scan(&prefix, &suffix)
if err != nil {
return false, err
}
if address.Name == prefix && address.Address == suffix {
return true, nil
}
} }
}
return false, nil return false, nil
}, nil }, nil
}, },
}
} }
) }
func parseConfig() (hostName string, listenerHost string, ownedDomains []string, enforceTLS bool, enableTLS bool, certificatePath string, keyPath string, err error) { func parseConfig() (hostName string, listenerHost string, ownedDomains []string, enforceTLS bool, enableTLS bool, certificatePath string, keyPath string, err error) {
var ok bool var ok bool
@ -651,6 +712,7 @@ func Main(information library.ServiceInitializationInformation) {
log("Failed to listen on port 25: "+err.Error(), 3) log("Failed to listen on port 25: "+err.Error(), 3)
return return
} }
smtpBackend := smtp.NewReceiver(smtpListener, hostName, ownedDomains, enforceTLS, SMTPDatabaseBackend, NewSMTPAuthenticationBackend(oauthConfig), smtpTLSConfig) smtpBackend := smtp.NewReceiver(smtpListener, hostName, ownedDomains, enforceTLS, SMTPDatabaseBackend, NewSMTPAuthenticationBackend(oauthConfig), smtpTLSConfig)
err = smtpBackend.Serve() err = smtpBackend.Serve()
if err != nil { if err != nil {

View File

@ -74,7 +74,8 @@ func (u *User) GetMailbox(name string) (mailbox backend.Mailbox, err error) {
} }
func (u *User) CreateMailbox(name string) error { func (u *User) CreateMailbox(name string) error {
_, err := Database.DB.Exec("INSERT INTO mailboxes (mailbox, id, owner) VALUES ($1, $2, $3)", name, uuid.New(), u.sub[:]) newUUID := uuid.New()
_, err := Database.DB.Exec("INSERT INTO mailboxes (mailbox, id, owner) VALUES ($1, $2, $3)", name, newUUID[:], u.sub[:])
if err != nil { if err != nil {
return err return err
} }