aboutsummaryrefslogtreecommitdiff
path: root/hotline
diff options
context:
space:
mode:
Diffstat (limited to 'hotline')
-rw-r--r--hotline/access.go18
-rw-r--r--hotline/client.go14
-rw-r--r--hotline/client_conn.go47
-rw-r--r--hotline/client_conn_test.go2
-rw-r--r--hotline/field.go3
-rw-r--r--hotline/file_store.go20
-rw-r--r--hotline/flattened_file_object.go6
-rw-r--r--hotline/handshake.go49
-rw-r--r--hotline/news.go33
-rw-r--r--hotline/server.go29
-rw-r--r--hotline/server_blackbox_test.go27
-rw-r--r--hotline/server_test.go208
-rw-r--r--hotline/tracker.go30
-rw-r--r--hotline/transaction_handlers.go34
-rw-r--r--hotline/transaction_handlers_test.go20
-rw-r--r--hotline/ui.go6
-rw-r--r--hotline/user.go17
17 files changed, 263 insertions, 300 deletions
diff --git a/hotline/access.go b/hotline/access.go
index 1cdbeed..8d6dd63 100644
--- a/hotline/access.go
+++ b/hotline/access.go
@@ -28,25 +28,25 @@ const (
accessOpenUser = 16
accessModifyUser = 17
// accessChangeOwnPass = 18 // Documented but unused?
- //accessSendPrivMsg = 19 // This doesn't do what it seems like it should do. TODO: Investigate
+ // accessSendPrivMsg = 19 // This doesn't do what it seems like it should do. TODO: Investigate
accessNewsReadArt = 20
accessNewsPostArt = 21
accessDisconUser = 22 // Toggles red user name in user list
accessCannotBeDiscon = 23
accessGetClientInfo = 24
- //accessUploadAnywhere = 25
- //accessAnyName = 26
- //accessNoAgreement = 27
- //accessSetFileComment = 28
- //accessSetFolderComment = 29
- //accessViewDropBoxes = 30
+ // accessUploadAnywhere = 25
+ // accessAnyName = 26
+ // accessNoAgreement = 27
+ // accessSetFileComment = 28
+ // accessSetFolderComment = 29
+ // accessViewDropBoxes = 30
accessMakeAlias = 31
accessBroadcast = 32
accessNewsDeleteArt = 33
accessNewsCreateCat = 34
- //accessNewsDeleteCat = 35
+ // accessNewsDeleteCat = 35
accessNewsCreateFldr = 36
- //accessNewsDeleteFldr = 37
+ // accessNewsDeleteFldr = 37
)
type accessBitmap [8]byte
diff --git a/hotline/client.go b/hotline/client.go
index 33ec511..4ca42f3 100644
--- a/hotline/client.go
+++ b/hotline/client.go
@@ -336,8 +336,8 @@ func handleGetMsgs(c *Client, t *Transaction) (res []Transaction, err error) {
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
@@ -605,10 +605,10 @@ var ServerHandshake = []byte{
}
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)
}
@@ -644,7 +644,7 @@ func (c *Client) Send(t Transaction) error {
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 {
diff --git a/hotline/client_conn.go b/hotline/client_conn.go
index b43a6ff..ce9d7f1 100644
--- a/hotline/client_conn.go
+++ b/hotline/client_conn.go
@@ -1,9 +1,7 @@
package hotline
import (
- "bytes"
"encoding/binary"
- "errors"
"golang.org/x/crypto/bcrypt"
"math/big"
"net"
@@ -35,7 +33,7 @@ type ClientConn struct {
Server *Server
Version *[]byte
Idle bool
- AutoReply *[]byte
+ AutoReply []byte
Transfers map[int][]*FileTransfer
Agreed bool
}
@@ -54,7 +52,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
// Validate that required field is present
if field.ID == nil {
- cc.Server.Logger.Infow(
+ cc.Server.Logger.Errorw(
"Missing required field",
"Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
)
@@ -176,47 +174,6 @@ func (cc ClientConn) NotifyOthers(t Transaction) {
}
}
-type handshake struct {
- Protocol [4]byte // Must be 0x54525450 TRTP
- SubProtocol [4]byte
- Version [2]byte // Always 1
- SubVersion [2]byte
-}
-
-// Handshake
-// After establishing TCP connection, both client and server start the handshake process
-// in order to confirm that each of them comply with requirements of the other.
-// The information provided in this initial data exchange identifies protocols,
-// and their versions, used in the communication. In the case where, after inspection,
-// the capabilities of one of the subjects do not comply with the requirements of the other,
-// the connection is dropped.
-//
-// The following information is sent to the server:
-// Description Size Data Note
-// Protocol ID 4 TRTP 0x54525450
-// Sub-protocol ID 4 HOTL User defined
-// VERSION 2 1 Currently 1
-// Sub-version 2 2 User defined
-//
-// The server replies with the following:
-// Description Size Data Note
-// Protocol ID 4 TRTP
-//Error code 4 Error code returned by the server (0 = no error)
-func Handshake(conn net.Conn, buf []byte) error {
- var h handshake
- r := bytes.NewReader(buf)
- if err := binary.Read(r, binary.BigEndian, &h); err != nil {
- return err
- }
-
- if h.Protocol != [4]byte{0x54, 0x52, 0x54, 0x50} {
- return errors.New("invalid handshake")
- }
-
- _, err := conn.Write([]byte{84, 82, 84, 80, 0, 0, 0, 0})
- return err
-}
-
// NewReply returns a reply Transaction with fields for the ClientConn
func (cc *ClientConn) NewReply(t *Transaction, fields ...Field) Transaction {
reply := Transaction{
diff --git a/hotline/client_conn_test.go b/hotline/client_conn_test.go
index 2176185..da0a2cc 100644
--- a/hotline/client_conn_test.go
+++ b/hotline/client_conn_test.go
@@ -17,7 +17,7 @@ func TestClientConn_handleTransaction(t *testing.T) {
Server *Server
Version *[]byte
Idle bool
- AutoReply *[]byte
+ AutoReply []byte
}
type args struct {
transaction *Transaction
diff --git a/hotline/field.go b/hotline/field.go
index 7955fe3..26012af 100644
--- a/hotline/field.go
+++ b/hotline/field.go
@@ -16,7 +16,8 @@ const fieldRefNum = 107
const fieldTransferSize = 108
const fieldChatOptions = 109
const fieldUserAccess = 110
-//const fieldUserAlias = 111 TODO: implement
+
+// const fieldUserAlias = 111 TODO: implement
const fieldUserFlags = 112
const fieldOptions = 113
const fieldChatID = 114
diff --git a/hotline/file_store.go b/hotline/file_store.go
index c1c3929..f528c36 100644
--- a/hotline/file_store.go
+++ b/hotline/file_store.go
@@ -14,25 +14,25 @@ type FileStore interface {
Symlink(oldname, newname string) error
// TODO: implement these
- //Rename(oldpath string, newpath string) error
- //RemoveAll(path string) error
+ // Rename(oldpath string, newpath string) error
+ // RemoveAll(path string) error
}
type OSFileStore struct{}
-func (fs OSFileStore) Mkdir(name string, perm os.FileMode) error {
+func (fs *OSFileStore) Mkdir(name string, perm os.FileMode) error {
return os.Mkdir(name, perm)
}
-func (fs OSFileStore) Stat(name string) (os.FileInfo, error) {
+func (fs *OSFileStore) Stat(name string) (os.FileInfo, error) {
return os.Stat(name)
}
-func (fs OSFileStore) Open(name string) (*os.File, error) {
+func (fs *OSFileStore) Open(name string) (*os.File, error) {
return os.Open(name)
}
-func (fs OSFileStore) Symlink(oldname, newname string) error {
+func (fs *OSFileStore) Symlink(oldname, newname string) error {
return os.Symlink(oldname, newname)
}
@@ -40,12 +40,12 @@ type MockFileStore struct {
mock.Mock
}
-func (mfs MockFileStore) Mkdir(name string, perm os.FileMode) error {
+func (mfs *MockFileStore) Mkdir(name string, perm os.FileMode) error {
args := mfs.Called(name, perm)
return args.Error(0)
}
-func (mfs MockFileStore) Stat(name string) (os.FileInfo, error) {
+func (mfs *MockFileStore) Stat(name string) (os.FileInfo, error) {
args := mfs.Called(name)
if args.Get(0) == nil {
return nil, args.Error(1)
@@ -54,12 +54,12 @@ func (mfs MockFileStore) Stat(name string) (os.FileInfo, error) {
return args.Get(0).(os.FileInfo), args.Error(1)
}
-func (mfs MockFileStore) Open(name string) (*os.File, error) {
+func (mfs *MockFileStore) Open(name string) (*os.File, error) {
args := mfs.Called(name)
return args.Get(0).(*os.File), args.Error(1)
}
-func (mfs MockFileStore) Symlink(oldname, newname string) error {
+func (mfs *MockFileStore) Symlink(oldname, newname string) error {
args := mfs.Called(oldname, newname)
return args.Error(0)
}
diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go
index 2cafbc9..fae8ab8 100644
--- a/hotline/flattened_file_object.go
+++ b/hotline/flattened_file_object.go
@@ -77,7 +77,7 @@ func NewFlatFileInformationFork(fileName string, modifyTime []byte) FlatFileInfo
func (ffif FlatFileInformationFork) DataSize() []byte {
size := make([]byte, 4)
- //TODO: Can I do math directly on two byte slices?
+ // TODO: Can I do math directly on two byte slices?
dataSize := len(ffif.Name) + len(ffif.Comment) + 74
binary.BigEndian.PutUint32(size, uint32(dataSize))
@@ -124,8 +124,8 @@ func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
comment := bytes[commentStartPos:commentEndPos]
- //dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
- //dataSize := binary.BigEndian.Uint32(dataSizeField)
+ // dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
+ // dataSize := binary.BigEndian.Uint32(dataSizeField)
ffo := flattenedFileObject{
FlatFileHeader: NewFlatFileHeader(),
diff --git a/hotline/handshake.go b/hotline/handshake.go
new file mode 100644
index 0000000..66bf643
--- /dev/null
+++ b/hotline/handshake.go
@@ -0,0 +1,49 @@
+package hotline
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "net"
+)
+
+type handshake struct {
+ Protocol [4]byte // Must be 0x54525450 TRTP
+ SubProtocol [4]byte
+ Version [2]byte // Always 1
+ SubVersion [2]byte
+}
+
+// Handshake
+// After establishing TCP connection, both client and server start the handshake process
+// in order to confirm that each of them comply with requirements of the other.
+// The information provided in this initial data exchange identifies protocols,
+// and their versions, used in the communication. In the case where, after inspection,
+// the capabilities of one of the subjects do not comply with the requirements of the other,
+// the connection is dropped.
+//
+// The following information is sent to the server:
+// Description Size Data Note
+// Protocol ID 4 TRTP 0x54525450
+// Sub-protocol ID 4 HOTL User defined
+// VERSION 2 1 Currently 1
+// Sub-version 2 2 User defined
+//
+// The server replies with the following:
+// Description Size Data Note
+// Protocol ID 4 TRTP
+// Error code 4 Error code returned by the server (0 = no error)
+func Handshake(conn net.Conn, buf []byte) error {
+ var h handshake
+ r := bytes.NewReader(buf)
+ if err := binary.Read(r, binary.BigEndian, &h); err != nil {
+ return err
+ }
+
+ if h.Protocol != [4]byte{0x54, 0x52, 0x54, 0x50} {
+ return errors.New("invalid handshake")
+ }
+
+ _, err := conn.Write([]byte{84, 82, 84, 80, 0, 0, 0, 0})
+ return err
+}
diff --git a/hotline/news.go b/hotline/news.go
index a6b8372..77c6696 100644
--- a/hotline/news.go
+++ b/hotline/news.go
@@ -12,7 +12,7 @@ type ThreadedNews struct {
}
type NewsCategoryListData15 struct {
- Type []byte `yaml:"Type"` //Size 2 ; Bundle (2) or category (3)
+ Type []byte `yaml:"Type"` // Size 2 ; Bundle (2) or category (3)
Count []byte // Article or SubCategory count Size 2
NameSize byte
Name string `yaml:"Name"` //
@@ -64,11 +64,11 @@ func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
type NewsArtData struct {
Title string `yaml:"Title"`
Poster string `yaml:"Poster"`
- Date []byte `yaml:"Date"` //size 8
- PrevArt []byte `yaml:"PrevArt"` //size 4
- NextArt []byte `yaml:"NextArt"` //size 4
- ParentArt []byte `yaml:"ParentArt"` //size 4
- FirstChildArt []byte `yaml:"FirstChildArtArt"` //size 4
+ Date []byte `yaml:"Date"` // size 8
+ PrevArt []byte `yaml:"PrevArt"` // size 4
+ NextArt []byte `yaml:"NextArt"` // size 4
+ ParentArt []byte `yaml:"ParentArt"` // size 4
+ FirstChildArt []byte `yaml:"FirstChildArtArt"` // size 4
DataFlav []byte `yaml:"DataFlav"` // "text/plain"
Data string `yaml:"Data"`
}
@@ -202,27 +202,6 @@ func (newscat *NewsCategoryListData15) nameLen() []byte {
return []byte{uint8(len(newscat.Name))}
}
-//type NewsPath struct {
-// Paths []string
-//}
-//
-//func (np *NewsPath) Payload() []byte {
-// var out []byte
-//
-// count := make([]byte, 2)
-// binary.BigEndian.PutUint16(count, uint16(len(np.Paths)))
-//
-// out = append(out, count...)
-// for _, p := range np.Paths {
-// pLen := byte(len(p))
-// out = append(out, []byte{0, 0}...) // what is this?
-// out = append(out, pLen)
-// out = append(out, []byte(p)...)
-// }
-//
-// return out
-//}
-
func ReadNewsPath(newsPath []byte) []string {
if len(newsPath) == 0 {
return []string{}
diff --git a/hotline/server.go b/hotline/server.go
index c5a29ba..1da2859 100644
--- a/hotline/server.go
+++ b/hotline/server.go
@@ -8,7 +8,6 @@ import (
"go.uber.org/zap"
"io"
"io/ioutil"
- "log"
"math/big"
"math/rand"
"net"
@@ -21,7 +20,6 @@ import (
"sync"
"time"
- "golang.org/x/crypto/bcrypt"
"gopkg.in/yaml.v2"
)
@@ -50,8 +48,8 @@ type Server struct {
APIListener net.Listener
FileListener net.Listener
- newsReader io.Reader
- newsWriter io.WriteCloser
+ // newsReader io.Reader
+ // newsWriter io.WriteCloser
outbox chan Transaction
@@ -327,7 +325,7 @@ func (s *Server) NewClientConn(conn net.Conn) *ClientConn {
Connection: conn,
Server: s,
Version: &[]byte{},
- AutoReply: &[]byte{},
+ AutoReply: []byte{},
Transfers: make(map[int][]*FileTransfer),
Agreed: false,
}
@@ -376,7 +374,7 @@ func (s *Server) connectedUsers() []Field {
var connectedUsers []Field
for _, c := range sortedClients(s.Clients) {
- if c.Agreed == false {
+ if !c.Agreed {
continue
}
user := User{
@@ -577,21 +575,6 @@ func (s *Server) handleNewConnection(conn net.Conn) error {
}
}
-func hashAndSalt(pwd []byte) string {
- // Use GenerateFromPassword to hash & salt pwd.
- // MinCost is just an integer constant provided by the bcrypt
- // package along with DefaultCost & MaxCost.
- // The cost can be any value you want provided it isn't lower
- // than the MinCost (4)
- hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
- if err != nil {
- log.Println(err)
- }
- // GenerateFromPassword returns a byte slice so we need to
- // convert the bytes to a string and return it
- return string(hash)
-}
-
// NewTransactionRef generates a random ID for the file transfer. The Hotline client includes this ID
// in the file transfer request payload, and the file transfer server will use it to map the request
// to a transfer
@@ -796,7 +779,7 @@ func (s *Server) TransferFile(conn net.Conn) error {
}
// Read the client's Next Action request
- //TODO: Remove hardcoded behavior and switch behaviors based on the next action send
+ // TODO: Remove hardcoded behavior and switch behaviors based on the next action send
if _, err := conn.Read(readBuffer); err != nil {
return err
}
@@ -848,7 +831,7 @@ func (s *Server) TransferFile(conn net.Conn) error {
bytesRead, err := file.Read(sendBuffer)
if err == io.EOF {
// Read the client's Next Action request
- //TODO: Remove hardcoded behavior and switch behaviors based on the next action send
+ // TODO: Remove hardcoded behavior and switch behaviors based on the next action send
if _, err := conn.Read(readBuffer); err != nil {
s.Logger.Errorf("error reading next action: %v", err)
return err
diff --git a/hotline/server_blackbox_test.go b/hotline/server_blackbox_test.go
index 601d745..ee5f76b 100644
--- a/hotline/server_blackbox_test.go
+++ b/hotline/server_blackbox_test.go
@@ -66,7 +66,7 @@ func NewTestLogger() *zap.SugaredLogger {
func StartTestServer() (*Server, context.Context, context.CancelFunc) {
ctx, cancelRoot := context.WithCancel(context.Background())
- FS = OSFileStore{}
+ FS = &OSFileStore{}
srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger())
if err != nil {
@@ -84,7 +84,7 @@ func StartTestServer() (*Server, context.Context, context.CancelFunc) {
}
func TestHandshake(t *testing.T) {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
fh, _ := os.Open("./test/config/Agreement.txt")
mfs.On("Open", "/test/config/Agreement.txt").Return(fh, nil)
fh, _ = os.Open("./test/config/config.yaml")
@@ -115,7 +115,7 @@ func TestHandshake(t *testing.T) {
}
-//func TestLogin(t *testing.T) {
+// func TestLogin(t *testing.T) {
//
// tests := []struct {
// name string
@@ -131,13 +131,13 @@ func TestHandshake(t *testing.T) {
//
// })
// }
-//}
+// }
func TestNewUser(t *testing.T) {
srv, _, _ := StartTestServer()
tests := []testCase{
- //{
+ // {
// name: "a valid new account",
// mockHandler: func() mockClientHandler {
// mh := mockClientHandler{}
@@ -180,8 +180,8 @@ func TestNewUser(t *testing.T) {
// want: &Transaction{
// Fields: []Field{},
// },
- //},
- //{
+ // },
+ // {
// name: "a newUser request from a user without the required access",
// mockHandler: func() *mockClientHandler {
// mh := mockClientHandler{}
@@ -211,8 +211,8 @@ func TestNewUser(t *testing.T) {
// NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
// NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
// ),
- //},
- //{
+ // },
+ // {
// name: "when user does not have required permission",
// mockHandler: func() map[int]*mockClientHandler {
// mockHandlers := make(map[int]*mockClientHandler)
@@ -251,9 +251,9 @@ func TestNewUser(t *testing.T) {
// NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
// NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
// ),
- //},
+ // },
- //{
+ // {
// name: "a request to create a user that already exists",
// setup: func() {
//
@@ -279,7 +279,7 @@ func TestNewUser(t *testing.T) {
// NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")),
// },
// },
- //},
+ // },
}
for _, test := range tests {
@@ -301,7 +301,7 @@ func TestNewUser(t *testing.T) {
// send test case request
_ = c.Send(*test.request)
- //time.Sleep(1 * time.Second)
+ // time.Sleep(1 * time.Second)
// ===
transactions, _ := readN(c.Connection, 1)
@@ -320,6 +320,7 @@ func TestNewUser(t *testing.T) {
}
}
+// tranAssertEqual compares equality of transactions slices after stripping out the random ID
func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool {
var newT1 []Transaction
var newT2 []Transaction
diff --git a/hotline/server_test.go b/hotline/server_test.go
index c69177f..5937adc 100644
--- a/hotline/server_test.go
+++ b/hotline/server_test.go
@@ -1,7 +1,7 @@
package hotline
//
-//import (
+// import (
// "bytes"
// "fmt"
// "github.com/google/go-cmp/cmp"
@@ -11,18 +11,18 @@ package hotline
// "strings"
// "sync"
// "testing"
-//)
+// )
//
-//type transactionTest struct {
+// type transactionTest struct {
// description string // Human understandable description
// account Account // Account struct for a user that will test transaction will execute under
// request Transaction // transaction that will be sent by the client to the server
// want Transaction // transaction that the client expects to receive in response
// setup func() // Optional setup required for the test scenario
// teardown func() // Optional teardown for test scenario
-//}
+// }
//
-//func (tt *transactionTest) Setup(srv *Server) error {
+// func (tt *transactionTest) Setup(srv *Server) error {
// if err := srv.NewUser(tt.account.Login, tt.account.Name, NegatedUserString([]byte(tt.account.Password)), tt.account.Access); err != nil {
// return err
// }
@@ -32,9 +32,9 @@ package hotline
// }
//
// return nil
-//}
+// }
//
-//func (tt *transactionTest) Teardown(srv *Server) error {
+// func (tt *transactionTest) Teardown(srv *Server) error {
// if err := srv.DeleteUser(tt.account.Login); err != nil {
// return err
// }
@@ -44,10 +44,10 @@ package hotline
// }
//
// return nil
-//}
+// }
//
-//// StartTestServer
-//func StartTestServer() (srv *Server, lnPort int) {
+// // StartTestServer
+// func StartTestServer() (srv *Server, lnPort int) {
// hotlineServer, _ := NewServer("test/config/")
// ln, err := net.Listen("tcp", ":0")
//
@@ -61,9 +61,9 @@ package hotline
// }
// }()
// return hotlineServer, ln.Addr().(*net.TCPAddr).Port
-//}
+// }
//
-//func StartTestClient(serverPort int, login, passwd string) (*Client, error) {
+// func StartTestClient(serverPort int, login, passwd string) (*Client, error) {
// c := NewClient("")
//
// err := c.JoinServer(fmt.Sprintf(":%v", serverPort), login, passwd)
@@ -72,9 +72,9 @@ package hotline
// }
//
// return c, nil
-//}
+// }
//
-//func StartTestServerWithClients(clientCount int) ([]*Client, int) {
+// func StartTestServerWithClients(clientCount int) ([]*Client, int) {
// _, serverPort := StartTestServer()
//
// var clients []*Client
@@ -88,48 +88,48 @@ package hotline
// clients[0].ReadN(2)
//
// return clients, serverPort
-//}
+// }
//
-////func TestHandleTranAgreed(t *testing.T) {
-//// clients, _ := StartTestServerWithClients(2)
-////
-//// chatMsg := "Test Chat"
-////
-//// // Assert that both clients should receive the user join notification
-//// var wg sync.WaitGroup
-//// for _, client := range clients {
-//// wg.Add(1)
-//// go func(wg *sync.WaitGroup, c *Client) {
-//// defer wg.Done()
-////
-//// receivedMsg := c.ReadTransactions()[0].GetField(fieldData).Data
-////
-//// want := []byte(fmt.Sprintf("test: %s\r", chatMsg))
-//// if bytes.Compare(receivedMsg, want) != 0 {
-//// t.Errorf("%q, want %q", receivedMsg, want)
-//// }
-//// }(&wg, client)
-//// }
-////
-//// trans := clients[1].ReadTransactions()
-//// spew.Dump(trans)
-////
-//// // Send the agreement
-//// clients[1].Connection.Write(
-//// NewTransaction(
-//// tranAgreed, 0,
-//// []Field{
-//// NewField(fieldUserName, []byte("testUser")),
-//// NewField(fieldUserIconID, []byte{0x00,0x07}),
-//// },
-//// ).Payload(),
-//// )
-////
-//// wg.Wait()
-////}
+// //func TestHandleTranAgreed(t *testing.T) {
+// // clients, _ := StartTestServerWithClients(2)
+// //
+// // chatMsg := "Test Chat"
+// //
+// // // Assert that both clients should receive the user join notification
+// // var wg sync.WaitGroup
+// // for _, client := range clients {
+// // wg.Add(1)
+// // go func(wg *sync.WaitGroup, c *Client) {
+// // defer wg.Done()
+// //
+// // receivedMsg := c.ReadTransactions()[0].GetField(fieldData).Data
+// //
+// // want := []byte(fmt.Sprintf("test: %s\r", chatMsg))
+// // if bytes.Compare(receivedMsg, want) != 0 {
+// // t.Errorf("%q, want %q", receivedMsg, want)
+// // }
+// // }(&wg, client)
+// // }
+// //
+// // trans := clients[1].ReadTransactions()
+// // spew.Dump(trans)
+// //
+// // // Send the agreement
+// // clients[1].Connection.Write(
+// // NewTransaction(
+// // tranAgreed, 0,
+// // []Field{
+// // NewField(fieldUserName, []byte("testUser")),
+// // NewField(fieldUserIconID, []byte{0x00,0x07}),
+// // },
+// // ).Payload(),
+// // )
+// //
+// // wg.Wait()
+// //}
//
-//func TestChatSend(t *testing.T) {
+// func TestChatSend(t *testing.T) {
// //srvPort := StartTestServer()
// //
// //senderClient := NewClient("senderClient")
@@ -169,9 +169,9 @@ package hotline
// )
//
// wg.Wait()
-//}
+// }
//
-//func TestSetClientUserInfo(t *testing.T) {
+// func TestSetClientUserInfo(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
//
// newIcon := []byte{0x00, 0x01}
@@ -208,11 +208,11 @@ package hotline
// }
//
// wg.Wait()
-//}
+// }
//
-//// TestSendInstantMsg tests that client A can send an instant message to client B
-////
-//func TestSendInstantMsg(t *testing.T) {
+// // TestSendInstantMsg tests that client A can send an instant message to client B
+// //
+// func TestSendInstantMsg(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
//
// instantMsg := "Test IM"
@@ -258,9 +258,9 @@ package hotline
// }
//
// wg.Wait()
-//}
+// }
//
-//func TestOldPostNews(t *testing.T) {
+// func TestOldPostNews(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
//
// newsPost := "Test News Post"
@@ -287,35 +287,35 @@ package hotline
// )
//
// wg.Wait()
-//}
+// }
//
-//// TODO: Fixme
-////func TestGetFileNameList(t *testing.T) {
-//// clients, _ := StartTestServerWithClients(2)
-////
-//// clients[0].Connection.Write(
-//// NewTransaction(
-//// tranGetFileNameList, 0,
-//// []Field{},
-//// ).Payload(),
-//// )
-////
-//// ts := clients[0].ReadTransactions()
-//// testfileSit := ReadFileNameWithInfo(ts[0].Fields[1].Data)
-////
-//// want := "testfile.sit"
-//// got := testfileSit.Name
-//// diff := cmp.Diff(want, got)
-//// if diff != "" {
-//// t.Fatalf(diff)
-//// }
-//// if testfileSit.Name != "testfile.sit" {
-//// t.Errorf("news post missing")
-//// t.Errorf("%q, want %q", testfileSit.Name, "testfile.sit")
-//// }
-////}
+// // TODO: Fixme
+// //func TestGetFileNameList(t *testing.T) {
+// // clients, _ := StartTestServerWithClients(2)
+// //
+// // clients[0].Connection.Write(
+// // NewTransaction(
+// // tranGetFileNameList, 0,
+// // []Field{},
+// // ).Payload(),
+// // )
+// //
+// // ts := clients[0].ReadTransactions()
+// // testfileSit := ReadFileNameWithInfo(ts[0].Fields[1].Data)
+// //
+// // want := "testfile.sit"
+// // got := testfileSit.Name
+// // diff := cmp.Diff(want, got)
+// // if diff != "" {
+// // t.Fatalf(diff)
+// // }
+// // if testfileSit.Name != "testfile.sit" {
+// // t.Errorf("news post missing")
+// // t.Errorf("%q, want %q", testfileSit.Name, "testfile.sit")
+// // }
+// //}
//
-//func TestNewsCategoryList(t *testing.T) {
+// func TestNewsCategoryList(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
// client := clients[0]
//
@@ -344,9 +344,9 @@ package hotline
// if diff != "" {
// t.Fatalf(diff)
// }
-//}
+// }
//
-//func TestNestedNewsCategoryList(t *testing.T) {
+// func TestNestedNewsCategoryList(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
// client := clients[0]
// newsPath := NewsPath{
@@ -381,9 +381,9 @@ package hotline
// if diff != "" {
// t.Fatalf(diff)
// }
-//}
+// }
//
-//func TestFileDownload(t *testing.T) {
+// func TestFileDownload(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
// client := clients[0]
//
@@ -436,9 +436,9 @@ package hotline
// t.Errorf("TestFileDownload: fieldTransferSize: %s: got %#v, want %#v", test.fileName, got, test.want.transferSize)
// }
// }
-//}
+// }
//
-//func TestFileUpload(t *testing.T) {
+// func TestFileUpload(t *testing.T) {
// clients, _ := StartTestServerWithClients(2)
// client := clients[0]
//
@@ -479,10 +479,10 @@ package hotline
// }
// }
// }
-//}
+// }
//
-//// TODO: Make canonical
-//func TestNewUser(t *testing.T) {
+// // TODO: Make canonical
+// func TestNewUser(t *testing.T) {
// srv, port := StartTestServer()
//
// var tests = []struct {
@@ -604,9 +604,9 @@ package hotline
// test.teardown()
// }
// }
-//}
+// }
//
-//func TestDeleteUser(t *testing.T) {
+// func TestDeleteUser(t *testing.T) {
// srv, port := StartTestServer()
//
// var tests = []transactionTest{
@@ -676,9 +676,9 @@ package hotline
//
// test.Teardown(srv)
// }
-//}
+// }
//
-//func TestDeleteFile(t *testing.T) {
+// func TestDeleteFile(t *testing.T) {
// srv, port := StartTestServer()
//
// var tests = []transactionTest{
@@ -769,9 +769,9 @@ package hotline
//
// test.Teardown(srv)
// }
-//}
+// }
//
-//func Test_authorize(t *testing.T) {
+// func Test_authorize(t *testing.T) {
// accessBitmap := big.NewInt(int64(0))
// accessBitmap.SetBit(accessBitmap, accessCreateFolder, 1)
// fmt.Printf("%v %b %x\n", accessBitmap, accessBitmap, accessBitmap)
@@ -802,4 +802,4 @@ package hotline
// }
// })
// }
-//}
+// }
diff --git a/hotline/tracker.go b/hotline/tracker.go
index 4ee192a..0825fc7 100644
--- a/hotline/tracker.go
+++ b/hotline/tracker.go
@@ -49,9 +49,6 @@ func register(tracker string, tr TrackerRegistration) error {
return nil
}
-type ServerListing struct {
-}
-
const trackerTimeout = 5 * time.Second
// All string values use 8-bit ASCII character set encoding.
@@ -67,10 +64,10 @@ type TrackerHeader struct {
Version [2]byte // Old protocol (1) or new (2)
}
-//Message type 2 1 Sending list of servers
-//Message data size 2 Remaining size of this request
-//Number of servers 2 Number of servers in the server list
-//Number of servers 2 Same as previous field
+// Message type 2 1 Sending list of servers
+// Message data size 2 Remaining size of this request
+// Number of servers 2 Number of servers in the server list
+// Number of servers 2 Same as previous field
type ServerInfoHeader struct {
MsgType [2]byte // always has value of 1
MsgDataSize [2]byte // Remaining size of request
@@ -177,25 +174,6 @@ func (s *ServerRecord) Read(b []byte) (n int, err error) {
return 12 + nameLen + int(s.DescriptionSize), nil
}
-//
-//func (s *ServerRecord) UnmarshalBinary(b []byte) (err error) {
-// r := bytes.NewReader(b[:10])
-// if err := binary.Read(r, binary.BigEndian, s); err != nil {
-// return err
-// }
-//
-// copy(s.IPAddr[:], b[0:4])
-// s.Port = b[4:6]
-// s.NumUsers = b[6:8]
-// s.NameSize = b[10]
-// nameLen := int(b[10])
-// s.Name = b[11 : 11+nameLen]
-// s.DescriptionSize = b[11+nameLen]
-// s.Description = b[12+nameLen : 12+nameLen+int(s.DescriptionSize)]
-//
-// return nil
-//}
-
func (s *ServerRecord) PortInt() int {
data := binary.BigEndian.Uint16(s.Port[:])
return int(data)
diff --git a/hotline/transaction_handlers.go b/hotline/transaction_handlers.go
index ade846e..91a4c0d 100644
--- a/hotline/transaction_handlers.go
+++ b/hotline/transaction_handlers.go
@@ -237,8 +237,8 @@ var TransactionHandlers = map[uint16]TransactionType{
},
tranSendInstantMsg: {
Access: accessAlwaysAllow,
- //Access: accessSendPrivMsg,
- //DenyMsg: "You are not allowed to send private messages",
+ // Access: accessSendPrivMsg,
+ // DenyMsg: "You are not allowed to send private messages",
Name: "tranSendInstantMsg",
Handler: HandleSendInstantMsg,
RequiredFields: []requiredField{
@@ -357,13 +357,13 @@ func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err erro
// 101 Data Optional
// 214 Quoting message Optional
//
-//Fields used in the reply:
+// Fields used in the reply:
// None
func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
msg := t.GetField(fieldData)
ID := t.GetField(fieldUserID)
// TODO: Implement reply quoting
- //options := transaction.GetField(hotline.fieldOptions)
+ // options := transaction.GetField(hotline.fieldOptions)
res = append(res,
*NewTransaction(
@@ -377,23 +377,18 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er
)
id, _ := byteToInt(ID.Data)
- //keys := make([]uint16, 0, len(cc.Server.Clients))
- //for k := range cc.Server.Clients {
- // keys = append(keys, k)
- //}
-
otherClient := cc.Server.Clients[uint16(id)]
if otherClient == nil {
return res, errors.New("ohno")
}
// Respond with auto reply if other client has it enabled
- if len(*otherClient.AutoReply) > 0 {
+ if len(otherClient.AutoReply) > 0 {
res = append(res,
*NewTransaction(
tranServerMsg,
cc.ID,
- NewField(fieldData, *otherClient.AutoReply),
+ NewField(fieldData, otherClient.AutoReply),
NewField(fieldUserName, otherClient.UserName),
NewField(fieldUserID, *otherClient.ID),
NewField(fieldOptions, []byte{0, 1}),
@@ -450,7 +445,7 @@ func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e
return nil, err
}
- //fileComment := t.GetField(fieldFileComment).Data
+ // fileComment := t.GetField(fieldFileComment).Data
fileNewName := t.GetField(fieldFileNewName).Data
if fileNewName != nil {
@@ -662,8 +657,8 @@ func HandleSetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error
}
func HandleGetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- userLogin := string(t.GetField(fieldUserLogin).Data)
- account := cc.Server.Accounts[userLogin]
+ // userLogin := string(t.GetField(fieldUserLogin).Data)
+ account := cc.Server.Accounts[string(t.GetField(fieldUserLogin).Data)]
if account == nil {
errorT := cc.NewErrReply(t, "Account does not exist.")
res = append(res, errorT)
@@ -854,9 +849,9 @@ func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err er
// Check auto response
if optBitmap.Bit(autoResponse) == 1 {
- *cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
+ cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
} else {
- *cc.AutoReply = []byte{}
+ cc.AutoReply = []byte{}
}
_, _ = cc.notifyNewUserHasJoined()
@@ -1286,6 +1281,9 @@ func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, er
}
fullFilePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(fieldFilePath).Data, t.GetField(fieldFileName).Data)
+ if err != nil {
+ return res, err
+ }
transferSize, err := CalcTotalSize(fullFilePath)
if err != nil {
@@ -1385,9 +1383,9 @@ func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction,
// Check auto response
if optBitmap.Bit(autoResponse) == 1 {
- *cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
+ cc.AutoReply = t.GetField(fieldAutomaticResponse).Data
} else {
- *cc.AutoReply = []byte{}
+ cc.AutoReply = []byte{}
}
}
diff --git a/hotline/transaction_handlers_test.go b/hotline/transaction_handlers_test.go
index a83f4cc..5ffe45c 100644
--- a/hotline/transaction_handlers_test.go
+++ b/hotline/transaction_handlers_test.go
@@ -579,7 +579,7 @@ func TestHandleNewFolder(t *testing.T) {
),
},
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
FS = mfs
@@ -613,7 +613,7 @@ func TestHandleNewFolder(t *testing.T) {
),
},
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
FS = mfs
@@ -650,7 +650,7 @@ func TestHandleNewFolder(t *testing.T) {
),
},
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
FS = mfs
@@ -675,7 +675,7 @@ func TestHandleNewFolder(t *testing.T) {
),
},
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
FS = mfs
@@ -717,7 +717,7 @@ func TestHandleNewFolder(t *testing.T) {
),
},
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
FS = mfs
@@ -851,9 +851,9 @@ func TestHandleUploadFile(t *testing.T) {
t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !tranAssertEqual(t, tt.wantRes, gotRes) {
- t.Errorf("HandleUploadFile() gotRes = %v, want %v", gotRes, tt.wantRes)
- }
+
+ tranAssertEqual(t, tt.wantRes, gotRes)
+
})
}
}
@@ -873,7 +873,7 @@ func TestHandleMakeAlias(t *testing.T) {
{
name: "with valid input and required permissions",
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
path, _ := os.Getwd()
mfs.On(
"Symlink",
@@ -924,7 +924,7 @@ func TestHandleMakeAlias(t *testing.T) {
{
name: "when symlink returns an error",
setup: func() {
- mfs := MockFileStore{}
+ mfs := &MockFileStore{}
path, _ := os.Getwd()
mfs.On(
"Symlink",
diff --git a/hotline/ui.go b/hotline/ui.go
index d0fd6c0..79cb5e6 100644
--- a/hotline/ui.go
+++ b/hotline/ui.go
@@ -231,7 +231,7 @@ func (ui *UI) renderJoinServerForm(name, server, login, password, backPage strin
joinServerForm.
// AddInputField("Name", server, 0, func(textToCheck string, lastChar rune) bool {
// return false
- //}, nil).
+ // }, nil).
AddInputField("Server", server, 0, nil, nil).
AddInputField("Login", login, 0, nil, nil).
AddPasswordField("Password", password, 0, '*', nil).
@@ -370,7 +370,7 @@ func (ui *UI) renderServerUI() *tview.Flex {
newsPostForm := tview.NewForm().
SetButtonsAlign(tview.AlignRight).
- //AddButton("Cancel", nil). // TODO: implement cancel button behavior
+ // AddButton("Cancel", nil). // TODO: implement cancel button behavior
AddButton("Send", nil)
newsPostForm.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
@@ -438,7 +438,7 @@ func (ui *UI) renderServerUI() *tview.Flex {
SetDirection(tview.FlexRow).
AddItem(nil, 0, 1, false).
AddItem(newsFlex, 15, 1, true).
- //AddItem(newsPostForm, 3, 0, false).
+ // AddItem(newsPostForm, 3, 0, false).
AddItem(nil, 0, 1, false), 40, 1, false).
AddItem(nil, 0, 1, false)
diff --git a/hotline/user.go b/hotline/user.go
index 5d09b2d..00fe98c 100644
--- a/hotline/user.go
+++ b/hotline/user.go
@@ -2,6 +2,8 @@ package hotline
import (
"encoding/binary"
+ "golang.org/x/crypto/bcrypt"
+ "log"
)
// User flags are stored as a 2 byte bitmap with the following values:
@@ -68,3 +70,18 @@ func negateString(clearText []byte) []byte {
}
return obfuText
}
+
+func hashAndSalt(pwd []byte) string {
+ // Use GenerateFromPassword to hash & salt pwd.
+ // MinCost is just an integer constant provided by the bcrypt
+ // package along with DefaultCost & MaxCost.
+ // The cost can be any value you want provided it isn't lower
+ // than the MinCost (4)
+ hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
+ if err != nil {
+ log.Println(err)
+ }
+ // GenerateFromPassword returns a byte slice so we need to
+ // convert the bytes to a string and return it
+ return string(hash)
+}