package hotline
-import (
- "encoding/binary"
- "math/big"
-)
-
const (
accessDeleteFile = 0 // File System Maintenance: Can Delete Files
accessUploadFile = 1 // File System Maintenance: Can Upload Files
bits[i/8] |= 1 << uint(7-i%8)
}
-// authorize checks if 64 bit access slice contain has accessBit set
-// TODO: refactor to use accessBitmap type
-func authorize(access *[]byte, accessBit int) bool {
- bits := big.NewInt(int64(binary.BigEndian.Uint64(*access)))
-
- return bits.Bit(63-accessBit) == 1
+func (bits *accessBitmap) IsSet(i int) bool {
+ return bits[i/8]&(1<<uint(7-i%8)) != 0
}
--- /dev/null
+package hotline
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func Test_accessBitmap_IsSet(t *testing.T) {
+ type args struct {
+ i int
+ }
+ tests := []struct {
+ name string
+ bits accessBitmap
+ args args
+ want bool
+ }{
+ {
+ name: "returns true when bit is set",
+ bits: func() (access accessBitmap) {
+ access.Set(22)
+ return access
+ }(),
+ args: args{i: 22},
+ want: true,
+ },
+ {
+ name: "returns false when bit is unset",
+ bits: accessBitmap{},
+ args: args{i: 22},
+ want: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, tt.bits.IsSet(tt.args.i), "IsSet(%v)", tt.args.i)
+ })
+ }
+}
const GuestAccount = "guest" // default account used when no login is provided for a connection
type Account struct {
- Login string `yaml:"Login"`
- Name string `yaml:"Name"`
- Password string `yaml:"Password"`
- Access *[]byte `yaml:"Access"` // 8 byte bitmap
+ Login string `yaml:"Login"`
+ Name string `yaml:"Name"`
+ Password string `yaml:"Password"`
+ Access accessBitmap `yaml:"Access"`
}
// Read implements io.Reader interface for Account
fields := []Field{
NewField(fieldUserName, []byte(a.Name)),
NewField(fieldUserLogin, negateString([]byte(a.Login))),
- NewField(fieldUserAccess, *a.Access),
+ NewField(fieldUserAccess, a.Access[:]),
}
if bcrypt.CompareHashAndPassword([]byte(a.Password), []byte("")) != nil {
// Authorize checks if the user account has the specified permission
func (cc *ClientConn) Authorize(access int) bool {
- i := big.NewInt(int64(binary.BigEndian.Uint64(*cc.Account.Access)))
-
- return i.Bit(63-access) == 1
+ return cc.Account.Access.IsSet(access)
}
// Disconnect notifies other clients that a client has disconnected
}
// NewUser creates a new user account entry in the server map and config file
-func (s *Server) NewUser(login, name, password string, access []byte) error {
+func (s *Server) NewUser(login, name, password string, access accessBitmap) error {
s.mux.Lock()
defer s.mux.Unlock()
Login: login,
Name: name,
Password: hashAndSalt([]byte(password)),
- Access: &access,
+ Access: access,
}
out, err := yaml.Marshal(&account)
if err != nil {
return s.FS.WriteFile(filepath.Join(s.ConfigDir, "Users", login+".yaml"), out, 0666)
}
-func (s *Server) UpdateUser(login, newLogin, name, password string, access []byte) error {
+func (s *Server) UpdateUser(login, newLogin, name, password string, access accessBitmap) error {
s.mux.Lock()
defer s.mux.Unlock()
}
account := s.Accounts[newLogin]
- account.Access = &access
+ account.Access = access
account.Name = name
account.Password = password
)
// Send user access privs so client UI knows how to behave
- c.Server.outbox <- *NewTransaction(tranUserAccess, c.ID, NewField(fieldUserAccess, *c.Account.Access))
+ c.Server.outbox <- *NewTransaction(tranUserAccess, c.ID, NewField(fieldUserAccess, c.Account.Access[:]))
// Accounts with accessNoAgreement do not receive the server agreement on login. The behavior is different between
// client versions. For 1.2.3 client, we do not send tranShowAgreement. For other client versions, we send
}
func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessSendChat) {
+ if !cc.Authorize(accessSendChat) {
res = append(res, cc.NewErrReply(t, "You are not allowed to participate in chat."))
return res, err
}
for _, c := range sortedClients(cc.Server.Clients) {
// Filter out clients that do not have the read chat permission
- if authorize(c.Account.Access, accessReadChat) {
+ if c.Authorize(accessReadChat) {
res = append(res, *NewTransaction(tranChatMsg, c.ID, NewField(fieldData, []byte(formattedMsg))))
}
}
if t.GetField(fieldFileComment).Data != nil {
switch mode := fi.Mode(); {
case mode.IsDir():
- if !authorize(cc.Account.Access, accessSetFolderComment) {
+ if !cc.Authorize(accessSetFolderComment) {
res = append(res, cc.NewErrReply(t, "You are not allowed to set comments for folders."))
return res, err
}
case mode.IsRegular():
- if !authorize(cc.Account.Access, accessSetFileComment) {
+ if !cc.Authorize(accessSetFileComment) {
res = append(res, cc.NewErrReply(t, "You are not allowed to set comments for files."))
return res, err
}
if fileNewName != nil {
switch mode := fi.Mode(); {
case mode.IsDir():
- if !authorize(cc.Account.Access, accessRenameFolder) {
+ if !cc.Authorize(accessRenameFolder) {
res = append(res, cc.NewErrReply(t, "You are not allowed to rename folders."))
return res, err
}
return res, err
}
case mode.IsRegular():
- if !authorize(cc.Account.Access, accessRenameFile) {
+ if !cc.Authorize(accessRenameFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to rename files."))
return res, err
}
switch mode := fi.Mode(); {
case mode.IsDir():
- if !authorize(cc.Account.Access, accessDeleteFolder) {
+ if !cc.Authorize(accessDeleteFolder) {
res = append(res, cc.NewErrReply(t, "You are not allowed to delete folders."))
return res, err
}
case mode.IsRegular():
- if !authorize(cc.Account.Access, accessDeleteFile) {
+ if !cc.Authorize(accessDeleteFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to delete files."))
return res, err
}
}
switch mode := fi.Mode(); {
case mode.IsDir():
- if !authorize(cc.Account.Access, accessMoveFolder) {
+ if !cc.Authorize(accessMoveFolder) {
res = append(res, cc.NewErrReply(t, "You are not allowed to move folders."))
return res, err
}
case mode.IsRegular():
- if !authorize(cc.Account.Access, accessMoveFile) {
+ if !cc.Authorize(accessMoveFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to move files."))
return res, err
}
}
func HandleNewFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessCreateFolder) {
+ if !cc.Authorize(accessCreateFolder) {
res = append(res, cc.NewErrReply(t, "You are not allowed to create folders."))
return res, err
}
}
func HandleSetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessModifyUser) {
+ if !cc.Authorize(accessModifyUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to modify accounts."))
return res, err
}
newAccessLvl := t.GetField(fieldUserAccess).Data
account := cc.Server.Accounts[login]
- account.Access = &newAccessLvl
account.Name = userName
+ copy(account.Access[:], newAccessLvl)
// If the password field is cleared in the Hotline edit user UI, the SetUser transaction does
// not include fieldUserPassword
res = append(res, *newT)
flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(c.Flags)))
- if authorize(c.Account.Access, accessDisconUser) {
+ if cc.Authorize(accessDisconUser) {
flagBitmap.SetBit(flagBitmap, userFlagAdmin, 1)
} else {
flagBitmap.SetBit(flagBitmap, userFlagAdmin, 0)
}
func HandleGetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessOpenUser) {
+ if !cc.Authorize(accessOpenUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to view accounts."))
return res, err
}
NewField(fieldUserName, []byte(account.Name)),
NewField(fieldUserLogin, negateString(t.GetField(fieldUserLogin).Data)),
NewField(fieldUserPassword, []byte(account.Password)),
- NewField(fieldUserAccess, *account.Access),
+ NewField(fieldUserAccess, account.Access[:]),
))
return res, err
}
func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessOpenUser) {
+ if !cc.Authorize(accessOpenUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to view accounts."))
return res, err
}
login := DecodeUserString(getField(fieldData, &subFields).Data)
cc.logger.Infow("DeleteUser", "login", login)
- if !authorize(cc.Account.Access, accessDeleteUser) {
+ if !cc.Authorize(accessDeleteUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to delete accounts."))
return res, err
}
cc.logger.Infow("UpdateUser", "login", login)
// account dataFile, so this is an update action
- if !authorize(cc.Account.Access, accessModifyUser) {
+ if !cc.Authorize(accessModifyUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to modify accounts."))
return res, err
}
}
if getField(fieldUserAccess, &subFields) != nil {
- acc.Access = &getField(fieldUserAccess, &subFields).Data
+ copy(acc.Access[:], getField(fieldUserAccess, &subFields).Data)
}
err = cc.Server.UpdateUser(
DecodeUserString(getField(fieldUserLogin, &subFields).Data),
string(getField(fieldUserName, &subFields).Data),
acc.Password,
- *acc.Access,
+ acc.Access,
)
if err != nil {
return res, err
} else {
cc.logger.Infow("CreateUser", "login", login)
- if !authorize(cc.Account.Access, accessCreateUser) {
+ if !cc.Authorize(accessCreateUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to create new accounts."))
return res, err
}
- err := cc.Server.NewUser(
- login,
- string(getField(fieldUserName, &subFields).Data),
- string(getField(fieldUserPassword, &subFields).Data),
- getField(fieldUserAccess, &subFields).Data,
- )
+ newAccess := accessBitmap{}
+ copy(newAccess[:], getField(fieldUserAccess, &subFields).Data[:])
+
+ err := cc.Server.NewUser(login, string(getField(fieldUserName, &subFields).Data), string(getField(fieldUserPassword, &subFields).Data), newAccess)
if err != nil {
return []Transaction{}, err
}
// HandleNewUser creates a new user account
func HandleNewUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessCreateUser) {
+ if !cc.Authorize(accessCreateUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to create new accounts."))
return res, err
}
return res, err
}
- if err := cc.Server.NewUser(
- login,
- string(t.GetField(fieldUserName).Data),
- string(t.GetField(fieldUserPassword).Data),
- t.GetField(fieldUserAccess).Data,
- ); err != nil {
+ newAccess := accessBitmap{}
+ copy(newAccess[:], t.GetField(fieldUserAccess).Data[:])
+
+ if err := cc.Server.NewUser(login, string(t.GetField(fieldUserName).Data), string(t.GetField(fieldUserPassword).Data), newAccess); err != nil {
return []Transaction{}, err
}
}
func HandleDeleteUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessDeleteUser) {
+ if !cc.Authorize(accessDeleteUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to delete accounts."))
return res, err
}
// HandleUserBroadcast sends an Administrator Message to all connected clients of the server
func HandleUserBroadcast(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessBroadcast) {
+ if !cc.Authorize(accessBroadcast) {
res = append(res, cc.NewErrReply(t, "You are not allowed to send broadcast messages."))
return res, err
}
// 102 User name
// 101 Data User info text string
func HandleGetClientInfoText(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessGetClientInfo) {
+ if !cc.Authorize(accessGetClientInfo) {
res = append(res, cc.NewErrReply(t, "You are not allowed to get client info."))
return res, err
}
// Fields used in this request:
// 101 Data
func HandleTranOldPostNews(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsPostArt) {
+ if !cc.Authorize(accessNewsPostArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to post news."))
return res, err
}
}
func HandleDisconnectUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessDisconUser) {
+ if !cc.Authorize(accessDisconUser) {
res = append(res, cc.NewErrReply(t, "You are not allowed to disconnect users."))
return res, err
}
clientConn := cc.Server.Clients[binary.BigEndian.Uint16(t.GetField(fieldUserID).Data)]
- if authorize(clientConn.Account.Access, accessCannotBeDiscon) {
+ if clientConn.Authorize(accessCannotBeDiscon) {
res = append(res, cc.NewErrReply(t, clientConn.Account.Login+" is not allowed to be disconnected."))
return res, err
}
// Fields used in the request:
// 325 News path (Optional)
func HandleGetNewsCatNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsReadArt) {
+ if !cc.Authorize(accessNewsReadArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to read news."))
return res, err
}
}
func HandleNewNewsCat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsCreateCat) {
+ if !cc.Authorize(accessNewsCreateCat) {
res = append(res, cc.NewErrReply(t, "You are not allowed to create news categories."))
return res, err
}
// 322 News category name
// 325 News path
func HandleNewNewsFldr(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsCreateFldr) {
+ if !cc.Authorize(accessNewsCreateFldr) {
res = append(res, cc.NewErrReply(t, "You are not allowed to create news folders."))
return res, err
}
// Reply fields:
// 321 News article list data Optional
func HandleGetNewsArtNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsReadArt) {
+ if !cc.Authorize(accessNewsReadArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to read news."))
return res, err
}
}
func HandleGetNewsArtData(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsReadArt) {
+ if !cc.Authorize(accessNewsReadArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to read news."))
return res, err
}
}
func HandleDelNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsDeleteArt) {
+ if !cc.Authorize(accessNewsDeleteArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to delete news articles."))
return res, err
}
// 327 News article data flavor Currently “text/plain”
// 333 News article data
func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsPostArt) {
+ if !cc.Authorize(accessNewsPostArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to post news articles."))
return res, err
}
// HandleGetMsgs returns the flat news data
func HandleGetMsgs(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessNewsReadArt) {
+ if !cc.Authorize(accessNewsReadArt) {
res = append(res, cc.NewErrReply(t, "You are not allowed to read news."))
return res, err
}
}
func HandleDownloadFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessDownloadFile) {
+ if !cc.Authorize(accessDownloadFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to download files."))
return res, err
}
// Download all files from the specified folder and sub-folders
func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessDownloadFile) {
+ if !cc.Authorize(accessDownloadFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to download folders."))
return res, err
}
}
// Handle special cases for Upload and Drop Box folders
- if !authorize(cc.Account.Access, accessUploadAnywhere) {
+ if !cc.Authorize(accessUploadAnywhere) {
if !fp.IsUploadDir() && !fp.IsDropbox() {
res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload of the folder \"%v\" because you are only allowed to upload to the \"Uploads\" folder.", string(t.GetField(fieldFileName).Data))))
return res, err
// Used only to resume download, currently has value 2"
// 108 File transfer size "Optional used if download is not resumed"
func HandleUploadFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessUploadFile) {
+ if !cc.Authorize(accessUploadFile) {
res = append(res, cc.NewErrReply(t, "You are not allowed to upload files."))
return res, err
}
}
// Handle special cases for Upload and Drop Box folders
- if !authorize(cc.Account.Access, accessUploadAnywhere) {
+ if !cc.Authorize(accessUploadAnywhere) {
if !fp.IsUploadDir() && !fp.IsDropbox() {
res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload of the file \"%v\" because you are only allowed to upload to the \"Uploads\" folder.", string(fileName))))
return res, err
}
// Handle special case for drop box folders
- if fp.IsDropbox() && !authorize(cc.Account.Access, accessViewDropBoxes) {
+ if fp.IsDropbox() && !cc.Authorize(accessViewDropBoxes) {
res = append(res, cc.NewErrReply(t, "You are not allowed to view drop boxes."))
return res, err
}
// HandleInviteNewChat invites users to new private chat
func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessOpenChat) {
+ if !cc.Authorize(accessOpenChat) {
res = append(res, cc.NewErrReply(t, "You are not allowed to request private chat."))
return res, err
}
}
func HandleInviteToChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessOpenChat) {
+ if !cc.Authorize(accessOpenChat) {
res = append(res, cc.NewErrReply(t, "You are not allowed to request private chat."))
return res, err
}
// Fields used in the reply:
// None
func HandleMakeAlias(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
- if !authorize(cc.Account.Access, accessMakeAlias) {
+ if !cc.Authorize(accessMakeAlias) {
res = append(res, cc.NewErrReply(t, "You are not allowed to make aliases."))
return res, err
}
ClientConn: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
ClientConn: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendChat)
- access := bits[:]
- return &access
+ return bits
}(),
},
UserName: []byte{0x00, 0x01},
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendChat)
- access := bits[:]
- return &access
+ return bits
}(),
},
UserName: []byte("Testy McTest"),
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 2},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendChat)
- access := bits[:]
- return &access
+ return bits
}(),
},
UserName: []byte{0x00, 0x01},
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
- },
+ Access: func() accessBitmap {
+ var bits accessBitmap
+ bits.Set(accessReadChat)
+ return bits
+ }()},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
+ Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
},
ID: &[]byte{0, 2},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendChat)
- access := bits[:]
- return &access
+ return bits
}(),
},
UserName: []byte{0x00, 0x01},
Clients: map[uint16]*ClientConn{
uint16(1): {
Account: &Account{
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
ID: &[]byte{0, 1},
},
uint16(2): {
Account: &Account{
- Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
+ Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
},
ID: &[]byte{0, 2},
},
uint16(3): {
Account: &Account{
- Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
+ Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
},
ID: &[]byte{0, 3},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCreateFolder)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCreateFolder)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCreateFolder)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCreateFolder)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCreateFolder)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
FileUpload: {},
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessUploadFile)
bits.Set(accessUploadAnywhere)
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
cc: &ClientConn{
logger: NewTestLogger(),
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessMakeAlias)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
cc: &ClientConn{
logger: NewTestLogger(),
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessMakeAlias)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
cc: &ClientConn{
logger: NewTestLogger(),
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessOpenUser)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
Login: "guest",
Name: "Guest",
Password: "password",
- Access: &[]byte{1},
+ Access: accessBitmap{},
},
},
},
NewField(fieldUserName, []byte("Guest")),
NewField(fieldUserLogin, negateString([]byte("guest"))),
NewField(fieldUserPassword, []byte("password")),
- NewField(fieldUserAccess, []byte{1}),
+ NewField(fieldUserAccess, []byte{0, 0, 0, 0, 0, 0, 0, 0}),
},
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessOpenUser)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDeleteUser)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
Login: "testuser",
Name: "Testy McTest",
Password: "password",
- Access: &[]byte{1},
+ Access: accessBitmap{},
},
},
FS: func() *MockFileStore {
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessNewsReadArt)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessOpenUser)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
Name: "guest",
Login: "guest",
Password: "zz",
- Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
},
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{},
FileDownload: {},
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDownloadFile)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
transfers: map[int]map[[4]byte]*FileTransfer{
FileDownload: {},
}, Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDownloadFile)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
Logger: NewTestLogger(),
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
},
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
},
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
uint16(1): {
Account: &Account{
Login: "unnamed",
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessCannotBeDiscon)
- access := bits[:]
- return &access
+ return bits
}(),
},
},
},
},
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDisconUser)
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendPrivMsg)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessSendPrivMsg)
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDeleteFile)
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
Server: &Server{
UserName: []byte("Testy McTest"),
RemoteAddr: "1.2.3.4:12345",
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessGetClientInfo)
- access := bits[:]
- return &access
+ return bits
}(),
Name: "test",
Login: "test",
UserName: []byte("Testy McTest"),
RemoteAddr: "1.2.3.4:12345",
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessGetClientInfo)
- access := bits[:]
- return &access
+ return bits
}(),
Name: "test",
Login: "test",
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
bits.Set(accessDisconUser)
bits.Set(accessAnyName)
- access := bits[:]
- return &access
+ return bits
}()},
Icon: []byte{0, 1},
Flags: []byte{0, 1},
args: args{
cc: &ClientConn{
Account: &Account{
- Access: func() *[]byte {
+ Access: func() accessBitmap {
var bits accessBitmap
- access := bits[:]
- return &access
+ return bits
}(),
},
ID: &[]byte{0, 1},