0 votes
by (210 points)
edited by

ZipArchive's PasswordRequired event appears to be called if the item is encrypted and the archive's password is null, but not if the item is encrypted and the archive's password is non-null and incorrect.

Any thoughts?

Thank you.

EDIT BY REBEX:
The issue was related to ZipItem.Open() method only.
It was fixed in version 2016 R1.

Applies to: Rebex ZIP

2 Answers

0 votes
by (72.7k points)

No, ZipArchive.PasswordRequired is raised if no password is specified or data cannot be extracted successfully (incorrect password, CRC32 check failed, corrupted data).

However, the event is raised for one file maximally 5 times by default. This value can be changed by the ZipArchive.Options.MaxPasswordRetryCount property.

Using this, you can easily write code to ask an user to enter the password N-times.

There is no possibility to instruct the ZipArchive class to raise PasswordRequired infinitely. You can either specify MaxPasswordRetryCount to int.MaxValue or write a loop like this:

while (true)
{
    zip.Options.MaxPasswordRetryCount = 0;
    zip.Password = GetPassword();
    try
    {
        zip.ExtractAll(outPath);
        break;
    }
    catch (ZipException ex)
    {
        if (ex.ProblemType == ArchiveProblemType.IncorrectPasswordOrCorruptedData)
            continue;
        throw;
    }
}
by (210 points)
edited by
Sorry, I wasn't clear. I understand PasswordRequired can't be called infinitely.

For an encrypted file, the PasswordRequired event is raised only if the attempted password is null. It should also be raised if the attempted password is non-null but incorrect.

Moreover, if an incorrect password is set in the PasswordRequired handler, an exception is raised rather than PasswordRequired being raised for another attempt (even with MaxPasswordRetryCount > 0).
+1 vote
by (72.7k points)

I understand your question, but I still have to say that the event should be raised. Can you please send me your code to support@rebex.net, I will diagnose it?

Here is my proof, that the PasswordRequired is raised even the Password is set to non-null but incorrect password:

Code:

using (ZipArchive zip = new ZipArchive(new MemoryStream()))
{
    // add an encrypted file
    zip.Password = "test3";
    zip.AddFile(new MemoryStream(new byte[1]), "file.txt");

    // set incorrect password
    zip.Password = "test";

    int idx = 0;

    // register handler
    zip.PasswordRequired += (s, e) =>
    {
        e.Password = "test" + idx;
        Console.WriteLine("ZipArchive.Password: {0}, PasswordRequired.Password: {1}", zip.Password, e.Password);
        idx++;
    };

    Console.WriteLine("MaxPasswordRetryCount: " + zip.Options.MaxPasswordRetryCount);

    // extract file
    zip["file.txt"].ExtractToArray();
}

Output:

MaxPasswordRetryCount: 5
ZipArchive.Password: test, PasswordRequired.Password: test0
ZipArchive.Password: test, PasswordRequired.Password: test1
ZipArchive.Password: test, PasswordRequired.Password: test2
ZipArchive.Password: test, PasswordRequired.Password: test3

Alternatively, instead of setting e.Password, you can set zip.Password. Result will be the same:

Code:

// register handler
zip.PasswordRequired += (s, e) =>
{
    zip.Password = "test" + idx;
    Console.WriteLine("ZipArchive.Password: {0}, PasswordRequired.Password: {1}", zip.Password, e.Password);
    idx++;
};

Output:

MaxPasswordRetryCount: 5
ZipArchive.Password: test0, PasswordRequired.Password:
ZipArchive.Password: test1, PasswordRequired.Password:
ZipArchive.Password: test2, PasswordRequired.Password:
ZipArchive.Password: test3, PasswordRequired.Password:
by (210 points)
This is a really beautiful proof.  Change your ExtractToArray() to Open() and you will replicate my results.
by (72.7k points)
Great, I am finally able to reproduce your issue.
I considered it as bug, I will send you fixed version to your email within hour.
by (72.7k points)
The bug was fixed in version 2016 R1.
...