iron_os_packages_api/
client.rs

1use crate::action::Action;
2use crate::error::{Result, Error};
3use crate::packages::{Channel, Package, BoardArch, TargetArch};
4use crate::requests::{
5	PackageInfoReq, DeviceId,
6	SetPackageInfoReq,
7	GetFileReq, GetFile,
8	GetFileBuilder,
9	SetFileReq,
10	AuthKey, AuthenticateReaderReq,
11	AuthenticateWriter1Req, AuthenticateWriter2Req,
12	NewAuthKeyReaderReq,
13	ChangeWhitelistReq
14};
15
16use std::time::Duration;
17use std::collections::HashSet;
18
19use stream_api::client::{Config, Client as StreamClient, EncryptedBytes};
20
21use crypto::signature::{PublicKey, Keypair};
22use crypto::hash::Hash;
23
24use tokio::net::{TcpStream, ToSocketAddrs};
25
26const TIMEOUT: Duration = Duration::from_secs(10);
27
28
29pub struct Client {
30	inner: StreamClient<Action, EncryptedBytes>
31}
32
33impl Client {
34	pub async fn connect<A>(addr: A, pub_key: PublicKey) -> Result<Self>
35	where A: ToSocketAddrs {
36		let stream = TcpStream::connect(addr).await
37			.map_err(|e| Error::Other(format!("could not connect {}", e)))?;
38		Ok(Self {
39			inner: StreamClient::<_, EncryptedBytes>::new_encrypted(
40				stream,
41				Config {
42					timeout: TIMEOUT,
43					body_limit: 0
44				},
45				None,
46				pub_key
47			)
48		})
49	}
50
51	/// can be called by anyone
52	pub async fn package_info(
53		&self,
54		channel: Channel,
55		arch: BoardArch,
56		device_id: Option<DeviceId>,
57		name: String
58	) -> Result<Option<Package>> {
59		let req = PackageInfoReq { channel, arch, name, device_id };
60		self.inner.request(req).await
61			.map(|r| r.package)
62	}
63
64	/// can only be called if you authenticated as a writer
65	pub async fn set_package_info(
66		&self,
67		package: Package,
68		whitelist: HashSet<DeviceId>,
69		auto_whitelist_limit: u32
70	) -> Result<()> {
71		let req = SetPackageInfoReq {
72			package, whitelist, auto_whitelist_limit
73		};
74		self.inner.request(req).await
75			.map(|_r| ())
76	}
77
78	/// can be called by anyone
79	/// does not return FileNotFound
80	pub async fn get_file(
81		&self,
82		hash: Hash
83	) -> Result<GetFile<EncryptedBytes>> {
84		let req = GetFileReq::new(hash);
85		self.inner.request(req).await
86	}
87
88	/// If this function returns Ok(())
89	/// and the builder is not completed you can call this function again
90	/// immediately
91	/// 
92	/// can return FileNotFound
93	pub async fn get_file_with_builder(
94		&self,
95		builder: &mut GetFileBuilder
96	) -> Result<()> {
97		let r = builder.next_req();
98		let resp = self.inner.request(r).await?;
99		builder.add_resp(resp);
100
101		Ok(())
102	}
103
104	/// you need to be authentiacated as a writer
105	pub async fn set_file(
106		&self,
107		req: SetFileReq<EncryptedBytes>
108	) -> Result<()> {
109		self.inner.request(req).await
110			.map(|_| ())
111	}
112
113	/// authenticate as reader
114	pub async fn authenticate_reader(&self, key: AuthKey) -> Result<()> {
115		self.inner.request(AuthenticateReaderReq { key }).await
116			.map(|_| ())
117	}
118
119	/// authenticate as writer
120	pub async fn authenticate_writer(
121		&self,
122		channel: &Channel,
123		key: &Keypair
124	) -> Result<()> {
125		let resp = self.inner.request(AuthenticateWriter1Req {
126			channel: *channel
127		}).await?;
128		self.inner.request(AuthenticateWriter2Req {
129			signature: key.sign(&resp.challenge)
130		}).await
131			.map(|_| ())
132	}
133
134	/// need to be authenticate as a writer
135	pub async fn new_auth_key_reader(&self) -> Result<AuthKey> {
136		self.inner.request(NewAuthKeyReaderReq).await
137			.map(|r| r.0)
138	}
139
140	/// Allows to change the whitelist. The whitelist can either be replaced
141	/// or can be additive.
142	/// 
143	/// ## Auth
144	/// need to be authenticate as a writer.
145	pub async fn change_whitelist(
146		&self,
147		arch: TargetArch,
148		name: String,
149		version: Hash,
150		whitelist: HashSet<DeviceId>,
151		// if the whitelist should added or replaced
152		add: bool,
153		auto_whitelist_limit: u32
154	) -> Result<()> {
155		self.inner.request(ChangeWhitelistReq {
156			arch, name, version, whitelist, add,
157			auto_whitelist_limit
158		}).await
159			.map(|_| ())
160	}
161
162	pub async fn close(self) {
163		let _ = self.inner.close().await;
164	}
165}