]>
Commit | Line | Data |
---|---|---|
6988a057 JH |
1 | package hotline |
2 | ||
3 | import ( | |
4 | "encoding/binary" | |
9cf66aea | 5 | "io" |
a2ef262a | 6 | "math/big" |
9cf66aea | 7 | "slices" |
6988a057 JH |
8 | ) |
9 | ||
b1658a46 | 10 | // User flags are stored as a 2 byte bitmap and represent various user states |
6988a057 | 11 | const ( |
a2ef262a JH |
12 | UserFlagAway = 0 // User is away |
13 | UserFlagAdmin = 1 // User is admin | |
14 | UserFlagRefusePM = 2 // User refuses private messages | |
15 | UserFlagRefusePChat = 3 // User refuses private chat | |
6988a057 JH |
16 | ) |
17 | ||
d9bc63a1 | 18 | // User options are sent from clients and represent options set in the client's preferences. |
003a743e | 19 | const ( |
a2ef262a JH |
20 | UserOptRefusePM = 0 // User has "Refuse private messages" pref set |
21 | UserOptRefuseChat = 1 // User has "Refuse private chat" pref set | |
22 | UserOptAutoResponse = 2 // User has "Automatic response" pref set | |
003a743e JH |
23 | ) |
24 | ||
a2ef262a JH |
25 | type UserFlags [2]byte |
26 | ||
d9bc63a1 JH |
27 | func (f *UserFlags) IsSet(i int) bool { |
28 | flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(f[:]))) | |
a2ef262a JH |
29 | return flagBitmap.Bit(i) == 1 |
30 | } | |
31 | ||
d9bc63a1 JH |
32 | func (f *UserFlags) Set(i int, newVal uint) { |
33 | flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(f[:]))) | |
a2ef262a | 34 | flagBitmap.SetBit(flagBitmap, i, newVal) |
d9bc63a1 | 35 | binary.BigEndian.PutUint16(f[:], uint16(flagBitmap.Int64())) |
a2ef262a JH |
36 | } |
37 | ||
6988a057 | 38 | type User struct { |
a2ef262a | 39 | ID [2]byte |
6988a057 JH |
40 | Icon []byte // Size 2 |
41 | Flags []byte // Size 2 | |
42 | Name string // Variable length user name | |
95159e55 JH |
43 | |
44 | readOffset int // Internal offset to track read progress | |
6988a057 JH |
45 | } |
46 | ||
9cf66aea | 47 | func (u *User) Read(p []byte) (int, error) { |
6988a057 JH |
48 | nameLen := make([]byte, 2) |
49 | binary.BigEndian.PutUint16(nameLen, uint16(len(u.Name))) | |
50 | ||
51 | if len(u.Icon) == 4 { | |
52 | u.Icon = u.Icon[2:] | |
53 | } | |
54 | ||
55 | if len(u.Flags) == 4 { | |
56 | u.Flags = u.Flags[2:] | |
57 | } | |
58 | ||
95159e55 | 59 | b := slices.Concat( |
a2ef262a | 60 | u.ID[:], |
9cf66aea JH |
61 | u.Icon, |
62 | u.Flags, | |
63 | nameLen, | |
64 | []byte(u.Name), | |
95159e55 JH |
65 | ) |
66 | ||
67 | if u.readOffset >= len(b) { | |
68 | return 0, io.EOF // All bytes have been read | |
69 | } | |
70 | ||
71 | n := copy(p, b) | |
a2ef262a | 72 | u.readOffset = n |
95159e55 | 73 | |
a2ef262a | 74 | return n, nil |
6988a057 JH |
75 | } |
76 | ||
9cf66aea JH |
77 | func (u *User) Write(p []byte) (int, error) { |
78 | namelen := int(binary.BigEndian.Uint16(p[6:8])) | |
a2ef262a | 79 | u.ID = [2]byte(p[0:2]) |
9cf66aea JH |
80 | u.Icon = p[2:4] |
81 | u.Flags = p[4:6] | |
82 | u.Name = string(p[8 : 8+namelen]) | |
83 | ||
84 | return 8 + namelen, nil | |
6988a057 JH |
85 | } |
86 | ||
fd740bc4 | 87 | // EncodeString takes []byte s containing cleartext and rotates by 255 into obfuscated cleartext. |
b25c4a19 JH |
88 | // The Hotline protocol uses this format for sending passwords over network. |
89 | // Not secure, but hey, it was the 90s! | |
fd740bc4 | 90 | func EncodeString(clearText []byte) []byte { |
b25c4a19 JH |
91 | obfuText := make([]byte, len(clearText)) |
92 | for i := 0; i < len(clearText); i++ { | |
93 | obfuText[i] = 255 - clearText[i] | |
6988a057 | 94 | } |
b25c4a19 | 95 | return obfuText |
6988a057 | 96 | } |