hotline

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2024 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package hotline provides Hotline client and server implementations.

Index

Constants

View Source
const (
	AccessDeleteFile       = 0  // File System Maintenance: Can Delete Files
	AccessUploadFile       = 1  // File System Maintenance: Can Upload Files
	AccessDownloadFile     = 2  // File System Maintenance: Can Download Files
	AccessRenameFile       = 3  // File System Maintenance: Can Rename Files
	AccessMoveFile         = 4  // File System Maintenance: Can Move Files
	AccessCreateFolder     = 5  // File System Maintenance: Can Create Folders
	AccessDeleteFolder     = 6  // File System Maintenance: Can Delete Folders
	AccessRenameFolder     = 7  // File System Maintenance: Can Rename Folders
	AccessMoveFolder       = 8  // File System Maintenance: Can Move Folders
	AccessReadChat         = 9  // Chat: Can Read Chat
	AccessSendChat         = 10 // Chat: Can Send Chat
	AccessOpenChat         = 11 // Chat: Can Initial Private Chat
	AccessCloseChat        = 12 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessShowInList       = 13 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessCreateUser       = 14 // User Maintenance: Can Create Accounts
	AccessDeleteUser       = 15 // User Maintenance: Can Delete Accounts
	AccessOpenUser         = 16 // User Maintenance: Can Read Accounts
	AccessModifyUser       = 17 // User Maintenance: Can Modify Accounts
	AccessChangeOwnPass    = 18 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessNewsReadArt      = 20 // News: Can Read Articles
	AccessNewsPostArt      = 21 // News: Can Post Articles
	AccessDisconUser       = 22 // User Maintenance: Can Disconnect Users (Note: Turns username red in user list)
	AccessCannotBeDiscon   = 23 // User Maintenance: Cannot be Disconnected
	AccessGetClientInfo    = 24 // User Maintenance: Can Get User Info
	AccessUploadAnywhere   = 25 // File System Maintenance: Can Upload Anywhere
	AccessAnyName          = 26 // Miscellaneous: Can User Any Name
	AccessNoAgreement      = 27 // Miscellaneous: Don't Show Agreement
	AccessSetFileComment   = 28 // File System Maintenance: Can Comment Files
	AccessSetFolderComment = 29 // File System Maintenance: Can Comment Folders
	AccessViewDropBoxes    = 30 // File System Maintenance: Can View Drop Boxes
	AccessMakeAlias        = 31 // File System Maintenance: Can Make Aliases
	AccessBroadcast        = 32 // Messaging: Can Broadcast
	AccessNewsDeleteArt    = 33 // News: Can Delete Articles
	AccessNewsCreateCat    = 34 // News: Can Create Categories
	AccessNewsDeleteCat    = 35 // News: Can Delete Categories
	AccessNewsCreateFldr   = 36 // News: Can Create News Bundles
	AccessNewsDeleteFldr   = 37 // News: Can Delete News Bundles
	AccessUploadFolder     = 38 // File System Maintenance: Can Upload Folders
	AccessDownloadFolder   = 39 // File System Maintenance: Can Download Folders
	AccessSendPrivMsg      = 40 // Messaging: Can Send Messages (Note: 1.9 protocol doc incorrectly says this is bit 19)
)
View Source
const (
	DlFldrActionSendFile   = 1
	DlFldrActionResumeFile = 2
	DlFldrActionNextFile   = 3
)

Folder download actions. Send by the client to indicate the next action the server should take for a folder download.

View Source
const (
	FileDownload   = FileTransferType(0)
	FileUpload     = FileTransferType(1)
	FolderDownload = FileTransferType(2)
	FolderUpload   = FileTransferType(3)
	BannerDownload = FileTransferType(4)
)
View Source
const (
	IncompleteFileSuffix = ".incomplete"
	InfoForkNameTemplate = ".info_%s" // template string for info fork filenames
	RsrcForkNameTemplate = ".rsrc_%s" // template string for resource fork filenames
)
View Source
const (
	StatCurrentlyConnected = iota
	StatDownloadsInProgress
	StatUploadsInProgress
	StatWaitingDownloads
	StatConnectionPeak
	StatConnectionCounter
	StatDownloadCounter
	StatUploadCounter
)

Stat counter keys

View Source
const (
	UserFlagAway        = 0 // User is away
	UserFlagAdmin       = 1 // User is admin
	UserFlagRefusePM    = 2 // User refuses private messages
	UserFlagRefusePChat = 3 // User refuses private chat
)

User flags are stored as a 2 byte bitmap and represent various user states

View Source
const (
	UserOptRefusePM     = 0 // User has "Refuse private messages" pref set
	UserOptRefuseChat   = 1 // User has "Refuse private chat" pref set
	UserOptAutoResponse = 2 // User has "Automatic response" pref set
)

User options are sent from clients and represent options set in the client's preferences.

View Source
const BanDuration = 30 * time.Minute

BanDuration is the length of time for temporary bans.

View Source
const GuestAccount = "guest" // default account used when no login is provided for a connection
View Source
const LimitChatMsg = 8192

The total size of a chat message data field is 8192 bytes.

View Source
const NewsDateFormat = "Jan02 15:04" // Jun23 20:49
View Source
const NewsTemplate = `From %s (%s):

%s

__________________________________________________________`

Variables

View Source
var (
	FieldError               = [2]byte{0x00, 0x64} // 100
	FieldData                = [2]byte{0x00, 0x65} // 101
	FieldUserName            = [2]byte{0x00, 0x66} // 102
	FieldUserID              = [2]byte{0x00, 0x67} // 103
	FieldUserIconID          = [2]byte{0x00, 0x68} // 104
	FieldUserLogin           = [2]byte{0x00, 0x69} // 105
	FieldUserPassword        = [2]byte{0x00, 0x6A} // 106
	FieldRefNum              = [2]byte{0x00, 0x6B} // 107
	FieldTransferSize        = [2]byte{0x00, 0x6C} // 108
	FieldChatOptions         = [2]byte{0x00, 0x6D} // 109
	FieldUserAccess          = [2]byte{0x00, 0x6E} // 110
	FieldUserFlags           = [2]byte{0x00, 0x70} // 112
	FieldOptions             = [2]byte{0x00, 0x71} // 113
	FieldChatID              = [2]byte{0x00, 0x72} // 114
	FieldChatSubject         = [2]byte{0x00, 0x73} // 115
	FieldWaitingCount        = [2]byte{0x00, 0x74} // 116
	FieldBannerType          = [2]byte{0x00, 0x98} // 152
	FieldNoServerAgreement   = [2]byte{0x00, 0x98} // 152
	FieldVersion             = [2]byte{0x00, 0xA0} // 160
	FieldCommunityBannerID   = [2]byte{0x00, 0xA1} // 161
	FieldServerName          = [2]byte{0x00, 0xA2} // 162
	FieldFileNameWithInfo    = [2]byte{0x00, 0xC8} // 200
	FieldFileName            = [2]byte{0x00, 0xC9} // 201
	FieldFilePath            = [2]byte{0x00, 0xCA} // 202
	FieldFileResumeData      = [2]byte{0x00, 0xCB} // 203
	FieldFileTransferOptions = [2]byte{0x00, 0xCC} // 204
	FieldFileTypeString      = [2]byte{0x00, 0xCD} // 205
	FieldFileCreatorString   = [2]byte{0x00, 0xCE} // 206
	FieldFileSize            = [2]byte{0x00, 0xCF} // 207
	FieldFileCreateDate      = [2]byte{0x00, 0xD0} // 208
	FieldFileModifyDate      = [2]byte{0x00, 0xD1} // 209
	FieldFileComment         = [2]byte{0x00, 0xD2} // 210
	FieldFileNewName         = [2]byte{0x00, 0xD3} // 211
	FieldFileNewPath         = [2]byte{0x00, 0xD4} // 212
	FieldFileType            = [2]byte{0x00, 0xD5} // 213
	FieldQuotingMsg          = [2]byte{0x00, 0xD6} // 214
	FieldAutomaticResponse   = [2]byte{0x00, 0xD7} // 215
	FieldFolderItemCount     = [2]byte{0x00, 0xDC} // 220
	FieldUsernameWithInfo    = [2]byte{0x01, 0x2C} // 300
	FieldNewsArtListData     = [2]byte{0x01, 0x41} // 321
	FieldNewsCatName         = [2]byte{0x01, 0x42} // 322
	FieldNewsCatListData15   = [2]byte{0x01, 0x43} // 323
	FieldNewsPath            = [2]byte{0x01, 0x45} // 325
	FieldNewsArtID           = [2]byte{0x01, 0x46} // 326
	FieldNewsArtDataFlav     = [2]byte{0x01, 0x47} // 327
	FieldNewsArtTitle        = [2]byte{0x01, 0x48} // 328
	FieldNewsArtPoster       = [2]byte{0x01, 0x49} // 329
	FieldNewsArtDate         = [2]byte{0x01, 0x4A} // 330
	FieldNewsArtPrevArt      = [2]byte{0x01, 0x4B} // 331
	FieldNewsArtNextArt      = [2]byte{0x01, 0x4C} // 332
	FieldNewsArtData         = [2]byte{0x01, 0x4D} // 333
	FieldNewsArtParentArt    = [2]byte{0x01, 0x4F} // 335
	FieldNewsArt1stChildArt  = [2]byte{0x01, 0x50} // 336
	FieldNewsArtRecurseDel   = [2]byte{0x01, 0x51} // 337

)

List of Hotline protocol field types taken from the official 1.9 protocol document

View Source
var (
	NewsBundle   = [2]byte{0, 2}
	NewsCategory = [2]byte{0, 3}
)
View Source
var (
	NewsFlavorLen = []byte{0x0a}
	NewsFlavor    = []byte("text/plain")
)
View Source
var (
	TranError                = TranType{0x00, 0x00} // 0
	TranGetMsgs              = TranType{0x00, 0x65} // 101
	TranNewMsg               = TranType{0x00, 0x66} // 102
	TranOldPostNews          = TranType{0x00, 0x67} // 103
	TranServerMsg            = TranType{0x00, 0x68} // 104
	TranChatSend             = TranType{0x00, 0x69} // 105
	TranChatMsg              = TranType{0x00, 0x6A} // 106
	TranLogin                = TranType{0x00, 0x6B} // 107
	TranSendInstantMsg       = TranType{0x00, 0x6C} // 108
	TranShowAgreement        = TranType{0x00, 0x6D} // 109
	TranDisconnectUser       = TranType{0x00, 0x6E} // 110
	TranDisconnectMsg        = TranType{0x00, 0x6F} // 111
	TranInviteNewChat        = TranType{0x00, 0x70} // 112
	TranInviteToChat         = TranType{0x00, 0x71} // 113
	TranRejectChatInvite     = TranType{0x00, 0x72} // 114
	TranJoinChat             = TranType{0x00, 0x73} // 115
	TranLeaveChat            = TranType{0x00, 0x74} // 116
	TranNotifyChatChangeUser = TranType{0x00, 0x75} // 117
	TranNotifyChatDeleteUser = TranType{0x00, 0x76} // 118
	TranNotifyChatSubject    = TranType{0x00, 0x77} // 119
	TranSetChatSubject       = TranType{0x00, 0x78} // 120
	TranAgreed               = TranType{0x00, 0x79} // 121
	TranServerBanner         = TranType{0x00, 0x7A} // 122
	TranGetFileNameList      = TranType{0x00, 0xC8} // 200
	TranDownloadFile         = TranType{0x00, 0xCA} // 202
	TranUploadFile           = TranType{0x00, 0xCB} // 203
	TranNewFolder            = TranType{0x00, 0xCD} // 205
	TranDeleteFile           = TranType{0x00, 0xCC} // 204
	TranGetFileInfo          = TranType{0x00, 0xCE} // 206
	TranSetFileInfo          = TranType{0x00, 0xCF} // 207
	TranMoveFile             = TranType{0x00, 0xD0} // 208
	TranMakeFileAlias        = TranType{0x00, 0xD1} // 209
	TranDownloadFldr         = TranType{0x00, 0xD2} // 210
	TranDownloadInfo         = TranType{0x00, 0xD3} // 211
	TranDownloadBanner       = TranType{0x00, 0xD4} // 212
	TranUploadFldr           = TranType{0x00, 0xD5} // 213
	TranGetUserNameList      = TranType{0x01, 0x2C} // 300
	TranNotifyChangeUser     = TranType{0x01, 0x2D} // 301
	TranNotifyDeleteUser     = TranType{0x01, 0x2E} // 302
	TranGetClientInfoText    = TranType{0x01, 0x2F} // 303
	TranSetClientUserInfo    = TranType{0x01, 0x30} // 304
	TranListUsers            = TranType{0x01, 0x5C} // 348
	TranUpdateUser           = TranType{0x01, 0x5D} // 349
	TranNewUser              = TranType{0x01, 0x5E} // 350
	TranDeleteUser           = TranType{0x01, 0x5F} // 351
	TranGetUser              = TranType{0x01, 0x60} // 352
	TranSetUser              = TranType{0x01, 0x61} // 353
	TranUserAccess           = TranType{0x01, 0x62} // 354
	TranUserBroadcast        = TranType{0x01, 0x63} // 355
	TranGetNewsCatNameList   = TranType{0x01, 0x72} // 370
	TranGetNewsArtNameList   = TranType{0x01, 0x73} // 371
	TranDelNewsItem          = TranType{0x01, 0x7C} // 380
	TranNewNewsFldr          = TranType{0x01, 0x7D} // 381
	TranNewNewsCat           = TranType{0x01, 0x7E} // 382
	TranGetNewsArtData       = TranType{0x01, 0x90} // 400
	TranPostNewsArt          = TranType{0x01, 0x9A} // 410
	TranDelNewsArt           = TranType{0x01, 0x9B} // 411
	TranKeepAlive            = TranType{0x01, 0xF4} // 500
)
View Source
var ClientHandshake = []byte{
	0x54, 0x52, 0x54, 0x50,
	0x48, 0x4f, 0x54, 0x4c,
	0x00, 0x01,
	0x00, 0x02,
}
View Source
var HTXF = [4]byte{0x48, 0x54, 0x58, 0x46} // (HTXF) is the only supported transfer protocol
View Source
var ServerHandshake = []byte{
	0x54, 0x52, 0x54, 0x50,
	0x00, 0x00, 0x00, 0x00,
}

Functions

func CalcItemCount

func CalcItemCount(filePath string) ([]byte, error)

CalcItemCount recurses through a file path and counts the number of non-hidden files.

func CalcTotalSize

func CalcTotalSize(filePath string) ([]byte, error)

func DownloadFolderHandler added in v0.13.0

func DownloadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileTransfer, fileStore FileStore, rLogger *slog.Logger, preserveForks bool) error

func DownloadHandler added in v0.13.0

func DownloadHandler(w io.Writer, fullPath string, fileTransfer *FileTransfer, fs FileStore, rLogger *slog.Logger, preserveForks bool) error

func EncodeFilePath

func EncodeFilePath(filePath string) []byte

func EncodeString added in v0.13.0

func EncodeString(clearText []byte) []byte

EncodeString takes []byte s containing cleartext and rotates by 255 into obfuscated cleartext. The Hotline protocol uses this format for sending passwords over network. Not secure, but hey, it was the 90s!

func FieldScanner added in v0.13.0

func FieldScanner(data []byte, _ bool) (advance int, token []byte, err error)

FieldScanner implements bufio.SplitFunc for parsing byte slices into complete tokens

func HashAndSalt added in v0.13.0

func HashAndSalt(pwd []byte) string

HashAndSalt generates a password hash from a users obfuscated plaintext password

func NewFileWrapper added in v0.13.0

func NewFileWrapper(fs FileStore, path string, dataOffset int64) (*fileWrapper, error)

func ReadPath added in v0.13.0

func ReadPath(fileRoot string, filePath, fileName []byte) (fullPath string, err error)

func UploadFolderHandler added in v0.13.0

func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileTransfer, fileStore FileStore, rLogger *slog.Logger, preserveForks bool) error

func UploadHandler added in v0.13.0

func UploadHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileTransfer, fileStore FileStore, rLogger *slog.Logger, preserveForks bool) error

func WithConfig added in v0.13.0

func WithConfig(config Config) func(s *Server)

func WithInterface added in v0.13.0

func WithInterface(netInterface string) func(s *Server)

WithInterface optionally sets a specific interface to listen on.

func WithLogger added in v0.13.0

func WithLogger(logger *slog.Logger) func(s *Server)

func WithPort added in v0.13.0

func WithPort(port int) func(s *Server)

WithPort optionally overrides the default TCP port.

Types

type AccessBitmap added in v0.13.0

type AccessBitmap [8]byte

func (*AccessBitmap) IsSet added in v0.13.0

func (bits *AccessBitmap) IsSet(i int) bool

func (AccessBitmap) MarshalYAML added in v0.17.0

func (bits AccessBitmap) MarshalYAML() (interface{}, error)

func (*AccessBitmap) Set added in v0.13.0

func (bits *AccessBitmap) Set(i int)

func (*AccessBitmap) UnmarshalYAML added in v0.17.0

func (bits *AccessBitmap) UnmarshalYAML(unmarshal func(interface{}) error) error

type Account

type Account struct {
	Login    string       `yaml:"Login"`
	Name     string       `yaml:"Name"`
	Password string       `yaml:"Password"`
	Access   AccessBitmap `yaml:"Access"`
	FileRoot string       `yaml:"FileRoot"`
	// contains filtered or unexported fields
}

func NewAccount added in v0.13.0

func NewAccount(login, name, password string, access AccessBitmap) *Account

func (*Account) Read added in v0.7.0

func (a *Account) Read(p []byte) (int, error)

Read implements io.Reader interface for Account

type AccountManager added in v0.13.0

type AccountManager interface {
	Create(account Account) error
	Update(account Account, newLogin string) error
	Get(login string) *Account
	List() []Account
	Delete(login string) error
}

type BanMgr added in v0.13.0

type BanMgr interface {
	Add(ip string, until *time.Time) error
	IsBanned(ip string) (bool, *time.Time)
}

type ChatID added in v0.13.0

type ChatID [4]byte

type ChatManager added in v0.13.0

type ChatManager interface {
	New(cc *ClientConn) ChatID
	GetSubject(id ChatID) string
	Join(id ChatID, cc *ClientConn)
	Leave(id ChatID, clientID [2]byte)
	SetSubject(id ChatID, subject string)
	Members(id ChatID) []*ClientConn
}

type Client

type Client struct {
	Connection net.Conn
	Logger     *slog.Logger
	Pref       *ClientPrefs
	Handlers   map[[2]byte]ClientHandler

	UserList []User
	// contains filtered or unexported fields
}

func NewClient

func NewClient(username string, logger *slog.Logger) *Client

func (*Client) Connect added in v0.10.19

func (c *Client) Connect(address, login, passwd string) (err error)

JoinServer connects to a Hotline server and completes the login flow

func (*Client) Disconnect

func (c *Client) Disconnect() error

func (*Client) HandleFunc added in v0.10.20

func (c *Client) HandleFunc(tranType [2]byte, handler ClientHandler)

func (*Client) HandleTransaction

func (c *Client) HandleTransaction(ctx context.Context, t *Transaction) error

func (*Client) HandleTransactions added in v0.10.19

func (c *Client) HandleTransactions(ctx context.Context) error

func (*Client) Handshake

func (c *Client) Handshake() error

func (*Client) Send

func (c *Client) Send(t Transaction) error

type ClientConn

type ClientConn struct {
	Connection io.ReadWriteCloser
	RemoteAddr string
	ID         ClientID
	Icon       []byte // TODO: make fixed size of 2
	Version    []byte // TODO: make fixed size of 2

	FlagsMU sync.Mutex // TODO: move into UserFlags struct
	Flags   UserFlags

	UserName  []byte
	Account   *Account
	IdleTime  int
	Server    *Server // TODO: consider adding methods to interact with server
	AutoReply []byte

	ClientFileTransferMgr ClientFileTransferMgr

	Logger *slog.Logger
	// contains filtered or unexported fields
}

ClientConn represents a client connected to a Server

func (*ClientConn) Authenticate

func (cc *ClientConn) Authenticate(login string, password []byte) bool

func (*ClientConn) Authorize

func (cc *ClientConn) Authorize(access int) bool

Authorize checks if the user account has the specified permission

func (*ClientConn) Disconnect

func (cc *ClientConn) Disconnect()

Disconnect notifies other clients that a client has disconnected and closes the connection.

func (*ClientConn) FileRoot added in v0.14.0

func (cc *ClientConn) FileRoot() string

func (*ClientConn) NewErrReply

func (cc *ClientConn) NewErrReply(t *Transaction, errMsg string) []Transaction

NewErrReply returns an error reply Transaction with errMsg

func (*ClientConn) NewFileTransfer added in v0.13.0

func (cc *ClientConn) NewFileTransfer(transferType FileTransferType, fileroot string, fileName, filePath, size []byte) *FileTransfer

func (*ClientConn) NewReply

func (cc *ClientConn) NewReply(t *Transaction, fields ...Field) Transaction

NewReply returns a reply Transaction with fields for the ClientConn

func (*ClientConn) NotifyOthers

func (cc *ClientConn) NotifyOthers(t Transaction) (trans []Transaction)

NotifyOthers sends transaction t to other clients connected to the server

func (*ClientConn) SendAll added in v0.13.0

func (cc *ClientConn) SendAll(t [2]byte, fields ...Field)

func (*ClientConn) String added in v0.7.0

func (cc *ClientConn) String() string

type ClientFileTransferMgr added in v0.13.0

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

func NewClientFileTransferMgr added in v0.13.0

func NewClientFileTransferMgr() ClientFileTransferMgr

func (*ClientFileTransferMgr) Add added in v0.13.0

func (cftm *ClientFileTransferMgr) Add(ftType FileTransferType, ft *FileTransfer)

func (*ClientFileTransferMgr) Delete added in v0.13.0

func (cftm *ClientFileTransferMgr) Delete(ftType FileTransferType, id FileTransferID)

func (*ClientFileTransferMgr) Get added in v0.13.0

type ClientHandler added in v0.10.20

type ClientHandler func(context.Context, *Client, *Transaction) ([]Transaction, error)

type ClientID added in v0.13.0

type ClientID [2]byte

type ClientManager added in v0.13.0

type ClientManager interface {
	List() []*ClientConn // Returns list of sorted clients
	Get(id ClientID) *ClientConn
	Add(cc *ClientConn)
	Delete(id ClientID)
}

type ClientPrefs

type ClientPrefs struct {
	Username   string `yaml:"Username"`
	IconID     int    `yaml:"IconID"`
	Tracker    string `yaml:"Tracker"`
	EnableBell bool   `yaml:"EnableBell"`
}

func (*ClientPrefs) IconBytes

func (cp *ClientPrefs) IconBytes() []byte

type ClientTHandler added in v0.10.19

type ClientTHandler interface {
	Handle(*Client, *Transaction) ([]Transaction, error)
}

type ClientTransaction added in v0.10.19

type ClientTransaction struct {
	Name    string
	Handler func(*Client, *Transaction) ([]Transaction, error)
}

func (ClientTransaction) Handle added in v0.10.19

func (ch ClientTransaction) Handle(cc *Client, t *Transaction) ([]Transaction, error)

type Config

type Config struct {
	Name                      string   `yaml:"Name" validate:"required,max=50"`         // Name used for Tracker registration
	Description               string   `yaml:"Description" validate:"required,max=200"` // Description used for Tracker registration
	BannerFile                string   `yaml:"BannerFile"`                              // Path to Banner jpg
	FileRoot                  string   `yaml:"FileRoot" validate:"required"`            // Path to Files
	EnableTrackerRegistration bool     `yaml:"EnableTrackerRegistration"`               // Toggle Tracker Registration
	Trackers                  []string `yaml:"Trackers" validate:"dive,hostname_port"`  // List of trackers that the server should register with
	NewsDelimiter             string   `yaml:"NewsDelimiter"`                           // String used to separate news posts
	NewsDateFormat            string   `yaml:"NewsDateFormat"`                          // Go template string to customize news date format
	MaxDownloads              int      `yaml:"MaxDownloads"`                            // Global simultaneous download limit
	MaxDownloadsPerClient     int      `yaml:"MaxDownloadsPerClient"`                   // Per client simultaneous download limit
	MaxConnectionsPerIP       int      `yaml:"MaxConnectionsPerIP"`                     // Max connections per IP
	PreserveResourceForks     bool     `yaml:"PreserveResourceForks"`                   // Enable preservation of file info and resource forks in sidecar files
	IgnoreFiles               []string `yaml:"IgnoreFiles"`                             // List of regular expression for filtering files from the file list
	EnableBonjour             bool     `yaml:"EnableBonjour"`                           // Enable service announcement on local network with Bonjour
}

type Counter added in v0.13.0

type Counter interface {
	Increment(keys ...int)
	Decrement(key int)
	Set(key, val int)
	Get(key int) int
	Values() map[string]interface{}
}

type Dialer added in v0.13.0

type Dialer interface {
	Dial(network, address string) (net.Conn, error)
}

Dialer interface to abstract the dialing operation

type Field

type Field struct {
	Type      [2]byte // Type of field
	FieldSize [2]byte // Size of the data field
	Data      []byte  // Field data
	// contains filtered or unexported fields
}

func GetField added in v0.13.0

func GetField(id [2]byte, fields *[]Field) *Field

func GetFileNameList added in v0.13.0

func GetFileNameList(path string, ignoreList []string) (fields []Field, err error)

func NewField

func NewField(fieldType [2]byte, data []byte) Field

func (*Field) DecodeInt added in v0.13.0

func (f *Field) DecodeInt() (int, error)

DecodeInt decodes the field bytes to an int. The official Hotline clients will send uint32s as 2 bytes if possible, but some third party clients such as Frogblast and Heildrun will always send 4 bytes

func (*Field) DecodeNewsPath added in v0.13.0

func (f *Field) DecodeNewsPath() ([]string, error)

DecodeNewsPath decodes the field data to a news path. Example News Path data for a Category nested under two Bundles: 00000000 00 03 00 00 10 54 6f 70 20 4c 65 76 65 6c 20 42 |.....Top Level B| 00000010 75 6e 64 6c 65 00 00 13 53 65 63 6f 6e 64 20 4c |undle...Second L| 00000020 65 76 65 6c 20 42 75 6e 64 6c 65 00 00 0f 4e 65 |evel Bundle...Ne| 00000030 73 74 65 64 20 43 61 74 65 67 6f 72 79 |sted Category|

func (*Field) DecodeObfuscatedString added in v0.13.0

func (f *Field) DecodeObfuscatedString() string

func (*Field) Read added in v0.13.0

func (f *Field) Read(p []byte) (int, error)

Read implements io.Reader for Field

func (*Field) Write added in v0.13.0

func (f *Field) Write(p []byte) (int, error)

Write implements io.Writer for Field

type FileHeader

type FileHeader struct {
	Size     [2]byte // Total size of FileHeader payload
	Type     [2]byte // 0 for file, 1 for dir
	FilePath []byte  // encoded file path
	// contains filtered or unexported fields
}

func NewFileHeader

func NewFileHeader(fileName string, isDir bool) FileHeader

func (*FileHeader) Read added in v0.13.0

func (fh *FileHeader) Read(p []byte) (int, error)

type FileNameWithInfo

type FileNameWithInfo struct {
	FileNameWithInfoHeader
	Name []byte // File Name
	// contains filtered or unexported fields
}

func (*FileNameWithInfo) Read added in v0.13.0

func (f *FileNameWithInfo) Read(p []byte) (int, error)

Read implements io.Reader for FileNameWithInfo

func (*FileNameWithInfo) Write added in v0.13.0

func (f *FileNameWithInfo) Write(p []byte) (int, error)

type FileNameWithInfoHeader added in v0.13.0

type FileNameWithInfoHeader struct {
	Type       [4]byte // File type code
	Creator    [4]byte // File creator code
	FileSize   [4]byte // File Size in bytes
	RSVD       [4]byte
	NameScript [2]byte // ??
	NameSize   [2]byte // Length of Name field
}

FileNameWithInfoHeader contains the fixed length fields of FileNameWithInfo

type FilePath

type FilePath struct {
	ItemCount [2]byte
	Items     []FilePathItem
}

func (*FilePath) IsDropbox added in v0.2.0

func (fp *FilePath) IsDropbox() bool

IsDropbox checks if a FilePath matches the special drop box folder type

func (*FilePath) IsUploadDir added in v0.2.0

func (fp *FilePath) IsUploadDir() bool

func (*FilePath) Len

func (fp *FilePath) Len() uint16

func (*FilePath) Write added in v0.8.3

func (fp *FilePath) Write(b []byte) (n int, err error)

Write implements io.Writer interface for FilePath

type FilePathItem

type FilePathItem struct {
	Len  byte
	Name []byte
}

FilePathItem represents the file or directory portion of a delimited file path (e.g. foo and bar in "/foo/bar") Example bytes: 00 00 09 73 75 62 66 6f 6c 64 65 72 "subfolder"

func (*FilePathItem) Write added in v0.8.3

func (fpi *FilePathItem) Write(b []byte) (n int, err error)

Write implements the io.Writer interface for FilePathItem

type FileResumeData added in v0.3.0

type FileResumeData struct {
	Format       [4]byte  // "RFLT"
	Version      [2]byte  // Always 1
	RSVD         [34]byte // Unused
	ForkCount    [2]byte  // Length of ForkInfoList.  Either 2 or 3 depending on whether file has a resource fork
	ForkInfoList []ForkInfoList
}

FileResumeData is sent when a client or server would like to resume a transfer from an offset

func NewFileResumeData added in v0.3.0

func NewFileResumeData(list []ForkInfoList) *FileResumeData

func (*FileResumeData) BinaryMarshal added in v0.3.0

func (frd *FileResumeData) BinaryMarshal() ([]byte, error)

func (*FileResumeData) UnmarshalBinary added in v0.3.0

func (frd *FileResumeData) UnmarshalBinary(b []byte) error

type FileStore

type FileStore interface {
	Create(name string) (*os.File, error)
	Mkdir(name string, perm os.FileMode) error
	Open(name string) (*os.File, error)
	OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error)
	Remove(name string) error
	RemoveAll(path string) error
	Rename(oldpath string, newpath string) error
	Stat(name string) (fs.FileInfo, error)
	Symlink(oldname, newname string) error
	WriteFile(name string, data []byte, perm fs.FileMode) error
	ReadFile(name string) ([]byte, error)
}

type FileTransfer

type FileTransfer struct {
	FileRoot        string
	FileName        []byte
	FilePath        []byte
	RefNum          [4]byte
	Type            FileTransferType
	TransferSize    []byte
	FolderItemCount []byte
	FileResumeData  *FileResumeData
	Options         []byte

	ClientConn *ClientConn
	// contains filtered or unexported fields
}

func (*FileTransfer) ItemCount added in v0.3.0

func (ft *FileTransfer) ItemCount() int

func (*FileTransfer) String

func (ft *FileTransfer) String() string

String returns a string representation of a file transfer and its progress for display in the GetInfo window Example: MasterOfOrionII1.4.0. 0% 197.9M

type FileTransferID added in v0.13.0

type FileTransferID [4]byte

type FileTransferMgr added in v0.13.0

type FileTransferMgr interface {
	Add(ft *FileTransfer)
	Get(id FileTransferID) *FileTransfer
	Delete(id FileTransferID)
}

type FileTransferType added in v0.13.0

type FileTransferType uint8

File transfer types

type FlatFileForkHeader added in v0.6.0

type FlatFileForkHeader struct {
	ForkType        [4]byte // Either INFO, DATA or MACR
	CompressionType [4]byte
	RSVD            [4]byte
	DataSize        [4]byte
}

type FlatFileHeader

type FlatFileHeader struct {
	Format    [4]byte  // Always "FILP"
	Version   [2]byte  // Always 1
	RSVD      [16]byte // Always empty zeros
	ForkCount [2]byte  // Number of forks, either 2 or 3 if there is a resource fork
}

FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.

type FlatFileInformationFork

type FlatFileInformationFork struct {
	Platform         [4]byte // Operating System used. ("AMAC" or "MWIN")
	TypeSignature    [4]byte // File type signature
	CreatorSignature [4]byte // File creator signature
	Flags            [4]byte
	PlatformFlags    [4]byte
	RSVD             [32]byte
	CreateDate       [8]byte
	ModifyDate       [8]byte
	NameScript       [2]byte
	NameSize         [2]byte // Length of file name (Maximum 128 characters)
	Name             []byte  // File name
	CommentSize      [2]byte // Length of the comment
	Comment          []byte  // File comment
	// contains filtered or unexported fields
}

func NewFlatFileInformationFork

func NewFlatFileInformationFork(fileName string, modifyTime [8]byte, typeSignature string, creatorSignature string) FlatFileInformationFork

func (*FlatFileInformationFork) DataSize

func (ffif *FlatFileInformationFork) DataSize() []byte

DataSize calculates the size of the flat file information fork, which is 72 bytes for the fixed length fields plus the length of the Name + Comment

func (*FlatFileInformationFork) FriendlyCreator added in v0.13.0

func (ffif *FlatFileInformationFork) FriendlyCreator() []byte

func (*FlatFileInformationFork) FriendlyType added in v0.13.0

func (ffif *FlatFileInformationFork) FriendlyType() []byte

func (*FlatFileInformationFork) Read added in v0.13.0

func (ffif *FlatFileInformationFork) Read(p []byte) (int, error)

func (*FlatFileInformationFork) ReadNameSize

func (ffif *FlatFileInformationFork) ReadNameSize() []byte

func (*FlatFileInformationFork) SetComment added in v0.13.0

func (ffif *FlatFileInformationFork) SetComment(comment []byte) error

func (*FlatFileInformationFork) Size added in v0.6.0

func (ffif *FlatFileInformationFork) Size() [4]byte

func (*FlatFileInformationFork) UnmarshalBinary added in v0.2.1

func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error

func (*FlatFileInformationFork) Write added in v0.13.0

func (ffif *FlatFileInformationFork) Write(p []byte) (int, error)

Write implements the io.Writer interface for FlatFileInformationFork

type ForkInfoList added in v0.3.0

type ForkInfoList struct {
	Fork     [4]byte // "DATA" or "MACR"
	DataSize [4]byte // offset from which to resume the transfer of data
	RSVDA    [4]byte // Unused
	RSVDB    [4]byte // Unused
}

func NewForkInfoList added in v0.3.0

func NewForkInfoList(b []byte) *ForkInfoList

type HandlerFunc added in v0.10.19

type HandlerFunc func(*ClientConn, *Transaction) []Transaction

HandlerFunc is the signature of a func to handle a Hotline transaction.

type MemChatManager added in v0.13.0

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

func NewMemChatManager added in v0.13.0

func NewMemChatManager() *MemChatManager

func (*MemChatManager) GetSubject added in v0.13.0

func (cm *MemChatManager) GetSubject(id ChatID) string

func (*MemChatManager) Join added in v0.13.0

func (cm *MemChatManager) Join(id ChatID, cc *ClientConn)

func (*MemChatManager) Leave added in v0.13.0

func (cm *MemChatManager) Leave(id ChatID, clientID [2]byte)

func (*MemChatManager) Members added in v0.13.0

func (cm *MemChatManager) Members(id ChatID) []*ClientConn

func (*MemChatManager) New added in v0.13.0

func (cm *MemChatManager) New(cc *ClientConn) ChatID

func (*MemChatManager) SetSubject added in v0.13.0

func (cm *MemChatManager) SetSubject(id ChatID, subject string)

type MemClientMgr added in v0.13.0

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

func NewMemClientMgr added in v0.13.0

func NewMemClientMgr() *MemClientMgr

func (*MemClientMgr) Add added in v0.13.0

func (cm *MemClientMgr) Add(cc *ClientConn)

func (*MemClientMgr) Delete added in v0.13.0

func (cm *MemClientMgr) Delete(id ClientID)

func (*MemClientMgr) Get added in v0.13.0

func (cm *MemClientMgr) Get(id ClientID) *ClientConn

func (*MemClientMgr) List added in v0.13.0

func (cm *MemClientMgr) List() []*ClientConn

List returns slice of sorted clients.

type MemFileTransferMgr added in v0.13.0

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

func NewMemFileTransferMgr added in v0.13.0

func NewMemFileTransferMgr() *MemFileTransferMgr

func (*MemFileTransferMgr) Add added in v0.13.0

func (ftm *MemFileTransferMgr) Add(ft *FileTransfer)

func (*MemFileTransferMgr) Delete added in v0.13.0

func (ftm *MemFileTransferMgr) Delete(id FileTransferID)

func (*MemFileTransferMgr) Get added in v0.13.0

type MockChatManager added in v0.13.0

type MockChatManager struct {
	mock.Mock
}

func (*MockChatManager) GetSubject added in v0.13.0

func (m *MockChatManager) GetSubject(id ChatID) string

func (*MockChatManager) Join added in v0.13.0

func (m *MockChatManager) Join(id ChatID, cc *ClientConn)

func (*MockChatManager) Leave added in v0.13.0

func (m *MockChatManager) Leave(id ChatID, clientID [2]byte)

func (*MockChatManager) Members added in v0.13.0

func (m *MockChatManager) Members(id ChatID) []*ClientConn

func (*MockChatManager) New added in v0.13.0

func (m *MockChatManager) New(cc *ClientConn) ChatID

func (*MockChatManager) SetSubject added in v0.13.0

func (m *MockChatManager) SetSubject(id ChatID, subject string)

type MockClientMgr added in v0.13.0

type MockClientMgr struct {
	mock.Mock
}

func (*MockClientMgr) Add added in v0.13.0

func (m *MockClientMgr) Add(cc *ClientConn)

func (*MockClientMgr) Delete added in v0.13.0

func (m *MockClientMgr) Delete(id ClientID)

func (*MockClientMgr) Get added in v0.13.0

func (m *MockClientMgr) Get(id ClientID) *ClientConn

func (*MockClientMgr) List added in v0.13.0

func (m *MockClientMgr) List() []*ClientConn

type MockFileInfo added in v0.6.0

type MockFileInfo struct {
	mock.Mock
}

func (*MockFileInfo) IsDir added in v0.6.0

func (mfi *MockFileInfo) IsDir() bool

func (*MockFileInfo) ModTime added in v0.6.0

func (mfi *MockFileInfo) ModTime() time.Time

func (*MockFileInfo) Mode added in v0.6.0

func (mfi *MockFileInfo) Mode() fs.FileMode

func (*MockFileInfo) Name added in v0.6.0

func (mfi *MockFileInfo) Name() string

func (*MockFileInfo) Size added in v0.6.0

func (mfi *MockFileInfo) Size() int64

func (*MockFileInfo) Sys added in v0.6.0

func (mfi *MockFileInfo) Sys() interface{}

type MockFileStore

type MockFileStore struct {
	mock.Mock
}

func (*MockFileStore) Create added in v0.2.1

func (mfs *MockFileStore) Create(name string) (*os.File, error)

func (*MockFileStore) Mkdir

func (mfs *MockFileStore) Mkdir(name string, perm os.FileMode) error

func (*MockFileStore) Open added in v0.0.13

func (mfs *MockFileStore) Open(name string) (*os.File, error)

func (*MockFileStore) OpenFile added in v0.6.0

func (mfs *MockFileStore) OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error)

func (*MockFileStore) ReadFile added in v0.6.0

func (mfs *MockFileStore) ReadFile(name string) ([]byte, error)

func (*MockFileStore) Remove added in v0.2.0

func (mfs *MockFileStore) Remove(name string) error

func (*MockFileStore) RemoveAll added in v0.6.0

func (mfs *MockFileStore) RemoveAll(name string) error

func (*MockFileStore) Rename added in v0.6.0

func (mfs *MockFileStore) Rename(oldpath, newpath string) error

func (*MockFileStore) Stat

func (mfs *MockFileStore) Stat(name string) (os.FileInfo, error)
func (mfs *MockFileStore) Symlink(oldname, newname string) error

func (*MockFileStore) WriteFile added in v0.3.0

func (mfs *MockFileStore) WriteFile(name string, data []byte, perm fs.FileMode) error

type MockThreadNewsMgr added in v0.13.0

type MockThreadNewsMgr struct {
	mock.Mock
}

func (*MockThreadNewsMgr) CreateGrouping added in v0.13.0

func (m *MockThreadNewsMgr) CreateGrouping(newsPath []string, name string, itemType [2]byte) error

func (*MockThreadNewsMgr) DeleteArticle added in v0.13.0

func (m *MockThreadNewsMgr) DeleteArticle(newsPath []string, articleID uint32, recursive bool) error

func (*MockThreadNewsMgr) DeleteNewsItem added in v0.13.0

func (m *MockThreadNewsMgr) DeleteNewsItem(newsPath []string) error

func (*MockThreadNewsMgr) GetArticle added in v0.13.0

func (m *MockThreadNewsMgr) GetArticle(newsPath []string, articleID uint32) *NewsArtData

func (*MockThreadNewsMgr) GetCategories added in v0.13.0

func (m *MockThreadNewsMgr) GetCategories(paths []string) []NewsCategoryListData15

func (*MockThreadNewsMgr) ListArticles added in v0.13.0

func (m *MockThreadNewsMgr) ListArticles(newsPath []string) NewsArtListData

func (*MockThreadNewsMgr) NewsItem added in v0.13.0

func (m *MockThreadNewsMgr) NewsItem(newsPath []string) NewsCategoryListData15

func (*MockThreadNewsMgr) PostArticle added in v0.13.0

func (m *MockThreadNewsMgr) PostArticle(newsPath []string, parentArticleID uint32, article NewsArtData) error

type NewsArtData

type NewsArtData struct {
	Title         string  `yaml:"Title"`
	Poster        string  `yaml:"Poster"`
	Date          [8]byte `yaml:"Date,flow"`
	PrevArt       [4]byte `yaml:"PrevArt,flow"`
	NextArt       [4]byte `yaml:"NextArt,flow"`
	ParentArt     [4]byte `yaml:"ParentArt,flow"`
	FirstChildArt [4]byte `yaml:"FirstChildArtArt,flow"`
	DataFlav      []byte  `yaml:"-"` // MIME type string.  Always "text/plain".
	Data          string  `yaml:"Data"`
}

NewsArtData represents an individual news article.

func (*NewsArtData) DataSize

func (art *NewsArtData) DataSize() [2]byte

type NewsArtList

type NewsArtList struct {
	ID          [4]byte
	TimeStamp   [8]byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
	ParentID    [4]byte
	Flags       [4]byte
	FlavorCount [2]byte
	// Title size	1
	Title []byte // string
	// Poster size	1
	// Poster	Poster string
	Poster     []byte
	FlavorList []NewsFlavorList
	// Flavor list…			Optional (if flavor count > 0)
	ArticleSize [2]byte // Size 2
	// contains filtered or unexported fields
}

NewsArtList is a summarized version of a NewArtData record for display in list view

func (*NewsArtList) Read added in v0.13.0

func (nal *NewsArtList) Read(p []byte) (int, error)

type NewsArtListData

type NewsArtListData struct {
	ID          [4]byte `yaml:"Type"`
	Name        []byte  `yaml:"Name"`
	Description []byte  `yaml:"Description"` // not used?
	NewsArtList []byte  // List of articles			Optional (if article count > 0)
	Count       int
	// contains filtered or unexported fields
}

func (*NewsArtListData) Read added in v0.13.0

func (nald *NewsArtListData) Read(p []byte) (int, error)

type NewsCategoryListData15

type NewsCategoryListData15 struct {
	Type     [2]byte                           `yaml:"Type,flow"` // Bundle (2) or category (3)
	Name     string                            `yaml:"Name"`
	Articles map[uint32]*NewsArtData           `yaml:"Articles"` // Optional, if Type is Category
	SubCats  map[string]NewsCategoryListData15 `yaml:"SubCats"`
	GUID     [16]byte                          `yaml:"-"` // What does this do?  Undocumented and seeming unused.
	AddSN    [4]byte                           `yaml:"-"` // What does this do?  Undocumented and seeming unused.
	DeleteSN [4]byte                           `yaml:"-"` // What does this do?  Undocumented and seeming unused.
	// contains filtered or unexported fields
}

func (*NewsCategoryListData15) GetNewsArtListData

func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData

func (*NewsCategoryListData15) Read added in v0.13.0

func (newscat *NewsCategoryListData15) Read(p []byte) (int, error)

type NewsFlavorList

type NewsFlavorList struct {
}

type OSFileStore

type OSFileStore struct{}

func (*OSFileStore) Create added in v0.2.1

func (fs *OSFileStore) Create(name string) (*os.File, error)

func (*OSFileStore) Mkdir

func (fs *OSFileStore) Mkdir(name string, perm os.FileMode) error

func (*OSFileStore) Open added in v0.0.13

func (fs *OSFileStore) Open(name string) (*os.File, error)

func (*OSFileStore) OpenFile added in v0.6.0

func (fs *OSFileStore) OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error)

func (*OSFileStore) ReadFile added in v0.6.0

func (fs *OSFileStore) ReadFile(name string) ([]byte, error)

func (*OSFileStore) Remove added in v0.2.0

func (fs *OSFileStore) Remove(name string) error

func (*OSFileStore) RemoveAll added in v0.6.0

func (fs *OSFileStore) RemoveAll(name string) error

func (*OSFileStore) Rename added in v0.6.0

func (fs *OSFileStore) Rename(oldpath string, newpath string) error

func (*OSFileStore) Stat

func (fs *OSFileStore) Stat(name string) (os.FileInfo, error)
func (fs *OSFileStore) Symlink(oldname, newname string) error

func (*OSFileStore) WriteFile added in v0.3.0

func (fs *OSFileStore) WriteFile(name string, data []byte, perm fs.FileMode) error

type Option added in v0.13.0

type Option = func(s *Server)

type PrivateChat

type PrivateChat struct {
	Subject    string
	ClientConn map[[2]byte]*ClientConn
}

type RealDialer added in v0.13.0

type RealDialer struct{}

RealDialer is the real implementation of the Dialer interface

func (*RealDialer) Dial added in v0.13.0

func (d *RealDialer) Dial(network, address string) (net.Conn, error)

type Server

type Server struct {
	NetInterface string
	Port         int

	Config Config
	Logger *slog.Logger

	TrackerPassID [4]byte

	Stats Counter

	FS FileStore // Storage backend to use for File storage

	Agreement io.ReadSeeker
	Banner    []byte

	FileTransferMgr FileTransferMgr
	ChatMgr         ChatManager
	ClientMgr       ClientManager
	AccountManager  AccountManager
	ThreadedNewsMgr ThreadedNewsMgr
	BanList         BanMgr

	MessageBoard io.ReadWriteSeeker
	// contains filtered or unexported fields
}

func NewServer

func NewServer(options ...Option) (*Server, error)

func (*Server) CurrentStats added in v0.9.0

func (s *Server) CurrentStats() map[string]interface{}

func (*Server) HandleFunc added in v0.13.0

func (s *Server) HandleFunc(tranType [2]byte, handler HandlerFunc)

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context) error

func (*Server) NewClientConn

func (s *Server) NewClientConn(conn io.ReadWriteCloser, remoteAddr string) *ClientConn

func (*Server) SendAll added in v0.14.0

func (s *Server) SendAll(t TranType, fields ...Field)

func (*Server) Serve

func (s *Server) Serve(ctx context.Context, ln net.Listener) error

func (*Server) ServeFileTransfers

func (s *Server) ServeFileTransfers(ctx context.Context, ln net.Listener) error

func (*Server) Shutdown added in v0.14.0

func (s *Server) Shutdown(msg []byte)

type ServerConfig added in v0.13.0

type ServerConfig struct {
}

type ServerInfoHeader

type ServerInfoHeader struct {
	MsgType     [2]byte // Always has value of 1
	MsgDataSize [2]byte // Remaining size of request
	SrvCount    [2]byte // Number of servers in the server list
	SrvCountDup [2]byte // Same as previous field ¯\_(ツ)_/¯
}

type ServerRecord

type ServerRecord struct {
	IPAddr          [4]byte
	Port            [2]byte
	NumUsers        [2]byte // Number of users connected to this particular server
	Unused          [2]byte
	NameSize        byte   // Length of Name string
	Name            []byte // Server Name
	DescriptionSize byte
	Description     []byte
}

ServerRecord is a tracker listing for a single server

func GetListing

func GetListing(conn io.ReadWriteCloser) ([]ServerRecord, error)

func (*ServerRecord) Addr

func (s *ServerRecord) Addr() string

func (*ServerRecord) Write added in v0.13.0

func (s *ServerRecord) Write(b []byte) (n int, err error)

Write implements io.Writer for ServerRecord

type Stats

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

func NewStats added in v0.13.0

func NewStats() *Stats

func (*Stats) Decrement added in v0.13.0

func (s *Stats) Decrement(key int)

func (*Stats) Get added in v0.13.0

func (s *Stats) Get(key int) int

func (*Stats) Increment added in v0.13.0

func (s *Stats) Increment(keys ...int)

func (*Stats) Set added in v0.13.0

func (s *Stats) Set(key, val int)

func (*Stats) Values added in v0.13.0

func (s *Stats) Values() map[string]interface{}

type ThreadedNews

type ThreadedNews struct {
	Categories map[string]NewsCategoryListData15 `yaml:"Categories"`
}

ThreadedNews contains the top level of threaded news categories, bundles, and articles.

type ThreadedNewsMgr added in v0.13.0

type ThreadedNewsMgr interface {
	ListArticles(newsPath []string) NewsArtListData
	GetArticle(newsPath []string, articleID uint32) *NewsArtData
	DeleteArticle(newsPath []string, articleID uint32, recursive bool) error
	PostArticle(newsPath []string, parentArticleID uint32, article NewsArtData) error
	CreateGrouping(newsPath []string, name string, t [2]byte) error
	GetCategories(paths []string) []NewsCategoryListData15
	NewsItem(newsPath []string) NewsCategoryListData15
	DeleteNewsItem(newsPath []string) error
}

type Time added in v0.13.0

type Time [8]byte

func NewTime added in v0.13.0

func NewTime(t time.Time) (b Time)

NewTime converts a time.Time to the 8 byte Hotline time format: Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)

type TrackerHeader

type TrackerHeader struct {
	Protocol [4]byte // "HTRK" 0x4854524B
	Version  [2]byte // Old protocol (1) or new (2)
}

TrackerHeader is sent in reply Reply received from the tracker starts with a header:

type TrackerRegistration

type TrackerRegistration struct {
	Port        [2]byte // Server's listening TCP port number
	UserCount   int     // Number of users connected to this particular server
	PassID      [4]byte // Random number generated by the server
	Name        string  // Server Name
	Description string  // Description of the server
	Password    string  // Tracker password, if required by tracker
	// contains filtered or unexported fields
}

TrackerRegistration represents the payload a Hotline server sends to a Tracker to register

func (*TrackerRegistration) Read added in v0.8.3

func (tr *TrackerRegistration) Read(p []byte) (int, error)

Read implements io.Reader to write tracker registration payload bytes to slice

type TranType added in v0.13.0

type TranType [2]byte

type Transaction

type Transaction struct {
	Flags      byte     // Reserved (should be 0)
	IsReply    byte     // Request (0) or reply (1)
	Type       TranType // Requested operation (user defined)
	ID         [4]byte  // Unique transaction ID (must be != 0)
	ErrorCode  [4]byte  // Used in the reply (user defined, 0 = no error)
	TotalSize  [4]byte  // Total data size for the fields in this transaction.
	DataSize   [4]byte  // Size of data in this transaction part. This allows splitting large transactions into smaller parts.
	ParamCount [2]byte  // Number of the parameters for this transaction
	Fields     []Field

	ClientID ClientID // Internal identifier for target client
	// contains filtered or unexported fields
}

func NewTransaction

func NewTransaction(t TranType, clientID ClientID, fields ...Field) Transaction

NewTransaction creates a new Transaction with the specified type, client, and optional fields.

func (*Transaction) GetField

func (t *Transaction) GetField(id [2]byte) *Field

func (*Transaction) Read added in v0.13.0

func (t *Transaction) Read(p []byte) (int, error)

Read implements the io.Reader interface for Transaction

func (*Transaction) Size

func (t *Transaction) Size() []byte

Size returns the total size of the transaction payload

func (*Transaction) Write added in v0.8.3

func (t *Transaction) Write(p []byte) (n int, err error)

Write implements io.Writer interface for Transaction. Transactions read from the network are read as complete tokens with a bufio.Scanner, so the arg p is guaranteed to have the full byte payload of a complete transaction.

type User

type User struct {
	ID    [2]byte
	Icon  []byte // Size 2
	Flags []byte // Size 2
	Name  string // Variable length user name
	// contains filtered or unexported fields
}

func (*User) Read added in v0.13.0

func (u *User) Read(p []byte) (int, error)

func (*User) Write added in v0.13.0

func (u *User) Write(p []byte) (int, error)

type UserFlags added in v0.13.0

type UserFlags [2]byte

func (*UserFlags) IsSet added in v0.13.0

func (f *UserFlags) IsSet(i int) bool

func (*UserFlags) Set added in v0.13.0

func (f *UserFlags) Set(i int, newVal uint)

type WriteCounter added in v0.7.0

type WriteCounter struct {
	Total int64 // Total # of bytes written
	// contains filtered or unexported fields
}

WriteCounter counts the number of bytes written to it.

func (*WriteCounter) Write added in v0.7.0

func (wc *WriteCounter) Write(p []byte) (int, error)

Write implements the io.Writer interface.

Always completes and never returns an error.

Jump to

Keyboard shortcuts

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