aboutsummaryrefslogtreecommitdiff
path: root/hotline/access.go
blob: 370f8c2fcfad92a8da17a8191c2490a35cc5afaf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package hotline

import "fmt"

const (
	AccessDeleteFile       = 0  // File System Maintenance: Can Delete Files
	AccessUploadFile       = 1  // File System Maintenance: Can Upload Files
	AccessDownloadFile     = 2  // File System Maintenance: Can Download Files
	AccessRenameFile       = 3  // File System Maintenance: Can Rename Files
	AccessMoveFile         = 4  // File System Maintenance: Can Move Files
	AccessCreateFolder     = 5  // File System Maintenance: Can Create Folders
	AccessDeleteFolder     = 6  // File System Maintenance: Can Delete Folders
	AccessRenameFolder     = 7  // File System Maintenance: Can Rename Folders
	AccessMoveFolder       = 8  // File System Maintenance: Can Move Folders
	AccessReadChat         = 9  // Chat: Can Read Chat
	AccessSendChat         = 10 // Chat: Can Send Chat
	AccessOpenChat         = 11 // Chat: Can Initial Private Chat
	AccessCloseChat        = 12 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessShowInList       = 13 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessCreateUser       = 14 // User Maintenance: Can Create Accounts
	AccessDeleteUser       = 15 // User Maintenance: Can Delete Accounts
	AccessOpenUser         = 16 // User Maintenance: Can Read Accounts
	AccessModifyUser       = 17 // User Maintenance: Can Modify Accounts
	AccessChangeOwnPass    = 18 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
	AccessNewsReadArt      = 20 // News: Can Read Articles
	AccessNewsPostArt      = 21 // News: Can Post Articles
	AccessDisconUser       = 22 // User Maintenance: Can Disconnect Users (Note: Turns username red in user list)
	AccessCannotBeDiscon   = 23 // User Maintenance: Cannot be Disconnected
	AccessGetClientInfo    = 24 // User Maintenance: Can Get User Info
	AccessUploadAnywhere   = 25 // File System Maintenance: Can Upload Anywhere
	AccessAnyName          = 26 // Miscellaneous: Can User Any Name
	AccessNoAgreement      = 27 // Miscellaneous: Don't Show Agreement
	AccessSetFileComment   = 28 // File System Maintenance: Can Comment Files
	AccessSetFolderComment = 29 // File System Maintenance: Can Comment Folders
	AccessViewDropBoxes    = 30 // File System Maintenance: Can View Drop Boxes
	AccessMakeAlias        = 31 // File System Maintenance: Can Make Aliases
	AccessBroadcast        = 32 // Messaging: Can Broadcast
	AccessNewsDeleteArt    = 33 // News: Can Delete Articles
	AccessNewsCreateCat    = 34 // News: Can Create Categories
	AccessNewsDeleteCat    = 35 // News: Can Delete Categories
	AccessNewsCreateFldr   = 36 // News: Can Create News Bundles
	AccessNewsDeleteFldr   = 37 // News: Can Delete News Bundles
	AccessUploadFolder     = 38 // File System Maintenance: Can Upload Folders
	AccessDownloadFolder   = 39 // File System Maintenance: Can Download Folders
	AccessSendPrivMsg      = 40 // Messaging: Can Send Messages (Note: 1.9 protocol doc incorrectly says this is bit 19)
)

type AccessBitmap [8]byte

func (bits *AccessBitmap) Set(i int) {
	bits[i/8] |= 1 << uint(7-i%8)
}

func (bits *AccessBitmap) IsSet(i int) bool {
	return bits[i/8]&(1<<uint(7-i%8)) != 0
}

func (bits *AccessBitmap) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var flags interface{}
	err := unmarshal(&flags)
	if err != nil {
		return fmt.Errorf("unmarshal access bitmap: %w", err)
	}

	switch v := flags.(type) {
	case []interface{}:
		// Mobius versions < v0.17.0 store the user access bitmap as an array of int values like:
		// [96, 112, 12, 32, 3, 128, 0, 0]
		// This case supports reading of user config files using this format.
		for i, v := range flags.([]interface{}) {
			bits[i] = byte(v.(int))
		}
	case map[string]interface{}:
		// Mobius versions >= v0.17.0 store the user access bitmap as map[string]bool to provide a human-readable view of
		// the account permissions.
		accessMap := map[string]int{
			"DeleteFile":           AccessDeleteFile,
			"UploadFile":           AccessUploadFile,
			"DownloadFile":         AccessDownloadFile,
			"RenameFile":           AccessRenameFile,
			"MoveFile":             AccessMoveFile,
			"CreateFolder":         AccessCreateFolder,
			"DeleteFolder":         AccessDeleteFolder,
			"RenameFolder":         AccessRenameFolder,
			"MoveFolder":           AccessMoveFolder,
			"ReadChat":             AccessReadChat,
			"SendChat":             AccessSendChat,
			"OpenChat":             AccessOpenChat,
			"CloseChat":            AccessCloseChat,
			"ShowInList":           AccessShowInList,
			"CreateUser":           AccessCreateUser,
			"DeleteUser":           AccessDeleteUser,
			"OpenUser":             AccessOpenUser,
			"ModifyUser":           AccessModifyUser,
			"ChangeOwnPass":        AccessChangeOwnPass,
			"NewsReadArt":          AccessNewsReadArt,
			"NewsPostArt":          AccessNewsPostArt,
			"DisconnectUser":       AccessDisconUser,
			"CannotBeDisconnected": AccessCannotBeDiscon,
			"GetClientInfo":        AccessGetClientInfo,
			"UploadAnywhere":       AccessUploadAnywhere,
			"AnyName":              AccessAnyName,
			"NoAgreement":          AccessNoAgreement,
			"SetFileComment":       AccessSetFileComment,
			"SetFolderComment":     AccessSetFolderComment,
			"ViewDropBoxes":        AccessViewDropBoxes,
			"MakeAlias":            AccessMakeAlias,
			"Broadcast":            AccessBroadcast,
			"NewsDeleteArt":        AccessNewsDeleteArt,
			"NewsCreateCat":        AccessNewsCreateCat,
			"NewsDeleteCat":        AccessNewsDeleteCat,
			"NewsCreateFldr":       AccessNewsCreateFldr,
			"NewsDeleteFldr":       AccessNewsDeleteFldr,
			"SendPrivMsg":          AccessSendPrivMsg,
			"UploadFolder":         AccessUploadFolder,
			"DownloadFolder":       AccessDownloadFolder,
		}

		for key, accessBit := range accessMap {
			if flag, ok := v[key].(bool); ok && flag {
				bits.Set(accessBit)
			}
		}
	}

	return nil
}

// accessFlags is used to render the access bitmap to human-readable boolean flags in the account yaml.
type accessFlags struct {
	DownloadFile         bool `yaml:"DownloadFile"`
	DownloadFolder       bool `yaml:"DownloadFolder"`
	UploadFile           bool `yaml:"UploadFile"`
	UploadFolder         bool `yaml:"UploadFolder"`
	DeleteFile           bool `yaml:"DeleteFile"`
	RenameFile           bool `yaml:"RenameFile"`
	MoveFile             bool `yaml:"MoveFile"`
	CreateFolder         bool `yaml:"CreateFolder"`
	DeleteFolder         bool `yaml:"DeleteFolder"`
	RenameFolder         bool `yaml:"RenameFolder"`
	MoveFolder           bool `yaml:"MoveFolder"`
	ReadChat             bool `yaml:"ReadChat"`
	SendChat             bool `yaml:"SendChat"`
	OpenChat             bool `yaml:"OpenChat"`
	CloseChat            bool `yaml:"CloseChat"`
	ShowInList           bool `yaml:"ShowInList"`
	CreateUser           bool `yaml:"CreateUser"`
	DeleteUser           bool `yaml:"DeleteUser"`
	OpenUser             bool `yaml:"OpenUser"`
	ModifyUser           bool `yaml:"ModifyUser"`
	ChangeOwnPass        bool `yaml:"ChangeOwnPass"`
	NewsReadArt          bool `yaml:"NewsReadArt"`
	NewsPostArt          bool `yaml:"NewsPostArt"`
	DisconnectUser       bool `yaml:"DisconnectUser"`
	CannotBeDisconnected bool `yaml:"CannotBeDisconnected"`
	GetClientInfo        bool `yaml:"GetClientInfo"`
	UploadAnywhere       bool `yaml:"UploadAnywhere"`
	AnyName              bool `yaml:"AnyName"`
	NoAgreement          bool `yaml:"NoAgreement"`
	SetFileComment       bool `yaml:"SetFileComment"`
	SetFolderComment     bool `yaml:"SetFolderComment"`
	ViewDropBoxes        bool `yaml:"ViewDropBoxes"`
	MakeAlias            bool `yaml:"MakeAlias"`
	Broadcast            bool `yaml:"Broadcast"`
	NewsDeleteArt        bool `yaml:"NewsDeleteArt"`
	NewsCreateCat        bool `yaml:"NewsCreateCat"`
	NewsDeleteCat        bool `yaml:"NewsDeleteCat"`
	NewsCreateFldr       bool `yaml:"NewsCreateFldr"`
	NewsDeleteFldr       bool `yaml:"NewsDeleteFldr"`
	SendPrivMsg          bool `yaml:"SendPrivMsg"`
}

func (bits AccessBitmap) MarshalYAML() (interface{}, error) {
	return accessFlags{
		DownloadFile:         bits.IsSet(AccessDownloadFile),
		DownloadFolder:       bits.IsSet(AccessDownloadFolder),
		UploadFolder:         bits.IsSet(AccessUploadFolder),
		DeleteFile:           bits.IsSet(AccessDeleteFile),
		UploadFile:           bits.IsSet(AccessUploadFile),
		RenameFile:           bits.IsSet(AccessRenameFile),
		MoveFile:             bits.IsSet(AccessMoveFile),
		CreateFolder:         bits.IsSet(AccessCreateFolder),
		DeleteFolder:         bits.IsSet(AccessDeleteFolder),
		RenameFolder:         bits.IsSet(AccessRenameFolder),
		MoveFolder:           bits.IsSet(AccessMoveFolder),
		ReadChat:             bits.IsSet(AccessReadChat),
		SendChat:             bits.IsSet(AccessSendChat),
		OpenChat:             bits.IsSet(AccessOpenChat),
		CloseChat:            bits.IsSet(AccessCloseChat),
		ShowInList:           bits.IsSet(AccessShowInList),
		CreateUser:           bits.IsSet(AccessCreateUser),
		DeleteUser:           bits.IsSet(AccessDeleteUser),
		OpenUser:             bits.IsSet(AccessOpenUser),
		ModifyUser:           bits.IsSet(AccessModifyUser),
		ChangeOwnPass:        bits.IsSet(AccessChangeOwnPass),
		NewsReadArt:          bits.IsSet(AccessNewsReadArt),
		NewsPostArt:          bits.IsSet(AccessNewsPostArt),
		DisconnectUser:       bits.IsSet(AccessDisconUser),
		CannotBeDisconnected: bits.IsSet(AccessCannotBeDiscon),
		GetClientInfo:        bits.IsSet(AccessGetClientInfo),
		UploadAnywhere:       bits.IsSet(AccessUploadAnywhere),
		AnyName:              bits.IsSet(AccessAnyName),
		NoAgreement:          bits.IsSet(AccessNoAgreement),
		SetFileComment:       bits.IsSet(AccessSetFileComment),
		SetFolderComment:     bits.IsSet(AccessSetFolderComment),
		ViewDropBoxes:        bits.IsSet(AccessViewDropBoxes),
		MakeAlias:            bits.IsSet(AccessMakeAlias),
		Broadcast:            bits.IsSet(AccessBroadcast),
		NewsDeleteArt:        bits.IsSet(AccessNewsDeleteArt),
		NewsCreateCat:        bits.IsSet(AccessNewsCreateCat),
		NewsDeleteCat:        bits.IsSet(AccessNewsDeleteCat),
		NewsCreateFldr:       bits.IsSet(AccessNewsCreateFldr),
		NewsDeleteFldr:       bits.IsSet(AccessNewsDeleteFldr),
		SendPrivMsg:          bits.IsSet(AccessSendPrivMsg),
	}, nil
}