From: Jeff Halter Date: Sat, 28 May 2022 18:35:50 +0000 (-0700) Subject: Misc cleanup X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/commitdiff_plain/aebc4d3647b9823ae8cbb57b21b1af83bfd011fb?hp=decc2fbf5db4a05aec93462ad35d890930bddd04 Misc cleanup * Removed some unnecessary user of pointers * Removed dead cruft * Reorganized code --- diff --git a/cmd/mobius-hotline-server/main.go b/cmd/mobius-hotline-server/main.go index cf69ea8..aa1f015 100644 --- a/cmd/mobius-hotline-server/main.go +++ b/cmd/mobius-hotline-server/main.go @@ -44,7 +44,7 @@ func main() { logger.Fatalw("Configuration directory not found", "path", configDir) } - hotline.FS = hotline.OSFileStore{} + hotline.FS = &hotline.OSFileStore{} srv, err := hotline.NewServer(*configDir, "", *basePort, logger) if err != nil { 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 TestChatSend(t *testing.T) { +// //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) { // //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") -//// } -////} -// -//func TestNewsCategoryList(t *testing.T) { +// } +// +// // 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) { // 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) +}