param: escape newlines in values

Newlines are escaped by repeating them.

This encoding is compatible to existing values, because they do not
contain newlines.
This commit is contained in:
Alexander Krotov
2020-09-26 02:10:39 +03:00
committed by link2xt
parent 39d8cffe18
commit 9121e30600

View File

@@ -3,12 +3,13 @@ use std::fmt;
use std::str; use std::str;
use async_std::path::PathBuf; use async_std::path::PathBuf;
use itertools::Itertools;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::blob::{BlobError, BlobObject}; use crate::blob::{BlobError, BlobObject};
use crate::context::Context; use crate::context::Context;
use crate::error::{self, bail, ensure}; use crate::error::{self, bail};
use crate::message::MsgId; use crate::message::MsgId;
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
@@ -146,7 +147,12 @@ impl fmt::Display for Params {
if i > 0 { if i > 0 {
writeln!(f)?; writeln!(f)?;
} }
write!(f, "{}={}", *key as u8 as char, value)?; write!(
f,
"{}={}",
*key as u8 as char,
value.split('\n').join("\n\n")
)?;
} }
Ok(()) Ok(())
} }
@@ -157,27 +163,28 @@ impl str::FromStr for Params {
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut inner = BTreeMap::new(); let mut inner = BTreeMap::new();
for pair in s.trim().lines() { let mut lines = s.lines().peekable();
let pair = pair.trim();
if pair.is_empty() {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
ensure!(key.is_some(), "Missing key"); while let Some(line) = lines.next() {
ensure!(value.is_some(), "Missing value"); if let [key, value] = line.splitn(2, '=').collect::<Vec<_>>()[..] {
let key = key.to_string();
let mut value = value.to_string();
while let Some(s) = lines.peek() {
if !s.is_empty() {
break;
}
lines.next();
value.push('\n');
value += lines.next().unwrap_or_default();
}
let key = key.unwrap_or_default().trim(); if let Some(key) = key.as_bytes().first().and_then(|key| Param::from_u8(*key)) {
let value = value.unwrap_or_default().trim(); inner.insert(key, value);
} else {
if let Some(key) = key.as_bytes().first().and_then(|key| Param::from_u8(*key)) { bail!("Unknown key: {}", key);
inner.insert(key, value.to_string()); }
} else { } else {
bail!("Unknown key: {}", key); bail!("Not a key-value pair: {:?}", line);
} }
} }
@@ -373,7 +380,7 @@ mod tests {
#[test] #[test]
fn test_dc_param() { fn test_dc_param() {
let mut p1: Params = "\r\n\r\na=1\nf=2\n\nc = 3 ".parse().unwrap(); let mut p1: Params = "a=1\nf=2\nc=3".parse().unwrap();
assert_eq!(p1.get_int(Param::Forwarded), Some(1)); assert_eq!(p1.get_int(Param::Forwarded), Some(1));
assert_eq!(p1.get_int(Param::File), Some(2)); assert_eq!(p1.get_int(Param::File), Some(2));
@@ -407,6 +414,14 @@ mod tests {
assert_eq!(p1.len(), 0) assert_eq!(p1.len(), 0)
} }
#[test]
fn test_roundtrip() {
let mut params = Params::new();
params.set(Param::Height, "foo\nbar=baz\nquux");
params.set(Param::Width, "\n\n\na=\n=");
assert_eq!(params.to_string().parse::<Params>().unwrap(), params);
}
#[test] #[test]
fn test_regression() { fn test_regression() {
let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB" let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB"