//! OpenPGP packet parser. //! //! Wraps [`sequoia-openpgp::parse::PacketParser`] and [related //! functionality]. //! //! [`sequoia-openpgp::parse::PacketParser`]: ../../sequoia_openpgp/parse/struct.PacketParser.html //! [related functionality]: ../../sequoia_openpgp/parse/index.html use std::mem::forget; use std::ptr; use std::slice; use std::io::Read; use libc::{uint8_t, c_char, c_int, size_t}; extern crate sequoia_openpgp as openpgp; extern crate time; use self::openpgp::{ Packet, }; use self::openpgp::parse::{ Parse, PacketParserResult, PacketParser, PacketParserEOF, }; use error::Status; /// Starts parsing OpenPGP packets stored in a `pgp_reader_t` /// object. /// /// This function returns a `PacketParser` for the first packet in /// the stream. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_from_reader<'a> (errp: Option<&mut *mut failure::Error>, reader: *mut Box<'a + Read>) -> *mut PacketParserResult<'a> { ffi_make_fry_from_errp!(errp); let reader = ffi_param_ref_mut!(reader); ffi_try_box!(PacketParser::from_reader(reader)) } /// Starts parsing OpenPGP packets stored in a file named `path`. /// /// This function returns a `PacketParser` for the first packet in /// the stream. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_from_file (errp: Option<&mut *mut failure::Error>, filename: *const c_char) -> *mut PacketParserResult<'static> { ffi_make_fry_from_errp!(errp); let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned(); ffi_try_box!(PacketParser::from_file(&filename)) } /// Starts parsing OpenPGP packets stored in a buffer. /// /// This function returns a `PacketParser` for the first packet in /// the stream. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_from_bytes (errp: Option<&mut *mut failure::Error>, b: *const uint8_t, len: size_t) -> *mut PacketParserResult<'static> { ffi_make_fry_from_errp!(errp); assert!(!b.is_null()); let buf = unsafe { slice::from_raw_parts(b, len as usize) }; ffi_try_box!(PacketParser::from_bytes(buf)) } /// Frees the packet parser result #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_result_free( ppr: Option<&mut PacketParserResult>) { ffi_free!(ppr) } /// Frees the packet parser. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_free(pp: Option<&mut PacketParser>) { ffi_free!(pp) } /// Frees the packet parser EOF object. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_eof_is_message( eof: *const PacketParserEOF) -> bool { let eof = ffi_param_ref!(eof); eof.is_message() } /// Frees the packet parser EOF object. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_eof_free (eof: Option<&mut PacketParserEOF>) { ffi_free!(eof) } /// Returns a reference to the packet that is being parsed. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_packet (pp: *const PacketParser) -> *const Packet { let pp = ffi_param_ref!(pp); &pp.packet } /// Returns the current packet's recursion depth. /// /// A top-level packet has a recursion depth of 0. Packets in a /// top-level container have a recursion depth of 1, etc. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_recursion_depth (pp: *const PacketParser) -> uint8_t { let pp = ffi_param_ref!(pp); pp.recursion_depth() as u8 } /// Finishes parsing the current packet and starts parsing the /// following one. /// /// This function finishes parsing the current packet. By /// default, any unread content is dropped. (See /// [`PacketParsererBuilder`] for how to configure this.) It then /// creates a new packet parser for the following packet. If the /// current packet is a container, this function does *not* /// recurse into the container, but skips any packets it contains. /// To recurse into the container, use the [`recurse()`] method. /// /// [`PacketParsererBuilder`]: ../../sequoia_openpgp/parse/struct.PacketParserBuilder.html /// [`recurse()`]: fn.pgp_packet_parser_recurse.html /// /// The return value is a tuple containing: /// /// - A `Packet` holding the fully processed old packet; /// /// - The old packet's recursion depth; /// /// - A `PacketParser` holding the new packet; /// /// - And, the recursion depth of the new packet. /// /// A recursion depth of 0 means that the packet is a top-level /// packet, a recursion depth of 1 means that the packet is an /// immediate child of a top-level-packet, etc. /// /// Since the packets are serialized in depth-first order and all /// interior nodes are visited, we know that if the recursion /// depth is the same, then the packets are siblings (they have a /// common parent) and not, e.g., cousins (they have a common /// grandparent). This is because, if we move up the tree, the /// only way to move back down is to first visit a new container /// (e.g., an aunt). /// /// Using the two positions, we can compute the change in depth as /// new_depth - old_depth. Thus, if the change in depth is 0, the /// two packets are siblings. If the value is 1, the old packet /// is a container, and the new packet is its first child. And, /// if the value is -1, the new packet is contained in the old /// packet's grandparent. The idea is illustrated below: /// /// ```text /// ancestor /// | \ /// ... -n /// | /// grandparent /// | \ /// parent -1 /// | \ /// packet 0 /// | /// 1 /// ``` /// /// Note: since this function does not automatically recurse into /// a container, the change in depth will always be non-positive. /// If the current container is empty, this function DOES pop that /// container off the container stack, and returns the following /// packet in the parent container. /// /// The items of the tuple are returned in out-parameters. If you do /// not wish to receive the value, pass `NULL` as the parameter. /// /// Consumes the given packet parser. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_next<'a> (errp: Option<&mut *mut failure::Error>, pp: *mut PacketParser<'a>, old_packet: Option<&mut *mut Packet>, ppr: Option<&mut *mut PacketParserResult<'a>>) -> Status { ffi_make_fry_from_errp!(errp); let pp = ffi_param_move!(pp); match pp.next() { Ok((old_p, new_ppr)) => { if let Some(p) = old_packet { *p = box_raw!(old_p); } if let Some(p) = ppr { *p = box_raw!(new_ppr); } Status::Success }, Err(e) => ffi_try_status!(Err::<(), failure::Error>(e)), } } /// Finishes parsing the current packet and starts parsing the /// next one, recursing if possible. /// /// This method is similar to the [`next()`] method (see that /// method for more details), but if the current packet is a /// container (and we haven't reached the maximum recursion depth, /// and the user hasn't started reading the packet's contents), we /// recurse into the container, and return a `PacketParser` for /// its first child. Otherwise, we return the next packet in the /// packet stream. If this function recurses, then the new /// packet's recursion depth will be `last_recursion_depth() + 1`; /// because we always visit interior nodes, we can't recurse more /// than one level at a time. /// /// [`next()`]: fn.pgp_packet_parser_next.html /// /// The items of the tuple are returned in out-parameters. If you do /// not wish to receive the value, pass `NULL` as the parameter. /// /// Consumes the given packet parser. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_recurse<'a> (errp: Option<&mut *mut failure::Error>, pp: *mut PacketParser<'a>, old_packet: Option<&mut *mut Packet>, ppr: Option<&mut *mut PacketParserResult<'a>>) -> Status { ffi_make_fry_from_errp!(errp); let pp = ffi_param_move!(pp); match pp.recurse() { Ok((old_p, new_ppr)) => { if let Some(p) = old_packet { *p = box_raw!(old_p); } if let Some(p) = ppr { *p = box_raw!(new_ppr); } Status::Success }, Err(e) => ffi_try_status!(Err::<(), failure::Error>(e)), } } /// Causes the PacketParser to buffer the packet's contents. /// /// The packet's contents are stored in `packet.content`. In /// general, you should avoid buffering a packet's content and /// prefer streaming its content unless you are certain that the /// content is small. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_buffer_unread_content<'a> (errp: Option<&mut *mut failure::Error>, pp: *mut PacketParser<'a>, len: *mut usize) -> *const uint8_t { ffi_make_fry_from_errp!(errp); let pp = ffi_param_ref_mut!(pp); let len = ffi_param_ref_mut!(len); let buf = ffi_try!(pp.buffer_unread_content()); *len = buf.len(); buf.as_ptr() } /// Finishes parsing the current packet. /// /// By default, this drops any unread content. Use, for instance, /// `PacketParserBuild` to customize the default behavior. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_finish<'a> (errp: Option<&mut *mut failure::Error>, pp: *mut PacketParser<'a>, packet: Option<&mut *const Packet>) -> Status { ffi_make_fry_from_errp!(errp); let pp = ffi_param_ref_mut!(pp); match pp.finish() { Ok(p) => { if let Some(out_p) = packet { *out_p = p; } Status::Success }, Err(e) => { let status = Status::from(&e); if let Some(errp) = errp { *errp = box_raw!(e); } status }, } } /// Tries to decrypt the current packet. /// /// On success, this function pushes one or more readers onto the /// `PacketParser`'s reader stack, and sets the packet's /// `decrypted` flag. /// /// If this function is called on a packet that does not contain /// encrypted data, or some of the data was already read, then it /// returns `Error::InvalidOperation`. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_decrypt<'a> (errp: Option<&mut *mut failure::Error>, pp: *mut PacketParser<'a>, algo: uint8_t, // XXX key: *const uint8_t, key_len: size_t) -> Status { ffi_make_fry_from_errp!(errp); let pp = ffi_param_ref_mut!(pp); let key = unsafe { slice::from_raw_parts(key, key_len as usize) }; let key = key.to_owned().into(); ffi_try_status!(pp.decrypt((algo as u8).into(), &key)) } /* PacketParserResult. */ /// Returns the current packet's tag. /// /// This is a convenience function to inspect the containing packet, /// without turning the `PacketParserResult` into a `PacketParser`. /// /// This function does not consume the ppr. /// /// Returns 0 if the PacketParserResult does not contain a packet. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_result_tag<'a> (ppr: *mut PacketParserResult<'a>) -> c_int { let ppr = ffi_param_ref_mut!(ppr); let tag : u8 = match ppr { PacketParserResult::Some(ref pp) => pp.packet.tag().into(), PacketParserResult::EOF(_) => 0, }; tag as c_int } /// If the `PacketParserResult` contains a `PacketParser`, returns it, /// otherwise, returns NULL. /// /// If the `PacketParser` reached EOF, then the `PacketParserResult` /// contains a `PacketParserEOF` and you should use /// `pgp_packet_parser_result_eof` to get it. /// /// If this function returns a `PacketParser`, then it consumes the /// `PacketParserResult` and ownership of the `PacketParser` is /// returned to the caller, i.e., the caller is responsible for /// ensuring that the `PacketParser` is freed. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_result_packet_parser<'a> (ppr: *mut PacketParserResult<'a>) -> *mut PacketParser<'a> { let ppr = ffi_param_move!(ppr); match *ppr { PacketParserResult::Some(pp) => box_raw!(pp), PacketParserResult::EOF(_) => { // Don't free ppr! forget(ppr); ptr::null_mut() } } } /// If the `PacketParserResult` contains a `PacketParserEOF`, returns /// it, otherwise, returns NULL. /// /// If the `PacketParser` did not yet reach EOF, then the /// `PacketParserResult` contains a `PacketParser` and you should use /// `pgp_packet_parser_result_packet_parser` to get it. /// /// If this function returns a `PacketParserEOF`, then it consumes the /// `PacketParserResult` and ownership of the `PacketParserEOF` is /// returned to the caller, i.e., the caller is responsible for /// ensuring that the `PacketParserEOF` is freed. #[::ffi_catch_abort] #[no_mangle] pub extern "system" fn pgp_packet_parser_result_eof<'a> (ppr: *mut PacketParserResult<'a>) -> *mut PacketParserEOF { let ppr = ffi_param_move!(ppr); match *ppr { PacketParserResult::Some(_) => { forget(ppr); ptr::null_mut() } PacketParserResult::EOF(eof) => box_raw!(eof), } }