1 // Written in the D programming language.
2 
3 /**
4 This module contains a set of various useful functions for more convenient work with 9P / Styx messages.
5 Note: This module contains helpers for building all types of messages (and some other stuff), except stat (R-message) and wstat (T-message), since these messages have a complex structure and must be processed directly in the code that forms them.
6 
7 Copyright: LightHouse Software, 2021
8 License:   $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0).
9 Authors:   Oleg Bakharev,
10 		   Ilya Pertsev   
11 */
12 module styx2000.extrautil.styxmessage;
13 
14 private {
15 	import styx2000.extrautil.casts;
16 	
17 	import styx2000.protoconst;
18 	
19 	import styx2000.protobj;
20 }
21 
22 /// Convenient aliases
23 alias StyxMessage = StyxObject[];
24 
25 /// Convenient alias for array of styx messages (not described in 9P / Styx protocol)
26 alias StyxMail = StyxMessage[];
27 
28 /// StyxMessage is client message ?
29 bool isTmessage(StyxMessage msg)
30 {
31 	bool isMessage = false;
32 	Type type = cast(Type) msg[1];
33 	
34 	if (type !is null)
35 	{
36 		with (STYX_MESSAGE_TYPE)
37 		{
38 			switch(type.getType) 
39 			{
40 				// version
41 				case T_VERSION:
42 					isMessage = true;
43 					break;
44 				// auth
45 				case T_AUTH:
46 					isMessage = true;
47 					break;
48 				// flush
49 				case T_FLUSH:
50 					isMessage = true;
51 					break;
52 				// attach
53 				case T_ATTACH:
54 					isMessage = true;
55 					break;
56 				// walk
57 				case T_WALK:
58 					isMessage = true;
59 					break;
60 				case T_OPEN:
61 					isMessage = true;
62 					break;
63 				// create
64 				case T_CREATE:
65 					isMessage = true;
66 					break;
67 				// read
68 				case T_READ:
69 					isMessage = true;
70 					break;
71 				// write
72 				case T_WRITE:
73 					isMessage = true;
74 					break;
75 				// clunk
76 				case T_CLUNK:
77 					isMessage = true;
78 					break;
79 				// remove
80 				case T_REMOVE:
81 					isMessage = true;
82 					break;
83 				// stat
84 				case T_STAT:
85 					isMessage = true;
86 					break;
87 				// wstat
88 				case T_WSTAT:
89 					isMessage = true;
90 					break;
91 				default:
92 					break;
93 			}
94 		}
95 	}
96 	
97 	return isMessage;
98 }
99 
100 /// StyxMessage is server message ?
101 bool isRmessage(StyxMessage msg)
102 {
103 	bool isMessage = false;
104 	Type type = cast(Type) msg[1];
105 	
106 	if (type !is null)
107 	{
108 		with (STYX_MESSAGE_TYPE)
109 		{
110 			switch(type.getType) 
111 			{
112 				// version
113 				case R_VERSION:
114 					isMessage = true;
115 					break;
116 				// auth
117 				case R_AUTH:
118 					isMessage = true;
119 					break;
120 				// flush
121 				case R_FLUSH:
122 					isMessage = true;
123 					break;
124 				// attach
125 				case R_ATTACH:
126 					isMessage = true;
127 					break;
128 				// walk
129 				case R_WALK:
130 					isMessage = true;
131 					break;
132 				case R_OPEN:
133 					isMessage = true;
134 					break;
135 				// create
136 				case R_CREATE:
137 					isMessage = true;
138 					break;
139 				// read
140 				case R_READ:
141 					isMessage = true;
142 					break;
143 				// write
144 				case R_WRITE:
145 					isMessage = true;
146 					break;
147 				// clunk
148 				case R_CLUNK:
149 					isMessage = true;
150 					break;
151 				// remove
152 				case R_REMOVE:
153 					isMessage = true;
154 					break;
155 				// stat
156 				case R_STAT:
157 					isMessage = true;
158 					break;
159 				// wstat
160 				case R_WSTAT:
161 					isMessage = true;
162 					break;
163 				// error
164 				case R_ERROR:
165 					isMessage = true;
166 					break;
167 				default:
168 					break;
169 			}
170 		}
171 	}
172 	
173 	return isMessage;
174 }
175 
176 /// StyxMessage is error message ?
177 bool isErrorMessage(StyxMessage msg)
178 {
179 	bool isMessage = false;
180 	Type type = cast(Type) msg[1];
181 	
182 	if (type !is null)
183 	{
184 		if (type.getType == STYX_MESSAGE_TYPE.R_ERROR)
185 		{
186 			isMessage = true;
187 		}
188 	}
189 	
190 	return isMessage;
191 }
192 
193 /// Create base header for 9P / Styx messages
194 auto createHeader(uint size = 0, STYX_MESSAGE_TYPE type = STYX_MESSAGE_TYPE.R_ERROR, ushort tag = STYX_NOTAG)
195 {
196 	return cast(StyxMessage) [
197 		new Size(size),
198 		new Type(type),
199 		new Tag(tag)
200 	];
201 }
202 
203 /// Parse header of Styx message. Returns anonymous struct that contains size, type and tag fields.
204 auto extractHeader(StyxMessage msg)
205 {
206 	struct StyxHeader {
207 		/// message size
208 		uint size;
209 		/// message type
210 		STYX_MESSAGE_TYPE type;
211 		/// tag
212 		ushort tag;
213 	}
214 	
215 	StyxHeader header = StyxHeader(0, STYX_MESSAGE_TYPE.R_ERROR, STYX_NOTAG);
216 	
217 	if (msg.length >= 3)
218 	{
219 		auto size = msg[0].toSize;
220 		
221 		if (size !is null)
222 		{
223 			header.size = size.getSize;
224 		}
225 		
226 		auto type = msg[1].toType;
227 		
228 		if (type !is null)
229 		{
230 			header.type = type.getType;
231 		}
232 		
233 		auto tag = msg[2].toTag;
234 		
235 		if (tag !is null)
236 		{
237 			header.tag = tag.getTag;
238 		}
239 	}
240 	
241 	return header;
242 }
243 
244 /// Create version message from client
245 auto createTmsgVersion(ushort tag = STYX_NOTAG, uint maximalSize = 8192, string vers = STYX_VERSION)
246 {
247 	return createHeader(0, STYX_MESSAGE_TYPE.T_VERSION, tag) ~ cast(StyxMessage) [
248 		new Msize(maximalSize),
249 		new Version(vers)
250 	];
251 }
252 
253 /// Create version message from server
254 auto createRmsgVersion(ushort tag = STYX_NOTAG, uint maximalSize = 8192, string vers = STYX_VERSION)
255 {
256 	return createHeader(0, STYX_MESSAGE_TYPE.R_VERSION, tag) ~ cast(StyxMessage) [
257 		new Msize(maximalSize),
258 		new Version(vers)
259 	];
260 }
261 
262 /// Create auth message from client
263 auto createTmsgAuth(ushort tag = STYX_NOTAG, uint afid = STYX_NOFID, string uname = "", string aname = "")
264 {
265 	return createHeader(0, STYX_MESSAGE_TYPE.T_AUTH, tag) ~ cast(StyxMessage) [
266 		new Afid(afid),
267 		new Uname(uname),
268 		new Aname(aname)
269 	];
270 }
271 
272 /// Create auth message from server
273 auto createRmsgAuth(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0)
274 {
275 	return createHeader(0, STYX_MESSAGE_TYPE.R_AUTH, tag) ~ cast(StyxMessage) [
276 		new Aqid(type, vers, path)
277 	];
278 }
279 
280 /// Create attach message from client
281 auto createTmsgAttach(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, uint afid = STYX_NOFID, string uname = "", string aname = "")
282 {
283 	return createHeader(0, STYX_MESSAGE_TYPE.T_ATTACH, tag) ~ cast(StyxMessage) [
284 		new Fid(fid),
285 		new Afid(afid),
286 		new Uname(uname),
287 		new Aname(aname)
288 	];
289 }
290 
291 /// Create attach message from server
292 auto createRmsgAttach(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0)
293 {
294 	return createHeader(0, STYX_MESSAGE_TYPE.R_ATTACH, tag) ~ cast(StyxMessage) [
295 		new Qid(type, vers, path)
296 	];
297 }
298 
299 /// Create error message from server (for client this type of message does not exists)
300 auto createRmsgError(ushort tag = STYX_NOTAG, string ename = "")
301 {
302 	return createHeader(0, STYX_MESSAGE_TYPE.R_ERROR, tag) ~ cast(StyxMessage) [
303 		new Ename(ename)
304 	];
305 }
306 
307 /// Create clunk message from client
308 auto createTmsgClunk(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID)
309 {
310 	return createHeader(0, STYX_MESSAGE_TYPE.T_CLUNK, tag) ~ cast(StyxMessage) [
311 		new Fid(fid)
312 	];
313 }
314 
315 /// Create clunk message from server
316 auto createRmsgClunk(ushort tag = STYX_NOTAG)
317 {
318 	return createHeader(0, STYX_MESSAGE_TYPE.R_CLUNK, tag);
319 }
320 
321 /// Create flush message from client
322 auto createTmsgFlush(ushort tag = STYX_NOTAG, ushort oldTag = STYX_NOTAG)
323 {
324 	return createHeader(0, STYX_MESSAGE_TYPE.T_FLUSH, tag) ~ cast(StyxMessage) [
325 		new OldTag(oldTag)
326 	];
327 }
328 
329 /// Create flush message from server
330 auto createRmsgFlush(ushort tag = STYX_NOTAG)
331 {
332 	return createHeader(0, STYX_MESSAGE_TYPE.R_FLUSH, tag);
333 }
334 
335 /// Create remove message from client
336 auto createTmsgRemove(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID)
337 {
338 	return createHeader(0, STYX_MESSAGE_TYPE.T_REMOVE, tag) ~ cast(StyxMessage) [
339 		new Fid(fid)
340 	];
341 }
342 
343 /// Create remove message from server
344 auto createRmsgRemove(ushort tag = STYX_NOTAG)
345 {
346 	return createHeader(0, STYX_MESSAGE_TYPE.R_REMOVE, tag);
347 }
348 
349 /// Create open message from client
350 auto createTmsgOpen(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, STYX_FILE_MODE mode = STYX_FILE_MODE.OREAD)
351 {
352 	return createHeader(0, STYX_MESSAGE_TYPE.T_OPEN, tag) ~ cast(StyxMessage) [
353 		new Fid(fid),
354 		new Mode(mode)
355 	];
356 }
357 
358 /// Create open message from server
359 auto createRmsgOpen(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0, uint iounit = 8164)
360 {
361 	return createHeader(0, STYX_MESSAGE_TYPE.R_OPEN, tag) ~ cast(StyxMessage) [
362 		new Qid(type, vers, path),
363 		new Iounit(iounit)
364 	];
365 }
366 
367 /// Create create message from client
368 auto createTmsgCreate(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, STYX_FILE_MODE mode = STYX_FILE_MODE.OREAD, STYX_FILE_PERMISSION[] perms = [STYX_FILE_PERMISSION.OWNER_READ, STYX_FILE_PERMISSION.OWNER_WRITE, STYX_FILE_PERMISSION.OWNER_EXEC])
369 {
370 	auto perm = new Perm;
371 	perm.setPerm(perms);
372 	return createHeader(0, STYX_MESSAGE_TYPE.T_CREATE, tag) ~ cast(StyxMessage) [
373 		new Fid(fid),
374 		perm,
375 		new Mode(mode)
376 	];
377 }
378 
379 /// Create create message from server
380 auto createRmsgCreate(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0, uint iounit = 8164)
381 {
382 	return createHeader(0, STYX_MESSAGE_TYPE.R_CREATE, tag) ~ cast(StyxMessage) [
383 		new Qid(type, vers, path),
384 		new Iounit(iounit)
385 	];
386 }
387 
388 /// Create read message from client
389 auto createTmsgRead(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, ulong offset = 0, uint count = 0)
390 {
391 	return createHeader(0, STYX_MESSAGE_TYPE.T_READ, tag) ~ cast(StyxMessage) [
392 		new Fid(fid),
393 		new Offset(offset),
394 		new Count(count)
395 	];
396 }
397 
398 /// Create read message from server
399 auto createRmsgRead(ushort tag = STYX_NOTAG, uint count = 0, ubyte[] data = [])
400 {
401 	return createHeader(0, STYX_MESSAGE_TYPE.R_READ, tag) ~ cast(StyxMessage) [
402 		new Count(count),
403 		new Data(data)
404 	];
405 }
406 
407 /// Create write message from client
408 auto createTmsgWrite(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, ulong offset = 0, uint count = 0, ubyte[] data = [])
409 {
410 	return createHeader(0, STYX_MESSAGE_TYPE.T_WRITE, tag) ~ cast(StyxMessage) [
411 		new Fid(fid),
412 		new Offset(offset),
413 		new Count(count),
414 		new Data(data)
415 	];
416 }
417 
418 /// Create write message from server
419 auto createRmsgWrite(ushort tag = STYX_NOTAG, uint count = 0)
420 {
421 	return createHeader(0, STYX_MESSAGE_TYPE.R_WRITE, tag) ~ cast(StyxMessage) [
422 		new Count(count)
423 	];
424 }
425 
426 
427 /// Create walk message from client
428 auto createTmsgWalk(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, uint newFid = STYX_NOFID, string[] nwname = []) 
429 {
430 	return createHeader(0, STYX_MESSAGE_TYPE.T_WALK, tag) ~ cast(StyxMessage) [
431 		new Fid(fid),
432 		new NewFid(newFid),
433 		new Nwname(nwname)
434 	];
435 }
436 
437 
438 /// Create walk message from server
439 auto createRmsgWalk(ushort tag = STYX_NOTAG, Qid[] nwqid = []) 
440 {
441 	return createHeader(0, STYX_MESSAGE_TYPE.R_WALK, tag) ~ cast(StyxMessage) [
442 		new Nwqid(nwqid)
443 	];
444 }
445 
446 
447 /// Create stat message from client
448 auto createTmsgStat(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID) 
449 {
450 	return createHeader(0, STYX_MESSAGE_TYPE.T_STAT, tag) ~ cast(StyxMessage) [
451 		new Fid(fid)
452 	];
453 }
454 
455 
456 /// Create wstat message from server
457 auto createRmsgWstat(ushort tag = STYX_NOTAG)
458 {
459 	return createHeader(0, STYX_MESSAGE_TYPE.R_WSTAT, tag);
460 }
461 
462 /// Calculate real (!) message size (in bytes)
463 auto calculateMessageSize(StyxMessage msg)
464 {
465 	uint realSize = 0;
466 	
467 	foreach (e; msg)
468 	{
469 		realSize += cast(uint) e.pack.length;
470 	}
471 	
472 	return realSize;
473 }
474 
475 
476 /// Fix incorrect size in some messages which generated by helpers (fix zero sizes)
477 auto fixMessageSize(StyxMessage msg)
478 {
479 	StyxMessage newMsg = msg[1..$];
480 	uint realSize = 4; // Size field has length = 4 bytes
481 	
482 	foreach (e; newMsg)
483 	{
484 		realSize += cast(uint) e.pack.length;
485 	}
486 	
487 	return cast(StyxMessage) [new Size(realSize)] ~ newMsg;
488 }