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_FLUSH_FORK: u8 = 11;
19pub const AFP_CMD_GET_FORK_PARMS: u8 = 14;
20pub const AFP_CMD_GET_SRVR_PARMS: u8 = 16;
21pub const AFP_CMD_GET_VOL_PARMS: u8 = 17;
22pub const AFP_CMD_LOGIN: u8 = 18;
23pub const AFP_CMD_LOGOUT: u8 = 20;
24pub const AFP_CMD_MOVE_AND_RENAME: u8 = 23;
25pub const AFP_CMD_OPEN_VOL: u8 = 24;
26pub const AFP_CMD_OPEN_FORK: u8 = 26;
27pub const AFP_CMD_READ: u8 = 27;
28pub const AFP_CMD_RENAME: u8 = 28;
29pub const AFP_CMD_SET_DIR_PARMS: u8 = 29;
30pub const AFP_CMD_SET_FORK_PARMS: u8 = 31;
31pub const AFP_CMD_WRITE: u8 = 33;
32pub const AFP_CMD_GET_FILE_DIR_PARMS: u8 = 34;
33pub const AFP_CMD_SET_FILE_DIR_PARMS: u8 = 35;
34pub const AFP_CMD_GET_SRVR_MSG: u8 = 38;
35pub const AFP_CMD_OPEN_DT: u8 = 48;
36pub const AFP_CMD_CLOSE_DT: u8 = 49;
37pub const AFP_CMD_GET_ICON: u8 = 51;
38pub const AFP_CMD_GTICNINFO: u8 = 52;
39pub const AFP_CMD_ADD_APPL: u8 = 53;
40pub const AFP_CMD_REMOVE_APPL: u8 = 54;
41pub const AFP_CMD_GET_APPL: u8 = 55;
42pub const AFP_CMD_ADD_COMMENT: u8 = 56;
43pub const AFP_CMD_REMOVE_COMMENT: u8 = 57;
44pub const AFP_CMD_GET_COMMENT: u8 = 58;
45pub const AFP_CMD_ADD_ICON: u8 = 192;
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum FPLoginAuth {
50 NoUserAuthent,
52
53 CleartxtPasswrd {
55 username: MacString,
56 password: [u8; 8], },
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct FPLogin {
63 pub afp_version: AfpVersion,
65
66 pub auth: FPLoginAuth,
68}
69
70impl FPLogin {
71 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
72 if buf.len() < 2 {
73 return Err(AfpError::InvalidSize);
74 }
75
76 let mut offset = 0;
77
78 let read_pstr = |offset: usize| -> Result<(MacString, usize), AfpError> {
80 let parsed = MacString::try_from(&buf[offset..])?;
81 let next_offset = offset + parsed.byte_len();
82 Ok((parsed, next_offset))
83 };
84
85 let (afp_version_str, next_offset) = read_pstr(offset)?;
87 let afp_version = AfpVersion::try_from(afp_version_str.as_str())?;
88 offset = next_offset;
89
90 let (uam_str, next_offset) = read_pstr(offset)?;
92 let uam = AfpUam::try_from(uam_str.as_str())?;
93 offset = next_offset;
94
95 let auth = match uam {
97 AfpUam::NoUserAuthent => FPLoginAuth::NoUserAuthent,
98
99 AfpUam::CleartxtPasswrd => {
100 let (username, next_offset) = read_pstr(offset)?;
102 offset = next_offset;
103
104 if offset + 8 > buf.len() {
106 return Err(AfpError::InvalidSize);
107 }
108 let mut password = [0u8; 8];
109 password.copy_from_slice(&buf[offset..offset + 8]);
110
111 FPLoginAuth::CleartxtPasswrd { username, password }
112 }
113
114 _ => return Err(AfpError::BadUam),
115 };
116
117 Ok(FPLogin { afp_version, auth })
118 }
119
120 pub fn to_bytes(&self) -> Result<Vec<u8>, AfpError> {
121 let mut buf = Vec::new();
122
123 let afp_version_str = self.afp_version.as_str();
125 let len = afp_version_str.len() as u8;
126 buf.push(len);
127 buf.extend_from_slice(afp_version_str.as_bytes());
128
129 match &self.auth {
131 FPLoginAuth::NoUserAuthent => {
132 let uam_str = AfpUam::NoUserAuthent.as_str();
133 let len = uam_str.len() as u8;
134 buf.push(len);
135 buf.extend_from_slice(uam_str.as_bytes());
136 }
138
139 FPLoginAuth::CleartxtPasswrd { username, password } => {
140 let uam_str = AfpUam::CleartxtPasswrd.as_str();
141 let len = uam_str.len() as u8;
142 buf.push(len);
143 buf.extend_from_slice(uam_str.as_bytes());
144
145 let mut username_buf = [0u8; 256];
147 let written = username.bytes(&mut username_buf)?;
148 buf.extend_from_slice(&username_buf[..written]);
149
150 buf.extend_from_slice(password);
152 }
153 }
154
155 Ok(buf)
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct FPGetSrvrInfo {
161 pub machine_type: MacString,
162 pub afp_versions: Vec<AfpVersion>,
163 pub uams: Vec<AfpUam>,
164 pub volume_icon: Option<[u8; 256]>,
165 pub flags: u16,
166 pub server_name: MacString,
167}
168
169impl FPGetSrvrInfo {
170 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
171 if buf.len() < 11 {
172 return Err(AfpError::InvalidSize);
174 }
175
176 let machine_type_offset = u16::from_be_bytes([buf[0], buf[1]]) as usize;
177 let afp_versions_offset = u16::from_be_bytes([buf[2], buf[3]]) as usize;
178 let uams_offset = u16::from_be_bytes([buf[4], buf[5]]) as usize;
179 let volume_icon_offset = u16::from_be_bytes([buf[6], buf[7]]) as usize;
180 let flags = u16::from_be_bytes([buf[8], buf[9]]);
181
182 let server_name_len = buf[10] as usize;
184 if 10 + 1 + server_name_len > buf.len() {
185 return Err(AfpError::InvalidSize);
186 }
187 let server_name = MacString::try_from(&buf[10..10 + 1 + server_name_len])?;
188
189 let read_pstr = |offset: usize| -> Result<MacString, AfpError> {
191 if offset >= buf.len() {
192 return Err(AfpError::InvalidSize);
193 }
194 let len = buf[offset] as usize;
195 if offset + 1 + len > buf.len() {
196 return Err(AfpError::InvalidSize);
197 }
198 MacString::try_from(&buf[offset..offset + 1 + len])
199 };
200
201 let read_pstr_list = |offset: usize| -> Result<Vec<MacString>, AfpError> {
203 if offset >= buf.len() {
204 return Err(AfpError::InvalidSize);
205 }
206 let count = buf[offset] as usize;
207 let mut strings: Vec<MacString> = Vec::with_capacity(count);
208 let mut current_pos = offset + 1;
209
210 for _ in 0..count {
211 if current_pos >= buf.len() {
212 return Err(AfpError::InvalidSize);
213 }
214
215 let new_string = MacString::try_from(&buf[current_pos..])?;
216 current_pos += new_string.byte_len();
217 strings.push(new_string);
218 }
219 Ok(strings)
220 };
221
222 let machine_type = read_pstr(machine_type_offset)?;
223 let afp_versions_strings: Vec<MacString> = read_pstr_list(afp_versions_offset)?;
224 let afp_versions: Vec<AfpVersion> = afp_versions_strings
225 .iter()
226 .map(|s| AfpVersion::try_from(s.as_str()))
227 .collect::<Result<Vec<_>, _>>()
228 .map_err(|_| AfpError::BadVersNum)?;
229 let uams_strings: Vec<MacString> = read_pstr_list(uams_offset)?;
230 let uams: Vec<AfpUam> = uams_strings
231 .iter()
232 .map(|s| AfpUam::try_from(s.as_str()).map_err(|_| AfpError::BadUam))
233 .collect::<Result<Vec<_>, _>>()?;
234
235 let volume_icon = if volume_icon_offset != 0 {
238 if volume_icon_offset + 256 > buf.len() {
239 return Err(AfpError::InvalidSize);
240 }
241 let mut icon = [0u8; 256];
242 icon.copy_from_slice(&buf[volume_icon_offset..volume_icon_offset + 256]);
243 Some(icon)
244 } else {
245 None
246 };
247
248 Ok(Self {
249 machine_type,
250 afp_versions,
251 uams,
252 volume_icon,
253 flags,
254 server_name,
255 })
256 }
257
258 pub fn to_bytes(&self) -> Result<Vec<u8>, AfpError> {
259 let mut buf = Vec::new();
260
261 let mut server_name_buf = [0u8; 256];
262 let written = self.server_name.bytes(&mut server_name_buf)?;
263 let server_name_len = (written - 1).min(255);
264
265 let mut base_offset = 10 + 1 + server_name_len;
267 let mut padding_needed = 0;
268
269 if base_offset % 2 != 0 {
272 base_offset += 1;
273 padding_needed = 1;
274 }
275
276 let mut current_offset = base_offset as u16;
277
278 let mut variable_data = Vec::new();
279
280 let mut tmp_macstr = [0u8; 256];
281 let machine_type_ptr = current_offset;
282 {
283 let written_mt = self.machine_type.bytes(&mut tmp_macstr)?;
284 variable_data.extend_from_slice(&tmp_macstr[..written_mt]);
285 }
286 current_offset = base_offset as u16 + variable_data.len() as u16;
287
288 let afp_versions_ptr = current_offset;
289 variable_data.push(self.afp_versions.len() as u8);
290 for v in &self.afp_versions {
291 let s = v.as_str();
292 let len = s.len() as u8;
293 variable_data.push(len);
294 variable_data.extend_from_slice(s.as_bytes());
295 }
296 current_offset = base_offset as u16 + variable_data.len() as u16;
297
298 let uams_ptr = current_offset;
299 variable_data.push(self.uams.len() as u8);
300 for u in &self.uams {
301 let s = u.as_str();
302 let len = s.len() as u8;
303 variable_data.push(len);
304 variable_data.extend_from_slice(s.as_bytes());
305 }
306 current_offset = base_offset as u16 + variable_data.len() as u16;
307
308 let volume_icon_ptr = if let Some(icon) = &self.volume_icon {
309 let ptr = current_offset;
310 variable_data.extend_from_slice(icon);
311 ptr
312 } else {
313 0
314 };
315
316 buf.extend_from_slice(&machine_type_ptr.to_be_bytes());
317 buf.extend_from_slice(&afp_versions_ptr.to_be_bytes());
318 buf.extend_from_slice(&uams_ptr.to_be_bytes());
319 buf.extend_from_slice(&volume_icon_ptr.to_be_bytes());
320 buf.extend_from_slice(&self.flags.to_be_bytes());
321
322 buf.push(server_name_len as u8);
323 buf.extend_from_slice(&server_name_buf[1..1 + server_name_len]);
324
325 if padding_needed > 0 {
326 buf.push(0); }
328
329 buf.extend_from_slice(&variable_data);
330
331 Ok(buf)
332 }
333}
334
335pub struct FPVolume {
336 pub has_password: bool,
337 pub has_config_info: bool,
338 pub name: MacString,
339}
340
341impl FPVolume {
342 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, AfpError> {
343 let target_size = 2 + self.name.len();
345
346 if buf.len() < target_size {
347 return Err(AfpError::InvalidSize);
348 }
349
350 let target = &mut buf[..target_size];
352
353 target[0] = (self.has_password as u8) << 7 | (self.has_config_info as u8) << 6;
354 target[1] = (self.name.byte_len() - 1) as u8;
355 self.name.bytes(&mut target[1..])?;
356
357 Ok(target_size)
358 }
359
360 pub fn size(&self) -> usize {
361 2 + self.name.byte_len() - 1
362 }
363}
364
365pub struct FPGetSrvrParms {
366 pub server_time: u32,
367 pub volumes: Vec<FPVolume>,
368}
369
370impl FPGetSrvrParms {
371 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, AfpError> {
372 let mut offset = 0;
373
374 let target_size = 5 + self.volumes.iter().map(|v| v.size()).sum::<usize>();
376
377 if buf.len() < target_size {
378 return Err(AfpError::InvalidSize);
379 }
380
381 let target = &mut buf[..target_size];
383
384 target[offset..offset + 4].copy_from_slice(&self.server_time.to_be_bytes());
385 offset += 4;
386 target[offset] = self.volumes.len() as u8;
387 offset += 1;
388
389 for volume in &self.volumes {
390 let volume_size = volume.to_bytes(&mut target[offset..])?;
391 offset += volume_size;
392 }
393
394 Ok(target_size)
395 }
396}
397
398#[derive(Debug)]
399pub struct FPEnumerate {
400 pub volume_id: u16,
401 pub directory_id: u32,
402 pub file_bitmap: FPFileBitmap,
403 pub directory_bitmap: FPDirectoryBitmap,
404 pub req_count: u16,
405 pub start_index: u16,
406 pub max_reply_size: u16,
407 pub path: MacString,
408}
409
410impl FPEnumerate {
411 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
412 let volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
413 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
414 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
415 let directory_bitmap =
416 FPDirectoryBitmap::from(u16::from_be_bytes(*buf[8..10].as_array().unwrap()));
417 let req_count = u16::from_be_bytes(*buf[10..12].as_array().unwrap());
418 let start_index = u16::from_be_bytes(*buf[12..14].as_array().unwrap());
419 let max_reply_size = u16::from_be_bytes(*buf[14..16].as_array().unwrap());
420 let _path_type = buf[16];
421 let path = MacString::try_from(&buf[17..])?;
422
423 Ok(Self {
424 volume_id,
425 directory_id,
426 file_bitmap,
427 directory_bitmap,
428 req_count,
429 start_index,
430 max_reply_size,
431 path,
432 })
433 }
434}
435
436#[derive(Debug)]
437pub struct FPByteRangeLock {
438 pub fork_id: u16,
439 pub offset: i32,
440 pub length: u32,
441 pub flags: FPByteRangeLockFlags,
442}
443
444impl FPByteRangeLock {
445 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
446 let flags = FPByteRangeLockFlags::from(buf[0]);
449 let fork_id = u16::from_be_bytes(*buf[1..3].as_array().unwrap());
450 let offset = i32::from_be_bytes(*buf[3..7].as_array().unwrap());
451 let length = u32::from_be_bytes(*buf[7..11].as_array().unwrap());
452
453 Ok(Self {
454 fork_id,
455 offset,
456 length,
457 flags,
458 })
459 }
460}
461
462#[derive(Debug)]
463pub struct FPCloseFork {
464 pub fork_id: u16,
465}
466
467impl FPCloseFork {
468 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
469 let fork_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
470
471 Ok(Self { fork_id })
472 }
473}
474
475#[derive(Debug)]
476pub struct FPSetDirParms {
477 pub volume_id: u16,
478 pub directory_id: u32,
479 pub dir_bitmap: FPDirectoryBitmap,
480 pub path: MacString,
481 pub attributes: Option<FPFileAttributes>,
482 pub finder_info: Option<[u8; 32]>,
483 pub owner_id: Option<u32>,
484 pub group_id: Option<u32>,
485 pub owner_access: Option<FPAccessRights>,
486 pub group_access: Option<FPAccessRights>,
487 pub everyone_access: Option<FPAccessRights>,
488 pub user_access: Option<FPAccessRights>,
489}
490
491impl FPSetDirParms {
492 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
493 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
494 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
495 let dir_bitmap =
496 FPDirectoryBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
497 let _path_type = buf[8];
498 let path = MacString::try_from(&buf[9..])?;
499
500 let mut offset = 9 + path.byte_len();
501
502 let mut parsed_parms = Self {
503 volume_id,
504 directory_id,
505 dir_bitmap,
506 path,
507 attributes: None,
508 finder_info: None,
509 owner_id: None,
510 group_id: None,
511 owner_access: None,
512 group_access: None,
513 everyone_access: None,
514 user_access: None,
515 };
516
517 if dir_bitmap.contains(FPDirectoryBitmap::ATTRIBUTES) {
518 let attributes = FPFileAttributes::from(u16::from_be_bytes(
519 *buf[offset..offset + 2].as_array().unwrap(),
520 ));
521 parsed_parms.attributes = Some(attributes);
522 offset += 2;
523 }
524
525 if dir_bitmap.contains(FPDirectoryBitmap::FINDER_INFO) {
526 let mut finder_info = [0u8; 32];
527 finder_info.copy_from_slice(&buf[offset..offset + 32]);
528 parsed_parms.finder_info = Some(finder_info);
529 offset += 32;
530 }
531
532 if dir_bitmap.contains(FPDirectoryBitmap::OWNER_ID) {
533 let owner_id = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
534 parsed_parms.owner_id = Some(owner_id);
535 offset += 4;
536 }
537
538 if dir_bitmap.contains(FPDirectoryBitmap::GROUP_ID) {
539 let group_id = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
540 parsed_parms.group_id = Some(group_id);
541 offset += 4;
542 }
543
544 if dir_bitmap.contains(FPDirectoryBitmap::ACCESS_RIGHTS) {
545 let owner_access = FPAccessRights::from(buf[offset]);
546 parsed_parms.owner_access = Some(owner_access);
547 offset += 1;
548
549 let group_access = FPAccessRights::from(buf[offset]);
550 parsed_parms.group_access = Some(group_access);
551 offset += 1;
552
553 let everyone_access = FPAccessRights::from(buf[offset]);
554 parsed_parms.everyone_access = Some(everyone_access);
555 offset += 1;
556
557 let user_access = FPAccessRights::from(buf[offset]);
558 parsed_parms.everyone_access = Some(user_access);
559 }
560
561 Ok(parsed_parms)
562 }
563}
564
565#[derive(Debug)]
566pub struct FPRead {
567 pub fork_id: u16,
569 pub offset: u32,
571 pub req_count: u32,
574 pub newline_mask: u8,
578 pub newline_char: u8,
580}
581
582impl FPRead {
583 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
584 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
585 let offset = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
586 let req_count = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
587 let newline_mask = buf[10];
588 let newline_char = buf[11];
589
590 Ok(Self {
591 fork_id,
592 offset,
593 req_count,
594 newline_mask,
595 newline_char,
596 })
597 }
598
599 pub fn byte_matches_newline(&self, byte: u8) -> bool {
601 (byte & self.newline_mask) == self.newline_char
602 }
603}
604
605pub struct FPFlush {
606 pub volume_id: u16,
607}
608
609impl FPFlush {
610 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
611 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
612 Ok(Self { volume_id })
613 }
614}
615
616pub struct FPFlushFork {
617 pub fork_id: u16,
618}
619
620impl FPFlushFork {
621 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
622 if buf.len() < 2 {
623 return Err(AfpError::InvalidSize);
624 }
625 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
626 Ok(Self { fork_id })
627 }
628}
629
630pub struct FPGetVolParms {
631 pub volume_id: u16,
632 pub bitmap: FPVolumeBitmap,
633}
634
635impl FPGetVolParms {
636 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
637 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
638 let bitmap = FPVolumeBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
639 Ok(Self { volume_id, bitmap })
640 }
641}
642
643pub struct FPDelete {
644 pub volume_id: u16,
645 pub directory_id: u32,
646 pub path: MacString,
647}
648
649impl FPDelete {
650 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
651 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
652 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
653 let _path_type = buf[6];
654 let path = MacString::try_from(&buf[7..])?;
655
656 Ok(Self {
657 volume_id,
658 directory_id,
659 path,
660 })
661 }
662}
663
664#[derive(Debug)]
665pub struct FPAddIcon {
666 pub dt_ref_num: u16,
667 pub file_creator: [u8; 4],
668 pub file_type: [u8; 4],
669 pub icon_type: u8,
670 pub icon_tag: u32,
671 pub size: u16,
672}
673
674impl FPAddIcon {
675 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
676 if buf.len() < 18 {
677 return Err(AfpError::InvalidSize);
678 }
679 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
680 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
681 let file_type: [u8; 4] = *buf[6..10].as_array().unwrap();
682 let icon_type = buf[10];
683 let icon_tag = u32::from_be_bytes(*buf[12..16].as_array().unwrap());
685 let size = u16::from_be_bytes(*buf[16..18].as_array().unwrap());
686
687 Ok(Self {
688 dt_ref_num,
689 file_creator,
690 file_type,
691 icon_type,
692 icon_tag,
693 size,
694 })
695 }
696}
697
698#[derive(Debug)]
699pub struct FPGetIcon {
700 pub dt_ref_num: u16,
701 pub file_creator: [u8; 4],
702 pub file_type: [u8; 4],
703 pub icon_type: u8,
704 pub size: u16,
705}
706
707impl FPGetIcon {
708 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
709 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
710 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
711 let file_type: [u8; 4] = *buf[6..10].as_array().unwrap();
712 let icon_type = buf[10];
713 let size = u16::from_be_bytes(*buf[12..14].as_array().unwrap());
715
716 Ok(Self {
717 dt_ref_num,
718 file_creator,
719 file_type,
720 icon_type,
721 size,
722 })
723 }
724}
725
726#[derive(Debug)]
727pub struct FPGetIconInfo {
728 pub dt_ref_num: u16,
729 pub file_creator: [u8; 4],
730 pub icon_type: u16,
731}
732
733impl FPGetIconInfo {
734 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
735 if buf.len() < 8 {
736 return Err(AfpError::InvalidSize);
737 }
738 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
739 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
740 let icon_type = u16::from_be_bytes(*buf[6..8].as_array().unwrap());
742
743 Ok(Self {
744 dt_ref_num,
745 file_creator,
746 icon_type,
747 })
748 }
749}
750
751#[derive(Debug)]
752pub struct FPGetComment {
753 pub dt_ref_num: u16,
754 pub directory_id: u32,
755 pub path: MacString,
756}
757
758impl FPGetComment {
759 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
760 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
761 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
762 let _path_type = buf[6];
763 let path = MacString::try_from(&buf[7..])?;
764
765 Ok(Self {
766 dt_ref_num,
767 directory_id,
768 path,
769 })
770 }
771}
772
773#[derive(Debug)]
774pub struct FPRemoveComment {
775 pub directory_id: u32,
776 pub path: MacString,
777}
778
779impl FPRemoveComment {
780 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
781 let directory_id = u32::from_be_bytes(*buf[1..5].as_array().unwrap());
782 let _path_type = buf[5];
783 let path = MacString::try_from(&buf[6..])?;
784
785 Ok(Self { directory_id, path })
786 }
787}
788
789#[derive(Debug)]
790pub struct FPAddComment {
791 pub dt_ref_num: u16,
792 pub directory_id: u32,
793 pub path: MacString,
794 pub comment: Vec<u8>,
795}
796
797impl FPAddComment {
798 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
799 let dt_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
800 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
801 let _path_type = buf[6];
802 let path = if buf.len() > 7 {
803 MacString::try_from(&buf[7..])?
804 } else {
805 MacString::from("")
806 };
807
808 let mut comment_offset = 7 + path.byte_len() - 1;
810 if comment_offset % 2 != 0 {
814 comment_offset += 1;
815 }
816
817 let comment_data = if comment_offset < buf.len() {
818 let comment_len = buf[comment_offset] as usize;
819 if comment_len > 0 && comment_offset + 1 + comment_len <= buf.len() {
820 buf[comment_offset + 1..comment_offset + 1 + comment_len].to_vec()
821 } else {
822 vec![]
823 }
824 } else {
825 vec![]
826 };
827
828 Ok(Self {
829 dt_ref_num,
830 directory_id,
831 path,
832 comment: comment_data,
833 })
834 }
835}
836
837#[derive(Debug)]
847pub struct FPAddAPPL {
848 pub dt_ref_num: u16,
849 pub file_creator: [u8; 4],
850 pub tag: u32,
851 pub directory_id: u32,
852 pub path: MacString,
853}
854
855impl FPAddAPPL {
856 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
857 if buf.len() < 16 {
858 return Err(AfpError::InvalidSize);
859 }
860 let dt_ref_num = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
861 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
862 let tag = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
863 let directory_id = u32::from_be_bytes(*buf[10..14].as_array().unwrap());
864 let _path_type = buf[14];
865 let path = MacString::try_from(&buf[15..])?;
866 Ok(Self { dt_ref_num, file_creator, tag, directory_id, path })
867 }
868}
869
870#[derive(Debug)]
879pub struct FPRemoveAPPL {
880 pub dt_ref_num: u16,
881 pub file_creator: [u8; 4],
882 pub directory_id: u32,
883 pub path: MacString,
884}
885
886impl FPRemoveAPPL {
887 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
888 if buf.len() < 12 {
889 return Err(AfpError::InvalidSize);
890 }
891 let dt_ref_num = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
892 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
893 let directory_id = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
894 let _path_type = buf[10];
895 let path = MacString::try_from(&buf[11..])?;
896 Ok(Self { dt_ref_num, file_creator, directory_id, path })
897 }
898}
899
900#[derive(Debug)]
909pub struct FPGetAPPL {
910 pub dt_ref_num: u16,
911 pub file_creator: [u8; 4],
912 pub appl_index: u16,
913}
914
915impl FPGetAPPL {
916 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
917 if buf.len() < 8 {
918 return Err(AfpError::InvalidSize);
919 }
920 let dt_ref_num = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
921 let file_creator: [u8; 4] = *buf[2..6].as_array().unwrap();
922 let appl_index = u16::from_be_bytes(*buf[6..8].as_array().unwrap());
923 Ok(Self { dt_ref_num, file_creator, appl_index })
924 }
925}
926
927#[derive(Debug)]
928pub struct FPWrite {
929 pub fork_id: u16,
930 pub offset: u32,
931 pub req_count: u32,
932 pub start_end_flag: bool,
933}
934
935impl FPWrite {
936 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
937 if buf.len() < 10 {
938 return Err(AfpError::InvalidSize);
939 }
940 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
941 let offset = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
942 let req_count_raw = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
943
944 let start_end_flag = (req_count_raw & 0x8000_0000) != 0;
946 let req_count = req_count_raw & 0x7FFF_FFFF;
947
948 Ok(Self {
949 fork_id,
950 offset,
951 req_count,
952 start_end_flag,
953 })
954 }
955}
956
957#[derive(Debug)]
961pub struct FPSetForkParms {
962 pub fork_ref_num: u16,
964 pub file_bitmap: FPFileBitmap,
966 pub data_fork_length: Option<u32>,
968 pub resource_fork_length: Option<u32>,
970}
971
972impl FPSetForkParms {
973 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
974 let fork_ref_num = u16::from_be_bytes(*buf[..2].as_array().unwrap());
975 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
976
977 let mut offset = 4;
978
979 let data_fork_length = if file_bitmap.contains(FPFileBitmap::DATA_FORK_LENGTH) {
980 if offset + 4 > buf.len() {
981 return Err(AfpError::InvalidSize);
982 }
983 let val = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
984 offset += 4;
985 Some(val)
986 } else {
987 None
988 };
989
990 let resource_fork_length = if file_bitmap.contains(FPFileBitmap::RESOURCE_FORK_LENGTH) {
991 if offset + 4 > buf.len() {
992 return Err(AfpError::InvalidSize);
993 }
994 let val = u32::from_be_bytes(*buf[offset..offset + 4].as_array().unwrap());
995 Some(val)
996 } else {
997 None
998 };
999
1000 Ok(Self {
1001 fork_ref_num,
1002 file_bitmap,
1003 data_fork_length,
1004 resource_fork_length,
1005 })
1006 }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011 use super::*;
1012
1013 #[test]
1014 fn test_fp_enumerate_parse() {
1015 let buf = &[
1016 0x9u8, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x7, 0x7f, 0x13, 0x7f, 0x0, 0x45, 0x0, 0x1,
1017 0x12, 0x0, 0x2, 0x0,
1018 ];
1019 let _enumerate = FPEnumerate::parse(&buf[2..]).unwrap();
1020 }
1021
1022 #[test]
1023 fn test_fp_rename_parse() {
1024 #[rustfmt::skip]
1026 let raw: &[u8] = &[
1027 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', ];
1037
1038 let cmd = FPRename::parse(&raw[2..]).expect("parse should succeed");
1039
1040 assert_eq!(cmd.volume_id, 1);
1041 assert_eq!(cmd.directory_id, 2);
1042 assert_eq!(cmd.path_type, PathType::LongName);
1043 assert_eq!(cmd.path.as_str(), "old.txt");
1044 assert_eq!(cmd.new_name_path_type, PathType::LongName);
1045 assert_eq!(cmd.new_name.as_str(), "new.txt");
1046 }
1047
1048 #[test]
1049 fn test_fp_move_and_rename_parse() {
1050 #[rustfmt::skip]
1053 let raw: &[u8] = &[
1054 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',
1061 b'.', b's', b'm', b'i', b'.', b'b', b'i', b'n', 0x02, 0x00, 0x02, 0x00, ];
1067
1068 let cmd = FPMoveAndRename::parse(&raw[2..]).expect("parse should succeed");
1070
1071 assert_eq!(cmd.volume_id, 1);
1072 assert_eq!(cmd.src_directory_id, 2);
1073 assert_eq!(cmd.dst_directory_id, 13);
1074 assert_eq!(cmd.src_path_type, PathType::LongName);
1075 assert_eq!(cmd.src_path.as_str(), "appleshare.smi.bin");
1076 assert_eq!(cmd.dst_path_type, PathType::LongName);
1077 assert_eq!(cmd.dst_path.as_str(), "");
1078 assert_eq!(cmd.new_name_path_type, PathType::LongName);
1079 assert_eq!(cmd.new_name.as_str(), "");
1080 }
1081}
1082
1083#[derive(Debug)]
1084pub struct FPOpenDT {
1085 pub volume_id: u16,
1086}
1087
1088impl FPOpenDT {
1089 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1090 if buf.len() < 2 {
1091 return Err(AfpError::InvalidSize);
1092 }
1093 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1094 Ok(Self { volume_id })
1095 }
1096}
1097
1098#[derive(Debug)]
1099pub struct FPGetForkParms {
1100 pub fork_id: u16,
1101 pub file_bitmap: FPFileBitmap,
1102}
1103
1104impl FPGetForkParms {
1105 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1106 if buf.len() < 4 {
1107 return Err(AfpError::InvalidSize);
1108 }
1109 let fork_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1110 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[2..4].as_array().unwrap()));
1111 Ok(Self { fork_id, file_bitmap })
1112 }
1113}
1114
1115#[derive(Debug)]
1116pub struct FPCreateDir {
1117 pub volume_id: u16,
1118 pub directory_id: u32,
1119 pub path_type: PathType,
1120 pub path: MacString,
1121}
1122
1123impl FPCreateDir {
1124 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1125 if buf.len() < 8 {
1126 return Err(AfpError::InvalidSize);
1127 }
1128 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1129 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1130 let path_type = PathType::from(buf[6]);
1131 let path = MacString::try_from(&buf[7..])?;
1132 Ok(Self { volume_id, directory_id, path_type, path })
1133 }
1134}
1135
1136#[derive(Debug)]
1137pub struct FPCreateFile {
1138 pub create_flag: CreateFlag,
1139 pub volume_id: u16,
1140 pub directory_id: u32,
1141 pub path_type: PathType,
1142 pub path: MacString,
1143}
1144
1145impl FPCreateFile {
1146 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1147 if buf.len() < 9 {
1150 return Err(AfpError::InvalidSize);
1151 }
1152 let create_flag = CreateFlag::from(buf[0]);
1153 let volume_id = u16::from_be_bytes(*buf[1..3].as_array().unwrap());
1154 let directory_id = u32::from_be_bytes(*buf[3..7].as_array().unwrap());
1155 let path_type = PathType::from(buf[7]);
1156 let path = MacString::try_from(&buf[8..])?;
1157 Ok(Self { create_flag, volume_id, directory_id, path_type, path })
1158 }
1159}
1160
1161#[derive(Debug)]
1162pub struct FPOpenFork {
1163 pub volume_id: u16,
1164 pub directory_id: u32,
1165 pub file_bitmap: FPFileBitmap,
1166 pub access_mode: u16,
1167 pub path_type: PathType,
1168 pub path: MacString,
1169}
1170
1171impl FPOpenFork {
1172 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1173 if buf.len() < 12 {
1174 return Err(AfpError::InvalidSize);
1175 }
1176 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1177 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1178 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1179 let access_mode = u16::from_be_bytes(*buf[8..10].as_array().unwrap());
1180 let path_type = PathType::from(buf[10]);
1181 let path = MacString::try_from(&buf[11..])?;
1182 Ok(Self { volume_id, directory_id, file_bitmap, access_mode, path_type, path })
1183 }
1184}
1185
1186#[derive(Debug)]
1187pub struct FPGetFileDirParms {
1188 pub volume_id: u16,
1189 pub directory_id: u32,
1190 pub file_bitmap: FPFileBitmap,
1191 pub dir_bitmap: FPDirectoryBitmap,
1192 pub path_type: PathType,
1193 pub path: MacString,
1194}
1195
1196impl FPGetFileDirParms {
1197 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1198 if buf.len() < 12 {
1199 return Err(AfpError::InvalidSize);
1200 }
1201 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1202 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1203 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1204 let dir_bitmap = FPDirectoryBitmap::from(u16::from_be_bytes(*buf[8..10].as_array().unwrap()));
1205 let path_type = PathType::from(buf[10]);
1206 let path = MacString::try_from(&buf[11..])?;
1207 Ok(Self { volume_id, directory_id, file_bitmap, dir_bitmap, path_type, path })
1208 }
1209}
1210
1211#[derive(Debug)]
1212pub struct FPSetFileDirParms {
1213 pub volume_id: u16,
1214 pub directory_id: u32,
1215 pub file_bitmap: FPFileBitmap,
1217 pub path_type: PathType,
1218 pub path: MacString,
1219 pub params: Vec<u8>,
1220}
1221
1222impl FPSetFileDirParms {
1223 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1224 if buf.len() < 10 {
1225 return Err(AfpError::InvalidSize);
1226 }
1227 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1228 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1229 let file_bitmap = FPFileBitmap::from(u16::from_be_bytes(*buf[6..8].as_array().unwrap()));
1230 let path_type = PathType::from(buf[8]);
1231 let path = MacString::try_from(&buf[9..])?;
1232 let mut param_offset = 9 + path.byte_len();
1233 if param_offset % 2 != 0 {
1234 param_offset += 1;
1235 }
1236 let params = buf[param_offset..].to_vec();
1237 Ok(Self { volume_id, directory_id, file_bitmap, path_type, path, params })
1238 }
1239}
1240
1241#[derive(Debug)]
1251pub struct FPRename {
1252 pub volume_id: u16,
1253 pub directory_id: u32,
1254 pub path_type: PathType,
1255 pub path: MacString,
1256 pub new_name_path_type: PathType,
1257 pub new_name: MacString,
1258}
1259
1260impl FPRename {
1261 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1262 if buf.len() < 8 {
1263 return Err(AfpError::InvalidSize);
1264 }
1265 let volume_id = u16::from_be_bytes(*buf[..2].as_array().unwrap());
1266 let directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1267 let path_type = PathType::from(buf[6]);
1268 let path = MacString::try_from(&buf[7..])?;
1269
1270 let new_name_type_offset = 7 + path.byte_len();
1271 if new_name_type_offset >= buf.len() {
1272 return Err(AfpError::InvalidSize);
1273 }
1274 let new_name_path_type = PathType::from(buf[new_name_type_offset]);
1275 let new_name = MacString::try_from(&buf[new_name_type_offset + 1..])?;
1276
1277 Ok(Self {
1278 volume_id,
1279 directory_id,
1280 path_type,
1281 path,
1282 new_name_path_type,
1283 new_name,
1284 })
1285 }
1286}
1287
1288#[derive(Debug)]
1302pub struct FPCopyFile {
1303 pub src_volume_id: u16,
1304 pub src_directory_id: u32,
1305 pub dst_volume_id: u16,
1306 pub dst_directory_id: u32,
1307 pub src_path_type: PathType,
1308 pub src_path: MacString,
1309 pub dst_path_type: PathType,
1310 pub dst_path: MacString,
1311 pub new_name_path_type: PathType,
1312 pub new_name: MacString,
1314}
1315
1316impl FPCopyFile {
1317 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1318 if buf.len() < 14 {
1319 return Err(AfpError::InvalidSize);
1320 }
1321 let src_volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
1322 let src_directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1323 let dst_volume_id = u16::from_be_bytes(*buf[6..8].as_array().unwrap());
1324 let dst_directory_id = u32::from_be_bytes(*buf[8..12].as_array().unwrap());
1325
1326 let src_path_type = PathType::from(buf[12]);
1327 let src_path = MacString::try_from(&buf[13..])?;
1328
1329 let dst_type_offset = 13 + src_path.byte_len();
1330 if dst_type_offset >= buf.len() {
1331 return Err(AfpError::InvalidSize);
1332 }
1333 let dst_path_type = PathType::from(buf[dst_type_offset]);
1334 let dst_path = MacString::try_from(&buf[dst_type_offset + 1..])?;
1335
1336 let new_name_type_offset = dst_type_offset + 1 + dst_path.byte_len();
1337 let (new_name_path_type, new_name) = if new_name_type_offset < buf.len() {
1338 let nnt = PathType::from(buf[new_name_type_offset]);
1339 let nn = if new_name_type_offset + 1 < buf.len() {
1340 MacString::try_from(&buf[new_name_type_offset + 1..])?
1341 } else {
1342 MacString::from("")
1343 };
1344 (nnt, nn)
1345 } else {
1346 (PathType::LongName, MacString::from(""))
1347 };
1348
1349 Ok(Self {
1350 src_volume_id,
1351 src_directory_id,
1352 dst_volume_id,
1353 dst_directory_id,
1354 src_path_type,
1355 src_path,
1356 dst_path_type,
1357 dst_path,
1358 new_name_path_type,
1359 new_name,
1360 })
1361 }
1362}
1363
1364#[derive(Debug)]
1377pub struct FPMoveAndRename {
1378 pub volume_id: u16,
1379 pub src_directory_id: u32,
1380 pub dst_directory_id: u32,
1381 pub src_path_type: PathType,
1382 pub src_path: MacString,
1383 pub dst_path_type: PathType,
1384 pub dst_path: MacString,
1385 pub new_name_path_type: PathType,
1386 pub new_name: MacString,
1388}
1389
1390impl FPMoveAndRename {
1391 pub fn parse(buf: &[u8]) -> Result<Self, AfpError> {
1392 if buf.len() < 12 {
1393 return Err(AfpError::InvalidSize);
1394 }
1395 let volume_id = u16::from_be_bytes(*buf[0..2].as_array().unwrap());
1396 let src_directory_id = u32::from_be_bytes(*buf[2..6].as_array().unwrap());
1397 let dst_directory_id = u32::from_be_bytes(*buf[6..10].as_array().unwrap());
1398 let src_path_type = PathType::from(buf[10]);
1399 let src_path = MacString::try_from(&buf[11..])?;
1400
1401 let dst_type_offset = 11 + src_path.byte_len();
1402 if dst_type_offset >= buf.len() {
1403 return Err(AfpError::InvalidSize);
1404 }
1405 let dst_path_type = PathType::from(buf[dst_type_offset]);
1406 let dst_path = MacString::try_from(&buf[dst_type_offset + 1..])?;
1407
1408 let new_name_type_offset = dst_type_offset + 1 + dst_path.byte_len();
1411 let (new_name_path_type, new_name) = if new_name_type_offset < buf.len() {
1412 let new_name_path_type = PathType::from(buf[new_name_type_offset]);
1413 let new_name = if new_name_type_offset + 1 < buf.len() {
1414 MacString::try_from(&buf[new_name_type_offset + 1..])?
1415 } else {
1416 MacString::new(String::new())
1417 };
1418 (new_name_path_type, new_name)
1419 } else {
1420 (PathType::LongName, MacString::new(String::new()))
1421 };
1422
1423 Ok(Self {
1424 volume_id,
1425 src_directory_id,
1426 dst_directory_id,
1427 src_path_type,
1428 src_path,
1429 dst_path_type,
1430 dst_path,
1431 new_name_path_type,
1432 new_name,
1433 })
1434 }
1435}