melib/nntp: limit LIST ACTIVE command length to 512 octets
According to RFC 3977: > Command lines MUST NOT exceed 512 octets, which includes the > terminating CRLF pair Sending a `LIST ACTIVE` command with lots of newgroups and passing the 512 byte limit is therefore invalid. This commit splits the mailboxes in chunks and sends a separate command for each maximal chunk that has a valid length. Fixes #269. Reported-by: r3k2fix/269-invalid-ctext-loop
parent
2a079ee175
commit
c8abb15d78
|
@ -731,39 +731,65 @@ impl NntpType {
|
||||||
pub async fn nntp_mailboxes(connection: &Arc<FutureMutex<NntpConnection>>) -> Result<()> {
|
pub async fn nntp_mailboxes(connection: &Arc<FutureMutex<NntpConnection>>) -> Result<()> {
|
||||||
let mut res = String::with_capacity(8 * 1024);
|
let mut res = String::with_capacity(8 * 1024);
|
||||||
let mut conn = connection.lock().await;
|
let mut conn = connection.lock().await;
|
||||||
let command = {
|
let mut mailboxes = {
|
||||||
let mailboxes_lck = conn.uid_store.mailboxes.lock().await;
|
let mailboxes_lck = conn.uid_store.mailboxes.lock().await;
|
||||||
mailboxes_lck
|
mailboxes_lck
|
||||||
.values()
|
.values()
|
||||||
.fold("LIST ACTIVE ".to_string(), |mut acc, x| {
|
.map(|m| m.name().to_string())
|
||||||
if acc.len() != "LIST ACTIVE ".len() {
|
.collect::<SmallVec<[String; 16]>>()
|
||||||
acc.push(',');
|
|
||||||
}
|
|
||||||
acc.push_str(x.name());
|
|
||||||
acc
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
conn.send_command(command.as_bytes()).await?;
|
mailboxes.reverse();
|
||||||
conn.read_response(&mut res, true, &["215 "])
|
while !mailboxes.is_empty() {
|
||||||
.await
|
let mut command = "LIST ACTIVE ".to_string();
|
||||||
.chain_err_summary(|| {
|
'batch: while let Some(m) = mailboxes.pop() {
|
||||||
format!(
|
/* first check if the group name itself is too big for `LIST ACTIVE`. */
|
||||||
"Could not get newsgroups {}: expected LIST ACTIVE response but got: {}",
|
if "LIST ACTIVE ".len() + m.len() + "\r\n".len() >= 512 {
|
||||||
&conn.uid_store.account_name, res
|
log::warn!(
|
||||||
)
|
"{}: Newsgroup named {} has a name that exceeds RFC 3977 limits of \
|
||||||
})?;
|
maximum command lines (512 octets) with LIST ACTIVE. Skipping it.",
|
||||||
debug!(&res);
|
&conn.uid_store.account_name,
|
||||||
let mut mailboxes_lck = conn.uid_store.mailboxes.lock().await;
|
m
|
||||||
for l in res.split_rn().skip(1) {
|
);
|
||||||
let s = l.split_whitespace().collect::<SmallVec<[&str; 4]>>();
|
continue 'batch;
|
||||||
if s.len() != 3 {
|
}
|
||||||
continue;
|
if command.len() != "LIST ACTIVE ".len() {
|
||||||
|
command.push(',');
|
||||||
|
}
|
||||||
|
// RFC 3977
|
||||||
|
// 3. Basic Concepts
|
||||||
|
// 3.1. Commands and Responses
|
||||||
|
// Command lines MUST NOT exceed 512 octets, which includes the terminating CRLF
|
||||||
|
// pair.
|
||||||
|
if command.len() + m.len() + "\r\n".len() >= 512 {
|
||||||
|
mailboxes.push(m);
|
||||||
|
if command.ends_with(',') {
|
||||||
|
command.pop();
|
||||||
|
}
|
||||||
|
break 'batch;
|
||||||
|
}
|
||||||
|
command.push_str(&m);
|
||||||
|
}
|
||||||
|
conn.send_command(command.as_bytes()).await?;
|
||||||
|
conn.read_response(&mut res, true, &["215 "])
|
||||||
|
.await
|
||||||
|
.chain_err_summary(|| {
|
||||||
|
format!(
|
||||||
|
"Could not get newsgroups {}: expected LIST ACTIVE response but got: {}",
|
||||||
|
&conn.uid_store.account_name, res
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let mut mailboxes_lck = conn.uid_store.mailboxes.lock().await;
|
||||||
|
for l in res.split_rn().skip(1) {
|
||||||
|
let s = l.split_whitespace().collect::<SmallVec<[&str; 4]>>();
|
||||||
|
if s.len() != 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mailbox_hash = MailboxHash(get_path_hash!(&s[0]));
|
||||||
|
mailboxes_lck.entry(mailbox_hash).and_modify(|m| {
|
||||||
|
*m.high_watermark.lock().unwrap() = usize::from_str(s[1]).unwrap_or(0);
|
||||||
|
*m.low_watermark.lock().unwrap() = usize::from_str(s[2]).unwrap_or(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let mailbox_hash = MailboxHash(get_path_hash!(&s[0]));
|
|
||||||
mailboxes_lck.entry(mailbox_hash).and_modify(|m| {
|
|
||||||
*m.high_watermark.lock().unwrap() = usize::from_str(s[1]).unwrap_or(0);
|
|
||||||
*m.low_watermark.lock().unwrap() = usize::from_str(s[2]).unwrap_or(0);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue