bt_obex/header/
mod.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use chrono::NaiveDateTime;
6use fuchsia_bluetooth::types::Uuid;
7use log::trace;
8use packet_encoding::{decodable_enum, Decodable, Encodable};
9
10pub use self::header_set::HeaderSet;
11use self::obex_string::ObexString;
12use crate::error::PacketError;
13
14/// A collection of Headers sent & received in an OBEX packet.
15pub mod header_set;
16/// Definition of an OBEX-specific String type used in Headers.
17mod obex_string;
18
19/// Uniquely identifies an OBEX connection between a Client and Server.
20/// Defined in OBEX 1.5 Section 2.2.11.
21#[derive(Copy, Clone, Debug, PartialEq)]
22pub struct ConnectionIdentifier(pub(crate) u32);
23
24impl ConnectionIdentifier {
25    pub fn id(&self) -> u32 {
26        self.0
27    }
28}
29
30impl TryFrom<u32> for ConnectionIdentifier {
31    type Error = PacketError;
32
33    fn try_from(src: u32) -> Result<Self, Self::Error> {
34        // Per OBEX 1.5 Section 2.2.11, 0xffffffff is a reserved ID and considered invalid.
35        if src == u32::MAX {
36            return Err(PacketError::Reserved);
37        }
38        Ok(Self(src))
39    }
40}
41
42/// Specifies the type of action of the Action Operation.
43/// Defined in OBEX 1.5 Section 2.2.20.
44#[derive(Copy, Clone, Debug, PartialEq)]
45#[repr(u8)]
46pub enum ActionIdentifier {
47    Copy = 0x00,
48    MoveOrRename = 0x01,
49    SetPermissions = 0x02,
50    /// 0x03 - 0x7f is RFA.
51    /// 0x80 - 0xff is reserved for vendor specific actions.
52    Vendor(u8),
53}
54
55impl ActionIdentifier {
56    fn is_vendor(id_raw: u8) -> bool {
57        id_raw >= 0x80
58    }
59}
60
61impl From<&ActionIdentifier> for u8 {
62    fn from(src: &ActionIdentifier) -> u8 {
63        match src {
64            ActionIdentifier::Copy => 0x00,
65            ActionIdentifier::MoveOrRename => 0x01,
66            ActionIdentifier::SetPermissions => 0x02,
67            ActionIdentifier::Vendor(v) => *v,
68        }
69    }
70}
71
72impl TryFrom<u8> for ActionIdentifier {
73    type Error = PacketError;
74
75    fn try_from(src: u8) -> Result<ActionIdentifier, Self::Error> {
76        match src {
77            0x00 => Ok(ActionIdentifier::Copy),
78            0x01 => Ok(ActionIdentifier::MoveOrRename),
79            0x02 => Ok(ActionIdentifier::SetPermissions),
80            v if ActionIdentifier::is_vendor(v) => Ok(ActionIdentifier::Vendor(v)),
81            _v => Err(Self::Error::Reserved),
82        }
83    }
84}
85
86/// Describes the type of an OBEX object. Used in `Header::Type`.
87///
88/// See OBEX 1.5 Section 2.2.3.
89#[derive(Clone, Debug, PartialEq)]
90pub struct MimeType(String);
91
92impl MimeType {
93    pub fn len(&self) -> usize {
94        // The encoded format for the MimeType includes a 1 byte null terminator.
95        self.0.len() + 1
96    }
97
98    pub fn to_be_bytes(&self) -> Vec<u8> {
99        // The Type header is encoded as a byte sequence of null terminated ASCII text.
100        let mut encoded_buf: Vec<u8> = self.0.clone().into_bytes();
101        encoded_buf.push(0); // Add the null terminator
102        encoded_buf
103    }
104}
105
106impl TryFrom<&[u8]> for MimeType {
107    type Error = PacketError;
108
109    fn try_from(src: &[u8]) -> Result<Self, Self::Error> {
110        if src.len() == 0 {
111            return Err(PacketError::data("empty Type header"));
112        }
113
114        let mut text = String::from_utf8(src.to_vec()).map_err(PacketError::external)?;
115        // The Type Header is represented as null terminated UTF-8 text. See OBEX 1.5 Section 2.2.3.
116        if !text.ends_with('\0') {
117            return Err(PacketError::data("Type missing null terminator"));
118        }
119
120        let _ = text.pop();
121        Ok(Self(text))
122    }
123}
124
125impl From<String> for MimeType {
126    fn from(src: String) -> MimeType {
127        MimeType(src)
128    }
129}
130
131impl From<&str> for MimeType {
132    fn from(src: &str) -> MimeType {
133        MimeType(src.to_string())
134    }
135}
136
137/// Represents a user-defined Header type.
138// TODO(https://fxbug.dev/42072539): This representation may change depending on what kind of user
139// headers we expect.
140#[derive(Clone, Debug, PartialEq)]
141pub struct UserDefinedHeader {
142    /// The Header Identifier (HI) can be any value between 0x30 and 0x3f. See
143    /// `HeaderIdentifier::User` for more details.
144    identifier: u8,
145    /// The user data.
146    value: Vec<u8>,
147}
148
149/// Used to support a variety of application request/response parameters
150/// which are represented in Tag-Length-Value triplets.
151pub type TypeValue = (u8, Vec<u8>);
152
153/// Represents the vector of TypeLengthValue triplets in application
154/// parameter headers.
155/// See OBEX spec v1.5 Section 2.2.12.
156#[derive(Clone, Debug, PartialEq)]
157pub struct TagLengthValue(Vec<TypeValue>);
158
159impl TagLengthValue {
160    /// Minimum data length of a single triplet includes the tag and length byte.
161    const MIN_TRIPLET_LENGTH_BYTES: usize = 2;
162
163    pub fn iter(&self) -> std::slice::Iter<'_, (u8, Vec<u8>)> {
164        self.0.iter()
165    }
166}
167
168impl From<Vec<TypeValue>> for TagLengthValue {
169    fn from(value: Vec<TypeValue>) -> Self {
170        Self(value)
171    }
172}
173
174impl Encodable for TagLengthValue {
175    type Error = PacketError;
176
177    fn encoded_len(&self) -> core::primitive::usize {
178        self.0
179            .iter()
180            .fold(0, |init, triplet| init + Self::MIN_TRIPLET_LENGTH_BYTES + triplet.1.len())
181    }
182
183    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
184        if buf.len() < self.encoded_len() {
185            return Err(PacketError::BufferTooSmall);
186        }
187        let mut start_index = 0;
188        for (tag, value) in &self.0 {
189            buf[start_index] = *tag;
190            buf[start_index + 1] = value.len() as u8;
191            buf[start_index + 2..start_index + 2 + value.len()].copy_from_slice(value.as_slice());
192            start_index += 2 + value.len();
193        }
194        Ok(())
195    }
196}
197
198impl TryFrom<&[u8]> for TagLengthValue {
199    type Error = PacketError;
200
201    /// Attempts to parse application parameters header from raw data.
202    fn try_from(raw_data: &[u8]) -> Result<Self, Self::Error> {
203        let data_len = raw_data.len();
204
205        let mut decoded_len = 0;
206        let mut list = vec![];
207
208        while decoded_len < data_len {
209            if data_len < (decoded_len + Self::MIN_TRIPLET_LENGTH_BYTES) {
210                return Err(PacketError::DataLength);
211            }
212            let tag: u8 = raw_data[decoded_len];
213            let value_len = raw_data[decoded_len + 1] as usize;
214            if data_len < (decoded_len + Self::MIN_TRIPLET_LENGTH_BYTES + value_len) {
215                return Err(PacketError::DataLength);
216            }
217            let value_start_idx = decoded_len + Self::MIN_TRIPLET_LENGTH_BYTES;
218            let value = (&raw_data[value_start_idx..value_start_idx + value_len]).into();
219            let _ = list.push((tag, value));
220            decoded_len += Self::MIN_TRIPLET_LENGTH_BYTES + value_len;
221        }
222        Ok(TagLengthValue(list))
223    }
224}
225
226decodable_enum! {
227    /// Value indicating support for the Single Response Mode (SRM) feature in OBEX. Used as part
228    /// of `Header::SingleResponseMode`.
229    /// Defined in OBEX 1.5 Section 2.2.23.
230    pub enum SingleResponseMode<u8, PacketError, Reserved> {
231        // A request to disable SRM for the current operation.
232        Disable = 0x00,
233        // A request to enable SRM for the current operation.
234        Enable = 0x01,
235        // Per GOEP 2.1.1 Section 4.6, the `Supported` SRM flag is not used.
236        // Supported = 0x02,
237    }
238}
239
240impl From<bool> for SingleResponseMode {
241    fn from(src: bool) -> SingleResponseMode {
242        if src {
243            SingleResponseMode::Enable
244        } else {
245            SingleResponseMode::Disable
246        }
247    }
248}
249
250impl From<SingleResponseMode> for Header {
251    fn from(src: SingleResponseMode) -> Header {
252        Header::SingleResponseMode(src)
253    }
254}
255
256decodable_enum! {
257    /// The Header Encoding is the upper 2 bits of the Header Identifier (HI) and describes the type
258    /// of payload included in the Header.
259    /// Defined in OBEX 1.5 Section 2.1.
260    enum HeaderEncoding<u8, PacketError, HeaderEncoding> {
261        /// A Header with null terminated Unicode text. The text is encoded in UTF-16 format. The
262        /// text length is encoded as a two byte unsigned integer.
263        Text = 0x00,
264        /// A Header with a byte sequence. The sequence length is encoded as a two byte unsigned
265        /// integer.
266        Bytes = 0x40,
267        /// A Header with a 1-byte payload.
268        OneByte = 0x80,
269        /// A Header with a 4-byte payload.
270        FourBytes = 0xC0,
271    }
272}
273
274/// The OBEX Header Identifier (HI) identifies the type of OBEX packet.
275///
276/// The HI is a one-byte unsigned value and is split into the upper 2 bits and lower 6 bits. The
277/// upper 2 bits indicate the header encoding and the lower 6 bits indicate the type of the
278/// header.
279/// Defined in OBEX 1.5 Section 2.1.
280#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
281#[repr(u8)]
282pub enum HeaderIdentifier {
283    /// Number of objects.
284    Count = 0xC0,
285    /// Name of the object (typically a file name).
286    Name = 0x01,
287    /// Type of object (e.g. text, html, ...)
288    Type = 0x42,
289    /// The length of the object in bytes.
290    Length = 0xC3,
291    /// Date/time stamp - ISO 8601. This representation is preferred.
292    TimeIso8601 = 0x44,
293    /// Date/time stamp - 4 byte representation.
294    Time4Byte = 0xC4,
295    /// Text description of the object.
296    Description = 0x05,
297    /// Name of the service that the operation is targeting.
298    Target = 0x46,
299    /// An HTTP 1.x header.
300    Http = 0x47,
301    /// A chunk of the object body.
302    Body = 0x48,
303    /// The final chunk of the object body.
304    EndOfBody = 0x49,
305    /// Identifies the OBEX application session.
306    Who = 0x4A,
307    /// An identifier associated with the OBEX connection - used for connection multiplexing.
308    ConnectionId = 0xCB,
309    /// Extended information about the OBEX connection.
310    ApplicationParameters = 0x4C,
311    /// Authentication digest challenge.
312    AuthenticationChallenge = 0x4D,
313    /// Authentication digest response.
314    AuthenticationResponse = 0x4E,
315    /// Indicates the creator of the object.
316    CreatorId = 0xCF,
317    /// Uniquely identifies the network client.
318    WanUuid = 0x50,
319    /// Class of an OBEX object,
320    ObjectClass = 0x51,
321    /// Parameters associated with the OBEX session.
322    SessionParameters = 0x52,
323    /// Sequence number included in each OBEX packet - used for reliability.
324    SessionSequenceNumber = 0x93,
325    /// Specifies the type of ACTION Operation.
326    ActionId = 0x94,
327    /// The destination for an object - used in certain ACTION Operations.
328    DestName = 0x15,
329    /// Bit mask for setting permissions.
330    Permissions = 0xD6,
331    /// Indicates that Single Response Mode (SRM) should be used.
332    SingleResponseMode = 0x97,
333    /// Specifies the parameters used during SRM.
334    SingleResponseModeParameters = 0x98,
335    // 0x30 to 0x3F, 0x70 to 0x7F, 0xB0 to 0xBF, 0xF0 to 0xFF, is user defined.
336    User(u8),
337    // 0x19 to 0x2F, 0x59 to 0x6F, 0x99 to 0xAF, 0xD9 to 0xEF, is RFA.
338}
339
340impl HeaderIdentifier {
341    fn is_user(id: u8) -> bool {
342        // The user-defined space is between 0x30 and 0x3f and includes all combinations of the
343        // upper 2 bits of the `id`.
344        let lower_6_bits = id & 0x3f;
345        lower_6_bits >= 0x30 && lower_6_bits <= 0x3f
346    }
347
348    fn is_reserved(id: u8) -> bool {
349        // The reserved space is between 0x19 and 0x2f and includes all combinations of the
350        // upper 2 bits of the `id`.
351        let lower_6_bits = id & 0x3f;
352        lower_6_bits >= 0x19 && lower_6_bits <= 0x2f
353    }
354
355    fn encoding(&self) -> HeaderEncoding {
356        let id_raw: u8 = self.into();
357        // The encoding is the upper 2 bits of the HeaderIdentifier.
358        HeaderEncoding::try_from(id_raw & 0xc0).expect("valid Header encoding")
359    }
360}
361
362impl TryFrom<u8> for HeaderIdentifier {
363    type Error = PacketError;
364
365    fn try_from(src: u8) -> Result<Self, Self::Error> {
366        match src {
367            0xC0 => Ok(Self::Count),
368            0x01 => Ok(Self::Name),
369            0x42 => Ok(Self::Type),
370            0xC3 => Ok(Self::Length),
371            0x44 => Ok(Self::TimeIso8601),
372            0xC4 => Ok(Self::Time4Byte),
373            0x05 => Ok(Self::Description),
374            0x46 => Ok(Self::Target),
375            0x47 => Ok(Self::Http),
376            0x48 => Ok(Self::Body),
377            0x49 => Ok(Self::EndOfBody),
378            0x4A => Ok(Self::Who),
379            0xCB => Ok(Self::ConnectionId),
380            0x4C => Ok(Self::ApplicationParameters),
381            0x4D => Ok(Self::AuthenticationChallenge),
382            0x4E => Ok(Self::AuthenticationResponse),
383            0xCF => Ok(Self::CreatorId),
384            0x50 => Ok(Self::WanUuid),
385            0x51 => Ok(Self::ObjectClass),
386            0x52 => Ok(Self::SessionParameters),
387            0x93 => Ok(Self::SessionSequenceNumber),
388            0x94 => Ok(Self::ActionId),
389            0x15 => Ok(Self::DestName),
390            0xD6 => Ok(Self::Permissions),
391            0x97 => Ok(Self::SingleResponseMode),
392            0x98 => Ok(Self::SingleResponseModeParameters),
393            id if HeaderIdentifier::is_user(id) => Ok(Self::User(id)),
394            id if HeaderIdentifier::is_reserved(id) => Err(Self::Error::Reserved),
395            id => Err(Self::Error::Identifier(id)),
396        }
397    }
398}
399
400impl Into<u8> for &HeaderIdentifier {
401    fn into(self) -> u8 {
402        match &self {
403            HeaderIdentifier::Count => 0xC0,
404            HeaderIdentifier::Name => 0x01,
405            HeaderIdentifier::Type => 0x42,
406            HeaderIdentifier::Length => 0xC3,
407            HeaderIdentifier::TimeIso8601 => 0x44,
408            HeaderIdentifier::Time4Byte => 0xC4,
409            HeaderIdentifier::Description => 0x05,
410            HeaderIdentifier::Target => 0x46,
411            HeaderIdentifier::Http => 0x47,
412            HeaderIdentifier::Body => 0x48,
413            HeaderIdentifier::EndOfBody => 0x49,
414            HeaderIdentifier::Who => 0x4A,
415            HeaderIdentifier::ConnectionId => 0xCB,
416            HeaderIdentifier::ApplicationParameters => 0x4C,
417            HeaderIdentifier::AuthenticationChallenge => 0x4D,
418            HeaderIdentifier::AuthenticationResponse => 0x4E,
419            HeaderIdentifier::CreatorId => 0xCF,
420            HeaderIdentifier::WanUuid => 0x50,
421            HeaderIdentifier::ObjectClass => 0x51,
422            HeaderIdentifier::SessionParameters => 0x52,
423            HeaderIdentifier::SessionSequenceNumber => 0x93,
424            HeaderIdentifier::ActionId => 0x94,
425            HeaderIdentifier::DestName => 0x15,
426            HeaderIdentifier::Permissions => 0xD6,
427            HeaderIdentifier::SingleResponseMode => 0x97,
428            HeaderIdentifier::SingleResponseModeParameters => 0x98,
429            HeaderIdentifier::User(id) => *id,
430        }
431    }
432}
433
434/// The building block of an OBEX object. A single OBEX object consists of one or more Headers.
435/// Defined in OBEX 1.5 Section 2.0.
436#[derive(Clone, Debug, PartialEq)]
437pub enum Header {
438    Count(u32),
439    /// The Name header can be empty which is a special parameter for the GET and SETPATH
440    /// operations. It is also valid to provide a Name header with an empty OBEX string.
441    /// See OBEX 1.5 Section 2.2.2.
442    Name(Option<ObexString>),
443    Type(MimeType),
444    /// Number of bytes.
445    Length(u32),
446    /// Time represented as a well-formed NaiveDateTime (no timezone). This is typically UTC.
447    /// See OBEX 1.5 Section 2.2.5.
448    TimeIso8601(NaiveDateTime),
449    /// Time represented as the number of seconds elapsed since midnight UTC on January 1, 1970.
450    /// This is saved as a well-formed NaiveDateTime (no timezone).
451    Time4Byte(NaiveDateTime),
452    Description(ObexString),
453    Target(Vec<u8>),
454    Http(Vec<u8>),
455    Body(Vec<u8>),
456    EndOfBody(Vec<u8>),
457    Who(Vec<u8>),
458    ConnectionId(ConnectionIdentifier),
459    ApplicationParameters(TagLengthValue),
460    AuthenticationChallenge(Vec<u8>),
461    AuthenticationResponse(Vec<u8>),
462    CreatorId(u32),
463    WanUuid(Uuid),
464    ObjectClass(Vec<u8>),
465    SessionParameters(Vec<u8>),
466    SessionSequenceNumber(u8),
467    ActionId(ActionIdentifier),
468    DestName(ObexString),
469    /// 4-byte bit mask.
470    Permissions(u32),
471    SingleResponseMode(SingleResponseMode),
472    /// 1-byte quantity containing the parameters for the SRM session.
473    SingleResponseModeParameters(u8),
474    /// User defined Header type.
475    User(UserDefinedHeader),
476}
477
478impl Header {
479    /// The minimal Header contains at least a 1-byte identifier.
480    const MIN_HEADER_LENGTH_BYTES: usize = 1;
481
482    /// A Unicode or Byte Sequence Header must be at least 3 bytes - 1 byte for the HI and 2 bytes
483    /// for the encoded data length.
484    const MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES: usize = 3;
485
486    /// The ISO 8601 time format used in the Time Header packet.
487    /// The format is YYYYMMDDTHHMMSS where "T" delimits the date from the time. It is assumed that
488    /// the time is the local time, but per OBEX 2.2.5, a suffix of "Z" can be included to indicate
489    /// UTC time.
490    const ISO_8601_TIME_FORMAT: &'static str = "%Y%m%dT%H%M%S";
491
492    /// The ISO 8601 time format used in the Time Header Packet.
493    /// The format is YYYYMMDDTHHMMSSZ, where the "Z" denotes UTC time. The format is sent as a UTF-
494    /// 16 null terminated string. There are 32 bytes for the time and 2 bytes for the terminator.
495    // TODO(https://fxbug.dev/42084674): This encoding assumes the timezone is always UTC. Update this
496    // when `Header` supports timezones.
497    const ISO_8601_LENGTH_BYTES: usize = 34;
498
499    pub fn identifier(&self) -> HeaderIdentifier {
500        match &self {
501            Self::Count(_) => HeaderIdentifier::Count,
502            Self::Name(_) => HeaderIdentifier::Name,
503            Self::Type(_) => HeaderIdentifier::Type,
504            Self::Length(_) => HeaderIdentifier::Length,
505            Self::TimeIso8601(_) => HeaderIdentifier::TimeIso8601,
506            Self::Time4Byte(_) => HeaderIdentifier::Time4Byte,
507            Self::Description(_) => HeaderIdentifier::Description,
508            Self::Target(_) => HeaderIdentifier::Target,
509            Self::Http(_) => HeaderIdentifier::Http,
510            Self::Body(_) => HeaderIdentifier::Body,
511            Self::EndOfBody(_) => HeaderIdentifier::EndOfBody,
512            Self::Who(_) => HeaderIdentifier::Who,
513            Self::ConnectionId(_) => HeaderIdentifier::ConnectionId,
514            Self::ApplicationParameters(_) => HeaderIdentifier::ApplicationParameters,
515            Self::AuthenticationChallenge(_) => HeaderIdentifier::AuthenticationChallenge,
516            Self::AuthenticationResponse(_) => HeaderIdentifier::AuthenticationResponse,
517            Self::CreatorId(_) => HeaderIdentifier::CreatorId,
518            Self::WanUuid(_) => HeaderIdentifier::WanUuid,
519            Self::ObjectClass(_) => HeaderIdentifier::ObjectClass,
520            Self::SessionParameters(_) => HeaderIdentifier::SessionParameters,
521            Self::SessionSequenceNumber(_) => HeaderIdentifier::SessionSequenceNumber,
522            Self::ActionId(_) => HeaderIdentifier::ActionId,
523            Self::DestName(_) => HeaderIdentifier::DestName,
524            Self::Permissions(_) => HeaderIdentifier::Permissions,
525            Self::SingleResponseMode(_) => HeaderIdentifier::SingleResponseMode,
526            Self::SingleResponseModeParameters(_) => HeaderIdentifier::SingleResponseModeParameters,
527            Self::User(UserDefinedHeader { identifier, .. }) => HeaderIdentifier::User(*identifier),
528        }
529    }
530
531    /// Returns the length, in bytes, of the Header's payload.
532    fn data_length(&self) -> usize {
533        use Header::*;
534        match &self {
535            SessionSequenceNumber(_)
536            | ActionId(_)
537            | SingleResponseMode(_)
538            | SingleResponseModeParameters(_) => 1,
539            Count(_) | Length(_) | ConnectionId(_) | CreatorId(_) | Permissions(_)
540            | Time4Byte(_) => 4,
541            Name(option_str) => option_str.as_ref().map_or(0, |s| s.len()),
542            Description(s) | DestName(s) => s.len(),
543            Target(b)
544            | Http(b)
545            | Body(b)
546            | EndOfBody(b)
547            | Who(b)
548            | AuthenticationChallenge(b)
549            | AuthenticationResponse(b)
550            | ObjectClass(b)
551            | SessionParameters(b) => b.len(),
552            ApplicationParameters(params) => params.0.iter().fold(0, |acc, param| {
553                acc + TagLengthValue::MIN_TRIPLET_LENGTH_BYTES + param.1.len()
554            }),
555            Type(mime_type) => mime_type.len(),
556            TimeIso8601(_) => Self::ISO_8601_LENGTH_BYTES,
557            WanUuid(_) => Uuid::BLUETOOTH_UUID_LENGTH_BYTES,
558            User(UserDefinedHeader { value, .. }) => value.len(),
559        }
560    }
561
562    pub fn name(s: &str) -> Self {
563        Self::Name(Some(s.into()))
564    }
565
566    pub fn empty_name() -> Self {
567        Self::Name(None)
568    }
569}
570
571impl Encodable for Header {
572    type Error = PacketError;
573
574    fn encoded_len(&self) -> usize {
575        // Only the `Text` and `Bytes` formats encode the payload length in the buffer.
576        let payload_length_encoded_bytes = match self.identifier().encoding() {
577            HeaderEncoding::Text | HeaderEncoding::Bytes => 2,
578            _ => 0,
579        };
580        Self::MIN_HEADER_LENGTH_BYTES + payload_length_encoded_bytes + self.data_length()
581    }
582
583    fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error> {
584        if buf.len() < self.encoded_len() {
585            return Err(PacketError::BufferTooSmall);
586        }
587
588        // Encode the Header Identifier.
589        buf[0] = (&self.identifier()).into();
590
591        // Optionally encode the payload length if it is a variable length payload.
592        let start_index = match self.identifier().encoding() {
593            HeaderEncoding::Text | HeaderEncoding::Bytes => {
594                let data_length_bytes = (self.encoded_len() as u16).to_be_bytes();
595                buf[Self::MIN_HEADER_LENGTH_BYTES..Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES]
596                    .copy_from_slice(&data_length_bytes);
597                Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES
598            }
599            _ => Self::MIN_HEADER_LENGTH_BYTES,
600        };
601
602        // Encode the payload.
603        use Header::*;
604        match &self {
605            Count(v)
606            | Length(v)
607            | ConnectionId(ConnectionIdentifier(v))
608            | CreatorId(v)
609            | Permissions(v) => {
610                // Encode all 4-byte value headers.
611                buf[start_index..start_index + 4].copy_from_slice(&v.to_be_bytes());
612            }
613            Name(None) => {} // An "empty" Name only encodes the HeaderIdentifier & Length (3).
614            Name(Some(str)) | Description(str) | DestName(str) => {
615                // Encode all ObexString Headers.
616                let s = str.to_be_bytes();
617                buf[start_index..start_index + s.len()].copy_from_slice(&s);
618            }
619            Target(src)
620            | Http(src)
621            | Body(src)
622            | EndOfBody(src)
623            | Who(src)
624            | AuthenticationChallenge(src)
625            | AuthenticationResponse(src)
626            | ObjectClass(src)
627            | SessionParameters(src) => {
628                // Encode all byte buffer headers.
629                let n = src.len();
630                buf[start_index..start_index + n].copy_from_slice(&src[..]);
631            }
632            ApplicationParameters(params) => {
633                params.encode(&mut buf[start_index..])?;
634            }
635            SessionSequenceNumber(v) | SingleResponseModeParameters(v) => {
636                // Encode all 1-byte value headers.
637                buf[start_index] = *v;
638            }
639            SingleResponseMode(v) => {
640                buf[start_index] = v.into();
641            }
642            Type(mime_type) => {
643                let b = mime_type.to_be_bytes();
644                buf[start_index..start_index + b.len()].copy_from_slice(&b[..]);
645            }
646            ActionId(v) => {
647                buf[start_index] = v.into();
648            }
649            TimeIso8601(time) => {
650                let mut formatted = time.format(Self::ISO_8601_TIME_FORMAT).to_string();
651                // Always assume it's UTC.
652                // TODO(https://fxbug.dev/42084674): Conditionally do this when we store timezones.
653                formatted.push('Z');
654                let s = ObexString::from(formatted).to_be_bytes();
655                buf[start_index..start_index + s.len()].copy_from_slice(&s);
656            }
657            Time4Byte(time) => {
658                let timestamp_bytes = (time.timestamp() as u32).to_be_bytes();
659                buf[start_index..start_index + 4].copy_from_slice(&timestamp_bytes[..])
660            }
661            WanUuid(uuid) => buf[start_index..start_index + Uuid::BLUETOOTH_UUID_LENGTH_BYTES]
662                .copy_from_slice(&uuid.as_be_bytes()[..]),
663            User(UserDefinedHeader { value, .. }) => {
664                let n = value.len();
665                buf[start_index..start_index + n].copy_from_slice(&value[..]);
666            }
667        }
668        Ok(())
669    }
670}
671
672impl Decodable for Header {
673    type Error = PacketError;
674
675    fn decode(buf: &[u8]) -> Result<Self, Self::Error> {
676        // The buffer should contain at least the Header Identifier.
677        if buf.len() < Self::MIN_HEADER_LENGTH_BYTES {
678            return Err(PacketError::BufferTooSmall);
679        }
680
681        let id = HeaderIdentifier::try_from(buf[0])?;
682        let mut start_idx = 1;
683        let data_length = match id.encoding() {
684            HeaderEncoding::Text | HeaderEncoding::Bytes => {
685                if buf.len() < Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES {
686                    return Err(PacketError::BufferTooSmall);
687                }
688                // For Unicode Text and Byte Sequences, the payload length is encoded in the next
689                // two bytes - this value includes 1 byte for the HI and 2 bytes for the length.
690                let total_length = u16::from_be_bytes(
691                    buf[Self::MIN_HEADER_LENGTH_BYTES..Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES]
692                        .try_into()
693                        .expect("checked length"),
694                ) as usize;
695                let data_length = total_length
696                    .checked_sub(Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES)
697                    .ok_or(PacketError::DataLength)?;
698                start_idx = Self::MIN_UNICODE_OR_BYTE_SEQ_LENGTH_BYTES;
699                data_length
700            }
701            HeaderEncoding::OneByte => 1, // Otherwise, a 1-byte payload is included.
702            HeaderEncoding::FourBytes => 4, // Otherwise, a 4-byte payload is included.
703        };
704        trace!(id:?, data_length:%; "Parsed OBEX packet");
705
706        if buf.len() < start_idx + data_length {
707            return Err(PacketError::BufferTooSmall);
708        }
709
710        let data = &buf[start_idx..start_idx + data_length];
711        let mut out_buf = vec![0; data_length];
712        match id {
713            HeaderIdentifier::Count => {
714                Ok(Header::Count(u32::from_be_bytes(data[..].try_into().unwrap())))
715            }
716            HeaderIdentifier::Name => {
717                let name = if data.len() == 0 { None } else { Some(ObexString::try_from(data)?) };
718                Ok(Header::Name(name))
719            }
720            HeaderIdentifier::Type => Ok(Header::Type(MimeType::try_from(data)?)),
721            HeaderIdentifier::Length => {
722                Ok(Header::Length(u32::from_be_bytes(data[..].try_into().unwrap())))
723            }
724            HeaderIdentifier::TimeIso8601 => {
725                let mut time_str = ObexString::try_from(data).map(|s| s.to_string())?;
726                // TODO(https://fxbug.dev/42084674): In some cases, the peer can send a local time instead of
727                // UTC. The timezone is not specified. Figure out how to represent this using
728                // DateTime/NaiveDateTime.
729                if time_str.ends_with("Z") {
730                    let _ = time_str.pop();
731                }
732                let parsed = NaiveDateTime::parse_from_str(&time_str, Self::ISO_8601_TIME_FORMAT)
733                    .map_err(PacketError::external)?;
734                Ok(Header::TimeIso8601(parsed))
735            }
736            HeaderIdentifier::Time4Byte => {
737                let elapsed_time_seconds = u32::from_be_bytes(data[..].try_into().unwrap());
738                let parsed = NaiveDateTime::from_timestamp_opt(
739                    elapsed_time_seconds.into(),
740                    /*nsecs= */ 0,
741                )
742                .ok_or_else(|| PacketError::external(anyhow::format_err!("invalid timestamp")))?;
743                Ok(Header::Time4Byte(parsed))
744            }
745            HeaderIdentifier::Description => Ok(Header::Description(ObexString::try_from(data)?)),
746            HeaderIdentifier::Target => {
747                out_buf.copy_from_slice(&data[..]);
748                Ok(Header::Target(out_buf))
749            }
750            HeaderIdentifier::Http => {
751                out_buf.copy_from_slice(&data[..]);
752                Ok(Header::Http(out_buf))
753            }
754            HeaderIdentifier::Body => {
755                out_buf.copy_from_slice(&data[..]);
756                Ok(Header::Body(out_buf))
757            }
758            HeaderIdentifier::EndOfBody => {
759                out_buf.copy_from_slice(&data[..]);
760                Ok(Header::EndOfBody(out_buf))
761            }
762            HeaderIdentifier::Who => {
763                out_buf.copy_from_slice(&data[..]);
764                Ok(Header::Who(out_buf))
765            }
766            HeaderIdentifier::ConnectionId => Ok(Header::ConnectionId(
767                ConnectionIdentifier::try_from(u32::from_be_bytes(data[..].try_into().unwrap()))?,
768            )),
769            HeaderIdentifier::ApplicationParameters => {
770                Ok(Header::ApplicationParameters(TagLengthValue::try_from(&data[..])?))
771            }
772            HeaderIdentifier::AuthenticationChallenge => {
773                out_buf.copy_from_slice(&data[..]);
774                Ok(Header::AuthenticationChallenge(out_buf))
775            }
776            HeaderIdentifier::AuthenticationResponse => {
777                out_buf.copy_from_slice(&data[..]);
778                Ok(Header::AuthenticationResponse(out_buf))
779            }
780            HeaderIdentifier::CreatorId => {
781                Ok(Header::CreatorId(u32::from_be_bytes(data[..].try_into().unwrap())))
782            }
783            HeaderIdentifier::WanUuid => {
784                let bytes: [u8; Uuid::BLUETOOTH_UUID_LENGTH_BYTES] =
785                    data[..].try_into().map_err(|_| PacketError::BufferTooSmall)?;
786                Ok(Header::WanUuid(Uuid::from_be_bytes(bytes)))
787            }
788            HeaderIdentifier::ObjectClass => {
789                out_buf.copy_from_slice(&data[..]);
790                Ok(Header::ObjectClass(out_buf))
791            }
792            HeaderIdentifier::SessionParameters => {
793                out_buf.copy_from_slice(&data[..]);
794                Ok(Header::SessionParameters(out_buf))
795            }
796            HeaderIdentifier::SessionSequenceNumber => {
797                Ok(Header::SessionSequenceNumber(buf[start_idx]))
798            }
799            HeaderIdentifier::ActionId => {
800                Ok(Header::ActionId(ActionIdentifier::try_from(buf[start_idx])?))
801            }
802            HeaderIdentifier::DestName => Ok(Header::DestName(ObexString::try_from(data)?)),
803            HeaderIdentifier::Permissions => {
804                Ok(Header::Permissions(u32::from_be_bytes(data[..].try_into().unwrap())))
805            }
806            HeaderIdentifier::SingleResponseMode => {
807                Ok(Header::SingleResponseMode(SingleResponseMode::try_from(buf[start_idx])?))
808            }
809            HeaderIdentifier::SingleResponseModeParameters => {
810                Ok(Header::SingleResponseModeParameters(buf[start_idx]))
811            }
812            HeaderIdentifier::User(identifier) => {
813                out_buf.copy_from_slice(&data[..]);
814                Ok(Header::User(UserDefinedHeader { identifier, value: out_buf }))
815            }
816        }
817    }
818}
819
820#[cfg(test)]
821mod tests {
822    use super::*;
823
824    use assert_matches::assert_matches;
825    use chrono::{NaiveDate, NaiveTime};
826
827    #[fuchsia::test]
828    fn convert_action_identifier_success() {
829        let id_raw = 0x00;
830        let result = ActionIdentifier::try_from(id_raw).expect("valid identifier");
831        assert_eq!(result, ActionIdentifier::Copy);
832        let converted: u8 = (&result).into();
833        assert_eq!(id_raw, converted);
834
835        let vendor_id_raw = 0x85;
836        let result = ActionIdentifier::try_from(vendor_id_raw).expect("valid identifier");
837        assert_eq!(result, ActionIdentifier::Vendor(0x85));
838        let converted: u8 = (&result).into();
839        assert_eq!(vendor_id_raw, converted);
840    }
841
842    #[fuchsia::test]
843    fn convert_action_identifier_error() {
844        let invalid = 0x03;
845        assert_matches!(ActionIdentifier::try_from(invalid), Err(PacketError::Reserved));
846
847        let invalid = 0x7f;
848        assert_matches!(ActionIdentifier::try_from(invalid), Err(PacketError::Reserved));
849    }
850
851    #[fuchsia::test]
852    fn is_user_id() {
853        let ids = [0x00, 0x10, 0x29, 0x40, 0x80, 0xc0];
854        for id in ids {
855            assert!(!HeaderIdentifier::is_user(id));
856        }
857
858        let ids = [0x30, 0x3f, 0x71, 0x7f, 0xb5, 0xbf, 0xf0, 0xff];
859        for id in ids {
860            assert!(HeaderIdentifier::is_user(id));
861        }
862    }
863
864    #[fuchsia::test]
865    fn is_reserved_id() {
866        let ids = [0x00, 0x10, 0x30, 0x70, 0xb0, 0xf0];
867        for id in ids {
868            assert!(!HeaderIdentifier::is_reserved(id));
869        }
870
871        let ids = [0x19, 0x2f, 0x60, 0x6f, 0x99, 0xae, 0xd9, 0xef];
872        for id in ids {
873            assert!(HeaderIdentifier::is_reserved(id));
874        }
875    }
876
877    #[fuchsia::test]
878    fn valid_header_id_parsed_ok() {
879        let valid = 0x15;
880        let result = HeaderIdentifier::try_from(valid);
881        assert_matches!(result, Ok(HeaderIdentifier::DestName));
882    }
883
884    #[fuchsia::test]
885    fn user_header_id_is_ok() {
886        let user_header_id_raw = 0x33;
887        let result = HeaderIdentifier::try_from(user_header_id_raw);
888        assert_matches!(result, Ok(HeaderIdentifier::User(_)));
889    }
890
891    #[fuchsia::test]
892    fn rfa_header_id_is_reserved_error() {
893        let rfa_header_id_raw = 0x20;
894        let result = HeaderIdentifier::try_from(rfa_header_id_raw);
895        assert_matches!(result, Err(PacketError::Reserved));
896    }
897
898    #[fuchsia::test]
899    fn unknown_header_id_is_error() {
900        // The lower 6 bits of this represent the Length Header. However, the upper 2 bits are
901        // incorrect - therefore the Header ID is considered invalid.
902        let unknown_header_id_raw = 0x03;
903        let result = HeaderIdentifier::try_from(unknown_header_id_raw);
904        assert_matches!(result, Err(PacketError::Identifier(_)));
905    }
906
907    #[fuchsia::test]
908    fn header_encoding_from_identifier() {
909        assert_eq!(HeaderIdentifier::SessionSequenceNumber.encoding(), HeaderEncoding::OneByte);
910        assert_eq!(HeaderIdentifier::Count.encoding(), HeaderEncoding::FourBytes);
911        assert_eq!(HeaderIdentifier::Name.encoding(), HeaderEncoding::Text);
912        assert_eq!(HeaderIdentifier::Target.encoding(), HeaderEncoding::Bytes);
913    }
914
915    #[fuchsia::test]
916    fn decode_empty_header_is_error() {
917        assert_matches!(Header::decode(&[]), Err(PacketError::BufferTooSmall));
918    }
919
920    #[fuchsia::test]
921    fn decode_header_no_payload_is_error() {
922        // Valid Count Header but no contents.
923        assert_matches!(Header::decode(&[0xc0]), Err(PacketError::BufferTooSmall));
924    }
925
926    #[fuchsia::test]
927    fn decode_byte_seq_invalid_length_is_error() {
928        // Valid `Name` Header (Text) but the provided length is only 1 byte.
929        let buf = [0x01, 0x07];
930        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
931
932        // Valid `Target` Header (Byte Seq) but the provided length is only 1 byte.
933        let buf = [0x46, 0x05];
934        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
935
936        // Valid `Body` Header (Byte Seq) but the provided length is too small - it must be >= 3.
937        let buf = [0x48, 0x00, 0x02];
938        assert_matches!(Header::decode(&buf), Err(PacketError::DataLength));
939    }
940
941    #[fuchsia::test]
942    fn decode_header_invalid_payload_is_error() {
943        // The provided payload doesn't match the expected data length.
944        let buf = [
945            0x42, // `Type` Header (Text)
946            0x00, 0x0b, // Total length = 10 implies a data length of 7.
947            0x00, 0x68, 0x00, 0x69, 0x00,
948            0x00, // Payload = "hi" with a null terminator -> 6 length
949        ];
950        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
951
952        // The provided payload doesn't match the expected data length.
953        let buf = [
954            0xc3, // `Length` Header (4 bytes)
955            0x00, 0x00, 0x00, // Payload = 3 bytes (should be 4).
956        ];
957        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
958
959        // The provided payload doesn't match the expected data length.
960        let buf = [
961            0x94, // `ActionId` Header (Expect 1 byte payload)
962        ];
963        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
964
965        // The provided payload doesn't match the expected data length.
966        let buf = [
967            0x42, // `EndOfBody` Header (Byte seq)
968            0x00, 0x06, // Total length = 6 implies a data length of 3.
969            0x12, 0x34, // Payload = 2 bytes (should be 3),
970        ];
971        assert_matches!(Header::decode(&buf), Err(PacketError::BufferTooSmall));
972    }
973
974    #[fuchsia::test]
975    fn decode_valid_header_success() {
976        // Text Header
977        let name_buf = [
978            0x01, // HI = Name
979            0x00, 0x17, // Total length = 23 bytes
980            0x00, 0x54, 0x00, 0x48, 0x00, 0x49,
981            0x00, // 20 byte payload = "THING.DOC" (utf-16)
982            0x4e, 0x00, 0x47, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x43, 0x00, 0x00,
983        ];
984        let result = Header::decode(&name_buf).expect("can decode name header");
985        assert_eq!(result, Header::name("THING.DOC"));
986
987        // Byte Sequence Header
988        let object_class_buf = [
989            0x51, // HI = Object Class
990            0x00, 0x0a, // Total length = 10 bytes
991            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 7 byte payload
992        ];
993        let result = Header::decode(&object_class_buf).expect("can decode object class header");
994        assert_eq!(result, Header::ObjectClass(vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
995
996        // One-byte Header
997        let session_seq_num_buf = [
998            0x93, // HI = Session Sequence Number
999            0x05, // 1 byte payload
1000        ];
1001        let result = Header::decode(&session_seq_num_buf).expect("can decode valid name header");
1002        assert_eq!(result, Header::SessionSequenceNumber(5));
1003
1004        // Four-byte Header
1005        let connection_id_buf = [
1006            0xcb, // HI = Session Sequence Number
1007            0x00, 0x00, 0x12, 0x34, // 4 byte payload
1008        ];
1009        let result = Header::decode(&connection_id_buf).expect("can decode connection id header");
1010        assert_eq!(result, Header::ConnectionId(ConnectionIdentifier(0x1234)));
1011    }
1012
1013    #[fuchsia::test]
1014    fn decode_user_data_header_success() {
1015        let user_buf = [
1016            0xb3, // HI = Random User defined
1017            0x05, // Upper 2 bits of HI indicate 1 byte payload
1018        ];
1019        let result = Header::decode(&user_buf).expect("can decode user header");
1020        assert_eq!(result, Header::User(UserDefinedHeader { identifier: 0xb3, value: vec![0x05] }));
1021    }
1022
1023    #[fuchsia::test]
1024    fn decode_time_header_success() {
1025        let utc_time_buf = [
1026            0x44, // HI = Time ISO 8601
1027            0x00, 0x25, // Total length = 37 bytes
1028            0x00, 0x32, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x30, 0x00, 0x32, 0x00, 0x32,
1029            0x00, 0x34, 0x00, 0x54, 0x00, 0x31, 0x00, 0x32, 0x00, 0x34, 0x00, 0x31, 0x00, 0x33,
1030            0x00, 0x30, 0x00, 0x5a, 0x00, 0x00, // Time = "20230224T124130Z" (UTC), 34 bytes
1031        ];
1032        let result = Header::decode(&utc_time_buf).expect("can decode a utc time header");
1033        assert_matches!(result, Header::TimeIso8601(t) if t == NaiveDate::from_ymd_opt(2023, 2, 24).unwrap().and_hms_opt(12, 41, 30).unwrap());
1034
1035        let local_time_buf = [
1036            0x44, // HI = Time ISO 8601
1037            0x00, 0x23, // Total length = 35 bytes
1038            0x00, 0x32, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x30, 0x00, 0x32, 0x00, 0x32,
1039            0x00, 0x34, 0x00, 0x54, 0x00, 0x31, 0x00, 0x32, 0x00, 0x34, 0x00, 0x31, 0x00, 0x33,
1040            0x00, 0x30, 0x00, 0x00, // Time = "20230224T124130" (UTC), 32 bytes
1041        ];
1042        let result = Header::decode(&local_time_buf).expect("can decode a local time header");
1043        assert_matches!(result, Header::TimeIso8601(t) if t == NaiveDate::from_ymd_opt(2023, 2, 24).unwrap().and_hms_opt(12, 41, 30).unwrap());
1044
1045        // The timestamp corresponds to September 9, 2001 at 01:46:40.
1046        let timestamp_buf = [
1047            0xc4, // HI = Time 4-byte timestamp
1048            0x3b, 0x9a, 0xca, 0x00, // Timestamp = 1e9 seconds after Jan 1, 1970.
1049        ];
1050        let result = Header::decode(&timestamp_buf).expect("can decode a timestamp header");
1051        assert_matches!(result, Header::Time4Byte(t) if t == NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_opt(1, 46, 40).unwrap());
1052    }
1053
1054    #[fuchsia::test]
1055    fn decode_invalid_time_header_is_error() {
1056        let invalid_utc_time_buf = [
1057            0x44, // HI = Time ISO 8601
1058            0x00, 0x23, // Total length = 35 bytes
1059            0x00, 0x32, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x30, 0x00, 0x32, 0x00, 0x32,
1060            0x00, 0x34, 0x00, 0x31, 0x00, 0x32, 0x00, 0x34, 0x00, 0x31, 0x00, 0x33, 0x00, 0x30,
1061            0x00, 0x5a, 0x00,
1062            0x00, // Time = "20230224124130Z" (UTC, missing the "T" delineator)
1063        ];
1064        assert_matches!(Header::decode(&invalid_utc_time_buf), Err(PacketError::Other(_)));
1065
1066        // The timestamp Header should never produce an Error since the timestamp is bounded by
1067        // u32::MAX, whereas the only potential failure case in NaiveDateTime is i64::MAX.
1068    }
1069
1070    #[fuchsia::test]
1071    fn decode_name_header_success() {
1072        let empty_name_buf = [
1073            0x01, // HI = Name
1074            0x00, 0x03, // Total length = 3
1075        ];
1076        let result = Header::decode(&empty_name_buf).expect("can decode empty name");
1077        assert_eq!(result, Header::Name(None));
1078
1079        let empty_string_name_buf = [
1080            0x01, // HI = Name
1081            0x00, 0x05, // Total length = 5
1082            0x00, 0x00, // Name = "" (null terminated)
1083        ];
1084        let result = Header::decode(&empty_string_name_buf).expect("can decode empty string name");
1085        assert_eq!(result, Header::Name(Some("".into())));
1086
1087        let name_buf = [
1088            0x01, // HI = Name
1089            0x00, 0x0b, // Total length = 11
1090            0x00, 0x44, 0x00, 0x4f, 0x00, 0x43, 0x00, 0x00, // Name = "DOC" (null terminated)
1091        ];
1092        let result = Header::decode(&name_buf).expect("can decode empty string name");
1093        assert_eq!(result, Header::Name(Some("DOC".into())));
1094    }
1095
1096    #[fuchsia::test]
1097    fn decode_invalid_name_header_is_error() {
1098        let invalid_name_buf = [
1099            0x01, // HI = Name
1100            0x00, 0x09, // Total length = 9
1101            0x00, 0x44, 0x00, 0x4f, 0x00, 0x43, // Name = "DOC" (missing null terminator)
1102        ];
1103        let result = Header::decode(&invalid_name_buf);
1104        assert_matches!(result, Err(PacketError::Data(_)));
1105    }
1106
1107    #[fuchsia::test]
1108    fn decode_type_header_success() {
1109        let type_buf = [
1110            0x42, // HI = Type
1111            0x00, 0x10, // Total length = 16
1112            0x74, 0x65, 0x78, 0x74, 0x2f, 0x78, 0x2d, 0x76, 0x43, 0x61, 0x72, 0x64,
1113            0x00, // 'text/x-vCard'
1114        ];
1115        let result = Header::decode(&type_buf).expect("can decode type header");
1116        assert_eq!(result, Header::Type("text/x-vCard".into()));
1117
1118        let empty_type_buf = [
1119            0x42, // HI = Type
1120            0x00, 0x04, // Total length = 4
1121            0x00, // Text = empty string with null terminator.
1122        ];
1123        let result = Header::decode(&empty_type_buf).expect("can decode type header");
1124        assert_eq!(result, Header::Type("".into()));
1125    }
1126
1127    #[fuchsia::test]
1128    fn decode_invalid_type_header_is_error() {
1129        // Empty type is an Error since it's missing the null terminator.
1130        let invalid_type_buf = [
1131            0x42, // HI = Type
1132            0x00, 0x03, // Total length = 3
1133        ];
1134        assert_matches!(Header::decode(&invalid_type_buf), Err(PacketError::Data(_)));
1135
1136        // Valid type string but missing null terminator.
1137        let invalid_type_buf = [
1138            0x42, // HI = Type
1139            0x00, 0x0f, // Total length = 15
1140            0x74, 0x65, 0x78, 0x74, 0x2f, 0x78, 0x2d, 0x76, 0x43, 0x61, 0x72,
1141            0x64, // 'text/x-vCard'
1142        ];
1143        assert_matches!(Header::decode(&invalid_type_buf), Err(PacketError::Data(_)));
1144
1145        // Invalid utf-8 string.
1146        let invalid_string_type_buf = [
1147            0x42, // HI = Type
1148            0x00, 0x07, // Total length = 7
1149            0x9f, 0x92, 0x96, 0x00, // Invalid utf-8
1150        ];
1151        assert_matches!(Header::decode(&invalid_string_type_buf), Err(PacketError::Other(_)));
1152    }
1153
1154    #[fuchsia::test]
1155    fn encode_user_data_header_success() {
1156        // Encoding a 1-byte User Header should succeed.
1157        let user = Header::User(UserDefinedHeader { identifier: 0xb3, value: vec![0x12] });
1158        assert_eq!(user.encoded_len(), 2);
1159        let mut buf = vec![0; user.encoded_len()];
1160        user.encode(&mut buf).expect("can encode");
1161        let expected_buf = [0xb3, 0x12];
1162        assert_eq!(buf, expected_buf);
1163
1164        // Encoding a 4-byte User Header should succeed.
1165        let user = Header::User(UserDefinedHeader {
1166            identifier: 0xf5,
1167            value: vec![0x00, 0x01, 0x02, 0x03],
1168        });
1169        assert_eq!(user.encoded_len(), 5);
1170        let mut buf = vec![0; user.encoded_len()];
1171        user.encode(&mut buf).expect("can encode");
1172        let expected_buf = [0xf5, 0x00, 0x01, 0x02, 0x03];
1173        assert_eq!(buf, expected_buf);
1174
1175        // Encoding a Text User Header should succeed.
1176        let user = Header::User(UserDefinedHeader {
1177            identifier: 0x38,
1178            value: vec![0x00, 0x01, 0x00, 0x02, 0x00, 0x00],
1179        });
1180        assert_eq!(user.encoded_len(), 9);
1181        let mut buf = vec![0; user.encoded_len()];
1182        user.encode(&mut buf).expect("can encode");
1183        let expected_buf = [0x38, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00];
1184        assert_eq!(buf, expected_buf);
1185
1186        // Encoding a Bytes User Header should succeed.
1187        let user =
1188            Header::User(UserDefinedHeader { identifier: 0x70, value: vec![0x01, 0x02, 0x03] });
1189        assert_eq!(user.encoded_len(), 6);
1190        let mut buf = vec![0; user.encoded_len()];
1191        user.encode(&mut buf).expect("can encode");
1192        let expected_buf = [0x70, 0x00, 0x06, 0x01, 0x02, 0x03];
1193        assert_eq!(buf, expected_buf);
1194    }
1195
1196    #[fuchsia::test]
1197    fn encode_time_header_success() {
1198        // Represents the date "20150603T123456Z" - 34 bytes in UTF 16.
1199        let time = Header::TimeIso8601(NaiveDateTime::new(
1200            NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(),
1201            NaiveTime::from_hms_milli_opt(12, 34, 56, 0).unwrap(),
1202        ));
1203        // Total length should be 3 bytes (ID, Length) + 34 bytes (Time).
1204        assert_eq!(time.encoded_len(), 37);
1205        // Encoding with a larger buffer is OK.
1206        let mut buf = vec![0; time.encoded_len() + 2];
1207        time.encode(&mut buf).expect("can encode");
1208        let expected_buf = [
1209            0x44, // Header ID = Time ISO8601
1210            0x00, 0x25, // Length = 37 bytes total
1211            0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30,
1212            0x00, 0x33, 0x00, 0x54, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35,
1213            0x00, 0x36, 0x00, 0x5a, 0x00, 0x00, // "20150603T123456Z"
1214            0x00, 0x00, // Extra padding on the input `buf`.
1215        ];
1216        assert_eq!(buf, expected_buf);
1217
1218        let timestamp = Header::Time4Byte(NaiveDateTime::from_timestamp_opt(1_000_000, 0).unwrap());
1219        assert_eq!(timestamp.encoded_len(), 5);
1220        let mut buf = vec![0; timestamp.encoded_len()];
1221        timestamp.encode(&mut buf).expect("can encode");
1222        let expected_buf = [
1223            0xc4, // Header ID = Time 4byte
1224            0x00, 0x0f, 0x42, 0x40, // Time = 1_000_000 in hex bytes, 0xf4240
1225        ];
1226        assert_eq!(buf, expected_buf);
1227    }
1228
1229    #[fuchsia::test]
1230    fn encode_uuid_header_success() {
1231        let uuid = Header::WanUuid(Uuid::new16(0x180d));
1232        // Total length should be 3 bytes (ID, length) + 16 bytes (UUID).
1233        assert_eq!(uuid.encoded_len(), 19);
1234        let mut buf = vec![0; uuid.encoded_len()];
1235        uuid.encode(&mut buf).expect("can encode");
1236        let expected_buf = [
1237            0x50, // Header ID = WanUuid
1238            0x00, 0x13, // Length = 19 bytes
1239            0x00, 0x00, 0x18, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b,
1240            0x34, 0xfb, // UUID (stringified) = "0000180d-0000-1000-8000-00805f9b34fb"
1241        ];
1242        assert_eq!(buf, expected_buf);
1243    }
1244
1245    #[fuchsia::test]
1246    fn encode_name_header_success() {
1247        let empty_name = Header::empty_name();
1248        // Total length should be 3 bytes (ID, length). No name.
1249        assert_eq!(empty_name.encoded_len(), 3);
1250        let mut buf = vec![0; empty_name.encoded_len()];
1251        empty_name.encode(&mut buf).expect("can encode");
1252        let expected_buf = [0x01, 0x00, 0x03];
1253        assert_eq!(buf, expected_buf);
1254
1255        let empty_string_name = Header::name("");
1256        // Total length should be 5 bytes (ID, 2-byte length). Null terminated empty string.
1257        assert_eq!(empty_string_name.encoded_len(), 5);
1258        let mut buf = vec![0; empty_string_name.encoded_len()];
1259        empty_string_name.encode(&mut buf).expect("can encode");
1260        let expected_buf = [0x01, 0x00, 0x05, 0x00, 0x00];
1261        assert_eq!(buf, expected_buf);
1262
1263        let normal_string_name = Header::name("f");
1264        // Total length should be 7 bytes (ID, 2-byte length). Null terminated "f".
1265        assert_eq!(normal_string_name.encoded_len(), 7);
1266        let mut buf = vec![0; normal_string_name.encoded_len()];
1267        normal_string_name.encode(&mut buf).expect("can encode");
1268        let expected_buf = [0x01, 0x00, 0x07, 0x00, 0x66, 0x00, 0x00];
1269        assert_eq!(buf, expected_buf);
1270    }
1271
1272    #[fuchsia::test]
1273    fn encode_type_header_success() {
1274        let type_ = Header::Type("text/html".into());
1275        // Total length should be 3 bytes (ID, length) + 10 bytes (null terminated string)
1276        assert_eq!(type_.encoded_len(), 13);
1277        let mut buf = vec![0; type_.encoded_len()];
1278        type_.encode(&mut buf).expect("can encode");
1279        let expected_buf = [
1280            0x42, // Header ID = Type
1281            0x00, 0x0d, // Length = 13 bytes
1282            0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x00, // 'text/html'
1283        ];
1284        assert_eq!(buf, expected_buf);
1285    }
1286
1287    #[fuchsia::test]
1288    fn encode_valid_header_success() {
1289        // Encoding a 1-byte Header should succeed.
1290        let srm = Header::SingleResponseMode(SingleResponseMode::Disable);
1291        assert_eq!(srm.encoded_len(), 2);
1292        let mut buf = vec![0; srm.encoded_len()];
1293        srm.encode(&mut buf).expect("can encode");
1294        let expected_buf = [
1295            0x97, // Header ID = SRM,
1296            0x00, // SRM = 0x00 (disable)
1297        ];
1298        assert_eq!(buf, expected_buf);
1299
1300        // Encoding a 4-byte Header should succeed.
1301        let count = Header::Count(0x1234);
1302        assert_eq!(count.encoded_len(), 5);
1303        let mut buf = vec![0; count.encoded_len()];
1304        count.encode(&mut buf).expect("can encode");
1305        let expected_buf = [
1306            0xc0, // Header ID = Count
1307            0x00, 0x00, 0x12, 0x34, // Count = 0x1234
1308        ];
1309        assert_eq!(buf, expected_buf);
1310
1311        // Encoding a Text Header should succeed.
1312        let desc = Header::Description("obextest".into());
1313        assert_eq!(desc.encoded_len(), 21);
1314        let mut buf = vec![0; desc.encoded_len()];
1315        desc.encode(&mut buf).expect("can encode");
1316        let expected_buf = [
1317            0x05, // Header ID = Description
1318            0x00, 0x15, // Length = 21 bytes
1319            0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73,
1320            0x00, 0x74, 0x00, 0x00, // "obextest" with null terminator
1321        ];
1322        assert_eq!(buf, expected_buf);
1323
1324        // Encoding a Bytes Header should succeed.
1325        let auth_response = Header::AuthenticationResponse(vec![0x11, 0x22]);
1326        assert_eq!(auth_response.encoded_len(), 5);
1327        let mut buf = vec![0; auth_response.encoded_len()];
1328        auth_response.encode(&mut buf).expect("can encode");
1329        let expected_buf = [
1330            0x4e, // Header ID = Authentication Response
1331            0x00, 0x05, // Total Length = 5 bytes
1332            0x11, 0x22, // Response = [0x11, 0x22]
1333        ];
1334        assert_eq!(buf, expected_buf);
1335    }
1336
1337    #[fuchsia::test]
1338    fn decode_application_header_success() {
1339        #[rustfmt::skip]
1340        let buf = [
1341            0x4C,                   // HI = Application Parameters
1342            0x00, 0x09,             // Total Length = 9 bytes
1343            0x29,                   // Tag
1344            0x04,                   // Length
1345            0x20, 0x00, 0x01, 0x00, // Value
1346        ];
1347        let result = Header::decode(&buf).expect("can decode application parameters header");
1348        assert_eq!(
1349            result,
1350            Header::ApplicationParameters(vec![(0x29, vec![0x20, 0x00, 0x01, 0x00])].into()),
1351        );
1352
1353        #[rustfmt::skip]
1354        let buf = [
1355            0x4C,       // HI = Application Parameters
1356            0x00, 0x0B, // Total Length = 11 bytes
1357            0x01,       // Tag 1
1358            0x02,       // Length
1359            0x00, 0x01, // Value
1360            0x02,       // Tag 2
1361            0x02,       // Length
1362            0x00, 0x02  // Value
1363        ];
1364        let result = Header::decode(&buf).expect("can decode application parameters header");
1365        assert_eq!(
1366            result,
1367            Header::ApplicationParameters(
1368                vec![(0x01, vec![0x00, 0x01]), (0x02, vec![0x00, 0x02]),].into()
1369            ),
1370        );
1371    }
1372
1373    #[fuchsia::test]
1374    fn decode_application_header_fail() {
1375        #[rustfmt::skip]
1376        let buf = [
1377            0x4C,                   // HI = Application Parameters
1378            0x00, 0x09,             // Total Length = 9 bytes
1379            0x29,                   // Tag
1380            0x03,                   // Length (incorrect)
1381            0x20, 0x00, 0x01, 0x00, // Value
1382        ];
1383        let _ = Header::decode(&buf).expect_err("should have failed");
1384
1385        #[rustfmt::skip]
1386        let buf = [
1387            0x4C,       // HI = Application Parameters
1388            0x00, 0x0B, // Total Length = 11 bytes
1389            0x01,       // Tag 1
1390            0x02,       // Length
1391            0x00, 0x01, // Value
1392            0x02,       // Tag 2
1393            0x01,       // Length (incorrect)
1394            0x00, 0x02  // Value
1395        ];
1396        let _ = Header::decode(&buf).expect_err("should have failed");
1397    }
1398
1399    #[fuchsia::test]
1400    fn encode_application_header() {
1401        // Single parameter.
1402        let header =
1403            Header::ApplicationParameters(vec![(0x29, vec![0x20, 0x00, 0x01, 0x00])].into());
1404        let mut buf = vec![0; header.encoded_len()];
1405        let _ = header.encode(&mut buf).expect("should encode successfully");
1406
1407        #[rustfmt::skip]
1408        assert_eq!(buf, vec![
1409            0x4C,                   // HI = Application Parameters
1410            0x00, 0x09,             // Total Length = 9 bytes
1411            0x29,                   // Tag
1412            0x04,                   // Length
1413            0x20, 0x00, 0x01, 0x00, // Value
1414        ]);
1415
1416        // Multiple parameters.
1417    }
1418}