1use xso::{AsXml, FromXml};
8
9use crate::date::DateTime;
10use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
11use crate::ns;
12use minidom::Element;
13
14#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
16#[xml(namespace = ns::CAM, name = "list")]
17pub struct List;
18
19impl IqGetPayload for List {}
20
21generate_attribute!(
22 Type, "type", {
26 Session => "session",
28
29 Access => "access",
32 }
33);
34
35#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
37#[xml(namespace = ns::CAM, name = "user-agent")]
38pub struct UserAgent {
39 #[xml(extract(fields(text)))]
41 pub software: String,
42
43 #[xml(extract(default, fields(text(type_ = String))))]
45 pub uri: Option<String>,
46
47 #[xml(extract(default, fields(text(type_ = String))))]
49 pub device: Option<String>,
50}
51
52#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
54#[xml(namespace = ns::CAM, name = "auth")]
55pub struct Auth {
56 #[xml(flag)]
58 pub password: bool,
59
60 #[xml(flag)]
62 pub grant: bool,
63
64 #[xml(flag)]
66 pub fast: bool,
67
68 #[xml(child(n = ..))]
71 pub other: Vec<Element>,
72}
73
74generate_attribute!(
75 Status, "status", {
77 Unrestricted => "unrestricted",
79
80 Normal => "normal",
83
84 Restricted => "restricted",
88 }
89);
90
91#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
93#[xml(namespace = ns::CAM, name = "permission")]
94pub struct Permission {
95 #[xml(attribute)]
97 pub status: Status,
98}
99
100#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
102#[xml(namespace = ns::CAM, name = "client")]
103pub struct Client {
104 #[xml(attribute)]
108 pub connected: bool,
109
110 #[xml(attribute)]
112 pub id: String,
113
114 #[xml(attribute = "type")]
118 pub type_: Type,
119
120 #[xml(extract(default, name = "first-seen", fields(text(type_ = DateTime))))]
123 pub first_seen: Option<DateTime>,
124
125 #[xml(extract(default, name = "last-seen", fields(text(type_ = DateTime))))]
127 pub last_seen: Option<DateTime>,
128
129 #[xml(child)]
131 pub user_agent: UserAgent,
132
133 #[xml(child)]
136 pub auth: Auth,
137
138 #[xml(child)]
140 pub permission: Permission,
141}
142
143#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
145#[xml(namespace = ns::CAM, name = "clients")]
146pub struct Clients {
147 #[xml(child(n = ..))]
149 pub clients: Vec<Client>,
150}
151
152impl IqResultPayload for Clients {}
153
154#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
156#[xml(namespace = ns::CAM, name = "revoke")]
157pub struct Revoke {
158 #[xml(attribute)]
160 pub id: String,
161}
162
163impl IqSetPayload for Revoke {}
164
165#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
170#[xml(namespace = ns::CAM, name = "password-reset-required")]
171pub struct PasswordResetRequiredError;
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use minidom::Element;
177
178 #[cfg(target_pointer_width = "32")]
179 #[test]
180 fn test_size() {
181 assert_size!(List, 0);
182 assert_size!(Type, 1);
183 assert_size!(UserAgent, 36);
184 assert_size!(Auth, 16);
185 assert_size!(Status, 1);
186 assert_size!(Permission, 1);
187 assert_size!(Client, 100);
188 assert_size!(Clients, 12);
189 assert_size!(Revoke, 12);
190 assert_size!(PasswordResetRequiredError, 0);
191 }
192
193 #[cfg(target_pointer_width = "64")]
194 #[test]
195 fn test_size() {
196 assert_size!(List, 0);
197 assert_size!(Type, 1);
198 assert_size!(UserAgent, 72);
199 assert_size!(Auth, 32);
200 assert_size!(Status, 1);
201 assert_size!(Permission, 1);
202 assert_size!(Client, 168);
203 assert_size!(Clients, 24);
204 assert_size!(Revoke, 24);
205 assert_size!(PasswordResetRequiredError, 0);
206 }
207
208 #[test]
209 fn test_example_2() {
210 let elem: Element = "<clients xmlns='urn:xmpp:cam:0'>
211 <client connected='true' id='zeiP41HLglIu' type='session'>
212 <first-seen>2023-04-06T14:26:08Z</first-seen>
213 <last-seen>2023-04-06T14:37:25Z</last-seen>
214 <auth>
215 <password/>
216 </auth>
217 <permission status='unrestricted'/>
218 <user-agent>
219 <software>Gajim</software>
220 <uri>https://gajim.org/</uri>
221 <device>Juliet's laptop</device>
222 </user-agent>
223 </client>
224 <client connected='false' id='HjEEr45_LQr' type='access'>
225 <first-seen>2023-03-27T15:16:09Z</first-seen>
226 <last-seen>2023-03-27T15:37:24Z</last-seen>
227 <auth>
228 <grant/>
229 </auth>
230 <permission status='normal'/>
231 <user-agent>
232 <software>REST client</software>
233 </user-agent>
234 </client>
235 </clients>"
236 .parse()
237 .unwrap();
238 let clients = Clients::try_from(elem).unwrap();
239 let client = &clients.clients[0];
240 assert_eq!(client.connected, true);
241 assert_eq!(client.id, "zeiP41HLglIu");
242 assert_eq!(client.type_, Type::Session);
243 assert_eq!(
244 client.first_seen,
245 Some("2023-04-06T14:26:08Z".parse().unwrap())
246 );
247 assert_eq!(
248 client.last_seen,
249 Some("2023-04-06T14:37:25Z".parse().unwrap())
250 );
251 assert_eq!(client.auth.password, true);
252 assert_eq!(client.auth.grant, false);
253 assert_eq!(client.auth.fast, false);
254 assert!(client.auth.other.is_empty());
255 assert_eq!(client.permission.status, Status::Unrestricted);
256 assert_eq!(client.user_agent.software, "Gajim");
257 assert_eq!(
258 client.user_agent.uri,
259 Some(String::from("https://gajim.org/"))
260 );
261 assert_eq!(
262 client.user_agent.device,
263 Some(String::from("Juliet's laptop"))
264 );
265
266 let client = &clients.clients[1];
267 assert_eq!(client.connected, false);
268 assert_eq!(client.id, "HjEEr45_LQr");
269 assert_eq!(client.type_, Type::Access);
270 assert_eq!(
271 client.first_seen,
272 Some("2023-03-27T15:16:09Z".parse().unwrap())
273 );
274 assert_eq!(
275 client.last_seen,
276 Some("2023-03-27T15:37:24Z".parse().unwrap())
277 );
278 assert_eq!(client.auth.password, false);
279 assert_eq!(client.auth.grant, true);
280 assert_eq!(client.auth.fast, false);
281 assert!(client.auth.other.is_empty());
282 assert_eq!(client.permission.status, Status::Normal);
283 assert_eq!(client.user_agent.software, "REST client");
284 assert!(client.user_agent.uri.is_none());
285 assert!(client.user_agent.device.is_none());
286 }
287}