1use crate::prelude::*;
6
7#[derive(Debug,Serialize,Deserialize)]
8pub enum MgmtCommand {
9 Noop,
10 SetSuperuser(bool),
11 SetRestrictedSshScope { key: sshkeys::KeySpec },
12
13 CreateAccount(AccountDetails),
14 UpdateAccount(AccountDetails),
15 DeleteAccount(AccountName),
16 ListAccounts { all: Option<bool> },
17
18 SelectAccount(AccountName), CheckAccount, CreateGame {
22 game: InstanceName,
23 insns: Vec<MgmtGameInstruction>,
24 },
25 ListGames { all: Option<bool> },
26 AlterGame {
27 game: InstanceName,
28 insns: Vec<MgmtGameInstruction>,
29 how: MgmtGameUpdateMode,
30 },
31 DestroyGame {
32 game: InstanceName,
33 },
34
35 ClearBundles {
36 game: InstanceName,
37 },
38 UploadBundle {
39 game: InstanceName,
40 size: usize,
41 hash: bundles::Hash,
42 kind: bundles::Kind,
43 #[serde(default)] progress: ProgressUpdateMode,
44 },
45 ListBundles {
46 game: InstanceName,
47 },
48 DownloadBundle {
49 game: InstanceName,
50 id: bundles::Id,
51 },
52
53 LibraryListLibraries {
54 game: InstanceName,
55 },
56 LibraryListByGlob {
57 game: InstanceName,
58 lib: Option<String>,
59 pat: String,
60 },
61
62 SshListKeys,
63 SshAddKey { akl: sshkeys::AuthkeysLine },
64 SshDeleteKey { index: usize, id: sshkeys::Id },
65 ThisConnAuthBy, SshReinstallKeys, LoadFakeRng(Vec<String>),
69 SetFakeTime(FakeTimeSpec),
70}
71
72#[derive(Debug,Clone,Hash,Eq,PartialEq)]
75#[derive(Serialize,Deserialize)]
76pub struct SshFingerprint(pub String);
77
78#[derive(Debug,Serialize,Deserialize)]
79pub struct AccountDetails {
80 pub account: AccountName,
81 pub nick: Option<String>,
82 pub timezone: Option<String>,
83 pub layout: Option<PresentationLayout>,
84 #[serde(flatten)]
85 pub access: Option<Box<dyn PlayerAccessSpec>>,
86}
88
89#[derive(Debug,Serialize,Deserialize)]
97pub enum MgmtResponse {
98 Fine,
99 Progress(ProgressInfo<'static>),
100 Error { error: MgmtError },
101 AlterGame { error: Option<MgmtError>, responses: Vec<MgmtGameResponse> },
102 AccountsList(Vec<Arc<AccountName>>),
103 GamesList(Vec<Arc<InstanceName>>),
104 Libraries(Vec<LibraryEnquiryData>),
105 LibraryItems(Vec<ItemEnquiryData>),
106 Bundles { bundles: MgmtBundleList },
107 Bundle { bundle: bundles::Id },
108 SshKeys(Vec<sshkeys::MgmtKeyReport>),
109 SshKeyAdded { index: usize, id: sshkeys::Id },
110 ThisConnAuthBy(MgmtThisConnAuthBy),
111}
112
113pub type MgmtBundleList = BTreeMap<bundles::Id, bundles::State>;
114
115#[derive(Debug,Clone,Serialize,Deserialize)]
116pub enum MgmtThisConnAuthBy {
117 Local,
118 Ssh { key: sshkeys::KeySpec },
119}
120
121#[derive(Debug,Serialize,Deserialize)]
122pub enum MgmtGameInstruction {
123 Noop,
124 Info,
125 SetTableSize(Pos),
126 SetTableColour(ColourSpec),
127
128 InsnMark(Box<[u8]>),
129 Synch, SynchLog,
131 PieceIdLookupFwd { piece: PieceId, player: PlayerId, },
133 PieceIdLookupRev { vpid: VisiblePieceId, player: PlayerId, },
135
136 ListPieces,
137 AddPieces(PiecesSpec),
138 DeletePiece(PieceId),
139 DeletePieceAlias(String),
140 DefinePieceAlias { alias: String, target: Box<dyn PieceSpec> },
141
142 ClearGame { },
143 ResetFromNamedSpec { spec: String },
144 ResetFromGameSpec { spec_toml: String },
145
146 ResetPlayerAccess(PlayerId),
147 RedeliverPlayerAccess(PlayerId),
148
149 JoinGame { details: MgmtPlayerDetails },
150 UpdatePlayer { player: PlayerId, details: MgmtPlayerDetails },
151 LeaveGame(PlayerId),
152
153 SetLinks(HashMap<LinkKind, UrlSpec>),
154 RemoveLink { kind: LinkKind },
155 SetLink { kind: LinkKind, url: UrlSpec },
156
157 ClearLog,
158 SetACL { acl: Acl<TablePermission> },
159 }
161
162#[derive(Debug,Serialize,Deserialize)]
163pub struct MgmtPlayerDetails {
164 pub nick: Option<String>,
165}
166
167#[derive(Debug,Serialize,Deserialize)]
168pub enum MgmtGameResponse {
169 Fine,
170 Info(MgmtGameResponseGameInfo),
171 InsnMark(Box<[u8]>),
172 InsnExpanded,
173 Synch(Generation),
174
175 InternalPieceId(Option<PieceId>),
176 VisiblePieceId(Option<VisiblePieceId>),
177
178 Pieces {
179 pieces: Vec<MgmtGamePieceInfo>,
180 pcaliases: BTreeSet<String>,
181 },
182
183 JoinGame {
184 nick: String,
185 player: PlayerId,
186 token: AccessTokenReport,
187 },
188 PlayerAccessToken(AccessTokenReport),
189}
190
191#[derive(Debug,Clone,Serialize,Deserialize)]
192pub struct AccessTokenInfo { pub url: String }
193
194#[derive(Debug,Clone,Serialize,Deserialize)]
195pub struct AccessTokenReport { pub lines: Vec<String> }
196
197#[derive(Debug,Clone,Serialize,Deserialize)]
198pub struct MgmtGameResponseGameInfo {
199 pub table_size: Pos,
200 pub players: SecondarySlotMap<PlayerId, MgmtPlayerInfo>,
201 pub links: Vec<(LinkKind, UrlSpec)>,
202}
203
204#[derive(Debug,Clone,Serialize,Deserialize)]
205pub struct MgmtPlayerInfo {
206 pub account: AccountName,
207 pub nick: String,
208}
209
210#[derive(Debug,Clone,Serialize,Deserialize)]
211pub struct MgmtGamePieceInfo {
212 pub piece: PieceId,
213 pub itemname: shapelib::GoodItemName,
214 #[serde(flatten)]
215 pub visible: Option<MgmtGamePieceVisibleInfo>,
216}
217
218#[derive(Debug,Clone,Serialize,Deserialize)]
219pub struct MgmtGamePieceVisibleInfo {
220 pub pos: Pos,
221 pub face: FaceId,
222 pub desc_html: Html,
223 pub bbox: Rect,
224}
225
226#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
227pub enum MgmtGameUpdateMode {
228 Online,
229 Bulk,
230}
231
232#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Serialize,Deserialize)]
233pub enum ProgressUpdateMode {
234 None,
235 Simplex,
236 Duplex,
237}
238impl Default for ProgressUpdateMode {
239 fn default() -> Self { PUM::None }
240}
241
242#[derive(Debug,Clone,Error,Serialize,Deserialize)]
243pub enum MgmtError {
244 #[error("failed to parse protocol command: {0}")] CommandParseFailed(String),
245 #[error("not authorised")] AuthorisationError,
246 #[error("superuse authorisation requiredr")] SuperuserAuthorisationRequired,
247 #[error("command requires account specified")] SpecifyAccount,
248 #[error("command requires nick specified")] MustSpecifyNick,
249 #[error("object or item already exists")] AlreadyExists,
250 #[error("game already has player with that nick")] NickCollision,
251 #[error("{0}")] GameBeingDestroyed (#[from] GameBeingDestroyed),
252 #[error("game not found")] GameNotFound,
253 #[error("{0}")] AccountNotFound (#[from] AccountNotFound),
254 #[error("{0}")] PlayerNotFound (#[from] PlayerNotFound),
255 #[error("piece not found in game")] PieceNotFound,
256 #[error("limit exceeded")] LimitExceeded,
257 #[error("server failure: {0}")] ServerFailure(String),
258 #[error("{0}")] ZCoordinateOverflow (#[from] zcoord::Overflow),
259 #[error("bad glob pattern: {pat:?}: {msg}")]
260 BadGlob { pat: String, msg: String },
261 #[error("{0}")] BadSpec (#[from] SpecError),
262 #[error("access token delivery failed: {0}")]
263 TokenDeliveryFailed (#[from] TokenDeliveryError),
264 #[error("{0}")] CoordinateOverflow (#[from] CoordinateOverflow),
265 #[error("TOML syntax error: {0}")] TomlSyntaxError(String),
266 #[error("TOML structure error: {0}")] TomlStructureError(String),
267 #[error("{0}, command not supported")] RngIsReal(#[from] RngIsReal),
268 #[error("{0}, command not supported")] TimeIsReal(#[from] TimeIsReal),
269 #[error("upload truncated")] UploadTruncated,
270 #[error("upload corrupted")] UploadCorrupted,
271 #[error("too many bundles")] TooManyBundles,
272 #[error("bad bundle: {0}")] BadBundle(String),
273 #[error("bundle not found")] BundleNotFound,
274 #[error("bundle(s) in use, cannot clear ({0})")] BundlesInUse(String),
275 #[error("game spec not found")] GameSpecNotFound,
276 #[error("game contains invalid UTF-8")] GameSpecInvalidData,
277 #[error("idle timeout waiting for mgmt command")] IdleTimeout,
278 #[error("upload took too long (timed out)")] UploadTimeout,
279 #[error("ssh keys are only for main subaccount")] NoSshKeysForSubaccount,
280 #[error("ssh key not found")] SshKeyNotFound,
281 #[error("ssh key id default, ie invalid")] InvalidSshKeyId,
282 #[error("ssh key invalid: {0}")] InvalidSshKey(#[from] sshkeys::KeyError),
283 #[error("command forbides account specified")] AccountSpecified,
284}
285
286impl AccountDetails {
287 pub fn default(account: AccountName) -> AccountDetails { AccountDetails {
288 account,
289 nick: None, timezone: None, layout: None, access: None,
290 } }
291}
292
293impl From<InternalError> for MgmtError {
294 fn from(e: InternalError) -> MgmtError {
295 MgmtError::ServerFailure(format!("internal error: {}", &e))
296 }
297}
298
299impl AccessTokenInfo {
300 pub fn report(self) -> Vec<String> {
301 vec![
302 "Game access url (personal and private):".to_string(),
303 self.url,
304 ]
305 }
306}