Merge pull request #56 from rhn/quote-password

Escape password and other astring tokens
This commit is contained in:
Jon Gjengset 2017-11-02 16:09:53 -04:00 committed by GitHub
commit d9caeccc57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -15,6 +15,12 @@ const INITIAL_TAG: u32 = 0;
const CR: u8 = 0x0d; const CR: u8 = 0x0d;
const LF: u8 = 0x0a; const LF: u8 = 0x0a;
macro_rules! quote {
($x: expr) => (
format!("\"{}\"", $x.replace(r"\", r"\\").replace("\"", "\\\""))
)
}
/// Stream to interface with the IMAP server. This interface is only for the command stream. /// Stream to interface with the IMAP server. This interface is only for the command stream.
#[derive(Debug)] #[derive(Debug)]
pub struct Client<T: Read + Write> { pub struct Client<T: Read + Write> {
@ -278,18 +284,18 @@ impl<T: Read + Write> Client<T> {
/// Log in to the IMAP server. /// Log in to the IMAP server.
pub fn login(&mut self, username: &str, password: &str) -> Result<()> { pub fn login(&mut self, username: &str, password: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password)) self.run_command_and_check_ok(&format!("LOGIN {} {}", quote!(username), quote!(password)))
} }
/// Selects a mailbox /// Selects a mailbox
pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> { pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!(self.run_command_and_read_response(&format!("SELECT {}", mailbox_name))); let lines = try!(self.run_command_and_read_response(&format!("SELECT {}", quote!(mailbox_name))));
parse_select_or_examine(lines) parse_select_or_examine(lines)
} }
/// Examine is identical to Select, but the selected mailbox is identified as read-only /// Examine is identical to Select, but the selected mailbox is identified as read-only
pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> { pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> {
let lines = try!(self.run_command_and_read_response(&format!("EXAMINE {}", mailbox_name))); let lines = try!(self.run_command_and_read_response(&format!("EXAMINE {}", quote!(mailbox_name))));
parse_select_or_examine(lines) parse_select_or_examine(lines)
} }
@ -314,33 +320,33 @@ impl<T: Read + Write> Client<T> {
/// Create creates a mailbox with the given name. /// Create creates a mailbox with the given name.
pub fn create(&mut self, mailbox_name: &str) -> Result<()> { pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name)) self.run_command_and_check_ok(&format!("CREATE {}", quote!(mailbox_name)))
} }
/// Delete permanently removes the mailbox with the given name. /// Delete permanently removes the mailbox with the given name.
pub fn delete(&mut self, mailbox_name: &str) -> Result<()> { pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name)) self.run_command_and_check_ok(&format!("DELETE {}", quote!(mailbox_name)))
} }
/// Rename changes the name of a mailbox. /// Rename changes the name of a mailbox.
pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> { pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!( self.run_command_and_check_ok(&format!(
"RENAME {} {}", "RENAME {} {}",
current_mailbox_name, quote!(current_mailbox_name),
new_mailbox_name quote!(new_mailbox_name)
)) ))
} }
/// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed" /// Subscribe adds the specified mailbox name to the server's set of "active" or "subscribed"
/// mailboxes as returned by the LSUB command. /// mailboxes as returned by the LSUB command.
pub fn subscribe(&mut self, mailbox: &str) -> Result<()> { pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox)) self.run_command_and_check_ok(&format!("SUBSCRIBE {}", quote!(mailbox)))
} }
/// Unsubscribe removes the specified mailbox name from the server's set of /// Unsubscribe removes the specified mailbox name from the server's set of
/// "active" or "subscribed mailboxes as returned by the LSUB command. /// "active" or "subscribed mailboxes as returned by the LSUB command.
pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> { pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox)) self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", quote!(mailbox)))
} }
/// Capability requests a listing of capabilities that the server supports. /// Capability requests a listing of capabilities that the server supports.
@ -393,7 +399,7 @@ impl<T: Read + Write> Client<T> {
) -> Result<Vec<String>> { ) -> Result<Vec<String>> {
self.run_command_and_parse(&format!( self.run_command_and_parse(&format!(
"LIST {} {}", "LIST {} {}",
reference_name, quote!(reference_name),
mailbox_search_pattern mailbox_search_pattern
)) ))
} }
@ -407,7 +413,7 @@ impl<T: Read + Write> Client<T> {
) -> Result<Vec<String>> { ) -> Result<Vec<String>> {
self.run_command_and_parse(&format!( self.run_command_and_parse(&format!(
"LSUB {} {}", "LSUB {} {}",
reference_name, quote!(reference_name),
mailbox_search_pattern mailbox_search_pattern
)) ))
} }
@ -601,7 +607,7 @@ mod tests {
let response = b"a1 OK Logged in\r\n".to_vec(); let response = b"a1 OK Logged in\r\n".to_vec();
let username = "username"; let username = "username";
let password = "password"; let password = "password";
let command = format!("a1 LOGIN {} {}\r\n", username, password); let command = format!("a1 LOGIN {} {}\r\n", quote!(username), quote!(password));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.login(username, password).unwrap(); client.login(username, password).unwrap();
@ -631,8 +637,8 @@ mod tests {
let new_mailbox_name = "NEWINBOX"; let new_mailbox_name = "NEWINBOX";
let command = format!( let command = format!(
"a1 RENAME {} {}\r\n", "a1 RENAME {} {}\r\n",
current_mailbox_name, quote!(current_mailbox_name),
new_mailbox_name quote!(new_mailbox_name)
); );
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
@ -649,7 +655,7 @@ mod tests {
fn subscribe() { fn subscribe() {
let response = b"a1 OK SUBSCRIBE completed\r\n".to_vec(); let response = b"a1 OK SUBSCRIBE completed\r\n".to_vec();
let mailbox = "INBOX"; let mailbox = "INBOX";
let command = format!("a1 SUBSCRIBE {}\r\n", mailbox); let command = format!("a1 SUBSCRIBE {}\r\n", quote!(mailbox));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.subscribe(mailbox).unwrap(); client.subscribe(mailbox).unwrap();
@ -663,7 +669,7 @@ mod tests {
fn unsubscribe() { fn unsubscribe() {
let response = b"a1 OK UNSUBSCRIBE completed\r\n".to_vec(); let response = b"a1 OK UNSUBSCRIBE completed\r\n".to_vec();
let mailbox = "INBOX"; let mailbox = "INBOX";
let command = format!("a1 UNSUBSCRIBE {}\r\n", mailbox); let command = format!("a1 UNSUBSCRIBE {}\r\n", quote!(mailbox));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.unsubscribe(mailbox).unwrap(); client.unsubscribe(mailbox).unwrap();
@ -718,7 +724,7 @@ mod tests {
uid_validity: Some(1257842737), uid_validity: Some(1257842737),
}; };
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 EXAMINE {}\r\n", mailbox_name); let command = format!("a1 EXAMINE {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let mailbox = client.examine(mailbox_name).unwrap(); let mailbox = client.examine(mailbox_name).unwrap();
@ -752,7 +758,7 @@ mod tests {
uid_validity: Some(1257842737), uid_validity: Some(1257842737),
}; };
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 SELECT {}\r\n", mailbox_name); let command = format!("a1 SELECT {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
let mailbox = client.select(mailbox_name).unwrap(); let mailbox = client.select(mailbox_name).unwrap();
@ -786,7 +792,7 @@ mod tests {
fn create() { fn create() {
let response = b"a1 OK CREATE completed\r\n".to_vec(); let response = b"a1 OK CREATE completed\r\n".to_vec();
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 CREATE {}\r\n", mailbox_name); let command = format!("a1 CREATE {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.create(mailbox_name).unwrap(); client.create(mailbox_name).unwrap();
@ -800,7 +806,7 @@ mod tests {
fn delete() { fn delete() {
let response = b"a1 OK DELETE completed\r\n".to_vec(); let response = b"a1 OK DELETE completed\r\n".to_vec();
let mailbox_name = "INBOX"; let mailbox_name = "INBOX";
let command = format!("a1 DELETE {}\r\n", mailbox_name); let command = format!("a1 DELETE {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut client = Client::new(mock_stream); let mut client = Client::new(mock_stream);
client.delete(mailbox_name).unwrap(); client.delete(mailbox_name).unwrap();
@ -910,4 +916,14 @@ mod tests {
"Invalid command" "Invalid command"
); );
} }
#[test]
fn quote_backslash() {
assert_eq!("\"test\\\\text\"", quote!(r"test\text"));
}
#[test]
fn quote_dquote() {
assert_eq!("\"test\\\"text\"", quote!("test\"text"));
}
} }