summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2022-12-07 14:03:44 +0100
committerJustus Winter <justus@sequoia-pgp.org>2022-12-07 15:33:30 +0100
commit0aa8d50df60eec2e70cc552ba9e4e7d98693e6d1 (patch)
treea51a3bbba794e9f89b4769fffe7538ea9e15e922
parent442cb9ccad926993e29713a62b6acd75a2b47813 (diff)
ipc: Add a convenience function to send simple commands.
-rw-r--r--ipc/src/assuan/mod.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/ipc/src/assuan/mod.rs b/ipc/src/assuan/mod.rs
index e1c32afc..51cd0c87 100644
--- a/ipc/src/assuan/mod.rs
+++ b/ipc/src/assuan/mod.rs
@@ -16,6 +16,7 @@ use tokio::io::{BufReader, ReadHalf, WriteHalf};
use tokio::io::{AsyncRead, AsyncWriteExt};
use crate::openpgp;
+use openpgp::crypto::mem::Protected;
use crate::Error;
use crate::Result;
@@ -152,6 +153,46 @@ impl Client {
Ok(())
}
+ /// Sends a simple command to the server and returns the response.
+ ///
+ /// This method can only be used with simple commands, i.e. those
+ /// which do not require handling inquiries from the server. To
+ /// send complex commands, use [`Client::send`] and handle the
+ /// inquiries.
+ pub async fn send_simple<C>(&mut self, cmd: C) -> Result<Protected>
+ where
+ C: AsRef<str>,
+ {
+ self.send(cmd.as_ref())?;
+ let mut data = Vec::new();
+ let mut ok = false;
+ while let Some(response) = self.next().await {
+ match response? {
+ Response::Data { partial } => {
+ // Securely erase partial.
+ let partial = Protected::from(partial);
+ data.extend_from_slice(&partial);
+ },
+ Response::Ok { .. } =>
+ ok = true,
+ Response::Comment { .. }
+ | Response::Status { .. } =>
+ (), // Ignore.
+ Response::Error { ref message, .. } =>
+ return operation_failed(self, message).await,
+ response =>
+ return protocol_error(&response),
+ }
+ }
+
+ if ok {
+ Ok(data.into())
+ } else {
+ Err(crate::gnupg::Error::ProtocolError(
+ "Got neither success nor error".into()).into())
+ }
+ }
+
/// Lazily cancels a pending operation.
///
/// For the command to be actually executed, stream the responses
@@ -208,6 +249,36 @@ impl Client {
}
}
+/// Returns a convenient Err value for use in the state machines.
+///
+/// This function must only be called after the assuan server returns
+/// an ERR. message is the error message returned from the server.
+/// This function first checks that the server hasn't sent anything
+/// else, which would be a protocol violation. If that is not the
+/// case, it turns the message into an Err.
+// XXX: It is a slight layering violation to return gnupg::Error here.
+pub(crate) async fn operation_failed<T>(agent: &mut Client,
+ message: &Option<String>)
+ -> Result<T>
+{
+ if let Some(response) = agent.next().await {
+ protocol_error(&response?)
+ } else {
+ Err(crate::gnupg::Error::OperationFailed(
+ message.as_ref().map(|e| e.to_string())
+ .unwrap_or_else(|| "Unknown reason".into()))
+ .into())
+ }
+}
+
+/// Returns a convenient Err value for use in the state machines.
+// XXX: It is a slight layering violation to return gnupg::Error here.
+pub(crate) fn protocol_error<T>(response: &Response) -> Result<T> {
+ Err(crate::gnupg::Error::ProtocolError(
+ format!("Got unexpected response {:?}", response))
+ .into())
+}
+
/// A future that will resolve to a `Client`.
struct ConnectionFuture(Option<Client>);