cancelRoot()
}()
- client := hotline.NewClient(*configDir, logger)
+ client := hotline.NewUIClient(*configDir, logger)
client.DebugBuf = db
client.UI.Start()
// Read implements io.Reader interface for Account
func (a *Account) Read(p []byte) (n int, err error) {
fields := []Field{
- NewField(fieldUserName, []byte(a.Name)),
- NewField(fieldUserLogin, negateString([]byte(a.Login))),
- NewField(fieldUserAccess, a.Access[:]),
+ NewField(FieldUserName, []byte(a.Name)),
+ NewField(FieldUserLogin, negateString([]byte(a.Login))),
+ NewField(FieldUserAccess, a.Access[:]),
}
if bcrypt.CompareHashAndPassword([]byte(a.Password), []byte("")) != nil {
- fields = append(fields, NewField(fieldUserPassword, []byte("x")))
+ fields = append(fields, NewField(FieldUserPassword, []byte("x")))
}
fieldCount := make([]byte, 2)
package hotline
import (
+ "bufio"
"bytes"
"embed"
"encoding/binary"
cfgPath string
DebugBuf *DebugBuffer
Connection net.Conn
- Login *[]byte
- Password *[]byte
- Flags *[]byte
- ID *[]byte
- Version []byte
UserAccess []byte
filePath []string
UserList []User
activeTasks map[uint32]*Transaction
serverName string
- pref *ClientPrefs
+ Pref *ClientPrefs
- Handlers map[uint16]clientTHandler
+ Handlers map[uint16]ClientTHandler
UI *UI
Inbox chan *Transaction
}
-func NewClient(cfgPath string, logger *zap.SugaredLogger) *Client {
+func NewClient(username string, logger *zap.SugaredLogger) *Client {
+ c := &Client{
+ Logger: logger,
+ activeTasks: make(map[uint32]*Transaction),
+ Handlers: make(map[uint16]ClientTHandler),
+ }
+ c.Pref = &ClientPrefs{Username: username}
+
+ return c
+}
+
+func NewUIClient(cfgPath string, logger *zap.SugaredLogger) *Client {
c := &Client{
cfgPath: cfgPath,
Logger: logger,
if err != nil {
logger.Fatal(fmt.Sprintf("unable to read config file %s\n", cfgPath))
}
- c.pref = prefs
+ c.Pref = prefs
return c
}
return fmt.Sprintf("\n\n\nWelcome to...\n\n[red::b]%s[-:-:-]\n\n", file)
}
-type clientTransaction struct {
+type ClientTransaction struct {
Name string
Handler func(*Client, *Transaction) ([]Transaction, error)
}
-func (ch clientTransaction) Handle(cc *Client, t *Transaction) ([]Transaction, error) {
+func (ch ClientTransaction) Handle(cc *Client, t *Transaction) ([]Transaction, error) {
return ch.Handler(cc, t)
}
-type clientTHandler interface {
+type ClientTHandler interface {
Handle(*Client, *Transaction) ([]Transaction, error)
}
return args.Get(0).([]Transaction), args.Error(1)
}
-var clientHandlers = map[uint16]clientTHandler{
+var clientHandlers = map[uint16]ClientTHandler{
// Server initiated
- tranChatMsg: clientTransaction{
- Name: "tranChatMsg",
+ TranChatMsg: ClientTransaction{
+ Name: "TranChatMsg",
Handler: handleClientChatMsg,
},
- tranLogin: clientTransaction{
- Name: "tranLogin",
+ TranLogin: ClientTransaction{
+ Name: "TranLogin",
Handler: handleClientTranLogin,
},
- tranShowAgreement: clientTransaction{
- Name: "tranShowAgreement",
+ TranShowAgreement: ClientTransaction{
+ Name: "TranShowAgreement",
Handler: handleClientTranShowAgreement,
},
- tranUserAccess: clientTransaction{
- Name: "tranUserAccess",
+ TranUserAccess: ClientTransaction{
+ Name: "TranUserAccess",
Handler: handleClientTranUserAccess,
},
- tranGetUserNameList: clientTransaction{
- Name: "tranGetUserNameList",
+ TranGetUserNameList: ClientTransaction{
+ Name: "TranGetUserNameList",
Handler: handleClientGetUserNameList,
},
- tranNotifyChangeUser: clientTransaction{
- Name: "tranNotifyChangeUser",
+ TranNotifyChangeUser: ClientTransaction{
+ Name: "TranNotifyChangeUser",
Handler: handleNotifyChangeUser,
},
- tranNotifyDeleteUser: clientTransaction{
- Name: "tranNotifyDeleteUser",
+ TranNotifyDeleteUser: ClientTransaction{
+ Name: "TranNotifyDeleteUser",
Handler: handleNotifyDeleteUser,
},
- tranGetMsgs: clientTransaction{
- Name: "tranNotifyDeleteUser",
+ TranGetMsgs: ClientTransaction{
+ Name: "TranNotifyDeleteUser",
Handler: handleGetMsgs,
},
- tranGetFileNameList: clientTransaction{
- Name: "tranGetFileNameList",
+ TranGetFileNameList: ClientTransaction{
+ Name: "TranGetFileNameList",
Handler: handleGetFileNameList,
},
- tranServerMsg: clientTransaction{
- Name: "tranServerMsg",
+ TranServerMsg: ClientTransaction{
+ Name: "TranServerMsg",
Handler: handleTranServerMsg,
},
- tranKeepAlive: clientTransaction{
- Name: "tranKeepAlive",
+ TranKeepAlive: ClientTransaction{
+ Name: "TranKeepAlive",
Handler: func(client *Client, transaction *Transaction) (t []Transaction, err error) {
return t, err
},
func handleTranServerMsg(c *Client, t *Transaction) (res []Transaction, err error) {
time := time.Now().Format(time.RFC850)
- msg := strings.ReplaceAll(string(t.GetField(fieldData).Data), "\r", "\n")
+ msg := strings.ReplaceAll(string(t.GetField(FieldData).Data), "\r", "\n")
msg += "\n\nAt " + time
- title := fmt.Sprintf("| Private Message From: %s |", t.GetField(fieldUserName).Data)
+ title := fmt.Sprintf("| Private Message From: %s |", t.GetField(FieldUserName).Data)
msgBox := tview.NewTextView().SetScrollable(true)
msgBox.SetText(msg).SetBackgroundColor(tcell.ColorDarkSlateBlue)
func handleGetFileNameList(c *Client, t *Transaction) (res []Transaction, err error) {
if t.IsError() {
- c.showErrMsg(string(t.GetField(fieldError).Data))
- c.Logger.Infof("Error: %s", t.GetField(fieldError).Data)
+ c.showErrMsg(string(t.GetField(FieldError).Data))
+ c.Logger.Infof("Error: %s", t.GetField(FieldError).Data)
return res, err
}
if selectedNode.GetText() == "<- Back" {
c.filePath = c.filePath[:len(c.filePath)-1]
- f := NewField(fieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
+ f := NewField(FieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
- if err := c.UI.HLClient.Send(*NewTransaction(tranGetFileNameList, nil, f)); err != nil {
+ if err := c.UI.HLClient.Send(*NewTransaction(TranGetFileNameList, nil, f)); err != nil {
c.UI.HLClient.Logger.Errorw("err", "err", err)
}
return event
c.Logger.Infow("get new directory listing", "name", string(entry.name))
c.filePath = append(c.filePath, string(entry.name))
- f := NewField(fieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
+ f := NewField(FieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
- if err := c.UI.HLClient.Send(*NewTransaction(tranGetFileNameList, nil, f)); err != nil {
+ if err := c.UI.HLClient.Send(*NewTransaction(TranGetFileNameList, nil, f)); err != nil {
c.UI.HLClient.Logger.Errorw("err", "err", err)
}
} else {
}
func handleGetMsgs(c *Client, t *Transaction) (res []Transaction, err error) {
- newsText := string(t.GetField(fieldData).Data)
+ newsText := string(t.GetField(FieldData).Data)
newsText = strings.ReplaceAll(newsText, "\r", "\n")
newsTextView := tview.NewTextView().
func handleNotifyChangeUser(c *Client, t *Transaction) (res []Transaction, err error) {
newUser := User{
- ID: t.GetField(fieldUserID).Data,
- Name: string(t.GetField(fieldUserName).Data),
- Icon: t.GetField(fieldUserIconID).Data,
- Flags: t.GetField(fieldUserFlags).Data,
+ ID: t.GetField(FieldUserID).Data,
+ Name: string(t.GetField(FieldUserName).Data),
+ Icon: t.GetField(FieldUserIconID).Data,
+ Flags: t.GetField(FieldUserFlags).Data,
}
// Possible cases:
}
func handleNotifyDeleteUser(c *Client, t *Transaction) (res []Transaction, err error) {
- exitUser := t.GetField(fieldUserID).Data
+ exitUser := t.GetField(FieldUserID).Data
var newUserList []User
for _, u := range c.UserList {
func handleClientGetUserNameList(c *Client, t *Transaction) (res []Transaction, err error) {
var users []User
for _, field := range t.Fields {
- // The Hotline protocol docs say that ClientGetUserNameList should only return fieldUsernameWithInfo (300)
- // fields, but shxd sneaks in fieldChatSubject (115) so it's important to filter explicitly for the expected
+ // The Hotline protocol docs say that ClientGetUserNameList should only return FieldUsernameWithInfo (300)
+ // fields, but shxd sneaks in FieldChatSubject (115) so it's important to filter explicitly for the expected
// field type. Probably a good idea to do everywhere.
if bytes.Equal(field.ID, []byte{0x01, 0x2c}) {
u, err := ReadUser(field.Data)
}
func handleClientChatMsg(c *Client, t *Transaction) (res []Transaction, err error) {
- if c.pref.EnableBell {
+ if c.Pref.EnableBell {
fmt.Println("\a")
}
- _, _ = fmt.Fprintf(c.UI.chatBox, "%s \n", t.GetField(fieldData).Data)
+ _, _ = fmt.Fprintf(c.UI.chatBox, "%s \n", t.GetField(FieldData).Data)
return res, err
}
func handleClientTranUserAccess(c *Client, t *Transaction) (res []Transaction, err error) {
- c.UserAccess = t.GetField(fieldUserAccess).Data
+ c.UserAccess = t.GetField(FieldUserAccess).Data
return res, err
}
func handleClientTranShowAgreement(c *Client, t *Transaction) (res []Transaction, err error) {
- agreement := string(t.GetField(fieldData).Data)
+ agreement := string(t.GetField(FieldData).Data)
agreement = strings.ReplaceAll(agreement, "\r", "\n")
agreeModal := tview.NewModal().
if buttonIndex == 0 {
res = append(res,
*NewTransaction(
- tranAgreed, nil,
- NewField(fieldUserName, []byte(c.pref.Username)),
- NewField(fieldUserIconID, c.pref.IconBytes()),
- NewField(fieldUserFlags, []byte{0x00, 0x00}),
- NewField(fieldOptions, []byte{0x00, 0x00}),
+ TranAgreed, nil,
+ NewField(FieldUserName, []byte(c.Pref.Username)),
+ NewField(FieldUserIconID, c.Pref.IconBytes()),
+ NewField(FieldUserFlags, []byte{0x00, 0x00}),
+ NewField(FieldOptions, []byte{0x00, 0x00}),
),
)
c.UI.Pages.HidePage("agreement")
func handleClientTranLogin(c *Client, t *Transaction) (res []Transaction, err error) {
if !bytes.Equal(t.ErrorCode, []byte{0, 0, 0, 0}) {
- errMsg := string(t.GetField(fieldError).Data)
+ errMsg := string(t.GetField(FieldError).Data)
errModal := tview.NewModal()
errModal.SetText(errMsg)
errModal.AddButtons([]string{"Oh no"})
c.UI.App.Draw() // TODO: errModal doesn't render without this. wtf?
- c.Logger.Error(string(t.GetField(fieldError).Data))
- return nil, errors.New("login error: " + string(t.GetField(fieldError).Data))
+ c.Logger.Error(string(t.GetField(FieldError).Data))
+ return nil, errors.New("login error: " + string(t.GetField(FieldError).Data))
}
c.UI.Pages.AddAndSwitchToPage(serverUIPage, c.UI.renderServerUI(), true)
c.UI.App.SetFocus(c.UI.chatInput)
- if err := c.Send(*NewTransaction(tranGetUserNameList, nil)); err != nil {
+ if err := c.Send(*NewTransaction(TranGetUserNameList, nil)); err != nil {
c.Logger.Errorw("err", "err", err)
}
return res, err
}
// JoinServer connects to a Hotline server and completes the login flow
-func (c *Client) JoinServer(address, login, passwd string) error {
+func (c *Client) Connect(address, login, passwd string) (err error) {
// Establish TCP connection to server
- if err := c.connect(address); err != nil {
+ c.Connection, err = net.DialTimeout("tcp", address, 5*time.Second)
+ if err != nil {
return err
}
return err
}
- // Authenticate (send tranLogin 107)
+ // Authenticate (send TranLogin 107)
if err := c.LogIn(login, passwd); err != nil {
return err
}
func (c *Client) keepalive() error {
for {
time.Sleep(300 * time.Second)
- _ = c.Send(*NewTransaction(tranKeepAlive, nil))
+ _ = c.Send(*NewTransaction(TranKeepAlive, nil))
c.Logger.Infow("Sent keepalive ping")
}
}
-// connect establishes a connection with a Server by sending handshake sequence
-func (c *Client) connect(address string) error {
- var err error
- c.Connection, err = net.DialTimeout("tcp", address, 5*time.Second)
- if err != nil {
- return err
- }
- return nil
-}
-
var ClientHandshake = []byte{
0x54, 0x52, 0x54, 0x50, // TRTP
0x48, 0x4f, 0x54, 0x4c, // HOTL
func (c *Client) LogIn(login string, password string) error {
return c.Send(
*NewTransaction(
- tranLogin, nil,
- NewField(fieldUserName, []byte(c.pref.Username)),
- NewField(fieldUserIconID, c.pref.IconBytes()),
- NewField(fieldUserLogin, negateString([]byte(login))),
- NewField(fieldUserPassword, negateString([]byte(password))),
+ TranLogin, nil,
+ NewField(FieldUserName, []byte(c.Pref.Username)),
+ NewField(FieldUserIconID, c.Pref.IconBytes()),
+ NewField(FieldUserLogin, negateString([]byte(login))),
+ NewField(FieldUserPassword, negateString([]byte(password))),
),
)
}
c.Send(t)
}
} else {
- c.Logger.Errorw(
+ c.Logger.Debugw(
"Unimplemented transaction type received",
"RequestID", requestNum,
"TransactionID", t.ID,
func (c *Client) Disconnect() error {
return c.Connection.Close()
}
+
+func (c *Client) HandleTransactions() error {
+ // Create a new scanner for parsing incoming bytes into transaction tokens
+ scanner := bufio.NewScanner(c.Connection)
+ scanner.Split(transactionScanner)
+
+ // Scan for new transactions and handle them as they come in.
+ for scanner.Scan() {
+ // Make a new []byte slice and copy the scanner bytes to it. This is critical to avoid a data race as the
+ // scanner re-uses the buffer for subsequent scans.
+ buf := make([]byte, len(scanner.Bytes()))
+ copy(buf, scanner.Bytes())
+
+ var t Transaction
+ _, err := t.Write(buf)
+ if err != nil {
+ break
+ }
+ if err := c.HandleTransaction(&t); err != nil {
+ c.Logger.Errorw("Error handling transaction", "err", err)
+ }
+ }
+
+ if scanner.Err() == nil {
+ return scanner.Err()
+ }
+ return nil
+}
cc.Server.mux.Lock()
defer cc.Server.mux.Unlock()
- if requestNum != tranKeepAlive {
+ if requestNum != TranKeepAlive {
// reset the user idle timer
cc.IdleTime = 0
cc.Idle = false
cc.sendAll(
- tranNotifyChangeUser,
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserFlags, cc.Flags),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserIconID, cc.Icon),
+ TranNotifyChangeUser,
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserFlags, cc.Flags),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserIconID, cc.Icon),
)
}
}
delete(cc.Server.Clients, binary.BigEndian.Uint16(*cc.ID))
- for _, t := range cc.notifyOthers(*NewTransaction(tranNotifyDeleteUser, nil, NewField(fieldUserID, *cc.ID))) {
+ for _, t := range cc.notifyOthers(*NewTransaction(TranNotifyDeleteUser, nil, NewField(FieldUserID, *cc.ID))) {
cc.Server.outbox <- t
}
ID: t.ID,
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte(errMsg)),
+ NewField(FieldError, []byte(errMsg)),
},
}
}
"github.com/jhalter/mobius/concat"
)
-const fieldError = 100
-const fieldData = 101
-const fieldUserName = 102
-const fieldUserID = 103
-const fieldUserIconID = 104
-const fieldUserLogin = 105
-const fieldUserPassword = 106
-const fieldRefNum = 107
-const fieldTransferSize = 108
-const fieldChatOptions = 109
-const fieldUserAccess = 110
-
-// const fieldUserAlias = 111 TODO: implement
-const fieldUserFlags = 112
-const fieldOptions = 113
-const fieldChatID = 114
-const fieldChatSubject = 115
-const fieldWaitingCount = 116
-const fieldBannerType = 152
-const fieldNoServerAgreement = 152
-const fieldVersion = 160
-const fieldCommunityBannerID = 161
-const fieldServerName = 162
-const fieldFileNameWithInfo = 200
-const fieldFileName = 201
-const fieldFilePath = 202
-const fieldFileResumeData = 203
-const fieldFileTransferOptions = 204
-const fieldFileTypeString = 205
-const fieldFileCreatorString = 206
-const fieldFileSize = 207
-const fieldFileCreateDate = 208
-const fieldFileModifyDate = 209
-const fieldFileComment = 210
-const fieldFileNewName = 211
-const fieldFileNewPath = 212
-const fieldFileType = 213
-const fieldQuotingMsg = 214
-const fieldAutomaticResponse = 215
-const fieldFolderItemCount = 220
-const fieldUsernameWithInfo = 300
-const fieldNewsArtListData = 321
-const fieldNewsCatName = 322
-const fieldNewsCatListData15 = 323
-const fieldNewsPath = 325
-const fieldNewsArtID = 326
-const fieldNewsArtDataFlav = 327
-const fieldNewsArtTitle = 328
-const fieldNewsArtPoster = 329
-const fieldNewsArtDate = 330
-const fieldNewsArtPrevArt = 331
-const fieldNewsArtNextArt = 332
-const fieldNewsArtData = 333
-
-// const fieldNewsArtFlags = 334
-const fieldNewsArtParentArt = 335
-const fieldNewsArt1stChildArt = 336
-
-// const fieldNewsArtRecurseDel = 337
+// List of Hotline protocol field types taken from the official 1.9 protocol document
+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
+)
type Field struct {
ID []byte // Type of field
if err != nil {
return nil, err
}
- fields = append(fields, NewField(fieldFileNameWithInfo, b))
+ fields = append(fields, NewField(FieldFileNameWithInfo, b))
}
return fields, nil
binary.BigEndian.PutUint16(c.Flags, uint16(flagBitmap.Int64()))
c.sendAll(
- tranNotifyChangeUser,
- NewField(fieldUserID, *c.ID),
- NewField(fieldUserFlags, c.Flags),
- NewField(fieldUserName, c.UserName),
- NewField(fieldUserIconID, c.Icon),
+ TranNotifyChangeUser,
+ NewField(FieldUserID, *c.ID),
+ NewField(FieldUserFlags, c.Flags),
+ NewField(FieldUserName, c.UserName),
+ NewField(FieldUserIconID, c.Icon),
)
}
}
Flags: c.Flags,
Name: string(c.UserName),
}
- connectedUsers = append(connectedUsers, NewField(fieldUsernameWithInfo, user.Payload()))
+ connectedUsers = append(connectedUsers, NewField(FieldUsernameWithInfo, user.Payload()))
}
return connectedUsers
}
// permaban
if banUntil == nil {
t := NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 0},
- NewField(fieldData, []byte("You are permanently banned on this server")),
- NewField(fieldChatOptions, []byte{0, 0}),
+ NewField(FieldData, []byte("You are permanently banned on this server")),
+ NewField(FieldChatOptions, []byte{0, 0}),
)
b, err := t.MarshalBinary()
// temporary ban
if time.Now().Before(*banUntil) {
t := NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 0},
- NewField(fieldData, []byte("You are temporarily banned on this server")),
- NewField(fieldChatOptions, []byte{0, 0}),
+ NewField(FieldData, []byte("You are temporarily banned on this server")),
+ NewField(FieldChatOptions, []byte{0, 0}),
)
b, err := t.MarshalBinary()
if err != nil {
c := s.NewClientConn(rwc, remoteAddr)
defer c.Disconnect()
- encodedLogin := clientLogin.GetField(fieldUserLogin).Data
- encodedPassword := clientLogin.GetField(fieldUserPassword).Data
- c.Version = clientLogin.GetField(fieldVersion).Data
+ encodedLogin := clientLogin.GetField(FieldUserLogin).Data
+ encodedPassword := clientLogin.GetField(FieldUserPassword).Data
+ c.Version = clientLogin.GetField(FieldVersion).Data
var login string
for _, char := range encodedLogin {
return nil
}
- if clientLogin.GetField(fieldUserIconID).Data != nil {
- c.Icon = clientLogin.GetField(fieldUserIconID).Data
+ if clientLogin.GetField(FieldUserIconID).Data != nil {
+ c.Icon = clientLogin.GetField(FieldUserIconID).Data
}
c.Account = c.Server.Accounts[login]
- if clientLogin.GetField(fieldUserName).Data != nil {
+ if clientLogin.GetField(FieldUserName).Data != nil {
if c.Authorize(accessAnyName) {
- c.UserName = clientLogin.GetField(fieldUserName).Data
+ c.UserName = clientLogin.GetField(FieldUserName).Data
} else {
c.UserName = []byte(c.Account.Name)
}
}
s.outbox <- c.NewReply(&clientLogin,
- NewField(fieldVersion, []byte{0x00, 0xbe}),
- NewField(fieldCommunityBannerID, []byte{0, 0}),
- NewField(fieldServerName, []byte(s.Config.Name)),
+ NewField(FieldVersion, []byte{0x00, 0xbe}),
+ NewField(FieldCommunityBannerID, []byte{0, 0}),
+ NewField(FieldServerName, []byte(s.Config.Name)),
)
// Send user access privs so client UI knows how to behave
- c.Server.outbox <- *NewTransaction(tranUserAccess, c.ID, NewField(fieldUserAccess, c.Account.Access[:]))
+ c.Server.outbox <- *NewTransaction(TranUserAccess, c.ID, NewField(FieldUserAccess, c.Account.Access[:]))
// Accounts with accessNoAgreement do not receive the server agreement on login. The behavior is different between
- // client versions. For 1.2.3 client, we do not send tranShowAgreement. For other client versions, we send
- // tranShowAgreement but with the NoServerAgreement field set to 1.
+ // client versions. For 1.2.3 client, we do not send TranShowAgreement. For other client versions, we send
+ // TranShowAgreement but with the NoServerAgreement field set to 1.
if c.Authorize(accessNoAgreement) {
// If client version is nil, then the client uses the 1.2.3 login behavior
if c.Version != nil {
- c.Server.outbox <- *NewTransaction(tranShowAgreement, c.ID, NewField(fieldNoServerAgreement, []byte{1}))
+ c.Server.outbox <- *NewTransaction(TranShowAgreement, c.ID, NewField(FieldNoServerAgreement, []byte{1}))
}
} else {
- c.Server.outbox <- *NewTransaction(tranShowAgreement, c.ID, NewField(fieldData, s.Agreement))
+ c.Server.outbox <- *NewTransaction(TranShowAgreement, c.ID, NewField(FieldData, s.Agreement))
}
// If the client has provided a username as part of the login, we can infer that it is using the 1.2.3 login
// flow and not the 1.5+ flow.
if len(c.UserName) != 0 {
// Add the client username to the logger. For 1.5+ clients, we don't have this information yet as it comes as
- // part of tranAgreed
+ // part of TranAgreed
c.logger = c.logger.With("name", string(c.UserName))
c.logger.Infow("Login successful", "clientVersion", "Not sent (probably 1.2.3)")
// Notify other clients on the server that the new user has logged in. For 1.5+ clients we don't have this
- // information yet, so we do it in tranAgreed instead
+ // information yet, so we do it in TranAgreed instead
for _, t := range c.notifyOthers(
*NewTransaction(
- tranNotifyChangeUser, nil,
- NewField(fieldUserName, c.UserName),
- NewField(fieldUserID, *c.ID),
- NewField(fieldUserIconID, c.Icon),
- NewField(fieldUserFlags, c.Flags),
+ TranNotifyChangeUser, nil,
+ NewField(FieldUserName, c.UserName),
+ NewField(FieldUserID, *c.ID),
+ NewField(FieldUserIconID, c.Icon),
+ NewField(FieldUserFlags, c.Flags),
),
) {
c.Server.outbox <- t
)
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 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
+ 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
)
type Transaction struct {
"time"
)
+type HandlerFunc func(*ClientConn, *Transaction) ([]Transaction, error)
+
type TransactionType struct {
- Handler func(*ClientConn, *Transaction) ([]Transaction, error) // function for handling the transaction type
- Name string // Name of transaction as it will appear in logging
+ Handler HandlerFunc // function for handling the transaction type
+ Name string // Name of transaction as it will appear in logging
RequiredFields []requiredField
}
var TransactionHandlers = map[uint16]TransactionType{
// Server initiated
- tranChatMsg: {
- Name: "tranChatMsg",
+ TranChatMsg: {
+ Name: "TranChatMsg",
},
// Server initiated
- tranNotifyChangeUser: {
- Name: "tranNotifyChangeUser",
+ TranNotifyChangeUser: {
+ Name: "TranNotifyChangeUser",
},
- tranError: {
- Name: "tranError",
+ TranError: {
+ Name: "TranError",
},
- tranShowAgreement: {
- Name: "tranShowAgreement",
+ TranShowAgreement: {
+ Name: "TranShowAgreement",
},
- tranUserAccess: {
- Name: "tranUserAccess",
+ TranUserAccess: {
+ Name: "TranUserAccess",
},
- tranNotifyDeleteUser: {
- Name: "tranNotifyDeleteUser",
+ TranNotifyDeleteUser: {
+ Name: "TranNotifyDeleteUser",
},
- tranAgreed: {
- Name: "tranAgreed",
+ TranAgreed: {
+ Name: "TranAgreed",
Handler: HandleTranAgreed,
},
- tranChatSend: {
- Name: "tranChatSend",
+ TranChatSend: {
+ Name: "TranChatSend",
Handler: HandleChatSend,
RequiredFields: []requiredField{
{
- ID: fieldData,
+ ID: FieldData,
minLen: 0,
},
},
},
- tranDelNewsArt: {
- Name: "tranDelNewsArt",
+ TranDelNewsArt: {
+ Name: "TranDelNewsArt",
Handler: HandleDelNewsArt,
},
- tranDelNewsItem: {
- Name: "tranDelNewsItem",
+ TranDelNewsItem: {
+ Name: "TranDelNewsItem",
Handler: HandleDelNewsItem,
},
- tranDeleteFile: {
- Name: "tranDeleteFile",
+ TranDeleteFile: {
+ Name: "TranDeleteFile",
Handler: HandleDeleteFile,
},
- tranDeleteUser: {
- Name: "tranDeleteUser",
+ TranDeleteUser: {
+ Name: "TranDeleteUser",
Handler: HandleDeleteUser,
},
- tranDisconnectUser: {
- Name: "tranDisconnectUser",
+ TranDisconnectUser: {
+ Name: "TranDisconnectUser",
Handler: HandleDisconnectUser,
},
- tranDownloadFile: {
- Name: "tranDownloadFile",
+ TranDownloadFile: {
+ Name: "TranDownloadFile",
Handler: HandleDownloadFile,
},
- tranDownloadFldr: {
- Name: "tranDownloadFldr",
+ TranDownloadFldr: {
+ Name: "TranDownloadFldr",
Handler: HandleDownloadFolder,
},
- tranGetClientInfoText: {
- Name: "tranGetClientInfoText",
+ TranGetClientInfoText: {
+ Name: "TranGetClientInfoText",
Handler: HandleGetClientInfoText,
},
- tranGetFileInfo: {
- Name: "tranGetFileInfo",
+ TranGetFileInfo: {
+ Name: "TranGetFileInfo",
Handler: HandleGetFileInfo,
},
- tranGetFileNameList: {
- Name: "tranGetFileNameList",
+ TranGetFileNameList: {
+ Name: "TranGetFileNameList",
Handler: HandleGetFileNameList,
},
- tranGetMsgs: {
- Name: "tranGetMsgs",
+ TranGetMsgs: {
+ Name: "TranGetMsgs",
Handler: HandleGetMsgs,
},
- tranGetNewsArtData: {
- Name: "tranGetNewsArtData",
+ TranGetNewsArtData: {
+ Name: "TranGetNewsArtData",
Handler: HandleGetNewsArtData,
},
- tranGetNewsArtNameList: {
- Name: "tranGetNewsArtNameList",
+ TranGetNewsArtNameList: {
+ Name: "TranGetNewsArtNameList",
Handler: HandleGetNewsArtNameList,
},
- tranGetNewsCatNameList: {
- Name: "tranGetNewsCatNameList",
+ TranGetNewsCatNameList: {
+ Name: "TranGetNewsCatNameList",
Handler: HandleGetNewsCatNameList,
},
- tranGetUser: {
- Name: "tranGetUser",
+ TranGetUser: {
+ Name: "TranGetUser",
Handler: HandleGetUser,
},
- tranGetUserNameList: {
+ TranGetUserNameList: {
Name: "tranHandleGetUserNameList",
Handler: HandleGetUserNameList,
},
- tranInviteNewChat: {
- Name: "tranInviteNewChat",
+ TranInviteNewChat: {
+ Name: "TranInviteNewChat",
Handler: HandleInviteNewChat,
},
- tranInviteToChat: {
- Name: "tranInviteToChat",
+ TranInviteToChat: {
+ Name: "TranInviteToChat",
Handler: HandleInviteToChat,
},
- tranJoinChat: {
- Name: "tranJoinChat",
+ TranJoinChat: {
+ Name: "TranJoinChat",
Handler: HandleJoinChat,
},
- tranKeepAlive: {
- Name: "tranKeepAlive",
+ TranKeepAlive: {
+ Name: "TranKeepAlive",
Handler: HandleKeepAlive,
},
- tranLeaveChat: {
- Name: "tranJoinChat",
+ TranLeaveChat: {
+ Name: "TranJoinChat",
Handler: HandleLeaveChat,
},
- tranListUsers: {
- Name: "tranListUsers",
+ TranListUsers: {
+ Name: "TranListUsers",
Handler: HandleListUsers,
},
- tranMoveFile: {
- Name: "tranMoveFile",
+ TranMoveFile: {
+ Name: "TranMoveFile",
Handler: HandleMoveFile,
},
- tranNewFolder: {
- Name: "tranNewFolder",
+ TranNewFolder: {
+ Name: "TranNewFolder",
Handler: HandleNewFolder,
},
- tranNewNewsCat: {
- Name: "tranNewNewsCat",
+ TranNewNewsCat: {
+ Name: "TranNewNewsCat",
Handler: HandleNewNewsCat,
},
- tranNewNewsFldr: {
- Name: "tranNewNewsFldr",
+ TranNewNewsFldr: {
+ Name: "TranNewNewsFldr",
Handler: HandleNewNewsFldr,
},
- tranNewUser: {
- Name: "tranNewUser",
+ TranNewUser: {
+ Name: "TranNewUser",
Handler: HandleNewUser,
},
- tranUpdateUser: {
- Name: "tranUpdateUser",
+ TranUpdateUser: {
+ Name: "TranUpdateUser",
Handler: HandleUpdateUser,
},
- tranOldPostNews: {
- Name: "tranOldPostNews",
+ TranOldPostNews: {
+ Name: "TranOldPostNews",
Handler: HandleTranOldPostNews,
},
- tranPostNewsArt: {
- Name: "tranPostNewsArt",
+ TranPostNewsArt: {
+ Name: "TranPostNewsArt",
Handler: HandlePostNewsArt,
},
- tranRejectChatInvite: {
- Name: "tranRejectChatInvite",
+ TranRejectChatInvite: {
+ Name: "TranRejectChatInvite",
Handler: HandleRejectChatInvite,
},
- tranSendInstantMsg: {
- Name: "tranSendInstantMsg",
+ TranSendInstantMsg: {
+ Name: "TranSendInstantMsg",
Handler: HandleSendInstantMsg,
RequiredFields: []requiredField{
{
- ID: fieldData,
+ ID: FieldData,
minLen: 0,
},
{
- ID: fieldUserID,
+ ID: FieldUserID,
},
},
},
- tranSetChatSubject: {
- Name: "tranSetChatSubject",
+ TranSetChatSubject: {
+ Name: "TranSetChatSubject",
Handler: HandleSetChatSubject,
},
- tranMakeFileAlias: {
- Name: "tranMakeFileAlias",
+ TranMakeFileAlias: {
+ Name: "TranMakeFileAlias",
Handler: HandleMakeAlias,
RequiredFields: []requiredField{
- {ID: fieldFileName, minLen: 1},
- {ID: fieldFilePath, minLen: 1},
- {ID: fieldFileNewPath, minLen: 1},
+ {ID: FieldFileName, minLen: 1},
+ {ID: FieldFilePath, minLen: 1},
+ {ID: FieldFileNewPath, minLen: 1},
},
},
- tranSetClientUserInfo: {
- Name: "tranSetClientUserInfo",
+ TranSetClientUserInfo: {
+ Name: "TranSetClientUserInfo",
Handler: HandleSetClientUserInfo,
},
- tranSetFileInfo: {
- Name: "tranSetFileInfo",
+ TranSetFileInfo: {
+ Name: "TranSetFileInfo",
Handler: HandleSetFileInfo,
},
- tranSetUser: {
- Name: "tranSetUser",
+ TranSetUser: {
+ Name: "TranSetUser",
Handler: HandleSetUser,
},
- tranUploadFile: {
- Name: "tranUploadFile",
+ TranUploadFile: {
+ Name: "TranUploadFile",
Handler: HandleUploadFile,
},
- tranUploadFldr: {
- Name: "tranUploadFldr",
+ TranUploadFldr: {
+ Name: "TranUploadFldr",
Handler: HandleUploadFolder,
},
- tranUserBroadcast: {
- Name: "tranUserBroadcast",
+ TranUserBroadcast: {
+ Name: "TranUserBroadcast",
Handler: HandleUserBroadcast,
},
- tranDownloadBanner: {
- Name: "tranDownloadBanner",
+ TranDownloadBanner: {
+ Name: "TranDownloadBanner",
Handler: HandleDownloadBanner,
},
}
// Truncate long usernames
trunc := fmt.Sprintf("%13s", cc.UserName)
- formattedMsg := fmt.Sprintf("\r%.14s: %s", trunc, t.GetField(fieldData).Data)
+ formattedMsg := fmt.Sprintf("\r%.14s: %s", trunc, t.GetField(FieldData).Data)
// By holding the option key, Hotline chat allows users to send /me formatted messages like:
// *** Halcyon does stuff
- // This is indicated by the presence of the optional field fieldChatOptions set to a value of 1.
+ // This is indicated by the presence of the optional field FieldChatOptions set to a value of 1.
// Most clients do not send this option for normal chat messages.
- if t.GetField(fieldChatOptions).Data != nil && bytes.Equal(t.GetField(fieldChatOptions).Data, []byte{0, 1}) {
- formattedMsg = fmt.Sprintf("\r*** %s %s", cc.UserName, t.GetField(fieldData).Data)
+ if t.GetField(FieldChatOptions).Data != nil && bytes.Equal(t.GetField(FieldChatOptions).Data, []byte{0, 1}) {
+ formattedMsg = fmt.Sprintf("\r*** %s %s", cc.UserName, t.GetField(FieldData).Data)
}
// The ChatID field is used to identify messages as belonging to a private chat.
// All clients *except* Frogblast omit this field for public chat, but Frogblast sends a value of 00 00 00 00.
- chatID := t.GetField(fieldChatID).Data
+ chatID := t.GetField(FieldChatID).Data
if chatID != nil && !bytes.Equal([]byte{0, 0, 0, 0}, chatID) {
chatInt := binary.BigEndian.Uint32(chatID)
privChat := cc.Server.PrivateChats[chatInt]
// send the message to all connected clients of the private chat
for _, c := range clients {
res = append(res, *NewTransaction(
- tranChatMsg,
+ TranChatMsg,
c.ID,
- NewField(fieldChatID, chatID),
- NewField(fieldData, []byte(formattedMsg)),
+ NewField(FieldChatID, chatID),
+ NewField(FieldData, []byte(formattedMsg)),
))
}
return res, err
for _, c := range sortedClients(cc.Server.Clients) {
// Filter out clients that do not have the read chat permission
if c.Authorize(accessReadChat) {
- res = append(res, *NewTransaction(tranChatMsg, c.ID, NewField(fieldData, []byte(formattedMsg))))
+ res = append(res, *NewTransaction(TranChatMsg, c.ID, NewField(FieldData, []byte(formattedMsg))))
}
}
return res, err
}
- msg := t.GetField(fieldData)
- ID := t.GetField(fieldUserID)
+ msg := t.GetField(FieldData)
+ ID := t.GetField(FieldUserID)
reply := NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&ID.Data,
- NewField(fieldData, msg.Data),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- NewField(fieldOptions, []byte{0, 1}),
+ NewField(FieldData, msg.Data),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldOptions, []byte{0, 1}),
)
- // Later versions of Hotline include the original message in the fieldQuotingMsg field so
+ // Later versions of Hotline include the original message in the FieldQuotingMsg field so
// the receiving client can display both the received message and what it is in reply to
- if t.GetField(fieldQuotingMsg).Data != nil {
- reply.Fields = append(reply.Fields, NewField(fieldQuotingMsg, t.GetField(fieldQuotingMsg).Data))
+ if t.GetField(FieldQuotingMsg).Data != nil {
+ reply.Fields = append(reply.Fields, NewField(FieldQuotingMsg, t.GetField(FieldQuotingMsg).Data))
}
id, _ := byteToInt(ID.Data)
if flagBitmap.Bit(userFLagRefusePChat) == 1 {
res = append(res,
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
cc.ID,
- NewField(fieldData, []byte(string(otherClient.UserName)+" does not accept private messages.")),
- NewField(fieldUserName, otherClient.UserName),
- NewField(fieldUserID, *otherClient.ID),
- NewField(fieldOptions, []byte{0, 2}),
+ NewField(FieldData, []byte(string(otherClient.UserName)+" does not accept private messages.")),
+ NewField(FieldUserName, otherClient.UserName),
+ NewField(FieldUserID, *otherClient.ID),
+ NewField(FieldOptions, []byte{0, 2}),
),
)
} else {
if len(otherClient.AutoReply) > 0 {
res = append(res,
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
cc.ID,
- NewField(fieldData, otherClient.AutoReply),
- NewField(fieldUserName, otherClient.UserName),
- NewField(fieldUserID, *otherClient.ID),
- NewField(fieldOptions, []byte{0, 1}),
+ NewField(FieldData, otherClient.AutoReply),
+ NewField(FieldUserName, otherClient.UserName),
+ NewField(FieldUserID, *otherClient.ID),
+ NewField(FieldOptions, []byte{0, 1}),
),
)
}
}
func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName)
if err != nil {
}
res = append(res, cc.NewReply(t,
- NewField(fieldFileName, []byte(fw.name)),
- NewField(fieldFileTypeString, fw.ffo.FlatFileInformationFork.friendlyType()),
- NewField(fieldFileCreatorString, fw.ffo.FlatFileInformationFork.friendlyCreator()),
- NewField(fieldFileComment, fw.ffo.FlatFileInformationFork.Comment),
- NewField(fieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature),
- NewField(fieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate),
- NewField(fieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate),
- NewField(fieldFileSize, fw.totalSize()),
+ NewField(FieldFileName, []byte(fw.name)),
+ NewField(FieldFileTypeString, fw.ffo.FlatFileInformationFork.friendlyType()),
+ NewField(FieldFileCreatorString, fw.ffo.FlatFileInformationFork.friendlyCreator()),
+ NewField(FieldFileComment, fw.ffo.FlatFileInformationFork.Comment),
+ NewField(FieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature),
+ NewField(FieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate),
+ NewField(FieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate),
+ NewField(FieldFileSize, fw.totalSize()),
))
return res, err
}
// * 210 File comment Optional
// Fields used in the reply: None
func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName)
if err != nil {
if err != nil {
return res, err
}
- if t.GetField(fieldFileComment).Data != nil {
+ if t.GetField(FieldFileComment).Data != nil {
switch mode := fi.Mode(); {
case mode.IsDir():
if !cc.Authorize(accessSetFolderComment) {
}
}
- if err := hlFile.ffo.FlatFileInformationFork.setComment(t.GetField(fieldFileComment).Data); err != nil {
+ if err := hlFile.ffo.FlatFileInformationFork.setComment(t.GetField(FieldFileComment).Data); err != nil {
return res, err
}
w, err := hlFile.infoForkWriter()
}
}
- fullNewFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, t.GetField(fieldFileNewName).Data)
+ fullNewFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, t.GetField(FieldFileNewName).Data)
if err != nil {
return nil, err
}
- fileNewName := t.GetField(fieldFileNewName).Data
+ fileNewName := t.GetField(FieldFileNewName).Data
if fileNewName != nil {
switch mode := fi.Mode(); {
// * 202 File path
// Fields used in the reply: none
func HandleDeleteFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName)
if err != nil {
// HandleMoveFile moves files or folders. Note: seemingly not documented
func HandleMoveFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- fileName := string(t.GetField(fieldFileName).Data)
+ fileName := string(t.GetField(FieldFileName).Data)
- filePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(fieldFilePath).Data, t.GetField(fieldFileName).Data)
+ filePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(FieldFilePath).Data, t.GetField(FieldFileName).Data)
if err != nil {
return res, err
}
- fileNewPath, err := readPath(cc.Server.Config.FileRoot, t.GetField(fieldFileNewPath).Data, nil)
+ fileNewPath, err := readPath(cc.Server.Config.FileRoot, t.GetField(FieldFileNewPath).Data, nil)
if err != nil {
return res, err
}
res = append(res, cc.NewErrReply(t, "You are not allowed to create folders."))
return res, err
}
- folderName := string(t.GetField(fieldFileName).Data)
+ folderName := string(t.GetField(FieldFileName).Data)
folderName = path.Join("/", folderName)
var subPath string
- // fieldFilePath is only present for nested paths
- if t.GetField(fieldFilePath).Data != nil {
+ // FieldFilePath is only present for nested paths
+ if t.GetField(FieldFilePath).Data != nil {
var newFp FilePath
- _, err := newFp.Write(t.GetField(fieldFilePath).Data)
+ _, err := newFp.Write(t.GetField(FieldFilePath).Data)
if err != nil {
return nil, err
}
return res, err
}
- login := DecodeUserString(t.GetField(fieldUserLogin).Data)
- userName := string(t.GetField(fieldUserName).Data)
+ login := DecodeUserString(t.GetField(FieldUserLogin).Data)
+ userName := string(t.GetField(FieldUserName).Data)
- newAccessLvl := t.GetField(fieldUserAccess).Data
+ newAccessLvl := t.GetField(FieldUserAccess).Data
account := cc.Server.Accounts[login]
account.Name = userName
copy(account.Access[:], newAccessLvl)
// If the password field is cleared in the Hotline edit user UI, the SetUser transaction does
- // not include fieldUserPassword
- if t.GetField(fieldUserPassword).Data == nil {
+ // not include FieldUserPassword
+ if t.GetField(FieldUserPassword).Data == nil {
account.Password = hashAndSalt([]byte(""))
}
- if len(t.GetField(fieldUserPassword).Data) > 1 {
- account.Password = hashAndSalt(t.GetField(fieldUserPassword).Data)
+ if len(t.GetField(FieldUserPassword).Data) > 1 {
+ account.Password = hashAndSalt(t.GetField(FieldUserPassword).Data)
}
out, err := yaml.Marshal(&account)
for _, c := range cc.Server.Clients {
if c.Account.Login == login {
// Note: comment out these two lines to test server-side deny messages
- newT := NewTransaction(tranUserAccess, c.ID, NewField(fieldUserAccess, newAccessLvl))
+ newT := NewTransaction(TranUserAccess, c.ID, NewField(FieldUserAccess, newAccessLvl))
res = append(res, *newT)
flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(c.Flags)))
c.Account.Access = account.Access
cc.sendAll(
- tranNotifyChangeUser,
- NewField(fieldUserID, *c.ID),
- NewField(fieldUserFlags, c.Flags),
- NewField(fieldUserName, c.UserName),
- NewField(fieldUserIconID, c.Icon),
+ TranNotifyChangeUser,
+ NewField(FieldUserID, *c.ID),
+ NewField(FieldUserFlags, c.Flags),
+ NewField(FieldUserName, c.UserName),
+ NewField(FieldUserIconID, c.Icon),
)
}
}
return res, err
}
- account := cc.Server.Accounts[string(t.GetField(fieldUserLogin).Data)]
+ account := cc.Server.Accounts[string(t.GetField(FieldUserLogin).Data)]
if account == nil {
res = append(res, cc.NewErrReply(t, "Account does not exist."))
return res, err
}
res = append(res, cc.NewReply(t,
- NewField(fieldUserName, []byte(account.Name)),
- NewField(fieldUserLogin, negateString(t.GetField(fieldUserLogin).Data)),
- NewField(fieldUserPassword, []byte(account.Password)),
- NewField(fieldUserAccess, account.Access[:]),
+ NewField(FieldUserName, []byte(account.Name)),
+ NewField(FieldUserLogin, negateString(t.GetField(FieldUserLogin).Data)),
+ NewField(FieldUserPassword, []byte(account.Password)),
+ NewField(FieldUserAccess, account.Access[:]),
))
return res, err
}
return res, err
}
- userFields = append(userFields, NewField(fieldData, b[:n]))
+ userFields = append(userFields, NewField(FieldData, b[:n]))
}
res = append(res, cc.NewReply(t, userFields...))
}
if len(subFields) == 1 {
- login := DecodeUserString(getField(fieldData, &subFields).Data)
+ login := DecodeUserString(getField(FieldData, &subFields).Data)
cc.logger.Infow("DeleteUser", "login", login)
if !cc.Authorize(accessDeleteUser) {
continue
}
- login := DecodeUserString(getField(fieldUserLogin, &subFields).Data)
+ login := DecodeUserString(getField(FieldUserLogin, &subFields).Data)
// check if the login dataFile; if so, we know we are updating an existing user
if acc, ok := cc.Server.Accounts[login]; ok {
return res, err
}
- if getField(fieldUserPassword, &subFields) != nil {
- newPass := getField(fieldUserPassword, &subFields).Data
+ if getField(FieldUserPassword, &subFields) != nil {
+ newPass := getField(FieldUserPassword, &subFields).Data
acc.Password = hashAndSalt(newPass)
} else {
acc.Password = hashAndSalt([]byte(""))
}
- if getField(fieldUserAccess, &subFields) != nil {
- copy(acc.Access[:], getField(fieldUserAccess, &subFields).Data)
+ if getField(FieldUserAccess, &subFields) != nil {
+ copy(acc.Access[:], getField(FieldUserAccess, &subFields).Data)
}
err = cc.Server.UpdateUser(
- DecodeUserString(getField(fieldData, &subFields).Data),
- DecodeUserString(getField(fieldUserLogin, &subFields).Data),
- string(getField(fieldUserName, &subFields).Data),
+ DecodeUserString(getField(FieldData, &subFields).Data),
+ DecodeUserString(getField(FieldUserLogin, &subFields).Data),
+ string(getField(FieldUserName, &subFields).Data),
acc.Password,
acc.Access,
)
}
newAccess := accessBitmap{}
- copy(newAccess[:], getField(fieldUserAccess, &subFields).Data[:])
+ copy(newAccess[:], getField(FieldUserAccess, &subFields).Data[:])
// Prevent account from creating new account with greater permission
for i := 0; i < 64; i++ {
}
}
- err := cc.Server.NewUser(login, string(getField(fieldUserName, &subFields).Data), string(getField(fieldUserPassword, &subFields).Data), newAccess)
+ err := cc.Server.NewUser(login, string(getField(FieldUserName, &subFields).Data), string(getField(FieldUserPassword, &subFields).Data), newAccess)
if err != nil {
return []Transaction{}, err
}
return res, err
}
- login := DecodeUserString(t.GetField(fieldUserLogin).Data)
+ login := DecodeUserString(t.GetField(FieldUserLogin).Data)
// If the account already dataFile, reply with an error
if _, ok := cc.Server.Accounts[login]; ok {
}
newAccess := accessBitmap{}
- copy(newAccess[:], t.GetField(fieldUserAccess).Data[:])
+ copy(newAccess[:], t.GetField(FieldUserAccess).Data[:])
// Prevent account from creating new account with greater permission
for i := 0; i < 64; i++ {
}
}
- if err := cc.Server.NewUser(login, string(t.GetField(fieldUserName).Data), string(t.GetField(fieldUserPassword).Data), newAccess); err != nil {
+ if err := cc.Server.NewUser(login, string(t.GetField(FieldUserName).Data), string(t.GetField(FieldUserPassword).Data), newAccess); err != nil {
return []Transaction{}, err
}
}
// TODO: Handle case where account doesn't exist; e.g. delete race condition
- login := DecodeUserString(t.GetField(fieldUserLogin).Data)
+ login := DecodeUserString(t.GetField(FieldUserLogin).Data)
if err := cc.Server.DeleteUser(login); err != nil {
return res, err
}
cc.sendAll(
- tranServerMsg,
- NewField(fieldData, t.GetField(tranGetMsgs).Data),
- NewField(fieldChatOptions, []byte{0}),
+ TranServerMsg,
+ NewField(FieldData, t.GetField(TranGetMsgs).Data),
+ NewField(FieldChatOptions, []byte{0}),
)
res = append(res, cc.NewReply(t))
return res, err
}
- clientID, _ := byteToInt(t.GetField(fieldUserID).Data)
+ clientID, _ := byteToInt(t.GetField(FieldUserID).Data)
clientConn := cc.Server.Clients[uint16(clientID)]
if clientConn == nil {
}
res = append(res, cc.NewReply(t,
- NewField(fieldData, []byte(clientConn.String())),
- NewField(fieldUserName, clientConn.UserName),
+ NewField(FieldData, []byte(clientConn.String())),
+ NewField(FieldUserName, clientConn.UserName),
))
return res, err
}
}
func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if t.GetField(fieldUserName).Data != nil {
+ if t.GetField(FieldUserName).Data != nil {
if cc.Authorize(accessAnyName) {
- cc.UserName = t.GetField(fieldUserName).Data
+ cc.UserName = t.GetField(FieldUserName).Data
} else {
cc.UserName = []byte(cc.Account.Name)
}
}
- cc.Icon = t.GetField(fieldUserIconID).Data
+ cc.Icon = t.GetField(FieldUserIconID).Data
cc.logger = cc.logger.With("name", string(cc.UserName))
cc.logger.Infow("Login successful", "clientVersion", fmt.Sprintf("%v", func() int { i, _ := byteToInt(cc.Version); return i }()))
- options := t.GetField(fieldOptions).Data
+ options := t.GetField(FieldOptions).Data
optBitmap := big.NewInt(int64(binary.BigEndian.Uint16(options)))
flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(cc.Flags)))
// Check auto response
if optBitmap.Bit(autoResponse) == 1 {
- cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
+ cc.AutoReply = t.GetField(FieldAutomaticResponse).Data
} else {
cc.AutoReply = []byte{}
}
trans := cc.notifyOthers(
*NewTransaction(
- tranNotifyChangeUser, nil,
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserIconID, cc.Icon),
- NewField(fieldUserFlags, cc.Flags),
+ TranNotifyChangeUser, nil,
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserIconID, cc.Icon),
+ NewField(FieldUserFlags, cc.Flags),
),
)
res = append(res, trans...)
if cc.Server.Config.BannerFile != "" {
- res = append(res, *NewTransaction(tranServerBanner, cc.ID, NewField(fieldBannerType, []byte("JPEG"))))
+ res = append(res, *NewTransaction(TranServerBanner, cc.ID, NewField(FieldBannerType, []byte("JPEG"))))
}
res = append(res, cc.NewReply(t))
newsTemplate = cc.Server.Config.NewsDelimiter
}
- newsPost := fmt.Sprintf(newsTemplate+"\r", cc.UserName, time.Now().Format(newsDateTemplate), t.GetField(fieldData).Data)
+ newsPost := fmt.Sprintf(newsTemplate+"\r", cc.UserName, time.Now().Format(newsDateTemplate), t.GetField(FieldData).Data)
newsPost = strings.Replace(newsPost, "\n", "\r", -1)
// update news in memory
// Notify all clients of updated news
cc.sendAll(
- tranNewMsg,
- NewField(fieldData, []byte(newsPost)),
+ TranNewMsg,
+ NewField(FieldData, []byte(newsPost)),
)
res = append(res, cc.NewReply(t))
return res, err
}
- clientConn := cc.Server.Clients[binary.BigEndian.Uint16(t.GetField(fieldUserID).Data)]
+ clientConn := cc.Server.Clients[binary.BigEndian.Uint16(t.GetField(FieldUserID).Data)]
if clientConn.Authorize(accessCannotBeDiscon) {
res = append(res, cc.NewErrReply(t, clientConn.Account.Login+" is not allowed to be disconnected."))
return res, err
}
- // If fieldOptions is set, then the client IP is banned in addition to disconnected.
+ // If FieldOptions is set, then the client IP is banned in addition to disconnected.
// 00 01 = temporary ban
// 00 02 = permanent ban
- if t.GetField(fieldOptions).Data != nil {
- switch t.GetField(fieldOptions).Data[1] {
+ if t.GetField(FieldOptions).Data != nil {
+ switch t.GetField(FieldOptions).Data[1] {
case 1:
// send message: "You are temporarily banned on this server"
cc.logger.Infow("Disconnect & temporarily ban " + string(clientConn.UserName))
res = append(res, *NewTransaction(
- tranServerMsg,
+ TranServerMsg,
clientConn.ID,
- NewField(fieldData, []byte("You are temporarily banned on this server")),
- NewField(fieldChatOptions, []byte{0, 0}),
+ NewField(FieldData, []byte("You are temporarily banned on this server")),
+ NewField(FieldChatOptions, []byte{0, 0}),
))
banUntil := time.Now().Add(tempBanDuration)
cc.logger.Infow("Disconnect & ban " + string(clientConn.UserName))
res = append(res, *NewTransaction(
- tranServerMsg,
+ TranServerMsg,
clientConn.ID,
- NewField(fieldData, []byte("You are permanently banned on this server")),
- NewField(fieldChatOptions, []byte{0, 0}),
+ NewField(FieldData, []byte("You are permanently banned on this server")),
+ NewField(FieldChatOptions, []byte{0, 0}),
))
cc.Server.banList[strings.Split(clientConn.RemoteAddr, ":")[0]] = nil
return res, err
}
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
cats := cc.Server.GetNewsCatByPath(pathStrs)
// To store the keys in slice in sorted order
cat := cats[k]
b, _ := cat.MarshalBinary()
fieldData = append(fieldData, NewField(
- fieldNewsCatListData15,
+ FieldNewsCatListData15,
b,
))
}
return res, err
}
- name := string(t.GetField(fieldNewsCatName).Data)
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ name := string(t.GetField(FieldNewsCatName).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
cats := cc.Server.GetNewsCatByPath(pathStrs)
cats[name] = NewsCategoryListData15{
return res, err
}
- name := string(t.GetField(fieldFileName).Data)
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ name := string(t.GetField(FieldFileName).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
cc.logger.Infof("Creating new news folder %s", name)
res = append(res, cc.NewErrReply(t, "You are not allowed to read news."))
return res, err
}
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
var cat NewsCategoryListData15
cats := cc.Server.ThreadedNews.Categories
nald := cat.GetNewsArtListData()
- res = append(res, cc.NewReply(t, NewField(fieldNewsArtListData, nald.Payload())))
+ res = append(res, cc.NewReply(t, NewField(FieldNewsArtListData, nald.Payload())))
return res, err
}
var cat NewsCategoryListData15
cats := cc.Server.ThreadedNews.Categories
- for _, fp := range ReadNewsPath(t.GetField(fieldNewsPath).Data) {
+ for _, fp := range ReadNewsPath(t.GetField(FieldNewsPath).Data) {
cat = cats[fp]
cats = cats[fp].SubCats
}
// The official Hotline clients will send the article ID as 2 bytes if possible, but
// some third party clients such as Frogblast and Heildrun will always send 4 bytes
- convertedID, err := byteToInt(t.GetField(fieldNewsArtID).Data)
+ convertedID, err := byteToInt(t.GetField(FieldNewsArtID).Data)
if err != nil {
return res, err
}
}
res = append(res, cc.NewReply(t,
- NewField(fieldNewsArtTitle, []byte(art.Title)),
- NewField(fieldNewsArtPoster, []byte(art.Poster)),
- NewField(fieldNewsArtDate, art.Date),
- NewField(fieldNewsArtPrevArt, art.PrevArt),
- NewField(fieldNewsArtNextArt, art.NextArt),
- NewField(fieldNewsArtParentArt, art.ParentArt),
- NewField(fieldNewsArt1stChildArt, art.FirstChildArt),
- NewField(fieldNewsArtDataFlav, []byte("text/plain")),
- NewField(fieldNewsArtData, []byte(art.Data)),
+ NewField(FieldNewsArtTitle, []byte(art.Title)),
+ NewField(FieldNewsArtPoster, []byte(art.Poster)),
+ NewField(FieldNewsArtDate, art.Date),
+ NewField(FieldNewsArtPrevArt, art.PrevArt),
+ NewField(FieldNewsArtNextArt, art.NextArt),
+ NewField(FieldNewsArtParentArt, art.ParentArt),
+ NewField(FieldNewsArt1stChildArt, art.FirstChildArt),
+ NewField(FieldNewsArtDataFlav, []byte("text/plain")),
+ NewField(FieldNewsArtData, []byte(art.Data)),
))
return res, err
}
// Fields used in the reply:
// None
func HandleDelNewsItem(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
cats := cc.Server.ThreadedNews.Categories
delName := pathStrs[len(pathStrs)-1]
// 325 News path
// 326 News article ID
// 337 News article – recursive delete Delete child articles (1) or not (0)
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
- ID, err := byteToInt(t.GetField(fieldNewsArtID).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
+ ID, err := byteToInt(t.GetField(FieldNewsArtID).Data)
if err != nil {
return res, err
}
return res, err
}
- pathStrs := ReadNewsPath(t.GetField(fieldNewsPath).Data)
+ pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data)
cats := cc.Server.GetNewsCatByPath(pathStrs[:len(pathStrs)-1])
catName := pathStrs[len(pathStrs)-1]
cat := cats[catName]
- artID, err := byteToInt(t.GetField(fieldNewsArtID).Data)
+ artID, err := byteToInt(t.GetField(FieldNewsArtID).Data)
if err != nil {
return res, err
}
binary.BigEndian.PutUint32(bs, convertedArtID)
newArt := NewsArtData{
- Title: string(t.GetField(fieldNewsArtTitle).Data),
+ Title: string(t.GetField(FieldNewsArtTitle).Data),
Poster: string(cc.UserName),
Date: toHotlineTime(time.Now()),
PrevArt: []byte{0, 0, 0, 0},
ParentArt: bs,
FirstChildArt: []byte{0, 0, 0, 0},
DataFlav: []byte("text/plain"),
- Data: string(t.GetField(fieldNewsArtData).Data),
+ Data: string(t.GetField(FieldNewsArtData).Data),
}
var keys []int
return res, err
}
- res = append(res, cc.NewReply(t, NewField(fieldData, cc.Server.FlatNews)))
+ res = append(res, cc.NewReply(t, NewField(FieldData, cc.Server.FlatNews)))
return res, err
}
return res, err
}
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
- resumeData := t.GetField(fieldFileResumeData).Data
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
+ resumeData := t.GetField(FieldFileResumeData).Data
var dataOffset int64
var frd FileResumeData
if resumeData != nil {
- if err := frd.UnmarshalBinary(t.GetField(fieldFileResumeData).Data); err != nil {
+ if err := frd.UnmarshalBinary(t.GetField(FieldFileResumeData).Data); err != nil {
return res, err
}
// TODO: handle rsrc fork offset
// TODO: refactor to remove this
if resumeData != nil {
var frd FileResumeData
- if err := frd.UnmarshalBinary(t.GetField(fieldFileResumeData).Data); err != nil {
+ if err := frd.UnmarshalBinary(t.GetField(FieldFileResumeData).Data); err != nil {
return res, err
}
ft.fileResumeData = &frd
// Optional field for when a HL v1.5+ client requests file preview
// Used only for TEXT, JPEG, GIFF, BMP or PICT files
// The value will always be 2
- if t.GetField(fieldFileTransferOptions).Data != nil {
- ft.options = t.GetField(fieldFileTransferOptions).Data
+ if t.GetField(FieldFileTransferOptions).Data != nil {
+ ft.options = t.GetField(FieldFileTransferOptions).Data
xferSize = hlFile.ffo.FlatFileDataForkHeader.DataSize[:]
}
res = append(res, cc.NewReply(t,
- NewField(fieldRefNum, ft.refNum[:]),
- NewField(fieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
- NewField(fieldTransferSize, xferSize),
- NewField(fieldFileSize, hlFile.ffo.FlatFileDataForkHeader.DataSize[:]),
+ NewField(FieldRefNum, ft.refNum[:]),
+ NewField(FieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
+ NewField(FieldTransferSize, xferSize),
+ NewField(FieldFileSize, hlFile.ffo.FlatFileDataForkHeader.DataSize[:]),
))
return res, err
return res, err
}
- fullFilePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(fieldFilePath).Data, t.GetField(fieldFileName).Data)
+ fullFilePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(FieldFilePath).Data, t.GetField(FieldFileName).Data)
if err != nil {
return res, err
}
return res, err
}
- fileTransfer := cc.newFileTransfer(FolderDownload, t.GetField(fieldFileName).Data, t.GetField(fieldFilePath).Data, transferSize)
+ fileTransfer := cc.newFileTransfer(FolderDownload, t.GetField(FieldFileName).Data, t.GetField(FieldFilePath).Data, transferSize)
var fp FilePath
- _, err = fp.Write(t.GetField(fieldFilePath).Data)
+ _, err = fp.Write(t.GetField(FieldFilePath).Data)
if err != nil {
return res, err
}
res = append(res, cc.NewReply(t,
- NewField(fieldRefNum, fileTransfer.ReferenceNumber),
- NewField(fieldTransferSize, transferSize),
- NewField(fieldFolderItemCount, itemCount),
- NewField(fieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
+ NewField(FieldRefNum, fileTransfer.ReferenceNumber),
+ NewField(FieldTransferSize, transferSize),
+ NewField(FieldFolderItemCount, itemCount),
+ NewField(FieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
))
return res, err
}
// 204 File transfer options "Optional Currently set to 1" (TODO: ??)
func HandleUploadFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
var fp FilePath
- if t.GetField(fieldFilePath).Data != nil {
- if _, err = fp.Write(t.GetField(fieldFilePath).Data); err != nil {
+ if t.GetField(FieldFilePath).Data != nil {
+ if _, err = fp.Write(t.GetField(FieldFilePath).Data); err != nil {
return res, err
}
}
// Handle special cases for Upload and Drop Box folders
if !cc.Authorize(accessUploadAnywhere) {
if !fp.IsUploadDir() && !fp.IsDropbox() {
- res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload of the folder \"%v\" because you are only allowed to upload to the \"Uploads\" folder.", string(t.GetField(fieldFileName).Data))))
+ res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload of the folder \"%v\" because you are only allowed to upload to the \"Uploads\" folder.", string(t.GetField(FieldFileName).Data))))
return res, err
}
}
fileTransfer := cc.newFileTransfer(FolderUpload,
- t.GetField(fieldFileName).Data,
- t.GetField(fieldFilePath).Data,
- t.GetField(fieldTransferSize).Data,
+ t.GetField(FieldFileName).Data,
+ t.GetField(FieldFilePath).Data,
+ t.GetField(FieldTransferSize).Data,
)
- fileTransfer.FolderItemCount = t.GetField(fieldFolderItemCount).Data
+ fileTransfer.FolderItemCount = t.GetField(FieldFolderItemCount).Data
- res = append(res, cc.NewReply(t, NewField(fieldRefNum, fileTransfer.ReferenceNumber)))
+ res = append(res, cc.NewReply(t, NewField(FieldRefNum, fileTransfer.ReferenceNumber)))
return res, err
}
return res, err
}
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
- transferOptions := t.GetField(fieldFileTransferOptions).Data
- transferSize := t.GetField(fieldTransferSize).Data // not sent for resume
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
+ transferOptions := t.GetField(FieldFileTransferOptions).Data
+ transferSize := t.GetField(FieldTransferSize).Data // not sent for resume
var fp FilePath
if filePath != nil {
ft := cc.newFileTransfer(FileUpload, fileName, filePath, transferSize)
- replyT := cc.NewReply(t, NewField(fieldRefNum, ft.ReferenceNumber))
+ replyT := cc.NewReply(t, NewField(FieldRefNum, ft.ReferenceNumber))
// client has requested to resume a partially transferred file
if transferOptions != nil {
ft.TransferSize = offset
- replyT.Fields = append(replyT.Fields, NewField(fieldFileResumeData, b))
+ replyT.Fields = append(replyT.Fields, NewField(FieldFileResumeData, b))
}
res = append(res, replyT)
}
func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if len(t.GetField(fieldUserIconID).Data) == 4 {
- cc.Icon = t.GetField(fieldUserIconID).Data[2:]
+ if len(t.GetField(FieldUserIconID).Data) == 4 {
+ cc.Icon = t.GetField(FieldUserIconID).Data[2:]
} else {
- cc.Icon = t.GetField(fieldUserIconID).Data
+ cc.Icon = t.GetField(FieldUserIconID).Data
}
if cc.Authorize(accessAnyName) {
- cc.UserName = t.GetField(fieldUserName).Data
+ cc.UserName = t.GetField(FieldUserName).Data
}
// the options field is only passed by the client versions > 1.2.3.
- options := t.GetField(fieldOptions).Data
+ options := t.GetField(FieldOptions).Data
if options != nil {
optBitmap := big.NewInt(int64(binary.BigEndian.Uint16(options)))
flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(cc.Flags)))
// Check auto response
if optBitmap.Bit(autoResponse) == 1 {
- cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
+ cc.AutoReply = t.GetField(FieldAutomaticResponse).Data
} else {
cc.AutoReply = []byte{}
}
for _, c := range sortedClients(cc.Server.Clients) {
res = append(res, *NewTransaction(
- tranNotifyChangeUser,
+ TranNotifyChangeUser,
c.ID,
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserIconID, cc.Icon),
- NewField(fieldUserFlags, cc.Flags),
- NewField(fieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserIconID, cc.Icon),
+ NewField(FieldUserFlags, cc.Flags),
+ NewField(FieldUserName, cc.UserName),
))
}
func HandleGetFileNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
fullPath, err := readPath(
cc.Server.Config.FileRoot,
- t.GetField(fieldFilePath).Data,
+ t.GetField(FieldFilePath).Data,
nil,
)
if err != nil {
}
var fp FilePath
- if t.GetField(fieldFilePath).Data != nil {
- if _, err = fp.Write(t.GetField(fieldFilePath).Data); err != nil {
+ if t.GetField(FieldFilePath).Data != nil {
+ if _, err = fp.Write(t.GetField(FieldFilePath).Data); err != nil {
return res, err
}
}
// =================================
// Hotline private chat flow
// =================================
-// 1. ClientA sends tranInviteNewChat to server with user ID to invite
+// 1. ClientA sends TranInviteNewChat to server with user ID to invite
// 2. Server creates new ChatID
-// 3. Server sends tranInviteToChat to invitee
+// 3. Server sends TranInviteToChat to invitee
// 4. Server replies to ClientA with new Chat ID
//
// A dialog box pops up in the invitee client with options to accept or decline the invitation.
// If Accepted is clicked:
-// 1. ClientB sends tranJoinChat with fieldChatID
+// 1. ClientB sends TranJoinChat with FieldChatID
// HandleInviteNewChat invites users to new private chat
func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
}
// Client to Invite
- targetID := t.GetField(fieldUserID).Data
+ targetID := t.GetField(FieldUserID).Data
newChatID := cc.Server.NewPrivateChat(cc)
// Check if target user has "Refuse private chat" flag
if flagBitmap.Bit(userFLagRefusePChat) == 1 {
res = append(res,
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
cc.ID,
- NewField(fieldData, []byte(string(targetClient.UserName)+" does not accept private chats.")),
- NewField(fieldUserName, targetClient.UserName),
- NewField(fieldUserID, *targetClient.ID),
- NewField(fieldOptions, []byte{0, 2}),
+ NewField(FieldData, []byte(string(targetClient.UserName)+" does not accept private chats.")),
+ NewField(FieldUserName, targetClient.UserName),
+ NewField(FieldUserID, *targetClient.ID),
+ NewField(FieldOptions, []byte{0, 2}),
),
)
} else {
res = append(res,
*NewTransaction(
- tranInviteToChat,
+ TranInviteToChat,
&targetID,
- NewField(fieldChatID, newChatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
+ NewField(FieldChatID, newChatID),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
),
)
}
res = append(res,
cc.NewReply(t,
- NewField(fieldChatID, newChatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserIconID, cc.Icon),
- NewField(fieldUserFlags, cc.Flags),
+ NewField(FieldChatID, newChatID),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserIconID, cc.Icon),
+ NewField(FieldUserFlags, cc.Flags),
),
)
}
// Client to Invite
- targetID := t.GetField(fieldUserID).Data
- chatID := t.GetField(fieldChatID).Data
+ targetID := t.GetField(FieldUserID).Data
+ chatID := t.GetField(FieldChatID).Data
res = append(res,
*NewTransaction(
- tranInviteToChat,
+ TranInviteToChat,
&targetID,
- NewField(fieldChatID, chatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
+ NewField(FieldChatID, chatID),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
),
)
res = append(res,
cc.NewReply(
t,
- NewField(fieldChatID, chatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserIconID, cc.Icon),
- NewField(fieldUserFlags, cc.Flags),
+ NewField(FieldChatID, chatID),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserIconID, cc.Icon),
+ NewField(FieldUserFlags, cc.Flags),
),
)
}
func HandleRejectChatInvite(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- chatID := t.GetField(fieldChatID).Data
+ chatID := t.GetField(FieldChatID).Data
chatInt := binary.BigEndian.Uint32(chatID)
privChat := cc.Server.PrivateChats[chatInt]
for _, c := range sortedClients(privChat.ClientConn) {
res = append(res,
*NewTransaction(
- tranChatMsg,
+ TranChatMsg,
c.ID,
- NewField(fieldChatID, chatID),
- NewField(fieldData, resMsg),
+ NewField(FieldChatID, chatID),
+ NewField(FieldData, resMsg),
),
)
}
// * 300 User name with info (Optional)
// * 300 (more user names with info)
func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- chatID := t.GetField(fieldChatID).Data
+ chatID := t.GetField(FieldChatID).Data
chatInt := binary.BigEndian.Uint32(chatID)
privChat := cc.Server.PrivateChats[chatInt]
- // Send tranNotifyChatChangeUser to current members of the chat to inform of new user
+ // Send TranNotifyChatChangeUser to current members of the chat to inform of new user
for _, c := range sortedClients(privChat.ClientConn) {
res = append(res,
*NewTransaction(
- tranNotifyChatChangeUser,
+ TranNotifyChatChangeUser,
c.ID,
- NewField(fieldChatID, chatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserIconID, cc.Icon),
- NewField(fieldUserFlags, cc.Flags),
+ NewField(FieldChatID, chatID),
+ NewField(FieldUserName, cc.UserName),
+ NewField(FieldUserID, *cc.ID),
+ NewField(FieldUserIconID, cc.Icon),
+ NewField(FieldUserFlags, cc.Flags),
),
)
}
privChat.ClientConn[cc.uint16ID()] = cc
- replyFields := []Field{NewField(fieldChatSubject, []byte(privChat.Subject))}
+ replyFields := []Field{NewField(FieldChatSubject, []byte(privChat.Subject))}
for _, c := range sortedClients(privChat.ClientConn) {
user := User{
ID: *c.ID,
Name: string(c.UserName),
}
- replyFields = append(replyFields, NewField(fieldUsernameWithInfo, user.Payload()))
+ replyFields = append(replyFields, NewField(FieldUsernameWithInfo, user.Payload()))
}
res = append(res, cc.NewReply(t, replyFields...))
// HandleLeaveChat is sent from a v1.8+ Hotline client when the user exits a private chat
// Fields used in the request:
-// - 114 fieldChatID
+// - 114 FieldChatID
//
// Reply is not expected.
func HandleLeaveChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- chatID := t.GetField(fieldChatID).Data
+ chatID := t.GetField(FieldChatID).Data
chatInt := binary.BigEndian.Uint32(chatID)
privChat, ok := cc.Server.PrivateChats[chatInt]
for _, c := range sortedClients(privChat.ClientConn) {
res = append(res,
*NewTransaction(
- tranNotifyChatDeleteUser,
+ TranNotifyChatDeleteUser,
c.ID,
- NewField(fieldChatID, chatID),
- NewField(fieldUserID, *cc.ID),
+ NewField(FieldChatID, chatID),
+ NewField(FieldUserID, *cc.ID),
),
)
}
// * 115 Chat subject
// Reply is not expected.
func HandleSetChatSubject(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- chatID := t.GetField(fieldChatID).Data
+ chatID := t.GetField(FieldChatID).Data
chatInt := binary.BigEndian.Uint32(chatID)
privChat := cc.Server.PrivateChats[chatInt]
- privChat.Subject = string(t.GetField(fieldChatSubject).Data)
+ privChat.Subject = string(t.GetField(FieldChatSubject).Data)
for _, c := range sortedClients(privChat.ClientConn) {
res = append(res,
*NewTransaction(
- tranNotifyChatSubject,
+ TranNotifyChatSubject,
c.ID,
- NewField(fieldChatID, chatID),
- NewField(fieldChatSubject, t.GetField(fieldChatSubject).Data),
+ NewField(FieldChatID, chatID),
+ NewField(FieldChatSubject, t.GetField(FieldChatSubject).Data),
),
)
}
res = append(res, cc.NewErrReply(t, "You are not allowed to make aliases."))
return res, err
}
- fileName := t.GetField(fieldFileName).Data
- filePath := t.GetField(fieldFilePath).Data
- fileNewPath := t.GetField(fieldFileNewPath).Data
+ fileName := t.GetField(FieldFileName).Data
+ filePath := t.GetField(FieldFilePath).Data
+ fileNewPath := t.GetField(FieldFileNewPath).Data
fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName)
if err != nil {
// 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
+// 107 FieldRefNum Used later for transfer
+// 108 FieldTransferSize Size of data to be downloaded
func HandleDownloadBanner(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
fi, err := cc.Server.FS.Stat(filepath.Join(cc.Server.ConfigDir, cc.Server.Config.BannerFile))
if err != nil {
binary.BigEndian.PutUint32(ft.TransferSize, uint32(fi.Size()))
res = append(res, cc.NewReply(t,
- NewField(fieldRefNum, ft.refNum[:]),
- NewField(fieldTransferSize, ft.TransferSize),
+ NewField(FieldRefNum, ft.refNum[:]),
+ NewField(FieldTransferSize, ft.TransferSize),
))
return res, err
ID: []byte{0, 0, 0, 1},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldChatSubject, []byte("Test Subject")),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldChatSubject, []byte("Test Subject")),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldChatSubject, []byte("Test Subject")),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldChatSubject, []byte("Test Subject")),
},
},
{
ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldChatSubject, []byte("Test Subject")),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldChatSubject, []byte("Test Subject")),
},
},
},
},
},
},
- t: NewTransaction(tranDeleteUser, nil, NewField(fieldChatID, []byte{0, 0, 0, 1})),
+ t: NewTransaction(TranDeleteUser, nil, NewField(FieldChatID, []byte{0, 0, 0, 1})),
},
want: []Transaction{
{
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldUserID, []byte{0, 2}),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldUserID, []byte{0, 2}),
},
},
},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
NewField(
- fieldUsernameWithInfo,
+ FieldUsernameWithInfo,
[]byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
),
NewField(
- fieldUsernameWithInfo,
+ FieldUsernameWithInfo,
[]byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
),
},
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("hai")),
+ NewField(FieldData, []byte("hai")),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
{
ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
},
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("hai")),
- NewField(fieldChatID, []byte{0, 0, 0, 0}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldChatID, []byte{0, 0, 0, 0}),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
{
ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
},
},
},
t: NewTransaction(
- tranChatSend, &[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
+ TranChatSend, &[]byte{0, 1},
+ NewField(FieldData, []byte("hai")),
),
},
want: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to participate in chat.")),
+ NewField(FieldError, []byte("You are not allowed to participate in chat.")),
},
},
},
wantErr: false,
},
{
- name: "sends chat msg as emote if fieldChatOptions is set to 1",
+ name: "sends chat msg as emote if FieldChatOptions is set to 1",
args: args{
cc: &ClientConn{
Account: &Account{
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("performed action")),
- NewField(fieldChatOptions, []byte{0x00, 0x01}),
+ NewField(FieldData, []byte("performed action")),
+ NewField(FieldChatOptions, []byte{0x00, 0x01}),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("\r*** Testy McTest performed action")),
+ NewField(FieldData, []byte("\r*** Testy McTest performed action")),
},
},
{
ID: []byte{0xf0, 0xc5, 0x34, 0x1e},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("\r*** Testy McTest performed action")),
+ NewField(FieldData, []byte("\r*** Testy McTest performed action")),
},
},
},
wantErr: false,
},
{
- name: "does not send chat msg as emote if fieldChatOptions is set to 0",
+ name: "does not send chat msg as emote if FieldChatOptions is set to 0",
args: args{
cc: &ClientConn{
Account: &Account{
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("hello")),
- NewField(fieldChatOptions, []byte{0x00, 0x00}),
+ NewField(FieldData, []byte("hello")),
+ NewField(FieldChatOptions, []byte{0x00, 0x00}),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("\r Testy McTest: hello")),
+ NewField(FieldData, []byte("\r Testy McTest: hello")),
},
},
{
ID: []byte{0xf0, 0xc5, 0x34, 0x1e},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("\r Testy McTest: hello")),
+ NewField(FieldData, []byte("\r Testy McTest: hello")),
},
},
},
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("hai")),
+ NewField(FieldData, []byte("hai")),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
},
},
t: &Transaction{
Fields: []Field{
- NewField(fieldData, []byte("hai")),
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0, 0, 0, 1}),
- NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ NewField(FieldChatID, []byte{0, 0, 0, 1}),
+ NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
},
},
},
},
},
t: NewTransaction(
- tranGetFileInfo, nil,
- NewField(fieldFileName, []byte("testfile.txt")),
- NewField(fieldFilePath, []byte{0x00, 0x00}),
+ TranGetFileInfo, nil,
+ NewField(FieldFileName, []byte("testfile.txt")),
+ NewField(FieldFilePath, []byte{0x00, 0x00}),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldFileName, []byte("testfile.txt")),
- NewField(fieldFileTypeString, []byte("Text File")),
- NewField(fieldFileCreatorString, []byte("ttxt")),
- NewField(fieldFileComment, []byte{}),
- NewField(fieldFileType, []byte("TEXT")),
- NewField(fieldFileCreateDate, make([]byte, 8)),
- NewField(fieldFileModifyDate, make([]byte, 8)),
- NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
+ NewField(FieldFileName, []byte("testfile.txt")),
+ NewField(FieldFileTypeString, []byte("Text File")),
+ NewField(FieldFileCreatorString, []byte("ttxt")),
+ NewField(FieldFileComment, []byte{}),
+ NewField(FieldFileType, []byte("TEXT")),
+ NewField(FieldFileCreateDate, make([]byte, 8)),
+ NewField(FieldFileModifyDate, make([]byte, 8)),
+ NewField(FieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
},
},
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to create folders.")),
+ NewField(FieldError, []byte("You are not allowed to create folders.")),
},
},
},
},
},
t: NewTransaction(
- tranNewFolder, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFolder")),
- NewField(fieldFilePath, []byte{
+ TranNewFolder, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFolder")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
},
},
t: NewTransaction(
- tranNewFolder, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFolder")),
+ TranNewFolder, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFolder")),
),
},
wantRes: []Transaction{
},
},
t: NewTransaction(
- tranNewFolder, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFolder")),
- NewField(fieldFilePath, []byte{
+ TranNewFolder, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFolder")),
+ NewField(FieldFilePath, []byte{
0x00,
}),
),
wantErr: true,
},
{
- name: "fieldFileName does not allow directory traversal",
+ name: "FieldFileName does not allow directory traversal",
args: args{
cc: &ClientConn{
Account: &Account{
},
},
t: NewTransaction(
- tranNewFolder, &[]byte{0, 1},
- NewField(fieldFileName, []byte("../../testFolder")),
+ TranNewFolder, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("../../testFolder")),
),
},
wantRes: []Transaction{
}, wantErr: false,
},
{
- name: "fieldFilePath does not allow directory traversal",
+ name: "FieldFilePath does not allow directory traversal",
args: args{
cc: &ClientConn{
Account: &Account{
},
},
t: NewTransaction(
- tranNewFolder, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFolder")),
- NewField(fieldFilePath, []byte{
+ TranNewFolder, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFolder")),
+ NewField(FieldFilePath, []byte{
0x00, 0x02,
0x00, 0x00,
0x03,
},
},
t: NewTransaction(
- tranUploadFile, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFile")),
- NewField(fieldFilePath, []byte{
+ TranUploadFile, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFile")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
+ NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
},
},
},
},
},
t: NewTransaction(
- tranUploadFile, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFile")),
- NewField(fieldFilePath, []byte{
+ TranUploadFile, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFile")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
+ NewField(FieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
},
},
},
},
},
t: NewTransaction(
- tranMakeFileAlias, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFile")),
- NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
- NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
+ TranMakeFileAlias, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFile")),
+ NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
+ NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
),
},
wantRes: []Transaction{
},
},
t: NewTransaction(
- tranMakeFileAlias, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFile")),
- NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
- NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
+ TranMakeFileAlias, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFile")),
+ NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
+ NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("Error creating alias")),
+ NewField(FieldError, []byte("Error creating alias")),
},
},
},
},
},
t: NewTransaction(
- tranMakeFileAlias, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFile")),
- NewField(fieldFilePath, []byte{
+ TranMakeFileAlias, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFile")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
0x2e, 0x2e, 0x2e,
}),
- NewField(fieldFileNewPath, []byte{
+ NewField(FieldFileNewPath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to make aliases.")),
+ NewField(FieldError, []byte("You are not allowed to make aliases.")),
},
},
},
},
},
t: NewTransaction(
- tranGetUser, &[]byte{0, 1},
- NewField(fieldUserLogin, []byte("guest")),
+ TranGetUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, []byte("guest")),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldUserName, []byte("Guest")),
- NewField(fieldUserLogin, negateString([]byte("guest"))),
- NewField(fieldUserPassword, []byte("password")),
- NewField(fieldUserAccess, []byte{0, 0, 0, 0, 0, 0, 0, 0}),
+ NewField(FieldUserName, []byte("Guest")),
+ NewField(FieldUserLogin, negateString([]byte("guest"))),
+ NewField(FieldUserPassword, []byte("password")),
+ NewField(FieldUserAccess, []byte{0, 0, 0, 0, 0, 0, 0, 0}),
},
},
},
},
},
t: NewTransaction(
- tranGetUser, &[]byte{0, 1},
- NewField(fieldUserLogin, []byte("nonExistentUser")),
+ TranGetUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, []byte("nonExistentUser")),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to view accounts.")),
+ NewField(FieldError, []byte("You are not allowed to view accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranGetUser, &[]byte{0, 1},
- NewField(fieldUserLogin, []byte("nonExistentUser")),
+ TranGetUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, []byte("nonExistentUser")),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("Account does not exist.")),
+ NewField(FieldError, []byte("Account does not exist.")),
},
},
},
},
},
t: NewTransaction(
- tranDeleteUser, &[]byte{0, 1},
- NewField(fieldUserLogin, negateString([]byte("testuser"))),
+ TranDeleteUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, negateString([]byte("testuser"))),
),
},
wantRes: []Transaction{
},
},
t: NewTransaction(
- tranDeleteUser, &[]byte{0, 1},
- NewField(fieldUserLogin, negateString([]byte("testuser"))),
+ TranDeleteUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, negateString([]byte("testuser"))),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete accounts.")),
+ NewField(FieldError, []byte("You are not allowed to delete accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranGetMsgs, &[]byte{0, 1},
+ TranGetMsgs, &[]byte{0, 1},
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("TEST")),
+ NewField(FieldData, []byte("TEST")),
},
},
},
},
},
t: NewTransaction(
- tranGetMsgs, &[]byte{0, 1},
+ TranGetMsgs, &[]byte{0, 1},
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to read news.")),
+ NewField(FieldError, []byte("You are not allowed to read news.")),
},
},
},
},
},
t: NewTransaction(
- tranNewUser, &[]byte{0, 1},
+ TranNewUser, &[]byte{0, 1},
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to create new accounts.")),
+ NewField(FieldError, []byte("You are not allowed to create new accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranNewUser, &[]byte{0, 1},
- NewField(fieldUserLogin, []byte("userB")),
+ TranNewUser, &[]byte{0, 1},
+ NewField(FieldUserLogin, []byte("userB")),
NewField(
- fieldUserAccess,
+ FieldUserAccess,
func() []byte {
var bits accessBitmap
bits.Set(accessDisconUser)
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("Cannot create account with more access than yourself.")),
+ NewField(FieldError, []byte("Cannot create account with more access than yourself.")),
},
},
},
},
},
t: NewTransaction(
- tranNewUser, &[]byte{0, 1},
+ TranNewUser, &[]byte{0, 1},
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to view accounts.")),
+ NewField(FieldError, []byte("You are not allowed to view accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranGetClientInfoText, &[]byte{0, 1},
- NewField(fieldUserID, []byte{0, 1}),
+ TranGetClientInfoText, &[]byte{0, 1},
+ NewField(FieldUserID, []byte{0, 1}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte{
+ NewField(FieldData, []byte{
0x00, 0x04, 0x00, 0x66, 0x00, 0x05, 0x67, 0x75, 0x65, 0x73, 0x74, 0x00, 0x69, 0x00, 0x05, 0x98,
0x8a, 0x9a, 0x8c, 0x8b, 0x00, 0x6e, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x6a, 0x00, 0x01, 0x78,
},
Server: &Server{},
},
- t: NewTransaction(tranDownloadFile, &[]byte{0, 1}),
+ t: NewTransaction(TranDownloadFile, &[]byte{0, 1}),
},
wantRes: []Transaction{
{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to download files.")),
+ NewField(FieldError, []byte("You are not allowed to download files.")),
},
},
},
t: NewTransaction(
accessDownloadFile,
&[]byte{0, 1},
- NewField(fieldFileName, []byte("testfile.txt")),
- NewField(fieldFilePath, []byte{0x0, 0x00}),
+ NewField(FieldFileName, []byte("testfile.txt")),
+ NewField(FieldFilePath, []byte{0x0, 0x00}),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
- NewField(fieldWaitingCount, []byte{0x00, 0x00}),
- NewField(fieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}),
- NewField(fieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}),
+ NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(FieldWaitingCount, []byte{0x00, 0x00}),
+ NewField(FieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}),
+ NewField(FieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}),
},
},
},
t: NewTransaction(
accessDownloadFile,
&[]byte{0, 1},
- NewField(fieldFileName, []byte("testfile-1k")),
- NewField(fieldFilePath, []byte{0x00, 0x00}),
+ NewField(FieldFileName, []byte("testfile-1k")),
+ NewField(FieldFilePath, []byte{0x00, 0x00}),
NewField(
- fieldFileResumeData,
+ FieldFileResumeData,
func() []byte {
frd := FileResumeData{
Format: [4]byte{},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
- NewField(fieldWaitingCount, []byte{0x00, 0x00}),
- NewField(fieldTransferSize, []byte{0x00, 0x00, 0x03, 0x8d}),
- NewField(fieldFileSize, []byte{0x00, 0x00, 0x03, 0x00}),
+ NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(FieldWaitingCount, []byte{0x00, 0x00}),
+ NewField(FieldTransferSize, []byte{0x00, 0x00, 0x03, 0x8d}),
+ NewField(FieldFileSize, []byte{0x00, 0x00, 0x03, 0x00}),
},
},
},
},
},
t: NewTransaction(
- tranUpdateUser,
+ TranUpdateUser,
&[]byte{0, 0},
- NewField(fieldData, []byte{
+ NewField(FieldData, []byte{
0x00, 0x04, // field count
- 0x00, 0x69, // fieldUserLogin = 105
+ 0x00, 0x69, // FieldUserLogin = 105
0x00, 0x03,
0x9d, 0x9d, 0x9d,
- 0x00, 0x6a, // fieldUserPassword = 106
+ 0x00, 0x6a, // FieldUserPassword = 106
0x00, 0x03,
0x9c, 0x9c, 0x9c,
- 0x00, 0x66, // fieldUserName = 102
+ 0x00, 0x66, // FieldUserName = 102
0x00, 0x03,
0x61, 0x61, 0x61,
- 0x00, 0x6e, // fieldUserAccess = 110
+ 0x00, 0x6e, // FieldUserAccess = 110
0x00, 0x08,
0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
}),
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to create new accounts.")),
+ NewField(FieldError, []byte("You are not allowed to create new accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranUpdateUser,
+ TranUpdateUser,
&[]byte{0, 0},
- NewField(fieldData, []byte{
+ NewField(FieldData, []byte{
0x00, 0x04, // field count
- 0x00, 0x69, // fieldUserLogin = 105
+ 0x00, 0x69, // FieldUserLogin = 105
0x00, 0x03,
0x9d, 0x9d, 0x9d,
- 0x00, 0x6a, // fieldUserPassword = 106
+ 0x00, 0x6a, // FieldUserPassword = 106
0x00, 0x03,
0x9c, 0x9c, 0x9c,
- 0x00, 0x66, // fieldUserName = 102
+ 0x00, 0x66, // FieldUserName = 102
0x00, 0x03,
0x61, 0x61, 0x61,
- 0x00, 0x6e, // fieldUserAccess = 110
+ 0x00, 0x6e, // FieldUserAccess = 110
0x00, 0x08,
0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
}),
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to modify accounts.")),
+ NewField(FieldError, []byte("You are not allowed to modify accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranUpdateUser,
+ TranUpdateUser,
&[]byte{0, 0},
- NewField(fieldData, []byte{
+ NewField(FieldData, []byte{
0x00, 0x01,
0x00, 0x65,
0x00, 0x03,
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete accounts.")),
+ NewField(FieldError, []byte("You are not allowed to delete accounts.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsArt,
+ TranDelNewsArt,
&[]byte{0, 0},
),
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete news articles.")),
+ NewField(FieldError, []byte("You are not allowed to delete news articles.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsArt,
+ TranDelNewsArt,
&[]byte{0, 0},
),
},
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to disconnect users.")),
+ NewField(FieldError, []byte("You are not allowed to disconnect users.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsArt,
+ TranDelNewsArt,
&[]byte{0, 0},
- NewField(fieldUserID, []byte{0, 1}),
+ NewField(FieldUserID, []byte{0, 1}),
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("unnamed is not allowed to be disconnected.")),
+ NewField(FieldError, []byte("unnamed is not allowed to be disconnected.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsArt,
+ TranDelNewsArt,
&[]byte{0, 0},
),
},
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to send private messages.")),
+ NewField(FieldError, []byte("You are not allowed to send private messages.")),
},
},
},
},
},
t: NewTransaction(
- tranSendInstantMsg,
+ TranSendInstantMsg,
&[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
- NewField(fieldUserID, []byte{0, 2}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldUserID, []byte{0, 2}),
),
},
wantRes: []Transaction{
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 2},
- NewField(fieldData, []byte("hai")),
- NewField(fieldUserName, []byte("User1")),
- NewField(fieldUserID, []byte{0, 1}),
- NewField(fieldOptions, []byte{0, 1}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldUserName, []byte("User1")),
+ NewField(FieldUserID, []byte{0, 1}),
+ NewField(FieldOptions, []byte{0, 1}),
),
{
clientID: &[]byte{0, 1},
},
},
t: NewTransaction(
- tranSendInstantMsg,
+ TranSendInstantMsg,
&[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
- NewField(fieldUserID, []byte{0, 2}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldUserID, []byte{0, 2}),
),
},
wantRes: []Transaction{
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 2},
- NewField(fieldData, []byte("hai")),
- NewField(fieldUserName, []byte("User1")),
- NewField(fieldUserID, []byte{0, 1}),
- NewField(fieldOptions, []byte{0, 1}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldUserName, []byte("User1")),
+ NewField(FieldUserID, []byte{0, 1}),
+ NewField(FieldOptions, []byte{0, 1}),
),
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 1},
- NewField(fieldData, []byte("autohai")),
- NewField(fieldUserName, []byte("User2")),
- NewField(fieldUserID, []byte{0, 2}),
- NewField(fieldOptions, []byte{0, 1}),
+ NewField(FieldData, []byte("autohai")),
+ NewField(FieldUserName, []byte("User2")),
+ NewField(FieldUserID, []byte{0, 2}),
+ NewField(FieldOptions, []byte{0, 1}),
),
{
clientID: &[]byte{0, 1},
},
},
t: NewTransaction(
- tranSendInstantMsg,
+ TranSendInstantMsg,
&[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
- NewField(fieldUserID, []byte{0, 2}),
+ NewField(FieldData, []byte("hai")),
+ NewField(FieldUserID, []byte{0, 2}),
),
},
wantRes: []Transaction{
*NewTransaction(
- tranServerMsg,
+ TranServerMsg,
&[]byte{0, 1},
- NewField(fieldData, []byte("User2 does not accept private messages.")),
- NewField(fieldUserName, []byte("User2")),
- NewField(fieldUserID, []byte{0, 2}),
- NewField(fieldOptions, []byte{0, 2}),
+ NewField(FieldData, []byte("User2 does not accept private messages.")),
+ NewField(FieldUserName, []byte("User2")),
+ NewField(FieldUserID, []byte{0, 2}),
+ NewField(FieldOptions, []byte{0, 2}),
),
{
clientID: &[]byte{0, 1},
},
},
t: NewTransaction(
- tranDeleteFile, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testfile")),
- NewField(fieldFilePath, []byte{
+ TranDeleteFile, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testfile")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete files.")),
+ NewField(FieldError, []byte("You are not allowed to delete files.")),
},
},
},
},
},
t: NewTransaction(
- tranDeleteFile, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testfile")),
- NewField(fieldFilePath, []byte{
+ TranDeleteFile, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testfile")),
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x03,
wantErr assert.ErrorAssertionFunc
}{
{
- name: "when fieldFilePath is a drop box, but user does not have accessViewDropBoxes ",
+ name: "when FieldFilePath is a drop box, but user does not have accessViewDropBoxes ",
args: args{
cc: &ClientConn{
Account: &Account{
},
},
t: NewTransaction(
- tranGetFileNameList, &[]byte{0, 1},
- NewField(fieldFilePath, []byte{
+ TranGetFileNameList, &[]byte{0, 1},
+ NewField(FieldFilePath, []byte{
0x00, 0x01,
0x00, 0x00,
0x08,
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to view drop boxes.")),
+ NewField(FieldError, []byte("You are not allowed to view drop boxes.")),
},
},
},
},
},
t: NewTransaction(
- tranGetFileNameList, &[]byte{0, 1},
- NewField(fieldFilePath, []byte{
+ TranGetFileNameList, &[]byte{0, 1},
+ NewField(FieldFilePath, []byte{
0x00, 0x00,
0x00, 0x00,
}),
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
NewField(
- fieldFileNameWithInfo,
+ FieldFileNameWithInfo,
func() []byte {
fnwi := FileNameWithInfo{
fileNameWithInfoHeader: fileNameWithInfoHeader{
},
},
t: NewTransaction(
- tranGetClientInfoText, &[]byte{0, 1},
- NewField(fieldUserID, []byte{0, 1}),
+ TranGetClientInfoText, &[]byte{0, 1},
+ NewField(FieldUserID, []byte{0, 1}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to get client info.")),
+ NewField(FieldError, []byte("You are not allowed to get client info.")),
},
},
},
},
},
t: NewTransaction(
- tranGetClientInfoText, &[]byte{0, 1},
- NewField(fieldUserID, []byte{0, 1}),
+ TranGetClientInfoText, &[]byte{0, 1},
+ NewField(FieldUserID, []byte{0, 1}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte(
+ NewField(FieldData, []byte(
strings.Replace(`Nickname: Testy McTest
Name: test
Account: test
`, "\n", "\r", -1)),
),
- NewField(fieldUserName, []byte("Testy McTest")),
+ NewField(FieldUserName, []byte("Testy McTest")),
},
},
},
},
},
t: NewTransaction(
- tranAgreed, nil,
- NewField(fieldUserName, []byte("username")),
- NewField(fieldUserIconID, []byte{0, 1}),
- NewField(fieldOptions, []byte{0, 0}),
+ TranAgreed, nil,
+ NewField(FieldUserName, []byte("username")),
+ NewField(FieldUserIconID, []byte{0, 1}),
+ NewField(FieldOptions, []byte{0, 0}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldBannerType, []byte("JPEG")),
+ NewField(FieldBannerType, []byte("JPEG")),
},
},
{
},
},
t: NewTransaction(
- tranSetClientUserInfo, nil,
- NewField(fieldUserIconID, []byte{0, 1}),
- NewField(fieldUserName, []byte("NOPE")),
+ TranSetClientUserInfo, nil,
+ NewField(FieldUserIconID, []byte{0, 1}),
+ NewField(FieldUserName, []byte("NOPE")),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldUserID, []byte{0, 1}),
- NewField(fieldUserIconID, []byte{0, 1}),
- NewField(fieldUserFlags, []byte{0, 1}),
- NewField(fieldUserName, []byte("Guest"))},
+ NewField(FieldUserID, []byte{0, 1}),
+ NewField(FieldUserIconID, []byte{0, 1}),
+ NewField(FieldUserFlags, []byte{0, 1}),
+ NewField(FieldUserName, []byte("Guest"))},
},
},
wantErr: assert.NoError,
},
},
t: NewTransaction(
- tranDelNewsItem, nil,
- NewField(fieldNewsPath,
+ TranDelNewsItem, nil,
+ NewField(FieldNewsPath,
[]byte{
0, 1,
0, 0,
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete news categories.")),
+ NewField(FieldError, []byte("You are not allowed to delete news categories.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsItem, nil,
- NewField(fieldNewsPath,
+ TranDelNewsItem, nil,
+ NewField(FieldNewsPath,
[]byte{
0, 1,
0, 0,
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to delete news folders.")),
+ NewField(FieldError, []byte("You are not allowed to delete news folders.")),
},
},
},
},
},
t: NewTransaction(
- tranDelNewsItem, nil,
- NewField(fieldNewsPath,
+ TranDelNewsItem, nil,
+ NewField(FieldNewsPath,
[]byte{
0, 1,
0, 0,
}(),
},
},
- t: NewTransaction(tranDownloadBanner, nil),
+ t: NewTransaction(TranDownloadBanner, nil),
},
wantRes: []Transaction{
{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldRefNum, []byte{1, 2, 3, 4}),
- NewField(fieldTransferSize, []byte{0, 0, 0, 0x64}),
+ NewField(FieldRefNum, []byte{1, 2, 3, 4}),
+ NewField(FieldTransferSize, []byte{0, 0, 0, 0x64}),
},
},
},
},
},
t: NewTransaction(
- tranOldPostNews, &[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
+ TranOldPostNews, &[]byte{0, 1},
+ NewField(FieldData, []byte("hai")),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to post news.")),
+ NewField(FieldError, []byte("You are not allowed to post news.")),
},
},
},
},
},
t: NewTransaction(
- tranOldPostNews, &[]byte{0, 1},
- NewField(fieldData, []byte("hai")),
+ TranOldPostNews, &[]byte{0, 1},
+ NewField(FieldData, []byte("hai")),
),
},
wantRes: []Transaction{
}(),
},
},
- t: NewTransaction(tranInviteNewChat, &[]byte{0, 1}),
+ t: NewTransaction(TranInviteNewChat, &[]byte{0, 1}),
},
wantRes: []Transaction{
{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to request private chat.")),
+ NewField(FieldError, []byte("You are not allowed to request private chat.")),
},
},
},
},
},
t: NewTransaction(
- tranInviteNewChat, &[]byte{0, 1},
- NewField(fieldUserID, []byte{0, 2}),
+ TranInviteNewChat, &[]byte{0, 1},
+ NewField(FieldUserID, []byte{0, 2}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
- NewField(fieldUserName, []byte("UserA")),
- NewField(fieldUserID, []byte{0, 1}),
+ NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(FieldUserName, []byte("UserA")),
+ NewField(FieldUserID, []byte{0, 1}),
},
},
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
- NewField(fieldUserName, []byte("UserA")),
- NewField(fieldUserID, []byte{0, 1}),
- NewField(fieldUserIconID, []byte{0, 1}),
- NewField(fieldUserFlags, []byte{0, 0}),
+ NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(FieldUserName, []byte("UserA")),
+ NewField(FieldUserID, []byte{0, 1}),
+ NewField(FieldUserIconID, []byte{0, 1}),
+ NewField(FieldUserFlags, []byte{0, 0}),
},
},
},
},
},
t: NewTransaction(
- tranInviteNewChat, &[]byte{0, 1},
- NewField(fieldUserID, []byte{0, 2}),
+ TranInviteNewChat, &[]byte{0, 1},
+ NewField(FieldUserID, []byte{0, 2}),
),
},
wantRes: []Transaction{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldData, []byte("UserB does not accept private chats.")),
- NewField(fieldUserName, []byte("UserB")),
- NewField(fieldUserID, []byte{0, 2}),
- NewField(fieldOptions, []byte{0, 2}),
+ NewField(FieldData, []byte("UserB does not accept private chats.")),
+ NewField(FieldUserName, []byte("UserB")),
+ NewField(FieldUserID, []byte{0, 2}),
+ NewField(FieldOptions, []byte{0, 2}),
},
},
{
ID: []byte{0, 0, 0, 0},
ErrorCode: []byte{0, 0, 0, 0},
Fields: []Field{
- NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
- NewField(fieldUserName, []byte("UserA")),
- NewField(fieldUserID, []byte{0, 1}),
- NewField(fieldUserIconID, []byte{0, 1}),
- NewField(fieldUserFlags, []byte{0, 0}),
+ NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(FieldUserName, []byte("UserA")),
+ NewField(FieldUserID, []byte{0, 1}),
+ NewField(FieldUserIconID, []byte{0, 1}),
+ NewField(FieldUserFlags, []byte{0, 0}),
},
},
},
},
},
t: NewTransaction(
- tranGetNewsArtData, &[]byte{0, 1},
+ TranGetNewsArtData, &[]byte{0, 1},
),
},
wantRes: []Transaction{
ID: []byte{0x9a, 0xcb, 0x04, 0x42},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to read news.")),
+ NewField(FieldError, []byte("You are not allowed to read news.")),
},
},
},
},
},
t: NewTransaction(
- tranGetNewsArtNameList, &[]byte{0, 1},
+ TranGetNewsArtNameList, &[]byte{0, 1},
),
},
wantRes: []Transaction{
Type: []byte{0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to read news.")),
+ NewField(FieldError, []byte("You are not allowed to read news.")),
},
},
},
},
},
t: NewTransaction(
- tranGetNewsArtNameList, &[]byte{0, 1},
+ TranGetNewsArtNameList, &[]byte{0, 1},
),
},
wantRes: []Transaction{
Type: []byte{0, 0},
ErrorCode: []byte{0, 0, 0, 1},
Fields: []Field{
- NewField(fieldError, []byte("You are not allowed to create news folders.")),
+ NewField(FieldError, []byte("You are not allowed to create news folders.")),
},
},
},
},
},
t: NewTransaction(
- tranGetNewsArtNameList, &[]byte{0, 1},
- NewField(fieldFileName, []byte("testFolder")),
- NewField(fieldNewsPath,
+ TranGetNewsArtNameList, &[]byte{0, 1},
+ NewField(FieldFileName, []byte("testFolder")),
+ NewField(FieldNewsPath,
[]byte{
0, 1,
0, 0,
// },
// },
// t: NewTransaction(
- // tranGetNewsArtNameList, &[]byte{0, 1},
- // NewField(fieldFileName, []byte("testFolder")),
- // NewField(fieldNewsPath,
+ // TranGetNewsArtNameList, &[]byte{0, 1},
+ // NewField(FieldFileName, []byte("testFolder")),
+ // NewField(FieldNewsPath,
// []byte{
// 0, 1,
// 0, 0,
// Type: []byte{0, 0},
// ErrorCode: []byte{0, 0, 0, 1},
// Fields: []Field{
- // NewField(fieldError, []byte("Error creating news folder.")),
+ // NewField(FieldError, []byte("Error creating news folder.")),
// },
// },
// },
args: args{
paramCount: []byte{0x00, 0x02},
buf: []byte{
- 0x00, 0x65, // ID: fieldData
+ 0x00, 0x65, // ID: FieldData
0x00, 0x04, // Size: 2 bytes
0x01, 0x02, 0x03, 0x04, // Data
- 0x00, 0x66, // ID: fieldUserName
+ 0x00, 0x66, // ID: FieldUserName
0x00, 0x02, // Size: 2 bytes
0x00, 0x01, // Data
},
args: args{
paramCount: []byte{0x00, 0x01},
buf: []byte{
- 0x00, 0x65, // ID: fieldData
+ 0x00, 0x65, // ID: FieldData
0x00, 0x04, // Size: 4 bytes
0x01, 0x02, 0x03, // Data
},
args: args{
paramCount: []byte{0x00, 0x01},
buf: []byte{
- 0x00, 0x65, // ID: fieldData
+ 0x00, 0x65, // ID: FieldData
0x00, 0x02, // Size: 2 bytes
0x01, 0x02, // Data
- 0x00, 0x65, // ID: fieldData
+ 0x00, 0x65, // ID: FieldData
0x00, 0x04, // Size: 4 bytes
0x01, 0x02, 0x03, // Data
},
args: args{
paramCount: []byte{0x00, 0x01},
buf: []byte{
- 0x00, 0x65, // ID: fieldData
+ 0x00, 0x65, // ID: FieldData
0x00, 0x02, // Size: 2 bytes
0x01, 0x02, 0x03, // Data
},
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
},
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
},
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
},
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
0,
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
},
00, 00, 00, 0x10,
00, 00, 00, 0x10,
00, 02,
- 00, 0x6c, // 108 - fieldTransferSize
+ 00, 0x6c, // 108 - FieldTransferSize
00, 02,
0x63, 0x3b,
- 00, 0x6b, // 107 = fieldRefNum
+ 00, 0x6b, // 107 = FieldRefNum
00, 0x04,
00, 0x02, 0x93, 0x47,
},
package hotline
import (
- "bufio"
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
}
_ = c.Send(
- *NewTransaction(tranChatSend, nil,
- NewField(fieldData, []byte(chatInput.GetText())),
+ *NewTransaction(TranChatSend, nil,
+ NewField(FieldData, []byte(chatInput.GetText())),
),
)
chatInput.SetText("") // clear the input field after chat send
list.Box.SetBorder(true).SetTitle("| Bookmarks |")
shortcut := 97 // rune for "a"
- for i, srv := range ui.HLClient.pref.Bookmarks {
+ for i, srv := range ui.HLClient.Pref.Bookmarks {
addr := srv.Addr
login := srv.Login
pass := srv.Password
}
func (ui *UI) getTrackerList() *tview.List {
- listing, err := GetListing(ui.HLClient.pref.Tracker)
+ listing, err := GetListing(ui.HLClient.Pref.Tracker)
if err != nil {
// TODO
}
}
func (ui *UI) renderSettingsForm() *tview.Flex {
- iconStr := strconv.Itoa(ui.HLClient.pref.IconID)
+ iconStr := strconv.Itoa(ui.HLClient.Pref.IconID)
settingsForm := tview.NewForm()
- settingsForm.AddInputField("Your Name", ui.HLClient.pref.Username, 0, nil, nil)
+ settingsForm.AddInputField("Your Name", ui.HLClient.Pref.Username, 0, nil, nil)
settingsForm.AddInputField("IconID", iconStr, 0, func(idStr string, _ rune) bool {
_, err := strconv.Atoi(idStr)
return err == nil
}, nil)
- settingsForm.AddInputField("Tracker", ui.HLClient.pref.Tracker, 0, nil, nil)
- settingsForm.AddCheckbox("Enable Terminal Bell", ui.HLClient.pref.EnableBell, nil)
+ settingsForm.AddInputField("Tracker", ui.HLClient.Pref.Tracker, 0, nil, nil)
+ settingsForm.AddCheckbox("Enable Terminal Bell", ui.HLClient.Pref.EnableBell, nil)
settingsForm.AddButton("Save", func() {
usernameInput := settingsForm.GetFormItem(0).(*tview.InputField).GetText()
if len(usernameInput) == 0 {
usernameInput = "unnamed"
}
- ui.HLClient.pref.Username = usernameInput
+ ui.HLClient.Pref.Username = usernameInput
iconStr = settingsForm.GetFormItem(1).(*tview.InputField).GetText()
- ui.HLClient.pref.IconID, _ = strconv.Atoi(iconStr)
- ui.HLClient.pref.Tracker = settingsForm.GetFormItem(2).(*tview.InputField).GetText()
- ui.HLClient.pref.EnableBell = settingsForm.GetFormItem(3).(*tview.Checkbox).IsChecked()
+ ui.HLClient.Pref.IconID, _ = strconv.Atoi(iconStr)
+ ui.HLClient.Pref.Tracker = settingsForm.GetFormItem(2).(*tview.InputField).GetText()
+ ui.HLClient.Pref.EnableBell = settingsForm.GetFormItem(3).(*tview.Checkbox).IsChecked()
- out, err := yaml.Marshal(&ui.HLClient.pref)
+ out, err := yaml.Marshal(&ui.HLClient.Pref)
if err != nil {
// TODO: handle err
}
if len(strings.Split(addr, ":")) == 1 {
addr += ":5500"
}
- if err := ui.HLClient.JoinServer(addr, login, password); err != nil {
+ if err := ui.HLClient.Connect(addr, login, password); err != nil {
return fmt.Errorf("Error joining server: %v\n", err)
}
go func() {
- // Create a new scanner for parsing incoming bytes into transaction tokens
- scanner := bufio.NewScanner(ui.HLClient.Connection)
- scanner.Split(transactionScanner)
-
- // Scan for new transactions and handle them as they come in.
- for scanner.Scan() {
- // Make a new []byte slice and copy the scanner bytes to it. This is critical to avoid a data race as the
- // scanner re-uses the buffer for subsequent scans.
- buf := make([]byte, len(scanner.Bytes()))
- copy(buf, scanner.Bytes())
-
- var t Transaction
- _, err := t.Write(buf)
- if err != nil {
- break
- }
- if err := ui.HLClient.HandleTransaction(&t); err != nil {
- ui.HLClient.Logger.Errorw("Error handling transaction", "err", err)
- }
+ if err := ui.HLClient.HandleTransactions(); err != nil {
+ ui.Pages.SwitchToPage("home")
}
- if scanner.Err() == nil {
- loginErrModal := tview.NewModal().
- AddButtons([]string{"Ok"}).
- SetText("The server connection has closed.").
- SetDoneFunc(func(buttonIndex int, buttonLabel string) {
- ui.Pages.SwitchToPage("home")
- })
- loginErrModal.Box.SetTitle("Server Connection Error")
-
- ui.Pages.AddPage("loginErr", loginErrModal, false, true)
- ui.App.Draw()
- return
- }
- ui.Pages.SwitchToPage("home")
+ loginErrModal := tview.NewModal().
+ AddButtons([]string{"Ok"}).
+ SetText("The server connection has closed.").
+ SetDoneFunc(func(buttonIndex int, buttonLabel string) {
+ ui.Pages.SwitchToPage("home")
+ })
+ loginErrModal.Box.SetTitle("Server Connection Error")
+ ui.Pages.AddPage("loginErr", loginErrModal, false, true)
+ ui.App.Draw()
}()
return nil
ui.HLClient.Logger.Infow("saving bookmark")
// TODO: Implement bookmark saving
- ui.HLClient.pref.AddBookmark(joinServerForm.GetFormItem(0).(*tview.InputField).GetText(), joinServerForm.GetFormItem(0).(*tview.InputField).GetText(), joinServerForm.GetFormItem(1).(*tview.InputField).GetText(), joinServerForm.GetFormItem(2).(*tview.InputField).GetText())
- out, err := yaml.Marshal(ui.HLClient.pref)
+ ui.HLClient.Pref.AddBookmark(joinServerForm.GetFormItem(0).(*tview.InputField).GetText(), joinServerForm.GetFormItem(0).(*tview.InputField).GetText(), joinServerForm.GetFormItem(1).(*tview.InputField).GetText(), joinServerForm.GetFormItem(2).(*tview.InputField).GetText())
+ out, err := yaml.Marshal(ui.HLClient.Pref)
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
- // pref := ui.HLClient.pref
+ // Pref := ui.HLClient.Pref
}).
AddButton("Cancel", func() {
ui.Pages.SwitchToPage(backPage)
// List files
if event.Key() == tcell.KeyCtrlF {
- if err := ui.HLClient.Send(*NewTransaction(tranGetFileNameList, nil)); err != nil {
+ if err := ui.HLClient.Send(*NewTransaction(TranGetFileNameList, nil)); err != nil {
ui.HLClient.Logger.Errorw("err", "err", err)
}
}
// Show News
if event.Key() == tcell.KeyCtrlN {
- if err := ui.HLClient.Send(*NewTransaction(tranGetMsgs, nil)); err != nil {
+ if err := ui.HLClient.Send(*NewTransaction(TranGetMsgs, nil)); err != nil {
ui.HLClient.Logger.Errorw("err", "err", err)
}
}
return event
}
err := ui.HLClient.Send(
- *NewTransaction(tranOldPostNews, nil,
- NewField(fieldData, []byte(newsText)),
+ *NewTransaction(TranOldPostNews, nil,
+ NewField(FieldData, []byte(newsText)),
),
)
if err != nil {
userFLagRefusePChat = 3 // User refuses private chat
)
-// fieldOptions flags are sent from v1.5+ clients as part of tranAgreed
+// FieldOptions flags are sent from v1.5+ clients as part of TranAgreed
const (
refusePM = 0 // User has "Refuse private messages" pref set
refuseChat = 1 // User has "Refuse private chat" pref set