m4a_agent/backend/
server.rs1use std::sync::Arc;
4
5use zeroize::Zeroizing;
6
7use super::wire::{HttpWire, Wire};
8use super::{Backend, BackendKind, Capabilities, Session};
9use crate::error::{AgentError, Result};
10use crate::identity::{IdentityStore, SessionIdentity};
11use crate::keyauth;
12
13pub struct ServerBackend {
14 base: String,
15 wire: Arc<dyn Wire>,
16 #[cfg(feature = "engine")]
17 live: super::live::Live,
18}
19
20impl ServerBackend {
21 pub fn new(base_url: &str) -> Result<Self> {
22 Self::with_wire(base_url, Box::new(HttpWire::new(base_url)?))
23 }
24
25 pub fn with_wire(base_url: &str, wire: Box<dyn Wire>) -> Result<Self> {
27 Ok(Self {
28 base: base_url.trim_end_matches('/').to_string(),
29 wire: Arc::from(wire),
30 #[cfg(feature = "engine")]
31 live: super::live::Live::new(crate::engine::HttpExec::new(base_url)?),
32 })
33 }
34
35 #[allow(unused_variables)]
36 fn established(&self, ids: &IdentityStore, id: &SessionIdentity, token: &str) {
37 #[cfg(feature = "engine")]
38 {
39 self.live.set_token(token);
40 self.live.with_exec(|e| e.set_keep_prefix(false));
42 if id.enrolled {
46 let (ids, id, wire) = (ids.clone(), id.clone(), Arc::clone(&self.wire));
47 self.live.set_refresher(Arc::new(move || Ok(keyauth::login(&*wire, &ids, &id)?.1)));
48 }
49 }
50 let _ = token;
51 }
52}
53
54impl Backend for ServerBackend {
55 fn kind(&self) -> BackendKind {
56 BackendKind::Server
57 }
58 fn capabilities(&self) -> Capabilities {
59 Capabilities { rooms: false, federation: false }
60 }
61 fn server_ref(&self) -> &str {
62 &self.base
63 }
64
65 fn ensure_session(&self, ids: &IdentityStore, id: &mut SessionIdentity, invite: Option<&str>) -> Result<Session> {
66 if id.tier != BackendKind::Server || id.server_ref != self.base {
67 return Err(AgentError::Identity("identity belongs to another tier or server".into()));
68 }
69 if let Some(t) = keyauth::stored_token(ids, id)? {
70 let (st, v) = self.wire.get("/product/v1/me", Some(&t))?;
71 if st == 200 {
72 let nick = v.get("nick").and_then(|n| n.as_str()).unwrap_or_default().to_string();
73 self.established(ids, id, &t);
74 return Ok(Session { token: Zeroizing::new(t), nick, cred_ref: id.key_id.clone(), user_id: None, device_id: None });
75 }
76 }
77 let (nick, token) = if id.enrolled {
78 keyauth::login(&*self.wire, ids, id)?
79 } else {
80 keyauth::enroll(&*self.wire, ids, id, invite.ok_or(AgentError::NeedsInvite)?)?
81 };
82 self.established(ids, id, &token);
83 Ok(Session { token: Zeroizing::new(token), nick, cred_ref: id.key_id.clone(), user_id: None, device_id: None })
84 }
85
86 #[cfg(feature = "engine")]
87 fn execute(&self, request: &mail4agent_messenger::OutgoingRequest) -> Result<mail4agent_messenger::HttpResponseDescriptor> {
88 self.live.execute(request)
89 }
90
91 #[cfg(feature = "engine")]
92 fn wait_for_news(&self, timeout: std::time::Duration) -> Result<crate::engine::News> {
93 self.live.wait_for_news(timeout)
94 }
95
96 #[cfg(feature = "engine")]
97 fn whoami(&self) -> Result<(String, String)> {
98 self.live.whoami()
99 }
100
101 #[cfg(feature = "engine")]
102 fn keep_prefix(&self) -> bool {
103 self.live.keep_prefix()
104 }
105}