mtp

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2024 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OkResponse    = "250 OK"
	OkResponseFmt = "250 OK %s"
)
View Source
const (
	// Dunno I don't know what to do or I don't want to decide,
	// let the next person in the chain make that decision
	// this should be the default answer, which does not confirm anything,
	// it only informs that we are moving to the next stage
	Dunno Decision = "dunno"
	// Accept we finish the entire operation and confirm; other mechanisms after this are not important
	// (they will not be checked) if you want to confirm the correctness of the operation,
	// you should return dunno accept only finally
	Accept = "accept"
	// Reject rejection, we do not proceed
	Reject = "reject"
)
View Source
const (
	DefaultMaxErrorPerConnection = 5
	DefaultMaxConnections        = 1000
	DefaultAddress               = "localhost:1025"

	// DefaultProtocolTimeoutSec How many seconds do we wait for a command to be issued from the client
	DefaultProtocolTimeoutSec = 8

	// DefaultDataStartReceivingTimeoutSec How many seconds do we wait for the start of data transmission
	// (just after receiving the data command
	DefaultDataStartReceivingTimeoutSec = 10

	// DefaultDataReceivingTimeoutSec How many seconds do we wait for data after the data command
	DefaultDataReceivingTimeoutSec = 10 // 5 * 60
)
View Source
const (
	DefaultListenerNameFormat              = "Super %s"
	DefaultListenerVersion                 = "1.0.0"
	DefaultListenerMaxMessageSizeInMB      = 25
	DefaultListenerMaxMessageSizeInBytes   = DefaultListenerMaxMessageSizeInMB * (1024 * 1024 * 1024)
	DefaultListenerMaxRecipientsPerMessage = 100
)
View Source
const (
	FromField       = "from"
	ToField         = "to"
	MessageUidField = "mid"
	// to the public?
	// safe to pass on to an outsider (user police, etc.)
	ForPublicField = "pub"
	// action name
	ActionNameField = "action"
	// if the value was parried, give me the original so you know what was the reason
	OriginalValue = "original"

	ListenAddress = "listenAddress"
	// state of message rejected|queued
	DeliveryStatusField = "deliveryStatus"

	DeliveryRejected = "rejected"
	DeliveryQueued   = "queued"

	// when we have specified what parameter and limit determines its limit,
	// and has its value that the user has provided
	LimitField = "limit"
	HasField   = "has"
)
View Source
const (
	Inbox   Maildir = ""
	Spam            = ".SPAM"
	Sent            = ".Sent"
	Draft           = ".Draft"
	Trash           = ".Trash"
	Archive         = ".Archive"
	Custom          = "$custom"
)

Special folders start with a dollar

View Source
const (
	ImapFlagSeen     = "\\Seen"
	ImapFlagAnswered = "\\Answered"
	ImapFlagFlagged  = "\\Flagged"
	ImapFlagDeleted  = "\\Delete"
	ImapFlagDraft    = "\\Draft"
)
View Source
const (
	PrivateHeader = iota
	AllHeaders
	MailHeader
)
View Source
const (
	// 64 flags
	// Authorization support
	Authorization bitmask.Flag64 = 1 << iota // 1 << 0 which is 00000001
	StartTls                                 // 1 << 1 which is 00000010
	// Receive emails from local users
	ReceiveFromLocalUsers // 1 << 2 which is 00000100
	// Receive emails from remote (not logged in) users
	ReceiveFromRemoteUsers
	AllowLocalhostConnection
	// As mandated by RFC5321 Section 4.5.5, DSNs MUST be sent with a NULL Return-Path (or MAIL FROM) sender.
	// The From: header MAILER-DAEMON@example.com is set by the receiving MTA, based on the NULL sender address.
	// czyli domyślnie zezwalamy na puste maile i uwaga jeżeli je przyjmiesz nie możesz ich odrzucić
	ReceiveFromNullSender
	RequireLocalHostAuthorization
	// we ignore many errors, e.g. wrong sender, etc
	// then check the SMTP already
	LmtpService
)
View Source
const (
	DefaultFolderPerm os.FileMode = 0700
	DefaultFilePerm   os.FileMode = 0640
)
View Source
const DecisionKey = "response"
View Source
const XForwardedToHeader = "X-Forwarded-To"

It means that we have already sent a message from this email and if it is again send a copy to then in this case we ignore the subsequent sending of the copy

Variables

View Source
var Commands = make(map[string]CommandHandler)
View Source
var NativeEndian binary.ByteOrder
View Source
var RegisteredCommands []string

RegisteredCommands preserves the order - which makes it possible to find an error

Functions

func AllowAuthorize

func AllowAuthorize(service api.Service, mb *api.Mailbox, useSsl api.Maybe) bool

func CheckErrorsCount

func CheckErrorsCount(client *Client, maxErrorsPerConnection int)

CheckErrorsCount check the number of errors and end the connection if necessary

func DefaultNewFileFromDelivery

func DefaultNewFileFromDelivery(proxy MessageReceiverProxy, delivery Delivery, hostName string, baseStoragePath string) (filename string, dataSize int64, err error)

DefaultNewFileFromDelivery Default function to save the message to disk

func DeliveryAddHeaderAs

func DeliveryAddHeaderAs(d *Delivery, name, value string, first bool)

func DeliveryDelHeader

func DeliveryDelHeader(d *Delivery, name string)

func DeliverySetHeader

func DeliverySetHeader(d *Delivery, name string, value string, first bool)

func DovecotMessageFilenameV2

func DovecotMessageFilenameV2(delivery *Delivery, proxy MessageReceiverProxy, hostname string, finalFileSize int) string

DovecotMessageFilenameV2 Dovecot compatible file name generator V2 that's because at the end :2,

https://wiki2.dovecot.org/MailboxFormat/Maildir About locks, files should always land in new, otherwise there may be problems, e.g. loss of messages when performing imap operations Delivering mails to new/ directory doesn't have any problems, so there's no need for LDAs to support any type of locking.

https://cr.yp.to/proto/maildir.html When you move a file from new to cur, you have to change its name from uniq to uniq:info. Make sure to preserve the uniq string, so that separate messages can't bump into each other. info is morally equivalent to the Status field used by mbox readers. It'd be useful to have MUAs agree on the meaning of info, so I'm keeping a list of info semantics. Here it is.

info starting with "1,": Experimental semantics.

info starting with "2,": Each character after the comma is an independent flag.

Flag "P" (passed): the user has resent/forwarded/bounced this message to someone else. Flag "R" (replied): the user has replied to this message. Flag "S" (seen): the user has viewed this message, though perhaps he didn't read all the way through it. Flag "T" (trashed): the user has moved this message to the trash; the trash will be emptied by a later user action. Flag "D" (draft): the user considers this message a draft; toggled at user discretion. Flag "F" (flagged): user-defined flag; toggled at user discretion. New flags may be defined later. Flags must be stored in ASCII order: e.g., "2,FRS".

func FilterSendCopyToEmails

func FilterSendCopyToEmails(logger *slog.Logger, mb *api.Mailbox, supervisor api.Supervisor,
	allowToHandler func(lowerAsciiEmailAddress string) bool) []string

FilterSendCopyToEmails Filters email addresses Send-Copies to returns unique email addresses that you can send forward are supported by us or we've given you the green light to send to them from the machine

func GetAbsoluteMaildirPath

func GetAbsoluteMaildirPath(defaultMailStorePath string, mb *api.Mailbox) string

GetAbsoluteMaildirPath build maildir path prefix @defaultMailStorePath is default startpath prefix, if mb.MountPath is not empty it overwrite defaultMailStorePath

func GetSendCopyTo

func GetSendCopyTo(logger *slog.Logger, fromMailerDaemon bool, mb *api.Mailbox, delivery *Delivery, proxy MessageReceiverProxy,
	supervisor api.Supervisor) (sendCopiesTo []string)

func HaveCapacity

func HaveCapacity(mb *api.Mailbox, needCapacity int64) bool

HaveCapacity whether we will fit a message about the size of the @needCapacity in the user's account

func IsBlacklisted

func IsBlacklisted(mb *api.Mailbox, asciiEmailLowerCase string) bool

IsBlacklisted Whether the @asciiEmailLowerCase is marked as a white sender

func IsRejectErr

func IsRejectErr(err error) bool

func IsWhitelisted

func IsWhitelisted(mb *api.Mailbox, asciiEmailLowerCase string) bool

IsWhitelisted Whether the @asciiEmailLowerCase is marked as a white sender

func MimeHeadersSliceToString

func MimeHeadersSliceToString(headers []string) string

it doesn't really matter

func MimeHeadersSliceToStringLength

func MimeHeadersSliceToStringLength(headers []string, maxLength int) string

func MimeHeadersToString

func MimeHeadersToString(h textproto.MIMEHeader) string

it doesn't really matter

func MimeHeadersToStringWithOrder

func MimeHeadersToStringWithOrder(h textproto.MIMEHeader, order []string) string

func NewFileFromMailboxDelivery

func NewFileFromMailboxDelivery(proxy MessageReceiverProxy, delivery Delivery, hostName string, baseStoragePath string, folderPerm, filePerm os.FileMode) (filename string, dataSize int64, err error)

func NewPostmasterAddressEmail

func NewPostmasterAddressEmail()

func NewRejectErr

func NewRejectErr(code RejectErrCode) error

func NewRejectErrMessage

func NewRejectErrMessage(code RejectErrCode, message string) error

func OpenFile

func OpenFile(filename string, flag int, perms os.FileMode) (f *os.File, err error)

func RegisterCommand

func RegisterCommand(name string, cmd CommandHandler)

func RejectCodeToMessage

func RejectCodeToMessage(code RejectErrCode) string

func RejectCodeToMessageWithMyMessage

func RejectCodeToMessageWithMyMessage(code RejectErrCode, appendMessage string) string

func SendAllRequiredCopies

func SendAllRequiredCopies(logger *slog.Logger, fromMailerDaemon bool, originalRecipient string, mb *api.Mailbox,
	d *Delivery, proxy MessageReceiverProxy, supervisor api.MaildirSupervisor)

func SendAutoresponseMessage

func SendAutoresponseMessage(logger *slog.Logger, msg *mail.Message, mb *api.Mailbox, d *Delivery) (string, *mimemsg.Message)

func StoreMessage

func StoreMessage(appendBytes []byte, proxy MessageReceiverProxy, delivery Delivery, filename string) (fileSize int64, err error)

StoreMessage file to remember that the folder you are saving to must have already been created by you,

so you need to create it yourself before calling this function

@filename the full save path

func StoreMessageFromWriter

func StoreMessageFromWriter(appendBytes []byte, wrto io.WriterTo, filename string) (fileSize int64, err error)

func TryOpenFile

func TryOpenFile(filename string, howManyTries int, sleepStep time.Duration, flag int, perms os.FileMode) (f *os.File, err error)

func ValidEmail

func ValidEmail(value string) api.Maybe

func WrapText

func WrapText(w io.Writer, text string, newline, indent, preIndent string, width int)

WrapText prepares comment text for presentation in textual output. It wraps paragraphs of text to width or fewer Unicode code points and then prefixes each line with the indent. In preformatted sections (such as program text), it prefixes each non-blank line with preIndent. newline - lamacz lini

Types

type AddressEmail

type AddressEmail struct {
	Username string
	Host     string
}

func NewAddressEmail

func NewAddressEmail(username, host string) AddressEmail

func NewAddressEmailSanitize

func NewAddressEmailSanitize(value string) (AddressEmail, bool)

func SanitizeEmailAddress

func SanitizeEmailAddress(value string) AddressEmail

func (AddressEmail) IsEmpty

func (a AddressEmail) IsEmpty() bool

func (AddressEmail) IsEqual

func (a AddressEmail) IsEqual(another AddressEmail) bool

func (AddressEmail) IsValid

func (a AddressEmail) IsValid() bool

func (AddressEmail) String

func (e AddressEmail) String() string

type AuthorizationHandler

type AuthorizationHandler func(username string, password string) (bool, error)

type Client

type Client struct {
	// current customer number at the time of connection
	CurrentClientNo int32

	WhenConnected time.Time
	//	Listener                *LocalSmtpServer
	Helo                    string
	ListenerReverseHostname string
	ListenerAppName         string
	Flags                   bitmask.Flag64
	DidStartTlsNotAllowed   bool
	UseEhlo                 bool
	DidHeloCmd              bool
	WeClose                 bool
	Username                string
	Password                string
	RejectInfo              []string
	MaxErrorsPerConnection  int
	MaxRecipientsPerMessage int
	MaxMessageSizeInBytes   int64
	// copy of listener.DataStartReceivingTimeoutDuration
	DataStartReceivingTimeoutDuration time.Duration
	// copy of listener.DataStartReceivingTimeoutDuration because we set it after each data reception
	ProtocolTimeoutDuration time.Duration

	Con  net.Conn
	Text *textproto.Conn

	ErrorsCount int
	Session     Session

	Info ConnectionInfo
	// contains filtered or unexported fields
}

func (*Client) AuthLoginGetPassword

func (c *Client) AuthLoginGetPassword(cmd string)

func (*Client) AuthLoginGetUsername

func (c *Client) AuthLoginGetUsername(cmd string)

func (*Client) AuthMe

func (c *Client) AuthMe(username, password string) bool

func (*Client) Cmd

func (c *Client) Cmd(expectCode int, format string, args ...interface{}) (int, string, error)

Cmd is a convenience function that sends a command and returns the response

func (*Client) DidHelo

func (c *Client) DidHelo() bool

func (*Client) DidMailFrom

func (c *Client) DidMailFrom() bool

func (*Client) DidRecipient

func (c *Client) DidRecipient() bool

func (*Client) GetHello

func (c *Client) GetHello() string

func (*Client) GetInfo

func (c *Client) GetInfo() ConnectionInfo

func (*Client) GetPassword

func (c *Client) GetPassword() string

func (*Client) GetSession

func (c *Client) GetSession() Session

func (*Client) GetUsername

func (c *Client) GetUsername() string

func (*Client) IsLoggedIn

func (c *Client) IsLoggedIn() bool

func (*Client) ReadLine

func (c *Client) ReadLine() (string, error)

func (*Client) UnknownCommand

func (c *Client) UnknownCommand(cmd string, msg string)

func (*Client) WriteCmd

func (c *Client) WriteCmd(s string) (err error)

type CommandHandler

type CommandHandler func(c *Client, supervisor api.Supervisor, args string)

type Connection

type Connection interface {
	GetInfo() ConnectionInfo
	GetHello() string
	GetUsername() string
	GetPassword() string
}

type ConnectionInfo

type ConnectionInfo struct {
	StartConnection time.Time
	Flags           bitmask.Flag64
	// instance no
	InstanceId int
	// another customer number since launch
	Id int64
	// it should be unique in the long run
	UUId            string
	LocalAddr       string
	RemoteAddr      string
	ReverseHostName string
}

func NewConnectionInfo

func NewConnectionInfo(id int64, flags bitmask.Flag64) ConnectionInfo

type ConnectionState

type ConnectionState struct {
	// contains filtered or unexported fields
}

func NewConnectionState

func NewConnectionState(flags bitmask.Flag64) ConnectionState

NewConnectionState As mandated by RFC5321 Section 4.5.5, DSNs MUST be sent with a NULL Return-Path (or MAIL FROM) sender. The From: header MAILER-DAEMON@example.com is set by the receiving MTA, based on the NULL sender address.

func (ConnectionState) Close

func (mp ConnectionState) Close()

Close If you have any pointers, remove them and close all streams

type Decision

type Decision string

func (Decision) String

func (d Decision) String() string

type Delivery

type Delivery struct {
	// another message number from the moment the application was launched
	MessageNo uint32
	// unique id of this message
	UniqueId string
	From     AddressEmail
	To       AddressEmail
	// where to deliver, to which folder on disk
	Mailbox Maildir
	// If mailbox = Custom, here is its physical name.
	// If you want to filter mail independently based on folders,
	// set Mailbox = Custom in CustomMailbox = ".Notifications"
	// like in gmail
	CustomMailbox string
	Flags         ImapFlag

	// if true, it will be placed in the "${MAILBOX}/new" folder,
	// otherwise in "${MAILBOX}/cur"
	// do not confuse it with the //Recent flag, which should not be used in ImapFlags
	// because it is a session flag assigned by the IMAP server at the session opening level ( RFC3501 )
	IsRecent bool

	// When the message was forwarded
	IsPassed bool
	// contains filtered or unexported fields
}

func CloneDelivery

func CloneDelivery(src *Delivery, toMailBox Maildir) Delivery

CloneDelivery cloning delivery of the same message, but to a different mailbox

func NewDelivery

func NewDelivery(from, to AddressEmail) Delivery

func NewDeliveryFromClient

func NewDeliveryFromClient(client Connection, from, to AddressEmail) Delivery

func (*Delivery) Close

func (d *Delivery) Close()

func (Delivery) GetHeader

func (d Delivery) GetHeader(msg *mail.Message, name string) string

GetHeader get the first header on the list, if we have an appendHeader, we return them - because they are priority, otherwise we return those from the email

func (*Delivery) HeaderContainsPattern

func (d *Delivery) HeaderContainsPattern(msg *mail.Message, names []string, patterns ...string) bool

func (Delivery) HeaderToString

func (d Delivery) HeaderToString() string

func (*Delivery) HeaderValues

func (d *Delivery) HeaderValues(msg *mail.Message, key string) (ret []string)

HeaderValues get the first priority headers on the list and then the rest of the message

func (*Delivery) MatchHeader

func (d *Delivery) MatchHeader(msg *mail.Message, name string, matcher func(value string) bool) bool

func (*Delivery) SpoolDir

func (d *Delivery) SpoolDir(basePath string) string

type DeliveryProxy

type DeliveryProxy interface {
	GetUniqueId() string
	GetFrom() AddressEmail
	GetTo() AddressEmail
	GetMailbox() Maildir
	GetCustomMailbox() string
	GetImapFlags() ImapFlag
	IsRecent() bool
	Filesize() int
	AddHeader(name, value string)
	DelHeader(name string)
	SetHeader(name string, value string)
}

type EmailAddressType

type EmailAddressType int

type ImapFlag

type ImapFlag map[string]bool

func (ImapFlag) Delete

func (i ImapFlag) Delete(key string)

func (ImapFlag) IsSet

func (i ImapFlag) IsSet(key string) bool

func (ImapFlag) Keys

func (i ImapFlag) Keys() []string

func (ImapFlag) Set

func (i ImapFlag) Set(key string)

func (ImapFlag) String

func (i ImapFlag) String() string

type Listener

type Listener struct {
	MaxConnections int
	// MaxMessageSizeInBytes   int
	MaxErrorsPerConnection  int
	MaxRecipientsPerMessage int
	MaxMessageSizeInBytes   int64

	ProtocolTimeoutDuration           time.Duration
	DataReceivingTimeoutDuration      time.Duration
	DataStartReceivingTimeoutDuration time.Duration
	// contains filtered or unexported fields
}

func NewDefaultListener

func NewDefaultListener() *Listener

func NewDefaultListenerExt

func NewDefaultListenerExt(address, reverseHostname string, flags bitmask.Flag64) *Listener

NewDefaultListenerExt Mainly for the needs of testing, but also for simple customers

func NewDefaultListenerWithAddress

func NewDefaultListenerWithAddress(address string) *Listener

func NewListener

func NewListener(address string, maxErrorsPerConnection int, maxConnections int, flags bitmask.Flag64) *Listener

func NewLmtp

func NewLmtp(hostname string) *Listener

func (*Listener) Address

func (l *Listener) Address() string

func (*Listener) Listen

func (l *Listener) Listen() (err error)

func (*Listener) Run

func (l *Listener) Run(ctx context.Context, supervisor SupervisorExt, cancelFunc context.CancelFunc) error

func (*Listener) Stop

func (l *Listener) Stop()

Stop You can quit by Shutdown or closing the context (ctx.Done) which is equivalent

type Maildir

type Maildir string

mailbox i.e. the directory to which to move mail after receiving it

func ToMailDir

func ToMailDir(emailLowerAsciiString string) Maildir

because adding headers would make us add to the message the headers already received in the previous session

func NewMessageDeliveryFrom(delivery Delivery, appendHeaders textproto.MIMEHeader, emailLowerAsciiString string) Delivery {
	//	delivery.SetHeader(headers.DeliveredTo, emailLowerAsciiString)
	//delivery.To = SanitizeEmailAddress(emailLowerAsciiString)
	delivery.Maildir = ToMailDir(emailLowerAsciiString)

	delivery.appendHeader = textproto.MIMEHeader{}
	for k, v := range appendHeaders {
		delivery.appendHeader[k] = v
	}
	return delivery
}

func (Maildir) CreateNewMailDirIfNotExists

func (m Maildir) CreateNewMailDirIfNotExists(basePath string, customValue string,
	folderPerm, filePerm os.FileMode) (string, error)

Create a folder ${MAILDIR}/new if it does not exist and return the full path where you should write

func (Maildir) Path

func (m Maildir) Path(basePath string, customValue string) string

type MessageReceiver

type MessageReceiver struct {
	// contains filtered or unexported fields
}

func NewMessageReceiver

func NewMessageReceiver() MessageReceiver

func (*MessageReceiver) AcceptData

func (mr *MessageReceiver) AcceptData(ctx context.Context, c *Client, r io.Reader) (err error)

func (*MessageReceiver) AcceptRecipient

func (mr *MessageReceiver) AcceptRecipient(currentHostname string, from, to AddressEmail, client *Client) bool

func (*MessageReceiver) ArrivalTime

func (mr *MessageReceiver) ArrivalTime() time.Time

func (*MessageReceiver) Close

func (mr *MessageReceiver) Close()

Close You clean everything here

func (*MessageReceiver) GetBodyOffset

func (mr *MessageReceiver) GetBodyOffset() int64

func (*MessageReceiver) GetBuffer

func (mr *MessageReceiver) GetBuffer() *filebuf.Buf

func (*MessageReceiver) GetMessage

func (mr *MessageReceiver) GetMessage() *mail.Message

func (*MessageReceiver) InitialMessageSize

func (mr *MessageReceiver) InitialMessageSize() int

InitialMessageSize The size of the message right after downloading, without the extra headers you created

func (*MessageReceiver) MoveBufferPosToMessageStartLine

func (mr *MessageReceiver) MoveBufferPosToMessageStartLine() error

MoveBufferPosToMessageStartLine move to the beginning of the line where the message begins

func (*MessageReceiver) MoveBufferPosToStart

func (mr *MessageReceiver) MoveBufferPosToStart() error

MoveBufferPosToStart move to the beginning of the file

func (*MessageReceiver) RecipientsCount

func (mr *MessageReceiver) RecipientsCount() int

func (*MessageReceiver) SeekMessage

func (mr *MessageReceiver) SeekMessage(client *Client, offset int64, whence int) bool

func (*MessageReceiver) SeekMessageToBeginning

func (mr *MessageReceiver) SeekMessageToBeginning(client *Client) bool

func (*MessageReceiver) SeekMessageToBody

func (mr *MessageReceiver) SeekMessageToBody(client *Client) bool

func (*MessageReceiver) ToAllEmails

func (mr *MessageReceiver) ToAllEmails() []AddressEmail

func (*MessageReceiver) ToAllEmailsAsString

func (mr *MessageReceiver) ToAllEmailsAsString() []string

type MessageReceiverProxy

type MessageReceiverProxy interface {
	InitialMessageSize() int
	GetBodyOffset() int64
	GetMessage() *mail.Message
	GetBuffer() *filebuf.Buf
	MoveBufferPosToStart() error
	ArrivalTime() time.Time
	MoveBufferPosToMessageStartLine() error
}

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

The needs of the program used should not be typical metrics, they are data that are intended to speed up the program's operation e.g. for Prometheus

func (*Metrics) ClientConnected

func (m *Metrics) ClientConnected() int32

func (*Metrics) ClientDisconnected

func (m *Metrics) ClientDisconnected() int32

func (*Metrics) ConnectedClients

func (m *Metrics) ConnectedClients() int32

type PostmasterHolder

type PostmasterHolder interface {
	MailerDaemonEmailAddress() string
}

type RejectBehaviour

type RejectBehaviour struct {
	Message string
	Close   bool
}

type RejectErr

type RejectErr struct {
	Code            RejectErrCode
	AppendMessage   string
	CloseConnection bool
}

func (RejectErr) Error

func (r RejectErr) Error() string

func (RejectErr) ToMtpResponse

func (r RejectErr) ToMtpResponse() string

ToMtpResponse To the replies in the servers (S|L)MTP

type RejectErrCode

type RejectErrCode int
const (

	// Reject should not be session-driven, should only inform about the following states:
	// 1 Server error
	TemporaryServerRejectErr RejectErrCode = iota

	// 2 Rejecting an email address (recipient or sender), it doesn't matter because it results from the context
	RecipientServerRejectErr

	// 3 Rejection due to message size
	// 4 Rejected due to lack of adequate free disk space
	QuotaLowRejectErr

	// 5 Spam rejection
	YouSpamerGoAwayRejectErr

	// 6 Rejection due to throtling
	TooManyConnectionsRejectErr

	// If the problem is temporary -- be sure to do your text
	TemporaryMailBoxProblemErr
	// Reject an attempt to send to the specified address (maybe you're a spammer,
	// or maybe the mailbox doesn't exist anymore) -- don't try again
	RecipientAddressRejectErr
)

type Session

type Session interface {
	IsLoggedIn() bool

	// From The address from the from header but also the login of the logged-in person (if so)
	From() AddressEmail

	MaxMessageSize() uint64

	// IsAllowSendAs Can a user send a message from this address only to logged in users?
	IsAllowSendAs(addressEmailInAsciiLowerCase AddressEmail) (bool, error)

	// OnAuthorization Login authorization
	// @username is already IDN decoded value as lowercase ASCII
	// @password is plain text value received from client (we don't support anything other than plain text at this time)
	// @service service type
	// @useSsl connection use ssl
	OnAuthorization(username string, password string, service api.Service, useSsl api.Maybe) (bool, error)

	// ResetMessageReceivingStatus Consider the shipment, if any, as not yet there and will be prepared to pick up another
	ResetMessageReceivingStatus() error

	// AcceptMessageFromEmail The client wants to send us a new message, if you return an error,
	// you will not accept the message,
	// at this stage you can already check the sender of the message
	// because it is the actual so-called Envelope lowercase and already in ascii
	AcceptMessageFromEmail(senderAscii AddressEmail) error

	// AcceptRecipient Do you accept the recipient of this message? is already in ASCII format,
	// you can treat it as caching to load all mailboxes to which it will deliver the message
	AcceptRecipient(recipientAscii AddressEmail) error

	// AcceptMessage: At this stage, you need to decide whether you will accept the message to all recipients or reject
	// it for everyone. If you can accept only some users, the rejection will always apply to the sender,
	// in the sense that we do not accept from him because he is a spammer.
	// You can return your own message, e.g. with message tracking or a link to a website that explains the reason,
	AcceptMessage(message *mail.Message) error

	// Physically deliver the message to the user's inbox
	ProcessDelivery(proxy MessageReceiverProxy, delivery Delivery, revHostname string) error

	Close()
}

Session connection session itself does not mean that the client is logged in, it should be generated by Super interface, the session works synchronously and without threads in the sense of accepting commands from the server

type State

type State int
const (
	StatePassive State = iota
	StateData
)

type Super

type Super interface {
	// Default hostname
	Hostname() string
	// Name visible when logging in
	Name() string
	// Software number (visible reporting processes)
	Version() string

	Flags() bitmask.Flag64

	// Used at the stage when we do not yet know or cannot determine the quota of the actual user
	DefaultMaxMessageSizeInBytes() int64
	DefaultMaxRecipientsPerMessage() int
}

type SupervisorExt

type SupervisorExt interface {
	api.Supervisor
	api.MaildirStorage

	// OpenSession Opening or ending the session.
	// If we don't want to connect, we need to give the content to rejectMessage
	OpenSession(ci ConnectionInfo, l *slog.Logger) (session Session, err error)
	CloseSession(session Session)
}

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL