Initial Commit
This commit is contained in:
commit
95e6676055
8 changed files with 500 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
1
.travis.yml
Normal file
1
.travis.yml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
language: rust
|
||||||
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
|
||||||
|
name = "imap"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Matt McCoy <mattnenterprise@yahoo.com>"]
|
||||||
|
repository = "https://github.com/mattnenterprise/rust-imap"
|
||||||
|
description = "IMAP client for Rust"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
openssl = "*"
|
||||||
|
regex = "*"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "example"
|
||||||
|
path = "example.rs"
|
||||||
20
LICENSE
Normal file
20
LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2015 Matt McCoy
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
71
README.md
Normal file
71
README.md
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
rust-imap
|
||||||
|
================
|
||||||
|
IMAP Client for Rust
|
||||||
|
|
||||||
|
This client has SSL support. SSL is configured using an SSLContext that is passed into the connect method of a IMAPStream. If no SSL
|
||||||
|
support is wanted just pass in None. The library rust-openssl is used to support SSL for this project.
|
||||||
|
|
||||||
|
|
||||||
|
[](https://travis-ci.org/mattnenterprise/rust-imap)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Add imap via your `Cargo.toml`:
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
imap = "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
```rs
|
||||||
|
extern crate imap;
|
||||||
|
extern crate openssl;
|
||||||
|
|
||||||
|
use openssl::ssl::{SslContext, SslMethod};
|
||||||
|
use imap::client::IMAPStream;
|
||||||
|
use imap::client::IMAPMailbox;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut imap_socket = match IMAPStream::connect("imap.gmail.com", 993, Some(SslContext::new(SslMethod::Sslv23).unwrap())) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => panic!("{}", e)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = imap_socket.login("username", "password") {
|
||||||
|
println!("Error: {}", e)
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.capability() {
|
||||||
|
Ok(capabilities) => {
|
||||||
|
for capability in capabilities.iter() {
|
||||||
|
println!("{}", capability);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error retreiving capabilities")
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.select("INBOX") {
|
||||||
|
Ok(IMAPMailbox{flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity}) => {
|
||||||
|
println!("flags: {}, exists: {}, recent: {}, unseen: {:?}, parmanent_flags: {:?}, uid_next: {:?}, uid_validity: {:?}", flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity);
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error selecting INBOX")
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.fetch("2", "body[text]") {
|
||||||
|
Ok(lines) => {
|
||||||
|
for line in lines.iter() {
|
||||||
|
print!("{}", line);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error Fetching email 2")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = imap_socket.logout() {
|
||||||
|
println!("Error: {}", e)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
MIT
|
||||||
46
example.rs
Normal file
46
example.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
extern crate imap;
|
||||||
|
extern crate openssl;
|
||||||
|
|
||||||
|
use openssl::ssl::{SslContext, SslMethod};
|
||||||
|
use imap::client::IMAPStream;
|
||||||
|
use imap::client::IMAPMailbox;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut imap_socket = match IMAPStream::connect("imap.gmail.com", 993, Some(SslContext::new(SslMethod::Sslv23).unwrap())) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => panic!("{}", e)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = imap_socket.login("username", "password") {
|
||||||
|
println!("Error: {}", e)
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.capability() {
|
||||||
|
Ok(capabilities) => {
|
||||||
|
for capability in capabilities.iter() {
|
||||||
|
//println!("{}", capability);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error retreiving capabilities")
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.select("INBOX") {
|
||||||
|
Ok(IMAPMailbox{flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity}) => {
|
||||||
|
println!("flags: {}, exists: {}, recent: {}, unseen: {:?}, parmanent_flags: {:?}, uid_next: {:?}, uid_validity: {:?}", flags, exists, recent, unseen, permanent_flags, uid_next, uid_validity);
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error selecting INBOX")
|
||||||
|
};
|
||||||
|
|
||||||
|
match imap_socket.fetch("2", "body[text]") {
|
||||||
|
Ok(lines) => {
|
||||||
|
for line in lines.iter() {
|
||||||
|
print!("{}", line);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => println!("Error Fetching email 2")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = imap_socket.logout() {
|
||||||
|
println!("Error: {}", e)
|
||||||
|
};
|
||||||
|
}
|
||||||
335
src/client.rs
Normal file
335
src/client.rs
Normal file
|
|
@ -0,0 +1,335 @@
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use openssl::ssl::{SslContext, SslStream};
|
||||||
|
use std::io::{Error, ErrorKind, Read, Result, Write};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
enum IMAPStreamTypes {
|
||||||
|
Basic(TcpStream),
|
||||||
|
Ssl(SslStream<TcpStream>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IMAPStream {
|
||||||
|
stream: IMAPStreamTypes,
|
||||||
|
pub host: &'static str,
|
||||||
|
pub port: u16,
|
||||||
|
tag: u32,
|
||||||
|
tag_prefix: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IMAPMailbox {
|
||||||
|
pub flags: String,
|
||||||
|
pub exists: u32,
|
||||||
|
pub recent: u32,
|
||||||
|
pub unseen: Option<u32>,
|
||||||
|
pub permanent_flags: Option<String>,
|
||||||
|
pub uid_next: Option<u32>,
|
||||||
|
pub uid_validity: Option<u32>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IMAPStream {
|
||||||
|
|
||||||
|
pub fn connect(host: &'static str, port: u16, ssl_context: Option<SslContext>) -> Result<IMAPStream> {
|
||||||
|
let connect_string = format!("{}:{}", host, port);
|
||||||
|
let tcp_stream = TcpStream::connect(&*connect_string).unwrap();
|
||||||
|
let mut socket = match ssl_context {
|
||||||
|
Some(context) => IMAPStream { stream: IMAPStreamTypes::Ssl(SslStream::new(&context, tcp_stream).unwrap()), host: host, port: port, tag: 1, tag_prefix: "a"},
|
||||||
|
None => IMAPStream { stream: IMAPStreamTypes::Basic(tcp_stream), host: host, port: port, tag: 1, tag_prefix: "a"},
|
||||||
|
};
|
||||||
|
|
||||||
|
match socket.read_greeting() {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read greet response"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
//LOGIN
|
||||||
|
pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("LOGIN {} {}", username, password).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//SELECT
|
||||||
|
pub fn select(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
|
||||||
|
match self.run_command(format!("SELECT {}", mailbox_name).as_str()) {
|
||||||
|
Ok(lines) => IMAPStream::parse_select_or_examine(lines),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_select_or_examine(lines: Vec<String>) -> Result<IMAPMailbox> {
|
||||||
|
let exists_regex = match Regex::new(r"^\* (\d+) EXISTS\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let recent_regex = match Regex::new(r"^\* (\d+) RECENT\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let flags_regex = match Regex::new(r"^\* FLAGS (.+)\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let unseen_regex = match Regex::new(r"^OK \[UNSEEN (\d+)\](.*)\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uid_validity_regex = match Regex::new(r"^OK \[UIDVALIDITY (\d+)\](.*)\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uid_next_regex = match Regex::new(r"^OK \[UIDNEXT (\d+)\](.*)\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let permanent_flags_regex = match Regex::new(r"^OK \[PERMANENTFLAGS (.+)\]\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
//Check Ok
|
||||||
|
match IMAPStream::parse_response_ok(lines.clone()) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mailbox = IMAPMailbox{
|
||||||
|
flags: String::from_str(""),
|
||||||
|
exists: 0,
|
||||||
|
recent: 0,
|
||||||
|
unseen: None,
|
||||||
|
permanent_flags: None,
|
||||||
|
uid_next: None,
|
||||||
|
uid_validity: None
|
||||||
|
};
|
||||||
|
|
||||||
|
for line in lines.iter() {
|
||||||
|
if exists_regex.is_match(line) {
|
||||||
|
let cap = exists_regex.captures(line).unwrap();
|
||||||
|
mailbox.exists = cap.at(1).unwrap().parse::<u32>().unwrap();
|
||||||
|
} else if recent_regex.is_match(line) {
|
||||||
|
let cap = recent_regex.captures(line).unwrap();
|
||||||
|
mailbox.recent = cap.at(1).unwrap().parse::<u32>().unwrap();
|
||||||
|
} else if flags_regex.is_match(line) {
|
||||||
|
let cap = flags_regex.captures(line).unwrap();
|
||||||
|
mailbox.flags = String::from_str(cap.at(1).unwrap());
|
||||||
|
} else if unseen_regex.is_match(line) {
|
||||||
|
let cap = unseen_regex.captures(line).unwrap();
|
||||||
|
mailbox.unseen = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
|
||||||
|
} else if uid_validity_regex.is_match(line) {
|
||||||
|
let cap = uid_validity_regex.captures(line).unwrap();
|
||||||
|
mailbox.uid_validity = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
|
||||||
|
} else if uid_next_regex.is_match(line) {
|
||||||
|
let cap = uid_next_regex.captures(line).unwrap();
|
||||||
|
mailbox.uid_next = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
|
||||||
|
} else if permanent_flags_regex.is_match(line) {
|
||||||
|
let cap = permanent_flags_regex.captures(line).unwrap();
|
||||||
|
mailbox.permanent_flags = Some(String::from_str(cap.at(1).unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(mailbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
//EXAMINE
|
||||||
|
pub fn examine(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
|
||||||
|
match self.run_command(format!("EXAMINE {}", mailbox_name).as_str()) {
|
||||||
|
Ok(lines) => IMAPStream::parse_select_or_examine(lines),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FETCH
|
||||||
|
pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
|
||||||
|
self.run_command(format!("FETCH {} {}", sequence_set, query).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOOP
|
||||||
|
pub fn noop(&mut self) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok("NOOP")
|
||||||
|
}
|
||||||
|
|
||||||
|
//LOGOUT
|
||||||
|
pub fn logout(&mut self) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok("LOGOUT")
|
||||||
|
}
|
||||||
|
|
||||||
|
//CREATE
|
||||||
|
pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("CREATE {}", mailbox_name).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//DELETE
|
||||||
|
pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("DELETE {}", mailbox_name).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//RENAME
|
||||||
|
pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//SUBSCRIBE
|
||||||
|
pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("SUBSCRIBE {}", mailbox).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//UNSUBSCRIBE
|
||||||
|
pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("UNSUBSCRIBE {}", mailbox).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
//CAPABILITY
|
||||||
|
pub fn capability(&mut self) -> Result<Vec<String>> {
|
||||||
|
match self.run_command(format!("CAPABILITY").as_str()) {
|
||||||
|
Ok(lines) => IMAPStream::parse_capability(lines),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
|
||||||
|
let capability_regex = match Regex::new(r"^\* CAPABILITY (.*)\r\n") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
//Check Ok
|
||||||
|
match IMAPStream::parse_response_ok(lines.clone()) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
};
|
||||||
|
|
||||||
|
for line in lines.iter() {
|
||||||
|
if capability_regex.is_match(line) {
|
||||||
|
let cap = capability_regex.captures(line).unwrap();
|
||||||
|
let capabilities_str = cap.at(1).unwrap();
|
||||||
|
return Ok(capabilities_str.split(' ').map(|x| String::from_str(x)).collect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::new(ErrorKind::Other, "Error parsing capabilities response"))
|
||||||
|
}
|
||||||
|
|
||||||
|
//COPY
|
||||||
|
pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
|
||||||
|
self.run_command_and_check_ok(format!("COPY {} {}", sequence_set, mailbox_name).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
|
||||||
|
match self.run_command(command) {
|
||||||
|
Ok(lines) => IMAPStream::parse_response_ok(lines),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
|
||||||
|
let command = self.create_command(String::from_str(untagged_command));
|
||||||
|
|
||||||
|
match self.write_str(&*command) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to write")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = match self.read_response() {
|
||||||
|
Ok(lines) => Ok(lines),
|
||||||
|
Err(_) => Err(Error::new(ErrorKind::Other, "Failed to read")),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.tag += 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response_ok(lines: Vec<String>) -> Result<()> {
|
||||||
|
let ok_regex = match Regex::new(r"^([a-zA-Z0-9]+) ([a-zA-Z0-9]+)(.*)") {
|
||||||
|
Ok(re) => re,
|
||||||
|
Err(err) => panic!("{}", err),
|
||||||
|
};
|
||||||
|
let last_line = lines.last().unwrap();
|
||||||
|
|
||||||
|
for cap in ok_regex.captures_iter(last_line) {
|
||||||
|
let response_type = cap.at(2).unwrap_or("");
|
||||||
|
if response_type == "OK" {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Error::new(ErrorKind::Other, format!("Invalid Response: {}", last_line).as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<()> {
|
||||||
|
match self.stream {
|
||||||
|
IMAPStreamTypes::Ssl(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
|
||||||
|
IMAPStreamTypes::Basic(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
match self.stream {
|
||||||
|
IMAPStreamTypes::Ssl(ref mut stream) => stream.read(buf),
|
||||||
|
IMAPStreamTypes::Basic(ref mut stream) => stream.read(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_response(&mut self) -> Result<Vec<String>> {
|
||||||
|
//Carriage return
|
||||||
|
let cr = 0x0d;
|
||||||
|
//Line Feed
|
||||||
|
let lf = 0x0a;
|
||||||
|
let mut found_tag_line = false;
|
||||||
|
let start_str = format!("a{} ", self.tag);
|
||||||
|
let mut lines: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
while !found_tag_line {
|
||||||
|
let mut line_buffer: Vec<u8> = Vec::new();
|
||||||
|
while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
|
||||||
|
let byte_buffer: &mut [u8] = &mut [0];
|
||||||
|
match self.read(byte_buffer) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
|
||||||
|
}
|
||||||
|
line_buffer.push(byte_buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = String::from_utf8(line_buffer).unwrap();
|
||||||
|
|
||||||
|
lines.push(line.clone());
|
||||||
|
|
||||||
|
if (&*line).starts_with(&*start_str) {
|
||||||
|
found_tag_line = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_greeting(&mut self) -> Result<()> {
|
||||||
|
//Carriage return
|
||||||
|
let cr = 0x0d;
|
||||||
|
//Line Feed
|
||||||
|
let lf = 0x0a;
|
||||||
|
|
||||||
|
let mut line_buffer: Vec<u8> = Vec::new();
|
||||||
|
while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
|
||||||
|
let byte_buffer: &mut [u8] = &mut [0];
|
||||||
|
match self.read(byte_buffer) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
|
||||||
|
}
|
||||||
|
line_buffer.push(byte_buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_command(&mut self, command: String) -> String {
|
||||||
|
let command = format!("{}{} {}\r\n", self.tag_prefix, self.tag, command);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#![feature(collections, convert)]
|
||||||
|
#![crate_name = "imap"]
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
extern crate openssl;
|
||||||
|
extern crate regex;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
Loading…
Add table
Reference in a new issue