1use super::types::{AfpError, AfpUam, AfpVersion, CreateFlag, PathType};
2use crate::afp::util::MacString;
3use crate::afp::{
4 FPAccessRights, FPByteRangeLockFlags, FPDirectoryBitmap, FPFileAttributes, FPFileBitmap,
5 FPVolumeBitmap,
6};
7
8pub const AFP_CMD_BYTE_RANGE_LOCK: u8 = 1;
10pub const AFP_CMD_CLOSE_VOL: u8 = 2;
11pub const AFP_CMD_COPY_FILE: u8 = 5;
12pub const AFP_CMD_CLOSE_FORK: u8 = 4;
13pub const AFP_CMD_CREATE_DIR: u8 = 6;
14pub const AFP_CMD_CREATE_FILE: u8 = 7;
15pub const AFP_CMD_DELETE: u8 = 8;
16pub const AFP_CMD_ENUMERATE: u8 = 9;
17pub const AFP_CMD_FLUSH: u8 = 10;
18pub const AFP_CMD_GET_FORK_PARMS: u8 = 14;
19pub const AFP_CMD_GET_SRVR_PARMS: u8 = 16;
20pub const AFP_CMD_GET_VOL_PARMS: u8 = 17;
21pub const AFP_CMD_LOGIN: u8 = 18;
22pub const AFP_CMD_LOGOUT: u8 = 20;
23pub const AFP_CMD_MOVE_AND_RENAME: u8 = 23;
24pub const AFP_CMD_OPEN_VOL: u8 = 24;
25pub const AFP_CMD_OPEN_FORK: u8 = 26;
26pub const AFP_CMD_READ: u8 = 27;
27pub const AFP_CMD_RENAME: u8 = 28;
28pub const AFP_CMD_SET_DIR_PARMS: u8 = 29;
29pub const AFP_CMD_SET_FORK_PARMS: u8 = 31;
30pub const AFP_CMD_WRITE: u8 = 33;
31pub const AFP_CMD_GET_FILE_DIR_PARMS: u8 = 34;
32pub const AFP_CMD_SET_FILE_DIR_PARMS: u8 = 35;
33pub const AFP_CMD_GET_SRVR_MSG: u8 = 38;
34pub const AFP_CMD_OPEN_DT: u8 = 48;
35pub const AFP_CMD_CLOSE_DT: u8 = 49;
36pub const AFP_CMD_GET_ICON: u8 = 51;
37pub const AFP_CMD_GTICNINFO: u8 = 52;
38pub const AFP_CMD_ADD_APPL: u8 = 53;
39pub const AFP_CMD_ADD_COMMENT: u8 = 56;
40pub const AFP_CMD_REMOVE_COMMENT: u8 = 57;
41pub const AFP_CMD_GET_COMMENT: u8 = 58;
42pub const AFP_CMD_ADD_ICON: u8 = 192;
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum FPLoginAuth {
47 NoUserAuthent,
49
50 CleartxtPasswrd {
52 username: MacString,
53 password: [u8; 8], },
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct FPLogin {
60 pub afp_version: AfpVersion,
62
63 pub auth: FPLoginAuth,
65}
66
67impl FPLogin {
68 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
69 if buf.len() < 2 {
70 return Err(AfpError::InvalidSize);
71 }
72
73 let mut offset = 0;
74
75 let read_pstr = |offset: usize| -> Result<(MacString, usize), AfpError> {
77 let parsed = MacString::try_from(&buf[offset..])?;
78 let next_offset = offset + parsed.byte_len();
79 Ok((parsed, next_offset))
80 };
81
82 let (afp_version_str, next_offset) = read_pstr(offset)?;
84 let afp_version = AfpVersion::try_from(afp_version_str.as_str())?;
85 offset = next_offset;
86
87 let (uam_str, next_offset) = read_pstr(offset)?;
89 let uam = AfpUam::try_from(uam_str.as_str())?;
90 offset = next_offset;
91
92 let auth = match uam {
94 AfpUam::NoUserAuthent => FPLoginAuth::NoUserAuthent,
95
96 AfpUam::CleartxtPasswrd => {
97 let (username, next_offset) = read_pstr(offset)?;
99 offset = next_offset;
100
101 if offset + 8 > buf.len() {
103 return Err(AfpError::InvalidSize);
104 }
105 let mut password = [0u8; 8];
106 password.copy_from_slice(&buf[offset..offset + 8]);
107
108 FPLoginAuth::CleartxtPasswrd { username, password }
109 }
110
111 _ => return Err(AfpError::BadUam),
112 };
113
114 Ok(FPLogin { afp_version, auth })
115 }
116
117 pub fn to_bytes(&self) -> Result<Vec<u8>, AfpError> {
118 let mut buf = Vec::new();
119
120 let afp_version_str = self.afp_version.as_str();
122 let len = afp_version_str.len() as u8;
123 buf.push(len);
124 buf.extend_from_slice(afp_version_str.as_bytes());
125
126 match &self.auth {
128 FPLoginAuth::NoUserAuthent => {
129 let uam_str = AfpUam::NoUserAuthent.as_str();
130 let len = uam_str.len() as u8;
131 buf.push(len);
132 buf.extend_from_slice(uam_str.as_bytes());
133 }
135
136 FPLoginAuth::CleartxtPasswrd { username, password } => {
137 let uam_str = AfpUam::CleartxtPasswrd.as_str();
138 let len = uam_str.len() as u8;
139 buf.push(len);
140 buf.extend_from_slice(uam_str.as_bytes());
141
142 let mut username_buf = [0u8; 256];
144 let written = username.bytes(&mut username_buf)?;
145 buf.extend_from_slice(&username_buf[..written]);
146
147 buf.extend_from_slice(password);
149 }
150 }
151
152 Ok(buf)
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct FPGetSrvrInfo {
158 pub machine_type: MacString,
159 pub afp_versions: Vec<AfpVersion>,
160 pub uams: Vec<AfpUam>,
161 pub volume_icon: Option<[u8; 256]>,
162 pub flags: u16,
163 pub server_name: MacString,
164}
165
166impl FPGetSrvrInfo {
167 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
168 if buf.len() < 11 {
169 return Err(AfpError::InvalidSize);
171 }
172
173 let machine_type_offset = u16::from_be_bytes([buf[0], buf[1]]) as usize;
174 let afp_versions_offset = u16::from_be_bytes([buf[2], buf[3]]) as usize;
175 let uams_offset = u16::from_be_bytes([buf[4], buf[5]]) as usize;
176 let volume_icon_offset = u16::from_be_bytes([buf[6], buf[7]]) as usize;
177 let flags = u16::from_be_bytes([buf[8], buf[9]]);
178
179 let server_name_len = buf[10] as usize;
181 if 10 + 1 + server_name_len > buf.len() {
182 return Err(AfpError::InvalidSize);
183 }
184 let server_name = MacString::try_from(&buf[10..10 + 1 + server_name_len])?;
185
186 let read_pstr = |offset: usize| -> Result<MacString, AfpError> {
188 if offset >= buf.len() {
189 return Err(AfpError::InvalidSize);
190 }
191 let len = buf[offset] as usize;
192 if offset + 1 + len > buf.len() {
193 return Err(AfpError::InvalidSize);
194 }
195 MacString::try_from(&buf[offset..offset + 1 + len])
196 };
197
198 let read_pstr_list = |offset: usize| -> Result<Vec<MacString>, AfpError> {
200 if offset >= buf.len() {
201 return Err(AfpError::InvalidSize);
202 }
203 let count = buf[offset] as usize;
204 let mut strings: Vec<MacString> = Vec::with_capacity(count);
205 let mut current_pos = offset + 1;
206
207 for _ in 0..count {
208 if current_pos >= buf.len() {
209 return Err(AfpError::InvalidSize);
210 }
211
212 let new_string = MacString::try_from(&buf[current_pos..])?;
213 current_pos += new_string.byte_len();
214 strings.push(new_string);
215 }
216 Ok(strings)
217 };
218
219 let machine_type = read_pstr(machine_type_offset)?;
220 let afp_versions_strings: Vec<MacString> = read_pstr_list(afp_versions_offset)?;
221 let afp_versions: Vec<AfpVersion> = afp_versions_strings
222 .iter()
223 .map(|s| AfpVersion::try_from(s.as_str()))
224 .collect::<Result<Vec<_>, _>>()
225 .map_err(|_| AfpError::BadVersNum)?;
226 let uams_strings: Vec<MacString> = read_pstr_list(uams_offset)?;
227 let uams: Vec<AfpUam> = uams_strings
228 .iter()
229 .map(|s| AfpUam::try_from(s.as_str()).map_err(|_| AfpError::BadUam))
230 .collect::<Result<Vec<_>, _>>()?;
231
232 let volume_icon = if volume_icon_offset != 0 {
235 if volume_icon_offset + 256 > buf.len() {
236 return Err(AfpError::InvalidSize);
237 }
238 let mut icon = [0u8; 256];
239 icon.copy_from_slice(&buf[volume_icon_offset..volume_icon_offset + 256]);
240 Some(icon)
241 } else {
242 None
243 };
244
245 Ok(Self {
246 machine_type,
247 afp_versions,
248 uams,
249 volume_icon,
250 flags,
251 server_name,
252 })
253 }
254
255 pub fn to_bytes(&self) -> Result<Vec<u8>, AfpError> {
256 let mut buf = Vec::new();
257
258 let mut server_name_buf = [0u8; 256];
259 let written = self.server_name.bytes(&mut server_name_buf)?;
260 let server_name_len = (written - 1).min(255);
261
262 let mut base_offset = 10 + 1 + server_name_len;
264 let mut padding_needed = 0;
265
266 if base_offset % 2 != 0 {
269 base_offset += 1;
270 padding_needed = 1;
271 }
272
273 let mut current_offset = base_offset as u16;
274
275 let mut variable_data = Vec::new();
276
277 let mut tmp_macstr = [0u8; 256];
278 let machine_type_ptr = current_offset;
279 {
280 let written_mt = self.machine_type.bytes(&mut tmp_macstr)?;
281 variable_data.extend_from_slice(&tmp_macstr[..written_mt]);
282 }
283 current_offset = base_offset as u16 + variable_data.len() as u16;
284
285 let afp_versions_ptr = current_offset;
286 variable_data.push(self.afp_versions.len() as u8);
287 for v in &self.afp_versions {
288 let s = v.as_str();
289 let len = s.len() as u8;
290 variable_data.push(len);
291 variable_data.extend_from_slice(s.as_bytes());
292 }
293 current_offset = base_offset as u16 + variable_data.len() as u16;
294
295 let uams_ptr = current_offset;
296 variable_data.push(self.uams.len() as u8);
297 for u in &self.uams {
298 let s = u.as_str();
299 let len = s.len() as u8;
300 variable_data.push(len);
301 variable_data.extend_from_slice(s.as_bytes());
302 }
303 current_offset = base_offset as u16 + variable_data.len() as u16;
304
305 let volume_icon_ptr = if let Some(icon) = &self.volume_icon {
306 let ptr = current_offset;
307 variable_data.extend_from_slice(icon);
308 ptr
309 } else {
310 0
311 };
312
313 buf.extend_from_slice(&machine_type_ptr.to_be_bytes());
314 buf.extend_from_slice(&afp_versions_ptr.to_be_bytes());
315 buf.extend_from_slice(&uams_ptr.to_be_bytes());
316 buf.extend_from_slice(&volume_icon_ptr.to_be_bytes());
317 buf.extend_from_slice(&self.flags.to_be_bytes());
318
319 buf.push(server_name_len as u8);
320 buf.extend_from_slice(&server_name_buf[1..1 + server_name_len]);
321
322 if padding_needed > 0 {
323 buf.push(0); }
325
326 buf.extend_from_slice(&variable_data);
327
328 Ok(buf)
329 }
330}
331
332pub struct FPVolume {
333 pub has_password: bool,
334 pub has_config_info: bool,
335 pub name: MacString,
336}
337
338impl FPVolume {
339 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, AfpError> {
340 let target_size = 2 + self.name.len();
342
343 if buf.len() < target_size {
344 return Err(AfpError::InvalidSize);
345 }
346
347 let target = &mut buf[..target_size];
349
350 target[0] = (self.has_password as u8) << 7 | (self.has_config_info as u8) << 6;
351 target[1] = (self.name.byte_len() - 1) as u8;
352 self.name.bytes(&mut target[1..])?;
353
354 Ok(target_size)
355 }
356
357 pub fn size(&self) -> usize {
358 2 + self.name.byte_len() - 1
359 }
360}
361
362pub struct FPGetSrvrParms {
363 pub server_time: u32,
364 pub volumes: Vec<FPVolume>,
365}
366
367impl FPGetSrvrParms {
368 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, AfpError> {
369 let mut offset = 0;
370
371 let target_size = 5 + self.volumes.iter().map(|v| v.size()).sum::<usize>();
373
374 if buf.len() < target_size {
375 return Err(AfpError::InvalidSize);
376 }
377
378 let target = &mut buf[..target_size];
380
381 target[offset..offset + 4].copy_from_slice(&self.server_time.to_be_bytes());
382 offset += 4;
383 target[offset] = self.volumes.len() as u8;
384 offset += 1;
385
386 for volume in &self.volumes {
387 let volume_size = volume.to_bytes(&mut target[offset..])?;
388 offset += volume_size;
389 }
390
391 Ok(target_size)
392 }
393}
394
395#[derive(Debug)]
396pub struct FPEnumerate {
397 pub volume_id: u16,
398 pub directory_id: u32,
399 pub file_bitmap: FPFileBitmap,
400 pub directory_bitmap: FPDirectoryBitmap,
401 pub req_count: u16,
402 pub start_index: u16,
403 pub max_reply_size: u16,
404 pub path: MacString,
405}
406
407impl FPEnumerate {
408 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
409 let volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
410 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
411 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
412 let directory_bitmap =
413 FPDirectoryBitmap::from(u16::from_be_bytes(*buf[8..10].as_array().unwrap()));
414 let req_count = u16::from_be_bytes(*buf[10..12].as_array().unwrap());
415 let start_index = u16::from_be_bytes(*buf[12..14].as_array().unwrap());
416 let max_reply_size = u16::from_be_bytes(*buf[14..16].as_array().unwrap());
417 let _path_type = buf[16];
418 let path = MacString::try_from(&buf[17..])?;
419
420 Ok(Self {
421 volume_id,
422 directory_id,
423 file_bitmap,
424 directory_bitmap,
425 req_count,
426 start_index,
427 max_reply_size,
428 path,
429 })
430 }
431}
432
433#[derive(Debug)]
434pub struct FPByteRangeLock {
435 pub fork_id: u16,
436 pub offset: i32,
437 pub length: u32,
438 pub flags: FPByteRangeLockFlags,
439}
440
441impl FPByteRangeLock {
442 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
443 let flags = FPByteRangeLockFlags::from(buf[0]);
446 let fork_id = u16::from_be_bytes(*buf[1..3].as_array().unwrap());
447 let offset = i32::from_be_bytes(*buf[3..7].as_array().unwrap());
448 let length = u32::from_be_bytes(*buf[7..11].as_array().unwrap());
449
450 Ok(Self {
451 fork_id,
452 offset,
453 length,
454 flags,
455 })
456 }
457}
458
459#[derive(Debug)]
460pub struct FPCloseFork {
461 pub fork_id: u16,
462}
463
464impl FPCloseFork {
465 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
466 let fork_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
467
468 Ok(Self { fork_id })
469 }
470}
471
472#[derive(Debug)]
473pub struct FPSetDirParms {
474 pub volume_id: u16,
475 pub directory_id: u32,
476 pub dir_bitmap: FPDirectoryBitmap,
477 pub path: MacString,
478 pub attributes: Option<FPFileAttributes>,
479 pub finder_info: Option<[u8; 32]>,
480 pub owner_id: Option<u32>,
481 pub group_id: Option<u32>,
482 pub owner_access: Option<FPAccessRights>,
483 pub group_access: Option<FPAccessRights>,
484 pub everyone_access: Option<FPAccessRights>,
485 pub user_access: Option<FPAccessRights>,
486}
487
488impl FPSetDirParms {
489 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
490 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
491 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
492 let dir_bitmap =
493 FPDirectoryBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
494 let _path_type = buf[8];
495 let path = MacString::try_from(&buf[9..])?;
496
497 let mut offset = 9 + path.byte_len();
498
499 let mut parsed_parms = Self {
500 volume_id,
501 directory_id,
502 dir_bitmap,
503 path,
504 attributes: None,
505 finder_info: None,
506 owner_id: None,
507 group_id: None,
508 owner_access: None,
509 group_access: None,
510 everyone_access: None,
511 user_access: None,
512 };
513
514 if dir_bitmap.contains(FPDirectoryBitmap::ATTRIBUTES) {
515 let attributes = FPFileAttributes::from(u16::from_be_bytes(
516 *buf[offset..offset + 2].as_array().unwrap(),
517 ));
518 parsed_parms.attributes = Some(attributes);
519 offset += 2;
520 }
521
522 if dir_bitmap.contains(FPDirectoryBitmap::FINDER_INFO) {
523 let mut finder_info = [0u8; 32];
524 finder_info.copy_from_slice(&buf[offset..offset + 32]);
525 parsed_parms.finder_info = Some(finder_info);
526 offset += 32;
527 }
528
529 if dir_bitmap.contains(FPDirectoryBitmap::OWNER_ID) {
530 let owner_id = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
531 parsed_parms.owner_id = Some(owner_id);
532 offset += 4;
533 }
534
535 if dir_bitmap.contains(FPDirectoryBitmap::GROUP_ID) {
536 let group_id = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
537 parsed_parms.group_id = Some(group_id);
538 offset += 4;
539 }
540
541 if dir_bitmap.contains(FPDirectoryBitmap::ACCESS_RIGHTS) {
542 let owner_access = FPAccessRights::from(buf[offset]);
543 parsed_parms.owner_access = Some(owner_access);
544 offset += 1;
545
546 let group_access = FPAccessRights::from(buf[offset]);
547 parsed_parms.group_access = Some(group_access);
548 offset += 1;
549
550 let everyone_access = FPAccessRights::from(buf[offset]);
551 parsed_parms.everyone_access = Some(everyone_access);
552 offset += 1;
553
554 let user_access = FPAccessRights::from(buf[offset]);
555 parsed_parms.everyone_access = Some(user_access);
556 }
557
558 Ok(parsed_parms)
559 }
560}
561
562#[derive(Debug)]
563pub struct FPRead {
564 pub fork_id: u16,
566 pub offset: u32,
568 pub req_count: u32,
571 pub newline_mask: u8,
575 pub newline_char: u8,
577}
578
579impl FPRead {
580 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
581 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
582 let offset = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
583 let req_count = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
584 let newline_mask = buf[10];
585 let newline_char = buf[11];
586
587 Ok(Self {
588 fork_id,
589 offset,
590 req_count,
591 newline_mask,
592 newline_char,
593 })
594 }
595
596 pub fn byte_matches_newline(&self, byte: u8) -> bool {
598 (byte & self.newline_mask) == self.newline_char
599 }
600}
601
602pub struct FPFlush {
603 pub volume_id: u16,
604}
605
606impl FPFlush {
607 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
608 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
609 Ok(Self { volume_id })
610 }
611}
612
613pub struct FPGetVolParms {
614 pub volume_id: u16,
615 pub bitmap: FPVolumeBitmap,
616}
617
618impl FPGetVolParms {
619 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
620 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
621 let bitmap = FPVolumeBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
622 Ok(Self { volume_id, bitmap })
623 }
624}
625
626pub struct FPDelete {
627 pub volume_id: u16,
628 pub directory_id: u32,
629 pub path: MacString,
630}
631
632impl FPDelete {
633 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
634 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
635 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
636 let _path_type = buf[6];
637 let path = MacString::try_from(&buf[7..])?;
638
639 Ok(Self {
640 volume_id,
641 directory_id,
642 path,
643 })
644 }
645}
646
647#[derive(Debug)]
648pub struct FPAddIcon {
649 pub dt_ref_num: u16,
650 pub file_creator: [u8; 4],
651 pub file_type: [u8; 4],
652 pub icon_type: u8,
653 pub icon_tag: u32,
654 pub size: u16,
655}
656
657impl FPAddIcon {
658 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
659 if buf.len() < 18 {
660 return Err(AfpError::InvalidSize);
661 }
662 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
663 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
664 let file_type: [u8; 4] = *buf[6..10].as_array().unwrap();
665 let icon_type = buf[10];
666 let icon_tag = u32::from_be_bytes(*buf[12..16].as_array().unwrap());
668 let size = u16::from_be_bytes(*buf[16..18].as_array().unwrap());
669
670 Ok(Self {
671 dt_ref_num,
672 file_creator,
673 file_type,
674 icon_type,
675 icon_tag,
676 size,
677 })
678 }
679}
680
681#[derive(Debug)]
682pub struct FPGetIcon {
683 pub dt_ref_num: u16,
684 pub file_creator: [u8; 4],
685 pub file_type: [u8; 4],
686 pub icon_type: u8,
687 pub size: u16,
688}
689
690impl FPGetIcon {
691 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
692 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
693 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
694 let file_type: [u8; 4] = *buf[6..10].as_array().unwrap();
695 let icon_type = buf[10];
696 let size = u16::from_be_bytes(*buf[12..14].as_array().unwrap());
698
699 Ok(Self {
700 dt_ref_num,
701 file_creator,
702 file_type,
703 icon_type,
704 size,
705 })
706 }
707}
708
709#[derive(Debug)]
710pub struct FPGetIconInfo {
711 pub dt_ref_num: u16,
712 pub file_creator: [u8; 4],
713 pub icon_type: u16,
714}
715
716impl FPGetIconInfo {
717 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
718 if buf.len() < 8 {
719 return Err(AfpError::InvalidSize);
720 }
721 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
722 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
723 let icon_type = u16::from_be_bytes(*buf[6..8].as_array().unwrap());
725
726 Ok(Self {
727 dt_ref_num,
728 file_creator,
729 icon_type,
730 })
731 }
732}
733
734#[derive(Debug)]
735pub struct FPGetComment {
736 pub dt_ref_num: u16,
737 pub directory_id: u32,
738 pub path: MacString,
739}
740
741impl FPGetComment {
742 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
743 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
744 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
745 let _path_type = buf[6];
746 let path = MacString::try_from(&buf[7..])?;
747
748 Ok(Self {
749 dt_ref_num,
750 directory_id,
751 path,
752 })
753 }
754}
755
756#[derive(Debug)]
757pub struct FPRemoveComment {
758 pub directory_id: u32,
759 pub path: MacString,
760}
761
762impl FPRemoveComment {
763 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
764 let directory_id = u32::from_be_bytes(*buf[1..5].as_array().unwrap());
765 let _path_type = buf[5];
766 let path = MacString::try_from(&buf[6..])?;
767
768 Ok(Self { directory_id, path })
769 }
770}
771
772#[derive(Debug)]
773pub struct FPAddComment {
774 pub dt_ref_num: u16,
775 pub directory_id: u32,
776 pub path: MacString,
777 pub comment: Vec<u8>,
778}
779
780impl FPAddComment {
781 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
782 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
783 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
784 let _path_type = buf[6];
785 let path = if buf.len() > 7 {
786 MacString::try_from(&buf[7..])?
787 } else {
788 MacString::from("")
789 };
790
791 let mut comment_offset = 7 + path.byte_len() - 1;
793 if comment_offset % 2 != 0 {
797 comment_offset += 1;
798 }
799
800 let comment_data = if comment_offset < buf.len() {
801 let comment_len = buf[comment_offset] as usize;
802 if comment_len > 0 && comment_offset + 1 + comment_len <= buf.len() {
803 buf[comment_offset + 1..comment_offset + 1 + comment_len].to_vec()
804 } else {
805 vec![]
806 }
807 } else {
808 vec![]
809 };
810
811 Ok(Self {
812 dt_ref_num,
813 directory_id,
814 path,
815 comment: comment_data,
816 })
817 }
818}
819
820#[derive(Debug)]
821pub struct FPWrite {
822 pub fork_id: u16,
823 pub offset: u32,
824 pub req_count: u32,
825 pub start_end_flag: bool,
826}
827
828impl FPWrite {
829 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
830 if buf.len() < 10 {
831 return Err(AfpError::InvalidSize);
832 }
833 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
834 let offset = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
835 let req_count_raw = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
836
837 let start_end_flag = (req_count_raw & 0x8000_0000) != 0;
839 let req_count = req_count_raw & 0x7FFF_FFFF;
840
841 Ok(Self {
842 fork_id,
843 offset,
844 req_count,
845 start_end_flag,
846 })
847 }
848}
849
850#[derive(Debug)]
854pub struct FPSetForkParms {
855 pub fork_ref_num: u16,
857 pub file_bitmap: FPFileBitmap,
859 pub data_fork_length: Option<u32>,
861 pub resource_fork_length: Option<u32>,
863}
864
865impl FPSetForkParms {
866 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
867 let fork_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
868 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
869
870 let mut offset = 4;
871
872 let data_fork_length = if file_bitmap.contains(FPFileBitmap::DATA_FORK_LENGTH) {
873 if offset + 4 > buf.len() {
874 return Err(AfpError::InvalidSize);
875 }
876 let val = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
877 offset += 4;
878 Some(val)
879 } else {
880 None
881 };
882
883 let resource_fork_length = if file_bitmap.contains(FPFileBitmap::RESOURCE_FORK_LENGTH) {
884 if offset + 4 > buf.len() {
885 return Err(AfpError::InvalidSize);
886 }
887 let val = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
888 Some(val)
889 } else {
890 None
891 };
892
893 Ok(Self {
894 fork_ref_num,
895 file_bitmap,
896 data_fork_length,
897 resource_fork_length,
898 })
899 }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905
906 #[test]
907 fn test_fp_enumerate_parse() {
908 let buf = &[
909 0x9u8, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x7, 0x7f, 0x13, 0x7f, 0x0, 0x45, 0x0, 0x1,
910 0x12, 0x0, 0x2, 0x0,
911 ];
912 let _enumerate = FPEnumerate::parse(&buf[2..]).unwrap();
913 }
914
915 #[test]
916 fn test_fp_rename_parse() {
917 #[rustfmt::skip]
919 let raw: &[u8] = &[
920 0x1c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, b'o', b'l', b'd', b'.', b't', b'x', b't', 0x02, 0x07, b'n', b'e', b'w', b'.', b't', b'x', b't', ];
930
931 let cmd = FPRename::parse(&raw[2..]).expect("parse should succeed");
932
933 assert_eq!(cmd.volume_id, 1);
934 assert_eq!(cmd.directory_id, 2);
935 assert_eq!(cmd.path_type, PathType::LongName);
936 assert_eq!(cmd.path.as_str(), "old.txt");
937 assert_eq!(cmd.new_name_path_type, PathType::LongName);
938 assert_eq!(cmd.new_name.as_str(), "new.txt");
939 }
940
941 #[test]
942 fn test_fp_move_and_rename_parse() {
943 #[rustfmt::skip]
946 let raw: &[u8] = &[
947 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x02, 0x12, b'a', b'p', b'p', b'l', b'e', b's', b'h', b'a', b'r', b'e',
954 b'.', b's', b'm', b'i', b'.', b'b', b'i', b'n', 0x02, 0x00, 0x02, 0x00, ];
960
961 let cmd = FPMoveAndRename::parse(&raw[2..]).expect("parse should succeed");
963
964 assert_eq!(cmd.volume_id, 1);
965 assert_eq!(cmd.src_directory_id, 2);
966 assert_eq!(cmd.dst_directory_id, 13);
967 assert_eq!(cmd.src_path_type, PathType::LongName);
968 assert_eq!(cmd.src_path.as_str(), "appleshare.smi.bin");
969 assert_eq!(cmd.dst_path_type, PathType::LongName);
970 assert_eq!(cmd.dst_path.as_str(), "");
971 assert_eq!(cmd.new_name_path_type, PathType::LongName);
972 assert_eq!(cmd.new_name.as_str(), "");
973 }
974}
975
976#[derive(Debug)]
977pub struct FPOpenDT {
978 pub volume_id: u16,
979}
980
981impl FPOpenDT {
982 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
983 if buf.len() < 2 {
984 return Err(AfpError::InvalidSize);
985 }
986 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
987 Ok(Self { volume_id })
988 }
989}
990
991#[derive(Debug)]
992pub struct FPGetForkParms {
993 pub fork_id: u16,
994 pub file_bitmap: FPFileBitmap,
995}
996
997impl FPGetForkParms {
998 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
999 if buf.len() < 4 {
1000 return Err(AfpError::InvalidSize);
1001 }
1002 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1003 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
1004 Ok(Self { fork_id, file_bitmap })
1005 }
1006}
1007
1008#[derive(Debug)]
1009pub struct FPCreateDir {
1010 pub volume_id: u16,
1011 pub directory_id: u32,
1012 pub path_type: PathType,
1013 pub path: MacString,
1014}
1015
1016impl FPCreateDir {
1017 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1018 if buf.len() < 8 {
1019 return Err(AfpError::InvalidSize);
1020 }
1021 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1022 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1023 let path_type = PathType::from(buf[6]);
1024 let path = MacString::try_from(&buf[7..])?;
1025 Ok(Self { volume_id, directory_id, path_type, path })
1026 }
1027}
1028
1029#[derive(Debug)]
1030pub struct FPCreateFile {
1031 pub create_flag: CreateFlag,
1032 pub volume_id: u16,
1033 pub directory_id: u32,
1034 pub path_type: PathType,
1035 pub path: MacString,
1036}
1037
1038impl FPCreateFile {
1039 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1040 if buf.len() < 9 {
1043 return Err(AfpError::InvalidSize);
1044 }
1045 let create_flag = CreateFlag::from(buf[0]);
1046 let volume_id = u16::from_be_bytes(*buf[1..3].as_array().unwrap());
1047 let directory_id = u32::from_be_bytes(*buf[3..7].as_array().unwrap());
1048 let path_type = PathType::from(buf[7]);
1049 let path = MacString::try_from(&buf[8..])?;
1050 Ok(Self { create_flag, volume_id, directory_id, path_type, path })
1051 }
1052}
1053
1054#[derive(Debug)]
1055pub struct FPOpenFork {
1056 pub volume_id: u16,
1057 pub directory_id: u32,
1058 pub file_bitmap: FPFileBitmap,
1059 pub access_mode: u16,
1060 pub path_type: PathType,
1061 pub path: MacString,
1062}
1063
1064impl FPOpenFork {
1065 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1066 if buf.len() < 12 {
1067 return Err(AfpError::InvalidSize);
1068 }
1069 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1070 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1071 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1072 let access_mode = u16::from_be_bytes(*buf[8..10].as_array().unwrap());
1073 let path_type = PathType::from(buf[10]);
1074 let path = MacString::try_from(&buf[11..])?;
1075 Ok(Self { volume_id, directory_id, file_bitmap, access_mode, path_type, path })
1076 }
1077}
1078
1079#[derive(Debug)]
1080pub struct FPGetFileDirParms {
1081 pub volume_id: u16,
1082 pub directory_id: u32,
1083 pub file_bitmap: FPFileBitmap,
1084 pub dir_bitmap: FPDirectoryBitmap,
1085 pub path_type: PathType,
1086 pub path: MacString,
1087}
1088
1089impl FPGetFileDirParms {
1090 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1091 if buf.len() < 12 {
1092 return Err(AfpError::InvalidSize);
1093 }
1094 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1095 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1096 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1097 let dir_bitmap = FPDirectoryBitmap::from(u16::from_be_bytes(*buf[8..10].as_array().unwrap()));
1098 let path_type = PathType::from(buf[10]);
1099 let path = MacString::try_from(&buf[11..])?;
1100 Ok(Self { volume_id, directory_id, file_bitmap, dir_bitmap, path_type, path })
1101 }
1102}
1103
1104#[derive(Debug)]
1105pub struct FPSetFileDirParms {
1106 pub volume_id: u16,
1107 pub directory_id: u32,
1108 pub file_bitmap: FPFileBitmap,
1110 pub path_type: PathType,
1111 pub path: MacString,
1112 pub params: Vec<u8>,
1113}
1114
1115impl FPSetFileDirParms {
1116 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1117 if buf.len() < 10 {
1118 return Err(AfpError::InvalidSize);
1119 }
1120 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1121 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1122 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1123 let path_type = PathType::from(buf[8]);
1124 let path = MacString::try_from(&buf[9..])?;
1125 let mut param_offset = 9 + path.byte_len();
1126 if param_offset % 2 != 0 {
1127 param_offset += 1;
1128 }
1129 let params = buf[param_offset..].to_vec();
1130 Ok(Self { volume_id, directory_id, file_bitmap, path_type, path, params })
1131 }
1132}
1133
1134#[derive(Debug)]
1144pub struct FPRename {
1145 pub volume_id: u16,
1146 pub directory_id: u32,
1147 pub path_type: PathType,
1148 pub path: MacString,
1149 pub new_name_path_type: PathType,
1150 pub new_name: MacString,
1151}
1152
1153impl FPRename {
1154 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1155 if buf.len() < 8 {
1156 return Err(AfpError::InvalidSize);
1157 }
1158 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1159 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1160 let path_type = PathType::from(buf[6]);
1161 let path = MacString::try_from(&buf[7..])?;
1162
1163 let new_name_type_offset = 7 + path.byte_len();
1164 if new_name_type_offset >= buf.len() {
1165 return Err(AfpError::InvalidSize);
1166 }
1167 let new_name_path_type = PathType::from(buf[new_name_type_offset]);
1168 let new_name = MacString::try_from(&buf[new_name_type_offset + 1..])?;
1169
1170 Ok(Self {
1171 volume_id,
1172 directory_id,
1173 path_type,
1174 path,
1175 new_name_path_type,
1176 new_name,
1177 })
1178 }
1179}
1180
1181#[derive(Debug)]
1195pub struct FPCopyFile {
1196 pub src_volume_id: u16,
1197 pub src_directory_id: u32,
1198 pub dst_volume_id: u16,
1199 pub dst_directory_id: u32,
1200 pub src_path_type: PathType,
1201 pub src_path: MacString,
1202 pub dst_path_type: PathType,
1203 pub dst_path: MacString,
1204 pub new_name_path_type: PathType,
1205 pub new_name: MacString,
1207}
1208
1209impl FPCopyFile {
1210 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1211 if buf.len() < 14 {
1212 return Err(AfpError::InvalidSize);
1213 }
1214 let src_volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
1215 let src_directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1216 let dst_volume_id = u16::from_be_bytes(*buf[6..8].as_array().unwrap());
1217 let dst_directory_id = u32::from_be_bytes(*buf[8..12].as_array().unwrap());
1218
1219 let src_path_type = PathType::from(buf[12]);
1220 let src_path = MacString::try_from(&buf[13..])?;
1221
1222 let dst_type_offset = 13 + src_path.byte_len();
1223 if dst_type_offset >= buf.len() {
1224 return Err(AfpError::InvalidSize);
1225 }
1226 let dst_path_type = PathType::from(buf[dst_type_offset]);
1227 let dst_path = MacString::try_from(&buf[dst_type_offset + 1..])?;
1228
1229 let new_name_type_offset = dst_type_offset + 1 + dst_path.byte_len();
1230 let (new_name_path_type, new_name) = if new_name_type_offset < buf.len() {
1231 let nnt = PathType::from(buf[new_name_type_offset]);
1232 let nn = if new_name_type_offset + 1 < buf.len() {
1233 MacString::try_from(&buf[new_name_type_offset + 1..])?
1234 } else {
1235 MacString::from("")
1236 };
1237 (nnt, nn)
1238 } else {
1239 (PathType::LongName, MacString::from(""))
1240 };
1241
1242 Ok(Self {
1243 src_volume_id,
1244 src_directory_id,
1245 dst_volume_id,
1246 dst_directory_id,
1247 src_path_type,
1248 src_path,
1249 dst_path_type,
1250 dst_path,
1251 new_name_path_type,
1252 new_name,
1253 })
1254 }
1255}
1256
1257#[derive(Debug)]
1270pub struct FPMoveAndRename {
1271 pub volume_id: u16,
1272 pub src_directory_id: u32,
1273 pub dst_directory_id: u32,
1274 pub src_path_type: PathType,
1275 pub src_path: MacString,
1276 pub dst_path_type: PathType,
1277 pub dst_path: MacString,
1278 pub new_name_path_type: PathType,
1279 pub new_name: MacString,
1281}
1282
1283impl FPMoveAndRename {
1284 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1285 if buf.len() < 12 {
1286 return Err(AfpError::InvalidSize);
1287 }
1288 let volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
1289 let src_directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1290 let dst_directory_id = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
1291 let src_path_type = PathType::from(buf[10]);
1292 let src_path = MacString::try_from(&buf[11..])?;
1293
1294 let dst_type_offset = 11 + src_path.byte_len();
1295 if dst_type_offset >= buf.len() {
1296 return Err(AfpError::InvalidSize);
1297 }
1298 let dst_path_type = PathType::from(buf[dst_type_offset]);
1299 let dst_path = MacString::try_from(&buf[dst_type_offset + 1..])?;
1300
1301 let new_name_type_offset = dst_type_offset + 1 + dst_path.byte_len();
1304 let (new_name_path_type, new_name) = if new_name_type_offset < buf.len() {
1305 let new_name_path_type = PathType::from(buf[new_name_type_offset]);
1306 let new_name = if new_name_type_offset + 1 < buf.len() {
1307 MacString::try_from(&buf[new_name_type_offset + 1..])?
1308 } else {
1309 MacString::new(String::new())
1310 };
1311 (new_name_path_type, new_name)
1312 } else {
1313 (PathType::LongName, MacString::new(String::new()))
1314 };
1315
1316 Ok(Self {
1317 volume_id,
1318 src_directory_id,
1319 dst_directory_id,
1320 src_path_type,
1321 src_path,
1322 dst_path_type,
1323 dst_path,
1324 new_name_path_type,
1325 new_name,
1326 })
1327 }
1328}