Skip to content

Commit

Permalink
Merge pull request #392 from dimastbk/check-password-ods
Browse files Browse the repository at this point in the history
feat(ods): detect ods password protected files
  • Loading branch information
tafia authored Jan 12, 2024
2 parents ad57593 + f7c5c99 commit 04ed0c4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/ods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub enum OdsError {
/// Found
found: String,
},
/// Workbook is password protected
Password,
/// Worksheet not found
WorksheetNotFound(String),
}
Expand Down Expand Up @@ -83,6 +85,7 @@ impl std::fmt::Display for OdsError {
OdsError::Mismatch { expected, found } => {
write!(f, "Expecting '{expected}', found '{found}'")
}
OdsError::Password => write!(f, "Workbook is password protected"),
OdsError::WorksheetNotFound(name) => write!(f, "Worksheet '{name}' not found"),
}
}
Expand Down Expand Up @@ -137,6 +140,8 @@ where
Err(e) => return Err(OdsError::Zip(e)),
}

check_for_password_protected(&mut zip)?;

#[cfg(feature = "picture")]
let pictures = read_pictures(&mut zip)?;

Expand Down Expand Up @@ -204,6 +209,50 @@ struct Content {
defined_names: Vec<(String, String)>,
}

/// Check password protection
fn check_for_password_protected<RS: Read + Seek>(zip: &mut ZipArchive<RS>) -> Result<(), OdsError> {
let mut reader = match zip.by_name("META-INF/manifest.xml") {
Ok(f) => {
let mut r = XmlReader::from_reader(BufReader::new(f));
r.check_end_names(false)
.trim_text(false)
.check_comments(false)
.expand_empty_elements(true);
r
}
Err(ZipError::FileNotFound) => return Err(OdsError::FileNotFound("META-INF/manifest.xml")),
Err(e) => return Err(OdsError::Zip(e)),
};

let mut buf = Vec::new();
let mut inner = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) if e.name() == QName(b"manifest:file-entry") => {
loop {
match reader.read_event_into(&mut inner) {
Ok(Event::Start(ref e))
if e.name() == QName(b"manifest:encryption-data") =>
{
return Err(OdsError::Password)
}
Ok(Event::Eof) => break,
Err(e) => return Err(OdsError::Xml(e)),
_ => (),
}
}
inner.clear()
}
Ok(Event::Eof) => break,
Err(e) => return Err(OdsError::Xml(e)),
_ => (),
}
buf.clear()
}

Ok(())
}

/// Parses content.xml and store the result in `self.content`
fn parse_content<RS: Read + Seek>(mut zip: ZipArchive<RS>) -> Result<Content, OdsError> {
let mut reader = match zip.by_name("content.xml") {
Expand Down
Binary file added tests/pass_protected.ods
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,19 @@ fn issue_385() {
);
}

#[test]
fn pass_protected_ods() {
let path = format!("{}/tests/pass_protected.ods", env!("CARGO_MANIFEST_DIR"));

assert!(
matches!(
open_workbook::<Ods<_>, std::string::String>(path),
Err(calamine::OdsError::Password)
),
"Is expeced to return OdsError::Password error"
);
}

#[test]
fn issue_384_multiple_formula() {
let path = format!("{}/tests/formula.issue.xlsx", env!("CARGO_MANIFEST_DIR"));
Expand Down

0 comments on commit 04ed0c4

Please sign in to comment.