Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@
^\.cache$
^tools/extra/[.]*$
^CLAUDE\.md$
^\.git$
^\.covrignore$
2 changes: 2 additions & 0 deletions .covrignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/miniz.c
src/mbedtls
9 changes: 9 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Install 7zip
run: |
if [ "$RUNNER_OS" = "Linux" ]; then
sudo apt-get install -y p7zip-full
elif [ "$RUNNER_OS" = "macOS" ]; then
brew ls --versions p7zip > /dev/null || brew install p7zip
fi
shell: bash

- uses: r-lib/actions/setup-pandoc@v2

- uses: r-lib/actions/setup-r@v2
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Install 7zip
run: sudo apt-get install -y p7zip-full

- uses: r-lib/actions/setup-r@v2

- uses: r-lib/actions/setup-r-dependencies@v2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.Rhistory
.RData
/src/*.o
/src/mbedtls/library/*.o
/src/zip.so
/revdep
/README.html
Expand Down
2 changes: 2 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Authors@R: c(
person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = c("aut", "cre")),
person("Kuba", "Podgórski", role = "ctb"),
person("Rich", "Geldreich", role = "ctb"),
person("Arm Limited", role = c("ctb", "cph"),
comment = "bundled Mbed TLS crypto subset in src/mbedtls/ (Apache-2.0)"),
person("Posit Software, PBC", role = c("cph", "fnd"),
comment = c(ROR = "03wc8by49"))
)
Expand Down
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# zip (development version)

* `zip()`, `zipr()`, `zip_append()`, `zipr_append()`, `zip_process()`, and
`unzip()` / `unzip_process()` now support password-protected archives
using WinZIP AES-256, and other encryption schemes.

* `zip_list()` now reports an `encryption` column indicating the encryption
scheme used for each entry.

* `zip_list()` and `unzip()` now work directly on `http://` and `https://`
URLs. They use HTTP range requests to download only the central directory
and the requested entries, so listing or extracting a few files from a
Expand Down
23 changes: 19 additions & 4 deletions R/process.R
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ unzip_process <- function() {
initialize = function(
zipfile,
exdir = ".",
password = NULL,
poll_connection = TRUE,
stderr = tempfile(),
...
Expand All @@ -95,9 +96,14 @@ unzip_process <- function() {
is_string(exdir)
)
exdir <- normalizePath(exdir, winslash = "\\", mustWork = FALSE)
pw <- resolve_password(password)
args <- enc2c(c(zipfile, exdir))
if (!is.null(pw)) {
args <- c(args, raw_to_hex(pw))
}
super$initialize(
unzip_exe(),
enc2c(c(zipfile, exdir)),
args,
poll_connection = poll_connection,
stderr = stderr,
...
Expand Down Expand Up @@ -127,8 +133,9 @@ unzip_process <- function() {
#'
#' Arguments:
#' * `zipfile`: Path to the zip file to create.
#' * `files`: Character vector of paths to files to add to the archive. Each specified file
#' or directory in is created as a top-level entry in the zip archive.
#' * `files`: Character vector of paths to files to add to the archive.
#' Each specified file or directory in is created as a top-level entry
#' in the zip archive.
#' * `recurse`: Whether to add the contents of directories recursively.
#' * `include_directories`: Whether to explicitly include directories
#' in the archive. Including directories might confuse MS Office when
Expand Down Expand Up @@ -166,6 +173,8 @@ zip_process <- function() {
files,
recurse = TRUE,
include_directories = TRUE,
password = NULL,
encryption = "aes256",
poll_connection = TRUE,
stderr = tempfile(),
...
Expand All @@ -181,9 +190,15 @@ zip_process <- function() {
include_directories,
private$params_file
)
pw <- resolve_password(password)
enc <- if (is.null(pw)) NULL else encryption_code(encryption)
args <- enc2c(c(zipfile, private$params_file))
if (!is.null(pw)) {
args <- c(args, raw_to_hex(pw), as.character(enc))
}
super$initialize(
zip_exe(),
enc2c(c(zipfile, private$params_file)),
args,
poll_connection = poll_connection,
stderr = stderr,
...
Expand Down
39 changes: 39 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,45 @@ enc2c <- function(x) {
}
}

raw_to_hex <- function(x) {
paste0(sprintf("%02x", as.integer(x)), collapse = "")
}

resolve_password <- function(password) {
password <- password %||% getOption("zip_password")
if (is.null(password)) {
return(NULL)
}
if (is.function(password)) {
password <- password()
}
if (is.raw(password)) {
if (length(password) == 0) {
stop("`password` must not be empty")
}
return(password)
}
if (!is.character(password) || length(password) != 1 || is.na(password)) {
stop(
"`password` must be a string, a raw vector, or a function returning one"
)
}
if (!nzchar(password)) {
stop("`password` must not be empty")
}
charToRaw(enc2utf8(password))
}

encryption_code <- function(encryption) {
encryption <- match.arg(encryption, c("aes256", "aes128", "zipcrypto"))
switch(
encryption,
aes256 = 3L,
aes128 = 1L,
zipcrypto = 4L
)
}

is_true_option <- function(name) {
opt <- getOption(name)
if (is.null(opt)) {
Expand Down
Loading
Loading