ldap3_serde/controls_impl/
content_sync.rs

1use std::collections::HashSet;
2
3use crate::controls::{ControlParser, MakeCritical, RawControl};
4use crate::ResultEntry;
5
6use bytes::BytesMut;
7
8use lber_serde::common::TagClass;
9use lber_serde::parse::{parse_tag, parse_uint};
10use lber_serde::structure::{StructureTag, PL};
11use lber_serde::structures::{ASNTag, Boolean, Enumerated, OctetString, Sequence, Tag};
12use lber_serde::universal::Types;
13use lber_serde::{write, IResult};
14
15pub const SYNC_REQUEST_OID: &str = "1.3.6.1.4.1.4203.1.9.1.1";
16pub const SYNC_STATE_OID: &str = "1.3.6.1.4.1.4203.1.9.1.2";
17pub const SYNC_DONE_OID: &str = "1.3.6.1.4.1.4203.1.9.1.3";
18const SYNC_INFO_OID: &str = "1.3.6.1.4.1.4203.1.9.1.4";
19
20/// Sync Request control ([RFC 4533](https://tools.ietf.org/html/rfc4533)).
21#[derive(Clone, Debug, Default)]
22pub struct SyncRequest {
23    pub mode: RefreshMode,
24    pub cookie: Option<Vec<u8>>,
25    pub reload_hint: bool,
26}
27
28/// Content refresh mode.
29///
30/// See the Content Synchronization specification
31/// ([RFC 4533](https://tools.ietf.org/html/rfc4533)).
32#[derive(Clone, Debug)]
33pub enum RefreshMode {
34    RefreshOnly,
35    RefreshAndPersist,
36}
37
38impl Default for RefreshMode {
39    fn default() -> Self {
40        RefreshMode::RefreshOnly
41    }
42}
43
44impl From<RefreshMode> for i64 {
45    fn from(mode: RefreshMode) -> i64 {
46        match mode {
47            RefreshMode::RefreshOnly => 1,
48            RefreshMode::RefreshAndPersist => 3,
49        }
50    }
51}
52
53impl MakeCritical for SyncRequest {}
54
55impl From<SyncRequest> for RawControl {
56    fn from(sr: SyncRequest) -> RawControl {
57        let mut cap_est = 16; // covers sequence, selector and hint if any
58        let mut tags = vec![Tag::Enumerated(Enumerated {
59            inner: i64::from(sr.mode),
60            ..Default::default()
61        })];
62        if let Some(cookie) = sr.cookie {
63            cap_est += cookie.len();
64            tags.push(Tag::OctetString(OctetString {
65                inner: cookie,
66                ..Default::default()
67            }));
68        }
69        if sr.reload_hint {
70            tags.push(Tag::Boolean(Boolean {
71                inner: sr.reload_hint,
72                ..Default::default()
73            }));
74        }
75        let sreq = Tag::Sequence(Sequence {
76            inner: tags,
77            ..Default::default()
78        })
79        .into_structure();
80        let mut buf = BytesMut::with_capacity(cap_est);
81        write::encode_into(&mut buf, sreq).expect("encoded");
82        RawControl {
83            ctype: SYNC_REQUEST_OID.to_owned(),
84            crit: false,
85            val: Some(Vec::from(&buf[..])),
86        }
87    }
88}
89
90/// Sync State response control ([RFC 4533](https://tools.ietf.org/html/rfc4533)).
91#[derive(Debug)]
92pub struct SyncState {
93    pub state: EntryState,
94    pub entry_uuid: Vec<u8>,
95    pub cookie: Option<Vec<u8>>,
96}
97
98/// Possible states for the Sync State control.
99#[derive(Debug)]
100pub enum EntryState {
101    Present,
102    Add,
103    Modify,
104    Delete,
105}
106
107impl ControlParser for SyncState {
108    fn parse(val: &[u8]) -> Self {
109        let mut tags = match parse_tag(val) {
110            IResult::Ok((_, tag)) => tag,
111            _ => panic!("syncstate: failed to parse tag"),
112        }
113        .expect_constructed()
114        .expect("syncstate: elements")
115        .into_iter();
116        let state = match match parse_uint(
117            tags.next()
118                .expect("syncstate: element 1")
119                .match_class(TagClass::Universal)
120                .and_then(|t| t.match_id(Types::Enumerated as u64))
121                .and_then(|t| t.expect_primitive())
122                .expect("syncstate: state")
123                .as_slice(),
124        ) {
125            Ok((_, state)) => state,
126            _ => panic!("syncstate: failed to parse state"),
127        } {
128            0 => EntryState::Present,
129            1 => EntryState::Add,
130            2 => EntryState::Modify,
131            3 => EntryState::Delete,
132            _ => panic!("syncstate: unknown state"),
133        };
134        let entry_uuid = tags
135            .next()
136            .expect("syncstate: element 2")
137            .expect_primitive()
138            .expect("syncstate: entryUUID");
139        let cookie = tags
140            .next()
141            .map(|tag| tag.expect_primitive().expect("syncstate: synCookie"));
142        SyncState {
143            state,
144            entry_uuid,
145            cookie,
146        }
147    }
148}
149
150/// Sync Done response control ([RFC 4533](https://tools.ietf.org/html/rfc4533)).
151#[derive(Debug)]
152pub struct SyncDone {
153    pub cookie: Option<Vec<u8>>,
154    pub refresh_deletes: bool,
155}
156
157impl ControlParser for SyncDone {
158    fn parse(val: &[u8]) -> Self {
159        let tags = match parse_tag(val) {
160            Ok((_, tag)) => tag,
161            _ => panic!("syncdone: failed to parse tag"),
162        }
163        .expect_constructed()
164        .expect("syncdone: elements")
165        .into_iter();
166        let mut cookie = None;
167        let mut refresh_deletes = false;
168        for tag in tags {
169            match tag {
170                StructureTag { id, payload, .. } if id == Types::OctetString as u64 => {
171                    cookie = Some(match payload {
172                        PL::P(ostr) => ostr,
173                        PL::C(_) => panic!("syncdone: constructed octet string?"),
174                    });
175                }
176                StructureTag { id, payload, .. } if id == Types::Boolean as u64 => {
177                    refresh_deletes = match payload {
178                        PL::P(ostr) => ostr[0] != 0,
179                        PL::C(_) => panic!("syncdone: constructed boolean?"),
180                    };
181                }
182                _ => panic!("syncdone: unrecognized component"),
183            }
184        }
185        SyncDone {
186            cookie,
187            refresh_deletes,
188        }
189    }
190}
191
192/// Values of the Sync Info intermediate message ([RFC 4533](https://tools.ietf.org/html/rfc4533)).
193#[derive(Clone, Debug)]
194pub enum SyncInfo {
195    NewCookie(Vec<u8>),
196    RefreshDelete {
197        cookie: Option<Vec<u8>>,
198        refresh_done: bool,
199    },
200    RefreshPresent {
201        cookie: Option<Vec<u8>>,
202        refresh_done: bool,
203    },
204    SyncIdSet {
205        cookie: Option<Vec<u8>>,
206        refresh_deletes: bool,
207        sync_uuids: HashSet<Vec<u8>>,
208    },
209}
210
211/// Parse the Sync Info value from the Search result entry.
212pub fn parse_syncinfo(entry: ResultEntry) -> SyncInfo {
213    let mut tags = entry
214        .0
215        .match_id(25)
216        .and_then(|t| t.expect_constructed())
217        .expect("intermediate seq")
218        .into_iter();
219    loop {
220        match tags.next() {
221            None => panic!("syncinfo: out of tags"),
222            Some(tag) if tag.id == 0 => {
223                let oid = String::from_utf8(tag.expect_primitive().expect("octet string"))
224                    .expect("intermediate oid");
225                if oid != SYNC_INFO_OID {
226                    panic!("syncinfo: oid mismatch");
227                }
228            }
229            Some(tag) if tag.id == 1 => {
230                let syncinfo_val =
231                    match parse_tag(tag.expect_primitive().expect("octet string").as_ref()) {
232                        Ok((_, tag)) => tag,
233                        _ => panic!("syncinfo: error parsing value"),
234                    };
235                return match syncinfo_val {
236                    StructureTag { id, class, payload } if class == TagClass::Context && id < 4 => {
237                        match id {
238                            0 => {
239                                let cookie = match payload {
240                                    PL::P(payload) => payload,
241                                    PL::C(_) => panic!("syncinfo: [0] not primitive"),
242                                };
243                                SyncInfo::NewCookie(cookie)
244                            }
245                            1 | 2 | 3 => {
246                                let mut syncinfo_val = match payload {
247                                    PL::C(payload) => payload,
248                                    PL::P(_) => panic!("syncinfo: [1,2,3] not a sequence"),
249                                }
250                                .into_iter();
251                                let mut sync_cookie = None;
252                                let mut flag = id != 3;
253                                let mut uuids = HashSet::new();
254                                let mut pass = 1;
255                                'it: loop {
256                                    match syncinfo_val.next() {
257                                        None => break 'it,
258                                        Some(comp) => match comp {
259                                            StructureTag { id, class, .. }
260                                                if class == TagClass::Universal
261                                                    && id == Types::OctetString as u64
262                                                    && pass <= 1 =>
263                                            {
264                                                sync_cookie = comp.expect_primitive();
265                                            }
266                                            StructureTag { id, class, .. }
267                                                if class == TagClass::Universal
268                                                    && id == Types::Boolean as u64
269                                                    && pass <= 2 =>
270                                            {
271                                                flag = comp
272                                                    .expect_primitive()
273                                                    .expect("octet string")[0]
274                                                    != 0;
275                                            }
276                                            StructureTag { id, class, .. }
277                                                if class == TagClass::Universal
278                                                    && id == Types::Set as u64
279                                                    && pass <= 3 =>
280                                            {
281                                                uuids = comp
282                                                    .expect_constructed()
283                                                    .expect("uuid set")
284                                                    .into_iter()
285                                                    .map(|u| {
286                                                        u.expect_primitive().expect("octet string")
287                                                    })
288                                                    .collect();
289                                            }
290                                            _ => panic!(),
291                                        },
292                                    }
293                                    pass += 1;
294                                }
295                                match id {
296                                    1 => SyncInfo::RefreshDelete {
297                                        cookie: sync_cookie,
298                                        refresh_done: flag,
299                                    },
300                                    2 => SyncInfo::RefreshPresent {
301                                        cookie: sync_cookie,
302                                        refresh_done: flag,
303                                    },
304                                    3 => SyncInfo::SyncIdSet {
305                                        cookie: sync_cookie,
306                                        refresh_deletes: flag,
307                                        sync_uuids: uuids,
308                                    },
309                                    _ => panic!("syncinfo: got id > 3"),
310                                }
311                            }
312                            _ => panic!("syncinfo: got id > 3"),
313                        }
314                    }
315                    _ => panic!("syncinfo: got id > 3"),
316                };
317            }
318            _ => panic!("syncinfo: unrecognized tag"),
319        }
320    }
321}