fbb

package
v0.0.14 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2024 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

fbb provides a client-side implementation of the B2 Forwarding Protocol and Winlink 2000 Message Structure for transfer of messages to and from a Winlink 2000 Radio Email Server (RMS) gateway.

Index

Constants

View Source
const (
	ProtocolOffsetSizeLimit = 999999
	MaxBlockSize            = 5

	// Paclink-unix uses 250, protocol maximum is 255, but we use 125 to allow use of AX.25 links with a paclen of 128.
	// TODO: Consider setting this dynamically.
	MaxMsgLength = 125
)
View Source
const (
	HEADER_MID     = `Mid`
	HEADER_TO      = `To`
	HEADER_DATE    = `Date`
	HEADER_TYPE    = `Type`
	HEADER_FROM    = `From`
	HEADER_CC      = `Cc`
	HEADER_SUBJECT = `Subject`
	HEADER_MBO     = `Mbo`
	HEADER_BODY    = `Body`
	HEADER_FILE    = `File`

	// These headers are stripped by the winlink system, but let's
	// include it anyway... just in case the winlink team one day
	// starts taking encoding seriously.
	HEADER_CONTENT_TYPE              = `Content-Type`
	HEADER_CONTENT_TRANSFER_ENCODING = `Content-Transfer-Encoding`

	// The default body charset seems to be ISO-8859-1
	//
	// The Winlink Message Structure docs says that the body should
	// be ASCII-only, but RMS Express seems to encode the body as
	// ISO-8859-1. This is also the charset set (Content-Type header)
	// when a message reaches an SMTP server.
	DefaultCharset = "ISO-8859-1"

	// Mails going out over SMTP from the Winlink system is sent
	// with the header 'Content-Transfer-Encoding: 7bit', but
	// let's be reasonable... we don't send ASCII-only body.
	DefaultTransferEncoding = "8bit"

	// The date (in UTC) format as described in the Winlink
	// Message Structure docs (YYYY/MM/DD HH:MM).
	DateLayout = `2006/01/02 15:04`
)

Common Winlink 2000 Message headers

View Source
const (
	Private        MsgType = "Private"
	Service                = "Service"
	Inquiry                = "Inquiry"
	PositionReport         = "Position Report"
	Option                 = "Option"
	System                 = "System"
)
View Source
const (
	BasicProposal PropCode = 'B' // Basic ASCII proposal (or compressed binary in v0/1)
	AsciiProposal          = 'A' // Compressed v0/1 ASCII proposal
	Wl2kProposal           = 'C' // Compressed v2 proposal (winlink extension)
	GzipProposal           = 'D' // Gzip compressed v2 proposal
)
View Source
const (
	Accept ProposalAnswer = '+'
	Reject                = '-'
	Defer                 = '='
)
View Source
const (
	RobustAuto     robustMode = iota // Run the connection in robust-mode when not transferring outbound messages.
	RobustForced                     // Always run the connection in robust-mode.
	RobustDisabled                   // Never run the connection in robust-mode.
)

The different robust-mode settings.

View Source
const MaxMIDLength = 12

Variables

View Source
var ErrConnLost = errors.New("connection lost")

ErrConnLost is returned by Session.Exchange if the connection is prematurely closed.

View Source
var ErrNoFB2 = errors.New("Remote does not support B2 Forwarding Protocol")
View Source
var ErrOffsetLimitExceeded error = errors.New("Protocol does not support offset larger than 6 digits")
View Source
var StdLogger = log.New(os.Stderr, "", log.LstdFlags)
View Source
var StdUA = UserAgent{Name: "wl2kgo", Version: "0.1a"}

Functions

func BodyFromBytes

func BodyFromBytes(data []byte, encoding string) (string, error)

BodyFromBytes translated the data based on the given charset encoding into a proper utf-8 string.

func GenerateMid

func GenerateMid(callsign string) string

Generates a unique message ID in the format specified by the protocol.

func IsLoginFailure

func IsLoginFailure(err error) bool

IsLoginFailure returns a boolean indicating whether the error is known to report that the secure login failed.

func ParseDate

func ParseDate(dateStr string) (time.Time, error)

func ReadLine

func ReadLine(rd io.Reader) (string, error)

func StringToBody

func StringToBody(str, encoding string) ([]byte, error)

StringToBytes converts the body into a slice of bytes with the given charset encoding.

CRLF line break is enforced. Line break are inserted if a line is longer than 1000 characters (including CRLF).

Types

type Address

type Address struct {
	Proto string
	Addr  string
}

Representation of a receiver/sender address.

func AddressFromString

func AddressFromString(addr string) Address

Function that constructs a proper Address from a string.

Supported formats: foo@bar.baz (SMTP proto), N0CALL (short winlink address) or N0CALL@winlink.org (full winlink address).

func (Address) EqualString

func (a Address) EqualString(b string) bool

EqualString reports whether the given address string is equal to this address.

func (Address) IsZero

func (a Address) IsZero() bool

IsZero reports whether the Address is unset.

func (Address) String

func (a Address) String() string

Textual representation of Address.

type ByDate

type ByDate []*Message

func (ByDate) Len

func (d ByDate) Len() int

func (ByDate) Less

func (d ByDate) Less(i, j int) bool

func (ByDate) Swap

func (d ByDate) Swap(i, j int)

type File

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

File represents an attachment.

func NewFile

func NewFile(name string, data []byte) *File

Create a new file (attachment) with the given name and data.

A B2F file must have an associated name. If the name is empty, NewFile will panic.

func (*File) Data

func (f *File) Data() []byte

Data returns a copy of the attachment content.

func (*File) MarshalJSON

func (f *File) MarshalJSON() ([]byte, error)

JSON marshaller for File.

func (*File) Name

func (f *File) Name() string

Name returns the attachment's filename.

func (*File) Size

func (f *File) Size() int

Size returns the attachments's size in bytes.

type Header map[string][]string

A Header represents the key-value pairs in a Winlink 2000 Message header.

func (Header) Add

func (h Header) Add(key, value string)

Add adds the key, value pair to the header. It appends to any existing values associated with key.

func (Header) Del

func (h Header) Del(key string)

Del deletes the values associated with key.

func (Header) Get

func (h Header) Get(key string) string

Get gets the first value associated with the given key. If there are no values associated with the key, Get returns "". To access multiple values of a key, access the map directly with CanonicalHeaderKey.

func (Header) Set

func (h Header) Set(key, value string)

Set sets the header entries associated with key to the single element value. It replaces any existing values associated with key.

func (Header) Write

func (h Header) Write(w io.Writer) error

Write writes a header in wire format.

type InboundHandler

type InboundHandler interface {
	// ProcessInbound should persist/save/process all messages received (msgs) returning an error if the operation was unsuccessful.
	//
	// The error will be delivered (if possble) to the remote to indicate that an error has occurred.
	ProcessInbound(msg ...*Message) error

	// GetInboundAnswer should return a ProposalAnwer (Accept/Reject/Defer) based on the remote's message Proposal p.
	//
	// An already successfully received message (see MID) should be rejected.
	GetInboundAnswer(p Proposal) ProposalAnswer
}

An InboundHandler handles all messages that can/is sent from the remote node.

type MBoxHandler

type MBoxHandler interface {
	InboundHandler
	OutboundHandler

	// Prepare is called before any other operation in a session.
	//
	// The returned error can be used to indicate that the mailbox is
	// not ready for a new session, the error will be forwarded to the
	// remote node.
	Prepare() error
}

Objects implementing the MBoxHandler interface can be used to handle inbound and outbound messages for a Session.

type Message

type Message struct {
	// The header names are case-insensitive.
	//
	// Users should normally access common header fields
	// using the appropriate Message methods.
	Header Header
	// contains filtered or unexported fields
}

Message represent the Winlink 2000 Message Structure as defined in http://winlink.org/B2F.

func NewMessage

func NewMessage(t MsgType, mycall string) *Message

NewMessage initializes and returns a new message with Type, Mbo, From and Date set.

If the message type t is empty, it defaults to Private.

func (*Message) AddCc

func (m *Message) AddCc(addr ...string)

AddCc adds a new carbon copy receiver to this message.

It adds a new Cc header field per given address. SMTP: prefix is automatically added if needed, see AddressFromString.

func (*Message) AddFile

func (m *Message) AddFile(f *File)

AddFile adds the given File as an attachment to m.

func (*Message) AddTo

func (m *Message) AddTo(addr ...string)

AddTo adds a new receiver for this message.

It adds a new To header field per given address. SMTP: prefix is automatically added if needed, see AddressFromString.

func (*Message) Body

func (m *Message) Body() (string, error)

Body returns this message's body encoded as utf8.

func (*Message) BodySize

func (m *Message) BodySize() int

BodySize returns the expected size of the body (in bytes) as defined in the header.

func (*Message) Bytes

func (m *Message) Bytes() ([]byte, error)

Bytes returns the message in the Winlink Message format.

func (*Message) Cc

func (m *Message) Cc() (cc []Address)

Cc returns the carbon copy receivers of this message.

func (*Message) Charset

func (m *Message) Charset() string

Charset returns the body character encoding as defined in the ContentType header field.

If the header field is unset, DefaultCharset is returned.

func (*Message) Date

func (m *Message) Date() time.Time

Date parses the Date header field according to the winlink format.

Parse errors are omitted, but it's checked at serialization.

func (*Message) Files

func (m *Message) Files() []*File

Files returns the message attachments.

func (*Message) From

func (m *Message) From() Address

From returns the From header field as an Address.

func (*Message) IsOnlyReceiver

func (m *Message) IsOnlyReceiver(addr Address) bool

Returns true if the given Address is the only receiver of this Message.

func (*Message) MID

func (m *Message) MID() string

MID returns the unique identifier of this message across the winlink system.

func (*Message) Mbo

func (m *Message) Mbo() string

Mbo returns the mailbox operator origin of this message.

func (*Message) Proposal

func (m *Message) Proposal(code PropCode) (*Proposal, error)

Method for generating a proposal of the message.

An error is returned if the Validate method fails.

func (*Message) ReadFrom

func (m *Message) ReadFrom(r io.Reader) error

Implements ReaderFrom for Message.

Reads the given io.Reader and fills in values fetched from the stream.

func (*Message) Receivers

func (m *Message) Receivers() []Address

Receivers returns a slice of all receivers of this message.

func (*Message) SetBody

func (m *Message) SetBody(body string) error

SetBody sets the given string as message body using DefaultCharset.

See SetBodyWithCharset for more info.

func (*Message) SetBodyWithCharset

func (m *Message) SetBodyWithCharset(charset, body string) error

SetBodyWithCharset translates and sets the body according to given charset.

Header field Content-Transfer-Encoding is set to DefaultTransferEncoding. Header field Content-Type is set according to charset. All lines are modified to ensure CRLF.

Use SetBody to use default character encoding.

func (*Message) SetDate

func (m *Message) SetDate(t time.Time)

Set date sets the Date header field.

The field is set in the format DateLayout, UTC.

func (*Message) SetFrom

func (m *Message) SetFrom(addr string)

SetFrom sets the From header field.

SMTP: prefix is automatically added if needed, see AddressFromString.

func (*Message) SetSubject

func (m *Message) SetSubject(str string)

SetSubject sets this message's subject field.

The Winlink Message Format only allow ASCII characters. Words containing non-ASCII characters are Q-encoded with DefaultCharset (as defined by RFC 2047).

func (*Message) String

func (m *Message) String() string

Message stringer.

func (*Message) Subject

func (m *Message) Subject() string

Subject returns this message's subject header decoded using WordDecoder.

func (*Message) To

func (m *Message) To() (to []Address)

To returns primary receivers of this message.

func (*Message) Type

func (m *Message) Type() MsgType

Type returns the message type.

See MsgType consts for details.

func (*Message) Validate

func (m *Message) Validate() error

Validate returns an error if this message violates any Winlink Message Structure constraints

func (*Message) Write

func (m *Message) Write(w io.Writer) (err error)

Writes Message to the given Writer in the Winlink Message format.

If the Date header field is not formatted correctly, an error will be returned.

type MsgType

type MsgType string

type OutboundHandler

type OutboundHandler interface {
	// GetOutbound should return all pending (outbound) messages addressed to (and only to) one of the fw addresses.
	//
	// No fw address implies that the remote node could be a Winlink CMS and all oubound
	// messages can be delivered through the connected node.
	GetOutbound(fw ...Address) (out []*Message)

	// SetSent should mark the the message identified by MID as successfully sent.
	//
	// If rejected is true, it implies that the remote node has already received the message.
	SetSent(MID string, rejected bool)

	// SetDeferred should mark the outbound message identified by MID as deferred.
	//
	// SetDeferred is called when the remote want's to receive the proposed message
	// (see MID) later.
	SetDeferred(MID string)
}

An OutboundHandler offer messages that can be delivered (a proposal) to the remote node and is notified when a message is sent or defered.

type PropCode

type PropCode byte

type Proposal

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

Proposal is the type representing a inbound or outbound proposal.

func NewProposal

func NewProposal(MID, title string, code PropCode, data []byte) *Proposal

Constructor for a new Proposal given a Winlink Message.

Reads the Winlink Message given and constructs a new proposal based on what's read and prepares for outbound delivery, returning a Proposal with the given data.

func (*Proposal) Data

func (p *Proposal) Data() []byte

Data returns the decompressed raw message

func (*Proposal) DataIsComplete

func (p *Proposal) DataIsComplete() bool

Method for checking if the Proposal is completely downloaded/loaded and ready to be read/sent.

Typically used to check if the whole message was successfully downloaded from the CMS.

func (*Proposal) MID

func (p *Proposal) MID() string

Returns the uniqe Message ID

func (*Proposal) Message

func (p *Proposal) Message() (*Message, error)

func (*Proposal) Title

func (p *Proposal) Title() string

Returns the title of this proposal

type ProposalAnswer

type ProposalAnswer byte

type Session

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

Session represents a B2F exchange session.

A session should only be used once.

func NewSession

func NewSession(mycall, targetcall, locator string, h MBoxHandler) *Session

Constructs a new Session object.

The Handler can be nil (but no messages will be exchanged).

Mycall and targetcall will be upper-cased.

func (*Session) AddAuxiliaryAddress

func (s *Session) AddAuxiliaryAddress(aux ...Address)

AddAuxiliaryAddress adds one or more addresses to request messages on behalf of.

Currently the Winlink System only support requesting messages for call signs, not full email addresses.

func (*Session) Done

func (s *Session) Done() bool

Done() returns true if either parties have existed from this session.

func (*Session) Exchange

func (s *Session) Exchange(conn net.Conn) (stats TrafficStats, err error)

Exchange is the main method for exchanging messages with a remote over the B2F protocol.

Sends outbound messages and downloads inbound messages prepared for this session.

Outbound messages should be added as proposals before calling the Exchange() method.

If conn implements the transport.Robust interface, the connection is run in robust-mode except when an outbound message is transferred.

After Exchange(), messages that was accepted and delivered successfully to the RMS is available through a call to Sent(). Messages downloaded successfully from the RMS is retrieved by calling Received().

The connection is closed at the end of the exchange. If the connection is closed before the exchange is done, ErrConnLost is returned.

Subsequent Exchange calls on the same session is a noop.

func (*Session) IsMaster

func (s *Session) IsMaster(isMaster bool)

IsMaster sets whether this end should initiate the handshake.

func (*Session) Mycall

func (s *Session) Mycall() string

Mycall returns this stations call sign.

func (*Session) RemoteForwarders

func (s *Session) RemoteForwarders() []Address

This method returns the call signs the remote is requesting traffic on behalf of. The call signs are not available until the handshake is done.

It will typically be the call sign of the remote P2P station and empty when the remote is a Winlink CMS.

func (*Session) RemoteSID

func (s *Session) RemoteSID() string

RemoteSID returns the remote's SID (if available).

func (*Session) SetLogger

func (s *Session) SetLogger(logger *log.Logger)

Sets custom logger.

func (*Session) SetMOTD

func (s *Session) SetMOTD(line ...string)

SetMOTD sets one or more lines to be sent before handshake.

The MOTD is only sent if the local node is session master.

func (*Session) SetRobustMode

func (s *Session) SetRobustMode(mode robustMode)

SetRobustMode sets the RobustMode for this exchange.

The mode is ignored if the exchange connection does not implement the transport.Robust interface.

Default is RobustAuto.

func (*Session) SetSecureLoginHandleFunc

func (s *Session) SetSecureLoginHandleFunc(f func(addr Address) (password string, err error))

SetSecureLoginHandleFunc registers a callback function used to prompt for password when a secure login challenge is received.

func (*Session) SetStatusUpdater

func (s *Session) SetStatusUpdater(updater StatusUpdater)

Set callback for status updates on receiving / sending messages

func (*Session) SetUserAgent

func (s *Session) SetUserAgent(ua UserAgent)

Set this session's user agent

func (*Session) Targetcall

func (s *Session) Targetcall() string

Targetcall returns the remote stations call sign (if known).

func (*Session) UserAgent

func (s *Session) UserAgent() UserAgent

Get this session's user agent

type Status

type Status struct {
	Receiving        *Proposal
	Sending          *Proposal
	BytesTransferred int
	BytesTotal       int
	Done             bool
	When             time.Time
}

Status holds information about ongoing transfers.

type StatusUpdater

type StatusUpdater interface {
	UpdateStatus(s Status)
}

type TrafficStats

type TrafficStats struct {
	Received []string // Received message MIDs.
	Sent     []string // Sent message MIDs.
}

TrafficStats holds exchange message traffic statistics.

type UserAgent

type UserAgent struct {
	Name    string
	Version string
}

Struct used to hold information that is reported during B2F handshake.

Non of the fields must contain a dash (-).

type ValidationError

type ValidationError struct {
	Field string // The field/part of the message that is not valid
	Err   string // Description of the error
}

ValidationError is the error type returned by functions validating a message.

func (ValidationError) Error

func (e ValidationError) Error() string

type WordDecoder

type WordDecoder struct{ mime.WordDecoder }

WordDecoder decodes MIME headers containing RFC 2047 encoded-words.

(See DecodeHeader for mime.WordDecoder differences).

func (*WordDecoder) DecodeHeader

func (d *WordDecoder) DecodeHeader(header string) (string, error)

Decode decodes an encoded-word.

If word is not a valid RFC 2047 encoded-word, word is decoded as raw ISO-8859-1 as a work-around for RMS Express' non-conforming encoding of the Subject header.

Jump to

Keyboard shortcuts

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