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