iron_os_packages_api/
requests.rs

1use crate::error::{Error, Result};
2use crate::action::Action;
3use crate::packages::{Channel, Package, BoardArch, TargetArch};
4
5use std::collections::HashSet;
6use std::marker::PhantomData;
7use std::result::Result as StdResult;
8
9use tokio::fs::File;
10use tokio::io::{AsyncReadExt, AsyncSeekExt, SeekFrom};
11
12use stream_api::{IntoMessage, FromMessage};
13use stream_api::message::{
14	Message, IntoMessage, FromMessage, PacketBytes
15};
16use stream_api::error::MessageError;
17use stream_api::request::Request;
18
19use serde::{Serialize, Deserialize};
20
21use crypto::signature::Signature;
22use crypto::hash::{Hasher, Hash};
23use crypto::token::Token;
24
25use bytes::{BytesRead, BytesReadRef, BytesWrite};
26
27// type Message = stream_api::message::Message<Action, EncryptedBytes>;
28
29pub type DeviceId = Token<32>;
30
31// /// All packages
32// ///
33// /// Can be called by anyone
34// #[derive(Debug, Clone, Serialize, Deserialize)]
35// #[serde(rename_all = "camelCase")]
36// pub struct AllPackagesReq {
37// 	pub channel: Channel
38// }
39
40// #[derive(Debug, Clone, Serialize, Deserialize)]
41// #[serde(rename_all = "camelCase")]
42// pub struct AllPackages {
43// 	pub list: Vec<Package>
44// }
45
46// impl<B> Request<Action, B> for AllPackagesReq {
47// 	type Response = AllPackages;
48// 	type Error = Error;
49// 	const ACTION: Action = Action::AllPackages;
50// }
51
52#[derive(Debug, Clone, Default, Serialize, Deserialize,
53	IntoMessage, FromMessage)]
54#[message(json)]
55pub struct EmptyJson;
56
57
58/// Package Info
59///
60/// Can be called by anyone
61#[derive(Debug, Clone, Serialize, Deserialize, IntoMessage, FromMessage)]
62#[serde(rename_all = "camelCase")]
63#[message(json)]
64pub struct PackageInfoReq {
65	pub channel: Channel,
66	pub arch: BoardArch,
67	pub name: String,
68	/// device_id gives the possibility to target an update
69	/// specific for only one device
70	pub device_id: Option<DeviceId>
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, IntoMessage, FromMessage)]
74#[serde(rename_all = "camelCase")]
75#[message(json)]
76pub struct PackageInfo {
77	// there may not exist any info
78	pub package: Option<Package>
79}
80
81impl Request for PackageInfoReq {
82	type Action = Action;
83	type Response = PackageInfo;
84	type Error = Error;
85
86	const ACTION: Action = Action::PackageInfo;
87}
88
89
90/// Needs to be authenticated as a writer
91#[derive(Debug, Clone, Serialize, Deserialize, IntoMessage, FromMessage)]
92#[serde(rename_all = "camelCase")]
93#[message(json)]
94pub struct SetPackageInfoReq {
95	pub package: Package,
96	// if empty no whitelist is applied
97	pub whitelist: HashSet<DeviceId>,
98	#[serde(default)]
99	pub auto_whitelist_limit: u32
100}
101
102impl Request for SetPackageInfoReq {
103	type Action = Action;
104	type Response = EmptyJson;
105	type Error = Error;
106
107	const ACTION: Action = Action::SetPackageInfo;
108}
109
110
111/// Get File
112///
113/// Can be accessed by anyone
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct GetFileReq<B> {
117	pub hash: Hash,
118	#[serde(skip)]
119	_bytes: PhantomData<B>
120}
121
122impl<B> GetFileReq<B> {
123	pub fn new(hash: Hash) -> Self {
124		Self {
125			hash,
126			_bytes: PhantomData
127		}
128	}
129}
130
131impl<B> IntoMessage<Action, B> for GetFileReq<B>
132where B: PacketBytes {
133	fn into_message(self) -> StdResult<Message<Action, B>, MessageError> {
134		stream_api::encdec::json::encode(self)
135	}
136}
137
138impl<B> FromMessage<Action, B> for GetFileReq<B>
139where B: PacketBytes {
140	fn from_message(msg: Message<Action, B>) -> StdResult<Self, MessageError> {
141		stream_api::encdec::json::decode(msg)
142	}
143}
144
145#[derive(Debug)]
146pub struct GetFile<B> {
147	// we keep the message so no new allocation is done
148	inner: Message<Action, B>
149}
150
151impl<B> GetFile<B>
152where B: PacketBytes {
153	/// Before sending this response you should check the hash
154	pub async fn from_file(mut file: File) -> Result<Self> {
155		let mut msg = Message::new();
156
157		// check how big the file is then allocate
158		let buf_size = file.metadata().await
159			.map(|m| m.len() as usize + 1)
160			.unwrap_or(0);
161
162		let mut body = msg.body_mut();
163		body.reserve(buf_size);
164
165		unsafe {
166			// safe because file.read_to_end only appends
167			let v = body.as_mut_vec();
168			file.read_to_end(v).await
169				.map_err(|e| Error::Other(
170					format!("could not read file {}", e)
171				))?;
172		}
173
174		Ok(Self { inner: msg })
175	}
176
177	pub fn empty() -> Self {
178		Self { inner: Message::new() }
179	}
180
181	pub fn is_empty(&self) -> bool {
182		self.inner.body().len() == 0
183	}
184
185	pub fn file(&self) -> &[u8] {
186		self.inner.body().inner()
187	}
188
189	/// creates a hash of the file
190	pub fn hash(&self) -> Hash {
191		Hasher::hash(self.file())
192	}
193}
194
195impl<B> IntoMessage<Action, B> for GetFile<B>
196where B: PacketBytes {
197	fn into_message(self) -> StdResult<Message<Action, B>, MessageError> {
198		Ok(self.inner)
199	}
200}
201
202impl<B> FromMessage<Action, B> for GetFile<B>
203where B: PacketBytes {
204	fn from_message(msg: Message<Action, B>) -> StdResult<Self, MessageError> {
205		Ok(Self { inner: msg })
206	}
207}
208
209impl<B> Request for GetFileReq<B> {
210	type Action = Action;
211	type Response = GetFile<B>;
212	type Error = Error;
213
214	const ACTION: Action = Action::GetFile;
215}
216
217
218/// Get File Part
219///
220/// Can be accessed by anyone
221#[derive(Debug, Clone, Serialize, Deserialize)]
222#[serde(rename_all = "camelCase")]
223pub struct GetFilePartReq<B> {
224	pub hash: Hash,
225	pub start: u64,
226	// can be longer that the file itself the returned file will tell you how
227	// long it is
228	pub len: u64,
229	#[serde(skip)]
230	_bytes: PhantomData<B>
231}
232
233impl<B> GetFilePartReq<B> {
234	pub fn new(hash: Hash, start: u64, len: u64) -> Self {
235		Self {
236			hash, start, len,
237			_bytes: PhantomData
238		}
239	}
240}
241
242impl<B> IntoMessage<Action, B> for GetFilePartReq<B>
243where B: PacketBytes {
244	fn into_message(self) -> StdResult<Message<Action, B>, MessageError> {
245		stream_api::encdec::json::encode(self)
246	}
247}
248
249impl<B> FromMessage<Action, B> for GetFilePartReq<B>
250where B: PacketBytes {
251	fn from_message(msg: Message<Action, B>) -> StdResult<Self, MessageError> {
252		stream_api::encdec::json::decode(msg)
253	}
254}
255
256#[derive(Debug)]
257pub struct GetFilePart<B> {
258	// we keep the message so no new allocation is done
259	// +------------------------+
260	// |          Body          |
261	// +--------------+---------+
262	// |Total File Len|File Part|
263	// +--------------+---------+
264	// |     u64      |   ...   |
265	// +--------------+---------+
266	inner: Message<Action, B>
267}
268
269impl<B> GetFilePart<B>
270where B: PacketBytes {
271	fn new(msg: Message<Action, B>) -> Result<Self> {
272		// make sure the body is at least 8bytes long
273		if msg.body().len() < 8 {
274			return Err(Error::Request(
275				"GetFilePart expects at least 8bytes".into()
276			))
277		}
278
279		Ok(Self { inner: msg })
280	}
281
282	/// Before sending this response you should check the hash
283	pub async fn from_file(
284		mut file: File,
285		start: u64,
286		len: u64
287	) -> Result<Self> {
288		let mut msg = Message::new();
289
290		let total_file_len = file.metadata().await
291			.map_err(|e| Error::Internal(e.to_string()))?
292			.len();
293
294		// let's calculate how much we need to read
295		let rem_max_len = total_file_len.checked_sub(start)
296			.ok_or(Error::StartUnreachable)?;
297
298		let len = len.min(rem_max_len);
299
300		let mut body = msg.body_mut();
301		body.reserve((8 + len + 1) as usize);
302		body.write_u64(total_file_len);
303
304		if len == 0 {
305			return Ok(Self { inner: msg })
306		}
307
308		file.seek(SeekFrom::Start(start)).await
309			.map_err(|e| Error::Internal(
310				format!("seeking file failed {}", e)
311			))?;
312
313		// make sure only to read at max len
314		let mut file_reader = file.take(len);
315
316		unsafe {
317			// safe because file.read_to_end only appends
318			let v = body.as_mut_vec();
319			file_reader.read_to_end(v).await
320				.map_err(|e| Error::Internal(
321					format!("could not read file {}", e)
322				))?;
323		}
324
325		Ok(Self { inner: msg })
326	}
327
328	pub fn total_file_len(&self) -> u64 {
329		let mut body = self.inner.body();
330		body.read_u64()
331	}
332
333	pub fn file_part(&self) -> &[u8] {
334		let mut body = self.inner.body();
335		let _ = body.read_u64();
336		body.remaining_ref()
337	}
338}
339
340impl<B> IntoMessage<Action, B> for GetFilePart<B>
341where B: PacketBytes {
342	fn into_message(self) -> StdResult<Message<Action, B>, MessageError> {
343		Ok(self.inner)
344	}
345}
346
347impl<B> FromMessage<Action, B> for GetFilePart<B>
348where B: PacketBytes {
349	fn from_message(msg: Message<Action, B>) -> StdResult<Self, MessageError> {
350		Self::new(msg).map_err(|e| MessageError::Other(e.to_string().into()))
351	}
352}
353
354impl<B> Request for GetFilePartReq<B> {
355	type Action = Action;
356	type Response = GetFilePart<B>;
357	type Error = Error;
358
359	const ACTION: Action = Action::GetFilePart;
360}
361
362#[derive(Debug, Clone)]
363pub struct GetFileBuilder {
364	hash: Hash,
365	total_len: Option<u64>,
366	bytes: Vec<u8>,
367	// how big should each packet be
368	part_size: u64
369}
370
371impl GetFileBuilder {
372	/// part_size: how big should each part be, which we request
373	pub fn new(hash: Hash, part_size: u64) -> Self {
374		Self {
375			hash, part_size,
376			total_len: None,
377			bytes: vec![]
378		}
379	}
380
381	#[doc(hidden)]
382	pub fn next_req<B>(&self) -> GetFilePartReq<B> {
383		GetFilePartReq::new(
384			self.hash.clone(),
385			// start
386			self.bytes.len() as u64,
387			// len
388			self.part_size
389		)
390	}
391
392	#[doc(hidden)]
393	pub fn add_resp<B>(&mut self, resp: GetFilePart<B>)
394	where B: PacketBytes {
395		self.total_len = Some(resp.total_file_len());
396		self.bytes.extend_from_slice(resp.file_part());
397	}
398
399	pub fn is_complete(&self) -> bool {
400		self.total_len.map(|l| self.bytes.len() as u64 >= l)
401			.unwrap_or(false)
402	}
403
404	/// you need to make sure the file is complete
405	pub fn file(&self) -> &[u8] {
406		&self.bytes
407	}
408
409	/// creates a hash of the file
410	pub fn hash(&self) -> Hash {
411		Hasher::hash(self.file())
412	}
413}
414
415
416/// Set a file
417///
418/// Needs to be authenticated as a writer
419#[derive(Debug)]
420pub struct SetFileReq<B> {
421	signature: Signature,
422	// message contains signature + 
423	message: Message<Action, B>
424}
425
426impl<B> SetFileReq<B>
427where
428	B: PacketBytes
429{
430	pub async fn new(sign: Signature, mut file: File) -> Result<Self> {
431		let mut msg = Message::new();
432
433		// check how big the file is then allocate
434		let buf_size = file.metadata().await
435			.map(|m| m.len() as usize + 1)
436			.unwrap_or(0);
437
438		let mut body = msg.body_mut();
439		body.reserve(buf_size + Signature::LEN);
440
441		body.write(sign.to_bytes());
442
443		unsafe {
444			// safe because file.read_to_end only appends
445			let v = body.as_mut_vec();
446			file.read_to_end(v).await
447				.map_err(|e| Error::Other(
448					format!("could not read file {}", e)
449				))?;
450		}
451
452		Ok(Self {
453			signature: sign,
454			message: msg
455		})
456	}
457
458	pub fn from_bytes(sign: Signature, ctn: &[u8]) -> Self {
459		let mut msg = Message::new();
460
461		let mut body = msg.body_mut();
462		body.write(sign.to_bytes());
463		body.write(ctn);
464
465		Self {
466			signature: sign,
467			message: msg
468		}
469	}
470
471	pub fn signature(&self) -> &Signature {
472		&self.signature
473	}
474
475	/// creates a hash of the file
476	pub fn hash(&self) -> Hash {
477		Hasher::hash(self.file())
478	}
479
480	pub fn file(&self) -> &[u8] {
481		let body = self.message.body();
482		&body.inner()[Signature::LEN..]
483	}
484}
485
486impl<B> IntoMessage<Action, B> for SetFileReq<B>
487where
488	B: PacketBytes
489{
490	fn into_message(self) -> StdResult<Message<Action, B>, MessageError> {
491		Ok(self.message)
492	}
493}
494
495impl<B> FromMessage<Action, B> for SetFileReq<B>
496where
497	B: PacketBytes
498{
499	fn from_message(msg: Message<Action, B>) -> StdResult<Self, MessageError> {
500		if msg.body().len() <= Signature::LEN {
501			return Err(MessageError::Other("no signature".into()))
502		}
503
504		let sign = Signature::from_slice(
505			msg.body().read(Signature::LEN)
506		);
507
508		Ok(Self {
509			signature: sign,
510			message: msg
511		})
512	}
513}
514
515impl<B> Request for SetFileReq<B> {
516	type Action = Action;
517	type Response = EmptyJson;
518	type Error = Error;
519
520	const ACTION: Action = Action::SetFile;
521}
522
523
524pub type AuthKey = Token<32>;
525
526#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
527#[message(json)]
528pub struct AuthenticateReaderReq {
529	pub key: AuthKey
530}
531
532impl Request for AuthenticateReaderReq {
533	type Action = Action;
534	type Response = EmptyJson;
535	type Error = Error;
536
537	const ACTION: Action = Action::AuthenticateReader;
538}
539
540
541#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
542#[message(json)]
543pub struct AuthenticateWriter1Req {
544	pub channel: Channel
545}
546
547pub type Challenge = Token<32>;
548
549#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
550#[message(json)]
551pub struct AuthenticateWriter1 {
552	pub challenge: Challenge
553}
554
555impl Request for AuthenticateWriter1Req {
556	type Action = Action;
557	type Response = AuthenticateWriter1;
558	type Error = Error;
559
560	const ACTION: Action = Action::AuthenticateWriter1;
561}
562
563
564#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
565#[message(json)]
566pub struct AuthenticateWriter2Req {
567	pub signature: Signature
568}
569
570impl Request for AuthenticateWriter2Req {
571	type Action = Action;
572	type Response = EmptyJson;
573	type Error = Error;
574
575	const ACTION: Action = Action::AuthenticateWriter2;
576}
577
578
579/// Needs to be authenticated as a writer
580#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
581#[message(json)]
582pub struct NewAuthKeyReaderReq;
583
584#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
585#[message(json)]
586#[repr(transparent)]
587pub struct NewAuthKeyReader(pub AuthKey);
588
589impl Request for NewAuthKeyReaderReq {
590	type Action = Action;
591	type Response = NewAuthKeyReader;
592	type Error = Error;
593
594	const ACTION: Action = Action::NewAuthKeyReader;
595}
596
597
598/// Changewhitelist
599///
600/// Needs to be authenticated as a writer
601#[derive(Debug, Serialize, Deserialize, IntoMessage, FromMessage)]
602#[message(json)]
603pub struct ChangeWhitelistReq {
604	pub arch: TargetArch,
605	pub name: String,
606	pub version: Hash,
607	pub whitelist: HashSet<DeviceId>,
608	#[serde(default)]
609	pub add: bool,
610	/// if auto_whitelist_limit is > 0 and whitelist is empty the whitelist is
611	/// not touched even tough add might not be set
612	///
613	/// if the whitelist get's set and auto_whitelist_limit is == 0 it does not
614	/// get updated
615	#[serde(default)]
616	pub auto_whitelist_limit: u32
617}
618
619impl Request for ChangeWhitelistReq {
620	type Action = Action;
621	type Response = EmptyJson;
622	type Error = Error;
623
624	const ACTION: Action = Action::ChangeWhitelist;
625}