relay_actions/actions/
send.rs1use serde::Serialize;
2
3use crate::{
4 actions::Action, cache::CacheManager, sign::sign_request, sinks::progress::ProgressSink,
5 storage::Storage,
6};
7use relay_lib::{
8 core::{Uuid, Version, chrono::Utc},
9 prelude::{Address, MetaEnvelope, PrivateEnvelope, e2e, sign},
10};
11
12#[derive(Serialize)]
13struct SendReq {
14 pub envelope: MetaEnvelope,
15 pub self_envelope: Option<MetaEnvelope>,
16}
17
18pub struct Send {
19 pub address: Address,
20 pub to: Address,
21 pub message: Option<String>,
22 pub subject: Option<String>,
23 pub self_encrypt: bool,
24}
25
26impl Action for Send {
27 type Output = ();
28
29 fn execute(
30 &self,
31 storage: &mut Storage,
32 cache: &mut CacheManager,
33 progress: &mut dyn ProgressSink,
34 ) -> Self::Output {
35 let identity = storage
36 .root
37 .get_identity(&self.address)
38 .unwrap_or_else(|| {
39 progress.abort(&format!("No identity found for address `{}`", self.address));
40 })
41 .clone();
42
43 progress.step("Fetching records", "Fetched records");
44 let agent_record = cache
45 .records
46 .agent(&mut cache.agents, self.address.agent())
47 .unwrap_or_else(|e| {
48 progress.error(&format!("{:#?}", e));
49 progress.abort("Failed to fetch agent record");
50 });
51 let recipient_record = cache
52 .records
53 .user(&mut cache.agents, self.to.agent(), self.to.user())
54 .unwrap_or_else(|e| {
55 progress.error(&format!("{:#?}", e));
56 progress.abort("Failed to fetch recipient record");
57 });
58
59 progress.step("Composing message", "Composed message");
60 let message = match &self.message {
61 Some(msg) => msg.clone(),
62 None => {
63 progress.pause();
64 let edited = edit::edit(
65 "\n# Please write your message here.\n# Lines starting with '#' will be ignored\n",
66 )
67 .unwrap_or_else(|e| {
68 progress.error(&format!("{:#?}", e));
69 progress.abort("Failed to open editor");
70 });
71 progress.resume();
72
73 edited
74 .lines()
75 .filter(|line| !line.starts_with('#'))
76 .collect::<Vec<_>>()
77 .join("\n")
78 }
79 };
80
81 let private = PrivateEnvelope {
82 content_type: "plain/text".to_string(),
83 message: message.as_bytes().to_vec(),
84 subject: self.subject.as_ref().map(|s| s.trim().as_bytes().to_vec()),
85 };
86
87 progress.step("Encrypting message", "Encrypted message");
88 let payload = serde_json::to_vec(&private).unwrap_or_else(|e| {
89 progress.error(&format!("{:#?}", e));
90 progress.abort("Failed to serialize message payload");
91 });
92
93 let (crypto, ciphertext) = e2e::encrypt(
94 relay_lib::crypto::OsRng,
95 &recipient_record.public_key(),
96 &payload,
97 &[],
98 )
99 .unwrap_or_else(|e| {
100 progress.error(&format!("{:#?}", e));
101 progress.abort("Failed to encrypt message");
102 });
103
104 progress.step("Signing message", "Signed message");
105 let signed = sign::sign(&ciphertext, &identity.signing_key());
106
107 let envelope = MetaEnvelope {
108 gid: Uuid::new_v4(),
109 version: Version::parse("0.0.1").unwrap(),
110 from: self.address.clone(),
111 to: self.to.clone(),
112 timestamp: Utc::now(),
113 crypto,
114 payload: signed,
115 self_encrypted: false,
116 };
117
118 let self_envelope = if self.self_encrypt {
119 let (crypto, ciphertext) = e2e::encrypt(
120 relay_lib::crypto::OsRng,
121 &identity.pub_record.public_key(),
122 &payload,
123 &[],
124 )
125 .unwrap_or_else(|e| {
126 progress.error(&format!("{:#?}", e));
127 progress.abort("Failed to encrypt self-envelope");
128 });
129
130 let signed = sign::sign(&ciphertext, &identity.signing_key());
131
132 Some(MetaEnvelope {
133 gid: Uuid::new_v4(),
134 version: Version::parse("0.0.1").unwrap(),
135 from: self.address.clone(),
136 to: self.address.clone(),
137 timestamp: Utc::now(),
138 crypto,
139 payload: signed,
140 self_encrypted: true,
141 })
142 } else {
143 None
144 };
145
146 progress.step("Sending message", "Sent message");
147 let request = sign_request(
148 &self.address.canonical(),
149 SendReq {
150 envelope,
151 self_envelope,
152 },
153 &agent_record,
154 &identity.signing_key(),
155 )
156 .unwrap_or_else(|e| {
157 progress.error(&format!("{:#?}", e));
158 progress.abort("Failed to sign send request");
159 });
160
161 let resp = reqwest::blocking::Client::new()
162 .post(
163 cache
164 .agents
165 .url(self.address.agent())
166 .unwrap_or_else(|e| {
167 progress.error(&format!("{:#?}", e));
168 progress.abort("failed to fetch agent information");
169 })
170 .join("/send/user")
171 .unwrap_or_else(|e| {
172 progress.error(&format!("{:#?}", e));
173 progress.abort("Failed to construct send user URL");
174 }),
175 )
176 .json(&request)
177 .send()
178 .unwrap_or_else(|e| {
179 progress.error(&format!("{:#?}", e));
180 progress.abort("Failed to send message request");
181 });
182
183 if resp.status().as_u16() == 404 {
184 progress.abort("Inbox not found on the Agent");
185 }
186 if !resp.status().is_success() {
187 progress.abort(&format!(
188 "Failed to send message. HTTP {}",
189 resp.status().as_u16()
190 ));
191 }
192
193 progress.done();
194 }
195}