package imap import ( "bytes" "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() var msgBuffer bytes.Buffer r = io.TeeReader(r, &msgBuffer) fileName, size, msgHeader, err := i.persistence.WriteBlobMessage(r) if err != nil { return fmt.Errorf("write blob: %w", err) } policyUser := policy.UserToStarlark(user, i.db) policyConn := policy.ConnToStarlark(conn) msgBytes := msgBuffer.Bytes() msg := &policy.Message{ OpenStream: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(msgBytes)), nil }, Size: size, } 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, msgHeader) 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 }