Files
lettre/src/extension.rs
Alexis Mousset 2fa499629d Email builder
2015-03-04 11:04:07 +01:00

107 lines
4.0 KiB
Rust

// Copyright 2014 Alexis Mousset. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ESMTP features
use std::str::FromStr;
use std::result::Result;
use tools::CRLF;
use response::Response;
use self::Extension::{PlainAuthentication, CramMd5Authentication, EightBitMime, SmtpUtfEight, StartTls};
/// Supported ESMTP keywords
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum Extension {
/// 8BITMIME keyword
///
/// RFC 6152: https://tools.ietf.org/html/rfc6152
EightBitMime,
/// SMTPUTF8 keyword
///
/// RFC 6531: https://tools.ietf.org/html/rfc6531
SmtpUtfEight,
/// STARTTLS keyword
///
/// RFC 2487: https://tools.ietf.org/html/rfc2487
StartTls,
/// AUTH PLAIN mecanism
///
/// RFC 4616: https://tools.ietf.org/html/rfc4616
PlainAuthentication,
/// AUTH CRAM-MD5 mecanism
///
/// RFC 2195: https://tools.ietf.org/html/rfc2195
CramMd5Authentication,
}
impl Extension {
fn from_str(s: &str) -> Result<Vec<Extension>, &'static str> {
let splitted : Vec<&str> = s.split(' ').collect();
match (splitted[0], splitted.len()) {
("8BITMIME", 1) => Ok(vec![EightBitMime]),
("SMTPUTF8", 1) => Ok(vec![SmtpUtfEight]),
("STARTTLS", 1) => Ok(vec![StartTls]),
("AUTH", _) => {
let mut mecanisms: Vec<Extension> = vec![];
for &mecanism in &splitted[1..] {
match mecanism {
"PLAIN" => mecanisms.push(PlainAuthentication),
"CRAM-MD5" => mecanisms.push(CramMd5Authentication),
_ => (),
}
}
Ok(mecanisms)
},
_ => Err("Unknown extension"),
}
}
/// Parses supported ESMTP features
pub fn parse_esmtp_response(message: &str) -> Vec<Extension> {
let mut esmtp_features: Vec<Extension> = Vec::new();
for line in message.split(CRLF) {
if let Ok(Response{code: 250, message}) = line.parse::<Response>() {
if let Ok(keywords) = Extension::from_str(message.unwrap().as_slice()) {
esmtp_features.push_all(&keywords);
};
}
}
esmtp_features
}
}
#[cfg(test)]
mod test {
use super::Extension;
#[test]
fn test_from_str() {
assert_eq!(Extension::from_str("8BITMIME"), Ok(vec![Extension::EightBitMime]));
assert_eq!(Extension::from_str("AUTH PLAIN"), Ok(vec![Extension::PlainAuthentication]));
assert_eq!(Extension::from_str("AUTH PLAIN LOGIN CRAM-MD5"), Ok(vec![Extension::PlainAuthentication, Extension::CramMd5Authentication]));
assert_eq!(Extension::from_str("AUTH CRAM-MD5 PLAIN"), Ok(vec![Extension::CramMd5Authentication, Extension::PlainAuthentication]));
assert_eq!(Extension::from_str("AUTH DIGEST-MD5 PLAIN CRAM-MD5"), Ok(vec![Extension::PlainAuthentication, Extension::CramMd5Authentication]));
}
#[test]
fn test_parse_esmtp_response() {
assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 SIZE 42"),
vec![Extension::EightBitMime]);
assert_eq!(Extension::parse_esmtp_response("me\r\n250-8BITMIME\r\n250 AUTH PLAIN CRAM-MD5\r\n250 UNKNON 42"),
vec![Extension::EightBitMime, Extension::PlainAuthentication, Extension::CramMd5Authentication]);
assert_eq!(Extension::parse_esmtp_response("me\r\n250-9BITMIME\r\n250 SIZE a"),
vec![]);
assert_eq!(Extension::parse_esmtp_response("me\r\n250-SIZE 42\r\n250 SIZE 43"),
vec![]);
assert_eq!(Extension::parse_esmtp_response(""),
vec![]);
}
}