Great. A few questions
-
Is the match supposed to be exact? E.g., will module version
1.2.3-alpha
match only release1.2.3-alpha
, or will it also match1.2.3
? It seems that the match must be exact. If so, is that really what you want? I mean, you could have a future release, say1.2.3
to which you upload a pre-release1.2.3-alpha
, or a release1.2.3
to which you upload a specific build1.2.3+french
. -
Should module extensions (
.vext
) not also be checked against the release? -
What about saves (
.vsav
), logs (.vlog
), and older extensions (.vmdx
)? Shouldn’t they be checked too? -
It seems that checking the release number depends on the uploaded file name ending in either
.vmod
or.vext
. Shouldn’t the check be done based on the content (mime-type) of the file? E.g., similar to what you have inimage.rs
for images:- Check if file is mime-type
application/zip
- Check if archive contains
moduledata
- If so, investigate
data/version
in the XML
- If so, investigate
- Check if archive contains
Currently, a user could upload a module which file name ends in say
.vmd
and thereby by-pass the release check. - Check if file is mime-type
Again, with the caveat that I’m not too familiar with Rust
pub fn is_magic_zip(mime: &Mime) -> bool {
mime == &mime::APPLICATION && mime.sub_type() == "zip";
}
fn is_zip_archive<P: AsRef<Path>>(
path: P,
buf: &[u8]
) -> bool {
match infer::get(buf) {
Some(info) =>
info.mime_type().parse::<Mime>()
.is_ok_and(|mime| is_magic_zip(&mime)),
None => false
}
}
fn moduledata_in_zip<P: AsRef<Path>>(
path: P) -> Result<String, Error>
{
let zipfile = File::open(zippath)?;
let mut archive = ZipArchive::new(zipfile)?;
// check for moduledata in archive
let mut file = archive.by_name("moduledata");
if ! file {
return Ok("");
}
let mut data = String::new();
file.read_to_string(&mut data)?;
Ok(data)
}
// Return empty string if not VASSAL file (zip archive and contains moduledata)
// otherwise, the version number set in moduledata
fn check_for_vassal_file(<P: AsRef<Path>>(
path: P,
buf: &[u8]
) -> Result<String,Error> {
if ! is_zip_archive(path,buf) {
return "";
}
let md = moduledata_in_zip(path)?;
if md == "" {
return "";
}
let package = sxd_document::parser::parse(md)?;
let document = package.as_document();
let value = sxd_xpath::evaluate_xpath(&document, "/data/version")?;
Ok(value.string())
}
and then in line 326 of prod_core.rs
let version = check_for_vassal_file(file.file_path(),file)?;
if version != "" {
let mod_version = version.parse::<Version>()?; // .await perhaps?
let rel_version = self.db.get_release_version(release).await?;
if mod_version != rel_version {
return Err(AddFileError::ReleaseVersionMismatch(
mod_version, rel_version
));
}
}
Oh, and in line 371 of GameSection
perhaps add the paragraph
<p>
Game box images can be PNG, GIF, JPEG, SVG, AVIF, or WEBP.
Images shold be no bigger than 200px × 200px, to not take up too
much space on the page.
</p>
With this kind of information on the page, I think many user errors can be prevented.
Ah, and another issue - when I add or change the game box image on a project page and press the ✓ I get the error
APIError: 500 Internal Server Error: error returned from database: (code: 787) FOREIGN KEY constraint failed
but if I click ✓ again, then all is good and the image is set or changed. Whether this is due to the project_id
or user_id
foreign key, I cannot tell. This does not happen when editing other fields in the GameSection
interface.
Yours,
Christian