Skip to content

Commit 2e78116

Browse files
committed
merge main into pgp-contacts
2 parents 4eb1a5e + b45d9aa commit 2e78116

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+419
-386
lines changed

Cargo.lock

Lines changed: 25 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ nu-ansi-term = "0.46"
189189
num-traits = "0.2"
190190
rand = "0.8"
191191
regex = "1.10"
192-
rusqlite = "0.32"
192+
rusqlite = "0.36"
193193
sanitize-filename = "0.5"
194194
serde = "1.0"
195195
serde_json = "1"
@@ -204,7 +204,8 @@ yerpc = "0.6.4"
204204
default = ["vendored"]
205205
internals = []
206206
vendored = [
207-
"rusqlite/bundled-sqlcipher-vendored-openssl"
207+
"rusqlite/bundled-sqlcipher-vendored-openssl",
208+
"async-native-tls/vendored"
208209
]
209210

210211
[lints.rust]

benches/send_events.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ use criterion::{criterion_group, criterion_main, Criterion};
33

44
use deltachat::context::Context;
55
use deltachat::stock_str::StockStrings;
6-
use deltachat::{info, Event, EventType, Events};
6+
use deltachat::{Event, EventType, Events};
77
use tempfile::tempdir;
88

99
async fn send_events_benchmark(context: &Context) {
1010
let emitter = context.get_event_emitter();
1111
for _i in 0..1_000_000 {
12-
info!(context, "interesting event...");
12+
context.emit_event(EventType::Info("interesting event...".to_string()));
1313
}
14-
info!(context, "DONE");
14+
context.emit_event(EventType::Info("DONE".to_string()));
1515

1616
loop {
1717
match emitter.recv().await.unwrap() {

deltachat-contact-tools/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl ContactAddress {
7676

7777
/// Allow converting [`ContactAddress`] to an SQLite type.
7878
impl rusqlite::types::ToSql for ContactAddress {
79-
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
79+
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
8080
let val = rusqlite::types::Value::Text(self.0.to_string());
8181
let out = rusqlite::types::ToSqlOutput::Owned(val);
8282
Ok(out)
@@ -282,7 +282,7 @@ impl EmailAddress {
282282
}
283283

284284
impl rusqlite::types::ToSql for EmailAddress {
285-
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
285+
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
286286
let val = rusqlite::types::Value::Text(self.to_string());
287287
let out = rusqlite::types::ToSqlOutput::Owned(val);
288288
Ok(out)

deltachat-contact-tools/src/vcard.rs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub struct VcardContact {
2020
pub key: Option<String>,
2121
/// The contact's profile image (=avatar) in Base64, vcard property `photo`
2222
pub profile_image: Option<String>,
23+
/// The biography, stored in the vcard property `note`
24+
pub biography: Option<String>,
2325
/// The timestamp when the vcard was created / last updated, vcard property `rev`
2426
pub timestamp: Result<i64>,
2527
}
@@ -44,21 +46,29 @@ pub fn make_vcard(contacts: &[VcardContact]) -> String {
4446
Some(datetime.format("%Y%m%dT%H%M%SZ").to_string())
4547
}
4648

49+
fn escape(s: &str) -> String {
50+
s.replace(',', "\\,")
51+
}
52+
4753
let mut res = "".to_string();
4854
for c in contacts {
49-
let addr = &c.addr;
50-
let display_name = c.display_name();
55+
// Mustn't contain ',', but it's easier to escape than to error out.
56+
let addr = escape(&c.addr);
57+
let display_name = escape(c.display_name());
5158
res += &format!(
5259
"BEGIN:VCARD\r\n\
5360
VERSION:4.0\r\n\
5461
EMAIL:{addr}\r\n\
5562
FN:{display_name}\r\n"
5663
);
5764
if let Some(key) = &c.key {
58-
res += &format!("KEY:data:application/pgp-keys;base64,{key}\r\n");
65+
res += &format!("KEY:data:application/pgp-keys;base64\\,{key}\r\n");
5966
}
6067
if let Some(profile_image) = &c.profile_image {
61-
res += &format!("PHOTO:data:image/jpeg;base64,{profile_image}\r\n");
68+
res += &format!("PHOTO:data:image/jpeg;base64\\,{profile_image}\r\n");
69+
}
70+
if let Some(biography) = &c.biography {
71+
res += &format!("NOTE:{}\r\n", escape(biography));
6272
}
6373
if let Some(timestamp) = format_timestamp(c) {
6474
res += &format!("REV:{timestamp}\r\n");
@@ -79,8 +89,8 @@ pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
7989
None
8090
}
8191
}
82-
/// Returns (parameters, value) tuple.
83-
fn vcard_property<'a>(line: &'a str, property: &str) -> Option<(&'a str, &'a str)> {
92+
/// Returns (parameters, raw value) tuple.
93+
fn vcard_property_raw<'a>(line: &'a str, property: &str) -> Option<(&'a str, &'a str)> {
8494
let remainder = remove_prefix(line, property)?;
8595
// If `s` is `EMAIL;TYPE=work:alice@example.com` and `property` is `EMAIL`,
8696
// then `remainder` is now `;TYPE=work:alice@example.com`
@@ -110,23 +120,25 @@ pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
110120
}
111121
Some((params, value))
112122
}
123+
/// Returns (parameters, unescaped value) tuple.
124+
fn vcard_property<'a>(line: &'a str, property: &str) -> Option<(&'a str, String)> {
125+
let (params, value) = vcard_property_raw(line, property)?;
126+
// Some fields can't contain commas, but unescape them everywhere for safety.
127+
Some((params, value.replace("\\,", ",")))
128+
}
113129
fn base64_key(line: &str) -> Option<&str> {
114-
let (params, value) = vcard_property(line, "key")?;
130+
let (params, value) = vcard_property_raw(line, "key")?;
115131
if params.eq_ignore_ascii_case("PGP;ENCODING=BASE64")
116132
|| params.eq_ignore_ascii_case("TYPE=PGP;ENCODING=b")
117133
{
118134
return Some(value);
119135
}
120-
if let Some(value) = remove_prefix(value, "data:application/pgp-keys;base64,")
121-
.or_else(|| remove_prefix(value, r"data:application/pgp-keys;base64\,"))
122-
{
123-
return Some(value);
124-
}
125-
126-
None
136+
remove_prefix(value, "data:application/pgp-keys;base64\\,")
137+
// Old Delta Chat format.
138+
.or_else(|| remove_prefix(value, "data:application/pgp-keys;base64,"))
127139
}
128140
fn base64_photo(line: &str) -> Option<&str> {
129-
let (params, value) = vcard_property(line, "photo")?;
141+
let (params, value) = vcard_property_raw(line, "photo")?;
130142
if params.eq_ignore_ascii_case("JPEG;ENCODING=BASE64")
131143
|| params.eq_ignore_ascii_case("ENCODING=BASE64;JPEG")
132144
|| params.eq_ignore_ascii_case("TYPE=JPEG;ENCODING=b")
@@ -136,13 +148,9 @@ pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
136148
{
137149
return Some(value);
138150
}
139-
if let Some(value) = remove_prefix(value, "data:image/jpeg;base64,")
140-
.or_else(|| remove_prefix(value, r"data:image/jpeg;base64\,"))
141-
{
142-
return Some(value);
143-
}
144-
145-
None
151+
remove_prefix(value, "data:image/jpeg;base64\\,")
152+
// Old Delta Chat format.
153+
.or_else(|| remove_prefix(value, "data:image/jpeg;base64,"))
146154
}
147155
fn parse_datetime(datetime: &str) -> Result<i64> {
148156
// According to https://www.rfc-editor.org/rfc/rfc6350#section-4.3.5, the timestamp
@@ -186,6 +194,7 @@ pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
186194
let mut addr = None;
187195
let mut key = None;
188196
let mut photo = None;
197+
let mut biography = None;
189198
let mut datetime = None;
190199

191200
for mut line in lines.by_ref() {
@@ -205,18 +214,24 @@ pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
205214
key.get_or_insert(k);
206215
} else if let Some(p) = base64_photo(line) {
207216
photo.get_or_insert(p);
217+
} else if let Some((_params, bio)) = vcard_property(line, "note") {
218+
biography.get_or_insert(bio);
208219
} else if let Some((_params, rev)) = vcard_property(line, "rev") {
209220
datetime.get_or_insert(rev);
210221
} else if line.eq_ignore_ascii_case("END:VCARD") {
211-
let (authname, addr) =
212-
sanitize_name_and_addr(display_name.unwrap_or(""), addr.unwrap_or(""));
222+
let (authname, addr) = sanitize_name_and_addr(
223+
&display_name.unwrap_or_default(),
224+
&addr.unwrap_or_default(),
225+
);
213226

214227
contacts.push(VcardContact {
215228
authname,
216229
addr,
217230
key: key.map(|s| s.to_string()),
218231
profile_image: photo.map(|s| s.to_string()),
232+
biography,
219233
timestamp: datetime
234+
.as_deref()
220235
.context("No timestamp in vcard")
221236
.and_then(parse_datetime),
222237
});

deltachat-contact-tools/src/vcard/vcard_tests.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,15 @@ fn test_make_and_parse_vcard() {
9191
authname: "Alice Wonderland".to_string(),
9292
key: Some("[base64-data]".to_string()),
9393
profile_image: Some("image in Base64".to_string()),
94+
biography: Some("Hi, I'm Alice".to_string()),
9495
timestamp: Ok(1713465762),
9596
},
9697
VcardContact {
9798
addr: "bob@example.com".to_string(),
9899
authname: "".to_string(),
99100
key: None,
100101
profile_image: None,
102+
biography: None,
101103
timestamp: Ok(0),
102104
},
103105
];
@@ -106,8 +108,9 @@ fn test_make_and_parse_vcard() {
106108
VERSION:4.0\r\n\
107109
EMAIL:alice@example.org\r\n\
108110
FN:Alice Wonderland\r\n\
109-
KEY:data:application/pgp-keys;base64,[base64-data]\r\n\
110-
PHOTO:data:image/jpeg;base64,image in Base64\r\n\
111+
KEY:data:application/pgp-keys;base64\\,[base64-data]\r\n\
112+
PHOTO:data:image/jpeg;base64\\,image in Base64\r\n\
113+
NOTE:Hi\\, I'm Alice\r\n\
111114
REV:20240418T184242Z\r\n\
112115
END:VCARD\r\n",
113116
"BEGIN:VCARD\r\n\
@@ -246,7 +249,8 @@ END:VCARD",
246249
assert_eq!(contacts[0].profile_image, None);
247250
}
248251

249-
/// Proton at some point slightly changed the format of their vcards
252+
/// Proton at some point slightly changed the format of their vcards.
253+
/// This also tests unescaped commas in PHOTO and KEY (old Delta Chat format).
250254
#[test]
251255
fn test_protonmail_vcard2() {
252256
let contacts = parse_vcard(

0 commit comments

Comments
 (0)