1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use std::convert::TryFrom;

use libsignal_protocol::{DeviceId, ProtocolAddress};
use uuid::Uuid;

pub use crate::push_service::ServiceIdType;

#[derive(thiserror::Error, Debug, Clone)]
pub enum ParseServiceAddressError {
    #[error("Supplied UUID could not be parsed")]
    InvalidUuid(#[from] uuid::Error),

    #[error("Envelope without UUID")]
    NoUuid,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ServiceAddress {
    pub uuid: Uuid,
    pub identity: ServiceIdType,
}

impl std::fmt::Display for ServiceAddress {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // This is used in ServiceAddress::to_service_id(&self), so keep this consistent.
        match self.identity {
            ServiceIdType::AccountIdentity => write!(f, "{}", self.uuid),
            ServiceIdType::PhoneNumberIdentity => {
                write!(f, "PNI:{}", self.uuid)
            },
        }
    }
}

impl ServiceAddress {
    pub fn to_protocol_address(
        &self,
        device_id: impl Into<DeviceId>,
    ) -> ProtocolAddress {
        match self.identity {
            ServiceIdType::AccountIdentity => {
                ProtocolAddress::new(self.uuid.to_string(), device_id.into())
            },
            ServiceIdType::PhoneNumberIdentity => ProtocolAddress::new(
                format!("PNI:{}", self.uuid),
                device_id.into(),
            ),
        }
    }

    pub fn new_aci(uuid: Uuid) -> Self {
        Self {
            uuid,
            identity: ServiceIdType::AccountIdentity,
        }
    }

    pub fn new_pni(uuid: Uuid) -> Self {
        Self {
            uuid,
            identity: ServiceIdType::PhoneNumberIdentity,
        }
    }

    pub fn aci(&self) -> Option<libsignal_protocol::Aci> {
        use libsignal_protocol::Aci;
        match self.identity {
            ServiceIdType::AccountIdentity => {
                Some(Aci::from_uuid_bytes(self.uuid.into_bytes()))
            },
            ServiceIdType::PhoneNumberIdentity => None,
        }
    }

    pub fn pni(&self) -> Option<libsignal_protocol::Pni> {
        use libsignal_protocol::Pni;
        match self.identity {
            ServiceIdType::AccountIdentity => None,
            ServiceIdType::PhoneNumberIdentity => {
                Some(Pni::from_uuid_bytes(self.uuid.into_bytes()))
            },
        }
    }

    pub fn to_service_id(&self) -> String {
        self.to_string()
    }
}

impl TryFrom<&ProtocolAddress> for ServiceAddress {
    type Error = ParseServiceAddressError;

    fn try_from(addr: &ProtocolAddress) -> Result<Self, Self::Error> {
        let value = addr.name();
        if let Some(pni) = value.strip_prefix("PNI:") {
            Ok(ServiceAddress::new_pni(Uuid::parse_str(pni)?))
        } else {
            Ok(ServiceAddress::new_aci(Uuid::parse_str(value)?))
        }
        .map_err(|e| {
            tracing::error!("Parsing ServiceAddress from {:?}", addr);
            ParseServiceAddressError::InvalidUuid(e)
        })
    }
}

impl TryFrom<&str> for ServiceAddress {
    type Error = ParseServiceAddressError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        if let Some(pni) = value.strip_prefix("PNI:") {
            Ok(ServiceAddress::new_pni(Uuid::parse_str(pni)?))
        } else {
            Ok(ServiceAddress::new_aci(Uuid::parse_str(value)?))
        }
        .map_err(|e| {
            tracing::error!("Parsing ServiceAddress from '{}'", value);
            ParseServiceAddressError::InvalidUuid(e)
        })
    }
}

impl TryFrom<&[u8]> for ServiceAddress {
    type Error = ParseServiceAddressError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        if let Some(pni) = value.strip_prefix(b"PNI:") {
            Ok(ServiceAddress::new_pni(Uuid::from_slice(pni)?))
        } else {
            Ok(ServiceAddress::new_aci(Uuid::from_slice(value)?))
        }
        .map_err(|e| {
            tracing::error!("Parsing ServiceAddress from {:?}", value);
            ParseServiceAddressError::InvalidUuid(e)
        })
    }
}