diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-01-17 11:11:27 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-01-17 16:48:28 +0100 |
commit | 3f58832474a4b270e136544016a401ef773ac065 (patch) | |
tree | c617160250c3040ca964c1b72ab5957cd872b82f /ffi/src | |
parent | 38b4108cc1eac851ac17932c5c33623dd535bebb (diff) |
openpgp-ffi: New crate.
- This creates a new crate, 'sequoia-openpgp-ffi', and moves a
handful of functions from 'sequoia-ffi' to it.
- The 'sequoia-ffi' crate is a superset of the 'sequoia-openpgp-ffi'
crate. This is accomplished by some include! magic.
- My first attempt involved having 'sequoia-ffi' depend on
'sequoia-openpgp-ffi', so that the former just re-exports the
symbols. However, that turned out to be unreliable, and might be
not what we want, because it could also duplicate parts of Rust's
standard library.
- Fixes #144.
Diffstat (limited to 'ffi/src')
-rw-r--r-- | ffi/src/core.rs | 175 | ||||
-rw-r--r-- | ffi/src/error.rs | 118 | ||||
-rw-r--r-- | ffi/src/lib.rs | 242 | ||||
-rw-r--r-- | ffi/src/openpgp/armor.rs | 347 | ||||
-rw-r--r-- | ffi/src/openpgp/crypto.rs | 54 | ||||
-rw-r--r-- | ffi/src/openpgp/fingerprint.rs | 110 | ||||
-rw-r--r-- | ffi/src/openpgp/keyid.rs | 99 | ||||
-rw-r--r-- | ffi/src/openpgp/mod.rs | 1637 | ||||
-rw-r--r-- | ffi/src/openpgp/packet_pile.rs | 93 | ||||
-rw-r--r-- | ffi/src/openpgp/tpk.rs | 698 | ||||
-rw-r--r-- | ffi/src/openpgp/tsk.rs | 76 |
11 files changed, 16 insertions, 3633 deletions
diff --git a/ffi/src/core.rs b/ffi/src/core.rs index 915dcffc..f644dc4b 100644 --- a/ffi/src/core.rs +++ b/ffi/src/core.rs @@ -39,15 +39,8 @@ //! ``` use failure; -use std::fs::File; -use std::io::{self, Read, Write, Cursor}; -use std::path::Path; use std::ptr; -use std::slice; -use libc::{uint8_t, c_void, c_char, c_int, size_t, ssize_t, realloc}; - -#[cfg(unix)] -use std::os::unix::io::FromRawFd; +use libc::{uint8_t, c_char, c_int}; use sequoia_core as core; use sequoia_core::Config; @@ -225,169 +218,3 @@ pub extern "system" fn sq_config_ephemeral(cfg: *mut Config) { let cfg = ffi_param_ref_mut!(cfg); cfg.set_ephemeral(); } - -/* Reader and writer. */ - -/// Opens a file returning a reader. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_reader_from_file(errp: Option<&mut *mut failure::Error>, - filename: *const c_char) - -> *mut Box<Read> { - ffi_make_fry_from_errp!(errp); - let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned(); - ffi_try_box!(File::open(Path::new(&filename)) - .map(|r| Box::new(r)) - .map_err(|e| e.into())) -} - -/// Opens a file descriptor returning a reader. -#[cfg(unix)] -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_reader_from_fd(fd: c_int) - -> *mut Box<Read> { - box_raw!(Box::new(unsafe { File::from_raw_fd(fd) })) -} - -/// Creates a reader from a buffer. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_reader_from_bytes(buf: *const uint8_t, - len: size_t) - -> *mut Box<Read> { - assert!(!buf.is_null()); - let buf = unsafe { - slice::from_raw_parts(buf, len as usize) - }; - box_raw!(Box::new(Cursor::new(buf))) -} - -/// Frees a reader. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_reader_free(reader: Option<&mut Box<Read>>) { - ffi_free!(reader) -} - -/// Reads up to `len` bytes into `buf`. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_reader_read(errp: Option<&mut *mut failure::Error>, - reader: *mut Box<Read>, - buf: *mut uint8_t, len: size_t) - -> ssize_t { - ffi_make_fry_from_errp!(errp); - let reader = ffi_param_ref_mut!(reader); - assert!(!buf.is_null()); - let buf = unsafe { - slice::from_raw_parts_mut(buf, len as usize) - }; - ffi_try_or!(reader.read(buf).map_err(|e| e.into()), -1) as ssize_t -} - - -/// Opens a file returning a writer. -/// -/// The file will be created if it does not exist, or be truncated -/// otherwise. If you need more control, use `sq_writer_from_fd`. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_from_file(errp: Option<&mut *mut failure::Error>, - filename: *const c_char) - -> *mut Box<Write> { - ffi_make_fry_from_errp!(errp); - let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned(); - ffi_try_box!(File::create(Path::new(&filename)) - .map(|r| Box::new(r)) - .map_err(|e| e.into())) -} - -/// Opens a file descriptor returning a writer. -#[cfg(unix)] -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_from_fd(fd: c_int) - -> *mut Box<Write> { - box_raw!(Box::new(unsafe { File::from_raw_fd(fd) })) -} - -/// Creates a writer from a buffer. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_from_bytes(buf: *mut uint8_t, - len: size_t) - -> *mut Box<Write> { - assert!(!buf.is_null()); - let buf = unsafe { - slice::from_raw_parts_mut(buf, len as usize) - }; - box_raw!(Box::new(Cursor::new(buf))) -} - -/// Creates an allocating writer. -/// -/// This writer allocates memory using `malloc`, and stores the -/// pointer to the memory and the number of bytes written to the given -/// locations `buf`, and `len`. Both must either be set to zero, or -/// reference a chunk of memory allocated using libc's heap allocator. -/// The caller is responsible to `free` it once the writer has been -/// destroyed. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_alloc(buf: *mut *mut c_void, - len: *mut size_t) - -> *mut Box<Write> { - let buf = ffi_param_ref_mut!(buf); - let len = ffi_param_ref_mut!(len); - - box_raw!(Box::new(WriterAlloc { - buf: buf, - len: len, - })) -} - -struct WriterAlloc { - buf: &'static mut *mut c_void, - len: &'static mut size_t, -} - -impl Write for WriterAlloc { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - let old_len = *self.len; - let new_len = old_len + buf.len(); - - let new = unsafe { - realloc(*self.buf, new_len) - }; - if new.is_null() { - return Err(io::Error::new(io::ErrorKind::Other, "out of memory")); - } - - *self.buf = new; - *self.len = new_len; - - let sl = unsafe { - slice::from_raw_parts_mut(new as *mut u8, new_len) - }; - &mut sl[old_len..].copy_from_slice(buf); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - // Do nothing. - Ok(()) - } -} - -/// Frees a writer. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_free(writer: Option<&mut Box<Write>>) { - ffi_free!(writer) -} - -/// Writes up to `len` bytes of `buf` into `writer`. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_writer_write(errp: Option<&mut *mut failure::Error>, - writer: *mut Box<Write>, - buf: *const uint8_t, len: size_t) - -> ssize_t { - ffi_make_fry_from_errp!(errp); - let writer = ffi_param_ref_mut!(writer); - assert!(!buf.is_null()); - let buf = unsafe { - slice::from_raw_parts(buf, len as usize) - }; - ffi_try_or!(writer.write(buf).map_err(|e| e.into()), -1) as ssize_t -} diff --git a/ffi/src/error.rs b/ffi/src/error.rs index 369cbb25..d9fb2c21 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -2,125 +2,17 @@ use failure; use std::io; -use libc::c_char; extern crate sequoia_openpgp as openpgp; use sequoia_core as core; +pub use openpgp::error::Status; -/// Frees an error. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_error_free(error: Option<&mut failure::Error>) { - ffi_free!(error) +trait FromSequoiaError<'a> { + fn from_sequoia_error(&'a failure::Error) -> Status; } -/// Returns the error message. -/// -/// The returned value must be freed with `free(3)`. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_error_string(error: *const failure::Error) - -> *mut c_char { - let error = ffi_param_ref!(error); - ffi_return_string!(&format!("{}", error)) -} - -/// Returns the error status code. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_error_status(error: *const failure::Error) - -> Status { - let error = ffi_param_ref!(error); - error.into() -} - -/// XXX: Reorder and name-space before release. -#[derive(PartialEq, Debug)] -#[repr(C)] -pub enum Status { - /// The operation was successful. - Success = 0, - - /// An unknown error occurred. - UnknownError = -1, - - /// The network policy was violated by the given action. - NetworkPolicyViolation = -2, - - /// An IO error occurred. - IoError = -3, - - /// A given argument is invalid. - InvalidArgument = -15, - - /// The requested operation is invalid. - InvalidOperation = -4, - - /// The packet is malformed. - MalformedPacket = -5, - - /// Unsupported hash algorithm. - UnsupportedHashAlgorithm = -9, - - /// Unsupported public key algorithm. - UnsupportedPublicKeyAlgorithm = -18, - - /// Unsupported elliptic curve. - UnsupportedEllipticCurve = -21, - - /// Unsupported symmetric algorithm. - UnsupportedSymmetricAlgorithm = -10, - - /// Unsupported AEAD algorithm. - UnsupportedAEADAlgorithm = -26, - - /// Unsupport signature type. - UnsupportedSignatureType = -20, - - /// Invalid password. - InvalidPassword = -11, - - /// Invalid session key. - InvalidSessionKey = -12, - - /// Missing session key. - MissingSessionKey = -27, - - /// Malformed TPK. - MalformedTPK = -13, - - // XXX: -14 was UserIDNotFound. - - // XXX: Skipping InvalidArgument = -15. - - /// Malformed MPI. - MalformedMPI = -16, - - // XXX: Skipping UnknownPublicKeyAlgorithm = -17. - // XXX: Skipping UnsupportedPublicKeyAlgorithm = -18 - - /// Bad signature. - BadSignature = -19, - - /// Message has been manipulated. - ManipulatedMessage = -25, - - // XXX: Skipping UnsupportedSignatureType = -20 - // XXX: Skipping UnsupportedEllipticCurve = -21 - - /// Malformed message. - MalformedMessage = -22, - - /// Index out of range. - IndexOutOfRange = -23, - - /// TPK not supported. - UnsupportedTPK = -24, - - // XXX: Skipping ManipulatedMessage = -25 - // XXX: Skipping UnsupportedAEADAlgorithm = -26 - // XXX: Skipping MissingSessionKey = -27 -} - -impl<'a> From<&'a failure::Error> for Status { - fn from(e: &'a failure::Error) -> Self { +impl<'a> FromSequoiaError<'a> for Status { + fn from_sequoia_error(e: &'a failure::Error) -> Self { if let Some(e) = e.downcast_ref::<core::Error>() { return match e { &core::Error::NetworkPolicyViolation(_) => diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 2f5f3bbe..60742680 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -119,136 +119,16 @@ extern crate sequoia_core; extern crate sequoia_net; extern crate sequoia_store; -use std::collections::hash_map::{DefaultHasher, RandomState}; -use std::hash::BuildHasher; - -/* Canonical free(). */ - -/// Transfers ownership from C to Rust, then frees the object. -/// -/// NOP if called with NULL. -macro_rules! ffi_free { - ($name:ident) => {{ - if let Some(ptr) = $name { - unsafe { - drop(Box::from_raw(ptr)) - } - } - }}; -} - -/* Parameter handling. */ - -/// Transfers ownership from C to Rust. -/// -/// # Panics -/// -/// Panics if called with NULL. -macro_rules! ffi_param_move { - ($name:expr) => {{ - if $name.is_null() { - panic!("Parameter {} is NULL", stringify!($name)); - } - unsafe { - Box::from_raw($name) - } - }}; -} - -/// Transfers a reference from C to Rust. -/// -/// # Panics -/// -/// Panics if called with NULL. -macro_rules! ffi_param_ref { - ($name:ident) => {{ - if $name.is_null() { - panic!("Parameter {} is NULL", stringify!($name)); - } - unsafe { - &*$name - } - }}; -} - -/// Transfers a mutable reference from C to Rust. -/// -/// # Panics -/// -/// Panics if called with NULL. -macro_rules! ffi_param_ref_mut { - ($name:ident) => {{ - if $name.is_null() { - panic!("Parameter {} is NULL", stringify!($name)); - } - unsafe { - &mut *$name - } - }}; -} - -/// Transfers a reference to a string from C to Rust. -/// -/// # Panics -/// -/// Panics if called with NULL. -macro_rules! ffi_param_cstr { - ($name:expr) => {{ - if $name.is_null() { - panic!("Parameter {} is NULL", stringify!($name)); - } - unsafe { - ::std::ffi::CStr::from_ptr($name) - } - }}; -} - -/* Return value handling. */ - -/// Duplicates a string similar to strndup(3). -fn strndup(src: &[u8]) -> Option<*mut libc::c_char> { - if src.contains(&0) { - return None; - } - - let l = src.len() + 1; - let s = unsafe { - ::std::slice::from_raw_parts_mut(libc::malloc(l) as *mut u8, l) - }; - &mut s[..l - 1].copy_from_slice(src); - s[l - 1] = 0; - - Some(s.as_mut_ptr() as *mut libc::c_char) -} - -/// Transfers a string from Rust to C, allocating it using malloc. -/// -/// # Panics -/// -/// Panics if the given string contains a 0. -macro_rules! ffi_return_string { - ($name:expr) => {{ - let string = $name; - let bytes: &[u8] = string.as_ref(); - ::strndup(bytes).expect( - &format!("Returned string {} contains a 0 byte.", stringify!($name)) - ) - }}; +#[macro_use] +pub mod openpgp { + //! Bindings for the low-level openpgp crate. + include!("../../openpgp-ffi/src/common.rs"); } -/// Transfers a string from Rust to C, allocating it using malloc. -/// -/// # Panics -/// -/// Does *NOT* panic if the given string contains a 0, but returns -/// `NULL`. -macro_rules! ffi_return_maybe_string { - ($name:expr) => {{ - let string = $name; - let bytes: &[u8] = string.as_ref(); - ::strndup(bytes).unwrap_or(::std::ptr::null_mut()) - }}; -} +pub(crate) use openpgp::{ + build_hasher, + strndup, +}; /* Error handling with implicit context. */ @@ -260,110 +140,8 @@ macro_rules! ffi_make_fry_from_ctx { } } -/* Error handling with implicit error return argument. */ - -/// Emits local macros for error handling that use the given context -/// to store complex errors. -macro_rules! ffi_make_fry_from_errp { - ($errp:expr) => { - /// Like try! for ffi glue. - /// - /// Evaluates the given expression. On success, evaluate to - /// `Status.Success`. On failure, stashes the error in the - /// context and evaluates to the appropriate Status code. - #[allow(unused_macros)] - macro_rules! ffi_try_status { - ($expr:expr) => { - match $expr { - Ok(_) => Status::Success, - Err(e) => { - let status = Status::from(&e); - if let Some(errp) = $errp { - *errp = box_raw!(e); - } - status - }, - } - }; - } - - /// Like try! for ffi glue. - /// - /// Unwraps the given expression. On failure, stashes the - /// error in the context and returns $or. - #[allow(unused_macros)] - macro_rules! ffi_try_or { - ($expr:expr, $or:expr) => { - match $expr { - Ok(v) => v, - Err(e) => { - if let Some(errp) = $errp { - *errp = box_raw!(e); - } - return $or; - }, - } - }; - } - - /// Like try! for ffi glue. - /// - /// Unwraps the given expression. On failure, stashes the - /// error in the context and returns NULL. - #[allow(unused_macros)] - macro_rules! ffi_try { - ($expr:expr) => { - ffi_try_or!($expr, ::std::ptr::null_mut()) - }; - } - - /// Like try! for ffi glue, then box into raw pointer. - /// - /// This is used to transfer ownership from Rust to C. - /// - /// Unwraps the given expression. On success, it boxes the - /// value and turns it into a raw pointer. On failure, - /// stashes the error in the context and returns NULL. - #[allow(unused_macros)] - macro_rules! ffi_try_box { - ($expr:expr) => { - Box::into_raw(Box::new(ffi_try!($expr))) - } - } - } -} - -/// Box, then turn into raw pointer. -/// -/// This is used to transfer ownership from Rust to C. -macro_rules! box_raw { - ($expr:expr) => { - Box::into_raw(Box::new($expr)) - } -} - -/// Box an Option<T>, then turn into raw pointer. -/// -/// This is used to transfer ownership from Rust to C. -macro_rules! maybe_box_raw { - ($expr:expr) => { - $expr.map(|x| box_raw!(x)).unwrap_or(ptr::null_mut()) - } -} - -/// Builds hashers for computing hashes. -/// -/// This is used to derive Hasher instances for computing hashes of -/// objects so that they can be used in hash tables by foreign code. -pub(crate) fn build_hasher() -> DefaultHasher { - lazy_static! { - static ref RANDOM_STATE: RandomState = RandomState::new(); - } - RANDOM_STATE.build_hasher() -} - -pub mod error; pub mod core; -pub mod openpgp; +pub mod error; pub mod net; pub mod store; + diff --git a/ffi/src/openpgp/armor.rs b/ffi/src/openpgp/armor.rs deleted file mode 100644 index 1a42c369..00000000 --- a/ffi/src/openpgp/armor.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! ASCII Armor. -//! -//! Wraps [`sequoia-openpgp::armor`]. -//! -//! [`sequoia-openpgp::armor`]: ../../../sequoia_openpgp/armor/index.html - -use std::mem::size_of; -use std::ptr; -use std::slice; -use std::io::{Read, Write}; -use libc::{self, uint8_t, c_char, c_int, size_t}; - -extern crate sequoia_openpgp; -use self::sequoia_openpgp::armor; - -/// Represents a (key, value) pair in an armor header. -#[repr(C)] -pub struct ArmorHeader { - key: *const c_char, - value: *const c_char, -} - -fn int_to_kind(kind: c_int) -> Option<armor::Kind> { - match kind { - 0 => None, - 1 => Some(armor::Kind::Message), - 2 => Some(armor::Kind::PublicKey), - 3 => Some(armor::Kind::SecretKey), - 4 => Some(armor::Kind::Signature), - 5 => Some(armor::Kind::File), - _ => panic!("Bad kind: {}", kind), - } -} - -fn kind_to_int(kind: Option<armor::Kind>) -> c_int { - match kind { - None => 0, - Some(armor::Kind::Message) => 1, - Some(armor::Kind::PublicKey) => 2, - Some(armor::Kind::SecretKey) => 3, - Some(armor::Kind::Signature) => 4, - Some(armor::Kind::File) => 5, - } -} - -/// Constructs a new filter for the given type of data. -/// -/// A filter that strips ASCII Armor from a stream of data. -/// -/// # Example -/// -/// ```c -/// #define _GNU_SOURCE -/// #include <assert.h> -/// #include <error.h> -/// #include <stdio.h> -/// #include <stdlib.h> -/// #include <string.h> -/// -/// #include <sequoia.h> -/// -/// const char *armored = -/// "-----BEGIN PGP ARMORED FILE-----\n" -/// "Key0: Value0\n" -/// "Key1: Value1\n" -/// "\n" -/// "SGVsbG8gd29ybGQh\n" -/// "=s4Gu\n" -/// "-----END PGP ARMORED FILE-----\n"; -/// -/// int -/// main (int argc, char **argv) -/// { -/// sq_error_t err; -/// sq_reader_t bytes; -/// sq_reader_t armor; -/// sq_armor_kind_t kind; -/// char message[12]; -/// sq_armor_header_t *header; -/// size_t header_len; -/// -/// bytes = sq_reader_from_bytes ((uint8_t *) armored, strlen (armored)); -/// armor = sq_armor_reader_new (bytes, SQ_ARMOR_KIND_ANY); -/// -/// header = sq_armor_reader_headers (&err, armor, &header_len); -/// if (header == NULL) -/// error (1, 0, "Getting headers failed: %s", sq_error_string (err)); -/// -/// assert (header_len == 2); -/// assert (strcmp (header[0].key, "Key0") == 0 -/// && strcmp (header[0].value, "Value0") == 0); -/// assert (strcmp (header[1].key, "Key1") == 0 -/// && strcmp (header[1].value, "Value1") == 0); -/// for (size_t i = 0; i < header_len; i++) -/// { -/// free (header[i].key); -/// free (header[i].value); -/// } -/// free (header); -/// -/// kind = sq_armor_reader_kind (armor); -/// assert (kind == SQ_ARMOR_KIND_FILE); -/// -/// if (sq_reader_read (&err, armor, (uint8_t *) message, 12) < 0) -/// error (1, 0, "Reading failed: %s", sq_error_string (err)); -/// -/// assert (memcmp (message, "Hello world!", 12) == 0); -/// -/// sq_reader_free (armor); -/// sq_reader_free (bytes); -/// return 0; -/// } -/// ``` -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_armor_reader_new(inner: *mut Box<Read>, - kind: c_int) - -> *mut Box<Read> { - let inner = ffi_param_ref_mut!(inner); - let kind = int_to_kind(kind); - - box_raw!(Box::new(armor::Reader::new(inner, kind))) -} - -/// Creates a `Reader` from a file. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_armor_reader_from_file(errp: Option<&mut *mut failure::Error>, - filename: *const c_char, - kind: c_int) - -> *mut Box<Read> { - ffi_make_fry_from_errp!(errp); - let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned(); - let kind = int_to_kind(kind); - - ffi_try_box!(armor::Reader::from_file(&filename, kind) - .map(|r| Box::new(r)) - .map_err(|e| e.into())) -} - -/// Creates a `Reader` from a buffer. -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_armor_reader_from_bytes(b: *const uint8_t, len: size_t, - kind: c_int) - -> *mut Box<Read> { - assert!(!b.is_null()); - let buf = unsafe { - slice::from_raw_parts(b, len as usize) - }; - let kind = int_to_kind(kind); - - box_raw!(Box::new(armor::Reader::from_bytes(buf, kind))) -} - -/// Returns the kind of data this reader is for. -/// -/// Useful if the kind of data is not known in advance. If the header -/// has not been encountered yet (try reading some data first!), this -/// function returns SQ_ARMOR_KIND_ANY. -/// -/// # Example -/// -/// See [this] example. -/// -/// [this]: fn.sq_armor_reader_new.html -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_armor_reader_kind(reader: *mut Box<Read>) - -> c_int { - // We need to downcast `reader`. To do that, we need to do a - // little dance. We will momentarily take ownership of `reader`, - // wrapping it in a Box again. Then, at the end of the function, - // we will leak it again. - let reader = ffi_param_move!(reader as *mut Box<armor::Reader>); - let kind = kind_to_int(reader.kind()); - Box::into_raw(reader); - kind -} - -/// Returns the armored headers. -/// -/// The tuples contain a key and a value. -/// -/// Note: if a key occurs multiple times, then there are multiple -/// entries in the vector with the same key; values with the same -/// key are *not* combined. -/// -/// The returned array and the strings in the headers have been -/// allocated with `malloc`, and the caller is responsible for freeing -/// both the array and the strings. -/// -/// # Example -/// -/// See [this] example. -/// -/// [this]: fn.sq_armor_reader_new.html -#[::ffi_catch_abort] #[no_mangle] -pub extern "system" fn sq_armor_reader_headers(errp: Option<&mut *mut failure::Error>, - reader: *mut Box<Read>, - len: *mut size_t) - -> *mut ArmorHeader { - ffi_make_fry_from_errp!(errp); - let len = ffi_param_ref_mut!(len); - - // We need to downcast `reade |