From ffea8efdacaf42eb2a9abbbdfd0e7272f9299a2e Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 8 Dec 2020 03:43:24 +0100 Subject: [PATCH] backup: add freeotp+ support --- src/backup/freeotp.rs | 30 +++++++++++++++++++++++++++++- src/models/account.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/backup/freeotp.rs b/src/backup/freeotp.rs index 042e422..bc455f2 100644 --- a/src/backup/freeotp.rs +++ b/src/backup/freeotp.rs @@ -1,7 +1,8 @@ use super::{Backupable, Restorable}; -use crate::models::ProvidersModel; +use crate::models::{Account, Provider, ProvidersModel}; use anyhow::Result; use gettextrs::gettext; +use gio::prelude::*; use serde::{Deserialize, Serialize}; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -21,6 +22,33 @@ impl Backupable for FreeOTP { } fn backup(model: ProvidersModel, into: gio::File) -> Result<()> { + let mut items = Vec::new(); + + for i in 0..model.get_n_items() { + let provider = model.get_object(i).unwrap().downcast::().unwrap(); + let accounts = provider.accounts_model(); + + for j in 0..accounts.get_n_items() { + let account = accounts + .get_object(j) + .unwrap() + .downcast::() + .unwrap(); + + items.push(account.otp_uri()); + } + } + + let content = items.join("\n"); + + into.replace_contents( + content.as_bytes(), + None, + false, + gio::FileCreateFlags::REPLACE_DESTINATION, + gio::NONE_CANCELLABLE, + )?; + Ok(()) } } diff --git a/src/models/account.rs b/src/models/account.rs index ff72841..67d7aef 100644 --- a/src/models/account.rs +++ b/src/models/account.rs @@ -273,6 +273,8 @@ impl Account { let priv_ = AccountPriv::from_instance(&account); priv_.token.set(token); + println!("{:#?}", account.otp_uri()); + account.init(); account } @@ -378,6 +380,41 @@ impl Account { priv_.token_id.borrow().clone() } + pub fn otp_uri(&self) -> String { + let provider = self.provider(); + + let label = self.name(); + let issuer = provider.name(); + let hmac_algorithm = provider.hmac_algorithm().to_string(); + let secret = self.token(); + let digits = provider.digits(); + + match provider.algorithm() { + Algorithm::HOTP => { + format!( + "otpauth://hotp/{}?secret={}&issuer={}&algorithm={}&digits={}&counter={}", + label, + secret, + issuer, + hmac_algorithm, + digits, + self.counter() + ) + } + _ => { + format!( + "otpauth://totp/{}?secret={}&issuer={}&algorithm={}&digits={}&period={}", + label, + secret, + issuer, + hmac_algorithm, + digits, + provider.period() + ) + } + } + } + pub fn set_name(&self, name: &str) -> Result<()> { let db = database::connection(); let conn = db.get()?;