use async_trait::async_trait;
use serde::Deserialize;
use sos_core::crypto::AccessKey;
use sos_vault::Vault;
use sos_vfs as vfs;
use std::path::{Path, PathBuf};
use tokio::io::AsyncRead;
use url::Url;
use super::{GenericCsvConvert, GenericCsvEntry, GenericPasswordRecord};
use crate::{import::read_csv_records, Convert, Result};
#[derive(Deserialize)]
pub struct FirefoxPasswordRecord {
pub url: Url,
pub username: String,
pub password: String,
#[serde(rename = "httpRealm")]
pub http_realm: String,
#[serde(rename = "formActionOrigin")]
pub form_action_origin: String,
pub guid: String,
#[serde(rename = "timeCreated")]
pub time_created: String,
#[serde(rename = "timeLastUsed")]
pub time_last_used: String,
#[serde(rename = "timePasswordChanged")]
pub time_password_changed: String,
}
impl From<FirefoxPasswordRecord> for GenericPasswordRecord {
fn from(value: FirefoxPasswordRecord) -> Self {
Self {
label: value.url.to_string(),
url: vec![value.url],
username: value.username,
password: value.password,
otp_auth: None,
tags: None,
note: None,
}
}
}
impl From<FirefoxPasswordRecord> for GenericCsvEntry {
fn from(value: FirefoxPasswordRecord) -> Self {
Self::Password(value.into())
}
}
pub async fn parse_reader<R: AsyncRead + Unpin + Send>(
reader: R,
) -> Result<Vec<FirefoxPasswordRecord>> {
read_csv_records::<FirefoxPasswordRecord, _>(reader).await
}
pub async fn parse_path<P: AsRef<Path>>(
path: P,
) -> Result<Vec<FirefoxPasswordRecord>> {
parse_reader(vfs::File::open(path).await?).await
}
pub struct FirefoxPasswordCsv;
#[async_trait]
impl Convert for FirefoxPasswordCsv {
type Input = PathBuf;
async fn convert(
&self,
source: Self::Input,
vault: Vault,
key: &AccessKey,
) -> crate::Result<Vault> {
let records: Vec<GenericCsvEntry> = parse_path(source)
.await?
.into_iter()
.map(|r| r.into())
.collect();
GenericCsvConvert.convert(records, vault, key).await
}
}