Initial testing for authentication
This commit is contained in:
parent
8653a02b87
commit
8a1162ada4
5 changed files with 83 additions and 24 deletions
|
|
@ -18,3 +18,6 @@ path = "src/lib.rs"
|
|||
[dependencies]
|
||||
openssl = "0.7.13"
|
||||
regex = "0.1.71"
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.2.0"
|
||||
|
|
|
|||
47
examples/gmail_oauth2.rs
Normal file
47
examples/gmail_oauth2.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
extern crate imap;
|
||||
extern crate openssl;
|
||||
extern crate base64;
|
||||
|
||||
use openssl::ssl::{SslContext, SslMethod};
|
||||
use base64::{encode, decode};
|
||||
use imap::client::Client;
|
||||
use imap::authenticator::Authenticator;
|
||||
|
||||
struct GmailOAuth2 {
|
||||
user: String,
|
||||
access_token: String
|
||||
}
|
||||
|
||||
impl Authenticator for GmailOAuth2 {
|
||||
fn process(&self, data: String) -> String {
|
||||
String::from("dXNlcj1tYXR0bWNjb3kxMTBAZ21haWwuY29tAWF1dGg9QmVhcmVyIHlhMjkuQ2k4UUEzQ1Y5SW1OQ0Z1NDNpbkZRcngtSUR0cjVFSkZHNXdEM1IySzBXdTdiM1dzVG1Md")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut gmail_auth = GmailOAuth2{
|
||||
user: String::from("email@gmail.com"),
|
||||
access_token: String::from("")
|
||||
};
|
||||
let mut imap_socket = Client::secure_connect(("imap.gmail.com", 993), SslContext::new(SslMethod::Sslv23).unwrap()).unwrap();
|
||||
|
||||
imap_socket.authenticate("XOAUTH2", gmail_auth).unwrap();
|
||||
|
||||
match imap_socket.select("INBOX") {
|
||||
Ok(mailbox) => {
|
||||
println!("{}", mailbox);
|
||||
},
|
||||
Err(e) => println!("Error selecting INBOX: {}", e)
|
||||
};
|
||||
|
||||
match imap_socket.fetch("2", "body[text]") {
|
||||
Ok(lines) => {
|
||||
for line in lines.iter() {
|
||||
print!("{}", line);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error Fetching email 2: {}", e)
|
||||
};
|
||||
|
||||
imap_socket.logout().unwrap();
|
||||
}
|
||||
|
|
@ -61,14 +61,20 @@ impl<T: Read+Write> Client<T> {
|
|||
}
|
||||
|
||||
pub fn authenticate<A: Authenticator>(&mut self, auth_type: &str, authenticator: A) -> Result<()> {
|
||||
match self.run_command(&format!("AUTHENTICATE {}\r\n", auth_type).to_string()) {
|
||||
Ok(lines) => {
|
||||
match self.run_command(&format!("AUTHENTICATE {}", auth_type).to_string()) {
|
||||
Ok(_) => {
|
||||
let line = match self.readline() {
|
||||
Ok(l) => l,
|
||||
Err(e) => return Err(e)
|
||||
};
|
||||
// TODO test this for the many authentication use cases
|
||||
let data = match parse_authenticate_response(lines) {
|
||||
let data = match parse_authenticate_response(String::from_utf8(line).unwrap()) {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Err(e)
|
||||
};
|
||||
println!("Done parsing authenticating response");
|
||||
let auth_response = authenticator.process(data);
|
||||
println!("Writing: {}", auth_response.clone());
|
||||
match self.stream.write_all(auth_response.into_bytes().as_slice()) {
|
||||
Err(e) => return Err(Error::Io(e)),
|
||||
_ => {}
|
||||
|
|
@ -79,7 +85,7 @@ impl<T: Read+Write> Client<T> {
|
|||
};
|
||||
match self.read_response() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => return Err(e)
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
|
|
@ -93,7 +99,7 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
/// Selects a mailbox
|
||||
pub fn select(&mut self, mailbox_name: &str) -> Result<Mailbox> {
|
||||
match self.run_command(&format!("SELECT {}", mailbox_name).to_string()) {
|
||||
match self.run_command_and_read_response(&format!("SELECT {}", mailbox_name).to_string()) {
|
||||
Ok(lines) => parse_select_or_examine(lines),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
|
|
@ -101,7 +107,7 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
/// Examine is identical to Select, but the selected mailbox is identified as read-only
|
||||
pub fn examine(&mut self, mailbox_name: &str) -> Result<Mailbox> {
|
||||
match self.run_command(&format!("EXAMINE {}", mailbox_name).to_string()) {
|
||||
match self.run_command_and_read_response(&format!("EXAMINE {}", mailbox_name).to_string()) {
|
||||
Ok(lines) => parse_select_or_examine(lines),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
|
|
@ -109,7 +115,7 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
/// Fetch retreives data associated with a message in the mailbox.
|
||||
pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
|
||||
self.run_command(&format!("FETCH {} {}", sequence_set, query).to_string())
|
||||
self.run_command_and_read_response(&format!("FETCH {} {}", sequence_set, query).to_string())
|
||||
}
|
||||
|
||||
/// Noop always succeeds, and it does nothing.
|
||||
|
|
@ -151,7 +157,7 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
/// Capability requests a listing of capabilities that the server supports.
|
||||
pub fn capability(&mut self) -> Result<Vec<String>> {
|
||||
match self.run_command(&format!("CAPABILITY").to_string()) {
|
||||
match self.run_command_and_read_response(&format!("CAPABILITY").to_string()) {
|
||||
Ok(lines) => parse_capability(lines),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
|
|
@ -198,7 +204,7 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
/// Runs a command and checks if it returns OK.
|
||||
pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
|
||||
match self.run_command(command) {
|
||||
match self.run_command_and_read_response(command) {
|
||||
Ok(lines) => parse_response_ok(lines),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
|
|
@ -206,22 +212,27 @@ impl<T: Read+Write> Client<T> {
|
|||
|
||||
// Run a command and parse the status response.
|
||||
pub fn run_command_and_parse(&mut self, command: &str) -> Result<Vec<String>> {
|
||||
match self.run_command(command) {
|
||||
match self.run_command_and_read_response(command) {
|
||||
Ok(lines) => parse_response(lines),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs any command passed to it.
|
||||
pub fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
|
||||
pub fn run_command(&mut self, untagged_command: &str) -> Result<()> {
|
||||
let command = self.create_command(untagged_command.to_string());
|
||||
|
||||
match self.stream.write_fmt(format_args!("{}", &*command)) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return Err(Error::Io(io::Error::new(io::ErrorKind::Other, "Failed to write"))),
|
||||
};
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(Error::Io(io::Error::new(io::ErrorKind::Other, "Failed to write"))),
|
||||
}
|
||||
}
|
||||
|
||||
self.read_response()
|
||||
pub fn run_command_and_read_response(&mut self, untagged_command: &str) -> Result<Vec<String>> {
|
||||
match self.run_command(untagged_command) {
|
||||
Ok(_) => self.read_response(),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_response(&mut self) -> Result<Vec<String>> {
|
||||
|
|
@ -265,8 +276,10 @@ impl<T: Read+Write> Client<T> {
|
|||
Ok(_) => {},
|
||||
Err(_) => return Err(Error::Io(io::Error::new(io::ErrorKind::Other, "Failed to read line"))),
|
||||
}
|
||||
print!("{}", String::from_utf8_lossy(byte_buffer));
|
||||
line_buffer.push(byte_buffer[0]);
|
||||
}
|
||||
println!("{}", String::from_utf8(line_buffer.clone()).unwrap());
|
||||
Ok(line_buffer)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub enum ParseError {
|
|||
// Error parsing the cabability response.
|
||||
Capability(Vec<String>),
|
||||
// Authentication errors.
|
||||
Authentication(Vec<String>)
|
||||
Authentication(String)
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
|
|
|
|||
10
src/parse.rs
10
src/parse.rs
|
|
@ -3,19 +3,15 @@ use regex::Regex;
|
|||
use super::mailbox::Mailbox;
|
||||
use super::error::{Error, ParseError, Result};
|
||||
|
||||
pub fn parse_authenticate_response(lines: Vec<String>) -> Result<String> {
|
||||
pub fn parse_authenticate_response(line: String) -> Result<String> {
|
||||
let authenticate_regex = Regex::new("^+(.*)\r\n").unwrap();
|
||||
let last_line = match lines.last() {
|
||||
Some(l) => l,
|
||||
None => return Err(Error::Parse(ParseError::Authentication(lines.clone())))
|
||||
};
|
||||
|
||||
for cap in authenticate_regex.captures_iter(last_line) {
|
||||
for cap in authenticate_regex.captures_iter(line.as_str()) {
|
||||
let data = cap.at(1).unwrap_or("");
|
||||
return Ok(String::from(data));
|
||||
}
|
||||
|
||||
Err(Error::Parse(ParseError::Authentication(lines.clone())))
|
||||
Err(Error::Parse(ParseError::Authentication(line)))
|
||||
}
|
||||
|
||||
pub fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue