package hotline
import (
- "bytes"
"encoding/binary"
- "errors"
"golang.org/x/crypto/bcrypt"
"math/big"
"net"
Flags *[]byte
UserName []byte
Account *Account
- IdleTime *int
+ IdleTime int
Server *Server
Version *[]byte
Idle bool
- AutoReply *[]byte
+ AutoReply []byte
Transfers map[int][]*FileTransfer
+ Agreed bool
}
func (cc *ClientConn) sendAll(t int, fields ...Field) {
// 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,
)
cc.Server.mux.Lock()
defer cc.Server.mux.Unlock()
- // if user was idle and this is a non-keepalive transaction
- if *cc.IdleTime > userIdleSeconds && requestNum != tranKeepAlive {
- flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(*cc.Flags)))
- flagBitmap.SetBit(flagBitmap, userFlagAway, 0)
- binary.BigEndian.PutUint16(*cc.Flags, uint16(flagBitmap.Int64()))
- cc.Idle = false
- //*cc.IdleTime = 0
-
- cc.sendAll(
- tranNotifyChangeUser,
- NewField(fieldUserID, *cc.ID),
- NewField(fieldUserFlags, *cc.Flags),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserIconID, *cc.Icon),
- )
-
- //return nil
+ if requestNum != tranKeepAlive {
+ // reset the user idle timer
+ cc.IdleTime = 0
+
+ // if user was previously idle, mark as not idle and notify other connected clients that
+ // the user is no longer away
+ if cc.Idle {
+ flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(*cc.Flags)))
+ flagBitmap.SetBit(flagBitmap, userFlagAway, 0)
+ binary.BigEndian.PutUint16(*cc.Flags, uint16(flagBitmap.Int64()))
+ cc.Idle = false
+
+ cc.sendAll(
+ tranNotifyChangeUser,
+ NewField(fieldUserID, *cc.ID),
+ NewField(fieldUserFlags, *cc.Flags),
+ NewField(fieldUserName, cc.UserName),
+ NewField(fieldUserIconID, *cc.Icon),
+ )
+ }
}
- // TODO: Don't we need to skip this if requestNum == tranKeepalive ??
- *cc.IdleTime = 0
-
return nil
}
delete(cc.Server.Clients, binary.BigEndian.Uint16(*cc.ID))
- cc.NotifyOthers(*NewTransaction(tranNotifyDeleteUser, nil, NewField(fieldUserID, *cc.ID)))
+ cc.notifyOthers(*NewTransaction(tranNotifyDeleteUser, nil, NewField(fieldUserID, *cc.ID)))
if err := cc.Connection.Close(); err != nil {
cc.Server.Logger.Errorw("error closing client connection", "RemoteAddr", cc.Connection.RemoteAddr())
}
}
-// NotifyOthers sends transaction t to other clients connected to the server
-func (cc ClientConn) NotifyOthers(t Transaction) {
+// notifyOthers sends transaction t to other clients connected to the server
+func (cc ClientConn) notifyOthers(t Transaction) {
for _, c := range sortedClients(cc.Server.Clients) {
- if c.ID != cc.ID {
+ if c.ID != cc.ID && c.Agreed {
t.clientID = c.ID
cc.Server.outbox <- t
}
}
}
-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{