actually start synchronizing emails
This commit is contained in:
parent
eb880e1b92
commit
3e0e3d5561
6 changed files with 67 additions and 10 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
.bind(acct.as_ref())
|
for attr in attrs {
|
||||||
.bind(folder.as_ref())
|
if let AttributeValue::BodySection(body_attr) = attr {
|
||||||
.bind(uid)
|
body = body_attr.data;
|
||||||
.execute(&self.pool)
|
|
||||||
.await?
|
|
||||||
.last_insert_rowid();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const STORE_EMAIL_SQL: &str = r#"
|
let body = match body {
|
||||||
INSERT INTO "mail" (account, folder, uid)
|
Some(v) => v,
|
||||||
VALUES (?, ?, ?)
|
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(folder.as_ref())
|
||||||
|
.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)
|
||||||
|
.await?
|
||||||
|
.last_insert_rowid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue