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 fn established(&self, token: &str) {
36 #[cfg(feature = "engine")]
37 {
38 self.live.set_token(token);
39 self.live.with_exec(|e| e.set_keep_prefix(false));
41 }
42 let _ = token;
43 }
44}
45
46impl Backend for ServerBackend {
47 fn kind(&self) -> BackendKind {
48 BackendKind::Server
49 }
50 fn capabilities(&self) -> Capabilities {
51 Capabilities { rooms: false, federation: false }
52 }
53 fn server_ref(&self) -> &str {
54 &self.base
55 }
56
57 fn ensure_session(&self, ids: &IdentityStore, id: &mut SessionIdentity, invite: Option<&str>) -> Result<Session> {
58 if id.tier != BackendKind::Server || id.server_ref != self.base {
59 return Err(AgentError::Identity("identity belongs to another tier or server".into()));
60 }
61 if let Some(t) = keyauth::stored_token(ids, id)? {
62 let (st, v) = self.wire.get("/product/v1/me", Some(&t))?;
63 if st == 200 {
64 let nick = v.get("nick").and_then(|n| n.as_str()).unwrap_or_default().to_string();
65 self.established(&t);
66 return Ok(Session { token: Zeroizing::new(t), nick, cred_ref: id.key_id.clone(), user_id: None, device_id: None });
67 }
68 }
69 let (nick, token) = if id.enrolled {
70 keyauth::login(&*self.wire, ids, id)?
71 } else {
72 keyauth::enroll(&*self.wire, ids, id, invite.ok_or(AgentError::NeedsInvite)?)?
73 };
74 self.established(&token);
75 Ok(Session { token: Zeroizing::new(token), nick, cred_ref: id.key_id.clone(), user_id: None, device_id: None })
76 }
77
78 #[cfg(feature = "engine")]
79 fn execute(&self, request: &mail4agent_messenger::OutgoingRequest) -> Result<mail4agent_messenger::HttpResponseDescriptor> {
80 self.live.execute(request)
81 }
82
83 #[cfg(feature = "engine")]
84 fn wait_for_news(&self, timeout: std::time::Duration) -> Result<crate::engine::News> {
85 self.live.wait_for_news(timeout)
86 }
87
88 #[cfg(feature = "engine")]
89 fn whoami(&self) -> Result<(String, String)> {
90 self.live.whoami()
91 }
92
93 #[cfg(feature = "engine")]
94 fn keep_prefix(&self) -> bool {
95 self.live.keep_prefix()
96 }
97}