package imap import ( "context" "errors" "fmt" "io" "log/slog" "net" "time" "postern/internal/db" "postern/internal/model" "postern/internal/policy" ) func (i *Server) SubmitMessage(ctx context.Context, conn net.Conn, user model.User, r io.Reader) error { logger := slog.With("component", "internal_submission", "user_id", user.ID) internalTime := time.Now() fileName, size, parsedMsg, err := i.persistence.WriteBlobMessage(r) if err != nil { return fmt.Errorf("write blob: %w", err) } if parsedMsg == nil { return errors.New("parsed message is nil") } policyUser := policy.UserToStarlark(user, i.db) policyConn := policy.ConnToStarlark(conn) fromAddr, err := policy.ParseAddress(parsedMsg.Header.Get("From")) if err != nil { return model.ErrMissingFromHeader } toAddr, err := policy.ParseAddress(parsedMsg.Header.Get("To")) if err != nil { return errors.New("missing To header") } subject := parsedMsg.Header.Get("Subject") msgSize := size msg := policy.MessageContext{ HeaderFrom: fromAddr, HeaderTo: toAddr, Subject: subject, Headers: parsedMsg.Header, Size: msgSize, } destinationMailbox, err := i.policyEngine.OnMessageDeliver(policyConn, policyUser, msg) if destinationMailbox == "" || err != nil { logger.Warn("Policy script error, falling back to INBOX", "error", err) destinationMailbox = "INBOX" } logger.InfoContext(ctx, "delivery", "mailbox", destinationMailbox) // Create mailbox if not exists mailboxID, _, err := i.db.GetMailboxID(ctx, user.ID, destinationMailbox) if err != nil { if errors.Is(err, db.ErrMailboxNotFound) { err = i.db.CreateMailbox(ctx, user.ID, destinationMailbox) if err != nil { return fmt.Errorf("create on-demand mailbox: %w", err) } mailboxID, _, err = i.db.GetMailboxID(ctx, user.ID, destinationMailbox) if err != nil { return fmt.Errorf("get on-demand mailbox ID: %w", err) } err = i.db.Subscribe(ctx, user.ID, mailboxID, destinationMailbox) if err != nil { return fmt.Errorf("subscribe on-demand mailbox: %w", err) } } else { return fmt.Errorf("get mailbox ID: %w", err) } } _, err = i.db.AppendMessage(ctx, mailboxID, user.ID, fileName, size, internalTime.UTC().Format(time.RFC3339), nil, parsedMsg) if err != nil { return fmt.Errorf("append message: %w", err) } // Notify user t := i.mTracker.get(user.ID, mailboxID) if t != nil { mb, err := i.db.GetUserMailbox(ctx, user.ID, destinationMailbox) if err != nil { return fmt.Errorf("get user mailbox: %w", err) } t.QueueNumMessages(mb.NumMessages) logger.Info("EXISTS message sent to user", "user", user.ID, "mailbox", mb.Name, "num_messages", mb.NumMessages) } return nil }