ldap3_serde/controls_impl/
content_sync.rs1use 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#[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#[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; 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#[derive(Debug)]
92pub struct SyncState {
93 pub state: EntryState,
94 pub entry_uuid: Vec<u8>,
95 pub cookie: Option<Vec<u8>>,
96}
97
98#[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#[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#[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
211pub 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}