actually start synchronizing emails

This commit is contained in:
Michael Zhang 2021-03-25 08:34:59 -05:00
parent eb880e1b92
commit 3e0e3d5561
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
6 changed files with 67 additions and 10 deletions

2
Cargo.lock generated
View file

@ -1954,6 +1954,7 @@ dependencies = [
"format-bytes", "format-bytes",
"futures 0.3.13", "futures 0.3.13",
"gluon", "gluon",
"hex 0.4.3",
"inotify", "inotify",
"log", "log",
"notify-rust", "notify-rust",
@ -1962,6 +1963,7 @@ dependencies = [
"parking_lot", "parking_lot",
"quoted_printable", "quoted_printable",
"serde", "serde",
"sha2",
"sqlx", "sqlx",
"structopt", "structopt",
"tokio 1.3.0", "tokio 1.3.0",

View file

@ -42,6 +42,8 @@ xdg = "2.2.0"
downcast-rs = "1.2.0" downcast-rs = "1.2.0"
quoted_printable = "0.4.2" quoted_printable = "0.4.2"
sqlx = { version = "0.5.1", features = ["runtime-tokio-rustls", "sqlite"] } sqlx = { version = "0.5.1", features = ["runtime-tokio-rustls", "sqlite"] }
sha2 = "0.9.3"
hex = "0.4.3"
[dependencies.panorama-imap] [dependencies.panorama-imap]
path = "imap" path = "imap"

View file

@ -184,7 +184,7 @@ fn build_msg_att_static(pair: Pair<Rule>) -> AttributeValue {
Rule::number => Some(build_number(unwrap1(pairs.next().unwrap()))), Rule::number => Some(build_number(unwrap1(pairs.next().unwrap()))),
_ => None, _ => None,
}; };
let data = Some(pairs.next().unwrap().as_str().to_owned()); let data = build_nstring(pairs.next().unwrap());
AttributeValue::BodySection(BodySection { AttributeValue::BodySection(BodySection {
section, section,
index, index,

View file

@ -8,5 +8,7 @@ CREATE TABLE IF NOT EXISTS "mail" (
"id" INTEGER PRIMARY KEY, "id" INTEGER PRIMARY KEY,
"account" TEXT, "account" TEXT,
"folder" TEXT, "folder" TEXT,
"uid" INTEGER "uidvalidity" INTEGER,
"uid" INTEGER,
"filename" TEXT
); );

View file

@ -73,14 +73,16 @@ pub async fn sync_main(
let select = authed.select(folder).await?; let select = authed.select(folder).await?;
debug!("select response: {:?}", select); debug!("select response: {:?}", select);
if let Some(exists) = select.exists { if let (Some(exists), Some(uidvalidity)) = (select.exists, select.uid_validity) {
if exists < 10 { if exists < 10 {
let mut fetched = authed let mut fetched = authed
.uid_fetch(&(1..=exists).collect::<Vec<_>>(), FetchItems::PanoramaAll) .uid_fetch(&(1..=exists).collect::<Vec<_>>(), FetchItems::PanoramaAll)
.await?; .await?;
while let Some((uid, attrs)) = fetched.next().await { while let Some((uid, attrs)) = fetched.next().await {
debug!("- {} : {:?}", uid, attrs); debug!("- {} : {:?}", uid, attrs);
mail_store.store_email(&acct_name, &folder, uid).await?; mail_store
.store_email(&acct_name, &folder, uid, uidvalidity, attrs)
.await?;
} }
} }
} }

View file

@ -3,9 +3,12 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use panorama_imap::response::AttributeValue;
use sha2::{Digest, Sha256, Sha512};
use sqlx::{ use sqlx::{
migrate::{MigrateDatabase, Migrator}, migrate::{MigrateDatabase, Migrator},
sqlite::{Sqlite, SqlitePool}, sqlite::{Sqlite, SqlitePool},
Error,
}; };
use tokio::fs; use tokio::fs;
@ -51,19 +54,65 @@ impl MailStore {
acct: impl AsRef<str>, acct: impl AsRef<str>,
folder: impl AsRef<str>, folder: impl AsRef<str>,
uid: u32, uid: u32,
uidvalidity: u32,
attrs: Vec<AttributeValue>,
) -> Result<()> { ) -> Result<()> {
let id = sqlx::query(STORE_EMAIL_SQL) let mut body = None;
for attr in attrs {
if let AttributeValue::BodySection(body_attr) = attr {
body = body_attr.data;
}
}
let body = match body {
Some(v) => v,
None => return Ok(()),
};
let mut hasher = Sha256::new();
hasher.update(body.as_bytes());
let hash = hasher.finalize();
let filename = format!("{}.mail", hex::encode(hash));
let path = self.mail_dir.join(&filename);
fs::write(path, body).await?;
let existing = sqlx::query(
r#"
SELECT FROM "mail"
WHERE account = ? AND folder = ?
AND uid = ? AND uidvalidity = ?
"#,
)
.bind(acct.as_ref()) .bind(acct.as_ref())
.bind(folder.as_ref()) .bind(folder.as_ref())
.bind(uid) .bind(uid)
.bind(uidvalidity)
.fetch_one(&self.pool)
.await;
let exists = match existing {
Ok(_) => true,
Err(Error::RowNotFound) => true,
_ => false,
};
if !exists {
let id = sqlx::query(
r#"
INSERT INTO "mail" (account, folder, uid, uidvalidity, filename)
VALUES (?, ?, ?, ?, ?)
"#,
)
.bind(acct.as_ref())
.bind(folder.as_ref())
.bind(uid)
.bind(uidvalidity)
.bind(filename)
.execute(&self.pool) .execute(&self.pool)
.await? .await?
.last_insert_rowid(); .last_insert_rowid();
}
Ok(()) Ok(())
} }
} }
const STORE_EMAIL_SQL: &str = r#"
INSERT INTO "mail" (account, folder, uid)
VALUES (?, ?, ?)
"#;