ldap3_serde/search.rs
1use std::collections::HashMap;
2use std::fmt::Debug;
3use std::sync::Arc;
4use std::time::Duration;
5
6use crate::adapters::Adapter;
7use crate::controls::Control;
8use crate::ldap::Ldap;
9use crate::parse_filter;
10use crate::protocol::LdapOp;
11use crate::result::{LdapError, LdapResult, Result};
12
13use tokio::sync::{mpsc, Mutex};
14use tokio::time;
15
16use lber_serde::common::TagClass;
17use lber_serde::structure::StructureTag;
18use lber_serde::structures::{Boolean, Enumerated, Integer, OctetString, Sequence, Tag};
19
20#[cfg(feature = "serde")]
21use serde::Serialize;
22
23/// Possible values for search scope.
24#[derive(Clone, Copy, Debug, PartialEq)]
25pub enum Scope {
26 /// Base object; search only the object named in the base DN.
27 Base = 0,
28 /// Search the objects immediately below the base DN.
29 OneLevel = 1,
30 /// Search the object named in the base DN and the whole subtree below it.
31 Subtree = 2,
32}
33
34/// Possible values for alias dereferencing during search.
35#[derive(Clone, Copy, Debug, PartialEq)]
36pub enum DerefAliases {
37 /// Never dereference.
38 Never = 0,
39 /// Dereference while retrieving objects according to search scope.
40 Searching = 1,
41 /// Dereference while finding the base object.
42 Finding = 2,
43 /// Always dereference.
44 Always = 3,
45}
46
47impl Default for DerefAliases {
48 fn default() -> Self {
49 DerefAliases::Never
50 }
51}
52
53#[derive(Debug)]
54pub enum SearchItem {
55 Entry(StructureTag),
56 Referral(StructureTag),
57 Done(LdapResult),
58}
59
60/// Wrapper for the internal structure of a result entry.
61#[derive(Debug, Clone)]
62#[non_exhaustive]
63pub struct ResultEntry(pub StructureTag, pub Vec<Control>);
64
65impl ResultEntry {
66 #[doc(hidden)]
67 pub fn new(st: StructureTag) -> ResultEntry {
68 ResultEntry(st, vec![])
69 }
70
71 /// Returns true if the enclosed entry is a referral.
72 pub fn is_ref(&self) -> bool {
73 self.0.id == 19
74 }
75
76 /// Returns true if the enclosed entry is an intermediate message.
77 pub fn is_intermediate(&self) -> bool {
78 self.0.id == 25
79 }
80}
81
82#[cfg(feature = "serde")]
83impl Serialize for ResultEntry {
84 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
85 where
86 S: serde::Serializer,
87 {
88 use serde::ser::SerializeStruct;
89 let mut seq = serializer.serialize_struct("ResultEntry", 2)?;
90 seq.serialize_field("entry", &self.0)?;
91 seq.serialize_field("controls", &self.1)?;
92 seq.end()
93 }
94}
95
96/// Additional parameters for the Search operation.
97#[derive(Clone, Debug, Default)]
98#[non_exhaustive]
99pub struct SearchOptions {
100 pub deref: DerefAliases,
101 pub typesonly: bool,
102 pub timelimit: i32,
103 pub sizelimit: i32,
104}
105
106impl SearchOptions {
107 /// Create an instance of the structure with default values.
108 pub fn new() -> Self {
109 SearchOptions {
110 ..Default::default()
111 }
112 }
113
114 /// Set the method for dereferencing aliases.
115 pub fn deref(mut self, d: DerefAliases) -> Self {
116 self.deref = d;
117 self
118 }
119
120 /// Set the indicator of returning just attribute names (`true`) vs. names and values (`false`).
121 pub fn typesonly(mut self, typesonly: bool) -> Self {
122 self.typesonly = typesonly;
123 self
124 }
125
126 /// Set the time limit, in seconds, for the whole search operation.
127 ///
128 /// This is a server-side limit of the elapsed time for performing the operation, _not_ a
129 /// network timeout for retrieving result entries or the result of the whole operation.
130 pub fn timelimit(mut self, timelimit: i32) -> Self {
131 self.timelimit = timelimit;
132 self
133 }
134
135 /// Set the size limit, in entries, for the whole search operation.
136 pub fn sizelimit(mut self, sizelimit: i32) -> Self {
137 self.sizelimit = sizelimit;
138 self
139 }
140}
141
142/// Parsed search result entry.
143///
144/// While LDAP attributes can have a variety of syntaxes, they're all returned in
145/// search results as octet strings, without any associated type information. A
146/// general-purpose result parser could leave all values in that format, but then
147/// retrieving them from user code would be cumbersome and tedious.
148///
149/// For that reason, the parser tries to convert every value into a `String`. If an
150/// attribute can contain unconstrained binary strings, the conversion may fail. In that case,
151/// the attribute and all its values will be in the `bin_attrs` hashmap. Since it's
152/// possible that a particular set of values for a binary attribute _could_ be
153/// converted into UTF-8 `String`s, the presence of such an attribute in the result
154/// entry should be checked for both in `attrs` and `bin_atrrs`.
155#[derive(Debug, Clone)]
156pub struct SearchEntry {
157 /// Entry DN.
158 pub dn: String,
159 /// Attributes.
160 pub attrs: HashMap<String, Vec<String>>,
161 /// Binary-valued attributes.
162 pub bin_attrs: HashMap<String, Vec<Vec<u8>>>,
163}
164
165impl SearchEntry {
166 /// Parse raw BER data and convert it into attribute map(s).
167 ///
168 /// __Note__: this function will panic on parsing error.
169 pub fn construct(re: ResultEntry) -> SearchEntry {
170 let mut tags =
171 re.0.match_id(4)
172 .and_then(|t| t.expect_constructed())
173 .expect("entry")
174 .into_iter();
175 let dn = String::from_utf8(
176 tags.next()
177 .expect("element")
178 .expect_primitive()
179 .expect("octet string"),
180 )
181 .expect("dn");
182 let mut attr_vals = HashMap::new();
183 let mut bin_attr_vals = HashMap::new();
184 let attrs = tags
185 .next()
186 .expect("element")
187 .expect_constructed()
188 .expect("attrs")
189 .into_iter();
190 for a_v in attrs {
191 let mut part_attr = a_v
192 .expect_constructed()
193 .expect("partial attribute")
194 .into_iter();
195 let a_type = String::from_utf8(
196 part_attr
197 .next()
198 .expect("element")
199 .expect_primitive()
200 .expect("octet string"),
201 )
202 .expect("attribute type");
203 let mut any_binary = false;
204 let values = part_attr
205 .next()
206 .expect("element")
207 .expect_constructed()
208 .expect("values")
209 .into_iter()
210 .map(|t| t.expect_primitive().expect("octet string"))
211 .filter_map(|s| {
212 if let Ok(s) = std::str::from_utf8(s.as_ref()) {
213 return Some(s.to_owned());
214 }
215 bin_attr_vals
216 .entry(a_type.clone())
217 .or_insert_with(Vec::new)
218 .push(s);
219 any_binary = true;
220 None
221 })
222 .collect::<Vec<String>>();
223 if any_binary {
224 bin_attr_vals.get_mut(&a_type).expect("bin vector").extend(
225 values
226 .into_iter()
227 .map(String::into_bytes)
228 .collect::<Vec<Vec<u8>>>(),
229 );
230 } else {
231 attr_vals.insert(a_type, values);
232 }
233 }
234 SearchEntry {
235 dn,
236 attrs: attr_vals,
237 bin_attrs: bin_attr_vals,
238 }
239 }
240}
241
242// Not really, IMO
243#[allow(rustdoc::invalid_html_tags)]
244/// Possible states of a `SearchStream`.
245///
246/// ## `SearchStream` call/state conceptual diagram
247///
248/// <div>
249/// <img src="data:image/png;base64,
250/// iVBORw0KGgoAAAANSUhEUgAAAbAAAAFACAYAAADd3CkzAAAABHNCSVQICAgIfAhk
251/// iAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3Nj
252/// YXBlLm9yZ5vuPBoAACAASURBVHic7d17VFTl/j/w9zhchAABlRQQBRKtFJSLQqkh
253/// qJiShTqD4iWz4+2U6VGXSoUXTO2oqa2+mWkd7Zhl2YklmubdVEwwCbIjqFw8COIV
254/// BOSiDjy/P1zsn9OAIgKbPfN+rcVazH6e/Vw+wHz2fvaejUoIIUBERKQwLeQeABHR
255/// g4QQ2Lp1K6ZMmQKtVoszZ84Y1Jk+fToOHTpU7z50Oh20Wi3S09Mfe9+vvvoK//zn
256/// P+vdNzUcJjAFiomJQWxsbKP2sXPnToSHhzdqH1S7cePG4d///rfcw5DF9u3b8fbb
257/// b8PT0xMDBgyAo6OjQZ2EhATk5eXVu4+qqiocOHAAt27deux9U1NTkZCQUO++qeGY
258/// yT0AenwXL16EhYVFo/Zx8+bNGo98qWmkp6eje/fucg9DFkePHkVYWBjmzp1ba53k
259/// 5OQn6sPCwgIFBQVP1AbJj2dgzczWrVsRGhoKX19fhIeHIy4uTipbtWoVtFotjh07
260/// hsOHD0Or1UKr1erV+fnnnxEVFYWAgAAEBgYiOjoaJSUlen38+eef0Gq1uHz5Ml5/
261/// /XX4+vpizJgxuHXrFvbu3QutVot169bhxo0bUh+NfcanBKtXr8bGjRvx0UcfoVev
262/// XhgyZAhSUlL06ly5cgUzZ85E3759MXjwYHz33Xd6ZaNGjcKff/4pbbt06RIiIyOR
263/// lpYGAPjHP/4BrVaLzMxMbN26VYp/UlJS00xSRqtXr4ZWq8VPP/2EkydPSnN/8EBq
264/// 9uzZ0vaalhDnzJmDuLg4zJ07F35+ftBqtcjNzdWrM3r0aKmNmpYQS0tLER0djRdf
265/// fBEBAQEYN24czp8/b1Bv06ZNCAoKwoABA3D48OEGiAA9Lp6BNSP79+/H5MmT8ckn
266/// n6Bz5864cOECMjMzpfIXXngBHTt2xJUrV2BmZgaNRgMA6Nq1q14b3bt3x7hx43D3
267/// 7l0sWbIE58+fx3/+8x+pzrVr17B9+3ZcvnwZwcHBGDhwIBITE3Hjxg14enpCo9Hg
268/// yJEjyM7OlvpwcnJqoig0XydPnsSRI0cQFRWFmJgYrFu3DpGRkUhPT4dKpUJRURH6
269/// 9OkDLy8vREdH4+rVq3j77beh0+kwZswYtGvXDl26dMGoUaOQlJQES0tLjB07Fp6e
270/// nnj22WcBAIMHD0ZxcTGSk5Ph7e2NYcOGAQBcXFzknHqTCAoKQocOHXD9+nVUVVXV
271/// +LsXFhaGoqIiTJ8+HYMGDUJISIheG/v27cM333yDt99+GwsXLsSCBQswbdo07Ny5
272/// U6ozYsQI3Lt3D1FRUXjnnXcMxjF37lwkJCRg6dKlaNmyJU6ePInc3Fx4eXlJdU6e
273/// PAl7e3u8++672LZtGzQaDXJycmBtbd3QYaGHEdRsLFu2TPj4+Dyy3tixY8XEiRPr
274/// 1ObBgweFWq0WlZWVetsAiA0bNtS636ZNm4Sbm1ud+jAVGo1G+Pv7S6//+OMPAUDk
275/// 5eUJIYT46KOPhKurqygvL5fqrFmzRvTo0UN6rdPpRP/+/cWkSZPEokWLxPPPPy9K
276/// S0sN+vL39xf//Oc/G3E2zdeECRPE+PHjH1rHw8NDbNy40WB79+7dxdixY6XX33//
277/// vbCzszOod+fOHQFAHDt2zKCsd+/eYsGCBbX2/Y9//EO4urqKe/fuCSGEuHnzpgAg
278/// EhMTHzpmang8A2tG+vfvj4ULF6JPnz549dVXpaXEx3Hp0iWsWrUKycnJKC8vR3l5
279/// OSorK1FUVAQHBwe9utVH91R33t7e0vft2rUDANy4cQPOzs5ISUmBlZUVFi1aJNXJ
280/// zs5Geno6hBBQqVRQq9XYunUrevbsibKyMvz66688am9gf/0ZFRcX4+7du3W+bjxo
281/// 0CCsXLkSaWlpePnllxEWFgZnZ2e9Os899xzMzO6/fTo6OsLS0hI3btxouElQnfAa
282/// WDMSGBiIlJQUhISEYNu2bfDz88OsWbPqvP/du3cREhKC/Px8LF++HN999x2WLl0K
283/// 4P5tw3/Vtm3bBhu7qbC0tJS+V6lUAO7f9g0AJSUlcHBw0Pvy9fXFokWLUFVVpbef
284/// SqWCEAItWvBPsKE9mKj++jOqi9jYWGzfvh1t2rTBe++9B09PT+zbt0+vzoO/B9X9
285/// PE4f1DB4BtbMPPfcc4iNjUVsbCzWr1+PGTNmYNWqVXpvdGZmZjUmpLS0NGRkZCAp
286/// KUk62zpx4kStfVX/cdfE3Ny8xj6odl5eXrh16xbmzZtXa53KykqMGTMGAwYMgKur
287/// KzQaDZKSkgzOwszMzFBZWdnYQ6ZaDB06FEOHDsUnn3yCYcOGYcOGDRg0aJDcw6K/
288/// 4OFfM3L48GHprighBK5cuYJ27doZHKV7eHjg119/NfgcjKOjI1QqFY4fPw7g/nLi
289/// hx9+WK+xeHh44OrVq0hISNA7e6DajRkzBgkJCdiwYYN0NH7hwgVs3bpVqhMbG4uc
290/// nBysW7cOsbGxsLW1xVtvvWXQloeHBw4ePIjCwsImGz/dt23bNinud+7cQWFhoUnc
291/// RKNETGDNyJkzZ+Dr64vWrVvj6aefxhdffIEvvvjCoN60adOkO9dUKhWWLVsGAOjQ
292/// oQMWL16MESNGwMXFBT169MDYsWPrNZagoCC8/fbbGD58ONRqNUJDQ59obqbA29sb
293/// 3377LRYvXgxbW1s4OjqiR48e0m3z+/fvx4oVK/DNN9/A1tYW5ubm+OabbxAXF4fN
294/// mzfrtfX++++jrKwMLi4uUKlU+PHHH2WYUfPy9ddfS8uvWVlZmDRpElQqFdzc3Orc
295/// RkxMDFQqlbQE2LdvX6hUKr2zqy1btqBt27ZwdXVF27Zt0aJFC7z//vsNPh96cirB
296/// hdtm5e7du8jOzoa5uTk6dOgAc3Pzx26joKAA+fn58PDwgJWVVSOMkh4lLy8PpaWl
297/// 6Nixo8H1Emr+bt26hdzcXLRp00a6WYeaHyYwIiJSJC4hEhGRIjGBERGRIjGBERGR
298/// IjGBERGRIjGBERGRIjGBERGRIjGBERGRIjGBERGRIjGBERGRIvFp9E3oYU9/NyVy
299/// PfyF8b+P8ZcXH37UcHgGRkREisQzMBmY6hFYczkCZ/zlxfhTQ+EZGBERKRITGBER
300/// KRITGBERKRITGBERKRITGBERKRITGBERKRITGBERKVKDfQ6ssrISKSkpAABLS0t0
301/// 6tQJNjY2tdafPn06IiIiEBIS0lBDIGr2UlJSkJ6eDjs7OwQGBsLR0VHuIREpVoOd
302/// gZWUlMDf3x8vvfQSevbsCQcHB0RGRqKgoKDG+gkJCcjLy2uo7uulX79+OHr0qKxj
303/// INPRp08fBAQEYPHixZg6dSo8PT2xa9cuuYdFpFgNvoT41VdfoaysDImJiUhLS0NE
304/// RESN9ZKTkzFu3LiG7v6x/P7777h165asYyDTERUVhStXriAtLQ0XL15EREQExo8f
305/// D51OJ/fQiBSpUa6BmZubw9fXFytWrMDRo0dx8uRJqWz27NnQarXQarU4dOhQjfuv
306/// WrUKX375JeLi4tC/f38EBgZi48aNUnl6ejomTZqEwMBAvPbaa/jll18M2khJScGb
307/// b76JoKAghISE4P/+7/+kssjISGi1WlRUVGDFihXSeHJzc/XaKC8vx4EDB5CWlvak
308/// ISHC3//+d7Ru3RoA0KJFC4wcORKFhYXIycmReWREytSoN3H0798fKpUKJ06ckLaF
309/// hYVBo9Hg6NGjyMrKqnG/X3/9FZ9++ik++OADjBo1ChMnTpTqZmRkICgoCEIILF68
310/// GP369cOQIUOQlJQk7X/q1Cm88MILqKqqQkxMDCZPnoyffvpJKh85ciQ0Gg3MzMzw
311/// 4osvQqPRQKPRwM7OTm8cV69excCBA/Hxxx83ZFiIAACpqalwcHCAi4uL3EMhUqRG
312/// fZivpaUl7O3tcfXqVWnboEGDAADz589/6L6XLl1CZmamQVJZsWIFunXrho0bN0Kl
313/// UiEsLAwXLlzA6tWrsW3bNgDAkiVL0K9fP2zatEnaLzIyUvpeo9EAACZOnIgXX3wR
314/// w4YNq3EMFhYW8PPzg5ub22PMmujRcnNzsXLlSqxcuRKWlpZyD4dIkRr9afQ6nQ5q
315/// tfqx9+vfv79B8gLuLw2q1WpER0dL2zIyMnD9+nXpdWpqKmbPnq23X32eBO3s7Izf
316/// fvvtsfcjepiSkhK8+uqrGDx4MCZOnCj3cIgUq1ETWElJCUpKSuDq6vrY+7Zt27bW
317/// Nj09PeHg4CBtGzBgAOzt7aXXt2/fhrW19eMPmKiR3blzBxEREWjTpg02b97Mf7FB
318/// 9AQaNYHFxcVBpVIhNDT0sfet7Q/by8sLzs7OmDdvXq37du7cGampqY/sw8zM7KF3
319/// gFVVVaGoqAiWlpZMiPTEKisrMW7cOJSWlmL//v2wsLCQe0hEitbgN3FcvXoVp06d
320/// wvr16zFnzhy88cYb6NKlS4O1P2HCBGzevBl79+6Vtp0+fVrv8zSvv/46/vWvf+HA
321/// gQMA7i9jbtmyxaAtT09P7NmzB2VlZTX2lZOTA0dHR8yaNavBxk+ma+LEiUhKSsK6
322/// detw7do1ZGVlISsrCxUVFXIPjUiZRAMpLCwUAAQAYWVlJby9vcVHH30kdDqdVGfL
323/// li1SnQe/OnTooNfW8OHDxVtvvVVrX2vWrBGtWrUS9vb2wsbGRjg6OorPP/9cKq+q
324/// qhIxMTHC2tpatG7dWlhaWoq+ffsatHP48GHx/PPPCwsLCwFA/Pe//9Urz87OFgDE
325/// lClT6hsWPdXzNVVyz1/u/q2trWv8/T969GiT9C/3/OXuX26mPv/GoBJCmf/fWwiB
326/// 7OxsqFQqdOjQAWZmhquhlZWVyMrKgo2NDdq3by/DKPVVL4sqNORPTO75y92/3OSe
327/// v9z9y83U598YFJvAlMjUf4Hlnr/c/ctN7vnL3b/cTH3+jYFPoyciIkViAiMiIkVi
328/// AiMiIkViAiMiIkViAiMiIkViAiMiIkViAiMiIkViAiMiIkViAiMiIkVq9P8HRob4
329/// LzTkxfjLi/GnhsIzMCIiUiQ+C9GE8Fls8mL85cX4Gx+egRERkSIxgRERkSIxgRER
330/// kSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRER
331/// kSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRER
332/// kSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRERkSIxgRER
333/// kSIxgRERkSIxgRERkSKZyT0Aanh5eXmYMGFCreUDBw402LZ582a4uLg04qhMB+Mv
334/// L8bfdDCBGSEXFxdYWlrip59+qrH8wIEDeq+HDh3KP94GxPjLi/E3HSohhJB7ENTw
335/// Tp8+jYCAANTlx5uYmIhevXo1wahMB+MvL8bfNPAamJHy8/PDkCFDHllv6NCh/ONt
336/// BIy/vBh/08AzMCNWl6NQHn02HsZfXoy/8eMZmBF71FEojz4bF+MvL8bf+PEMzMg9
337/// 7CiUR5+Nj/GXF+Nv3HgGZuRqOwrl0WfTYPzlxfgbN56BmYBTp04Z/LEmJSUhICBA
338/// phGZFsZfXoy/8eIZmAkICAjA0KFDpddDhw7lH28TYvzlxfgbL56BmYgHrwVw7b/p
339/// Mf7yYvyNExOYCQkPDwcA7Nq1S+aRmCbGX16Mv/FhAjMhp06dAgAun8iE8ZcX4298
340/// mMCIiEiReBMHEREpEhMYEREpEv+dShNSqVRyD6FZkGvVmvG/j/GXF6/aNByegRER
341/// kSLxDEwGpnoE1lyOwBl/eTH+1FB4BkZERIrEBEZERIrEBEZERIrEBEZERIrEBEZE
342/// RIrEBEZERIrEBEZERIrUpJ8Du3jxIm7evGmwvWfPnmjRgrm0qVy+fBn5+flwd3eH
343/// o6Oj3MMhahIVFRVITk5GVlYWWrVqhaCgILRp00buYdETaNKn0b/11lv49ttvUVFR
344/// gTt37qBVq1YAgLy8PFhZWTXVMGRT/UFGuT7Iefz4cYwZMwY5OTkAgC1btmDs2LFN
345/// 1r/c85e7f7nJPX+5+w8ODkZqairc3NxQXFyMGzduYNOmTRg5cmST9C/3/I1Rk572
346/// fPrppygoKMC7776Lp59+GgUFBSgoKDCJ5NUcODg4IDY2FmfPnpV7KERNbv369bhx
347/// 4wZSU1ORlZWFKVOm4G9/+xsqKyvlHhrVU7Natzt79iy0Wi1+++03DB06FIGBgVi5
348/// cqXeEcucOXMQFxeHuXPnws/PD1qtFrm5uXrtpKenY9KkSQgMDMRrr72GX375xaCv
349/// WbNmYc+ePfj0008RFBSEl156Cbt379arc+HCBRw4cAC3b99unAk3seeffx6vv/46
350/// unTpIvdQiJpc165doVarAdw/GxowYACKiopw48YNmUdG9dWsEtj169exfft2zJ07
351/// FxMnTsSoUaMQHR2Nffv2SXX27duHt956C/b29li4cCHOnz+PadOmSeUZGRkICgqC
352/// EAKLFy9Gv379MGTIECQlJen1tXfvXsTExCA+Ph6TJk3C8OHDce7cOb06GzZswMCB
353/// A5Gdnd24EyeiJlNZWYkLFy5g9erVeOGFF+Dk5CT3kKiemuXDfJcvX47evXsDAOLi
354/// 4nD06FGEhYVJ5aGhoXj33XcBAHfu3MHf/vY3qWzFihXo1q0bNm7cCJVKhbCwMOmX
355/// ddu2bXr9VFZWYs+ePbXeQOLi4gI/Pz8ucRIZiZ07d2LYsGEAAF9fX/z88898yK6C
356/// NcsE1r17d+n79u3bG5zie3t7S9+3a9cOxcXFuHv3LiwsLJCSkgK1Wo3o6GipTkZG
357/// Bq5fv27QzyuvvPLQux9nzpyJmTNnPslUiKgZCQ0NRWZmJi5fvozo6GhERETgl19+
358/// kZYWSVmaZQKztLSUvlepVAZ37VhYWOiVA///zp6SkhJ4enrCwcFBqjNgwADY29sb
359/// 9NO2bdsGHTcRNW/W1tbw8PCAh4cHvvnmG7i5ueHIkSMIDQ2Ve2hUD80ygT0JLy8v
360/// ODs7Y968eU/cVnl5OSoqKmBnZ8cjNCIjU31pwFhu0jJFTXoTx/Xr15GVlYXCwkJU
361/// VlYiKysLWVlZDfq5iAkTJmDz5s3Yu3evtO306dPYtWvXY7e1YMECODo6Gs1t5/fu
362/// 3ZNiDvz/n8etW7dkHhlR4yotLcXKlSuRnZ2NqqoqXLlyBe+88w7s7OwQGBgo9/Co
363/// npo0gc2dOxeenp5YvXo1rl27Bk9PT3h6eqK8vLzB+oiIiMDy5csRGRkJBwcH2Nra
364/// YtCgQbh8+XKD9aFUmZmZ8PT0ROfOnQHc/yiBp6cn1q1bJ/PIiBrfpk2b4OHhATMz
365/// M7Rv3x6JiYnYvn07nn76abmHRvXUpE/iaEpCCGRnZ0OlUqFDhw4wM5N/tdTUP4kv
366/// 9/zl7l9ucs9f7v4BoKCgAPn5+bC3t4eLi0uT9t0c5m9sjDaBNUem/gss9/zl7l9u
367/// cs9f7v7lZurzbwzN6oPMREREdcUERkREisQERkREisQERkREisQERkREisQERkRE
368/// isQERkREisQERkREisQERkREiiT/85VMEP+BnrwYf3kx/tRQeAZGRESKxGchEhGR
369/// IvEMjIiIFIkJjIiIFIkJzIQIIfivHGTE+MuL8Tc+TGAm5OTJk0hMTJR7GCaL8ZcX
370/// 4298eBu9Cdm+fTtUKhUCAwPlHopJYvzlxfgbH96FaCKEEOjUqROqqqqQk5PDz+I0
371/// McZfXoy/ceISoolITExETk4OcnNzuYwiA8ZfXoy/cWICMxHff/+99P327dtlHIlp
372/// YvzlxfgbJy4hmoDq5ZOcnBwAgKurK5dRmhDjLy/G33jxDMwEVC+fVOMyStNi/OXF
373/// +BsvJjAT8ODySTUuozQdxl9ejL/x4hKikRNCwN3dHf/73//0tnfo0AH/+9//uIzS
374/// yBh/eTH+xo1nYEbu5MmTBn+8AHDp0iUuozQBxl9ejL9xYwIzcg9bKuEySuNj/OXF
375/// +Bs3LiEasdqWT6pxGaVxMf7yYvyNH8/AjFhtyyfVuIzSuBh/eTH+xo8JzIjVZYmE
376/// yyiNh/GXF+Nv/LiEaKQetXxSjcsojYPxlxfjbxp4BmakEhMTH/nHC3AZpbEw/vJi
377/// /E0Dz8CIiEiReAZGRESKxARmQvr27Yu+ffvKPQyTxfjLi/E3PlxCNCHVF6r5I5cH
378/// 4y8vxt/48AyMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgU
379/// iQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgU
380/// iQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUiQmMiIgUSSWEEHIP
381/// wlS0atUKxcXFcg9DVnZ2digqKpKlb8af8ZebnPE3RkxgTUilUsk9hGZBrl85xv8+
382/// xl9efMttOGZyD8AUmeovcHN5A2P85cX4U0PhNTAiIlIkJjAiIlIkJjAiIlIkJjAi
383/// IlIkJjAiIlIkJjAiIlIkJjAiIlIkfg6MiEhBsrKyUFhYCB8fH5iZPfwtfPny5XB1
384/// dcW4ceOaaHQNQwiB5ORknD9/Hvb29ggKCoK9vb1BPZ6BEREpSHh4OPz9/XHkyJFH
385/// 1j1+/Dj++OOPBh/Dzp07ER4e3uDtVuvZsyeCgoKwZMkSTJo0Cc888wwOHjxoUI8J
386/// jIhIIS5cuIBz585hwIABiI+Pl20cN2/exJkzZxqt/TfffBPXrl3D2bNncfHiRYSG
387/// hmL8+PEG9ZjAiIgUIj4+Hr6+vhg7dmyNCez333/H8OHD4e/vjw8//NDgsV0///wz
388/// oqKiEBAQgMDAQERHR6OkpEQq3717N2bPno0vv/wSvXv3Rnh4OHbt2iWV7927F1qt
389/// FuvWrcONGzeg1Wqh1WoRGxur109CQgLGjBmDXr16YfTo0UhLSzMYa2RkJE6fPo2Y
390/// mBj06tULYWFhSE5OBgBMnz5dWjI0MzPDiBEjcPnyZVy7dk2vDSYwIiKFiI+PR1hY
391/// GAYNGoScnBykpqZKZfn5+ejXrx/at2+PJUuWIDEx0WCZcf/+/ejevTtiY2MRHR2N
392/// /fv3Y8KECVL5+fPn8dlnn+Hrr7/GggUL0LNnTwwfPhx//vknAMDT0xMajQYBAQGw
393/// traGRqOBRqPBSy+9JLVx+PBhhIaGwt3dHUuXLoW7uzsCAwORn5+vN5YffvgBU6ZM
394/// QXZ2NqZPn45+/frhwoULNc47NTUV7du3R+vWrfULBDUZAMKUQy73/OXuX25yz1/u
395/// /uX2pPO/fv26UKvV4ujRo0IIIXx8fERsbKxUvmjRIuHl5SWqqqqEEELcvn1b2Nra
396/// ijlz5tTa5sGDB4VarRaVlZVCCCHWrFkj1Gq1uHTpklQnODhYTJkyRW+/TZs2CTc3
397/// txrb7Nu3r3jzzTf1tgUHB4uFCxfqbWvRooXQarWPmLUQ586dE9bW1uLbb781KONd
398/// iERECrB7927Y2NggKCgIABAWFoYdO3YgJiYGAJCWlgY/Pz/pqfdPPfUUunXrptfG
399/// pUuXsGrVKiQnJ6O8vBzl5eWorKxEUVERHBwcAADt27eHq6urtE9AQAASExPrPM6U
400/// lBTY2tpi/vz50raioiKkp6cb1B02bNhD2yooKMDw4cMxZswYjBo1yqCcCYyISAGq
401/// r3m9/PLLAO4vGZ49exa5ublwdXVFWVkZ2rRpo7dPy5Ytpe/v3r2LkJAQ9OzZE8uX
402/// L0f79u1x5swZREREQKfT1bgPAFhaWqK0tLROY9TpdCgvL4eTk5OUEIH717s8PDwM
403/// 6js5OdXaVmlpKV555RV07twZn332WY11mMCIiJq5iooK7N27F5MnT0ZgYKC0fdKk
404/// Sdi5cyemTZuGjh07SteqgPufpcrMzISfnx+A+2doGRkZSEpKkpLLiRMnDPq6fPky
405/// KioqpESWnZ0NNzc3vTrm5uZ6Sa+amZkZPDw84OPjg5kzZ9Z7vnfu3MHw4cNhbW2N
406/// bdu2Qa1W11iPN3EQETVzhw8fRmlpKWbPni3dOKHRaBAaGiqdmUVGRuLYsWM4fvw4
407/// AGDLli3IycmR2nB0dIRKpZLKL126hA8//NCgr7KyMqxevRoAcPbsWcTHx0Oj0ejV
408/// 8fDwwNWrV5GQkICqqiq9sgkTJmDlypX4/fffAQCVlZU4dOgQEhIS6jzfqKgoZGdn
409/// Y/Xq1cjLy0NWVhaysrJw584d/YqPvIJGDQa8iM2bCGQk9/zl7l9uTzL/qVOnCm9v
410/// b4PtGzZsEJaWlqK4uFgIIcTChQuFWq0WrVu3Fj4+PiIoKEjvJo7Y2Fhhbm4unJ2d
411/// haOjo1i2bJkAIK5duyaEuH8TR+fOnUVwcLBwcHAQarVaTJkyReh0OoO+Z8yYIZyc
412/// nAQAERISIm3X6XRi1qxZomXLlqJt27bC0tJSuLi4iD179ujt36JFC7Fv3z6Ddisr
413/// K6VY/fUrOTlZr65KCBP9/94yqL64aqohl3v+cvcvN7nnL3f/cmuq+ZeUlCAvLw9e
414/// Xl5o0cJwka2goAD5+fnw8PCAlZWVXtnatWvx5Zdf4syZMzh//jzatm2rdy3rceh0
415/// OmRmZsLGxgbOzs7S/BsSr4ERERkRW1tbdO3atdZyR0dHODo6PrIdLy+vJxqHmZkZ
416/// unTp8kRtPAqvgREREYD7dyDa2dnJPYw64xJiE+ISCpew5CT3/OXuX26mPv/GwDMw
417/// IiJSJCYwIiJSJCYwIiJSJCYwIiJSJCYwIiJSJCYwIiJSJCYwIiJSJD6JQwaN8UgV
418/// qjvGX16MPzUUnoE1ISV9wr2xyBkDxp/xlxtj0LD4JA4TwicByIvxlxfjb3x4BkZE
419/// RIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZE
420/// RIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZE
421/// RIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZERIrEBEZE
422/// RIrEBEZERIrEBEZERIrEBEZERIrEBEZERIqkEkIIuQdhKlq1aoXi4mK5hyErOzs7
423/// FBUVOygiRwAAEwFJREFUydI348/4y03O+BsjJrAmpFKp5B5CsyDXrxzjfx/jLy++
424/// 5TYcM7kHYIpM9Re4ubyBMf7yYvypofAaGBERKRITGBERKRITGBERKRITGBERKRIT
425/// GBERKRITGBERKRITGBERKVK9ElhWVhZOnz4NnU73yLrLly/Hli1b6tNNk9qwYQPW
426/// rl0r9zDIyKWkpGDbtm3YvXs3CgoK5B4OKZAxvv/WJjc3F6dPn8atW7dqLK9XAgsP
427/// D4e/vz+OHDnyyLrHjx/HH3/8UZ9uHmrnzp0IDw9vsPZOnz6NxMTEBmuP6K/69OmD
428/// gIAALF68GFOnToWnpyd27dol97BIYYzx/fev9u3bhw4dOqBDhw4PnetjJ7CMjAyc
429/// O3cOoaGhiI+Pf9Jx1tvNmzdx5swZ2fonelxRUVG4cuUK0tLScPHiRURERGD8+PF1
430/// OpImAoALFy7g3LlzGDBggFG//zo5OWHZsmVITk5+aL3HTmA7duyAr68vxo0bV2MA
431/// f//9dwwfPhz+/v748MMPDR4b8/PPPyMqKgoBAQEIDAxEdHQ0SkpKpPLdu3dj9uzZ
432/// +PLLL9G7d2+Eh4frHaXu3bsXWq0W69atw40bN6DVaqHVahEbG6vXT0JCAsaMGYNe
433/// vXph9OjRSEtL0yvPysrC6NGj4efnh/nz5+PevXs1zjclJQUHDhww2cffUMP5+9//
434/// jtatWwMAWrRogZEjR6KwsBA5OTkyj4yUIj4+Hr6+vhg7dqyi338BIDIyEqdPn0ZM
435/// TAx69eqFsLAwKWH16NED48aNg6en50Pj8dgJLD4+HmFhYRg0aBBycnKQmpoqleXn
436/// 56Nfv35o3749lixZgsTERINTv/3796N79+6IjY1FdHQ09u/fjwkTJkjl58+fx2ef
437/// fYavv/4aCxYsQM+ePTF8+HD8+eefAABPT09oNBoEBATA2toaGo0GGo0GL730ktTG
438/// 4cOHERoaCnd3dyxduhTu7u4IDAxEfn4+AKCsrAx9+vQBAHzwwQe4ceMGtm3bVuN8
439/// Y2JiMHDgQB4lU4NLTU2Fg4MDXFxc5B4KKYQxvP9W++GHHzBlyhRkZ2dj+vTp6Nev
440/// Hy5cuPB4ARGP4fr160KtVoujR48KIYTw8fERsbGxUvmiRYuEl5eXqKqqEkIIcfv2
441/// bWFrayvmzJlTa5sHDx4UarVaVFZWCiGEWLNmjVCr1eLSpUtSneDgYDFlyhS9/TZt
442/// 2iTc3NxqbLNv377izTff1NsWHBwsFi5cKIQQ4osvvhAODg6ivLxcCCFEZWWl6NSp
443/// kxg1apRBWzNnzhR+fn7i3r17tc6hrgCIxwy5UZF7/nL3/6BLly4JBwcH8cUXXzRZ
444/// n3LPX+7+5fak8zeW999qLVq0EFqt9qFzLioqEgBEXFxcjeWP9TT63bt3w8bGBkFB
445/// QQCAsLAw7NixAzExMQCAtLQ0+Pn5SU9dfuqpp9CtWze9Ni5duoRVq1YhOTkZ5eXl
446/// KC8vR2VlJYqKiuDg4AAAaN++PVxdXaV9AgICHusGi5SUFNja2mL+/PnStqKiIqSn
447/// p0vj7NatG1q2bAng/nJOQEBAjW2tWbOmzv0S1UVJSQleffVVDB48GBMnTpR7OKQQ
448/// xvL++6Bhw4bVud2aPFYCq15zffnllwHcP2U9e/YscnNz4erqirKyMrRp00Zvn+ok
449/// AQB3795FSEgIevbsieXLl6N9+/Y4c+YMIiIi9JboHtwHACwtLVFaWlqnMep0OpSX
450/// l8PJyUn6gQD311s9PDwA3F9C/GsfLVu2rPU6GFFDuXPnDiIiItCmTRts3ryZ/2KD
451/// 6sxY3n8f5OTkVKd2a1PnBFZRUYG9e/di8uTJCAwMlLZPmjQJO3fuxLRp09CxY0dp
452/// rRS4/39/MjMz4efnB+D+EUJGRgaSkpKkyZ04ccKgr8uXL6OiokIKZHZ2Ntzc3PTq
453/// mJub13hdyszMDB4eHvDx8cHMmTNrnEvHjh2xf/9+vW2ZmZkGfQDA7du3ce/ePb0f
454/// BlF9VFZWYty4cSgtLcX+/fthYWEh95BIIYzp/bdBPXQB8gG7d+8WKpVK5OXl6W0f
455/// Pny4GDx4sBBCiGPHjgm1Wi2OHTsmhBDiq6++EgCkNdicnByhUqlEfHy89Pq5554T
456/// AMS1a9eEEPfXYAGIpUuXCiGE+O9//ytsbW3FN998o9fviRMnhFqtFsePH5fWb6st
457/// W7ZMODs7i+TkZCGEEDqdThw8eFAcP35cCCFERkaGUKvV4vvvvxdCCLF//36hUqlq
458/// vAYWHh4uAIi7d+/WNVS1Aq8BmPQ1mPHjx4uOHTuK5ORkkZmZKX1VX4ttbHLPX+7+
459/// 5fYk8zem999qLVq0EPv27atxvhUVFSIzM1OkpqYKAGL9+vUiMzNT3Lp1S69enaM5
460/// depU4e3tbbB9w4YNwtLSUhQXFwshhFi4cKFQq9WidevWwsfHRwQFBeldRIyNjRXm
461/// 5ubC2dlZODo6imXLlhkEsHPnziI4OFg4ODgItVotpkyZInQ6nUHfM2bMEE5OTgKA
462/// CAkJkbbrdDoxa9Ys0bJlS9G2bVthaWkpXFxcxJ49e6Q669evFxYWFqJ169bC3d1d
463/// hIWFMYE1MrnnL3f/1tbW0hge/Kq+KN/Y5J6/3P3L7Unmb2zvv0I8PIGdOnWqxr+V
464/// jz/+WK+eSoiG/4BTSUkJ8vLy4OXlhRYtDO/ULygoQH5+Pjw8PGBlZaVXtnbtWnz5
465/// 5Zc4c+YMzp8/j7Zt29Z7+U6n0yEzMxM2NjZwdnY2uN5QXl6Oixcv4plnnoG5uXm9
466/// +ngc1f03QsgVQe75y92/3OSev9z9y62p5q+U99+G8Fg3cdSVra0tunbtWmu5o6Mj
467/// HB0dH9mOl5fXE43DzMwMXbp0qbXcysoKzz777BP1QUTUnCjl/bchNLun0bds2RJ2
468/// dnZyD4OIyOQo7f23UZYQqWZcQuESlpzknr/c/cvN1OffGJrdGRgREVFdMIEREZEi
469/// MYEREZEiMYEREZEiMYEREZEiMYEREZEiMYEREZEiNcqTOOjh+C805MX4y4vxp4bC
470/// M7AmpKRPuDcWOWPA+DP+cmMMGhafxEFERIrEMzAiIlIkJjAiIlIkJjAiIlIkJjAi
471/// IlIkJjAiIlIkJjAiIlIkJjAiIlIkPolDQa5fv46cnByD7W3atEHHjh2bbByjR4/G
472/// /Pnz4ePj02R9NqWsrCwUFhbCx8cHZmaN8yei0+kQFRWF2NhYdO3atVH6MCaVlZVI
473/// SUkBAFhaWqJTp06wsbGReVQkN56BKch//vMf+Pv7Y+DAgXpfa9eubfJxXLlypUn7
474/// bErh4eHw9/fHkSNH6rX/mTNn4O/vj4qKilrrVFVV4cCBA7h161Y9R2laSkpK4O/v
475/// j5deegk9e/aEg4MDIiMjUVBQIPfQSEZMYApjbm6OgoICva81a9bIPSyjkZGRgXPn
476/// ziE0NBTx8fH1aqO0tBSnT59GVVVVrXUsLCxQUFCAwMDA+g7VJH311VcoKytDYmIi
477/// 0tLSEBERIfeQSEZMYEamsLAQWq0W586dw4wZM+Dv749hw4YhMzNTqvOvf/0Lr776
478/// KgIDAzFz5kwUFRXptXHq1CmMHDkSvr6+CA4OxgcffGDQz82bNzFx4kT4+flh8uTJ
479/// KC4ubvS5NYUdO3bA19cX48aNqzWBbd++HVqtFv7+/njttddw4MABAMCff/4JrVaL
480/// 9957DwAwbtw4aLVaTJkyRW//0aNHQ6vVQqvVIj09Xa/su+++w7vvvqu3TafTYezY
481/// sUhKSpK2JSQkYMyYMejVqxdGjx6NtLS0J567Upibm8PX1xcrVqzA0aNHcfLkSaks
482/// PT0db7zxBnr37o0RI0bgp59+0tt3zpw5iIuLw9y5c+Hn5wetVovc3Fy9Ounp6Zg0
483/// aRICAwPx2muv4ZdffmmSedHjYwJToMLCQr2vyspKqay8vBzbt2/H2LFjce/ePcyc
484/// ORPe3t64ePEiAGDx4sWYN28ewsPDsWjRImRmZuLll19G9SMxCwsLMXDgQHh4eODj
485/// jz/GO++8g8uXLxuMYeHChejVqxfmzZuHn376CUuWLGmSuTe2+Ph4hIWFYdCgQcjJ
486/// yUFqaqpe+dq1azF+/Hj06NEDy5cvx8CBA6VE5+TkBI1Gg9DQUABAREQENBoNXnnl
487/// Fb02RowYgYiICGzfvh03btzQK+vYsSNWrFiBq1evStsOHTqEH3/8UbpWdvjwYYSG
488/// hsLd3R1Lly6Fu7s7AgMDkZ+f3+DxaM769+8PlUqFEydOALh/5hsSEoKioiIsXrwY
489/// zz//PF577TW9BLdv3z689dZbsLe3x8KFC3H+/HlMmzZNKs/IyEBQUBCEEFi8eDH6
490/// 9euHIUOG6B08UDMiSDE+++wzAcDg68yZM1KdvLw8AUDMmjXLYP+ioiLRsmVL8e23
491/// 30rbiouLhZWVlTh27JgQQoikpCQBQFy7dq3WcZibm4sPPvhAeh0bGyt69erVEFOU
492/// 1fXr14VarRZHjx4VQgjh4+MjYmNjpfK7d+8KOzs7sXLlSr39qqqq9F7/+uuvAoAo
493/// LS2tta87d+4IAFLcH2zrmWeeEZ988om0bcKECUKj0Uiv+/btK9588029/YKDg8XC
494/// hQvrNlEFKiwsFADEDz/8oLfdwcFBzJ07VwghxOeffy7s7e1FWVmZVB4eHq4Xu+7d
495/// u4uxY8dKr7///nthZ2cnvZ40aZLo06eP3s906tSpIjIyssHnRE+OdyEqjJmZGc6d
496/// O6e3zdXV1aDesGHDDLalpaWhoqICx44dk+7oAgArKyukp6ejT58+ePbZZ9GhQwcE
497/// BQVhxIgRCA0NRXBwMCwsLPTa8vb2lr5v3769wZmEEu3evRs2NjYICgoCAISFhWHH
498/// jh2IiYkBAGRnZ6O4uBhDhgzR268h/7+VSqVCVFQUtm3bhrfffht37txBXFwcvvrq
499/// K6lOSkoKbG1tMX/+fGlbUVGRwXKkKdDpdFCr1QDuL/15e3vDyspKKu/duze2bdum
500/// t8+Dv7vt2rVDcXEx7t69CwsLC6SkpECtViM6Olqqk5GRgevXrzfyTKg+mMAURqVS
501/// wcPD45H1nJycDLbdvn0bAODs7Kx3e/jcuXOlW+JtbGyQnJyMrVu3Yu/evVi9ejV6
502/// 9OiB48ePw9LSUtrnrwlNGMF/5aleCnz55ZcBAPn5+Th79ixyc3Ph6uqK0tJSAIC1
503/// tXWjjiMqKgpLlixBTk4OkpOToVarpTHpdDqUl5fDyckJDg4O0j6RkZF1+r0wJiUl
504/// JSgpKZEO4EpLS9GyZUu9OlZWVigrK9Pb9uDvbvXBR/Xvb0lJCTw9PfViO2DAANjb
505/// 2zfKHOjJMIGZkM6dOwMAXn31VXTr1q3Wem3atMGMGTMwY8YMnD9/Hl27dsWvv/6K
506/// 4ODgJhpp06uoqMDevXsxefJkvTsDJ02ahJ07d2LatGnw9PSESqVCamoqOnXqVGtb
507/// 1QcHOp2uXmPp0qUL/Pz88N133+G3337DyJEjpTddMzMzeHh4wMfHBzNnzqxX+8Yi
508/// Li4OKpVKuubo5uaGQ4cO6dXJyMiAm5tbndv08vKCs7Mz5s2b16BjpcbBmzhMiJub
509/// G0JCQjBjxgxpSaSsrAz//ve/pc91XbhwAQcPHpSOSKtvJnB2dpZn0E3k8OHDKC0t
510/// xezZs6HRaKSvB2+nt7OzQ0REBN577z1kZ2cDuL909+OPP+q11alTJ6jVauzYsaPe
511/// SWzMmDHYvHkzdu3ahTFjxuiVTZgwAStXrsTvv/8O4P6HfA8dOoSEhIR69aUkV69e
512/// xalTp7B+/XrMmTMHb7zxBrp06QIA0Gg0yM7Oxtdffw3g/pL5d999h1GjRtW5/QkT
513/// JmDz5s3Yu3evtO306dPYtWtXw06EGobM1+DoMXz22WfC3Nz8oXWqb+I4e/ZsjeVX
514/// rlwRgwcPFmZmZqJdu3aiRYsWwsfHR1y+fFkIcf8mjrZt2worKyvh6uoqnnrqKfHh
515/// hx/qtWFubi5+/vln6fXGjRuFu7v7E85OXlOnThXe3t4G2zds2CAsLS1FcXGxEEKI
516/// goICERERIdRqtXj66aeFubm5WLBggcF+a9asEa6urkKlUglnZ2dp+/vvv1/jjTgD
517/// Bw7U2//y5ctCrVYLNzc3UVlZqVem0+nErFmzRMuWLUXbtm2FpaWlcHFxEXv27GmI
518/// UDRL1TdxABBWVlbC29tbfPTRR0Kn0+nV+/jjj4WVlZVo3bq1UKvVIioqSty5c0cq
519/// 7969u1i7dq30+tixYwKAqKiokLatWbNGtGrVStjb2wsbGxvh6OgoPv/888afJD02
520/// lRBGcPGCHltZWRkuXryIp59+Gq1bt9YrE0IgNzcXxcXFcHd3b/RrPkpUUVGB7Oxs
521/// ODs7o1WrVrKMQafTITMzEzY2NnB2dm7Qm0mU7N69e9LPpr6PmxJCIDs7GyqVCh06
522/// dGi0R4rRk2ECIyIiReI1MCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIi
523/// UiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIi
524/// UiQmMCIiUiQmMCIiUiQmMCIiUiQmMCIiUqT/B52Ay/eeLCmcAAAAAElFTkSuQmCC
525/// ">
526/// </div>
527///
528/// Columns depict method call chains. The __Inner__ row is the final call destination for
529/// all stream variants. At the bottom of each column is the expected state before
530/// the call to a method. Numbers in call site boxes are the points of state transitions.
531///
532/// `SearchStream` has two variants, direct and adapted, differentiated by the size of
533/// the adapter vector. The direct version, with an empty vector, is the regular one;
534/// the adapted version passes each method call through a chain of adapters before executing
535/// the direct call. In the diagram, direct calls start from the top of the column, while adapted
536/// calls start at the bottom.
537///
538/// Every `SearchStream` is created in the `Fresh` state, and the `start()` method is automatically
539/// called. The `start()` method, although publicly visible so that adapter chaining can work, is
540/// not meant for calls from user code. It will change the state from `Fresh` to `Active` at point (1),
541/// when the protocol request is successfully written to the network socket. Any error in submitting
542/// the request will change the state to `Error`. Calling `start()` in any state but `Fresh` will just
543/// immediately return.
544///
545/// Iterating through the stream with `next()` requires the `Active` state, which turns into `Done`
546/// when the final Search message is received. However, the transition must not be made in the
547/// inner method, since the adapters may need to keep providing additional entries even when
548/// the original operation is over. Therefore, point (2) occurs at the end of the first call
549/// in the chain (for the adapted streams), or in the shim method (for the direct ones). As before,
550/// any error will result in the `Error` state.
551///
552/// The `finish()` method may be called at any time. Adapters along the way can behave differently
553/// according to the state, and the final direct call will change the state to `Closed` at (3). Calling
554/// `finish()` on a stream in the `Closed` state will return a synthetic error-bearing `LdapResult`.
555#[derive(Clone, Copy, Debug, Eq, PartialEq)]
556pub enum StreamState {
557 /// Stream which hasn't yet been initialized in `start()`.
558 Fresh,
559 /// Initialized stream which can be iterated through with `next()`.
560 Active,
561 /// Stream from which all entries have been retrieved.
562 Done,
563 /// Properly finalized stream on which `finish()` was called.
564 Closed,
565 /// Stream in an error state after some fallible operation.
566 Error,
567}
568
569/// Asynchronous handle for obtaining a stream of search results. __*__
570///
571/// User code can't construct a stream directly, but only by using
572/// [`streaming_search()`](struct.Ldap.html#method.streaming_search) or
573/// [`streaming_search_with()`](struct.Ldap.html#method.streaming_search_with) on
574/// an `Ldap` handle.
575///
576/// A streaming search should be used for situations where the expected
577/// size of result entries varies considerably between searches, and/or
578/// can rise above a few tens to hundreds of KB. This is more of a concern
579/// for a long-lived process which is expected to have a predictable memory
580/// footprint (i.e., a server), but can also help with one-off searches if
581/// the result set is in the tens of thounsands of entries.
582///
583/// Once initiated, a streaming search is driven to the end by repeatedly calling
584/// [`next()`](#method.next) until it returns `Ok(None)` or an error. Then, a call
585/// to [`finish()`](#method.finish) will return the overall result of the search.
586/// Calling `finish()` earlier will terminate search result processing in the
587/// client; it is the user's responsibility to inform the server that the operation
588/// has been terminated by performing an Abandon or a Cancel operation.
589///
590/// There are two variants of `SearchStream`, direct and adapted. The former calls
591/// stream operations directly, while the latter first passes through a chain of
592/// [adapters](adapters/index.html) given at the time of stream creation.
593#[derive(Debug)]
594pub struct SearchStream<'a, S, A> {
595 pub(crate) ldap: Ldap,
596 pub(crate) rx: Option<mpsc::UnboundedReceiver<(SearchItem, Vec<Control>)>>,
597 state: StreamState,
598 #[allow(clippy::type_complexity)]
599 adapters: Vec<Arc<Mutex<Box<dyn Adapter<'a, S, A> + 'a>>>>,
600 ax: usize,
601 timeout: Option<Duration>,
602 pub res: Option<LdapResult>,
603}
604
605impl<'a, S, A> SearchStream<'a, S, A>
606where
607 S: AsRef<str> + Send + Sync + 'a,
608 A: AsRef<[S]> + Send + Sync + 'a,
609{
610 pub(crate) fn new(ldap: Ldap, adapters: Vec<Box<dyn Adapter<'a, S, A> + 'a>>) -> Self {
611 SearchStream {
612 ldap,
613 rx: None,
614 state: StreamState::Fresh,
615 adapters: adapters.into_iter().map(Mutex::new).map(Arc::new).collect(),
616 ax: 0,
617 timeout: None,
618 res: None,
619 }
620 }
621
622 pub(crate) async fn start_inner(
623 &mut self,
624 base: &str,
625 scope: Scope,
626 filter: &str,
627 attrs: A,
628 ) -> Result<()> {
629 let opts = match self.ldap.search_opts.take() {
630 Some(opts) => opts,
631 None => SearchOptions::new(),
632 };
633 self.timeout = self.ldap.timeout;
634 let req = Tag::Sequence(Sequence {
635 id: 3,
636 class: TagClass::Application,
637 inner: vec![
638 Tag::OctetString(OctetString {
639 inner: Vec::from(base.as_bytes()),
640 ..Default::default()
641 }),
642 Tag::Enumerated(Enumerated {
643 inner: scope as i64,
644 ..Default::default()
645 }),
646 Tag::Enumerated(Enumerated {
647 inner: opts.deref as i64,
648 ..Default::default()
649 }),
650 Tag::Integer(Integer {
651 inner: opts.sizelimit as i64,
652 ..Default::default()
653 }),
654 Tag::Integer(Integer {
655 inner: opts.timelimit as i64,
656 ..Default::default()
657 }),
658 Tag::Boolean(Boolean {
659 inner: opts.typesonly,
660 ..Default::default()
661 }),
662 match parse_filter(filter) {
663 Ok(filter) => filter,
664 _ => return Err(LdapError::FilterParsing),
665 },
666 Tag::Sequence(Sequence {
667 inner: attrs
668 .as_ref()
669 .iter()
670 .map(|s| {
671 Tag::OctetString(OctetString {
672 inner: Vec::from(s.as_ref()),
673 ..Default::default()
674 })
675 })
676 .collect(),
677 ..Default::default()
678 }),
679 ],
680 });
681 let (tx, rx) = mpsc::unbounded_channel();
682 self.rx = Some(rx);
683 if let Some(timeout) = self.timeout {
684 self.ldap.with_timeout(timeout);
685 }
686 self.ldap.op_call(LdapOp::Search(tx), req).await.map(|_| {
687 self.state = StreamState::Active;
688 })
689 }
690
691 pub(crate) async fn next_inner(&mut self) -> Result<Option<ResultEntry>> {
692 let item = if let Some(timeout) = self.timeout {
693 let res = time::timeout(timeout, self.rx.as_mut().unwrap().recv()).await;
694 if res.is_err() {
695 let last_id = self.ldap.last_id;
696 self.ldap.id_scrub_tx.send(last_id)?;
697 }
698 res?
699 } else {
700 self.rx.as_mut().unwrap().recv().await
701 };
702 let (item, controls) = match item {
703 Some((item, controls)) => (item, controls),
704 None => {
705 self.rx = None;
706 return Err(LdapError::EndOfStream);
707 }
708 };
709 match item {
710 SearchItem::Entry(tag) | SearchItem::Referral(tag) => {
711 return Ok(Some(ResultEntry(tag, controls)))
712 }
713 SearchItem::Done(mut res) => {
714 res.ctrls = controls;
715 self.res = Some(res);
716 self.rx = None;
717 }
718 }
719 Ok(None)
720 }
721
722 pub(crate) async fn finish_inner(&mut self) -> LdapResult {
723 if self.state != StreamState::Done {
724 let last_id = self.ldap.last_id;
725 if let Err(e) = self.ldap.id_scrub_tx.send(last_id) {
726 warn!(
727 "error sending scrub message from SearchStream::finish() for ID {}: {}",
728 last_id, e
729 );
730 }
731 }
732 self.state = StreamState::Closed;
733 self.rx = None;
734 self.res.take().unwrap_or_else(|| LdapResult {
735 rc: 88,
736 matched: String::from(""),
737 text: String::from("user cancelled"),
738 refs: vec![],
739 ctrls: vec![],
740 })
741 }
742
743 /// Initialize a streaming Search.
744 ///
745 /// This method exists as an initialization point for search adapters, and is
746 /// not meant for calling from regular user code. It must be public for user-defined
747 /// adapters to work, but explicitly calling it on a `SearchStream` handle
748 /// is a no-op: it will immediately return `Ok(())`.
749 pub async fn start(&mut self, base: &str, scope: Scope, filter: &str, attrs: A) -> Result<()> {
750 if self.state != StreamState::Fresh {
751 return Ok(());
752 }
753 if self.ax == self.adapters.len() {
754 let res = self.start_inner(base, scope, filter, attrs).await;
755 if res.is_err() {
756 self.state = StreamState::Error;
757 }
758 return res;
759 }
760 let adapter = self.adapters[self.ax].clone();
761 let mut adapter = adapter.lock().await;
762 self.ax += 1;
763 let res = (&mut adapter).start(self, base, scope, filter, attrs).await;
764 self.ax -= 1;
765 if res.is_err() {
766 self.state = StreamState::Error;
767 }
768 res
769 }
770
771 /// Fetch the next item from the result stream after executing the adapter chain
772 /// if there is one.
773 ///
774 /// Returns `Ok(None)` at the end of the stream.
775 #[allow(clippy::should_implement_trait)]
776 pub async fn next(&mut self) -> Result<Option<ResultEntry>> {
777 if self.state != StreamState::Active {
778 return Ok(None);
779 }
780 if self.ax == self.adapters.len() {
781 let res = self.next_inner().await;
782 if res.is_err() {
783 self.state = StreamState::Error;
784 }
785 return res;
786 }
787 let adapter = self.adapters[self.ax].clone();
788 let mut adapter = adapter.lock().await;
789 self.ax += 1;
790 let res = (&mut adapter).next(self).await;
791 self.ax -= 1;
792 match res {
793 Ok(None) if self.ax == 0 => self.state = StreamState::Done,
794 Err(_) => self.state = StreamState::Error,
795 _ => (),
796 }
797 res
798 }
799
800 /// Return the overall result of the Search.
801 ///
802 /// This method can be called at any time. If the stream has been read to the
803 /// end, the return value will be the actual result returned by the server.
804 /// Otherwise, a synthetic cancellation result is returned, and it's the user's
805 /// responsibility to abandon or cancel the operation on the server.
806 ///
807 /// If the Search is adapted, this method will first execute the `finish()` methods of
808 /// all adapters in the chain.
809 pub async fn finish(&mut self) -> LdapResult {
810 if self.state == StreamState::Closed {
811 return LdapResult {
812 rc: 80,
813 matched: String::from(""),
814 text: String::from("stream already finalized"),
815 refs: vec![],
816 ctrls: vec![],
817 };
818 }
819 if self.ax == self.adapters.len() {
820 return self.finish_inner().await;
821 }
822 let adapter = self.adapters[self.ax].clone();
823 let mut adapter = adapter.lock().await;
824 self.ax += 1;
825 let res = (&mut adapter).finish(self).await;
826 self.ax -= 1;
827 res
828 }
829
830 /// Return a vector of the remaining adapters in the chain at the point
831 /// of the method call. Adapter instances are cloned and collected into the
832 /// resulting vector. The purpose of this method is to enable uniformly
833 /// configured Search calls on the connections newly opened in an adapter.
834 pub async fn adapter_chain_tail(&mut self) -> Vec<Box<dyn Adapter<'a, S, A> + 'a>> {
835 let mut chain = vec![];
836 for ix in self.ax..self.adapters.len() {
837 let adapter = self.adapters[ix].clone();
838 let adapter = adapter.lock().await;
839 chain.push(adapter.as_ref().box_clone());
840 }
841 chain
842 }
843
844 /// Return the current state of the stream.
845 pub fn state(&self) -> StreamState {
846 self.state
847 }
848
849 /// Return the `Ldap` handle of the stream.
850 ///
851 /// Mutating the public elements of `Ldap` through the obtained handle can't affect
852 /// the current operation _except_ in the `start()` chain of an adapted streaming
853 /// Search, intentionally so.
854 pub fn ldap_handle(&mut self) -> &mut Ldap {
855 &mut self.ldap
856 }
857}
858
859/// Parse the referrals from the supplied BER-encoded sequence.
860pub fn parse_refs(t: StructureTag) -> Vec<String> {
861 t.expect_constructed()
862 .expect("referrals")
863 .into_iter()
864 .map(|t| t.expect_primitive().expect("octet string"))
865 .map(String::from_utf8)
866 .map(|s| s.expect("uri"))
867 .collect()
868}