Documentation
¶
Index ¶
- Constants
- Variables
- func BundleExtension(name string) bool
- func BundleFileName(destNet, destNode uint16, dayIndex int) string
- func CreateBundle(bundlePath string, pktPaths []string) (int, error)
- func DecodeFTNString(s, chrs string) string
- func EnsureBinkdConf(bbsRoot string, ftnCfg config.FTNConfig, server config.ServerConfig) (created bool, err error)
- func ExtractBundle(srcPath, destDir string) ([]string, error)
- func FormatFTNDateTime(t time.Time) string
- func FormatPackedMessageBody(parsed *ParsedBody) string
- func HasPlaceholders(content, bbsRoot string) bool
- func IsZIPBundle(path string) (bool, error)
- func ParseFTNDateTime(s string) (time.Time, error)
- func ReadPacket(r io.Reader) (*PacketHeader, []*PackedMessage, error)
- func RegenerateBinkdConf(confPath string, cfg BinkdConfig, nodes []BinkdNode) error
- func SyncBinkdConf(confPath string, identity BinkdIdentity, links map[string]BinkdLinkSync) error
- func SyncBinkdSettings(confPath string, port, logLevel int) error
- func UpdateBinkdConf(confPath string, cfg BinkdConfig) error
- func ValidateAddress(s string) error
- func WritePacket(w io.Writer, hdr *PacketHeader, msgs []*PackedMessage) error
- type Address
- type BinkdConfig
- type BinkdIdentity
- type BinkdLinkSync
- type BinkdNode
- type EchoArea
- type NodeLookup
- type Nodelist
- type NodelistEntry
- type PackedMessage
- type PacketHeader
- type ParsedBody
- type RegistryNetwork
Constants ¶
const ( MsgAttrPrivate = 0x0001 MsgAttrCrash = 0x0002 MsgAttrReceived = 0x0004 MsgAttrSent = 0x0008 MsgAttrFile = 0x0010 MsgAttrTransit = 0x0020 MsgAttrOrphan = 0x0040 MsgAttrKillSent = 0x0080 MsgAttrLocal = 0x0100 MsgAttrHold = 0x0200 MsgAttrFRQ = 0x0800 )
Packed message attribute flags (FTS-0001).
const CWValidation = 0x0100
CWValidation is the capability word validation value per FSC-0048.
const DefaultBinkpPort = 24554
DefaultBinkpPort is the standard BinkP TCP port.
const MaxFieldLen = 256
MaxFieldLen limits null-terminated string fields.
const PacketHeaderSize = 58
PacketHeaderSize is the fixed size of a Type-2+ packet header.
const PacketType2Plus = 2
PacketType2Plus is the packet version identifier for Type-2+ packets.
Variables ¶
var ( ErrInvalidPacketType = errors.New("ftn: invalid packet type (expected 2)") ErrTruncatedPacket = errors.New("ftn: truncated packet data") ErrTruncatedMessage = errors.New("ftn: truncated message in packet") ErrPacketTooLarge = errors.New("ftn: packet exceeds size limit") )
Errors
Functions ¶
func BundleExtension ¶
BundleExtension reports whether a filename looks like an FTN echomail bundle based on its extension. Binkd uses day-of-week suffixes for bundles:
.mo0 .tu0 .we0 .th0 .fr0 .sa0 .su0 (day-based, normal) .mo1 .tu1 ... .mo9 ... .saZ (day-based, overflow: 0-9, a-z) .out (normal outbound bundle) .zip (explicit ZIP bundle)
The trailing character is alphanumeric per FTS-5005; overflow uses 0-9 then a-z.
func BundleFileName ¶
BundleFileName returns the standard FTN bundle filename for a given destination link and day-of-week index (0=Mon, 6=Sun), using the BSO naming convention: NNNNFFFF.DDD where NNNN=destNet (4 hex digits), FFFF=destNode (4 hex digits), DDD=day extension (e.g., mo0).
func CreateBundle ¶
CreateBundle creates a ZIP bundle archive at bundlePath containing the .PKT files listed in pktPaths. Returns the number of files bundled. It writes to a temporary file and renames on success so a partial bundle is never left at bundlePath if an error occurs.
func DecodeFTNString ¶
DecodeFTNString converts an FTN packet header string (From, To, Subject) from its source encoding to UTF-8 for storage and display.
FTN messages traditionally use CP437 encoding for header fields. Modern software (e.g., Synchronet) may send UTF-8 encoded strings, signaled by a CHRS kludge value containing "UTF-8" (e.g., "UTF-8 4").
If chrs contains "UTF-8" (case-insensitive), the string is assumed to be valid UTF-8 and is returned unchanged. Otherwise, bytes 0x80–0xFF are treated as CP437 and converted to their Unicode equivalents.
func EnsureBinkdConf ¶ added in v0.5.0
func EnsureBinkdConf(bbsRoot string, ftnCfg config.FTNConfig, server config.ServerConfig) (created bool, err error)
EnsureBinkdConf regenerates <bbsRoot>/data/ftn/binkd.conf from configuration when the file is missing (e.g. deleted for a reset — the FTN Setup Wizard refuses to re-run for an existing network, so this is the only recovery path). It is a no-op when the file exists or when no network has a parseable own address. created is true only when a new file was written.
func ExtractBundle ¶
ExtractBundle extracts .PKT files from a ZIP bundle at srcPath into destDir. Returns the paths of extracted .PKT files.
func FormatFTNDateTime ¶
FormatFTNDateTime formats a time in FTN packed message format. Format: "DD Mon YY HH:MM:SS" (note: double space before time).
func FormatPackedMessageBody ¶
func FormatPackedMessageBody(parsed *ParsedBody) string
FormatPackedMessageBody reassembles a message body from its components. Returns the body with FTN line endings (\r).
func HasPlaceholders ¶ added in v0.3.0
HasPlaceholders reports whether binkd.conf content still contains template placeholder lines, meaning the FTN Setup Wizard has not been run. bbsRoot is the real BBS root, exempted as in isPlaceholderLine.
func IsZIPBundle ¶
IsZIPBundle reports whether the file at path begins with the ZIP magic bytes.
func ParseFTNDateTime ¶
ParseFTNDateTime parses an FTN datetime string back to time.Time.
func ReadPacket ¶
func ReadPacket(r io.Reader) (*PacketHeader, []*PackedMessage, error)
ReadPacket parses a complete .PKT file from the reader.
func RegenerateBinkdConf ¶ added in v0.4.0
func RegenerateBinkdConf(confPath string, cfg BinkdConfig, nodes []BinkdNode) error
RegenerateBinkdConf writes a complete binkd.conf from configuration alone: identity, domains, addresses, derived paths, and one node line per hub. Used when binkd.conf is missing (e.g. deleted for a reset) — the FTN Setup Wizard refuses to re-run for an existing network, so without this the file could not be recreated. The caller supplies real values; iport/loglevel are template defaults here and are corrected by SyncBinkdSettings afterwards.
func SyncBinkdConf ¶ added in v0.2.0
func SyncBinkdConf(confPath string, identity BinkdIdentity, links map[string]BinkdLinkSync) error
SyncBinkdConf updates binkd.conf to reflect the current FTN links and BBS identity fields (sysname, sysop, location). Node lines are upserted from links: an existing line (matched by address) gets its hostname and password refreshed, and a configured link with a hostname but no node line has one appended — so a hub change in the TUI fully propagates. Only lines that differ are rewritten; if nothing changed the file is not touched. Called from saveAll so TUI edits are reflected automatically.
links maps "address@network" (e.g. "21:1/100@fsxnet") to its sync values.
func SyncBinkdSettings ¶ added in v0.2.0
SyncBinkdSettings updates the iport and loglevel lines in binkd.conf to match the configured values. The file is only rewritten when a value differs; a missing binkd.conf is a no-op (the FTN Setup Wizard creates it). Non-positive port/logLevel values leave the corresponding line untouched.
func UpdateBinkdConf ¶ added in v0.2.0
func UpdateBinkdConf(confPath string, cfg BinkdConfig) error
UpdateBinkdConf reads an existing binkd.conf, strips placeholder lines, injects real domain/address/node/sysname values from cfg, and writes the result back. Existing wizard-managed node blocks (from prior runs) are preserved. If the file doesn't exist, a fresh one is generated.
func ValidateAddress ¶ added in v0.2.0
ValidateAddress checks whether s is a valid FTN address string.
func WritePacket ¶
func WritePacket(w io.Writer, hdr *PacketHeader, msgs []*PackedMessage) error
WritePacket writes a complete .PKT file to the writer.
Types ¶
type Address ¶ added in v0.2.0
Address represents an FTN address in zone:net/node.point format.
func ParseAddress ¶ added in v0.2.0
ParseAddress parses an FTN address string like "21:1/100" or "21:1/100.5".
type BinkdConfig ¶ added in v0.2.0
type BinkdConfig struct {
BBSRoot string // absolute path to BBS root directory
BoardName string // BBS name for sysname
SysopName string // sysop name (falls back to "SysOp")
Location string // BBS location (falls back to "Earth")
// Domains maps network name to zone (e.g. "fsxnet" -> 21).
Domains map[string]int
// Addresses lists all "address" lines (e.g. "21:4/158@fsxnet").
Addresses []string
// Node is the new hub node to add.
Node BinkdNode
}
BinkdConfig holds all data needed to generate or update binkd.conf.
type BinkdIdentity ¶ added in v0.2.0
type BinkdIdentity struct {
BoardName string // sysname
SysopName string // sysop
Location string // location
}
BinkdIdentity holds BBS identity fields synced to binkd.conf.
type BinkdLinkSync ¶ added in v0.4.0
BinkdLinkSync carries the per-link values synced into a binkd.conf node line. HostPort is "hostname:port"; when empty only the password of an existing line is synced and no new line is created (host unknown).
type BinkdNode ¶ added in v0.2.0
type BinkdNode struct {
Address string // FTN address (e.g. "21:1/100@fsxnet")
Hostname string // host:port
SessionPwd string // session password ("-" if none)
NetworkName string // used for section comment markers
}
BinkdNode holds the parameters for a binkd node line.
type EchoArea ¶ added in v0.2.0
EchoArea represents a single area from a backbone.na file.
func CleanEcholist ¶ added in v0.2.0
CleanEcholist applies network-specific cleanup rules to a parsed echolist. It removes areas whose tags match any exclude pattern and strips the titlePrefix from area descriptions.
func DownloadEcholist ¶ added in v0.2.0
DownloadEcholist fetches an echolist from a URL, parses it, and returns the areas. The request is bounded by the given context.
type NodeLookup ¶ added in v0.5.0
type NodeLookup struct {
Self *NodelistEntry // nil when the address is not in the nodelist
Uplink *NodelistEntry // hub/host/zone entry chosen as the uplink
Hostname string // resolved BinkP hostname for Uplink
Port int // resolved BinkP port for Uplink
Inferred bool // true when Self is nil (uplink inferred from the net segment)
}
NodeLookup is the result of resolving a node and its uplink hub.
type Nodelist ¶ added in v0.5.0
type Nodelist struct {
Entries []NodelistEntry
}
Nodelist is a parsed nodelist. Entry order is preserved because FTS-5000 expresses segment structure (which hub a node belongs to) purely by order.
func DownloadNodelist ¶ added in v0.5.0
DownloadNodelist fetches a nodelist from url and parses it. Zip payloads (detected by magic bytes, not URL extension) are unwrapped, extracting the member that looks most like a nodelist.
func ParseNodelist ¶ added in v0.5.0
ParseNodelist parses an FTS-5000 distribution nodelist. Comment lines (';'), blank lines, malformed lines, and lines before the first Zone line are skipped rather than failing the whole list.
func (*Nodelist) Lookup ¶ added in v0.5.0
func (nl *Nodelist) Lookup(addr Address, dnsSuffix string) (*NodeLookup, error)
Lookup finds addr's entry and its uplink hub. The uplink is the nearest enclosing Hub segment, else the net's Host, else the Zone entry — skipping entries that are Down/Hold or have no resolvable hostname. dnsSuffix (e.g. "binkp.net"), when non-empty, derives a hostname for uplinks that carry no INA/IBN hostname flag.
type NodelistEntry ¶ added in v0.5.0
type NodelistEntry struct {
Keyword string // "Zone", "Region", "Host", "Hub", "Pvt", "Down", "Hold", or "" for a plain node
Address Address // fully resolved zone:net/node
Name string // system name, underscores translated to spaces
Location string
Sysop string
Flags []string // raw flag fields after the baud field (e.g. "CM", "INA:host", "IBN:24556")
}
NodelistEntry is one parsed line of an FTS-5000 nodelist.
type PackedMessage ¶
type PackedMessage struct {
MsgType uint16 // Always 2 for stored messages
OrigNode uint16
DestNode uint16
OrigNet uint16
DestNet uint16
Attr uint16 // Message attribute flags
Cost uint16
DateTime string // "DD Mon YY HH:MM:SS\x00" format
To string // Max 36 chars
From string // Max 36 chars
Subject string // Max 72 chars
Body string // Full message body including kludges
}
PackedMessage represents a single message within an FTN packet.
type PacketHeader ¶
type PacketHeader struct {
OrigNode uint16
DestNode uint16
Year uint16
Month uint16 // 0-based (0=Jan)
Day uint16
Hour uint16
Minute uint16
Second uint16
Baud uint16 // Unused, set to 0
PktType uint16 // Must be 2
OrigNet uint16
DestNet uint16
ProdCode uint8
ProdRev uint8
Password [8]byte
QOrigZone uint16 // QMail orig zone
QDestZone uint16 // QMail dest zone
AuxNet uint16 // Auxiliary net (point routing)
CWCopy uint16 // Capability word validation copy (swapped)
ProdCode2 uint8 // Product code high byte
ProdRev2 uint8 // Product revision minor
CapWord uint16 // Capability word (bit 0 = Type-2+)
OrigZone uint16
DestZone uint16
OrigPoint uint16
DestPoint uint16
ProdData [4]byte // Product-specific data
}
PacketHeader represents an FTN Type-2+ packet header (58 bytes).
func NewPacketHeader ¶
func NewPacketHeader(origZone, origNet, origNode, origPoint uint16, destZone, destNet, destNode, destPoint uint16, password string) *PacketHeader
NewPacketHeader creates a header with sensible defaults for the given addresses.
func ReadPacketHeaderFromFile ¶
func ReadPacketHeaderFromFile(path string) (*PacketHeader, error)
ReadPacketHeaderFromFile reads only the 58-byte header from a .PKT file at path. This is more efficient than ReadPacket when only the destination address is needed.
type ParsedBody ¶
type ParsedBody struct {
Area string // AREA tag (echomail only, empty for netmail/local)
Kludges []string // ^A kludge lines (without the ^A prefix)
Text string // Message text (without kludges, SEEN-BY, PATH)
SeenBy []string // SEEN-BY lines (without "SEEN-BY: " prefix)
Path []string // PATH lines (without "\x01PATH: " prefix)
}
ParsedBody holds the components of a parsed FTN message body.
func ParsePackedMessageBody ¶
func ParsePackedMessageBody(body string) *ParsedBody
ParsePackedMessageBody separates an FTN message body into its components. Kludge lines start with \x01 (SOH), SEEN-BY/PATH are at the end.
type RegistryNetwork ¶ added in v0.2.0
type RegistryNetwork struct {
Zone int `json:"zone"`
Name string `json:"name"`
Description string `json:"description"`
InfoURL string `json:"info_url,omitempty"`
PackURL string `json:"pack_url,omitempty"`
Coordinator string `json:"coordinator,omitempty"`
CoordinatorEmail string `json:"coordinator_email,omitempty"`
CoordinatorFTN string `json:"coordinator_ftn,omitempty"`
AlsoContact string `json:"also_contact,omitempty"`
HubAddress string `json:"hub_address,omitempty"`
HubHostname string `json:"hub_hostname,omitempty"`
HubPort int `json:"hub_port,omitempty"`
DNSSuffix string `json:"dns_suffix,omitempty"`
EcholistURL string `json:"echolist_url,omitempty"`
NodelistURL string `json:"nodelist_url,omitempty"`
AreatagPrefix string `json:"areatag_prefix,omitempty"`
AreatagExclude []string `json:"areatag_exclude,omitempty"`
AreatitlePrefix string `json:"areatitle_prefix,omitempty"`
HandlesAllowed bool `json:"handles_allowed,omitempty"`
AreaManager string `json:"area_manager,omitempty"`
}
RegistryNetwork represents a single FTN network from the embedded registry.
func LoadOverrideRegistry ¶ added in v0.2.0
func LoadOverrideRegistry(configPath string) ([]RegistryNetwork, error)
LoadOverrideRegistry loads an optional sysop-provided ftn_networks.json from the given config directory. Returns nil (no error) if the file does not exist.
func LoadRegistry ¶ added in v0.2.0
func LoadRegistry() ([]RegistryNetwork, error)
LoadRegistry returns the embedded FTN network registry.