Git Product home page Git Product logo

junrar's Introduction

Build Status Maven Central Sonatype Nexus (Snapshots) javadoc codecov

Junrar

Read and extracts from a .rar file. This is a fork of the junrar codebase, formerly on sourceforge.

Code may not be used to develop a RAR (WinRAR) compatible archiver.

Supported features

  • RAR 4 and lower (there is no RAR 5 support)
  • password protected archives (also with encrypted headers)
  • multi-part archives
  • extract from File and InputStream
  • extract to File and OutputStream

Installation

Gradle

implementation "com.github.junrar:junrar:{version}"

Gradle (Kotlin DSL)

implementation("com.github.junrar:junrar:{version}")

Maven

<dependency>
  <groupId>com.github.junrar</groupId>
  <artifactId>junrar</artifactId>
  <version>{version}</version>
</dependency>

where {version} corresponds to version as below:

  • Java 8 Version: Maven Central
  • Java 6 Compatible Version: Maven Central

Apache Commons VFS support has been removed from 5.0.0, and moved to a dedicated repo: https://github.com/junrar/commons-vfs-rar

Usage

Extract from a file to a directory:

Junrar.extract("/tmp/foo.rar", "/tmp");
//or
final File rar = new File("foo.rar");  
final File destinationFolder = new File("destinationFolder");
Junrar.extract(rar, destinationFolder);    
//or
final InputStream resourceAsStream = Foo.class.getResourceAsStream("foo.rar");//only for a single rar file
Junrar.extract(resourceAsStream, tempFolder);

Extract from an InputStream to an OutputStream

// Assuming you already have an InputStream from the rar file and an OutputStream for writing to
final Archive archive = new Archive(inputStream);
while (true) {
  FileHeader fileHeader = archive.nextFileHeader();
  if (fileHeader == null) {
      break;
  }
  archive.extractFile(fileHeader, outputStream); 
}

Extract from an InputStream to an InputStream

// Assuming you already have an InputStream from the rar file and an OutputStream for writing to
final Archive archive = new Archive(inputStream);
while (true) {
  FileHeader fileHeader = archive.nextFileHeader();
  if (fileHeader == null) {
      break;
  }
  try (InputStream is = archive.getInputStream(fileHeader)) {
      // Then use the InputStream for any method that uses that as an input, ex.:
      Files.copy(is, Paths.get("destinationFile.txt"));
  }
}

List files

final List<ContentDescription> contentDescriptions = Junrar.getContentsDescription(testDocuments);    

Extract a password protected archive

Junrar.extract("/tmp/foo.rar", "/tmp", "password");
//or
final File rar = new File("foo.rar");  
final File destinationFolder = new File("destinationFolder");
Junrar.extract(rar, destinationFolder, "password");    
//or
final InputStream resourceAsStream = Foo.class.getResourceAsStream("foo.rar");//only for a single rar file
Junrar.extract(resourceAsStream, tempFolder, "password");

Extract a multi-volume archive

Junrar.extract("/tmp/foo.001.rar", "/tmp");

Configuration

Junrar allows for some tuning using System Properties:

  • Options for Archive#getInputStream(FileHeader):
    • junrar.extractor.buffer-size: accepts any positive integer. Defaults to 32 * 1024.
      • Sets the maximum size used for the dynamic byte buffer in the PipedInputStream.
    • junrar.extractor.use-executor: accepts either true or false. Defaults to true.
      • If true, it uses a cached thread pool for extracting the contents, which is generally faster.
      • If false, it will create a new thread on each call. This may be slower, but may require slightly less memory.
      • Options for tuning the thread pool:
        • junrar.extractor.max-threads: accepts any positive integer. Defaults to 2^31.
          • Sets the maximum number of threads to be used in the pool. By default, there is no hard limit on the number of threads, but they are only created when needed, so the maximum should not exceed the number of threads calling this method at any given moment. Use this if you need to restrict the number of threads.
        • junrar.extractor.thread-keep-alive-seconds: accepts any positive integer. Defaults to 5.
          • Sets the number of seconds a thread can be kept alive in the pool, waiting for a next extraction operation. After that time, the thread may be stopped.

junrar's People

Contributors

0xanasezz avatar alecharp avatar andrebrait avatar awaters1 avatar befora avatar beothorn avatar dependabot[bot] avatar edmund-wagner avatar github-actions[bot] avatar gotson avatar icirellik avatar mingun avatar nadahar avatar nawa avatar robbwatershed avatar rogiel avatar semantic-release-bot avatar sunny-shu avatar tballison avatar theobisproject avatar xavier630 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

junrar's Issues

How to disable logging in System.err?

Hi. We are receiving this information in System.err:

[ESMTPS004] WARN com.github.junrar.Archive - Support for rar version 5 is not yet implemented!
[ESMTPS004] WARN com.github.junrar.Archive - exception in archive constructor maybe file is encrypted, corrupt or support not yet implemented
com.github.junrar.exception.UnsupportedRarV5Exception
        at com.github.junrar.Archive.readHeaders(Archive.java:353)
        at com.github.junrar.Archive.setChannel(Archive.java:198)
        at com.github.junrar.Archive.setVolume(Archive.java:778)
        at com.github.junrar.Archive.<init>(Archive.java:148)
        at com.github.junrar.Archive.<init>(Archive.java:177)
        ....

Is it possible to disable this logging?

Maven executes not all unit tests

When running mvn test with Maven 3.5.2 and Java 8 only the unit tests in the SimpleTest class are executed. The other tests in the JUnRarTestUtil and JunrarTests class are ignored.

Support encrypted archives

With the recent work on dev branch we can decrypt password protected archives, but not yet encrypted archives.

@sunny-shu i think i have found where the salt for the header encryption is located. I have found this website that talk about it (it's in french). The 8 byte salt is located just after the Main Header block. They also have working python code for decryption which confirms the location of the salt.

I think we should refactor the code a bit, because the decrypt code is bundled inside DataComprIO. Ideally we should split the read code from the channel and the decryption code. Decryption code should be moved to crypt package in my opinion.

The code in DataComprIO should look more like: (pseudo-code):

  • read from channel (could be encrypted or not) into buffer
  • if fileHeader.isEncrypted: decrypt buffer

For the Archive class it's a bit more complex, because there are a lot of read operations in the readHeaders method. I was thinking we could use a thin wrapper around the channel to provide transparent decryption.

What do you think?

Add more tests

Many parts of the code need refactor, but first tests must be added to ensure the code still functions as expected.

Error unpacking large file(>2G)

Describe the bug
Error unpacking large file(>2G).

To Reproduce
When unpacking a large file(>2G),The program will report an error.

Expected behavior
I want to be able to unzip large files.

File
If possible, please attach or provide a link to the RAR file.
On line 124 of RandomAccessInputStream.readUntil() function ,the 'length' variable is int, wen the file length is large then Integer.MAX_VALUE, the 'length' variable whill be negative, then the program will not read the content.
Environment (please complete the following information):

  • OS: [Windows]
  • Junrar version: [7.5.3]

Additional context
Function RandomAccessInputStream.readUntil() put what you read in a vector, when the file is too large may happen OOM.

[BUG]Using inputStream to decompress some huge files causes OutOfMemoryError

Describe the bug
Using inputStream to decompress some huge files causes OutOfMemoryError。

the code like:
InputSteam inputStream = new FileInputStream(file);
Archive arch = new Archive(inputSteam);

All code is run in thread,The reason seems to be readHeaders() method will never reach EndArcHeader on Archive.java.

To Reproduce
NA

Expected behavior
NA

File
http://1pan.top/page/5fa5358d4243e72091c9c7c4
click:"获取下载链接"

Environment (please complete the following information):

  • OS: [Android Q/R]
  • Junrar version: [7.4.0]

Additional context

[Optimization] Junrar shouldn't load the entirety of the file into memory

Describe the bug
When initializing an archive to read its headers :

try (InputStream fi = FileHelper.getInputStream(context, file); BufferedInputStream bis = new BufferedInputStream(fi, BUFFER)) {
   Archive input = new Archive(bis);
}

Junrar loads the whole RAR file into memory (see screencap below), which isn't shocking in a desktop environment, but causes issues in a mobile environment (Android) where resources are more scarce. Some of my users gets OOM crashes because of that, when trying to read big files.

Stracktrace after an OOM for a 510MB RAR file
Fatal Exception: java.lang.OutOfMemoryError: Failed to allocate a 528 byte allocation with 63984 free bytes and 62KB until OOM, target footprint 536870912, growth limit 536870912; failed due to fragmentation (largest possible contiguous allocation 443285504 bytes) at com.github.junrar.io.RandomAccessInputStream.readUntil(RandomAccessInputStream.java:106) at com.github.junrar.io.RandomAccessInputStream.read(RandomAccessInputStream.java:72) at com.github.junrar.io.RandomAccessInputStream.readFully(RandomAccessInputStream.java:93) at com.github.junrar.io.SeekableReadOnlyInputStream.readFully(SeekableReadOnlyInputStream.java:51) at com.github.junrar.io.RawDataIo.readFully(RawDataIo.java:75) at com.github.junrar.Archive.readHeaders(Archive.java:312) at com.github.junrar.Archive.setChannel(Archive.java:178) at com.github.junrar.Archive.setVolume(Archive.java:675) at com.github.junrar.Archive.<init>(Archive.java:128) at com.github.junrar.Archive.<init>(Archive.java:157)

Memory monitor when loading a 285MB RAR file with the code snipped above
image
NB : loading happens twice because my code calls it twice 😉

Expected behavior
Junrar should only load what's useful in memory (e.g. headers, binary data of files to unrar), not the entire file.

File
Same on any RAR file

Environment (please complete the following information):

  • OS: Android 8.1 to 10 (didn't test on other versions)
  • Junrar version: 7.4.0

[BUG] Special characters are not recognized anymore

Describe the bug
Special characters in filenames are not recognized anymore. It was fixed in #34 but the problem is present again in last versions.

To Reproduce

final var file = Path.of("issue.rar");
Junrar.getContentsDescription(file.toFile()).forEach(System.out::println);
Exception in thread "main" com.github.junrar.exception.CorruptHeaderException: Invalid filename: ?.txt
	at com.github.junrar.rarfile.FileHeader.<init>(FileHeader.java:170)
	at com.github.junrar.Archive.readHeaders(Archive.java:450)
	at com.github.junrar.Archive.setChannel(Archive.java:199)
	at com.github.junrar.Archive.setVolume(Archive.java:811)
	at com.github.junrar.Archive.<init>(Archive.java:149)
	at com.github.junrar.Archive.<init>(Archive.java:170)
	at com.github.junrar.Junrar.createArchiveOrThrowException(Junrar.java:121)
	at com.github.junrar.Junrar.getContentsDescription(Junrar.java:76)

Expected behavior
Should print テ.txt.

File
issue.zip (extract to get the RAR file)

Environment

  • OS: Windows
  • Junrar version: 7.5.4 (latest)

Multipart extraction from stream

The current implementation doesn't work when trying to extract a multipart archive from a stream.

It should be possible to have it working, but it would require to provide all of the the input streams in order.

Could the junrar project benefit from free security help?

Hello junrar community! Open Source Technology Improvement Fund is piloting out helping critical projects like junrar with their security needs. We have some resources dedicated to helping improve security posture and tooling. I wasn't sure how best to reach out. Please let me know if this sounds interesting and who to connect with. Thank you in advance!

[BUG]com.github.junrar.exception.UnsupportedRarV5Exception

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior.

Expected behavior
A clear and concise description of what you expected to happen.

File
If possible, please attach or provide a link to the RAR file.

Environment (please complete the following information):

  • OS: [e.g. Windows]
  • Junrar version: [e.g. 4.0.0]

Additional context
Add any other context about the problem here.

Multi part file not support well

When decopressing multi part file, if the last file in xxx.part1.rar has no data in next volume, there will not go on decompressing xxx.part2.rar

[BUG]A carefully crafted RAR archive can trigger an infinite loop while parsing

Describe the bug
A carefully crafted RAR archive can trigger an infinite loop while parsing the file. This could be used to mount a denial of service attack against services that use junrar.

To Reproduce

InputStream inputStream = new FileInputStream("path/to/these/files");

final Archive archive = new Archive(inputStream);
while (true) {
	FileHeader fileHeader = archive.nextFileHeader();
	if (fileHeader == null) {
		break;
	}
	archive.extractFile(fileHeader, OutputStream.nullOutputStream()); 
}

Expected behavior
Infinite loop.

File
loops.zip

Environment (please complete the following information):

  • OS: Mac OS 12.1 & Ubuntu Linux 16.04 (4.15.0-163-generic)
  • Junrar version: 7.5.0

Additional context
These files can trigger the same while loop mentioned in this issue.

[BUG]A carefully crafted RAR archive can trigger an infinite loop while parsing.

Describe the bug
A carefully crafted RAR archive can trigger an infinite loop while parsing the file. This could be used to mount a denial of service attack against services that use junrar.

To Reproduce

String encodedString = "UmFyIRoHAM+Qcw4AAAAAKgAAAAAXF3QggE4ASwEAACICAAADZXUl9710qkIdNC4ApIEAAF9fTUlDT1NYXC5fYXBwbGUpdG91Y2gtaWNvbi03MC1wcmVjb21wb3NlZC5wbmcJ3RFMy9WBV2REVHpele6iC2cnv5JltbeqElgNF1pFjxFEQOWXhHq2ScsnIV5jb21wb3NlZC6Qcw4AAAAAKgAAAGREVA+VZ3qi7l4nv5Jly7W3qhJYDZDaRcURs78ajbZ7SWjLJyFah8afvQIAAADEvXYBRXwAQAUBBgAAAAAAAC/jZmlgun4oIP1Z4AG7ybwKgFwrlLrUKiSxjkiGPQFX/wAAL/8A////////+SVUCUzV3REly4FXZEReVHqV7qJYZyezxAAAAAAAAL+SZbVft6ruqo0QWkWPEUSMA5dA5QCE+3q2AAAAAAAAAgBJyyMhXocAAAAAAIefGkfCXwV8FHoKlbPhXgA//O5fgDAhAQAAAAAAAABuZphoBm71hnkO4GbmYGm6fs7f/VkAAAACAAFXqLsj6Xd3d3d3d3d3d3dgd3d3d3d3d3d3d3d3V3d3d3d3d3d3d3d3d3d3d3d3d3dzd3V3d3fTd3d3OJIAAABSAHIhGgcAz5BzAAANAAAAAAAAABcXdHkASwEAACICAAAD////////////////////+////////z1ldSV3dw==";
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
InputStream inputStream = new ByteArrayInputStream(decodedBytes);

final Archive archive = new Archive(inputStream);
while (true) {
	FileHeader fileHeader = archive.nextFileHeader();
	if (fileHeader == null) {
		break;
	}
	archive.extractFile(fileHeader, OutputStream.nullOutputStream()); 
}

Expected behavior
Infinite loop.

File
loop-913d3158487310b1b4b74086ab888f5ed56a8493.zip

Environment (please complete the following information):

  • OS: Mac OS 12.1 & Ubuntu Linux 16.04 (4.15.0-163-generic)
  • Junrar version: 7.4.0

Additional context
It seems this PoC can reach [this while loop] (

while (true) {
VMPreparedCommand cmd = preparedCode.get(IP);
int op1 = getOperand(cmd.getOp1());
int op2 = getOperand(cmd.getOp2());
switch (cmd.getOpCode()) {
case VM_MOV:
setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
mem, op2)); // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
break;
case VM_MOVB:
setValue(true, mem, op1, getValue(true, mem, op2));
break;
case VM_MOVD:
setValue(false, mem, op1, getValue(false, mem, op2));
break;
case VM_CMP: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = value1 - getValue(cmd.isByteMode(), mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_CMPB: {
int value1 = getValue(true, mem, op1);
int result = value1 - getValue(true, mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_CMPD: {
int value1 = getValue(false, mem, op1);
int result = value1 - getValue(false, mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_ADD: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = (int) ((((long) value1 + (long) getValue(cmd
.isByteMode(), mem, op2))) & 0xffffffff);
if (cmd.isByteMode()) {
result &= 0xff;
flags = (result < value1) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: ((result & 0x80) != 0) ? VMFlags.VM_FS
.getFlag() : 0);
// Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ?
// VM_FS:0));
} else {
flags = (result < value1) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
}
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_ADDB:
setValue(true, mem, op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff
+ (long) getValue(true, mem, op2) & 0xFFffFFff));
break;
case VM_ADDD:
setValue(
false,
mem,
op1,
(int) ((long) getValue(false, mem, op1) & 0xFFffFFff
+ (long) getValue(false, mem, op2) & 0xFFffFFff));
break;
case VM_SUB: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = (int) ((long) value1 & 0xffFFffFF
- (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff);
flags = (result == 0) ? VMFlags.VM_FZ.getFlag()
: (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
setValue(cmd.isByteMode(), mem, op1, result); // (Cmd->ByteMode,Op1,Result);
}
break;
case VM_SUBB:
setValue(true, mem, op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff
- (long) getValue(true, mem, op2) & 0xFFffFFff));
break;
case VM_SUBD:
setValue(
false,
mem,
op1,
(int) ((long) getValue(false, mem, op1) & 0xFFffFFff
- (long) getValue(false, mem, op2) & 0xFFffFFff));
break;
case VM_JZ:
if ((flags & VMFlags.VM_FZ.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JNZ:
if ((flags & VMFlags.VM_FZ.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_INC: {
int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff + 1);
if (cmd.isByteMode()) {
result &= 0xff;
}
setValue(cmd.isByteMode(), mem, op1, result);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_INCB:
setValue(
true,
mem,
op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff + 1));
break;
case VM_INCD:
setValue(false, mem, op1, (int) ((long) getValue(false, mem,
op1) & 0xFFffFFff + 1));
break;
case VM_DEC: {
int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff - 1);
setValue(cmd.isByteMode(), mem, op1, result);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_DECB:
setValue(
true,
mem,
op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff - 1));
break;
case VM_DECD:
setValue(false, mem, op1, (int) ((long) getValue(false, mem,
op1) & 0xFFffFFff - 1));
break;
case VM_JMP:
setIP(getValue(false, mem, op1));
continue;
case VM_XOR: {
int result = getValue(cmd.isByteMode(), mem, op1)
^ getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_AND: {
int result = getValue(cmd.isByteMode(), mem, op1)
& getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_OR: {
int result = getValue(cmd.isByteMode(), mem, op1)
| getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_TEST: {
int result = getValue(cmd.isByteMode(), mem, op1)
& getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_JS:
if ((flags & VMFlags.VM_FS.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JNS:
if ((flags & VMFlags.VM_FS.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JB:
if ((flags & VMFlags.VM_FC.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JBE:
if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JA:
if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JAE:
if ((flags & VMFlags.VM_FC.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_PUSH:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, getValue(false, mem,
op1));
break;
case VM_POP:
setValue(false, mem, op1, getValue(false, mem, R[7]
& VM_MEMMASK));
R[7] += 4;
break;
case VM_CALL:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, IP + 1);
setIP(getValue(false, mem, op1));
continue;
case VM_NOT:
setValue(cmd.isByteMode(), mem, op1, ~getValue(
cmd.isByteMode(), mem, op1));
break;
case VM_SHL: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = value1 << value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| (((value1 << (value2 - 1)) & 0x80000000) != 0 ? VMFlags.VM_FC
.getFlag()
: 0);
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SHR: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = value1 >>> value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| ((value1 >>> (value2 - 1)) & VMFlags.VM_FC.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SAR: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = ((int) value1) >>> value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| ((value1 >>> (value2 - 1)) & VMFlags.VM_FC.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_NEG: {
int result = -getValue(cmd.isByteMode(), mem, op1);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : VMFlags.VM_FC
.getFlag()
| (result & VMFlags.VM_FS.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_NEGB:
setValue(true, mem, op1, -getValue(true, mem, op1));
break;
case VM_NEGD:
setValue(false, mem, op1, -getValue(false, mem, op1));
break;
case VM_PUSHA: {
for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) {
setValue(false, mem, SP & VM_MEMMASK, R[i]);
}
R[7] -= regCount * 4;
}
break;
case VM_POPA: {
for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4) {
R[7 - i] = getValue(false, mem, SP & VM_MEMMASK);
}
}
break;
case VM_PUSHF:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, flags);
break;
case VM_POPF:
flags = getValue(false, mem, R[7] & VM_MEMMASK);
R[7] += 4;
break;
case VM_MOVZX:
setValue(false, mem, op1, getValue(true, mem, op2));
break;
case VM_MOVSX:
setValue(false, mem, op1, (byte) getValue(true, mem, op2));
break;
case VM_XCHG: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
mem, op2));
setValue(cmd.isByteMode(), mem, op2, value1);
}
break;
case VM_MUL: {
int result = (int) (((long) getValue(cmd.isByteMode(), mem, op1)
& 0xFFffFFff
* (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff) & 0xFFffFFff);
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_DIV: {
int divider = getValue(cmd.isByteMode(), mem, op2);
if (divider != 0) {
int result = getValue(cmd.isByteMode(), mem, op1) / divider;
setValue(cmd.isByteMode(), mem, op1, result);
}
}
break;
case VM_ADC: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int FC = (flags & VMFlags.VM_FC.getFlag());
int result = (int) ((long) value1 & 0xFFffFFff
+ (long) getValue(cmd.isByteMode(), mem, op2)
& 0xFFffFFff + (long) FC & 0xFFffFFff);
if (cmd.isByteMode()) {
result &= 0xff;
}
flags = (result < value1 || result == value1 && FC != 0) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SBB: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int FC = (flags & VMFlags.VM_FC.getFlag());
int result = (int) ((long) value1 & 0xFFffFFff
- (long) getValue(cmd.isByteMode(), mem, op2)
& 0xFFffFFff - (long) FC & 0xFFffFFff);
if (cmd.isByteMode()) {
result &= 0xff;
}
flags = (result > value1 || result == value1 && FC != 0) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_RET:
if (R[7] >= VM_MEMSIZE) {
return (true);
}
setIP(getValue(false, mem, R[7] & VM_MEMMASK));
R[7] += 4;
continue;
case VM_STANDARD:
ExecuteStandardFilter(VMStandardFilters.findFilter(cmd.getOp1()
.getData()));
break;
case VM_PRINT:
break;
}
IP++;
--maxOpCount;
}
)
but never break.

[BUG] Exception swallowed during initialization -- silently missing data we used to get in 7.4.1

Describe the bug
In 7.5.0, the parser is stopping earlier than it did in 7.4.1 without throwing an exception. The exception is logged, but the client has no idea that there was a problem with the file.

To Reproduce
Iterate through the entries...

          Archive rar = new Archive(tis.getFile());

            if (rar.isEncrypted()) {
                throw new EncryptedDocumentException();
            }

            FileHeader header = rar.nextFileHeader();
            while (header != null && !Thread.currentThread().isInterrupted()) {
                //DO STUFF
                header = rar.nextFileHeader();
            }

Expected behavior
Please throw the exception or continue parsing the file as happened in 7.4.1.

File
The file is part of our large regression corpus tests over on Apache Tika. We grabbed this file from Common Crawl. It is possible that it is truncated. My Ubuntu package utility does not like the file. It may actually be corrupted. We were able to get quite a bit more data out of it with 7.4.1.

https://corpora.tika.apache.org/base/docs/commoncrawl3/3X/3X4JRZZ4TQ2GK4QQDQEXMFCVLM3FM5I4

Environment (please complete the following information):

  • OS: Ubuntu, but not relevant
  • Junrar version: 7.5.0

Additional context
Decompiler shows the issue in Archive#setChannel:

        try {
            this.readHeaders(length);
        } catch (UnsupportedRarV5Exception | CorruptHeaderException | BadRarArchiveException | UnsupportedRarEncryptedException var6) {
            logger.warn("exception in archive constructor maybe file is encrypted, corrupt or support not yet implemented", var6);
            throw var6;
        } catch (Exception var7) {
            logger.warn("exception in archive constructor maybe file is encrypted, corrupt or support not yet implemented", var7);
        }

The exception is:

WARN  [main] 19:50:32,239 com.github.junrar.Archive exception in archive constructor maybe file is encrypted, corrupt or support not yet implemented
java.lang.ArrayIndexOutOfBoundsException: 55
	at com.github.junrar.io.Raw.readShortLittleEndian(Raw.java:104) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.rarfile.FileHeader.<init>(FileHeader.java:192) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.Archive.readHeaders(Archive.java:439) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.Archive.setChannel(Archive.java:198) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.Archive.setVolume(Archive.java:778) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.Archive.<init>(Archive.java:148) ~[junrar-7.5.0.jar:?]
	at com.github.junrar.Archive.<init>(Archive.java:161) ~[junrar-7.5.0.jar:?]
	at org.apache.tika.parser.pkg.RarParser.parse(RarParser.java:75) ~[classes/:?]
	at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:281) ~[classes/:?]
	at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:281) ~[classes/:?]
	at org.apache.tika.parser.AutoDetectParser.parse(AutoDetectParser.java:143) ~[classes/:?]

FileHeader times change depending on the current timezone

Hi @andrebrait ,

i feel like #76 introduced a bug around FileHeader times.

I'm running ArchiveTest.ExtendedTimeTest and i get different mtime depending on the default timezone:

Timezone: Asia/Kolkata
mtime as Date: Wed Feb 23 10:24:19 IST 2022
mtime as FileTime: 2022-02-23T04:54:19.1915433Z

Timezone: America/Los_Angeles
mtime as Date: Wed Feb 23 10:24:19 PST 2022
mtime as FileTime: 2022-02-23T18:24:19.1915433Z

Timezone: Europe/Amsterdam
mtime as Date: Wed Feb 23 10:24:19 CET 2022
mtime as FileTime: 2022-02-23T09:24:19.1915433Z

Timezone: America/Sao_Paulo
mtime as Date: Wed Feb 23 10:24:19 BRT 2022
mtime as FileTime: 2022-02-23T13:24:19.1915433Z

Here is what rar command line gives me for that file:

        Name: files/test/short-text.txt
        Type: File
        Size: 22
 Packed size: 22
       Ratio: 100%
       mtime: 2022-02-23 10:24:19,191543300
       ctime: 2022-02-23 10:34:59,759754700
       atime: 2022-03-02 18:45:18,694091100
  Attributes: ..A....
       CRC32: 8A3BE83B
     Host OS: Windows
 Compression: RAR 1.5(v29) -m0 -md=128K

However in the test comment i see:

Original timestamps:
MTime: 2022-02-23T09:24:19.191543300Z
CTime: 2022-02-23T09:34:59.759754700Z
ATime: 2022-03-02T17:45:18.694091100Z

which is different from what the rar CLI gives me.

FileTime doesn't have a timezone AFAIK, and thus should be timezone insensitive.

What do you reckon?

NPE on a possibly corrupt rar file

Hello,

I came across a NullPointerException from the Archive class which I wanted to bring to your notice. I believe this is happening on a corrupt file or an unsupported version (not really sure)

The problem happens on the file when reading headers, BaseBlock.getHeaderType() one of the Bytes that it comes across is a 0, hence it returns a null.

In the Archive class, there is a switch case based on the return value from above in the method readHeaders. The switch case throws a NullPointerException, I think handling the NPE and returning a RarException would be a better thing to do from here.

I'm attaching a file which can reproduce the issue. The rar in question is inside the zip.

some-corrupt-file.zip

Thanks,

On attempt to extract archive RAR5 the archive is locked

On attempt to extract rar archive version RAR5 the archive is locked.
Version 3.1.0, call:

File arch = new File(archive);
File dest = new File(destination);
Junrar.extract(arch, dest);

Exception stack:

Support for rar version 5 is not yet implemented!
exception in archive constructor maybe file is encrypted, corrupt or support not yet implemented
com.github.junrar.exception.RarException: unsupportedRarArchive
at com.github.junrar.Archive.readHeaders(Archive.java:282)
at com.github.junrar.Archive.setFile(Archive.java:153)
at com.github.junrar.Archive.setVolume(Archive.java:631)
at com.github.junrar.Archive.(Archive.java:123)
at com.github.junrar.Archive.(Archive.java:100)
at com.github.junrar.Junrar.createArchiveOrThrowException(Junrar.java:91)
at com.github.junrar.Junrar.extract(Junrar.java:33)

On attempt to delete the archive, the exception is thrown:

java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process

Solve license confusion

There are different, mutually excluding licenses on some files GPL (/junrar/src/main/java/com/github/junrar/Volume.java).
Not sure how to solve this.

[QUESTION / Architecture] DocumentFile support for Android

I'm about to fork and use this library on an Android 10 project where all file I/O has been rewritten to use DocumentFile instead of plain Java File. We did that in order to work with the Android SAF and comply to scoped storage security constraints.

Thing is, I do know how to change the code to replace File with DocumentFile but I have no idea how to properly integrate these changes into the library's structure without introducing Android dependencies that would certainly break stuff for "pure Java" users.

I'd like to ask to those who are familiar with this lib (and with Gradle) what would be the cleanest way to do that. Is there some new module / structure we could set up in Gradle ?

[BUG] NullPointerException when extracting solid archives

Describe the bug

Unpack.window is null during Archive.extractFile() if the archive is solid, which results in a NullPointerException.

Stacktrace:

java.lang.NullPointerException: Attempt to write to null array
     at com.github.junrar.unpack.Unpack.unpack29(Unpack.java:249)
     at com.github.junrar.unpack.Unpack.doUnpack(Unpack.java:116)
     at com.github.junrar.Archive.doExtractFile(Archive.java:587)
     at com.github.junrar.Archive.extractFile(Archive.java:534)
     ...

This seems to be a result of Unpack.init() only called when hd.isSolid() return false, and Unpack.init() seems to be the only wait to intialize Unpack.window which is used unconditionally in Unpack.unpack29().

if (!hd.isSolid()) {
this.unpack.init(null);
}

window[unpPtr++] = (byte) Ch;

To Reproduce

Use junrar to extract a file inside a solid archive, such as https://www.rarlab.com/themes/RARaddin_48x48.theme.rar .

In my case I'm using my own app Material Files to reproduce but I think any user of this library should encounter the same issue.

Expected behavior

Solid archives should be extractable and shouldn't cause a NullPointerException.

File

https://www.rarlab.com/themes/RARaddin_48x48.theme.rar

Environment (please complete the following information):

  • OS: Android
  • Junrar version: 7.4.1

Additional context

zhanghai/MaterialFiles#247

extract encrypted rar file with encrypted content as file content

There's a situation when i encrypted file with winRAR and select the "show password" button on the operation UI(as snapshort.bmp), when extract content from the file(test.rar), it throwed the RarException but the "PipedOutputStream out" parameter has been assigned the wrong value which is encrypted content.
while another encrypted file without selected the "show password" button, when extract it(1534240020.rar), it also thowed RarException but return nothing.

There behavied different at line 191 in ModelPPM decodeInit routine when it begin to unpack the file. But i don't know the reason.

test.zip

review class organizations and public API

I spent quite some time understanding the current organization of the code, here is what i have gathered so far:

  • To me there are 2 possible entry points in the library:
    • the Junrar class which provides only static methods. Those methods can be considered as helper methods, and remove the need to instantiate the Archive class.
    • the Archive class, which provide more fine-grained operations, for example to get access to the FileHeaders directly, or to use the UnrarCallback to monitor progress, or to have more details on the archive's option, like encryption, solid, etc.

On the other side, there are 2 interfaces Volume and VolumeManager that helps (via their implementation for File and InputStream) to handle RAR archives spanning multiple files.

One problem i have is that some of the Junrar methods and Archive constructors accept a VolumeManager as argument, de-facto exposing this interface in the public API, which means the implementation are also leaking in the client.

Given that both FileVolumeManager and InputStreamVolumeManager are just wrappers around File and InputStream, those should not be exposed in the public API, which should instead take the native File (or String as path) and InputStream arguments.

It seems most if not all classes are public at the moment. The underlying implementation should be made private as much as possible.

Modernize project

The project has not been very well maintained over the past years, i would like to make it easier to contribute.

I have setup a branch modernize to perform all the work needed:

  • migrate from Maven to Gradle
  • migrate to JUnit 5
  • migrate to AssertJ
  • carve-out VFS support into its own repo (#41)
  • raise the minimum version of Java to 8 (#20)
  • use Conventional Commits and setup commit-hooks with commitlint / husky
  • setup automatic publishing of releases with semantic-release to JCenter and Mavencentral
  • setup code coverage reporting

People who need Java 6/7 will still be able to use version 4.0.0.

Integration with Google oss-fuzz fuzzing service

Hi, I would like to help integrate this project into Google oss-fuzz.
It is a fuzzing service Google provided for automatically testing important open-source repositories.
I've submitted an integration PR to oss-fuzz.
The integration contains the scripts and fuzzers for testing this project.
Are you happy to support this integration?
Additionally, can you provide your email? It will be used to notify you when oss-fuzz found bugs of this project.

Minimum needed Java version

I am a bit confused about which is the minimum Java version requirement for Junrar. The version in the pom.xml for the maven-compiler plugin is Java 6 (source and target) but the apache-vfs dependency states that the minimum Java version for 2.2 is Java 7 (see https://commons.apache.org/proper/commons-vfs/).
Was the version of apache-vfs raised on purpose to require Java 7 for Junrar and the changes for the maven-compiler-plugin forgotten or did this happen on accident?

CrcErrorException

Describe the bug
He there .
I am trying to extract some rar files here is an example
The file was successfully extracted but i got this error

com.github.junrar.exception.CrcErrorException
	at com.github.junrar.Archive.doExtractFile(Archive.java:591)
	at com.github.junrar.Archive.extractFile(Archive.java:531)
	at com.github.junrar.LocalFolderExtractor.extract(LocalFolderExtractor.java:52)
	at com.github.junrar.Junrar.tryToExtract(Junrar.java:180)
	at com.github.junrar.Junrar.extractArchiveTo(Junrar.java:154)
	at com.github.junrar.Junrar.extract(Junrar.java:54)
	at com.github.junrar.Junrar.extract(Junrar.java:46)
	at ixidev.shamela.data.filedownloader.FileDownloaderKtTest.extractRarFile(FileDownloaderKtTest.kt:73)
	at java.lang.reflect.Method.invoke(Native Method)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2204)

And here is my code is an Instrumented test on Android studio

    @Test
    fun extractRarFile() {
        val bookId = 151016
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        val inputStream = appContext.assets.open("$bookId.rar")
        val tempFolder = File(appContext.cacheDir, "$bookId")
        tempFolder.deleteOnExit()
        tempFolder.mkdir()
        Junrar.extract(inputStream, tempFolder)
        val extract = tempFolder.list()!!
        assertEquals(extract.size, 1)
    }

Expected behavior
The rar file contain a file with Arabic name, I thing the error comes from here
File
rar file

Environment (please complete the following information):

  • OS: Android studio & Ubuntu , Android 10
  • Junrar version: [7.4.0]

Thanks

com.github.junrar.exception.CrcErrorException during extracting of archive

I have rar archive (http://qsp.su/index2.php?option=com_sobi2&sobi2Task=dd_download&fid=460&format=html) When i try to extract it on android:

    public static boolean unrar(Context context, DocumentFile rarFile, File gameDir) {
        try (InputStream in = context.getContentResolver().openInputStream(rarFile.getUri())) {
            try (Archive archive = new Archive(in)) {
                FileHeader fileHeader;
                while ((fileHeader = archive.nextFileHeader()) != null) {
                    final FileHeader fh = fileHeader;
                    extractEntry(
                            fh.getFileName(),
                            fh.isDirectory(),
                            gameDir,
                            out -> archive.extractFile(fh, out));
                }
                return true;
            }
        } catch (Exception ex) {
            logger.error("Failed to extract a RAR archive", ex);
            return false;
        }
    }

    private static void extractEntry(
            String entryName,
            boolean entryDir,
            File gameDir,
            ThrowingStreamWriter writer) throws Exception {
        String normEntryName = entryName.replace("\\", "/");
        if (entryDir) {
            createDirectories(gameDir, removeTrailingSlash(normEntryName));
            return;
        }
        File parentDir = getParentDirectory(gameDir, normEntryName);
        String filename = getFilename(normEntryName);
        File file = createFile(parentDir, filename);
        try (FileOutputStream out = new FileOutputStream(file)) {
            writer.accept(out);
        }
    }

    @FunctionalInterface
    private interface ThrowingStreamWriter {
        void accept(FileOutputStream stream) throws Exception;
    }
}

I get an exception:

02-20 01:39:42.903 32078   431 E Quest Player:ArchiveUtil: com.github.junrar.exception.CrcErrorException
02-20 01:39:42.903 32078   431 E Quest Player:ArchiveUtil:  at com.github.junrar.Archive.doExtractFile(Archive.java:594)
02-20 01:39:42.903 32078   431 E Quest Player:ArchiveUtil:  at com.github.junrar.Archive.extractFile(Archive.java:534)

Please fix this issue,because winrar for windows extract it without any problems.

[BUG] Pom License link leads to 404

Describe the bug
On https://search.maven.org/artifact/com.github.junrar/junrar/7.5.2/jar the pom contains a link to UnRar License which returns 404 ( https://github.com/junrar/junrar/LICENSE.md )

To Reproduce
search online in the maven repository.

Expected behavior
The link to the license file in the pom should absolutely be a valid link (i.e. https://github.com/junrar/junrar/blob/master/LICENSE ). Ideally it would be a permalink to the github hash or tag associated with the release so that licensing on old versions are not misrepresented in the future if there is a change to the license. (i.e. https://github.com/junrar/junrar/blob/v7.5.2/LICENSE )

File
Not applicable

Environment (please complete the following information):
Web/browse/build

[BUG] "IllegalArgumentException: Pipe Size <= 0" with an empty file in the archive

Describe the bug
Reading an archive with an empty file is throwing an IllegalArgumentException.

To Reproduce

try (final var archive = new Archive(Path.of("file.rar").toFile())) {
	while (true) {
		final var fileHeader = archive.nextFileHeader();
		if (null == fileHeader) {
			break;
		}
		final var outputStream = new ByteArrayOutputStream();
		//archive.extractFile(fileHeader, outputStream); // OK
		archive.getInputStream(fileHeader).transferTo(outputStream); // IllegalArgumentException: Pipe Size <= 0
		System.out.println(outputStream);
	}
}

Expected behavior
Should return an empty InputStream instead of throwing an IllegalArgumentException.

File
file.rar.zip
(need to extract the ZIP file to get the RAR inside)

Environment (please complete the following information):

  • OS: Windows
  • Junrar version: 7.5.1 (latest)

Additional context
None

Different results when initialising Archive with InputStream vs FileVolumeManager

In my case, making an Archive with 1. and then iterating through the files finds N number of files, but using 2. and iterating through finds 4N files.

InputStream inputStream = new FileInputStream(file);
final Archive archive = new Archive(inputStream);
final Archive a = new Archive(new FileVolumeManager(file));

The iteration is along the lines of:

   while (true) {
                FileHeader fh = a.nextFileHeader();
                if (fh == null) {
                    break;
                }

                String path = fh.getFileNameString();
                if (path.toLowerCase().contains("some_text")) {
                    try {
                        // Do something
                    } catch (IOException e) {
                        // Do something else
                    }
                }
            }

It's plausible that when using a InputStream, the next file header from the archive ends up being null one sooner than it should be.

New maintainer for this project

There is no one actively maintaining this project.
If anyone who is using this library in an active project have interest in taking over let me know.

RAR5 is not supported

Junrar just throws exception (there) when trying to extract from such archive.

Is there any plans to support it?

Hrlp me

Hello my used in code

try{
	Junrar.extract("/storage/emulated/0/Download/rarlng_android.rar", "/storage/emulated/0/Download/");
}catch(Exception e){
	Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();

}

End app me Error not The file is not extracted

Screenshot_20220623-225901_rar

If you can help me and send me a Java code, I can extract it in the file path. Thank you. I am waiting for your reply.

Output folder (Hadoop again)

I'm sorry, byt #11 is not enougth for complete Hadoop based unrar soultion.

  1. Output folder should also be something like org.apache.hadoop.fs.Path.

  2. And one another thing; sometimes we have a huge .rar file, where only 10% of packaged files should be unpacked. It will be great if we could have some filtering capabilities. Like Stream, so that we can iterate it and filter, map elements to OutputStream, and unpack only filtered files.

And, if we can have #2 implemented as mapping function from FileHeader to OutputStream, then we probably can implement #1 using this function only.

Remove dependency from org.slf4j.LoggerFactory

Describe the bug
Not all projects depends on LoggerFactory. Such general purpose library should not force to using any library that not strictly necessary for work.

To Reproduce

  • Create new java project in your favorite IDE
  • Add junrar dependency only
  • Instantiate Archive class: new Archive(new File("file.rar"))

Expected behavior
Object must be created, no exceptions expected

Environment (please complete the following information):

  • OS: Any
  • Junrar version: 7.4.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.