Search code examples
delphipasswordsarchivejedi

Delphi JEDI JCL JclCompression: Detect password-protected archive


Can you detect whether an archive is password protected with JclCompression from the JEDI Code Library (JCL)? I want to extract various archives, but obviously I don't want to show a password prompt unless the archive requires a password. I can set a password correctly, just not detect whether an archive needs one. The following SO post shows how to set the password:

Using 7-Zip from Delphi?

It's possible the option doesn't exist since there's a TODO in the procedure that appears to get archive properties such as ipEncrypted (from JCL 2.5):

procedure TJclDecompressItem.CheckGetProperty(
  AProperty: TJclCompressionItemProperty);
begin
  // TODO
end;

Solution

  • If the items within the archive are encrypted but the filenames aren't, just call ListFiles and after it returns loop over the items and check their Encrypted property. If any of them are true prompt the user for the password and assign it afterwards.

    If the filenames are encrypted too then no, the stock JCL distribution doesn't support detecting that beforehand. I have a fork of the JCL on github, and the sevenzip_error_handling branch contains a bunch of enhancements/fixes to TJclCompressionArchive, including the addition of an OnOpenPassword callback that's called if the filenames are encrypted. With that, the basic load looks like this:

    type
      TMyObject = class
      private
        FArchive: TJcl7zDecompressArchive;
        FEncryptedFilenames: Boolean;
        procedure GetOpenPassword(Sender: TObject;
          var APassword: WideString): Boolean;
      public
        procedure OpenArchive;
      end;
    
    ...
    
    procedure TMyObject.GetOpenPassword(Sender: TObject;
      var APassword: WideString): Boolean;
    var
      Dlg: TPasswordDialog;
    begin
      Dlg := TPasswordDialog.Create(nil);
      try
        Result := Dlg.ShowModal = mrOk;
        if Result then
        begin
          FEncryptedFilenames := True;
          FArchive.Password := Dlg.Password;
        end;
      finally
        Dlg.Free;
      end;        
    end;
    
    ...
    
    procedure TMyObject.OpenArchive;
    begin
      FArchive := TJcl7zUpdateArchive.Create(Filename);
      FArchive.OnOpenPassword := GetOpenPassword;
      while True do
      begin
        FEncryptedFilenames := False;
        try
          FArchive.ListFiles;
          Break;
        except
          on E: EJclCompressionFalse do
            if FEncryptedFilenames then
              // User probably entered incorrect password, loop
            else
              raise;
        end;
      end;
    end;