1use crate::backend::{QtThread, Shared, config_path, load_config, say, show, start_watcher, tr, want_password};
13use crate::pim::google_sentence;
14use cxx_qt_lib::QString;
15use sioul_core::config::{self, Account, AccountKind, Security};
16use sioul_sync::discover::{GOOGLE_IMAP, GOOGLE_SMTP};
17use sioul_sync::google::{self, Purpose};
18use std::sync::Arc;
19use std::sync::atomic::Ordering;
20
21fn key(address: &str, client_id: &str, client_secret: &str) -> Result<(String, String), String> {
24 if !client_id.trim().is_empty() {
25 return Ok((client_id.trim().to_string(), client_secret.trim().to_string()));
26 }
27 google::own_key(address).ok_or_else(|| if google::built_in().is_some() { tr().text("google-mail-built-in", None) } else { say("google-mail-no-key", &[("address", address.to_string())]) })
28}
29
30pub(crate) fn has_own_key(address: &str) -> bool {
33 google::own_key(address.trim()).is_some()
34}
35
36pub(crate) fn add_google_mail(qt: &QtThread, shared: &Arc<Shared>, address: String, client_id: String, client_secret: String, again: Option<String>) {
41 let (qt, shared) = (qt.clone(), Arc::clone(shared));
42 shared.google_stop.store(false, Ordering::Relaxed);
43 std::thread::spawn(move || {
44 let address = address.trim().to_ascii_lowercase();
45 let config = load_config();
46 let outcome = (|| -> Result<Account, String> {
47 let existing = match &again {
49 Some(id) => Some(config.every_account().find(|a| &a.id == id && a.kind == AccountKind::Imap && a.auth.as_deref() == Some("google")).cloned().ok_or_else(|| say("account-unknown", &[("id", id.clone())]))?),
50 None => None,
51 };
52 if existing.is_none()
53 && let Some(have) = config.every_account().find(|a| a.address.as_deref() == Some(address.as_str()) && matches!(a.kind, AccountKind::Imap | AccountKind::Jmap))
54 {
55 return Err(say("account-address-exists", &[("address", address.clone()), ("id", have.id.clone())]));
56 }
57 let (client_id, client_secret) = key(&address, &client_id, &client_secret)?;
58 let account = existing.clone().unwrap_or_else(|| {
59 let mut account = Account::imap(&config.free_id(&address), &address, GOOGLE_IMAP, 993, Security::Tls, None);
60 account.auth = Some("google".into());
61 account.smtp_host = Some(GOOGLE_SMTP.into());
62 account.smtp_port = Some(465);
63 account.smtp_security = Security::Tls;
64 account
65 });
66 let id = account.id.clone();
67 let sign_in = google::begin_for(Purpose::Mail, &client_id, &address).map_err(|e| google_sentence(&e, &id))?;
68 let url = sign_in.url.clone();
69 let _ = qt.queue(move |mut sioul| sioul.as_mut().open_url(QString::from(&url)));
70 google::finish(sign_in, &client_id, &client_secret, &address, &shared.google_stop).map_err(|e| google_sentence(&e, &id))?;
71 if let Err(e) = sioul_sync::test(&account, "") {
73 if existing.is_none() {
74 let _ = google::revoke_for(Purpose::Mail, &address);
76 }
77 return Err(e.sentence(tr(), &id));
78 }
79 if existing.is_none() {
80 config::add_imap_account(&config_path(), &account)?;
81 config::set_smtp(&config_path(), &id, GOOGLE_SMTP, 465, Security::Tls)?;
82 }
83 Ok(account)
84 })();
85 let line = outcome.as_ref().ok().map(|a| match &again {
86 Some(_) => say("account-signed-in-again", &[("account", a.id.clone())]),
87 None => say("account-added", &[("id", a.id.clone()), ("path", a.maildir_path().display().to_string())]),
88 });
89 if let Ok(account) = outcome.as_ref() {
90 want_password(&shared, &account.id, None);
91 if let Ok(mut statuses) = shared.statuses.lock() {
92 statuses.remove(&account.id);
93 }
94 start_watcher(&qt, &shared, account.clone());
96 }
97 let error = outcome.err();
98 let _ = qt.queue(move |mut sioul| {
99 sioul.as_mut().set_form_busy(false);
100 match (line, error) {
101 (Some(line), _) => {
102 sioul.as_mut().set_found(QString::default());
103 sioul.as_mut().set_status(QString::from(&line));
104 sioul.as_mut().account_added();
105 }
106 (None, Some(e)) => sioul.as_mut().set_form_error(QString::from(&e)),
107 (None, None) => {}
108 }
109 });
110 show(&qt, &shared);
111 });
112}