"github.com/rivo/tview"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
- "gopkg.in/yaml.v2"
+ "gopkg.in/yaml.v3"
"math/big"
"math/rand"
"net"
const (
trackerListPage = "trackerList"
+ serverUIPage = "serverUI"
)
//go:embed banners/*.txt
prefs := ClientPrefs{}
decoder := yaml.NewDecoder(fh)
- decoder.SetStrict(true)
if err := decoder.Decode(&prefs); err != nil {
return nil, err
}
UserList []User
Logger *zap.SugaredLogger
activeTasks map[uint32]*Transaction
+ serverName string
pref *ClientPrefs
UI *UI
- outbox chan *Transaction
- Inbox chan *Transaction
+ Inbox chan *Transaction
}
func NewClient(cfgPath string, logger *zap.SugaredLogger) *Client {
prefs, err := readConfig(cfgPath)
if err != nil {
- fmt.Printf("unable to read config file %s", cfgPath)
- os.Exit(1)
+ logger.Fatal(fmt.Sprintf("unable to read config file %s\n", cfgPath))
}
c.pref = prefs
return db.TextView.Write(p)
}
-// Sync is a noop function that exists to satisfy the zapcore.WriteSyncer interface
+// Sync is a noop function that dataFile to satisfy the zapcore.WriteSyncer interface
func (db *DebugBuffer) Sync() error {
return nil
}
Name: "tranServerMsg",
Handler: handleTranServerMsg,
},
+ 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 += "\n\nAt " + time
+ msg += "\n\nAt " + time
title := fmt.Sprintf("| Private Message From: %s |", t.GetField(fieldUserName).Data)
msgBox := tview.NewTextView().SetScrollable(true)
AddItem(nil, 0, 1, false), 0, 2, true).
AddItem(nil, 0, 1, false)
-
- c.UI.Pages.AddPage("serverMsgModal" + time, centeredFlex, true, true)
+ c.UI.Pages.AddPage("serverMsgModal"+time, centeredFlex, true, true)
c.UI.App.Draw() // TODO: errModal doesn't render without this. wtf?
return res, err
entry := selectedNode.GetReference().(*FileNameWithInfo)
- if bytes.Equal(entry.Type, []byte("fldr")) {
- c.Logger.Infow("get new directory listing", "name", string(entry.Name))
+ if bytes.Equal(entry.Type[:], []byte("fldr")) {
+ c.Logger.Infow("get new directory listing", "name", string(entry.name))
- c.filePath = append(c.filePath, string(entry.Name))
+ c.filePath = append(c.filePath, string(entry.name))
f := NewField(fieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
if err := c.UI.HLClient.Send(*NewTransaction(tranGetFileNameList, nil, f)); err != nil {
}
} else {
// TODO: initiate file download
- c.Logger.Infow("download file", "name", string(entry.Name))
+ c.Logger.Infow("download file", "name", string(entry.name))
}
}
root.AddChild(node)
}
- var fileList []FileNameWithInfo
for _, f := range t.Fields {
var fn FileNameWithInfo
- _, _ = fn.Read(f.Data)
- fileList = append(fileList, fn)
+ err = fn.UnmarshalBinary(f.Data)
+ if err != nil {
+ return nil, nil
+ }
- if bytes.Equal(fn.Type, []byte("fldr")) {
- node := tview.NewTreeNode(fmt.Sprintf("[blue::]📁 %s[-:-:-]", fn.Name))
+ if bytes.Equal(fn.Type[:], []byte("fldr")) {
+ node := tview.NewTreeNode(fmt.Sprintf("[blue::]📁 %s[-:-:-]", fn.name))
node.SetReference(&fn)
root.AddChild(node)
} else {
- size := binary.BigEndian.Uint32(fn.FileSize) / 1024
+ size := binary.BigEndian.Uint32(fn.FileSize[:]) / 1024
- node := tview.NewTreeNode(fmt.Sprintf(" %-40s %10v KB", fn.Name, size))
+ node := tview.NewTreeNode(fmt.Sprintf(" %-40s %10v KB", fn.name, size))
node.SetReference(&fn)
root.AddChild(node)
}
newsTextView := tview.NewTextView().
SetText(newsText).
SetDoneFunc(func(key tcell.Key) {
- c.UI.Pages.SwitchToPage("serverUI")
+ c.UI.Pages.SwitchToPage(serverUIPage)
c.UI.App.SetFocus(c.UI.chatInput)
})
newsTextView.SetBorder(true).SetTitle("News")
c.UI.Pages.AddPage("news", newsTextView, true, true)
- //c.UI.Pages.SwitchToPage("news")
- //c.UI.App.SetFocus(newsTextView)
+ // c.UI.Pages.SwitchToPage("news")
+ // c.UI.App.SetFocus(newsTextView)
c.UI.App.Draw()
return res, err
agreement := string(t.GetField(fieldData).Data)
agreement = strings.ReplaceAll(agreement, "\r", "\n")
- c.UI.agreeModal = tview.NewModal().
+ agreeModal := tview.NewModal().
SetText(agreement).
AddButtons([]string{"Agree", "Disagree"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
},
)
- c.Logger.Debug("show agreement page")
- c.UI.Pages.AddPage("agreement", c.UI.agreeModal, false, true)
- c.UI.Pages.ShowPage("agreement ")
- c.UI.App.Draw()
+ c.UI.Pages.AddPage("agreement", agreeModal, false, true)
return res, err
}
c.Logger.Error(string(t.GetField(fieldError).Data))
return nil, errors.New("login error: " + string(t.GetField(fieldError).Data))
}
- c.UI.Pages.AddAndSwitchToPage("serverUI", c.UI.renderServerUI(), true)
+ 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 {
return err
}
+ // start keepalive go routine
+ go func() { _ = c.keepalive() }()
+
return nil
}
+func (c *Client) keepalive() error {
+ for {
+ time.Sleep(300 * time.Second)
+ _ = 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
}
func (c *Client) Handshake() error {
- //Protocol ID 4 ‘TRTP’ 0x54 52 54 50
- //Sub-protocol ID 4 User defined
- //Version 2 1 Currently 1
- //Sub-version 2 User defined
+ // Protocol ID 4 ‘TRTP’ 0x54 52 54 50
+ // Sub-protocol ID 4 User defined
+ // Version 2 1 Currently 1
+ // Sub-version 2 User defined
if _, err := c.Connection.Write(ClientHandshake); err != nil {
return fmt.Errorf("handshake write err: %s", err)
}
return err
}
- if bytes.Compare(replyBuf, ServerHandshake) == 0 {
+ if bytes.Equal(replyBuf, ServerHandshake) {
return nil
}
NewField(fieldUserIconID, c.pref.IconBytes()),
NewField(fieldUserLogin, negateString([]byte(login))),
NewField(fieldUserPassword, negateString([]byte(password))),
- NewField(fieldVersion, []byte{0, 2}),
),
)
}
requestNum := binary.BigEndian.Uint16(t.Type)
tID := binary.BigEndian.Uint32(t.ID)
- //handler := TransactionHandlers[requestNum]
+ // handler := TransactionHandlers[requestNum]
// if transaction is NOT reply, add it to the list to transactions we're expecting a response for
if t.IsReply == 0 {
var n int
var err error
- if n, err = c.Connection.Write(t.Payload()); err != nil {
+ b, err := t.MarshalBinary()
+ if err != nil {
+ return err
+ }
+ if n, err = c.Connection.Write(b); err != nil {
return err
}
c.Logger.Debugw("Sent Transaction",
return nil
}
-func (c *Client) Connected() bool {
- // c.Agreed == true &&
- if c.UserAccess != nil {
- return true
- }
- return false
-}
-
func (c *Client) Disconnect() error {
- err := c.Connection.Close()
- if err != nil {
- return err
- }
- return nil
+ return c.Connection.Close()
}