From eb880e1b92398725dffb7b1a13f16ecdf1a76ac9 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Thu, 25 Mar 2021 07:14:05 -0500 Subject: [PATCH] insert mail into the database --- imap/src/parser/mod.rs | 17 +++++++++++------ imap/src/parser/rfc3501.pest | 8 +++----- imap/src/response.rs | 26 +++++++++++++++++++++----- migrations/1_initial.sql | 11 ++++------- src/mail/client.rs | 2 +- src/mail/store.rs | 21 ++++++++++++++++++++- src/main.rs | 2 +- 7 files changed, 61 insertions(+), 26 deletions(-) diff --git a/imap/src/parser/mod.rs b/imap/src/parser/mod.rs index 2da4540..bb10518 100644 --- a/imap/src/parser/mod.rs +++ b/imap/src/parser/mod.rs @@ -171,20 +171,25 @@ fn build_msg_att_static(pair: Pair) -> AttributeValue { Rule::msg_att_static_rfc822_size => AttributeValue::Rfc822Size(build_number(unwrap1(pair))), Rule::msg_att_static_envelope => AttributeValue::Envelope(build_envelope(unwrap1(pair))), // TODO: do this - Rule::msg_att_static_body_structure => AttributeValue::BodySection { + Rule::msg_att_static_body_structure => AttributeValue::BodySection(BodySection { section: None, index: None, data: None, - }, + }), Rule::msg_att_static_body_section => { + let mut pairs = pair.into_inner(); let section = None; - let index = None; - let data = None; - AttributeValue::BodySection { + pairs.next(); + let index = match pairs.peek().unwrap().as_rule() { + Rule::number => Some(build_number(unwrap1(pairs.next().unwrap()))), + _ => None, + }; + let data = Some(pairs.next().unwrap().as_str().to_owned()); + AttributeValue::BodySection(BodySection { section, index, data, - } + }) } Rule::msg_att_static_uid => AttributeValue::Uid(build_number(unwrap1(unwrap1(pair)))), _ => parse_fail!("{:#?}", pair), diff --git a/imap/src/parser/rfc3501.pest b/imap/src/parser/rfc3501.pest index 0cbb46d..3763507 100644 --- a/imap/src/parser/rfc3501.pest +++ b/imap/src/parser/rfc3501.pest @@ -75,8 +75,6 @@ mailbox_data_recent = { number ~ sp ~ ^"RECENT" } mailbox_data_search = { ^"SEARCH" ~ (sp ~ nz_number)* } mailbox_list = { mailbox_list_flags ~ sp ~ mailbox_list_string ~ sp ~ mailbox } mailbox_list_flags = { "(" ~ mbx_list_flags* ~ ")" } -// TODO: technically this should only be 1 quoted char -// mailbox_list_string = { dquote ~ quoted_char ~ dquote | nil } mailbox_list_string = ${ nstring } mbx_list_flags = { (mbx_list_oflag ~ sp)* ~ mbx_list_sflag ~ (sp ~ mbx_list_oflag)* | mbx_list_oflag ~ (sp ~ mbx_list_oflag)* } mbx_list_oflag = { "\\NoInferiors" | flag_extension } @@ -92,8 +90,8 @@ msg_att = { "(" ~ msg_att_dyn_or_stat ~ (sp ~ msg_att_dyn_or_stat)* ~ ")" } msg_att_dyn_or_stat = { msg_att_dynamic | msg_att_static } msg_att_dynamic = { ^"FLAGS" ~ sp ~ "(" ~ (flag_fetch ~ (sp ~ flag_fetch)*)? ~ ")" } msg_att_static = { msg_att_static_envelope | msg_att_static_internaldate | (^"RFC822" ~ (^".HEADER" | ^".TEXT") ~ sp ~ nstring) | msg_att_static_rfc822_size | msg_att_static_body_structure | msg_att_static_body_section | msg_att_static_uid } -msg_att_static_body_structure = { ^"BODY" ~ ^"STRUCTURE"? ~ sp ~ body } msg_att_static_body_section = { ^"BODY" ~ section ~ ("<" ~ number ~ ">")? ~ sp ~ nstring } +msg_att_static_body_structure = { ^"BODY" ~ ^"STRUCTURE"? ~ sp ~ body } msg_att_static_envelope = { ^"ENVELOPE" ~ sp ~ envelope } msg_att_static_internaldate = { ^"INTERNALDATE" ~ sp ~ date_time } msg_att_static_rfc822_size = { ^"RFC822.SIZE" ~ sp ~ number } @@ -112,12 +110,12 @@ resp_status = { (^"OK" | ^"NO" | ^"BAD") } resp_text = { ("[" ~ resp_text_code ~ "]" ~ sp)? ~ text } resp_text_code = { ^"ALERT" | (^"BADCHARSET" ~ (sp ~ "(" ~ astring ~ (sp ~ astring)* ~ ")")?) | capability_data | ^"PARSE" | resp_text_code_permanentflags | ^"READ-ONLY" | resp_text_code_readwrite | ^"TRYCREATE" | resp_text_code_uidnext | resp_text_code_uidvalidity | resp_text_code_unseen | resp_text_code_other } resp_text_code_atom = @{ (!"]" ~ text_char){1,} } +resp_text_code_other = { (atom ~ (sp ~ resp_text_code_atom)?) } resp_text_code_permanentflags = { ^"PERMANENTFLAGS" ~ sp ~ "(" ~ (flag_perm ~ (sp ~ flag_perm)*)? ~ ")" } resp_text_code_readwrite = { ^"READ-WRITE" } -resp_text_code_uidvalidity = { ^"UIDVALIDITY" ~ sp ~ nz_number } resp_text_code_uidnext = { ^"UIDNEXT" ~ sp ~ nz_number } +resp_text_code_uidvalidity = { ^"UIDVALIDITY" ~ sp ~ nz_number } resp_text_code_unseen = { ^"UNSEEN" ~ sp ~ nz_number } -resp_text_code_other = { (atom ~ (sp ~ resp_text_code_atom)?) } response = { continue_req | response_data | response_done } response_data = { "*" ~ sp ~ (resp_cond_state | resp_cond_bye | mailbox_data | message_data | capability_data) ~ crlf } response_done = { response_tagged | response_fatal } diff --git a/imap/src/response.rs b/imap/src/response.rs index 345d31f..17a59c3 100644 --- a/imap/src/response.rs +++ b/imap/src/response.rs @@ -1,5 +1,6 @@ //! Structs and enums that have to do with responses. +use std::fmt; use std::ops::RangeInclusive; use chrono::{DateTime, FixedOffset}; @@ -72,11 +73,7 @@ pub enum UidSetMember { #[derive(Clone, Debug, PartialEq, Eq)] pub enum AttributeValue { - BodySection { - section: Option, - index: Option, - data: Option, - }, + BodySection(BodySection), BodyStructure(BodyStructure), Envelope(Envelope), Flags(Vec), @@ -89,6 +86,25 @@ pub enum AttributeValue { Uid(u32), } +#[derive(Clone, PartialEq, Eq)] +pub struct BodySection { + pub section: Option, + pub index: Option, + pub data: Option, +} + +impl fmt::Debug for BodySection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "BodySection(section={:?} index={:?} data=<{}>", + self.section, + self.index, + self.data.as_ref().map(|s| s.len()).unwrap_or(0) + ) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum BodyStructure { Basic { diff --git a/migrations/1_initial.sql b/migrations/1_initial.sql index aa20c24..02cb0cc 100644 --- a/migrations/1_initial.sql +++ b/migrations/1_initial.sql @@ -1,15 +1,12 @@ CREATE TABLE IF NOT EXISTS "accounts" ( - "id" INTEGER PRIMARY KEY, + "name" TEXT PRIMARY KEY, -- hash of the account details, used to check if accounts have changed - "checksum" TEXT, - "name" TEXT NOT NULL + "checksum" TEXT ); CREATE TABLE IF NOT EXISTS "mail" ( "id" INTEGER PRIMARY KEY, - "account_id" INTEGER, + "account" TEXT, "folder" TEXT, - "uid" INTEGER, - - FOREIGN KEY ("account_id") REFERENCES "accounts" ("id") + "uid" INTEGER ); diff --git a/src/mail/client.rs b/src/mail/client.rs index 4718935..f8e6a7a 100644 --- a/src/mail/client.rs +++ b/src/mail/client.rs @@ -80,7 +80,7 @@ pub async fn sync_main( .await?; while let Some((uid, attrs)) = fetched.next().await { debug!("- {} : {:?}", uid, attrs); - mail_store.store_email(); + mail_store.store_email(&acct_name, &folder, uid).await?; } } } diff --git a/src/mail/store.rs b/src/mail/store.rs index d4d471c..26d509c 100644 --- a/src/mail/store.rs +++ b/src/mail/store.rs @@ -46,5 +46,24 @@ impl MailStore { pub fn get_new_uids(&self, exists: u32) {} /// Stores the given email - pub fn store_email(&self) {} + pub async fn store_email( + &self, + acct: impl AsRef, + folder: impl AsRef, + uid: u32, + ) -> Result<()> { + let id = sqlx::query(STORE_EMAIL_SQL) + .bind(acct.as_ref()) + .bind(folder.as_ref()) + .bind(uid) + .execute(&self.pool) + .await? + .last_insert_rowid(); + Ok(()) + } } + +const STORE_EMAIL_SQL: &str = r#" +INSERT INTO "mail" (account, folder, uid) +VALUES (?, ?, ?) +"#; diff --git a/src/main.rs b/src/main.rs index dff22a5..082db4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,7 +133,7 @@ fn setup_logger(log_file: Option>) -> Result<()> { message )) }) - .level(log::LevelFilter::Trace); + .level(log::LevelFilter::Debug); if let Some(log_file) = log_file { logger = logger.chain(fern::log_file(log_file)?); }