hotline

package
v0.12.1 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2024 License: MIT Imports: 35 Imported by: 0

Documentation

Index

Constants

View Source
const (
	FieldError               = 100
	FieldData                = 101
	FieldUserName            = 102
	FieldUserID              = 103
	FieldUserIconID          = 104
	FieldUserLogin           = 105
	FieldUserPassword        = 106
	FieldRefNum              = 107
	FieldTransferSize        = 108
	FieldChatOptions         = 109
	FieldUserAccess          = 110
	FieldUserAlias           = 111 // TODO: implement
	FieldUserFlags           = 112
	FieldOptions             = 113
	FieldChatID              = 114
	FieldChatSubject         = 115
	FieldWaitingCount        = 116
	FieldBannerType          = 152
	FieldNoServerAgreement   = 152
	FieldVersion             = 160
	FieldCommunityBannerID   = 161
	FieldServerName          = 162
	FieldFileNameWithInfo    = 200
	FieldFileName            = 201
	FieldFilePath            = 202
	FieldFileResumeData      = 203
	FieldFileTransferOptions = 204
	FieldFileTypeString      = 205
	FieldFileCreatorString   = 206
	FieldFileSize            = 207
	FieldFileCreateDate      = 208
	FieldFileModifyDate      = 209
	FieldFileComment         = 210
	FieldFileNewName         = 211
	FieldFileNewPath         = 212
	FieldFileType            = 213
	FieldQuotingMsg          = 214
	FieldAutomaticResponse   = 215
	FieldFolderItemCount     = 220
	FieldUsernameWithInfo    = 300
	FieldNewsArtListData     = 321
	FieldNewsCatName         = 322
	FieldNewsCatListData15   = 323
	FieldNewsPath            = 325
	FieldNewsArtID           = 326
	FieldNewsArtDataFlav     = 327
	FieldNewsArtTitle        = 328
	FieldNewsArtPoster       = 329
	FieldNewsArtDate         = 330
	FieldNewsArtPrevArt      = 331
	FieldNewsArtNextArt      = 332
	FieldNewsArtData         = 333
	FieldNewsArtFlags        = 334 // TODO: what is this used for?
	FieldNewsArtParentArt    = 335
	FieldNewsArt1stChildArt  = 336
	FieldNewsArtRecurseDel   = 337 // TODO: implement news article recusive deletion
)

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

View Source
const (
	FileDownload   = 0
	FileUpload     = 1
	FolderDownload = 2
	FolderUpload   = 3
)

File transfer types

View Source
const (
	TranError                = 0
	TranGetMsgs              = 101
	TranNewMsg               = 102
	TranOldPostNews          = 103
	TranServerMsg            = 104
	TranChatSend             = 105
	TranChatMsg              = 106
	TranLogin                = 107
	TranSendInstantMsg       = 108
	TranShowAgreement        = 109
	TranDisconnectUser       = 110
	TranDisconnectMsg        = 111 // TODO: implement server initiated friendly disconnect
	TranInviteNewChat        = 112
	TranInviteToChat         = 113
	TranRejectChatInvite     = 114
	TranJoinChat             = 115
	TranLeaveChat            = 116
	TranNotifyChatChangeUser = 117
	TranNotifyChatDeleteUser = 118
	TranNotifyChatSubject    = 119
	TranSetChatSubject       = 120
	TranAgreed               = 121
	TranServerBanner         = 122
	TranGetFileNameList      = 200
	TranDownloadFile         = 202
	TranUploadFile           = 203
	TranNewFolder            = 205
	TranDeleteFile           = 204
	TranGetFileInfo          = 206
	TranSetFileInfo          = 207
	TranMoveFile             = 208
	TranMakeFileAlias        = 209
	TranDownloadFldr         = 210
	TranDownloadInfo         = 211 // TODO: implement file transfer queue
	TranDownloadBanner       = 212
	TranUploadFldr           = 213
	TranGetUserNameList      = 300
	TranNotifyChangeUser     = 301
	TranNotifyDeleteUser     = 302
	TranGetClientInfoText    = 303
	TranSetClientUserInfo    = 304
	TranListUsers            = 348
	TranUpdateUser           = 349
	TranNewUser              = 350
	TranDeleteUser           = 351
	TranGetUser              = 352
	TranSetUser              = 353
	TranUserAccess           = 354
	TranUserBroadcast        = 355
	TranGetNewsCatNameList   = 370
	TranGetNewsArtNameList   = 371
	TranDelNewsItem          = 380
	TranNewNewsFldr          = 381
	TranNewNewsCat           = 382
	TranGetNewsArtData       = 400
	TranPostNewsArt          = 410
	TranDelNewsArt           = 411
	TranKeepAlive            = 500
)
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 GuestAccount = "guest" // default account used when no login is provided for a connection
View Source
const VERSION = "0.12.1"

Variables

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,
}
View Source
var TransactionHandlers = map[uint16]TransactionType{

	TranChatMsg: {
		Name: "TranChatMsg",
	},

	TranNotifyChangeUser: {
		Name: "TranNotifyChangeUser",
	},
	TranError: {
		Name: "TranError",
	},
	TranShowAgreement: {
		Name: "TranShowAgreement",
	},
	TranUserAccess: {
		Name: "TranUserAccess",
	},
	TranNotifyDeleteUser: {
		Name: "TranNotifyDeleteUser",
	},
	TranAgreed: {
		Name:    "TranAgreed",
		Handler: HandleTranAgreed,
	},
	TranChatSend: {
		Name:    "TranChatSend",
		Handler: HandleChatSend,
		RequiredFields: []requiredField{
			{
				ID: FieldData,
				// contains filtered or unexported fields
			},
		},
	},
	TranDelNewsArt: {
		Name:    "TranDelNewsArt",
		Handler: HandleDelNewsArt,
	},
	TranDelNewsItem: {
		Name:    "TranDelNewsItem",
		Handler: HandleDelNewsItem,
	},
	TranDeleteFile: {
		Name:    "TranDeleteFile",
		Handler: HandleDeleteFile,
	},
	TranDeleteUser: {
		Name:    "TranDeleteUser",
		Handler: HandleDeleteUser,
	},
	TranDisconnectUser: {
		Name:    "TranDisconnectUser",
		Handler: HandleDisconnectUser,
	},
	TranDownloadFile: {
		Name:    "TranDownloadFile",
		Handler: HandleDownloadFile,
	},
	TranDownloadFldr: {
		Name:    "TranDownloadFldr",
		Handler: HandleDownloadFolder,
	},
	TranGetClientInfoText: {
		Name:    "TranGetClientInfoText",
		Handler: HandleGetClientInfoText,
	},
	TranGetFileInfo: {
		Name:    "TranGetFileInfo",
		Handler: HandleGetFileInfo,
	},
	TranGetFileNameList: {
		Name:    "TranGetFileNameList",
		Handler: HandleGetFileNameList,
	},
	TranGetMsgs: {
		Name:    "TranGetMsgs",
		Handler: HandleGetMsgs,
	},
	TranGetNewsArtData: {
		Name:    "TranGetNewsArtData",
		Handler: HandleGetNewsArtData,
	},
	TranGetNewsArtNameList: {
		Name:    "TranGetNewsArtNameList",
		Handler: HandleGetNewsArtNameList,
	},
	TranGetNewsCatNameList: {
		Name:    "TranGetNewsCatNameList",
		Handler: HandleGetNewsCatNameList,
	},
	TranGetUser: {
		Name:    "TranGetUser",
		Handler: HandleGetUser,
	},
	TranGetUserNameList: {
		Name:    "tranHandleGetUserNameList",
		Handler: HandleGetUserNameList,
	},
	TranInviteNewChat: {
		Name:    "TranInviteNewChat",
		Handler: HandleInviteNewChat,
	},
	TranInviteToChat: {
		Name:    "TranInviteToChat",
		Handler: HandleInviteToChat,
	},
	TranJoinChat: {
		Name:    "TranJoinChat",
		Handler: HandleJoinChat,
	},
	TranKeepAlive: {
		Name:    "TranKeepAlive",
		Handler: HandleKeepAlive,
	},
	TranLeaveChat: {
		Name:    "TranJoinChat",
		Handler: HandleLeaveChat,
	},
	TranListUsers: {
		Name:    "TranListUsers",
		Handler: HandleListUsers,
	},
	TranMoveFile: {
		Name:    "TranMoveFile",
		Handler: HandleMoveFile,
	},
	TranNewFolder: {
		Name:    "TranNewFolder",
		Handler: HandleNewFolder,
	},
	TranNewNewsCat: {
		Name:    "TranNewNewsCat",
		Handler: HandleNewNewsCat,
	},
	TranNewNewsFldr: {
		Name:    "TranNewNewsFldr",
		Handler: HandleNewNewsFldr,
	},
	TranNewUser: {
		Name:    "TranNewUser",
		Handler: HandleNewUser,
	},
	TranUpdateUser: {
		Name:    "TranUpdateUser",
		Handler: HandleUpdateUser,
	},
	TranOldPostNews: {
		Name:    "TranOldPostNews",
		Handler: HandleTranOldPostNews,
	},
	TranPostNewsArt: {
		Name:    "TranPostNewsArt",
		Handler: HandlePostNewsArt,
	},
	TranRejectChatInvite: {
		Name:    "TranRejectChatInvite",
		Handler: HandleRejectChatInvite,
	},
	TranSendInstantMsg: {
		Name:    "TranSendInstantMsg",
		Handler: HandleSendInstantMsg,
		RequiredFields: []requiredField{
			{
				ID: FieldData,
				// contains filtered or unexported fields
			},
			{
				ID: FieldUserID,
			},
		},
	},
	TranSetChatSubject: {
		Name:    "TranSetChatSubject",
		Handler: HandleSetChatSubject,
	},
	TranMakeFileAlias: {
		Name:    "TranMakeFileAlias",
		Handler: HandleMakeAlias,
		RequiredFields: []requiredField{
			{ID: FieldFileName, /* contains filtered or unexported fields */},
			{ID: FieldFilePath, /* contains filtered or unexported fields */},
			{ID: FieldFileNewPath, /* contains filtered or unexported fields */},
		},
	},
	TranSetClientUserInfo: {
		Name:    "TranSetClientUserInfo",
		Handler: HandleSetClientUserInfo,
	},
	TranSetFileInfo: {
		Name:    "TranSetFileInfo",
		Handler: HandleSetFileInfo,
	},
	TranSetUser: {
		Name:    "TranSetUser",
		Handler: HandleSetUser,
	},
	TranUploadFile: {
		Name:    "TranUploadFile",
		Handler: HandleUploadFile,
	},
	TranUploadFldr: {
		Name:    "TranUploadFldr",
		Handler: HandleUploadFolder,
	},
	TranUserBroadcast: {
		Name:    "TranUserBroadcast",
		Handler: HandleUserBroadcast,
	},
	TranDownloadBanner: {
		Name:    "TranDownloadBanner",
		Handler: HandleDownloadBanner,
	},
}

Functions

func CalcItemCount

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

func CalcTotalSize

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

func EncodeFilePath

func EncodeFilePath(filePath string) []byte

func Handshake

func Handshake(rw io.ReadWriter) error

Handshake After establishing TCP connection, both client and server start the handshake process in order to confirm that each of them comply with requirements of the other. The information provided in this initial data exchange identifies protocols, and their versions, used in the communication. In the case where, after inspection, the capabilities of one of the subjects do not comply with the requirements of the other, the connection is dropped.

The following information is sent to the server: Description Size Data Note Protocol ID 4 TRTP 0x54525450 Sub-protocol ID 4 HOTL User defined VERSION 2 1 Currently 1 Sub-version 2 2 User defined

The server replies with the following: Description Size Data Note Protocol ID 4 TRTP Error code 4 Error code returned by the server (0 = no error)

func ReadNewsPath

func ReadNewsPath(newsPath []byte) []string

TODO: re-implement as bufio.Scanner interface

Types

type Account

type Account struct {
	Login    string       `yaml:"Login"`
	Name     string       `yaml:"Name"`
	Password string       `yaml:"Password"`
	Access   accessBitmap `yaml:"Access"`
}

func (*Account) Read added in v0.7.0

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

Read implements io.Reader interface for Account

type Bookmark

type Bookmark struct {
	Name     string `yaml:"Name"`
	Addr     string `yaml:"Addr"`
	Login    string `yaml:"Login"`
	Password string `yaml:"Password"`
}

type Client

type Client struct {
	DebugBuf   *DebugBuffer
	Connection net.Conn
	UserAccess []byte

	UserList []User
	Logger   *slog.Logger

	Pref *ClientPrefs

	Handlers map[uint16]ClientHandler

	UI *UI

	Inbox chan *Transaction
	// contains filtered or unexported fields
}

func NewClient

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

func NewUIClient added in v0.10.19

func NewUIClient(cfgPath 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(transactionID uint16, 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) LogIn

func (c *Client) LogIn(login string, password string) error

func (*Client) Send

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

type ClientConn

type ClientConn struct {
	Connection io.ReadWriteCloser
	RemoteAddr string
	ID         *[]byte
	Icon       []byte
	Flags      []byte
	UserName   []byte
	Account    *Account
	IdleTime   int
	Server     *Server
	Version    []byte
	Idle       bool
	AutoReply  []byte
	// 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

func (*ClientConn) NewErrReply

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

NewErrReply returns an error reply Transaction with errMsg

func (*ClientConn) NewReply

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

NewReply returns a reply Transaction with fields for the ClientConn

func (*ClientConn) String added in v0.7.0

func (cc *ClientConn) String() string

type ClientHandler added in v0.10.20

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

type ClientPrefs

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

func (*ClientPrefs) AddBookmark

func (cp *ClientPrefs) AddBookmark(name, addr, login, pass string)

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
}

type DebugBuffer

type DebugBuffer struct {
	TextView *tview.TextView
}

DebugBuffer wraps a *tview.TextView and adds a Sync() method to make it available as a Zap logger

func (*DebugBuffer) Sync

func (db *DebugBuffer) Sync() error

Sync is a noop function that dataFile to satisfy the zapcore.WriteSyncer interface

func (*DebugBuffer) Write

func (db *DebugBuffer) Write(p []byte) (int, error)

type Field

type Field struct {
	ID        []byte // Type of field
	FieldSize []byte // Size of the data part
	Data      []byte // Actual field content
}

func NewField

func NewField(id uint16, data []byte) Field

func ReadFields

func ReadFields(paramCount []byte, buf []byte) ([]Field, error)

func (Field) Payload

func (f Field) Payload() []byte

type FileHeader

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

func NewFileHeader

func NewFileHeader(fileName string, isDir bool) FileHeader

func (*FileHeader) Payload

func (fh *FileHeader) Payload() []byte

type FileNameWithInfo

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

func (*FileNameWithInfo) MarshalBinary

func (f *FileNameWithInfo) MarshalBinary() (data []byte, err error)

func (*FileNameWithInfo) UnmarshalBinary

func (f *FileNameWithInfo) UnmarshalBinary(data []byte) error

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 {
	FileName        []byte
	FilePath        []byte
	ReferenceNumber []byte

	Type            int
	TransferSize    []byte
	FolderItemCount []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 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         []byte // Operating System used. ("AMAC" or "MWIN")
	TypeSignature    []byte // File type signature
	CreatorSignature []byte // File creator signature
	Flags            []byte
	PlatformFlags    []byte
	RSVD             []byte
	CreateDate       []byte
	ModifyDate       []byte
	NameScript       []byte
	NameSize         []byte // Length of file name (Maximum 128 characters)
	Name             []byte // File name
	CommentSize      []byte // Length of the comment
	Comment          []byte // File comment
}

func NewFlatFileInformationFork

func NewFlatFileInformationFork(fileName string, modifyTime []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) MarshalBinary added in v0.6.0

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

func (*FlatFileInformationFork) ReadNameSize

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

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

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, error)

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 NewsArtData

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

NewsArtData represents single news article

func (*NewsArtData) DataSize

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

type NewsArtList

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

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

func (*NewsArtList) Payload

func (nal *NewsArtList) Payload() []byte

type NewsArtListData

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

func (*NewsArtListData) Payload

func (nald *NewsArtListData) Payload() []byte

type NewsCategoryListData15

type NewsCategoryListData15 struct {
	Type     []byte `yaml:"Type"` // Size 2 ; Bundle (2) or category (3)
	Count    []byte // Article or SubCategory count Size 2
	NameSize byte
	Name     string                            `yaml:"Name"`     //
	Articles map[uint32]*NewsArtData           `yaml:"Articles"` // Optional, if Type is Category
	SubCats  map[string]NewsCategoryListData15 `yaml:"SubCats"`
	GUID     []byte                            // Size 16
	AddSN    []byte                            // Size 4
	DeleteSN []byte                            // Size 4
}

func ReadNewsCategoryListData

func ReadNewsCategoryListData(payload []byte) NewsCategoryListData15

ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct For use on the client side

func (*NewsCategoryListData15) GetNewsArtListData

func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData

func (*NewsCategoryListData15) MarshalBinary

func (newscat *NewsCategoryListData15) MarshalBinary() (data []byte, err 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 PrivateChat

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

type Server

type Server struct {
	NetInterface string
	Port         int
	Accounts     map[string]*Account
	Agreement    []byte
	Clients      map[uint16]*ClientConn

	Config    *Config
	ConfigDir string
	Logger    *zap.SugaredLogger

	PrivateChatsMu sync.Mutex
	PrivateChats   map[uint32]*PrivateChat

	NextGuestID   *uint16
	TrackerPassID [4]byte

	StatsMu sync.Mutex
	Stats   *Stats

	FS FileStore // Storage backend to use for File storage

	ThreadedNews *ThreadedNews

	FlatNews []byte
	// contains filtered or unexported fields
}

func NewServer

func NewServer(configDir, netInterface string, netPort int, logger *zap.SugaredLogger, fs FileStore) (*Server, error)

NewServer constructs a new Server from a config dir

func (*Server) CurrentStats added in v0.9.0

func (s *Server) CurrentStats() Stats

func (*Server) DeleteUser

func (s *Server) DeleteUser(login string) error

DeleteUser deletes the user account

func (*Server) GetNewsCatByPath

func (s *Server) GetNewsCatByPath(paths []string) map[string]NewsCategoryListData15

func (*Server) ListenAndServe

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

func (*Server) NewClientConn

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

func (*Server) NewPrivateChat

func (s *Server) NewPrivateChat(cc *ClientConn) []byte

func (*Server) NewUser

func (s *Server) NewUser(login, name, password string, access accessBitmap) error

NewUser creates a new user account entry in the server map and config file

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) UpdateUser added in v0.3.1

func (s *Server) UpdateUser(login, newLogin, name, password string, access accessBitmap) error

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 ¯\_(ツ)_/¯
}

Message type 2 1 Sending list of servers Message data size 2 Remaining size of this request Number of servers 2 Number of servers in the server list Number of servers 2 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
}

func GetListing

func GetListing(addr string) ([]ServerRecord, error)

func (*ServerRecord) Addr

func (s *ServerRecord) Addr() string

func (*ServerRecord) Read

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

Read implements io.Reader for ServerRecord

type Stats

type Stats struct {
	CurrentlyConnected  int
	DownloadsInProgress int
	UploadsInProgress   int
	WaitingDownloads    int
	ConnectionPeak      int
	ConnectionCounter   int
	DownloadCounter     int
	UploadCounter       int
	Since               time.Time
}

type ThreadedNews

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

type TrackerHeader

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

Reply received from the tracker starts with a header:

type TrackerRegistration

type TrackerRegistration struct {
	Port        [2]byte // Server listening port number
	UserCount   int     // Number of users connected to this particular server
	PassID      []byte  // Random number generated by the server
	Name        string  // Server name
	Description string  // Description of the server
}

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() []byte

TODO: reimplement as io.Reader

type Transaction

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

func HandleChatSend

func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleDelNewsArt

func HandleDelNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleDelNewsItem

func HandleDelNewsItem(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleDelNewsItem deletes an existing threaded news folder or category from the server. Fields used in the request: 325 News path Fields used in the reply: None

func HandleDeleteFile

func HandleDeleteFile(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleDeleteFile deletes a file or folder Fields used in the request: * 201 File name * 202 File path Fields used in the reply: none

func HandleDeleteUser

func HandleDeleteUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleDisconnectUser

func HandleDisconnectUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleDownloadBanner added in v0.6.0

func HandleDownloadBanner(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleDownloadBanner handles requests for a new banner from the server Fields used in the request: None Fields used in the reply: 107 FieldRefNum Used later for transfer 108 FieldTransferSize Size of data to be downloaded

func HandleDownloadFile

func HandleDownloadFile(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleDownloadFolder

func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error)

Download all files from the specified folder and sub-folders

func HandleGetClientInfoText added in v0.7.0

func HandleGetClientInfoText(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleGetClientInfoText returns user information for the specific user.

Fields used in the request: 103 User ID

Fields used in the reply: 102 User name 101 Data User info text string

func HandleGetFileInfo

func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleGetFileNameList

func HandleGetFileNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleGetMsgs

func HandleGetMsgs(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleGetMsgs returns the flat news data

func HandleGetNewsArtData

func HandleGetNewsArtData(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleGetNewsArtData requests information about the specific news article. Fields used in the request:

Request fields 325 News path 326 News article ID 327 News article data flavor

Fields used in the reply: 328 News article title 329 News article poster 330 News article date 331 Previous article ID 332 Next article ID 335 Parent article ID 336 First child article ID 327 News article data flavor "Should be “text/plain” 333 News article data Optional (if data flavor is “text/plain”)

func HandleGetNewsArtNameList

func HandleGetNewsArtNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error)

Fields used in the reply: 321 News article list data Optional

func HandleGetNewsCatNameList

func HandleGetNewsCatNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleGetNewsCatNameList returns a list of news categories for a path Fields used in the request: 325 News path (Optional)

func HandleGetUser

func HandleGetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleGetUserNameList

func HandleGetUserNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleInviteNewChat

func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleInviteNewChat invites users to new private chat

func HandleInviteToChat

func HandleInviteToChat(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleJoinChat

func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleJoinChat is sent from a v1.8+ Hotline client when the joins a private chat Fields used in the reply: * 115 Chat subject * 300 User name with info (Optional) * 300 (more user names with info)

func HandleKeepAlive

func HandleKeepAlive(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleKeepAlive responds to keepalive transactions with an empty reply * HL 1.9.2 Client sends keepalive msg every 3 minutes * HL 1.2.3 Client doesn't send keepalives

func HandleLeaveChat

func HandleLeaveChat(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleLeaveChat is sent from a v1.8+ Hotline client when the user exits a private chat Fields used in the request:

  • 114 FieldChatID

Reply is not expected.

func HandleListUsers

func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleMakeAlias added in v0.2.0

func HandleMakeAlias(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleMakeAlias makes a file alias using the specified path. Fields used in the request: 201 File name 202 File path 212 File new path Destination path

Fields used in the reply: None

func HandleMoveFile

func HandleMoveFile(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleMoveFile moves files or folders. Note: seemingly not documented

func HandleNewFolder

func HandleNewFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleNewNewsCat

func HandleNewNewsCat(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleNewNewsFldr

func HandleNewNewsFldr(cc *ClientConn, t *Transaction) (res []Transaction, err error)

Fields used in the request: 322 News category name 325 News path

func HandleNewUser

func HandleNewUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleNewUser creates a new user account

func HandlePostNewsArt

func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err error)

Request fields 325 News path 326 News article ID ID of the parent article? 328 News article title 334 News article flags 327 News article data flavor Currently “text/plain” 333 News article data

func HandleRejectChatInvite

func HandleRejectChatInvite(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleSendInstantMsg

func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleSendInstantMsg sends instant message to the user on the current server. Fields used in the request:

103	User ID
113	Options
	One of the following values:
	- User message (myOpt_UserMessage = 1)
	- Refuse message (myOpt_RefuseMessage = 2)
	- Refuse chat (myOpt_RefuseChat  = 3)
	- Automatic response (myOpt_AutomaticResponse = 4)"
101	Data	Optional
214	Quoting message	Optional

Fields used in the reply: None

func HandleSetChatSubject

func HandleSetChatSubject(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleSetChatSubject is sent from a v1.8+ Hotline client when the user sets a private chat subject Fields used in the request: * 114 Chat ID * 115 Chat subject Reply is not expected.

func HandleSetClientUserInfo

func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleSetFileInfo

func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleSetFileInfo updates a file or folder name and/or comment from the Get Info window Fields used in the request: * 201 File name * 202 File path Optional * 211 File new name Optional * 210 File comment Optional Fields used in the reply: None

func HandleSetUser

func HandleSetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleTranAgreed

func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err error)

func HandleTranOldPostNews

func HandleTranOldPostNews(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleTranOldPostNews updates the flat news Fields used in this request: 101 Data

func HandleUpdateUser added in v0.3.1

func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleUpdateUser is used by the v1.5+ multi-user editor to perform account editing for multiple users at a time. An update can be a mix of these actions: * Create user * Delete user * Modify user (including renaming the account login)

The Transaction sent by the client includes one data field per user that was modified. This data field in turn contains another data field encoded in its payload with a varying number of sub fields depending on which action is performed. This seems to be the only place in the Hotline protocol where a data field contains another data field.

func HandleUploadFile

func HandleUploadFile(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleUploadFile Fields used in the request: 201 File name 202 File path 204 File transfer options "Optional Used only to resume download, currently has value 2" 108 File transfer size "Optional used if download is not resumed"

func HandleUploadFolder

func HandleUploadFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error)

Upload all files from the local folder and its subfolders to the specified path on the server Fields used in the request 201 File name 202 File path 108 transfer size Total size of all items in the folder 220 Folder item count 204 File transfer options "Optional Currently set to 1" (TODO: ??)

func HandleUserBroadcast

func HandleUserBroadcast(cc *ClientConn, t *Transaction) (res []Transaction, err error)

HandleUserBroadcast sends an Administrator Message to all connected clients of the server

func NewTransaction

func NewTransaction(t int, clientID *[]byte, fields ...Field) *Transaction

func (*Transaction) GetField

func (t *Transaction) GetField(id int) Field

func (*Transaction) IsError added in v0.8.3

func (t *Transaction) IsError() bool

func (*Transaction) MarshalBinary

func (t *Transaction) MarshalBinary() (data []byte, err error)

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

type TransactionType

type TransactionType struct {
	Handler        HandlerFunc // function for handling the transaction type
	Name           string      // Name of transaction as it will appear in logging
	RequiredFields []requiredField
}

type UI

type UI struct {
	App   *tview.Application
	Pages *tview.Pages

	HLClient *Client
	// contains filtered or unexported fields
}

func NewUI

func NewUI(c *Client) *UI

func (*UI) Start

func (ui *UI) Start()

type User

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

func ReadUser

func ReadUser(b []byte) (*User, error)

func (User) Payload

func (u User) Payload() []byte

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