diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-06-27 11:31:00 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-06-29 06:05:14 +0200 |
commit | 5641d48c910348b99dc96f4072f8e8beeea01358 (patch) | |
tree | 35154c4f396502dd1c12a9fa8fbab258e54042fe | |
parent | 7d23684183aa21cd3add16240d769353b9fafacb (diff) |
openpgp: Implement deleting, adding, and replacing subpackets.
-rw-r--r-- | openpgp/src/subpacket.rs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/openpgp/src/subpacket.rs b/openpgp/src/subpacket.rs index 6abe0750..a46b33d3 100644 --- a/openpgp/src/subpacket.rs +++ b/openpgp/src/subpacket.rs @@ -65,6 +65,8 @@ use quickcheck::{Arbitrary, Gen}; use buffered_reader::{BufferedReader, BufferedReaderMemory}; use { + Error, + Result, Signature, Packet, Fingerprint, @@ -410,6 +412,11 @@ impl SubpacketArea { } } + /// Invalidates the cache. + fn cache_invalidate(&self) { + *self.parsed.borrow_mut() = None; + } + // Returns the last subpacket, if any, with the specified tag. fn lookup(&self, tag: SubpacketTag) -> Option<SubpacketRaw> { self.cache_init(); @@ -425,6 +432,68 @@ impl SubpacketArea { None => None, } } + + /// Adds the given subpacket. + /// + /// # Errors + /// + /// Returns `Error::MalformedPacket` if adding the packet makes + /// the subpacket area exceed the size limit. + pub fn add(&mut self, packet: Subpacket) -> Result<()> { + use serialize::Serialize; + + if self.data.len() + packet.len() > ::std::u16::MAX as usize { + return Err(Error::MalformedPacket( + "Subpacket area exceeds maximum size".into()).into()); + } + + self.cache_invalidate(); + packet.serialize(&mut self.data) + } + + /// Adds the given subpacket, replacing all other subpackets with + /// the same tag. + /// + /// # Errors + /// + /// Returns `Error::MalformedPacket` if adding the packet makes + /// the subpacket area exceed the size limit. + pub fn replace(&mut self, packet: Subpacket) -> Result<()> { + let old = self.remove_all(packet.tag); + if let Err(e) = self.add(packet) { + // Restore old state. + self.data = old; + return Err(e); + } + Ok(()) + } + + /// Removes all subpackets with the given tag. + /// + /// Returns the old subpacket area, so that it can be restored if + /// necessary. + pub fn remove_all(&mut self, tag: SubpacketTag) -> Vec<u8> { + let mut new = Vec::new(); + + // Copy all but the matching subpackets. + for (_, _, raw) in self.iter() { + if raw.tag == tag { + // Drop. + continue; + } + + let l: SubpacketLength = 1 + raw.value.len() as u32; + let tag = u8::from(raw.tag) + | if raw.critical { 1 << 7 } else { 0 }; + + l.serialize(&mut new).unwrap(); + new.push(tag); + new.extend_from_slice(raw.value); + } + + self.cache_invalidate(); + ::std::mem::replace(&mut self.data, new) + } } /// Payload of a NotationData subpacket. |