1 //! ASCII Armor.
2 //!
3 //! Wraps [`sequoia-openpgp::armor`].
4 //!
5 //! [`sequoia-openpgp::armor`]: ../../../sequoia_openpgp/armor/index.html
6
7 use std::mem::size_of;
8 use std::ptr;
9 use std::slice;
10 use libc::{self, c_char, c_int, size_t};
11
12 extern crate sequoia_openpgp;
13 use self::sequoia_openpgp::armor;
14
15 use super::io::{Reader, ReaderKind, WriterKind};
16 use crate::Maybe;
17 use crate::MoveFromRaw;
18 use crate::MoveIntoRaw;
19 use crate::MoveResultIntoRaw;
20 use crate::RefRaw;
21 use crate::RefMutRaw;
22
23 /// Represents a (key, value) pair in an armor header.
24 #[repr(C)]
25 pub struct ArmorHeader {
26 key: *const c_char,
27 value: *const c_char,
28 }
29
int_to_kind(kind: c_int) -> Option<armor::Kind>30 fn int_to_kind(kind: c_int) -> Option<armor::Kind> {
31 match kind {
32 0 => None,
33 1 => Some(armor::Kind::Message),
34 2 => Some(armor::Kind::PublicKey),
35 3 => Some(armor::Kind::SecretKey),
36 4 => Some(armor::Kind::Signature),
37 5 => Some(armor::Kind::File),
38 _ => panic!("Bad kind: {}", kind),
39 }
40 }
41
kind_to_int(kind: Option<armor::Kind>) -> c_int42 fn kind_to_int(kind: Option<armor::Kind>) -> c_int {
43 match kind {
44 None => 0,
45 Some(armor::Kind::Message) => 1,
46 Some(armor::Kind::PublicKey) => 2,
47 Some(armor::Kind::SecretKey) => 3,
48 Some(armor::Kind::Signature) => 4,
49 Some(armor::Kind::File) => 5,
50 }
51 }
52
int_to_reader_mode(mode: c_int) -> armor::ReaderMode53 fn int_to_reader_mode(mode: c_int) -> armor::ReaderMode {
54 match mode {
55 -1 => armor::ReaderMode::VeryTolerant,
56 _ => armor::ReaderMode::Tolerant(int_to_kind(mode)),
57 }
58 }
59
60 // fn reader_mode_to_int(mode: armor::ReaderMode) -> c_int {
61 // match mode {
62 // armor::ReaderMode::VeryTolerant => -1,
63 // armor::ReaderMode::Tolerant(kind) => kind_to_int(kind),
64 // }
65 // }
66
67 /// Constructs a new filter for the given type of data.
68 ///
69 /// A filter that strips ASCII Armor from a stream of data.
70 ///
71 /// # Example
72 ///
73 /// ```c
74 /// #include <assert.h>
75 /// #include <error.h>
76 /// #include <stdio.h>
77 /// #include <stdlib.h>
78 /// #include <string.h>
79 ///
80 /// #include <sequoia/openpgp.h>
81 ///
82 /// const char *armored =
83 /// "-----BEGIN PGP ARMORED FILE-----\n"
84 /// "Key0: Value0\n"
85 /// "Key1: Value1\n"
86 /// "\n"
87 /// "SGVsbG8gd29ybGQh\n"
88 /// "=s4Gu\n"
89 /// "-----END PGP ARMORED FILE-----\n";
90 ///
91 /// pgp_reader_t bytes = pgp_reader_from_bytes ((uint8_t *) armored, strlen (armored));
92 /// pgp_reader_t armor = pgp_armor_reader_new (bytes, PGP_ARMOR_KIND_ANY);
93 ///
94 /// pgp_error_t err;
95 /// pgp_armor_header_t header;
96 /// size_t header_len;
97 /// header = pgp_armor_reader_headers (&err, armor, &header_len);
98 /// if (header == NULL)
99 /// error (1, 0, "Getting headers failed: %s", pgp_error_to_string (err));
100 ///
101 /// assert (header_len == 2);
102 /// assert (strcmp (header[0].key, "Key0") == 0
103 /// && strcmp (header[0].value, "Value0") == 0);
104 /// assert (strcmp (header[1].key, "Key1") == 0
105 /// && strcmp (header[1].value, "Value1") == 0);
106 /// for (size_t i = 0; i < header_len; i++)
107 /// {
108 /// free (header[i].key);
109 /// free (header[i].value);
110 /// }
111 /// free (header);
112 ///
113 /// char message[12];
114 /// if (pgp_reader_read (&err, armor, (uint8_t *) message, 12) < 0)
115 /// error (1, 0, "Reading failed: %s", pgp_error_to_string (err));
116 ///
117 /// assert (pgp_armor_reader_kind (armor) == PGP_ARMOR_KIND_FILE);
118 /// assert (memcmp (message, "Hello world!", 12) == 0);
119 ///
120 /// pgp_reader_free (armor);
121 /// pgp_reader_free (bytes);
122 /// ```
123 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_new(inner: *mut Reader, mode: c_int) -> *mut Reader124 pub extern "C" fn pgp_armor_reader_new(inner: *mut Reader,
125 mode: c_int)
126 -> *mut Reader
127 {
128 let inner = inner.ref_mut_raw();
129 let mode = int_to_reader_mode(mode);
130
131 ReaderKind::Armored(armor::Reader::new(inner, mode)).move_into_raw()
132 }
133
134 /// Creates a `Reader` from a file.
135 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_from_file(errp: Option<&mut *mut crate::error::Error>, filename: *const c_char, mode: c_int) -> Maybe<Reader>136 pub extern "C" fn pgp_armor_reader_from_file(errp: Option<&mut *mut crate::error::Error>,
137 filename: *const c_char,
138 mode: c_int)
139 -> Maybe<Reader>
140 {
141 let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned();
142 let mode = int_to_reader_mode(mode);
143
144 armor::Reader::from_file(&filename, mode)
145 .map(|r| ReaderKind::Armored(r))
146 .map_err(|e| ::anyhow::Error::from(e))
147 .move_into_raw(errp)
148 }
149
150 /// Creates a `Reader` from a buffer.
151 ///
152 /// # Example
153 ///
154 /// ```c
155 /// #include <assert.h>
156 /// #include <error.h>
157 /// #include <stdio.h>
158 /// #include <stdlib.h>
159 /// #include <string.h>
160 ///
161 /// #include <sequoia/openpgp.h>
162 ///
163 /// const char *armored =
164 /// "-----BEGIN PGP ARMORED FILE-----\n"
165 /// "Key0: Value0\n"
166 /// "Key1: Value1\n"
167 /// "\n"
168 /// "SGVsbG8gd29ybGQh\n"
169 /// "=s4Gu\n"
170 /// "-----END PGP ARMORED FILE-----\n";
171 ///
172 /// pgp_reader_t armor =
173 /// pgp_armor_reader_from_bytes ((uint8_t *) armored, strlen (armored),
174 /// PGP_ARMOR_KIND_ANY);
175 ///
176 /// pgp_error_t err;
177 /// pgp_armor_header_t header;
178 /// size_t header_len;
179 /// header = pgp_armor_reader_headers (&err, armor, &header_len);
180 /// if (header == NULL)
181 /// error (1, 0, "Getting headers failed: %s", pgp_error_to_string (err));
182 ///
183 /// assert (header_len == 2);
184 /// assert (strcmp (header[0].key, "Key0") == 0
185 /// && strcmp (header[0].value, "Value0") == 0);
186 /// assert (strcmp (header[1].key, "Key1") == 0
187 /// && strcmp (header[1].value, "Value1") == 0);
188 /// for (size_t i = 0; i < header_len; i++)
189 /// {
190 /// free (header[i].key);
191 /// free (header[i].value);
192 /// }
193 /// free (header);
194 ///
195 /// char message[12];
196 /// if (pgp_reader_read (&err, armor, (uint8_t *) message, 12) < 0)
197 /// error (1, 0, "Reading failed: %s", pgp_error_to_string (err));
198 ///
199 /// assert (pgp_armor_reader_kind (armor) == PGP_ARMOR_KIND_FILE);
200 /// assert (memcmp (message, "Hello world!", 12) == 0);
201 ///
202 /// pgp_reader_free (armor);
203 /// ```
204 #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C"
pgp_armor_reader_from_bytes(b: *const u8, len: size_t, mode: c_int) -> *mut Reader205 fn pgp_armor_reader_from_bytes(b: *const u8, len: size_t,
206 mode: c_int)
207 -> *mut Reader {
208 assert!(!b.is_null());
209 let buf = unsafe {
210 slice::from_raw_parts(b, len as usize)
211 };
212 let mode = int_to_reader_mode(mode);
213
214 ReaderKind::Armored(armor::Reader::from_bytes(buf, mode)).move_into_raw()
215 }
216
217 /// Returns the kind of data this reader is for.
218 ///
219 /// Useful if the kind of data is not known in advance. If the header
220 /// has not been encountered yet (try reading some data first!), this
221 /// function returns PGP_ARMOR_KIND_ANY.
222 ///
223 /// # Example
224 ///
225 /// See [this] example.
226 ///
227 /// [this]: fn.pgp_armor_reader_new.html
228 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_kind(reader: *const Reader) -> c_int229 pub extern "C" fn pgp_armor_reader_kind(reader: *const Reader)
230 -> c_int {
231 if let ReaderKind::Armored(ref armor_reader) = reader.ref_raw()
232 {
233 kind_to_int(armor_reader.kind())
234 } else {
235 panic!(
236 "FFI contract violation: Wrong parameter type: \
237 expected an armor reader")
238 }
239 }
240
241 /// Returns the armored headers.
242 ///
243 /// The tuples contain a key and a value.
244 ///
245 /// Note: if a key occurs multiple times, then there are multiple
246 /// entries in the vector with the same key; values with the same
247 /// key are *not* combined.
248 ///
249 /// The returned array and the strings in the headers have been
250 /// allocated with `malloc`, and the caller is responsible for freeing
251 /// both the array and the strings.
252 ///
253 /// # Example
254 ///
255 /// See [this] example.
256 ///
257 /// [this]: fn.pgp_armor_reader_new.html
258 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_headers(errp: Option<&mut *mut crate::error::Error>, reader: *mut Reader, len: *mut size_t) -> *mut ArmorHeader259 pub extern "C" fn pgp_armor_reader_headers(errp: Option<&mut *mut crate::error::Error>,
260 reader: *mut Reader,
261 len: *mut size_t)
262 -> *mut ArmorHeader {
263 ffi_make_fry_from_errp!(errp);
264 let len = ffi_param_ref_mut!(len);
265
266 let reader = if let ReaderKind::Armored(ref mut reader) = reader.ref_mut_raw() {
267 reader
268 } else {
269 panic!("FFI contract violation: Wrong parameter type: \
270 expected armor reader");
271 };
272
273 match reader.headers().map_err(|e| ::anyhow::Error::from(e)) {
274 Ok(headers) => {
275 // Allocate space for the result.
276 let buf = unsafe {
277 libc::calloc(headers.len(), size_of::<ArmorHeader>())
278 as *mut ArmorHeader
279 };
280 let sl = unsafe {
281 slice::from_raw_parts_mut(buf, headers.len())
282 };
283 for (i, (key, value)) in headers.iter().enumerate() {
284 sl[i].key =
285 super::strndup(key.as_bytes()).unwrap_or(ptr::null_mut());
286 sl[i].value =
287 super::strndup(value.as_bytes()).unwrap_or(ptr::null_mut());
288 }
289
290 *len = headers.len();
291 buf
292 },
293 Err(e) => {
294 if let Some(errp) = errp {
295 *errp = e.move_into_raw();
296 }
297 ptr::null_mut()
298 },
299 }
300 }
301
302 /// Constructs a new filter for the given type of data.
303 ///
304 /// A filter that applies ASCII Armor to the data written to it.
305 ///
306 /// Note: You must call `pgp_armor_writer_finalize` to deallocate this
307 /// writer.
308 ///
309 /// # Example
310 ///
311 /// ```c
312 /// #define _GNU_SOURCE
313 /// #include <assert.h>
314 /// #include <error.h>
315 /// #include <stdio.h>
316 /// #include <stdlib.h>
317 /// #include <string.h>
318 ///
319 /// #include <sequoia/openpgp.h>
320 ///
321 /// int
322 /// main (int argc, char **argv)
323 /// {
324 /// void *buf = NULL;
325 /// size_t len = 0;
326 /// pgp_writer_t alloc;
327 /// pgp_writer_t armor;
328 /// pgp_error_t err = NULL;
329 ///
330 /// char *message = "Hello world!";
331 /// struct pgp_armor_header header[] = {
332 /// { "Key0", "Value0" },
333 /// { "Key1", "Value1" },
334 /// };
335 ///
336 /// alloc = pgp_writer_alloc (&buf, &len);
337 /// armor = pgp_armor_writer_new (&err, alloc, PGP_ARMOR_KIND_FILE, header, 2);
338 /// if (armor == NULL)
339 /// error (1, 0, "Creating armor writer failed: %s", pgp_error_to_string (err));
340 ///
341 /// if (pgp_writer_write (&err, armor, (uint8_t *) message, strlen (message)) < 0)
342 /// error (1, 0, "Writing failed: %s", pgp_error_to_string (err));
343 ///
344 /// pgp_armor_writer_finalize (&err, armor);
345 /// assert (err == NULL);
346 /// pgp_writer_free (alloc);
347 ///
348 /// assert (len == 114);
349 /// assert (memcmp (buf,
350 /// "-----BEGIN PGP ARMORED FILE-----\n"
351 /// "Key0: Value0\n"
352 /// "Key1: Value1\n"
353 /// "\n"
354 /// "SGVsbG8gd29ybGQh\n"
355 /// "=s4Gu\n"
356 /// "-----END PGP ARMORED FILE-----\n",
357 /// len) == 0);
358 ///
359 /// free (buf);
360 /// return 0;
361 /// }
362 /// ```
363 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_writer_new(errp: Option<&mut *mut crate::error::Error>, inner: *mut super::io::Writer, kind: c_int, header: *const ArmorHeader, header_len: size_t) -> Maybe<super::io::Writer>364 pub extern "C" fn pgp_armor_writer_new
365 (errp: Option<&mut *mut crate::error::Error>,
366 inner: *mut super::io::Writer,
367 kind: c_int,
368 header: *const ArmorHeader,
369 header_len: size_t)
370 -> Maybe<super::io::Writer>
371 {
372 let inner = inner.ref_mut_raw();
373 let kind = int_to_kind(kind).expect("KIND must not be PGP_ARMOR_KIND_ANY");
374
375 let mut header_ = Vec::new();
376 if header_len > 0 {
377 let headers = ffi_param_ref!(header);
378 let headers = unsafe {
379 slice::from_raw_parts(headers, header_len)
380 };
381 for header in headers {
382 header_.push(
383 (ffi_param_cstr!(header.key).to_string_lossy(),
384 ffi_param_cstr!(header.value).to_string_lossy())
385 );
386 }
387 }
388
389 let header: Vec<(&str, &str)> =
390 header_.iter().map(|h| (h.0.as_ref(), h.1.as_ref())).collect();
391
392 armor::Writer::with_headers(inner, kind, header)
393 .map(|w| WriterKind::Armored(w))
394 .map_err(|e| ::anyhow::Error::from(e))
395 .move_into_raw(errp)
396 }
397
398 /// Finalizes the armor writer.
399 ///
400 /// Consumes the writer. No further deallocation of the writer is
401 /// required.
402 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_writer_finalize(errp: Option<&mut *mut crate::error::Error>, writer: *mut super::io::Writer) -> crate::error::Status403 pub extern "C" fn pgp_armor_writer_finalize
404 (errp: Option<&mut *mut crate::error::Error>,
405 writer: *mut super::io::Writer)
406 -> crate::error::Status
407 {
408 ffi_make_fry_from_errp!(errp);
409 let writer = if let WriterKind::Armored(writer) = writer.move_from_raw() {
410 writer
411 } else {
412 panic!("FFI contract violation: Wrong parameter type: \
413 expected armor writer");
414 };
415
416 ffi_try_status!(writer.finalize().map_err(|e| e.into()))
417 }
418