jam

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	Signature       = "JAM\x00"
	HeaderSize      = 1024 // Fixed header occupies first 1024 bytes of .jhr
	FixedHeaderSize = 76   // Actual data size within the fixed header
	SubfieldHdrSize = 8    // LoID(2) + HiID(2) + DatLen(4)
	IndexRecordSize = 8    // ToCRC(4) + HdrOffset(4)
	LastReadSize    = 16   // UserCRC(4) + UserID(4) + LastReadMsg(4) + HighReadMsg(4)
)

JAM file format constants

View Source
const (
	MsgLocal       = 0x00000001 // Created locally
	MsgInTransit   = 0x00000002 // In-transit
	MsgPrivate     = 0x00000004 // Private message
	MsgRead        = 0x00000008 // Read by addressee
	MsgSent        = 0x00000010 // Sent to remote
	MsgKillSent    = 0x00000020 // Kill when sent
	MsgArchiveSent = 0x00000040 // Archive when sent
	MsgHold        = 0x00000080 // Hold for pick-up
	MsgCrash       = 0x00000100 // Crash
	MsgImmediate   = 0x00000200 // Send immediately
	MsgDirect      = 0x00000400 // Send directly
	MsgGate        = 0x00000800 // Send via gateway
	MsgFileRequest = 0x00001000 // File request
	MsgFileAttach  = 0x00002000 // File(s) attached
	MsgTruncFile   = 0x00004000 // Truncate file(s)
	MsgKillFile    = 0x00008000 // Delete file(s)
	MsgReceiptReq  = 0x00010000 // Return receipt requested
	MsgConfirmReq  = 0x00020000 // Confirmation receipt requested
	MsgOrphan      = 0x00040000 // Unknown destination
	MsgEncrypt     = 0x00080000 // Encrypted
	MsgCompress    = 0x00100000 // Compressed
	MsgEscaped     = 0x00200000 // Seven bit ASCII
	MsgFPU         = 0x00400000 // Force pickup
	MsgTypeLocal   = 0x00800000 // Local use only
	MsgTypeEcho    = 0x01000000 // Conference/echo mail
	MsgTypeNet     = 0x02000000 // Direct network mail
	MsgNoDisp      = 0x20000000 // May not be displayed
	MsgLocked      = 0x40000000 // Locked
	MsgDeleted     = 0x80000000 // Deleted
)

Message attribute flags from JAM specification

View Source
const (
	SfldOAddress     = 0    // Origin network address
	SfldDAddress     = 1    // Destination network address
	SfldSenderName   = 2    // Sender name
	SfldReceiverName = 3    // Receiver name
	SfldMsgID        = 4    // Message ID (FTN MSGID)
	SfldReplyID      = 5    // Reply ID (FTN REPLY)
	SfldSubject      = 6    // Subject
	SfldPID          = 7    // Program ID
	SfldTrace        = 8    // Trace info
	SfldFTSKludge    = 2000 // FTN kludge line
	SfldSeenBy2D     = 2001 // SEEN-BY in 2D format
	SfldPath2D       = 2002 // PATH in 2D format
	SfldFlags        = 2003 // Message flags
	SfldTZUTCInfo    = 2004 // Timezone/UTC info
)

Subfield type identifiers from JAM specification

Variables

View Source
var (
	ErrInvalidSignature = errors.New("jam: invalid JAM signature")
	ErrInvalidMessage   = errors.New("jam: invalid message number")
	ErrBaseNotOpen      = errors.New("jam: message base not open")
	ErrNotFound         = errors.New("jam: not found")
)

Sentinel errors

View Source
var Version = version.Number

Version is kept for compatibility; use internal/version.Number for new code.

Functions

func AddCustomTearline

func AddCustomTearline(text, tearline string) string

AddCustomTearline appends a tearline to the message text. If tearline is empty, it uses the default ViSiON/3 tearline. If tearline already starts with "---", it is used as-is.

func AddOriginLine

func AddOriginLine(text, systemName, address string) string

AddOriginLine appends an origin line to the message text. Format: " * Origin: BBS Name (1:103/705)"

func AddTearline

func AddTearline(text string) string

AddTearline appends a tearline to the message text. Format: "--- ViSiON/3 0.1.0/darwin"

func CP437ToUnicode

func CP437ToUnicode(input []byte) string

CP437ToUnicode converts CP437-encoded bytes to a Unicode string.

func CRC32String

func CRC32String(s string) uint32

CRC32String calculates a JAM-specification CRC32 of a string. Per the JAM spec: lowercase only A-Z (not locale-aware), use IEEE polynomial, and invert the result.

func FormatPID

func FormatPID() string

FormatPID returns the PID kludge value.

func FormatTID

func FormatTID() string

FormatTID returns the TID kludge value.

Types

type Base

type Base struct {
	BasePath string
	// contains filtered or unexported fields
}

Base represents an open JAM message base backed by four files: .jhr (headers), .jdt (text), .jdx (index), .jlr (lastread).

func Open

func Open(basePath string) (*Base, error)

Open opens an existing JAM message base or creates a new one if it does not exist. basePath is the path without file extension (e.g., "data/msgbases/general").

func (*Base) Close

func (b *Base) Close() error

Close closes all file handles for the message base.

func (*Base) DeleteMessage

func (b *Base) DeleteMessage(msgNum int) error

DeleteMessage marks a message as deleted and zeroes its text length.

func (*Base) GenerateMSGID

func (b *Base) GenerateMSGID(origAddr string) (string, error)

GenerateMSGID creates a unique FTN-compatible MSGID using the base's serial counter. Format: "address hexserial" (e.g., "1:103/705 0012ab34"). Acquires b.mu internally; do not call while holding b.mu.

func (*Base) GetActiveMessageCount

func (b *Base) GetActiveMessageCount() int

GetActiveMessageCount returns the number of non-deleted messages.

func (*Base) GetAllLastReadRecords

func (b *Base) GetAllLastReadRecords() ([]LastReadRecord, error)

GetAllLastReadRecords reads all lastread records from the .jlr file.

func (*Base) GetFixedHeader

func (b *Base) GetFixedHeader() *FixedHeaderInfo

GetFixedHeader returns the fixed header info for the base.

func (*Base) GetLastRead

func (b *Base) GetLastRead(username string) (*LastReadRecord, error)

GetLastRead returns the lastread record for the given username. Returns ErrNotFound if the user has no record in this base.

func (*Base) GetMessageCount

func (b *Base) GetMessageCount() (int, error)

GetMessageCount returns the total number of messages (including deleted) by computing the number of index records in the .jdx file.

func (*Base) GetModCounter

func (b *Base) GetModCounter() (uint32, error)

GetModCounter returns the current ModCounter value from disk.

func (*Base) GetNextMsgSerial

func (b *Base) GetNextMsgSerial() (uint32, error)

GetNextMsgSerial atomically increments and returns the next MSGID serial. The serial counter is stored in Reserved[0:4] of the fixed header.

func (*Base) GetNextUnreadMessage

func (b *Base) GetNextUnreadMessage(username string) (int, error)

GetNextUnreadMessage returns the next unread message number for the user. Returns ErrNotFound if there are no unread messages.

func (*Base) GetUnreadCount

func (b *Base) GetUnreadCount(username string) (int, error)

GetUnreadCount returns the number of unread messages for the user.

func (*Base) IsOpen

func (b *Base) IsOpen() bool

IsOpen reports whether the message base is currently open.

func (b *Base) Link() (LinkResult, error)

Link rebuilds reply threading chains (ReplyTo/Reply1st/ReplyNext) by matching MSGID and ReplyID subfields across all active messages. This should be called after Pack or after deleting messages to keep threading consistent.

func (*Base) MarkMessageRead

func (b *Base) MarkMessageRead(username string, msgNum int) error

MarkMessageRead updates the lastread pointer after reading a message.

func (*Base) Pack

func (b *Base) Pack() (PackResult, error)

Pack defragments the message base by rewriting all non-deleted messages to new files, then atomically replacing the originals. The .jlr file is preserved as-is.

func (*Base) PackWithReplyIDCleanup

func (b *Base) PackWithReplyIDCleanup() (PackResult, error)

PackWithReplyIDCleanup performs a pack operation while cleaning malformed ReplyIDs.

func (*Base) ReadIndexRecord

func (b *Base) ReadIndexRecord(msgNum int) (*IndexRecord, error)

ReadIndexRecord reads the index record for a 1-based message number.

func (*Base) ReadMessage

func (b *Base) ReadMessage(msgNum int) (*Message, error)

ReadMessage reads a complete message (header + subfields + text) for the given 1-based message number.

func (*Base) ReadMessageHeader

func (b *Base) ReadMessageHeader(msgNum int) (*MessageHeader, error)

ReadMessageHeader reads a message header for the given 1-based message number.

func (*Base) ReadMessageText

func (b *Base) ReadMessageText(hdr *MessageHeader) (string, error)

ReadMessageText reads the raw message text (CP437) for the given header.

func (*Base) RefreshFixedHeader

func (b *Base) RefreshFixedHeader() (*FixedHeaderInfo, error)

RefreshFixedHeader reloads the fixed header from disk. Useful when external tools modify the base.

func (*Base) ResetLastRead

func (b *Base) ResetLastRead(username string) error

ResetLastRead zeroes a user's lastread and highread pointers.

func (*Base) ScanMessages

func (b *Base) ScanMessages(startMsg, maxMessages int) ([]*Message, error)

ScanMessages reads up to maxMessages starting from startMsg (1-based), skipping deleted messages. If maxMessages is 0, reads all.

func (*Base) SetLastRead

func (b *Base) SetLastRead(username string, lastRead, highRead uint32) error

SetLastRead updates or creates a lastread record for the given username.

func (*Base) UpdateMessageHeader

func (b *Base) UpdateMessageHeader(msgNum int, hdr *MessageHeader) error

UpdateMessageHeader rewrites an existing message header in place. This is used by the tosser to update DateProcessed after export.

func (*Base) WriteMessage

func (b *Base) WriteMessage(msg *Message) (int, error)

WriteMessage writes a complete local message to the base. Returns the 1-based message number assigned to the new message.

func (*Base) WriteMessageExt

func (b *Base) WriteMessageExt(msg *Message, msgType MessageType, echoTag, bbsName, tearline string) (int, error)

WriteMessageExt writes a message with full echomail support. For echomail messages it generates a MSGID, adds AREA/PID/TID kludges, tearline, and origin line. DateProcessed is set to 0 so the tosser knows to export it.

For local messages this behaves identically to WriteMessage.

type FidoAddress

type FidoAddress struct {
	Zone  int
	Net   int
	Node  int
	Point int
}

FidoAddress represents a parsed FidoNet 4D address (Zone:Net/Node.Point).

func ParseAddress

func ParseAddress(addr string) (*FidoAddress, error)

ParseAddress parses a FidoNet address string in the format "Z:N/N" or "Z:N/N.P".

func (*FidoAddress) String

func (a *FidoAddress) String() string

String returns the full 4D address. Point is omitted if zero.

func (*FidoAddress) String2D

func (a *FidoAddress) String2D() string

String2D returns the 2D address format (net/node) used for SEEN-BY and PATH.

type FixedHeaderInfo

type FixedHeaderInfo struct {
	Signature   [4]byte
	DateCreated uint32
	ModCounter  uint32
	ActiveMsgs  uint32
	PasswordCRC uint32
	BaseMsgNum  uint32
	Reserved    [1000]byte
}

FixedHeaderInfo represents the JAM base header (1024 bytes on disk). The first 76 bytes contain structured data; bytes 76-1023 are reserved. Reserved[0:4] stores the MSGID serial counter.

type IndexRecord

type IndexRecord struct {
	ToCRC     uint32 // CRC32 of lowercase recipient name
	HdrOffset uint32 // Byte offset of header in .jhr
}

IndexRecord represents an entry in the .jdx index file (8 bytes).

type LastReadRecord

type LastReadRecord struct {
	UserCRC     uint32 // CRC32 of lowercase username
	UserID      uint32
	LastReadMsg uint32
	HighReadMsg uint32
}

LastReadRecord represents a per-user lastread entry in the .jlr file (16 bytes).

type LinkResult

type LinkResult struct {
	MessagesScanned int
	LinksUpdated    int
}

LinkResult contains statistics from a Link operation.

type Message

type Message struct {
	Header   *MessageHeader
	From     string
	To       string
	Subject  string
	DateTime time.Time
	Text     string // CP437-encoded message body
	OrigAddr string // FidoNet origin address
	DestAddr string // FidoNet destination address
	MsgID    string
	ReplyID  string
	ReplyTo  uint32 // JAM parent message number (0 = none); written to the header
	PID      string
	Flags    string
	SeenBy   string
	Path     string
	Kludges  []string
}

Message is a high-level message structure combining header data, subfield-parsed fields, and message text.

func NewMessage

func NewMessage() *Message

NewMessage creates a new Message with default values.

func (*Message) GetAttribute

func (m *Message) GetAttribute() uint32

GetAttribute returns the JAM attribute flags for this message.

func (*Message) IsDeleted

func (m *Message) IsDeleted() bool

IsDeleted reports whether the message has been marked as deleted.

func (*Message) IsPrivate

func (m *Message) IsPrivate() bool

IsPrivate reports whether the message is private.

type MessageHeader

type MessageHeader struct {
	Signature     [4]byte
	Revision      uint16
	ReservedWord  uint16
	SubfieldLen   uint32
	TimesRead     uint32
	MSGIDcrc      uint32
	REPLYcrc      uint32
	ReplyTo       uint32
	Reply1st      uint32
	ReplyNext     uint32
	DateWritten   uint32
	DateReceived  uint32
	DateProcessed uint32
	MessageNumber uint32
	Attribute     uint32
	Attribute2    uint32
	Offset        uint32 // Offset into .jdt file
	TxtLen        uint32 // Length of text in .jdt
	PasswordCRC   uint32
	Cost          uint32
	Subfields     []Subfield
}

MessageHeader represents a JAM message header stored in the .jhr file.

func (*MessageHeader) GetAllSubfieldsByType

func (h *MessageHeader) GetAllSubfieldsByType(fieldType uint16) []Subfield

GetAllSubfieldsByType returns all subfields matching the given type.

func (*MessageHeader) GetSubfieldByType

func (h *MessageHeader) GetSubfieldByType(fieldType uint16) *Subfield

GetSubfieldByType returns the first subfield matching the given type, or nil if none is found.

type MessageType

type MessageType int

MessageType represents the type of message being created.

const (
	MsgTypeLocalMsg    MessageType = iota // Local BBS-only message
	MsgTypeEchomailMsg                    // FTN conference/echo message
	MsgTypeNetmailMsg                     // FTN direct network mail
)

func DetermineMessageType

func DetermineMessageType(areaType, echoTag string) MessageType

DetermineMessageType returns the MessageType based on area configuration.

func (MessageType) GetJAMAttribute

func (mt MessageType) GetJAMAttribute() uint32

GetJAMAttribute returns the JAM attribute flags for this message type.

func (MessageType) IsEchomail

func (mt MessageType) IsEchomail() bool

IsEchomail reports whether this is an echomail message.

func (MessageType) IsLocal

func (mt MessageType) IsLocal() bool

IsLocal reports whether this is a local message.

func (MessageType) IsNetmail

func (mt MessageType) IsNetmail() bool

IsNetmail reports whether this is a netmail message.

type PackResult

type PackResult struct {
	MessagesBefore int
	MessagesAfter  int
	DeletedRemoved int
	BytesBefore    int64
	BytesAfter     int64
}

PackResult contains statistics from a Pack operation.

type Subfield

type Subfield struct {
	LoID   uint16
	HiID   uint16
	DatLen uint32
	Buffer []byte
}

Subfield represents a variable-length field attached to a message header.

func CreateSubfield

func CreateSubfield(fieldType uint16, data string) Subfield

CreateSubfield creates a Subfield from a type identifier and string data.

Jump to

Keyboard shortcuts

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