summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-06-27 11:31:00 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-06-29 06:05:14 +0200
commit5641d48c910348b99dc96f4072f8e8beeea01358 (patch)
tree35154c4f396502dd1c12a9fa8fbab258e54042fe
parent7d23684183aa21cd3add16240d769353b9fafacb (diff)
openpgp: Implement deleting, adding, and replacing subpackets.
-rw-r--r--openpgp/src/subpacket.rs69
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.