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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Take a look at the license at the top of the repository in the LICENSE file.

#[cfg(not(docsrs))]
use std::ffi::OsStr;
#[cfg(unix)]
#[cfg(not(docsrs))]
use std::os::unix::ffi::OsStrExt;
use std::{path, ptr, slice};

use glib::translate::*;

use crate::{prelude::*, SocketAddress, UnixSocketAddress, UnixSocketAddressType};

#[derive(Debug)]
pub enum UnixSocketAddressPath<'a> {
    Path(&'a path::Path),
    Anonymous,
    Abstract(&'a [u8]),
    AbstractPadded(&'a [u8]),
}

impl<'a> UnixSocketAddressPath<'a> {
    fn to_type(&self) -> UnixSocketAddressType {
        use self::UnixSocketAddressPath::*;

        match *self {
            Path(_) => UnixSocketAddressType::Path,
            Anonymous => UnixSocketAddressType::Anonymous,
            Abstract(_) => UnixSocketAddressType::Abstract,
            AbstractPadded(_) => UnixSocketAddressType::AbstractPadded,
        }
    }
}

impl UnixSocketAddress {
    /// Creates a new #GUnixSocketAddress for @path.
    ///
    /// To create abstract socket addresses, on systems that support that,
    /// use g_unix_socket_address_new_abstract().
    /// ## `path`
    /// the socket path
    ///
    /// # Returns
    ///
    /// a new #GUnixSocketAddress
    #[doc(alias = "g_unix_socket_address_new")]
    pub fn new(path: &path::Path) -> UnixSocketAddress {
        unsafe {
            SocketAddress::from_glib_full(ffi::g_unix_socket_address_new(path.to_glib_none().0))
                .unsafe_cast()
        }
    }

    /// Creates a new #GUnixSocketAddress of type @type_ with name @path.
    ///
    /// If @type_ is [`UnixSocketAddressType::Path`][crate::UnixSocketAddressType::Path], this is equivalent to
    /// calling g_unix_socket_address_new().
    ///
    /// If @type_ is [`UnixSocketAddressType::Anonymous`][crate::UnixSocketAddressType::Anonymous], @path and @path_len will be
    /// ignored.
    ///
    /// If @path_type is [`UnixSocketAddressType::Abstract`][crate::UnixSocketAddressType::Abstract], then @path_len
    /// bytes of @path will be copied to the socket's path, and only those
    /// bytes will be considered part of the name. (If @path_len is -1,
    /// then @path is assumed to be NUL-terminated.) For example, if @path
    /// was "test", then calling g_socket_address_get_native_size() on the
    /// returned socket would return 7 (2 bytes of overhead, 1 byte for the
    /// abstract-socket indicator byte, and 4 bytes for the name "test").
    ///
    /// If @path_type is [`UnixSocketAddressType::AbstractPadded`][crate::UnixSocketAddressType::AbstractPadded], then
    /// @path_len bytes of @path will be copied to the socket's path, the
    /// rest of the path will be padded with 0 bytes, and the entire
    /// zero-padded buffer will be considered the name. (As above, if
    /// @path_len is -1, then @path is assumed to be NUL-terminated.) In
    /// this case, g_socket_address_get_native_size() will always return
    /// the full size of a `struct sockaddr_un`, although
    /// g_unix_socket_address_get_path_len() will still return just the
    /// length of @path.
    ///
    /// [`UnixSocketAddressType::Abstract`][crate::UnixSocketAddressType::Abstract] is preferred over
    /// [`UnixSocketAddressType::AbstractPadded`][crate::UnixSocketAddressType::AbstractPadded] for new programs. Of course,
    /// when connecting to a server created by another process, you must
    /// use the appropriate type corresponding to how that process created
    /// its listening socket.
    /// ## `path`
    /// the name
    /// ## `type_`
    /// a #GUnixSocketAddressType
    ///
    /// # Returns
    ///
    /// a new #GUnixSocketAddress
    #[doc(alias = "g_unix_socket_address_new_with_type")]
    pub fn with_type(address_type: UnixSocketAddressPath) -> Self {
        use self::UnixSocketAddressPath::*;

        let type_ = address_type.to_type();
        let new = |ptr, len| unsafe {
            SocketAddress::from_glib_full(ffi::g_unix_socket_address_new_with_type(
                ptr,
                len,
                type_.into_glib(),
            ))
            .unsafe_cast()
        };
        match address_type {
            Path(path) => new(path.to_glib_none().0, -1),
            Abstract(path) | AbstractPadded(path) => new(
                path.to_glib_none().0 as *mut libc::c_char,
                path.len() as i32,
            ),
            Anonymous => new(ptr::null_mut(), 0),
        }
    }
}

mod sealed {
    pub trait Sealed {}
    impl<T: super::IsA<super::UnixSocketAddress>> Sealed for T {}
}

pub trait UnixSocketAddressExtManual: sealed::Sealed + IsA<UnixSocketAddress> + 'static {
    /// Gets @self's path, or for abstract sockets the "name".
    ///
    /// Guaranteed to be zero-terminated, but an abstract socket
    /// may contain embedded zeros, and thus you should use
    /// g_unix_socket_address_get_path_len() to get the true length
    /// of this string.
    ///
    /// # Returns
    ///
    /// the path for @self
    #[doc(alias = "g_unix_socket_address_get_path")]
    #[doc(alias = "get_path")]
    fn path(&self) -> Option<UnixSocketAddressPath> {
        use self::UnixSocketAddressPath::*;

        let path = unsafe {
            let path = ffi::g_unix_socket_address_get_path(self.as_ref().to_glib_none().0);
            if path.is_null() || self.path_len() == 0 {
                &[]
            } else {
                slice::from_raw_parts(path as *const u8, self.path_len())
            }
        };
        match self.address_type() {
            UnixSocketAddressType::Anonymous => Some(Anonymous),
            #[cfg(not(docsrs))]
            UnixSocketAddressType::Path => Some(Path(path::Path::new(OsStr::from_bytes(path)))),
            #[cfg(docsrs)]
            UnixSocketAddressType::Path => unreachable!(),
            UnixSocketAddressType::Abstract => Some(Abstract(path)),
            UnixSocketAddressType::AbstractPadded => Some(AbstractPadded(path)),
            UnixSocketAddressType::Invalid | UnixSocketAddressType::__Unknown(_) => None,
        }
    }
}

impl<O: IsA<UnixSocketAddress>> UnixSocketAddressExtManual for O {}

#[cfg(test)]
mod test {
    use super::*;

    // Check the actual path and len are correct and are not the underlying OsString
    #[test]
    fn check_path() {
        let mut os_string = std::ffi::OsString::with_capacity(100);
        os_string.push("/tmp/foo");
        let path = os_string.as_ref();

        let addr = UnixSocketAddress::new(path);
        assert_eq!(addr.path_len(), 8);
        assert_eq!(addr.path_as_array().unwrap().as_ref(), b"/tmp/foo");

        let addr = UnixSocketAddress::with_type(UnixSocketAddressPath::Path(path));
        assert_eq!(addr.path_len(), 8);
        assert_eq!(addr.path_as_array().unwrap().as_ref(), b"/tmp/foo");
    }
}