5 "golang.org/x/crypto/bcrypt"
10 type byClientID []*ClientConn
12 func (s byClientID) Len() int {
16 func (s byClientID) Swap(i, j int) {
17 s[i], s[j] = s[j], s[i]
20 func (s byClientID) Less(i, j int) bool {
21 return s[i].uint16ID() < s[j].uint16ID()
24 const template = `Nickname: %s
29 -------- File Downloads ---------
33 ------- Folder Downloads --------
37 --------- File Uploads ----------
41 -------- Folder Uploads ---------
45 ------- Waiting Downloads -------
51 // ClientConn represents a client connected to a Server
52 type ClientConn struct {
53 Connection io.ReadWriteCloser
65 Transfers map[int][]*FileTransfer
69 func (cc *ClientConn) sendAll(t int, fields ...Field) {
70 for _, c := range sortedClients(cc.Server.Clients) {
71 cc.Server.outbox <- *NewTransaction(t, c.ID, fields...)
75 func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
76 requestNum := binary.BigEndian.Uint16(transaction.Type)
77 if handler, ok := TransactionHandlers[requestNum]; ok {
78 for _, reqField := range handler.RequiredFields {
79 field := transaction.GetField(reqField.ID)
81 // Validate that required field is present
83 cc.Server.Logger.Errorw(
84 "Missing required field",
85 "Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
90 if len(field.Data) < reqField.minLen {
91 cc.Server.Logger.Infow(
92 "Field does not meet minLen",
93 "Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
99 cc.Server.Logger.Infow(
100 "Received Transaction",
101 "login", cc.Account.Login,
102 "name", string(cc.UserName),
103 "RequestType", handler.Name,
106 transactions, err := handler.Handler(cc, transaction)
110 for _, t := range transactions {
111 cc.Server.outbox <- t
114 cc.Server.Logger.Errorw(
115 "Unimplemented transaction type received",
116 "UserName", string(cc.UserName), "RequestID", requestNum,
121 defer cc.Server.mux.Unlock()
123 if requestNum != tranKeepAlive {
124 // reset the user idle timer
127 // if user was previously idle, mark as not idle and notify other connected clients that
128 // the user is no longer away
130 flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(*cc.Flags)))
131 flagBitmap.SetBit(flagBitmap, userFlagAway, 0)
132 binary.BigEndian.PutUint16(*cc.Flags, uint16(flagBitmap.Int64()))
136 tranNotifyChangeUser,
137 NewField(fieldUserID, *cc.ID),
138 NewField(fieldUserFlags, *cc.Flags),
139 NewField(fieldUserName, cc.UserName),
140 NewField(fieldUserIconID, *cc.Icon),
148 func (cc *ClientConn) Authenticate(login string, password []byte) bool {
149 if account, ok := cc.Server.Accounts[login]; ok {
150 return bcrypt.CompareHashAndPassword([]byte(account.Password), password) == nil
156 func (cc *ClientConn) uint16ID() uint16 {
157 id, _ := byteToInt(*cc.ID)
161 // Authorize checks if the user account has the specified permission
162 func (cc *ClientConn) Authorize(access int) bool {
167 accessBitmap := big.NewInt(int64(binary.BigEndian.Uint64(*cc.Account.Access)))
169 return accessBitmap.Bit(63-access) == 1
172 // Disconnect notifies other clients that a client has disconnected
173 func (cc *ClientConn) Disconnect() {
175 defer cc.Server.mux.Unlock()
177 delete(cc.Server.Clients, binary.BigEndian.Uint16(*cc.ID))
179 cc.notifyOthers(*NewTransaction(tranNotifyDeleteUser, nil, NewField(fieldUserID, *cc.ID)))
181 if err := cc.Connection.Close(); err != nil {
182 cc.Server.Logger.Errorw("error closing client connection", "RemoteAddr", cc.RemoteAddr)
186 // notifyOthers sends transaction t to other clients connected to the server
187 func (cc *ClientConn) notifyOthers(t Transaction) {
188 for _, c := range sortedClients(cc.Server.Clients) {
189 if c.ID != cc.ID && c.Agreed {
191 cc.Server.outbox <- t
196 // NewReply returns a reply Transaction with fields for the ClientConn
197 func (cc *ClientConn) NewReply(t *Transaction, fields ...Field) Transaction {
198 reply := Transaction{
204 ErrorCode: []byte{0, 0, 0, 0},
211 // NewErrReply returns an error reply Transaction with errMsg
212 func (cc *ClientConn) NewErrReply(t *Transaction, errMsg string) Transaction {
219 ErrorCode: []byte{0, 0, 0, 1},
221 NewField(fieldError, []byte(errMsg)),