1#![allow(missing_docs)]
2use imap_proto::{self, MailboxDatum, Response};
4use lazy_static::lazy_static;
5use regex::Regex;
6use std::collections::HashSet;
7use std::sync::mpsc;
8
9use super::error::{Error, ParseError, Result};
10use super::types::*;
11
12lazy_static! {
13 static ref AUTH_RESP_REGEX: Regex = Regex::new("^\\+ (.*)\r\n").unwrap();
14}
15
16pub fn parse_authenticate_response(line: &str) -> Result<&str> {
17 if let Some(cap) = AUTH_RESP_REGEX.captures_iter(line).next() {
18 let data = cap.get(1).map(|x| x.as_str()).unwrap_or("");
19 return Ok(data);
20 }
21 Err(Error::Parse(ParseError::Authentication(
22 line.to_string(),
23 None,
24 )))
25}
26
27enum MapOrNot<T> {
28 Map(T),
29 Not(Response<'static>),
30 #[allow(dead_code)]
31 Ignore,
32}
33
34unsafe fn parse_many<T, F>(
35 lines: Vec<u8>,
36 mut map: F,
37 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
38) -> ZeroCopyResult<Vec<T>>
39where
40 F: FnMut(Response<'static>) -> Result<MapOrNot<T>>,
41{
42 let f = |mut lines: &'static [u8]| {
43 let mut things = Vec::new();
44 loop {
45 if lines.is_empty() {
46 break Ok(things);
47 }
48
49 match imap_proto::parse_response(lines) {
50 Ok((rest, resp)) => {
51 lines = rest;
52
53 match map(resp)? {
54 MapOrNot::Map(t) => things.push(t),
55 MapOrNot::Not(resp) => match handle_unilateral(resp, unsolicited) {
56 Some(Response::Fetch(..)) => continue,
57 Some(resp) => break Err(resp.into()),
58 None => {}
59 },
60 MapOrNot::Ignore => continue,
61 }
62 }
63 _ => {
64 break Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
65 }
66 }
67 }
68 };
69
70 ZeroCopy::make(lines, f)
71}
72
73pub fn parse_names(
74 lines: Vec<u8>,
75 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
76) -> ZeroCopyResult<Vec<Name>> {
77 let f = |resp| match resp {
78 Response::MailboxData(MailboxDatum::List {
80 flags,
81 delimiter,
82 name,
83 }) => Ok(MapOrNot::Map(Name {
84 attributes: flags.into_iter().map(NameAttribute::from).collect(),
85 delimiter,
86 name,
87 })),
88 resp => Ok(MapOrNot::Not(resp)),
89 };
90
91 unsafe { parse_many(lines, f, unsolicited) }
92}
93
94pub fn parse_fetches(
95 lines: Vec<u8>,
96 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
97) -> ZeroCopyResult<Vec<Fetch>> {
98 let f = |resp| match resp {
99 Response::Fetch(num, attrs) => {
100 let mut fetch = Fetch {
101 message: num,
102 flags: vec![],
103 uid: None,
104 size: None,
105 fetch: attrs,
106 };
107
108 for attr in &fetch.fetch {
110 use imap_proto::AttributeValue;
111 match attr {
112 AttributeValue::Flags(flags) => {
113 fetch.flags.extend(flags.iter().cloned().map(Flag::from));
114 }
115 AttributeValue::Uid(uid) => fetch.uid = Some(*uid),
116 AttributeValue::Rfc822Size(sz) => fetch.size = Some(*sz),
117 _ => {}
118 }
119 }
120
121 Ok(MapOrNot::Map(fetch))
122 }
123 resp => Ok(MapOrNot::Not(resp)),
124 };
125
126 unsafe { parse_many(lines, f, unsolicited) }
127}
128
129pub fn parse_expunge(
130 lines: Vec<u8>,
131 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
132) -> Result<Vec<u32>> {
133 let f = |resp| match resp {
134 Response::Expunge(id) => Ok(MapOrNot::Map(id)),
135 resp => Ok(MapOrNot::Not(resp)),
136 };
137
138 unsafe { parse_many(lines, f, unsolicited).map(|ids| ids.take()) }
139}
140
141pub fn parse_capabilities(
142 lines: Vec<u8>,
143 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
144) -> ZeroCopyResult<Capabilities> {
145 let f = |mut lines| {
146 let mut caps = HashSet::new();
147 loop {
148 match imap_proto::parse_response(lines) {
149 Ok((rest, Response::Capabilities(c))) => {
150 lines = rest;
151 caps.extend(c);
152 }
153 Ok((rest, data)) => {
154 lines = rest;
155 if let Some(resp) = handle_unilateral(data, unsolicited) {
156 break Err(resp.into());
157 }
158 }
159 _ => {
160 break Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
161 }
162 }
163
164 if lines.is_empty() {
165 break Ok(Capabilities(caps));
166 }
167 }
168 };
169
170 unsafe { ZeroCopy::make(lines, f) }
171}
172
173pub fn parse_noop(
174 lines: Vec<u8>,
175 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
176) -> Result<()> {
177 let mut lines: &[u8] = &lines;
178
179 loop {
180 if lines.is_empty() {
181 break Ok(());
182 }
183
184 match imap_proto::parse_response(lines) {
185 Ok((rest, data)) => {
186 lines = rest;
187 if let Some(resp) = handle_unilateral(data, unsolicited) {
188 break Err(resp.into());
189 }
190 }
191 _ => {
192 break Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
193 }
194 }
195 }
196}
197
198pub fn parse_mailbox(
199 mut lines: &[u8],
200 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
201) -> Result<Mailbox> {
202 let mut mailbox = Mailbox::default();
203
204 loop {
205 match imap_proto::parse_response(lines) {
206 Ok((rest, Response::Data { status, code, .. })) => {
207 lines = rest;
208
209 if let imap_proto::Status::Ok = status {
210 } else {
211 unreachable!();
213 }
214
215 use imap_proto::ResponseCode;
216 match code {
217 Some(ResponseCode::UidValidity(uid)) => {
218 mailbox.uid_validity = Some(uid);
219 }
220 Some(ResponseCode::UidNext(unext)) => {
221 mailbox.uid_next = Some(unext);
222 }
223 Some(ResponseCode::Unseen(n)) => {
224 mailbox.unseen = Some(n);
225 }
226 Some(ResponseCode::PermanentFlags(flags)) => {
227 mailbox
228 .permanent_flags
229 .extend(flags.into_iter().map(String::from).map(Flag::from));
230 }
231 _ => {}
232 }
233 }
234 Ok((rest, Response::MailboxData(m))) => {
235 lines = rest;
236
237 match m {
238 MailboxDatum::Status { mailbox, status } => {
239 unsolicited
240 .send(UnsolicitedResponse::Status {
241 mailbox: mailbox.into(),
242 attributes: status,
243 })
244 .unwrap();
245 }
246 MailboxDatum::Exists(e) => {
247 mailbox.exists = e;
248 }
249 MailboxDatum::Recent(r) => {
250 mailbox.recent = r;
251 }
252 MailboxDatum::Flags(flags) => {
253 mailbox
254 .flags
255 .extend(flags.into_iter().map(String::from).map(Flag::from));
256 }
257 MailboxDatum::List { .. }
258 | MailboxDatum::MetadataSolicited { .. }
259 | MailboxDatum::MetadataUnsolicited { .. } => {}
260 }
261 }
262 Ok((rest, Response::Expunge(n))) => {
263 lines = rest;
264 unsolicited.send(UnsolicitedResponse::Expunge(n)).unwrap();
265 }
266 Ok((_, resp)) => {
267 break Err(resp.into());
268 }
269 _ => {
270 break Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
271 }
272 }
273
274 if lines.is_empty() {
275 break Ok(mailbox);
276 }
277 }
278}
279
280pub fn parse_ids(
281 lines: &[u8],
282 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
283) -> Result<HashSet<u32>> {
284 let mut lines = &lines[..];
285 let mut ids = HashSet::new();
286 loop {
287 if lines.is_empty() {
288 break Ok(ids);
289 }
290
291 match imap_proto::parse_response(lines) {
292 Ok((rest, Response::IDs(c))) => {
293 lines = rest;
294 ids.extend(c);
295 }
296 Ok((rest, data)) => {
297 lines = rest;
298 if let Some(resp) = handle_unilateral(data, unsolicited) {
299 break Err(resp.into());
300 }
301 }
302 _ => {
303 break Err(Error::Parse(ParseError::Invalid(lines.to_vec())));
304 }
305 }
306 }
307}
308
309fn handle_unilateral<'a>(
312 res: Response<'a>,
313 unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
314) -> Option<Response<'a>> {
315 match res {
316 Response::MailboxData(MailboxDatum::Status { mailbox, status }) => {
317 unsolicited
318 .send(UnsolicitedResponse::Status {
319 mailbox: mailbox.into(),
320 attributes: status,
321 })
322 .unwrap();
323 }
324 Response::MailboxData(MailboxDatum::Recent(n)) => {
325 unsolicited.send(UnsolicitedResponse::Recent(n)).unwrap();
326 }
327 Response::MailboxData(MailboxDatum::Flags(_)) => {
328 }
330 Response::MailboxData(MailboxDatum::Exists(n)) => {
331 unsolicited.send(UnsolicitedResponse::Exists(n)).unwrap();
332 }
333 Response::Expunge(n) => {
334 unsolicited.send(UnsolicitedResponse::Expunge(n)).unwrap();
335 }
336 res => {
337 return Some(res);
338 }
339 }
340 None
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn parse_capability_test() {
349 let expected_capabilities = vec!["IMAP4rev1", "STARTTLS", "AUTH=GSSAPI", "LOGINDISABLED"];
350 let lines = b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n";
351 let (mut send, recv) = mpsc::channel();
352 let capabilities = parse_capabilities(lines.to_vec(), &mut send).unwrap();
353 assert!(recv.try_recv().is_err());
355 assert_eq!(capabilities.len(), 4);
356 for e in expected_capabilities {
357 assert!(capabilities.has_str(e));
358 }
359 }
360
361 #[test]
362 fn parse_capability_case_insensitive_test() {
363 let expected_capabilities = vec!["IMAP4rev1", "STARTTLS"];
365 let lines = b"* CAPABILITY IMAP4REV1 STARTTLS\r\n";
366 let (mut send, recv) = mpsc::channel();
367 let capabilities = parse_capabilities(lines.to_vec(), &mut send).unwrap();
368 assert!(recv.try_recv().is_err());
370 assert_eq!(capabilities.len(), 2);
371 for e in expected_capabilities {
372 assert!(capabilities.has_str(e));
373 }
374 }
375
376 #[test]
377 #[should_panic]
378 fn parse_capability_invalid_test() {
379 let (mut send, recv) = mpsc::channel();
380 let lines = b"* JUNK IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n";
381 parse_capabilities(lines.to_vec(), &mut send).unwrap();
382 assert!(recv.try_recv().is_err());
383 }
384
385 #[test]
386 fn parse_names_test() {
387 let lines = b"* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n";
388 let (mut send, recv) = mpsc::channel();
389 let names = parse_names(lines.to_vec(), &mut send).unwrap();
390 assert!(recv.try_recv().is_err());
391 assert_eq!(names.len(), 1);
392 assert_eq!(
393 names[0].attributes(),
394 &[NameAttribute::from("\\HasNoChildren")]
395 );
396 assert_eq!(names[0].delimiter(), Some("."));
397 assert_eq!(names[0].name(), "INBOX");
398 }
399
400 #[test]
401 fn parse_fetches_empty() {
402 let lines = b"";
403 let (mut send, recv) = mpsc::channel();
404 let fetches = parse_fetches(lines.to_vec(), &mut send).unwrap();
405 assert!(recv.try_recv().is_err());
406 assert!(fetches.is_empty());
407 }
408
409 #[test]
410 fn parse_fetches_test() {
411 let lines = b"\
412 * 24 FETCH (FLAGS (\\Seen) UID 4827943)\r\n\
413 * 25 FETCH (FLAGS (\\Seen))\r\n";
414 let (mut send, recv) = mpsc::channel();
415 let fetches = parse_fetches(lines.to_vec(), &mut send).unwrap();
416 assert!(recv.try_recv().is_err());
417 assert_eq!(fetches.len(), 2);
418 assert_eq!(fetches[0].message, 24);
419 assert_eq!(fetches[0].flags(), &[Flag::Seen]);
420 assert_eq!(fetches[0].uid, Some(4827943));
421 assert_eq!(fetches[0].body(), None);
422 assert_eq!(fetches[0].header(), None);
423 assert_eq!(fetches[1].message, 25);
424 assert_eq!(fetches[1].flags(), &[Flag::Seen]);
425 assert_eq!(fetches[1].uid, None);
426 assert_eq!(fetches[1].body(), None);
427 assert_eq!(fetches[1].header(), None);
428 }
429
430 #[test]
431 fn parse_fetches_w_unilateral() {
432 let lines = b"\
434 * 37 FETCH (UID 74)\r\n\
435 * 1 RECENT\r\n";
436 let (mut send, recv) = mpsc::channel();
437 let fetches = parse_fetches(lines.to_vec(), &mut send).unwrap();
438 assert_eq!(recv.try_recv(), Ok(UnsolicitedResponse::Recent(1)));
439 assert_eq!(fetches.len(), 1);
440 assert_eq!(fetches[0].message, 37);
441 assert_eq!(fetches[0].uid, Some(74));
442 }
443
444 #[test]
445 fn parse_names_w_unilateral() {
446 let lines = b"\
447 * LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n\
448 * 4 EXPUNGE\r\n";
449 let (mut send, recv) = mpsc::channel();
450 let names = parse_names(lines.to_vec(), &mut send).unwrap();
451
452 assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Expunge(4));
453
454 assert_eq!(names.len(), 1);
455 assert_eq!(
456 names[0].attributes(),
457 &[NameAttribute::from("\\HasNoChildren")]
458 );
459 assert_eq!(names[0].delimiter(), Some("."));
460 assert_eq!(names[0].name(), "INBOX");
461 }
462
463 #[test]
464 fn parse_capabilities_w_unilateral() {
465 let expected_capabilities = vec!["IMAP4rev1", "STARTTLS", "AUTH=GSSAPI", "LOGINDISABLED"];
466 let lines = b"\
467 * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n\
468 * STATUS dev.github (MESSAGES 10 UIDNEXT 11 UIDVALIDITY 1408806928 UNSEEN 0)\r\n\
469 * 4 EXISTS\r\n";
470 let (mut send, recv) = mpsc::channel();
471 let capabilities = parse_capabilities(lines.to_vec(), &mut send).unwrap();
472
473 assert_eq!(capabilities.len(), 4);
474 for e in expected_capabilities {
475 assert!(capabilities.has_str(e));
476 }
477
478 assert_eq!(
479 recv.try_recv().unwrap(),
480 UnsolicitedResponse::Status {
481 mailbox: "dev.github".to_string(),
482 attributes: vec![
483 StatusAttribute::Messages(10),
484 StatusAttribute::UidNext(11),
485 StatusAttribute::UidValidity(1408806928),
486 StatusAttribute::Unseen(0)
487 ]
488 }
489 );
490 assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Exists(4));
491 }
492
493 #[test]
494 fn parse_ids_w_unilateral() {
495 let lines = b"\
496 * SEARCH 23 42 4711\r\n\
497 * 1 RECENT\r\n\
498 * STATUS INBOX (MESSAGES 10 UIDNEXT 11 UIDVALIDITY 1408806928 UNSEEN 0)\r\n";
499 let (mut send, recv) = mpsc::channel();
500 let ids = parse_ids(lines, &mut send).unwrap();
501
502 assert_eq!(ids, [23, 42, 4711].iter().cloned().collect());
503
504 assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Recent(1));
505 assert_eq!(
506 recv.try_recv().unwrap(),
507 UnsolicitedResponse::Status {
508 mailbox: "INBOX".to_string(),
509 attributes: vec![
510 StatusAttribute::Messages(10),
511 StatusAttribute::UidNext(11),
512 StatusAttribute::UidValidity(1408806928),
513 StatusAttribute::Unseen(0)
514 ]
515 }
516 );
517 }
518
519 #[test]
520 fn parse_ids_test() {
521 let lines = b"* SEARCH 1600 1698 1739 1781 1795 1885 1891 1892 1893 1898 1899 1901 1911 1926 1932 1933 1993 1994 2007 2032 2033 2041 2053 2062 2063 2065 2066 2072 2078 2079 2082 2084 2095 2100 2101 2102 2103 2104 2107 2116 2120 2135 2138 2154 2163 2168 2172 2189 2193 2198 2199 2205 2212 2213 2221 2227 2267 2275 2276 2295 2300 2328 2330 2332 2333 2334\r\n\
522 * SEARCH 2335 2336 2337 2338 2339 2341 2342 2347 2349 2350 2358 2359 2362 2369 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2390 2392 2397 2400 2401 2403 2405 2409 2411 2414 2417 2419 2420 2424 2426 2428 2439 2454 2456 2467 2468 2469 2490 2515 2519 2520 2521\r\n";
523 let (mut send, recv) = mpsc::channel();
524 let ids = parse_ids(lines, &mut send).unwrap();
525 assert!(recv.try_recv().is_err());
526 let ids: HashSet<u32> = ids.iter().cloned().collect();
527 assert_eq!(
528 ids,
529 [
530 1600, 1698, 1739, 1781, 1795, 1885, 1891, 1892, 1893, 1898, 1899, 1901, 1911, 1926,
531 1932, 1933, 1993, 1994, 2007, 2032, 2033, 2041, 2053, 2062, 2063, 2065, 2066, 2072,
532 2078, 2079, 2082, 2084, 2095, 2100, 2101, 2102, 2103, 2104, 2107, 2116, 2120, 2135,
533 2138, 2154, 2163, 2168, 2172, 2189, 2193, 2198, 2199, 2205, 2212, 2213, 2221, 2227,
534 2267, 2275, 2276, 2295, 2300, 2328, 2330, 2332, 2333, 2334, 2335, 2336, 2337, 2338,
535 2339, 2341, 2342, 2347, 2349, 2350, 2358, 2359, 2362, 2369, 2371, 2372, 2373, 2374,
536 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2390, 2392,
537 2397, 2400, 2401, 2403, 2405, 2409, 2411, 2414, 2417, 2419, 2420, 2424, 2426, 2428,
538 2439, 2454, 2456, 2467, 2468, 2469, 2490, 2515, 2519, 2520, 2521
539 ]
540 .iter()
541 .cloned()
542 .collect()
543 );
544
545 let lines = b"* SEARCH\r\n";
546 let (mut send, recv) = mpsc::channel();
547 let ids = parse_ids(lines, &mut send).unwrap();
548 assert!(recv.try_recv().is_err());
549 let ids: HashSet<u32> = ids.iter().cloned().collect();
550 assert_eq!(ids, HashSet::<u32>::new());
551 }
552}