From 5f598ef5e6968f20edece6aa0c138a34d3f0b1c9 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 17:15:34 -0500 Subject: [PATCH 01/69] Update .gitignore --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index 4e6da25..c8048f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea +.DS_Store m4/ Makefile Makefile.in @@ -20,3 +22,14 @@ install-sh missing INSTALL stamp-h1 + +*.lo +*.la +*.lai +*.o +*.Plo +*.a +*~ + +*.dylib +*.so \ No newline at end of file From 55974b08ebdb7460143d1ce816f1832a97d38dba Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 17:16:19 -0500 Subject: [PATCH 02/69] Update devcontainer dependencies Apache 2.4.62 Imagemagick 7.1.1-40 libwebp 1.4.0 libtiff 1.7.0 --- .devcontainer/Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 32d5d55..f275cba 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,11 +1,11 @@ -ARG HTTPD_VERSION=2.4.49 +ARG HTTPD_VERSION=2.4.62 FROM httpd:${HTTPD_VERSION} as build-mod-dims -ARG DIMS_VERSION=3.3.26 -ARG IMAGEMAGICK_VERSION=6.9.12-34 -ARG WEBP_VERSION=1.2.1 -ARG TIFF_VERSION=4.3.0 +ARG DIMS_VERSION=3.3.30 +ARG IMAGEMAGICK_VERSION=7.1.1-40 +ARG WEBP_VERSION=1.4.0 +ARG TIFF_VERSION=4.7.0 ARG PREFIX=/usr/local/imagemagick ENV DIMS_DOWNLOAD_TIMEOUT=60000 From d9e85f371ace0088116d86703f4b2d8a63062c7a Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 17:17:06 -0500 Subject: [PATCH 03/69] Replace use of ParseSizeGeometry This was removed in Imagemagick 7. --- src/mod_dims.h | 2 +- src/mod_dims_ops.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mod_dims.h b/src/mod_dims.h index 189414e..f9ba9a9 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -33,7 +33,7 @@ #include #include -#include +#include #include diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 335ff6d..2317384 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -22,7 +22,7 @@ #include #include -#include +#include #define MAGICK_CHECK(func, rec) \ do { \ @@ -98,7 +98,8 @@ dims_resize_operation (dims_request_rec *d, char *args, char **err) { MagickStatusType flags; RectangleInfo rec; - flags = ParseSizeGeometry(GetImageFromMagickWand(d->wand), args, &rec); + SetGeometry(GetImageFromMagickWand(d->wand), &rec); + flags = ParseMetaGeometry(args, &rec.x, &rec.y, &rec.width, &rec.height); if(!(flags & AllValues)) { *err = "Parsing thumbnail geometry failed"; return DIMS_FAILURE; @@ -154,7 +155,8 @@ dims_thumbnail_operation (dims_request_rec *d, char *args, char **err) { RectangleInfo rec; char *resize_args = apr_psprintf(d->pool, "%s^", args); - flags = ParseSizeGeometry(GetImageFromMagickWand(d->wand), resize_args, &rec); + SetGeometry(GetImageFromMagickWand(d->wand), &rec); + flags = ParseMetaGeometry(resize_args, &rec.x, &rec.y, &rec.width, &rec.height); if(!(flags & AllValues)) { *err = "Parsing thumbnail (resize) geometry failed"; return DIMS_FAILURE; @@ -582,7 +584,8 @@ dims_legacy_thumbnail_operation (dims_request_rec *d, char *args, char **err) { int x, y; char *resize_args = apr_psprintf(d->pool, "%s^", args); - flags = ParseSizeGeometry(GetImageFromMagickWand(d->wand), resize_args, &rec); + SetGeometry(GetImageFromMagickWand(d->wand), &rec); + flags = ParseMetaGeometry(resize_args, &rec.x, &rec.y, &rec.width, &rec.height); if(!(flags & AllValues)) { *err = "Parsing thumbnail (resize) geometry failed"; return DIMS_FAILURE; From eb4876be327152a504265871b8c30ab588e2e753 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 17:34:06 -0500 Subject: [PATCH 04/69] Update devcontainer.json --- .devcontainer/devcontainer.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d793e84..0f494b0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,14 +8,15 @@ "containerUser": "root", "remoteUser": "root", - // Set *default* container specific settings.json values on container create. - "settings": {}, - - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "ms-vscode.cpptools" - ], + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "vscodevim.vim" + ] + } + }, // https://stackoverflow.com/questions/35860527/warning-error-disabling-address-space-randomization-operation-not-permitted "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], From 69ab918d6b1fda010ceb6b5b2de041c085550180 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 18:41:24 -0500 Subject: [PATCH 05/69] Reorganize code Starting to move code to separate files for better organization and to reduce the size of the mod_dims.c file. No logic changes. --- .gitignore | 1 + src/Makefile.am | 10 +- src/configuration.c | 365 +++++++++++++ src/configuration.h | 80 +++ src/curl.c | 210 ++++++++ src/curl.h | 31 ++ src/directives.h | 97 ++++ src/encryption.c | 148 ++++++ src/encryption.h | 8 + src/handler.c | 349 +++++++++++++ src/handler.h | 6 + src/mod_dims.c | 1199 +------------------------------------------ src/mod_dims.h | 156 +----- src/mod_dims_ops.c | 22 +- src/mod_dims_ops.h | 29 ++ src/module.c | 24 + src/module.h | 9 + src/request.h | 76 +++ 18 files changed, 1472 insertions(+), 1348 deletions(-) create mode 100644 src/configuration.c create mode 100644 src/configuration.h create mode 100644 src/curl.c create mode 100644 src/curl.h create mode 100644 src/directives.h create mode 100644 src/encryption.c create mode 100644 src/encryption.h create mode 100644 src/handler.c create mode 100644 src/handler.h create mode 100644 src/mod_dims_ops.h create mode 100644 src/module.c create mode 100644 src/module.h create mode 100644 src/request.h diff --git a/.gitignore b/.gitignore index c8048f7..3298f33 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ stamp-h1 *.lai *.o *.Plo +*.Tpo *.a *~ diff --git a/src/Makefile.am b/src/Makefile.am index ce68aaa..31fe592 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,15 @@ moddir = ${AP_LIBEXECDIR} lib_LTLIBRARIES = libmod_dims.la -libmod_dims_la_SOURCES = mod_dims.c mod_dims_ops.c mod_dims.h +libmod_dims_la_SOURCES = \ + curl.c curl.h \ + configuration.c configuration.h \ + encryption.c encryption.h \ + handler.c handler.h \ + mod_dims.c mod_dims.h \ + mod_dims_ops.c mod_dims_ops.h \ + module.c module.h \ + request.h libmod_dims_la_LDFLAGS = -module -avoid-version $(MagickCore_LIBS) $(MagickWand_LIBS) $(libcurl_LIBS) ${MODULE_LDFLAGS} libmod_dims_la_CFLAGS = -std=c99 -D_LARGEFILE64_SOURCE $(MagickCore_CFLAGS) $(MagickWand_CFLAGS) $(libcurl_CFLAGS) ${MODULE_CFLAGS} diff --git a/src/configuration.c b/src/configuration.c new file mode 100644 index 0000000..322808b --- /dev/null +++ b/src/configuration.c @@ -0,0 +1,365 @@ + +#include "mod_dims.h" +#include "module.h" + +#include +#include + +void * +dims_create_config(apr_pool_t *p, server_rec *s) +{ + dims_config_rec *config; + + config = (dims_config_rec *) apr_pcalloc(p, sizeof(dims_config_rec)); + config->whitelist = apr_table_make(p, 5); + config->clients = apr_hash_make(p); + config->ignore_default_output_format = apr_table_make(p, 3); + + config->download_timeout = 3000; + config->imagemagick_timeout = 3000; + + config->no_image_url = NULL; + config->no_image_expire = 60; + config->default_image_prefix = NULL; + + config->default_expire = 86400; + + config->strip_metadata = 1; + config->optimize_resize = 0; + config->disable_encoded_fetch = 0; + config->default_output_format = NULL; + + config->area_size = 128 * 1024 * 1024; // 128mb max. + config->memory_size = 512 * 1024 * 1024; // 512mb max. + config->map_size = 1024 * 1024 * 1024; // 1024mb max. + config->disk_size = 2048ul * 1024ul * 1024ul; // 2048mb max. + + config->curl_queue_size = 10; + config->cache_dir = NULL; + config->secret_key = apr_pstrdup(p,"m0d1ms"); + config->encryption_algorithm = "aes/ecb/pkcs5padding"; + config->max_expiry_period= 0; // never expire + + return (void *) config; +} + +const char * +dims_config_set_whitelist(cmd_parms *cmd, void *d, int argc, char *const argv[]) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, + &dims_module); + int i; + + for(i = 0; i < argc; i++) { + char *hostname = argv[i]; + + /* remove glob character and '.' if they're on the string and set + * the value in the hash to glob. + */ + if(hostname[0] == '*') { + if(*++hostname == '.') { + hostname++; + } + + apr_table_setn(config->whitelist, hostname, "glob"); + } else { + apr_table_setn(config->whitelist, argv[i], "exact"); + } + } + + return NULL; +} + +const char * +dims_config_set_ignore_default_output_format(cmd_parms *cmd, void *d, int argc, char *const argv[]) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, + &dims_module); + int i; + + for(i = 0; i < argc; i++) { + char *format = argv[i]; + char *s = format; + while (*s) { *s = toupper(*s); s++; } + + apr_table_setn(config->ignore_default_output_format, format, "1"); + } + return NULL; +} + +const char * +dims_config_set_default_expire(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->default_expire = atol(arg); + return NULL; +} + +const char * +dims_config_set_no_image_expire(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->no_image_expire = atol(arg); + return NULL; +} + +const char * +dims_config_set_download_timeout(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->download_timeout = atol(arg); + return NULL; +} + +const char * +dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->imagemagick_timeout = atol(arg); + return NULL; +} + +const char * +dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + // the default is 1, so anything other than "false" will use the default + if(strcmp(arg, "false") == 0) { + config->strip_metadata = 0; + } + else { + config->strip_metadata = 1; + } + return NULL; +} + +const char * +dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + if(strcmp(arg, "true") == 0) { + config->include_disposition = 1; + } + else { + config->include_disposition = 0; + } + return NULL; +} + +const char * +dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->optimize_resize = atof(arg); + return NULL; +} + +const char * +dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->disable_encoded_fetch = atoi(arg); + return NULL; +} + +const char * +dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->encryption_algorithm = (char *) arg; + return NULL; +} + +const char * +dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + char *output_format = (char *) arg; + char *s = output_format; + while (*s) { *s = toupper(*s); s++; } + config->default_output_format = output_format; + return NULL; +} + +const char * +dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + char *user_agent = (char *) arg; + config->user_agent_override = user_agent; + return NULL; +} + +const char * +dims_config_set_user_agent_enabled(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + if(strcmp(arg, "true") == 0) { + config->user_agent_enabled = 1; + } + else { + config->user_agent_enabled = 0; + } + return NULL; +} + +const char * +dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + + dims_client_config_rec *client_config = NULL; + + if(argc == 0) { + return NULL; + } + + if(argc >= 1) { + client_config = (dims_client_config_rec *) + apr_pcalloc(cmd->pool, + sizeof(dims_client_config_rec)); + + client_config->no_image_url = NULL; + client_config->cache_control_max_age = config->default_expire; + client_config->edge_control_downstream_ttl = -1; + client_config->trust_src = 0; + client_config->min_src_cache_control = -1; + client_config->max_src_cache_control = -1; + + switch(argc) { + case 8: + if(strcmp(argv[7], "-") != 0) { + client_config->secret_key = argv[7]; + } else { + client_config->secret_key = NULL; + } + case 7: + if(strcmp(argv[6], "-") != 0) { + if(atoi(argv[6]) <= 0 && strcmp(argv[6], "0") != 0) { + // erroneous value + client_config->max_src_cache_control = -2; + } + else { + client_config->max_src_cache_control = atoi(argv[6]); + } + } + case 6: + if(strcmp(argv[5], "-") != 0) { + if(atoi(argv[5]) <= 0 && strcmp(argv[5], "0") != 0) { + // erroneous value + client_config->min_src_cache_control = -2; + } + else { + client_config->min_src_cache_control = atoi(argv[5]); + } + } + case 5: + if(strcmp(argv[4], "trust") == 0) { + client_config->trust_src = 1; + } + case 4: + if(strcmp(argv[3], "-") != 0) { + client_config->edge_control_downstream_ttl = atoi(argv[3]); + } + case 3: + if(strcmp(argv[2], "-") != 0) { + client_config->cache_control_max_age = atoi(argv[2]); + } + case 2: + if(strcmp(argv[1], "-") != 0) { + client_config->no_image_url = argv[1]; + } + case 1: + client_config->id = argv[0]; + } + } + + apr_hash_set(config->clients, argv[0], APR_HASH_KEY_STRING, client_config); + + return NULL; +} + +const char * +dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->no_image_url = (char *) arg; + return NULL; +} + +const char * +dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->default_image_prefix = (char *) arg; + + if (strncmp(config->default_image_prefix, "https://", 8) != 0 && + strncmp(config->default_image_prefix, "http://", 7) != 0) { + return "dimsdefaultimageprefix must start with 'https://' or 'http://'"; + } + + return NULL; +} + +const char * +dims_config_set_imagemagick_disk_size(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->disk_size = atol(arg) * 1024 * 1024; + + return NULL; +} + +const char * +dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->max_expiry_period = atol(arg); + return NULL; +} + +const char * +dims_config_set_imagemagick_area_size(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->area_size = atol(arg) * 1024 * 1024; + return NULL; +} + +const char * +dims_config_set_imagemagick_map_size(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->map_size = atol(arg) * 1024 * 1024; + return NULL; +} + +const char * +dims_config_set_imagemagick_memory_size(cmd_parms *cmd, void *dummy, const char *arg) +{ + dims_config_rec *config = (dims_config_rec *) ap_get_module_config( + cmd->server->module_config, &dims_module); + config->memory_size = atol(arg) * 1024 * 1024; + return NULL; +} diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000..3483aac --- /dev/null +++ b/src/configuration.h @@ -0,0 +1,80 @@ + +#ifndef _CONFIGURATION_H_ +#define _CONFIGURATION_H_ + +#include +#include +#include + +typedef struct dims_config_rec dims_config_rec; +typedef struct dims_client_config_rec dims_client_config_rec; + +void *dims_create_config(apr_pool_t *p, server_rec *s); +const char *dims_config_set_whitelist(cmd_parms *cmd, void *d, int argc, char *const argv[]); +const char *dims_config_set_ignore_default_output_format(cmd_parms *cmd, void *d, int argc, char *const argv[]); +const char *dims_config_set_default_expire(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_no_image_expire(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_download_timeout(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_user_agent_enabled(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]); +const char *dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_imagemagick_disk_size(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_imagemagick_area_size(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_imagemagick_map_size(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_imagemagick_memory_size(cmd_parms *cmd, void *dummy, const char *arg); + +struct dims_config_rec { + int download_timeout; + int imagemagick_timeout; + + apr_table_t *whitelist; + apr_hash_t *clients; + apr_table_t *ignore_default_output_format; + + char *no_image_url; + long no_image_expire; + long default_expire; + int strip_metadata; + float optimize_resize; + int include_disposition; + int disable_encoded_fetch; + char *default_output_format; + + MagickSizeType area_size; + MagickSizeType memory_size; + MagickSizeType map_size; + MagickSizeType disk_size; + + int curl_queue_size; + char *secret_key; + char *encryption_algorithm; + long max_expiry_period; + char *cache_dir; + char *default_image_prefix; + + char *user_agent_override; + int user_agent_enabled; +}; + +struct dims_client_config_rec { + char *id; + char *no_image_url; + int cache_control_max_age; + int edge_control_downstream_ttl; + int trust_src; + int min_src_cache_control; + int max_src_cache_control; + char *secret_key; +}; + +#endif \ No newline at end of file diff --git a/src/curl.c b/src/curl.c new file mode 100644 index 0000000..60de840 --- /dev/null +++ b/src/curl.c @@ -0,0 +1,210 @@ + +#include + +#include "request.h" +#include "mod_dims.h" +#include "curl.h" + +/* Converts a hex character to its integer value */ +static char from_hex(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +/* Converts an integer value to its hex character*/ +static char to_hex(char code) { + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} + +/* Returns a url-encoded version of str */ +/* IMPORTANT: be sure to free() the returned string after use */ +static char *url_encode(char *str) { + char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; + while (*pstr) { + if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr == ':' || *pstr == '/' || *pstr == '?' || *pstr == '=' || *pstr == '&') + *pbuf++ = *pstr; + else + *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); + pstr++; + } + *pbuf = '\0'; + return buf; +} + +void +lock_share(CURL *handle, curl_lock_data data, + curl_lock_access access, void *userptr) +{ + dims_curl_rec *locks = (dims_curl_rec *) userptr; + + switch(data) { + case CURL_LOCK_DATA_DNS: + apr_thread_mutex_lock(locks->dns_mutex); + break; + default: + apr_thread_mutex_lock(locks->share_mutex); + } +} + +void +unlock_share(CURL *handle, curl_lock_data data, void *userptr) +{ + dims_curl_rec *locks = (dims_curl_rec *) userptr; + + switch(data) { + case CURL_LOCK_DATA_DNS: + apr_thread_mutex_unlock(locks->dns_mutex); + break; + default: + apr_thread_mutex_unlock(locks->share_mutex); + } +} + +static int +dims_curl_debug_cb(CURL *handle, + curl_infotype type, + char *data, + size_t size, + void *clientp) +{ + dims_request_rec *d = (dims_request_rec *) clientp; + switch(type) { + case CURLINFO_HEADER_OUT: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Curl request header data: %s ", data); + break; + default: + break; + } +} + +/** + * This callback is called by the libcurl API to write data into + * memory as it's being downloaded. + * + * The memory allocated here must be freed manually as it's not + * allocated into an apache memory pool. + */ +size_t +dims_write_image_cb(void *ptr, size_t size, size_t nmemb, void *data) +{ + dims_image_data_t *mem = (dims_image_data_t *) data; + size_t realsize = size * nmemb; + + /* Allocate more memory if needed. */ + if(mem->size - mem->used <= realsize) { + mem->size = mem->size == 0 ? realsize : (mem->size + realsize) * 1.25; + mem->data = (char *) realloc(mem->data, mem->size); + } + + if (mem->data) { + memcpy(&(mem->data[mem->used]), ptr, realsize); + mem->used += realsize; + } + + return realsize; +} + +static size_t +dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) +{ + dims_request_rec *d = (dims_request_rec *) data; + size_t realsize = size * nmemb; + char *start = (char *) ptr; + char *header = (char *) ptr; + char *key = NULL, *value = NULL; + + while(header < (start + realsize)) { + if(*header == ':') { + key = apr_pstrndup(d->pool, start, header - start); + while(*header == ' ') { + header++; + } + value = apr_pstrndup(d->pool, header, start + realsize - header - 2); + header = start + realsize; + } + header++; + } + + if(key && value && strcmp(key, "Cache-Control") == 0) { + d->cache_control = value; + } else if(key && value && strcmp(key, "Edge-Control") == 0) { + d->edge_control = value; + } else if(key && value && strcmp(key, "Last-Modified") == 0) { + d->last_modified = value; + } else if(key && value && strcmp(key, "ETag") == 0) { + d->etag = value; + } + + return realsize; +} + +CURLcode +dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data) +{ + CURL *curl_handle; + CURLcode code; + + dims_image_data_t image_data; + image_data.data = NULL; + image_data.size = 0; + image_data.used = 0; + int extra_time = 0; + + /* Allow for some extra time to download the NOIMAGE image. */ + void *s = NULL; + + if (d->status == DIMS_DOWNLOAD_TIMEOUT) { + extra_time += 500; + } + + apr_pool_userdata_get((void *) &s, DIMS_CURL_SHARED_KEY, + d->r->server->process->pool); + + /* Encode the fetch URL before downloading */ + if (!d->config->disable_encoded_fetch) { + fetch_url = url_encode(fetch_url); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Encoded URL: %s ", fetch_url); + } + + curl_handle = curl_easy_init(); + curl_easy_setopt(curl_handle, CURLOPT_URL, fetch_url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, dims_write_image_cb); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &image_data); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dims_write_header_cb); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) d); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, d->config->download_timeout + extra_time); + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl_handle, CURLOPT_DEBUGFUNCTION, dims_curl_debug_cb); + curl_easy_setopt(curl_handle, CURLOPT_DEBUGDATA, d); + + /* Set the user agent to dims/ */ + if (d->config->user_agent_override != NULL && d->config->user_agent_enabled == 1) { + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, d->config->user_agent_override); + } else if (d->config->user_agent_enabled == 1) { + char *dims_useragent = apr_psprintf(d->r->pool, "mod_dims/%s", MODULE_VERSION); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, dims_useragent); + } + + /* The curl shared handle allows this process to share DNS cache + * and prevents the DNS cache from going away after every request. + */ + if (s) { + dims_curl_rec *locks = (dims_curl_rec *) s; + curl_easy_setopt(curl_handle, CURLOPT_SHARE, locks->share); + } + + code = curl_easy_perform(curl_handle); + + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &image_data.response_code); + curl_easy_cleanup(curl_handle); + + *data = image_data; + + if (!d->config->disable_encoded_fetch) { + free(fetch_url); + } + + return code; +} diff --git a/src/curl.h b/src/curl.h new file mode 100644 index 0000000..39e0734 --- /dev/null +++ b/src/curl.h @@ -0,0 +1,31 @@ +#ifndef _CURL_H_ +#define _CURL_H + +#include +#include + +#include "request.h" + +#define DIMS_CURL_SHARED_KEY "dims_curl_shared" + +typedef struct { + CURLSH *share; + + server_rec *s; + + apr_thread_mutex_t *share_mutex; + apr_thread_mutex_t *dns_mutex; +} dims_curl_rec; + +typedef struct { + char *data; + size_t size; + size_t used; + long response_code; +} dims_image_data_t; + +CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); +void lock_share(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr); +void unlock_share(CURL *handle, curl_lock_data data, void *userptr); + +#endif \ No newline at end of file diff --git a/src/directives.h b/src/directives.h new file mode 100644 index 0000000..17f0837 --- /dev/null +++ b/src/directives.h @@ -0,0 +1,97 @@ + +#ifndef _DIRECTIVES_H_ +#define _DIRECTIVES_H_ + +#include +#include + +#include "configuration.h" + +static const command_rec dims_directives[] = +{ + AP_INIT_TAKE_ARGV("DimsAddWhitelist", + dims_config_set_whitelist, NULL, RSRC_CONF, + "Add whitelist hostname for DIMS URL requests."), + AP_INIT_TAKE_ARGV("DimsAddClient", + dims_config_set_client, NULL, RSRC_CONF, + "Add a client with optional no image url, max-age and downstream-ttl settings."), + AP_INIT_TAKE_ARGV("DimsIgnoreDefaultOutputFormat", + dims_config_set_ignore_default_output_format, NULL, RSRC_CONF, + "Add input formats that shouldn't be converted to the default output format."), + AP_INIT_TAKE1("DimsDefaultImageURL", + dims_config_set_no_image_url, NULL, RSRC_CONF, + "Default image if processing fails or original image doesn't exist."), + AP_INIT_TAKE1("DimsDefaultImagePrefix", + dims_config_set_image_prefix, NULL, RSRC_CONF, + "Default image prefix if URL is relative."), + AP_INIT_TAKE1("DimsCacheExpire", + dims_config_set_default_expire, NULL, RSRC_CONF, + "Default expire time for Cache-Control/Expires/Edge-Control headers, in seconds." + "The default is 86400"), + AP_INIT_TAKE1("DimsNoImageCacheExpire", + dims_config_set_no_image_expire, NULL, RSRC_CONF, + "Default expire time for Cache-Control/Expires/Edge-Control headers for NOIMAGE image, in seconds." + "The default is 60"), + AP_INIT_TAKE1("DimsDownloadTimeout", + dims_config_set_download_timeout, NULL, RSRC_CONF, + "Timeout for downloading remote images." + "The default is 3000."), + AP_INIT_TAKE1("DimsImagemagickTimeout", + dims_config_set_imagemagick_timeout, NULL, RSRC_CONF, + "Timeout for processing images." + "The default is 3000."), + AP_INIT_TAKE1("DimsImagemagickMemorySize", + dims_config_set_imagemagick_memory_size, NULL, RSRC_CONF, + "Maximum amount of memory in megabytes to use for pixel cache." + "The default is 512mb."), + AP_INIT_TAKE1("DimsImagemagickAreaSize", + dims_config_set_imagemagick_area_size, NULL, RSRC_CONF, + "Maximum amount of memory in megabytes that any one image can use." + "The default is 128mb."), + AP_INIT_TAKE1("DimsImagemagickMapSize", + dims_config_set_imagemagick_map_size, NULL, RSRC_CONF, + "Maximum amount of memory map in megabytes to use for the pixel cache." + "The default is 1024mb."), + AP_INIT_TAKE1("DimsImagemagickDiskSize", + dims_config_set_imagemagick_disk_size, NULL, RSRC_CONF, + "Maximum amount of disk space in megabytes to use for the pixel cache." + "The default is 1024mb."), + AP_INIT_TAKE1("DimsSecretMaxExpiryPeriod", + dims_config_set_secretkeyExpiryPeriod, NULL, RSRC_CONF, + "How long in the future (in seconds) can the expiry date on the URL be requesting. 0 = forever" + "The default is 0."), + AP_INIT_TAKE1("DimsStripMetadata", + dims_config_set_strip_metadata, NULL, RSRC_CONF, + "Should DIMS strip the metadata from the image, true OR false." + "The default is true."), + AP_INIT_TAKE1("DimsIncludeDisposition", + dims_config_set_include_disposition, NULL, RSRC_CONF, + "Should DIMS include Content-Disposition header, true OR false." + "The default is false."), + AP_INIT_TAKE1("DimsOptimizeResize", + dims_config_set_optimize_resize, NULL, RSRC_CONF, + "Should DIMS optimize resize operations. This has a slight impact on image quality. 0 = disabled" + "The default is 0."), + AP_INIT_TAKE1("DimsDisableEncodedFetch", + dims_config_set_encoded_fetch, NULL, RSRC_CONF, + "Should DIMS encode image url before fetching it." + "The default is 0."), + AP_INIT_TAKE1("DimsEncryptionAlgorithm", + dims_config_set_encryption_algorithm, NULL, RSRC_CONF, + "What algorithm should DIMS user to decrypt the 'eurl' parameter." + "The default is AES/ECB/PKCS5Padding."), + AP_INIT_TAKE1("DimsDefaultOutputFormat", + dims_config_set_default_output_format, NULL, RSRC_CONF, + "Default output format if 'format' command is not present in the request."), + AP_INIT_TAKE1("DimsUserAgentEnabled", + dims_config_set_user_agent_enabled, NULL, RSRC_CONF, + "Enable DIMS User-Agent header ('dims/'), true OR false." + "The default is false."), + AP_INIT_TAKE1("DimsUserAgentOverride", + dims_config_set_user_agent_override, NULL, RSRC_CONF, + "Override DIMS User-Agent header" + "The default is 'dims/."), + {NULL} +}; + +#endif \ No newline at end of file diff --git a/src/encryption.c b/src/encryption.c new file mode 100644 index 0000000..6d145a4 --- /dev/null +++ b/src/encryption.c @@ -0,0 +1,148 @@ + +#include +#include +#include +#include +#include +#include +#include + +int +aes_errors(const char *message, size_t length, void *u) +{ + request_rec *r = (request_rec *) u; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s", message); + return 0; +} + +char * +aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_text, int encrypted_length) +{ + EVP_CIPHER_CTX *ctx; + + if (!(ctx = EVP_CIPHER_CTX_new())) { + ERR_print_errors_cb(aes_errors, r); + return NULL; + } + + if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) { + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + int decrypted_length; + int plaintext_length, out_length; + char *plaintext = apr_palloc(r->pool, encrypted_length * sizeof(char)); + if (EVP_DecryptUpdate(ctx, (unsigned char *) plaintext, &out_length, encrypted_text, encrypted_length)) { + plaintext_length = out_length; + + if (!EVP_DecryptFinal_ex(ctx, (unsigned char *) plaintext + out_length, &plaintext_length)) { + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + plaintext_length += out_length; + plaintext[plaintext_length] = '\0'; + } else { + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + EVP_CIPHER_CTX_free(ctx); + + return plaintext; +} + +char * +aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_encrypted_text) { + EVP_CIPHER_CTX *ctx; + int ret; + int plaintext_length = 0; + int out_length; + char *plaintext; + + // Decode the Base64 input + int encrypted_length = apr_base64_decode_len((const char *)base64_encrypted_text); + unsigned char *encrypted_data = apr_palloc(r->pool, encrypted_length); + int decoded_length = apr_base64_decode((char *)encrypted_data, (const char *)base64_encrypted_text); + + // Extract IV (12 bytes), ciphertext, and tag (16 bytes) + unsigned char *iv = encrypted_data; + unsigned char *encrypted_text = encrypted_data + 12; // 12-byte IV + int ciphertext_length = decoded_length - 12 - 16; // 16-byte tag at the end + unsigned char *tag = encrypted_text + ciphertext_length; // 16-byte tag + + // Initialize the context + if (!(ctx = EVP_CIPHER_CTX_new())) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to create new EVP_CIPHER_CTX"); + ERR_print_errors_cb(aes_errors, r); + return NULL; + } + + // Initialize the decryption operation + if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (1)"); + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + // Set the IV length, if necessary + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set IV length"); + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + // Set the key and IV + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (2)"); + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + plaintext = apr_palloc(r->pool, ciphertext_length + 1); // +1 for null terminator + if (!plaintext) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Memory allocation failed"); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + // Provide the message to be decrypted and obtain the plaintext output + if (!EVP_DecryptUpdate(ctx, (unsigned char *)plaintext, &out_length, encrypted_text, ciphertext_length)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptUpdate failed"); + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + plaintext_length = out_length; + + // Set expected tag value (must be done after EVP_DecryptUpdate) + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set tag"); + ERR_print_errors_cb(aes_errors, r); + EVP_CIPHER_CTX_free(ctx); + return NULL; + } + + // Finalize the decryption + ret = EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext + plaintext_length, &out_length); + + EVP_CIPHER_CTX_free(ctx); + + if (ret > 0) { + plaintext_length += out_length; + plaintext[plaintext_length] = '\0'; // Explicitly add the null terminator + return plaintext; + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptFinal_ex failed"); + ERR_print_errors_cb(aes_errors, r); + return NULL; + } +} \ No newline at end of file diff --git a/src/encryption.h b/src/encryption.h new file mode 100644 index 0000000..7bbcd8c --- /dev/null +++ b/src/encryption.h @@ -0,0 +1,8 @@ +#ifndef _ENCRYPTION_H_ +#define _ENCRYPTION_H + +int aes_errors(const char *message, size_t length, void *u); +char *aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_text, int encrypted_length); +char *aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_encrypted_text); + +#endif diff --git a/src/handler.c b/src/handler.c new file mode 100644 index 0000000..262b80b --- /dev/null +++ b/src/handler.c @@ -0,0 +1,349 @@ + +#include +#include +#include +#include + +#include "mod_dims.h" +#include "handler.h" +#include "configuration.h" +#include "request.h" +#include "module.h" +#include "encryption.h" + +static void show_time(request_rec *r, apr_interval_time_t tsecs) +{ + int days, hrs, mins, secs; + + secs = (int)(tsecs % 60); + tsecs /= 60; + mins = (int)(tsecs % 60); + tsecs /= 60; + hrs = (int)(tsecs % 24); + days = (int)(tsecs / 24); + + ap_rprintf(r, "Uptime: "); + + if (days) ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); + if (hrs) ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); + if (mins) ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); + if (secs) ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); + + ap_rprintf(r, "\n"); +} + + +/** + * The apache handler. Apache will call this method when a request + * for /dims/, /dims3/, /dims4/ or an image is recieved. + * + * Depending on how this function is called it will do one of three + * things: + * + * 1) Transform old-style request into a new-style request and + * pass it along to the dims_handle_newstyle function. + * + * 2) Parse out the URL and commands and pass them along + * to the dims_handle_newstyle function. + * + * 3) Load the image from the filesystem and pass it along + * with the commands (r->path_info) to dims_process_image. + */ +apr_status_t +dims_handler(request_rec *r) +{ + dims_request_rec *d = (dims_request_rec *) + apr_palloc(r->pool, sizeof(dims_request_rec)); + + d->r = r; + d->pool = r->pool; + d->wand = NULL; + d->config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); + d->client_config = NULL; + d->no_image_url = d->config->no_image_url; + d->use_no_image = 0; + d->image_url = NULL; + d->filename = NULL; + d->cache_control = NULL; + d->edge_control = NULL; + d->etag = NULL; + d->last_modified = NULL; + d->request_hash = NULL; + d->status = APR_SUCCESS; + d->fetch_http_status = 0; + d->start_time = apr_time_now(); + d->download_time = 0; + d->imagemagick_time = 0; + d->use_secret_key=0; + d->optimize_resize = d->config->optimize_resize; + d->send_content_disposition = 0; + d->content_disposition_filename = NULL; + + /* Set initial notes to be logged by mod_log_config. */ + apr_table_setn(r->notes, "DIMS_STATUS", "0"); + apr_table_setn(r->notes, "DIMS_ORIG_BYTES", "-"); + apr_table_setn(r->notes, "DIMS_DL_TIME", "-"); + apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, + "Handler %s : %s", r->handler, r->uri); + /* Handle old-style DIMS parameters. */ + if(strcmp(r->handler, "dims-local") == 0 && + (r->path_info && strlen(r->path_info) != 0)) { + /* Handle local filesystem images w/DIMS parameters. */ + d->filename = r->canonical_filename; + d->unparsed_commands = r->path_info; + + return dims_handle_request(d); + } else if(r->uri && strncmp(r->uri, "/dims/", 6) == 0) { + int status = 0; + char appid[50], b[10], w[10], h[10], q[10]; + char *fixed_url, *url; + + /* Translate provided parameters into new-style parameters. */ + b[0] = w[0] = h[0] = q[0] = '-'; + status = sscanf(r->uri + 5, + "/%49[^/]/%9[^/]/%9[^/]/%9[^/]/%9[^/]/", + (char *) &appid, (char *) &b, (char *) &w, (char *) &h, + (char *) &q); + + if(status != 5) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } + + int bitmap = (b[0] != '-') ? atoi(b) : -1; + double width = (w[0] != '-') ? atof(w) : 0; + double height = (h[0] != '-') ? atof(h) : 0; + int quality = (q[0] != '-') ? atoi(q) : 0; + + if(bitmap == -1) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } + + /* HACK: If URL has "http:/" instead of "http://", correct it. */ + url = strstr(r->uri, "http:/"); + if(url && *(url + 6) != '/') { + fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); + } else if(!url) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } else { + fixed_url = url; + } + + char *commands = apr_psprintf(r->pool, "%s", appid); + + if(bitmap & LEGACY_DIMS_RESIZE && bitmap & LEGACY_DIMS_CROP) { + if(!width && !height) { + return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); + } + commands = apr_psprintf(r->pool, "%s/legacy_thumbnail/%ldx%ld", + commands, (long) width, (long) height); + } else if(bitmap & LEGACY_DIMS_CROP || bitmap & LEGACY_DIMS_RESIZE) { + char *cmd = (bitmap & LEGACY_DIMS_RESIZE) ? "resize" : "legacy_crop"; + + if(width && !height) { + commands = apr_psprintf(r->pool, "%s/%s/%ld", + commands, cmd, (long) width); + } else if(height && !width) { + commands = apr_psprintf(r->pool, "%s/%s/x%ld", + commands, cmd, (long) height); + } else if(width && height) { + commands = apr_psprintf(r->pool, "%s/%s/%ldx%ld", + commands, cmd, (long) width, (long) height); + } else { + return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); + } + } + + if(bitmap & LEGACY_DIMS_JPG) { + commands = apr_psprintf(r->pool, "%s/format/jpg", + commands); + } else if(bitmap & LEGACY_DIMS_PNG) { + commands = apr_psprintf(r->pool, "%s/format/png", + commands); + } else if(bitmap & LEGACY_DIMS_GIF) { + commands = apr_psprintf(r->pool, "%s/format/gif", + commands); + } + + if(bitmap & LEGACY_DIMS_SHARPEN) { + commands = apr_psprintf(r->pool, "%s/sharpen/0.0x1.5", + commands); + } + + if(quality > 0 && quality <= 100) { + commands = apr_psprintf(r->pool, "%s/quality/%d", + commands, quality); + } + + /* Locate pointer to the image URL. */ + d->image_url = fixed_url; + d->unparsed_commands = commands; + + return dims_handle_request(d); + } else if ((strcmp(r->handler, "dims3") == 0) || + (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || + (strcmp(r->handler, "dims4") == 0 )) { + /* Handle new-style DIMS parameters. */ + char *p, *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; + if (( strcmp( r->handler,"dims4") == 0)) { + d->use_secret_key = 1; + } + + char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); + d->client_id = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); + + if(!(d->client_config = + apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return dims_cleanup(d, "Application ID is not valid", DIMS_BAD_CLIENT); + } + + /* Check first if URL is passed as a query parameter. */ + if(r->args) { + const size_t args_len = strlen(r->args) + 1; + char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); + char *token; + char *strtokstate; + token = apr_strtok(args, "&", &strtokstate); + while (token) { + if(strncmp(token, "url=", 4) == 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "ARG: %s", token); + fixed_url = apr_pstrdup(r->pool, token + 4); + ap_unescape_url(fixed_url); + + if (strcmp(fixed_url, "") == 0) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } + } else if (strncmp(token, "download=1", 10) == 0) { + d->send_content_disposition = 1; + + } else if (strncmp(token, "eurl=", 4) == 0) { + eurl = apr_pstrdup(r->pool, token + 5); + + // Hash secret via SHA-1. + unsigned char *secret = (unsigned char *) d->client_config->secret_key; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1(secret, strlen((char *) secret), hash); + + // Convert to hex. + char hex[SHA_DIGEST_LENGTH * 2 + 1]; + if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { + return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); + } + + // Use first 16 bytes. + unsigned char key[17]; + strncpy((char *) key, hex, 16); + key[16] = '\0'; + + // Force key to uppercase + unsigned char *s = key; + while (*s) { *s = toupper(*s); s++; } + + if (d->config->encryption_algorithm != NULL && + strncmp((char *)d->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) { + + fixed_url = aes_128_gcm_decrypt(r, key, eurl); + } else { + //Default is AES/ECB/PKCS5Padding + unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl)); + int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); + fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); + } + if (fixed_url == NULL) { + return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Decrypted URL: %s", fixed_url); + break; + + } else if (strncmp(token, "optimizeResize=", 4) == 0) { + d->optimize_resize = atof(token + 15); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Overriding optimize resize: %f", d->optimize_resize); + } + token = apr_strtok(NULL, "&", &strtokstate); + } + } + + /* Parse out URL to image. + * HACK: If URL has "http:/" instead of "http://", correct it. + */ + commands = apr_pstrdup(r->pool, r->uri); + if(fixed_url == NULL) { + url = strstr(r->uri, "http:/"); + if(url && *(url + 6) != '/') { + fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); + } else if(!url) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } else { + fixed_url = url; + } + + /* Strip URL off URI. This leaves only the tranformation parameters. */ + p = strstr(commands, "http:/"); + if(!p) return dims_cleanup(d, NULL, DIMS_BAD_URL); + *p = '\0'; + } + + // Convert '+' in the fixed_url to ' '. + char *image_url = apr_pstrdup(d->r->pool, fixed_url); + char *s = image_url; + while (*s) { + if (*s == '+') { + *s = ' '; + } + + s++; + } + + d->image_url = image_url; + d->unparsed_commands = commands + 6; + + /* Calculate image filename for use with content disposition. */ + apr_uri_t uri; + if (apr_uri_parse(r->pool, d->image_url, &uri) == APR_SUCCESS) { + if (!uri.path) { + return dims_cleanup(d, NULL, DIMS_BAD_URL); + } + + const char *path = apr_filepath_name_get(uri.path); + d->content_disposition_filename = apr_pstrdup(d->r->pool, path); + } + + return dims_handle_request(d); + } else if(strcmp(r->handler, "dims-status") == 0) { + apr_time_t uptime; + + ap_set_content_type(r, "text/plain"); + ap_rvputs(r, "ALIVE\n\n", NULL); + + uptime = (apr_uint32_t) apr_time_sec(apr_time_now() - + ap_scoreboard_image->global->restart_time); + + show_time(r, uptime); + + ap_rprintf(r, "Restart time: %s\n", + ap_ht_time(r->pool, + ap_scoreboard_image->global->restart_time, + "%A, %d-%b-%Y %H:%M:%S %Z", 0)); + + ap_rprintf(r, "\nmod_dims version: %s (%s)\n", MODULE_VERSION, MODULE_RELEASE); + ap_rprintf(r, "ImageMagick version: %s\n", GetMagickVersion(NULL)); + ap_rprintf(r, "libcurl version: %s\n", curl_version()); + + ap_rprintf(r, "\nDetails\n-------\n"); + + ap_rprintf(r, "Successful requests: %d\n", + apr_atomic_read32(&stats->success_count)); + ap_rprintf(r, "Failed requests: %d\n\n", + apr_atomic_read32(&stats->failure_count)); + ap_rprintf(r, "Download timeouts: %d\n", + apr_atomic_read32(&stats->download_timeout_count)); + ap_rprintf(r, "Imagemagick Timeouts: %d\n", + apr_atomic_read32(&stats->imagemagick_timeout_count)); + + ap_rflush(r); + return OK; + } + + return DECLINED; +} diff --git a/src/handler.h b/src/handler.h new file mode 100644 index 0000000..6de9aa0 --- /dev/null +++ b/src/handler.h @@ -0,0 +1,6 @@ +#ifndef _HANDLER_H +#define _HANDLER_ + +apr_status_t dims_handler(request_rec *r); + +#endif \ No newline at end of file diff --git a/src/mod_dims.c b/src/mod_dims.c index 17bbad5..c2ccc53 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -33,26 +33,20 @@ * the License. */ -#define MODULE_RELEASE "$Revision: $" -#define MODULE_VERSION "3.3.30" - #include "mod_dims.h" +#include "mod_dims_ops.h" +#include "configuration.h" +#include "encryption.h" +#include "curl.h" +#include "request.h" +#include "module.h" #include "util_md5.h" #include "cmyk_icc.h" + #include #include #include #include -#include -#include -#include -#include - -#include - -module dims_module; - -#define DIMS_CURL_SHARED_KEY "dims_curl_shared" #define MAGICK_CHECK(func, d) \ do {\ @@ -62,470 +56,15 @@ module dims_module; return dims_cleanup(d, NULL, d->status); \ } while(0); -typedef struct { - CURLSH *share; - - server_rec *s; - - apr_thread_mutex_t *share_mutex; - apr_thread_mutex_t *dns_mutex; -} dims_curl_rec; - typedef struct { dims_request_rec *d; apr_time_t start_time; } dims_progress_rec; -typedef struct { - apr_uint32_t success_count; - apr_uint32_t failure_count; - apr_uint32_t download_timeout_count; - apr_uint32_t imagemagick_timeout_count; -} dims_stats_rec; - dims_stats_rec *stats; apr_shm_t *shm; apr_hash_t *ops; -static void * -dims_create_config(apr_pool_t *p, server_rec *s) -{ - dims_config_rec *config; - - config = (dims_config_rec *) apr_pcalloc(p, sizeof(dims_config_rec)); - config->whitelist = apr_table_make(p, 5); - config->clients = apr_hash_make(p); - config->ignore_default_output_format = apr_table_make(p, 3); - - config->download_timeout = 3000; - config->imagemagick_timeout = 3000; - - config->no_image_url = NULL; - config->no_image_expire = 60; - config->default_image_prefix = NULL; - - config->default_expire = 86400; - - config->strip_metadata = 1; - config->optimize_resize = 0; - config->disable_encoded_fetch = 0; - config->default_output_format = NULL; - - config->area_size = 128 * 1024 * 1024; // 128mb max. - config->memory_size = 512 * 1024 * 1024; // 512mb max. - config->map_size = 1024 * 1024 * 1024; // 1024mb max. - config->disk_size = 2048UL * 1024UL * 1024UL; // 2048mb max. - - config->curl_queue_size = 10; - config->cache_dir = NULL; - config->secret_key = apr_pstrdup(p,"m0d1ms"); - config->encryption_algorithm = "AES/ECB/PKCS5Padding"; - config->max_expiry_period= 0; // never expire - - return (void *) config; -} - -static const char * -dims_config_set_whitelist(cmd_parms *cmd, void *d, int argc, char *const argv[]) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, - &dims_module); - int i; - - for(i = 0; i < argc; i++) { - char *hostname = argv[i]; - - /* Remove glob character and '.' if they're on the string and set - * the value in the hash to glob. - */ - if(hostname[0] == '*') { - if(*++hostname == '.') { - hostname++; - } - - apr_table_setn(config->whitelist, hostname, "glob"); - } else { - apr_table_setn(config->whitelist, argv[i], "exact"); - } - } - - return NULL; -} - -static const char * -dims_config_set_ignore_default_output_format(cmd_parms *cmd, void *d, int argc, char *const argv[]) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, - &dims_module); - int i; - - for(i = 0; i < argc; i++) { - char *format = argv[i]; - char *s = format; - while (*s) { *s = toupper(*s); s++; } - - apr_table_setn(config->ignore_default_output_format, format, "1"); - } - return NULL; -} - -static const char * -dims_config_set_default_expire(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->default_expire = atol(arg); - return NULL; -} - -static const char * -dims_config_set_no_image_expire(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->no_image_expire = atol(arg); - return NULL; -} - -static const char * -dims_config_set_download_timeout(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->download_timeout = atol(arg); - return NULL; -} - -static const char * -dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->imagemagick_timeout = atol(arg); - return NULL; -} - -static const char * -dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - // The default is 1, so anything other than "false" will use the default - if(strcmp(arg, "false") == 0) { - config->strip_metadata = 0; - } - else { - config->strip_metadata = 1; - } - return NULL; -} - -static const char * -dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - if(strcmp(arg, "true") == 0) { - config->include_disposition = 1; - } - else { - config->include_disposition = 0; - } - return NULL; -} - -static const char * -dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->optimize_resize = atof(arg); - return NULL; -} - -static const char * -dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->disable_encoded_fetch = atoi(arg); - return NULL; -} - -static const char * -dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->encryption_algorithm = (char *) arg; - return NULL; -} - -static const char * -dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - char *output_format = (char *) arg; - char *s = output_format; - while (*s) { *s = toupper(*s); s++; } - config->default_output_format = output_format; - return NULL; -} - -static const char * -dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - char *user_agent = (char *) arg; - config->user_agent_override = user_agent; - return NULL; -} - -static const char * -dims_config_set_user_agent_enabled(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - if(strcmp(arg, "true") == 0) { - config->user_agent_enabled = 1; - } - else { - config->user_agent_enabled = 0; - } - return NULL; -} - -static const char * -dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - - dims_client_config_rec *client_config = NULL; - - if(argc == 0) { - return NULL; - } - - if(argc >= 1) { - client_config = (dims_client_config_rec *) - apr_pcalloc(cmd->pool, - sizeof(dims_client_config_rec)); - - client_config->no_image_url = NULL; - client_config->cache_control_max_age = config->default_expire; - client_config->edge_control_downstream_ttl = -1; - client_config->trust_src = 0; - client_config->min_src_cache_control = -1; - client_config->max_src_cache_control = -1; - - switch(argc) { - case 8: - if(strcmp(argv[7], "-") != 0) { - client_config->secret_key = argv[7]; - } else { - client_config->secret_key = NULL; - } - case 7: - if(strcmp(argv[6], "-") != 0) { - if(atoi(argv[6]) <= 0 && strcmp(argv[6], "0") != 0) { - // erroneous value - client_config->max_src_cache_control = -2; - } - else { - client_config->max_src_cache_control = atoi(argv[6]); - } - } - case 6: - if(strcmp(argv[5], "-") != 0) { - if(atoi(argv[5]) <= 0 && strcmp(argv[5], "0") != 0) { - // erroneous value - client_config->min_src_cache_control = -2; - } - else { - client_config->min_src_cache_control = atoi(argv[5]); - } - } - case 5: - if(strcmp(argv[4], "trust") == 0) { - client_config->trust_src = 1; - } - case 4: - if(strcmp(argv[3], "-") != 0) { - client_config->edge_control_downstream_ttl = atoi(argv[3]); - } - case 3: - if(strcmp(argv[2], "-") != 0) { - client_config->cache_control_max_age = atoi(argv[2]); - } - case 2: - if(strcmp(argv[1], "-") != 0) { - client_config->no_image_url = argv[1]; - } - case 1: - client_config->id = argv[0]; - } - } - - apr_hash_set(config->clients, argv[0], APR_HASH_KEY_STRING, client_config); - - return NULL; -} - -static const char * -dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->no_image_url = (char *) arg; - return NULL; -} - -static const char * -dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->default_image_prefix = (char *) arg; - - if (strncmp(config->default_image_prefix, "https://", 8) != 0 && - strncmp(config->default_image_prefix, "http://", 7) != 0) { - return "DimsDefaultImagePrefix must start with 'https://' or 'http://'"; - } - - return NULL; -} - -static const char * -dims_config_set_imagemagick_disk_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->disk_size = atol(arg) * 1024 * 1024; - - return NULL; -} -static const char * -dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->max_expiry_period = atol(arg); - return NULL; -} -static const char * -dims_config_set_imagemagick_area_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->area_size = atol(arg) * 1024 * 1024; - return NULL; -} - -static const char * -dims_config_set_imagemagick_map_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->map_size = atol(arg) * 1024 * 1024; - return NULL; -} - -static const char * -dims_config_set_imagemagick_memory_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->memory_size = atol(arg) * 1024 * 1024; - return NULL; -} - -static void show_time(request_rec *r, apr_interval_time_t tsecs) -{ - int days, hrs, mins, secs; - - secs = (int)(tsecs % 60); - tsecs /= 60; - mins = (int)(tsecs % 60); - tsecs /= 60; - hrs = (int)(tsecs % 24); - days = (int)(tsecs / 24); - - ap_rprintf(r, "Uptime: "); - - if (days) ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); - if (hrs) ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); - if (mins) ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); - if (secs) ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); - - ap_rprintf(r, "\n"); -} - -/** - * This callback is called by the libcurl API to write data into - * memory as it's being downloaded. - * - * The memory allocated here must be freed manually as it's not - * allocated into an apache memory pool. - */ -static size_t -dims_write_image_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - dims_image_data_t *mem = (dims_image_data_t *) data; - size_t realsize = size * nmemb; - - /* Allocate more memory if needed. */ - if(mem->size - mem->used <= realsize) { - mem->size = mem->size == 0 ? realsize : (mem->size + realsize) * 1.25; - mem->data = (char *) realloc(mem->data, mem->size); - } - - if (mem->data) { - memcpy(&(mem->data[mem->used]), ptr, realsize); - mem->used += realsize; - } - - return realsize; -} - -static size_t -dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - dims_request_rec *d = (dims_request_rec *) data; - size_t realsize = size * nmemb; - char *start = (char *) ptr; - char *header = (char *) ptr; - char *key = NULL, *value = NULL; - - while(header < (start + realsize)) { - if(*header == ':') { - key = apr_pstrndup(d->pool, start, header - start); - while(*header == ' ') { - header++; - } - value = apr_pstrndup(d->pool, header, start + realsize - header - 2); - header = start + realsize; - } - header++; - } - - if(key && value && strcmp(key, "Cache-Control") == 0) { - d->cache_control = value; - } else if(key && value && strcmp(key, "Edge-Control") == 0) { - d->edge_control = value; - } else if(key && value && strcmp(key, "Last-Modified") == 0) { - d->last_modified = value; - } else if(key && value && strcmp(key, "ETag") == 0) { - d->etag = value; - } - - return realsize; -} - /** * This callback is called by the MagicWand API during transformation * operations. How often it's called is dependent on the operation @@ -558,120 +97,6 @@ dims_imagemagick_progress_cb(const char *text, const MagickOffsetType offset, return MagickTrue; } -/* Converts a hex character to its integer value */ -char from_hex(char ch) { - return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; -} - -/* Converts an integer value to its hex character*/ -char to_hex(char code) { - static char hex[] = "0123456789abcdef"; - return hex[code & 15]; -} - -/* Returns a url-encoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -char *url_encode(char *str) { - char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; - while (*pstr) { - if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr == ':' || *pstr == '/' || *pstr == '?' || *pstr == '=' || *pstr == '&') - *pbuf++ = *pstr; - else - *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); - pstr++; - } - *pbuf = '\0'; - return buf; -} - -static int -dims_curl_debug_cb(CURL *handle, - curl_infotype type, - char *data, - size_t size, - void *clientp) -{ - dims_request_rec *d = (dims_request_rec *) clientp; - switch(type) { - case CURLINFO_HEADER_OUT: - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Curl request header data: %s ", data); - break; - default: - break; - } -} - -CURLcode -dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data) -{ - CURL *curl_handle; - CURLcode code; - - dims_image_data_t image_data; - image_data.data = NULL; - image_data.size = 0; - image_data.used = 0; - int extra_time = 0; - - /* Allow for some extra time to download the NOIMAGE image. */ - void *s = NULL; - - if (d->status == DIMS_DOWNLOAD_TIMEOUT) { - extra_time += 500; - } - - apr_pool_userdata_get((void *) &s, DIMS_CURL_SHARED_KEY, - d->r->server->process->pool); - - /* Encode the fetch URL before downloading */ - if (!d->config->disable_encoded_fetch) { - fetch_url = url_encode(fetch_url); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Encoded URL: %s ", fetch_url); - } - - curl_handle = curl_easy_init(); - curl_easy_setopt(curl_handle, CURLOPT_URL, fetch_url); - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, dims_write_image_cb); - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &image_data); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dims_write_header_cb); - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) d); - curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, d->config->download_timeout + extra_time); - curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl_handle, CURLOPT_DEBUGFUNCTION, dims_curl_debug_cb); - curl_easy_setopt(curl_handle, CURLOPT_DEBUGDATA, d); - - /* Set the user agent to dims/ */ - if (d->config->user_agent_override != NULL && d->config->user_agent_enabled == 1) { - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, d->config->user_agent_override); - } else if (d->config->user_agent_enabled == 1) { - char *dims_useragent = apr_psprintf(d->r->pool, "mod_dims/%s", MODULE_VERSION); - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, dims_useragent); - } - - /* The curl shared handle allows this process to share DNS cache - * and prevents the DNS cache from going away after every request. - */ - if (s) { - dims_curl_rec *locks = (dims_curl_rec *) s; - curl_easy_setopt(curl_handle, CURLOPT_SHARE, locks->share); - } - - code = curl_easy_perform(curl_handle); - - curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &image_data.response_code); - curl_easy_cleanup(curl_handle); - - *data = image_data; - - if (!d->config->disable_encoded_fetch) { - free(fetch_url); - } - - return code; -} - /** * Fetch remote image. If successful the MagicWand will * have the new image loaded. @@ -1020,7 +445,7 @@ dims_send_image(dims_request_rec *d) return OK; } -static apr_status_t +apr_status_t dims_cleanup(dims_request_rec *d, char *err_msg, int status) { if(status != DIMS_IGNORE) { @@ -1304,7 +729,7 @@ dims_process_image(dims_request_rec *d) return dims_send_image(d); } -static apr_status_t +apr_status_t dims_handle_request(dims_request_rec *d) { apr_time_t now_time; @@ -1561,476 +986,6 @@ dims_sizer(dims_request_rec *d) } int -aes_errors(const char *message, size_t length, void *u) -{ - request_rec *r = (request_rec *) u; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s", message); - return 0; -} - -static char * -aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_text, int encrypted_length) -{ - EVP_CIPHER_CTX *ctx; - - if (!(ctx = EVP_CIPHER_CTX_new())) { - ERR_print_errors_cb(aes_errors, r); - return NULL; - } - - if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - int decrypted_length; - int plaintext_length, out_length; - char *plaintext = apr_palloc(r->pool, encrypted_length * sizeof(char)); - if (EVP_DecryptUpdate(ctx, (unsigned char *) plaintext, &out_length, encrypted_text, encrypted_length)) { - plaintext_length = out_length; - - if (!EVP_DecryptFinal_ex(ctx, (unsigned char *) plaintext + out_length, &plaintext_length)) { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - plaintext_length += out_length; - plaintext[plaintext_length] = '\0'; - } else { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - EVP_CIPHER_CTX_free(ctx); - - return plaintext; -} - -static char * -aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_encrypted_text) { - EVP_CIPHER_CTX *ctx; - int ret; - int plaintext_length = 0; - int out_length; - char *plaintext; - - // Decode the Base64 input - int encrypted_length = apr_base64_decode_len((const char *)base64_encrypted_text); - unsigned char *encrypted_data = apr_palloc(r->pool, encrypted_length); - int decoded_length = apr_base64_decode((char *)encrypted_data, (const char *)base64_encrypted_text); - - // Extract IV (12 bytes), ciphertext, and tag (16 bytes) - unsigned char *iv = encrypted_data; - unsigned char *encrypted_text = encrypted_data + 12; // 12-byte IV - int ciphertext_length = decoded_length - 12 - 16; // 16-byte tag at the end - unsigned char *tag = encrypted_text + ciphertext_length; // 16-byte tag - - // Initialize the context - if (!(ctx = EVP_CIPHER_CTX_new())) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to create new EVP_CIPHER_CTX"); - ERR_print_errors_cb(aes_errors, r); - return NULL; - } - - // Initialize the decryption operation - if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (1)"); - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - // Set the IV length, if necessary - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set IV length"); - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - // Set the key and IV - if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (2)"); - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - plaintext = apr_palloc(r->pool, ciphertext_length + 1); // +1 for null terminator - if (!plaintext) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Memory allocation failed"); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - // Provide the message to be decrypted and obtain the plaintext output - if (!EVP_DecryptUpdate(ctx, (unsigned char *)plaintext, &out_length, encrypted_text, ciphertext_length)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptUpdate failed"); - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - plaintext_length = out_length; - - // Set expected tag value (must be done after EVP_DecryptUpdate) - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set tag"); - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - // Finalize the decryption - ret = EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext + plaintext_length, &out_length); - - EVP_CIPHER_CTX_free(ctx); - - if (ret > 0) { - plaintext_length += out_length; - plaintext[plaintext_length] = '\0'; // Explicitly add the null terminator - return plaintext; - } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptFinal_ex failed"); - ERR_print_errors_cb(aes_errors, r); - return NULL; - } -} - -/** - * The apache handler. Apache will call this method when a request - * for /dims/, /dims3/, /dims4/ or an image is recieved. - * - * Depending on how this function is called it will do one of three - * things: - * - * 1) Transform old-style request into a new-style request and - * pass it along to the dims_handle_newstyle function. - * - * 2) Parse out the URL and commands and pass them along - * to the dims_handle_newstyle function. - * - * 3) Load the image from the filesystem and pass it along - * with the commands (r->path_info) to dims_process_image. - */ -static apr_status_t -dims_handler(request_rec *r) -{ - dims_request_rec *d = (dims_request_rec *) - apr_palloc(r->pool, sizeof(dims_request_rec)); - - d->r = r; - d->pool = r->pool; - d->wand = NULL; - d->config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); - d->client_config = NULL; - d->no_image_url = d->config->no_image_url; - d->use_no_image = 0; - d->image_url = NULL; - d->filename = NULL; - d->cache_control = NULL; - d->edge_control = NULL; - d->etag = NULL; - d->last_modified = NULL; - d->request_hash = NULL; - d->status = APR_SUCCESS; - d->fetch_http_status = 0; - d->start_time = apr_time_now(); - d->download_time = 0; - d->imagemagick_time = 0; - d->use_secret_key=0; - d->optimize_resize = d->config->optimize_resize; - d->send_content_disposition = 0; - d->content_disposition_filename = NULL; - - /* Set initial notes to be logged by mod_log_config. */ - apr_table_setn(r->notes, "DIMS_STATUS", "0"); - apr_table_setn(r->notes, "DIMS_ORIG_BYTES", "-"); - apr_table_setn(r->notes, "DIMS_DL_TIME", "-"); - apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Handler %s : %s", r->handler, r->uri); - /* Handle old-style DIMS parameters. */ - if(strcmp(r->handler, "dims-local") == 0 && - (r->path_info && strlen(r->path_info) != 0)) { - /* Handle local filesystem images w/DIMS parameters. */ - d->filename = r->canonical_filename; - d->unparsed_commands = r->path_info; - - return dims_handle_request(d); - } else if(r->uri && strncmp(r->uri, "/dims/", 6) == 0) { - int status = 0; - char appid[50], b[10], w[10], h[10], q[10]; - char *fixed_url, *url; - - /* Translate provided parameters into new-style parameters. */ - b[0] = w[0] = h[0] = q[0] = '-'; - status = sscanf(r->uri + 5, - "/%49[^/]/%9[^/]/%9[^/]/%9[^/]/%9[^/]/", - (char *) &appid, (char *) &b, (char *) &w, (char *) &h, - (char *) &q); - - if(status != 5) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - int bitmap = (b[0] != '-') ? atoi(b) : -1; - double width = (w[0] != '-') ? atof(w) : 0; - double height = (h[0] != '-') ? atof(h) : 0; - int quality = (q[0] != '-') ? atoi(q) : 0; - - if(bitmap == -1) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - /* HACK: If URL has "http:/" instead of "http://", correct it. */ - url = strstr(r->uri, "http:/"); - if(url && *(url + 6) != '/') { - fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); - } else if(!url) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } else { - fixed_url = url; - } - - char *commands = apr_psprintf(r->pool, "%s", appid); - - if(bitmap & LEGACY_DIMS_RESIZE && bitmap & LEGACY_DIMS_CROP) { - if(!width && !height) { - return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); - } - commands = apr_psprintf(r->pool, "%s/legacy_thumbnail/%ldx%ld", - commands, (long) width, (long) height); - } else if(bitmap & LEGACY_DIMS_CROP || bitmap & LEGACY_DIMS_RESIZE) { - char *cmd = (bitmap & LEGACY_DIMS_RESIZE) ? "resize" : "legacy_crop"; - - if(width && !height) { - commands = apr_psprintf(r->pool, "%s/%s/%ld", - commands, cmd, (long) width); - } else if(height && !width) { - commands = apr_psprintf(r->pool, "%s/%s/x%ld", - commands, cmd, (long) height); - } else if(width && height) { - commands = apr_psprintf(r->pool, "%s/%s/%ldx%ld", - commands, cmd, (long) width, (long) height); - } else { - return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); - } - } - - if(bitmap & LEGACY_DIMS_JPG) { - commands = apr_psprintf(r->pool, "%s/format/jpg", - commands); - } else if(bitmap & LEGACY_DIMS_PNG) { - commands = apr_psprintf(r->pool, "%s/format/png", - commands); - } else if(bitmap & LEGACY_DIMS_GIF) { - commands = apr_psprintf(r->pool, "%s/format/gif", - commands); - } - - if(bitmap & LEGACY_DIMS_SHARPEN) { - commands = apr_psprintf(r->pool, "%s/sharpen/0.0x1.5", - commands); - } - - if(quality > 0 && quality <= 100) { - commands = apr_psprintf(r->pool, "%s/quality/%d", - commands, quality); - } - - /* Locate pointer to the image URL. */ - d->image_url = fixed_url; - d->unparsed_commands = commands; - - return dims_handle_request(d); - } else if ((strcmp(r->handler, "dims3") == 0) || - (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || - (strcmp(r->handler, "dims4") == 0 )) { - /* Handle new-style DIMS parameters. */ - char *p, *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; - if (( strcmp( r->handler,"dims4") == 0)) { - d->use_secret_key = 1; - } - - char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); - d->client_id = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); - - if(!(d->client_config = - apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Application ID is not valid", DIMS_BAD_CLIENT); - } - - /* Check first if URL is passed as a query parameter. */ - if(r->args) { - const size_t args_len = strlen(r->args) + 1; - char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); - char *token; - char *strtokstate; - token = apr_strtok(args, "&", &strtokstate); - while (token) { - if(strncmp(token, "url=", 4) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "ARG: %s", token); - fixed_url = apr_pstrdup(r->pool, token + 4); - ap_unescape_url(fixed_url); - - if (strcmp(fixed_url, "") == 0) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - } else if (strncmp(token, "download=1", 10) == 0) { - d->send_content_disposition = 1; - - } else if (strncmp(token, "eurl=", 4) == 0) { - eurl = apr_pstrdup(r->pool, token + 5); - - // Hash secret via SHA-1. - unsigned char *secret = (unsigned char *) d->client_config->secret_key; - unsigned char hash[SHA_DIGEST_LENGTH]; - SHA1(secret, strlen((char *) secret), hash); - - // Convert to hex. - char hex[SHA_DIGEST_LENGTH * 2 + 1]; - if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { - return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); - } - - // Use first 16 bytes. - unsigned char key[17]; - strncpy((char *) key, hex, 16); - key[16] = '\0'; - - // Force key to uppercase - unsigned char *s = key; - while (*s) { *s = toupper(*s); s++; } - - if (d->config->encryption_algorithm != NULL && - strncmp((char *)d->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) { - - fixed_url = aes_128_gcm_decrypt(r, key, eurl); - } else { - //Default is AES/ECB/PKCS5Padding - unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl)); - int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); - fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); - } - if (fixed_url == NULL) { - return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); - } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Decrypted URL: %s", fixed_url); - break; - - } else if (strncmp(token, "optimizeResize=", 4) == 0) { - d->optimize_resize = atof(token + 15); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Overriding optimize resize: %f", d->optimize_resize); - } - token = apr_strtok(NULL, "&", &strtokstate); - } - } - - /* Parse out URL to image. - * HACK: If URL has "http:/" instead of "http://", correct it. - */ - commands = apr_pstrdup(r->pool, r->uri); - if(fixed_url == NULL) { - url = strstr(r->uri, "http:/"); - if(url && *(url + 6) != '/') { - fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); - } else if(!url) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } else { - fixed_url = url; - } - - /* Strip URL off URI. This leaves only the tranformation parameters. */ - p = strstr(commands, "http:/"); - if(!p) return dims_cleanup(d, NULL, DIMS_BAD_URL); - *p = '\0'; - } - - // Convert '+' in the fixed_url to ' '. - char *image_url = apr_pstrdup(d->r->pool, fixed_url); - char *s = image_url; - while (*s) { - if (*s == '+') { - *s = ' '; - } - - s++; - } - - d->image_url = image_url; - d->unparsed_commands = commands + 6; - - /* Calculate image filename for use with content disposition. */ - apr_uri_t uri; - if (apr_uri_parse(r->pool, d->image_url, &uri) == APR_SUCCESS) { - if (!uri.path) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - const char *path = apr_filepath_name_get(uri.path); - d->content_disposition_filename = apr_pstrdup(d->r->pool, path); - } - - return dims_handle_request(d); - } else if(strcmp(r->handler, "dims-status") == 0) { - apr_time_t uptime; - - ap_set_content_type(r, "text/plain"); - ap_rvputs(r, "ALIVE\n\n", NULL); - - uptime = (apr_uint32_t) apr_time_sec(apr_time_now() - - ap_scoreboard_image->global->restart_time); - - show_time(r, uptime); - - ap_rprintf(r, "Restart time: %s\n", - ap_ht_time(r->pool, - ap_scoreboard_image->global->restart_time, - "%A, %d-%b-%Y %H:%M:%S %Z", 0)); - - ap_rprintf(r, "\nmod_dims version: %s (%s)\n", MODULE_VERSION, MODULE_RELEASE); - ap_rprintf(r, "ImageMagick version: %s\n", GetMagickVersion(NULL)); - ap_rprintf(r, "libcurl version: %s\n", curl_version()); - - ap_rprintf(r, "\nDetails\n-------\n"); - - ap_rprintf(r, "Successful requests: %d\n", - apr_atomic_read32(&stats->success_count)); - ap_rprintf(r, "Failed requests: %d\n\n", - apr_atomic_read32(&stats->failure_count)); - ap_rprintf(r, "Download timeouts: %d\n", - apr_atomic_read32(&stats->download_timeout_count)); - ap_rprintf(r, "Imagemagick Timeouts: %d\n", - apr_atomic_read32(&stats->imagemagick_timeout_count)); - - ap_rflush(r); - return OK; - } else if(strcmp(r->handler, "dims-sizer") == 0) { - char *url, *fixed_url; - url = strstr(r->uri, "http:/"); - if(url && *(url + 6) != '/') { - fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); - } else if(!url) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } else { - fixed_url = url; - } - d->image_url = fixed_url; - d->unparsed_commands = NULL; - - - return dims_sizer(d); - } - - return DECLINED; -} - -static int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) { dims_config_rec *config = (dims_config_rec *) @@ -2122,34 +1077,6 @@ dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) return OK; } -void -lock_share(CURL *handle, curl_lock_data data, - curl_lock_access access, void *userptr) -{ - dims_curl_rec *locks = (dims_curl_rec *) userptr; - - switch(data) { - case CURL_LOCK_DATA_DNS: - apr_thread_mutex_lock(locks->dns_mutex); - break; - default: - apr_thread_mutex_lock(locks->share_mutex); - } -} - -void unlock_share(CURL *handle, curl_lock_data data, void *userptr) -{ - dims_curl_rec *locks = (dims_curl_rec *) userptr; - - switch(data) { - case CURL_LOCK_DATA_DNS: - apr_thread_mutex_unlock(locks->dns_mutex); - break; - default: - apr_thread_mutex_unlock(locks->share_mutex); - } -} - static apr_status_t dims_child_cleanup(void *data) { @@ -2169,7 +1096,7 @@ dims_child_cleanup(void *data) return APR_SUCCESS; } -static void +void dims_child_init(apr_pool_t *p, server_rec *s) { MagickWandGenesis(); @@ -2201,109 +1128,3 @@ dims_child_init(apr_pool_t *p, server_rec *s) */ apr_pool_cleanup_register(p, locks, dims_child_cleanup, dims_child_cleanup); } - -static void -dims_register_hooks(apr_pool_t *p) -{ - ap_hook_post_config(dims_init, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_child_init(dims_child_init, NULL, NULL,APR_HOOK_MIDDLE); - ap_hook_handler(dims_handler, NULL, NULL, APR_HOOK_MIDDLE); -} - -static const command_rec dims_commands[] = -{ - AP_INIT_TAKE_ARGV("DimsAddWhitelist", - dims_config_set_whitelist, NULL, RSRC_CONF, - "Add whitelist hostname for DIMS URL requests."), - AP_INIT_TAKE_ARGV("DimsAddClient", - dims_config_set_client, NULL, RSRC_CONF, - "Add a client with optional no image url, max-age and downstream-ttl settings."), - AP_INIT_TAKE_ARGV("DimsIgnoreDefaultOutputFormat", - dims_config_set_ignore_default_output_format, NULL, RSRC_CONF, - "Add input formats that shouldn't be converted to the default output format."), - AP_INIT_TAKE1("DimsDefaultImageURL", - dims_config_set_no_image_url, NULL, RSRC_CONF, - "Default image if processing fails or original image doesn't exist."), - AP_INIT_TAKE1("DimsDefaultImagePrefix", - dims_config_set_image_prefix, NULL, RSRC_CONF, - "Default image prefix if URL is relative."), - AP_INIT_TAKE1("DimsCacheExpire", - dims_config_set_default_expire, NULL, RSRC_CONF, - "Default expire time for Cache-Control/Expires/Edge-Control headers, in seconds." - "The default is 86400"), - AP_INIT_TAKE1("DimsNoImageCacheExpire", - dims_config_set_no_image_expire, NULL, RSRC_CONF, - "Default expire time for Cache-Control/Expires/Edge-Control headers for NOIMAGE image, in seconds." - "The default is 60"), - AP_INIT_TAKE1("DimsDownloadTimeout", - dims_config_set_download_timeout, NULL, RSRC_CONF, - "Timeout for downloading remote images." - "The default is 3000."), - AP_INIT_TAKE1("DimsImagemagickTimeout", - dims_config_set_imagemagick_timeout, NULL, RSRC_CONF, - "Timeout for processing images." - "The default is 3000."), - AP_INIT_TAKE1("DimsImagemagickMemorySize", - dims_config_set_imagemagick_memory_size, NULL, RSRC_CONF, - "Maximum amount of memory in megabytes to use for pixel cache." - "The default is 512mb."), - AP_INIT_TAKE1("DimsImagemagickAreaSize", - dims_config_set_imagemagick_area_size, NULL, RSRC_CONF, - "Maximum amount of memory in megabytes that any one image can use." - "The default is 128mb."), - AP_INIT_TAKE1("DimsImagemagickMapSize", - dims_config_set_imagemagick_map_size, NULL, RSRC_CONF, - "Maximum amount of memory map in megabytes to use for the pixel cache." - "The default is 1024mb."), - AP_INIT_TAKE1("DimsImagemagickDiskSize", - dims_config_set_imagemagick_disk_size, NULL, RSRC_CONF, - "Maximum amount of disk space in megabytes to use for the pixel cache." - "The default is 1024mb."), - AP_INIT_TAKE1("DimsSecretMaxExpiryPeriod", - dims_config_set_secretkeyExpiryPeriod, NULL, RSRC_CONF, - "How long in the future (in seconds) can the expiry date on the URL be requesting. 0 = forever" - "The default is 0."), - AP_INIT_TAKE1("DimsStripMetadata", - dims_config_set_strip_metadata, NULL, RSRC_CONF, - "Should DIMS strip the metadata from the image, true OR false." - "The default is true."), - AP_INIT_TAKE1("DimsIncludeDisposition", - dims_config_set_include_disposition, NULL, RSRC_CONF, - "Should DIMS include Content-Disposition header, true OR false." - "The default is false."), - AP_INIT_TAKE1("DimsOptimizeResize", - dims_config_set_optimize_resize, NULL, RSRC_CONF, - "Should DIMS optimize resize operations. This has a slight impact on image quality. 0 = disabled" - "The default is 0."), - AP_INIT_TAKE1("DimsDisableEncodedFetch", - dims_config_set_encoded_fetch, NULL, RSRC_CONF, - "Should DIMS encode image url before fetching it." - "The default is 0."), - AP_INIT_TAKE1("DimsEncryptionAlgorithm", - dims_config_set_encryption_algorithm, NULL, RSRC_CONF, - "What algorithm should DIMS user to decrypt the 'eurl' parameter." - "The default is AES/ECB/PKCS5Padding."), - AP_INIT_TAKE1("DimsDefaultOutputFormat", - dims_config_set_default_output_format, NULL, RSRC_CONF, - "Default output format if 'format' command is not present in the request."), - AP_INIT_TAKE1("DimsUserAgentEnabled", - dims_config_set_user_agent_enabled, NULL, RSRC_CONF, - "Enable DIMS User-Agent header ('dims/'), true OR false." - "The default is false."), - AP_INIT_TAKE1("DimsUserAgentOverride", - dims_config_set_user_agent_override, NULL, RSRC_CONF, - "Override DIMS User-Agent header" - "The default is 'dims/."), - {NULL} -}; - -module AP_MODULE_DECLARE_DATA dims_module = -{ - STANDARD20_MODULE_STUFF, - NULL, /* dir config creater */ - NULL, /* dir merger --- default is to override */ - dims_create_config, /* server config */ - NULL, /* merge server config */ - dims_commands, /* command apr_table_t */ - dims_register_hooks /* register hooks */ -}; diff --git a/src/mod_dims.h b/src/mod_dims.h index f9ba9a9..1e8696e 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -17,6 +17,9 @@ #ifndef _MOD_DIMS_H #define _MOD_DIMS_H +#define MODULE_RELEASE "$Revision: $" +#define MODULE_VERSION "3.3.30" + #include #include #include @@ -37,6 +40,8 @@ #include +#include "request.h" + #define LEGACY_DIMS_RESIZE 1 #define LEGACY_DIMS_REFORMAT 2 #define LEGACY_DIMS_CROP 4 @@ -56,147 +61,20 @@ #define DIMS_HOSTNAME_NOT_IN_WHITELIST 64 #define DIMS_FILE_NOT_FOUND 128 -typedef struct dims_request_rec dims_request_rec; -typedef struct dims_config_rec dims_config_rec; -typedef struct dims_client_config_rec dims_client_config_rec; -typedef struct { - char *data; - size_t size; - size_t used; - long response_code; -} dims_image_data_t; - -typedef apr_status_t(dims_operation_func) (dims_request_rec *, char *args, char **err); -void smartCrop(MagickWand *wand, int resolution, unsigned long cropWidth, unsigned long cropHeight); -CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); - -dims_operation_func - dims_strip_operation, - dims_resize_operation, - dims_crop_operation, - dims_thumbnail_operation, - dims_sharpen_operation, - dims_quality_operation, - dims_format_operation, - dims_legacy_thumbnail_operation, - dims_smart_crop_operation, - dims_brightness_operation, - dims_flipflop_operation, - dims_sepia_operation, - dims_grayscale_operation, - dims_autolevel_operation, - dims_rotate_operation, - dims_invert_operation, - dims_watermark_operation, - dims_legacy_crop_operation; - -struct dims_config_rec { - int download_timeout; - int imagemagick_timeout; - - apr_table_t *whitelist; - apr_hash_t *clients; - apr_table_t *ignore_default_output_format; - - char *no_image_url; - long no_image_expire; - long default_expire; - int strip_metadata; - float optimize_resize; - int include_disposition; - int disable_encoded_fetch; - char *default_output_format; - - MagickSizeType area_size; - MagickSizeType memory_size; - MagickSizeType map_size; - MagickSizeType disk_size; - - int curl_queue_size; - char *secret_key; - char *encryption_algorithm; - long max_expiry_period; - char *cache_dir; - char *default_image_prefix; - - char *user_agent_override; - int user_agent_enabled; -}; - -struct dims_client_config_rec { - char *id; - char *no_image_url; - int cache_control_max_age; - int edge_control_downstream_ttl; - int trust_src; - int min_src_cache_control; - int max_src_cache_control; - char *secret_key; -}; - -struct dims_request_rec { - request_rec *r; - - apr_pool_t *pool; +int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); +void dims_child_init(apr_pool_t *p, server_rec *s); +void dims_register_hooks(apr_pool_t *p); - MagickWand *wand; +apr_status_t dims_handle_request(dims_request_rec *d); +apr_status_t dims_cleanup(dims_request_rec *d, char *err_msg, int status); - /* Client ID of this request. */ - char *client_id; - - /* The URL to the image being manipulated. */ - char *image_url; - int use_no_image; - - /* The URL to the NOIMAGE image in case of failures. */ - char *no_image_url; - - /* The filename if this is a local request. */ - char *filename; - - /* The unparsed commands (resize, crop, etc). */ - char *unparsed_commands; - - /* The original image size in bytes. */ - long original_image_size; - - /* The sample factor for optimizing resizing. */ - float optimize_resize; - - /* The global configuration. */ - dims_config_rec *config; - - /* The client specific configuration, if available. */ - dims_client_config_rec *client_config; - - /* The cache headers from the downloaded image. */ - char *cache_control; - char *edge_control; - char *last_modified; - char *etag; - char *request_hash; - - /* The current status of this request. If downloading - * or manipulating the image times out this will - * be set to DIMS_*_TIMEOUT. If everything is ok it will - * be set to DIMS_SUCCESS. - */ - apr_status_t status; - - /* The HTTP status code from fetching the original image */ - apr_status_t fetch_http_status; - - /* Time this request started. Used for statistics. */ - apr_time_t start_time; - apr_time_t download_time; - apr_time_t imagemagick_time; - - /* Use a whitelist, or use a secret key passed on the URI */ - int use_secret_key; +typedef struct { + apr_uint32_t success_count; + apr_uint32_t failure_count; + apr_uint32_t download_timeout_count; + apr_uint32_t imagemagick_timeout_count; +} dims_stats_rec; - /* Should Content-Disposition header bet set. */ - int send_content_disposition; - char *content_disposition_filename; -}; +extern dims_stats_rec *stats; #endif diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 2317384..7fc8b77 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -15,6 +15,8 @@ */ #include "mod_dims.h" +#include "curl.h" +#include "request.h" #include #include @@ -52,25 +54,7 @@ static DimsGravity gravities[] = { {NULL, CenterGravity} }; -/* -apr_status_t -dims_smart_crop_operation (dims_request_rec *d, char *args, char **err) { - MagickStatusType flags; - RectangleInfo rec; - ExceptionInfo ex_info; - - flags = ParseGravityGeometry(GetImageFromMagickWand(d->wand), args, &rec, &ex_info); - if(!(flags & AllValues)) { - *err = "Parsing crop geometry failed"; - return DIMS_FAILURE; - } - - // MAGICK_CHECK(MagickResizeImage(d->wand, rec.width, rec.height, UndefinedFilter, 1), d); - smartCrop(d->wand, 20, rec.width, rec.height); - - return DIMS_SUCCESS; -} -*/ +CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); apr_status_t dims_strip_operation (dims_request_rec *d, char *args, char **err) { diff --git a/src/mod_dims_ops.h b/src/mod_dims_ops.h new file mode 100644 index 0000000..773ab3f --- /dev/null +++ b/src/mod_dims_ops.h @@ -0,0 +1,29 @@ + +#ifndef _MOD_DIMS_OPS_H +#define _MOD_DIMS_OPS_H + +#include "request.h" + +typedef apr_status_t(dims_operation_func) (dims_request_rec *, char *args, char **err); + +dims_operation_func + dims_strip_operation, + dims_resize_operation, + dims_crop_operation, + dims_thumbnail_operation, + dims_sharpen_operation, + dims_quality_operation, + dims_format_operation, + dims_legacy_thumbnail_operation, + dims_smart_crop_operation, + dims_brightness_operation, + dims_flipflop_operation, + dims_sepia_operation, + dims_grayscale_operation, + dims_autolevel_operation, + dims_rotate_operation, + dims_invert_operation, + dims_watermark_operation, + dims_legacy_crop_operation; + +#endif \ No newline at end of file diff --git a/src/module.c b/src/module.c new file mode 100644 index 0000000..fa6027a --- /dev/null +++ b/src/module.c @@ -0,0 +1,24 @@ + +#include "mod_dims.h" +#include "handler.h" +#include "directives.h" +#include "module.h" + +void +dims_register_hooks(apr_pool_t *p) +{ + ap_hook_post_config(dims_init, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(dims_child_init, NULL, NULL,APR_HOOK_MIDDLE); + ap_hook_handler(dims_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA dims_module = +{ + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + dims_create_config, /* server config */ + NULL, /* merge server config */ + dims_directives, /* command apr_table_t */ + dims_register_hooks /* register hooks */ +}; \ No newline at end of file diff --git a/src/module.h b/src/module.h new file mode 100644 index 0000000..5eb927a --- /dev/null +++ b/src/module.h @@ -0,0 +1,9 @@ + +#ifndef _MODULE_H_ +#define _MODULE_H_ + +#include "configuration.h" + +extern module AP_MODULE_DECLARE_DATA dims_module; + +#endif \ No newline at end of file diff --git a/src/request.h b/src/request.h new file mode 100644 index 0000000..4bcafdb --- /dev/null +++ b/src/request.h @@ -0,0 +1,76 @@ +#ifndef _REQUEST_H +#define _REQUEST_H + +#include +#include + +#include "configuration.h" + +typedef struct dims_request_rec dims_request_rec; + +struct dims_request_rec { + request_rec *r; + + apr_pool_t *pool; + + MagickWand *wand; + + /* Client ID of this request. */ + char *client_id; + + /* The URL to the image being manipulated. */ + char *image_url; + int use_no_image; + + /* The URL to the NOIMAGE image in case of failures. */ + char *no_image_url; + + /* The filename if this is a local request. */ + char *filename; + + /* The unparsed commands (resize, crop, etc). */ + char *unparsed_commands; + + /* The original image size in bytes. */ + long original_image_size; + + /* The sample factor for optimizing resizing. */ + float optimize_resize; + + /* The global configuration. */ + dims_config_rec *config; + + /* The client specific configuration, if available. */ + dims_client_config_rec *client_config; + + /* The cache headers from the downloaded image. */ + char *cache_control; + char *edge_control; + char *last_modified; + char *etag; + char *request_hash; + + /* The current status of this request. If downloading + * or manipulating the image times out this will + * be set to DIMS_*_TIMEOUT. If everything is ok it will + * be set to DIMS_SUCCESS. + */ + apr_status_t status; + + /* The HTTP status code from fetching the original image */ + apr_status_t fetch_http_status; + + /* Time this request started. Used for statistics. */ + apr_time_t start_time; + apr_time_t download_time; + apr_time_t imagemagick_time; + + /* Use a whitelist, or use a secret key passed on the URI */ + int use_secret_key; + + /* Should Content-Disposition header bet set. */ + int send_content_disposition; + char *content_disposition_filename; +}; + +#endif \ No newline at end of file From 90ba1271febce2f3d0ad317f2c1e14ec9994f8bd Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 19:54:11 -0500 Subject: [PATCH 06/69] Remove dims_sizer endpoint --- src/mod_dims.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index c2ccc53..7940831 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -953,37 +953,6 @@ dims_handle_request(dims_request_rec *d) return dims_cleanup(d, NULL, DIMS_FAILURE); } -/** - * dims_sizer - return the size of the image (height: X\n width: X) - */ -static apr_status_t -dims_sizer(dims_request_rec *d) -{ - apr_time_t now_time; - - apr_uri_t uri; - long width, height; - - d->wand = NewMagickWand(); - now_time = apr_time_now(); - if(!d->image_url ) { - return DECLINED; - } - if(apr_uri_parse(d->pool, d->image_url, &uri) != APR_SUCCESS) { - return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); - } - if(dims_fetch_remote_image(d, d->image_url ) != 0) { - return dims_cleanup(d, "Unable to get image file", DIMS_FILE_NOT_FOUND); - } - - width = MagickGetImageWidth(d->wand); - height = MagickGetImageHeight(d->wand); - DestroyMagickWand(d->wand); - ap_set_content_type(d->r, "text/plain"); - ap_rprintf(d->r, "{\n\t\"height\": %ld,\n\t\"width\": %ld\n}", height, width ); - return OK; - -} int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) From 31c35c0a4d2456a5d3b72b86d76f11c8d26d5072 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 20:46:10 -0500 Subject: [PATCH 07/69] Update version to 4.0.0alpha --- configure.ac | 2 +- src/mod_dims.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2616604..0f85b05 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(mod_dims, 3.3.30, [jeremy.collins@beetlebug.org]) +AC_INIT(mod_dims, 4.0.0alpha, [jeremy.collins@beetlebug.org]) AM_INIT_AUTOMAKE([no-define]) AC_CONFIG_SRCDIR([src/mod_dims.c]) diff --git a/src/mod_dims.h b/src/mod_dims.h index 1e8696e..d1f1b8b 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -18,7 +18,7 @@ #define _MOD_DIMS_H #define MODULE_RELEASE "$Revision: $" -#define MODULE_VERSION "3.3.30" +#define MODULE_VERSION "4.0.0alpha" #include #include From cadb2d889ddfdaaf320226569dc0e67ed90e800f Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 21:25:05 -0500 Subject: [PATCH 08/69] Move curl init/cleanup to curl.c|h --- src/curl.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/curl.h | 4 ++-- src/mod_dims.c | 41 +++-------------------------------------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/curl.c b/src/curl.c index 60de840..bc6ed88 100644 --- a/src/curl.c +++ b/src/curl.c @@ -1,4 +1,5 @@ +#include #include #include "request.h" @@ -208,3 +209,46 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *dat return code; } + +static apr_status_t +dims_curl_cleanup(void *data) { + dims_curl_rec *locks = (dims_curl_rec *) data; + + curl_share_cleanup(locks->share); + curl_global_cleanup(); + + apr_thread_mutex_destroy(locks->share_mutex); + apr_thread_mutex_destroy(locks->dns_mutex); + + apr_pool_userdata_set(NULL, DIMS_CURL_SHARED_KEY, NULL, locks->s->process->pool); +} + +void +dims_curl_init(apr_pool_t *p, server_rec *s) { + curl_global_init(CURL_GLOBAL_ALL); + + dims_curl_rec *locks = (dims_curl_rec *) apr_pcalloc(p, sizeof(dims_curl_rec)); + + locks->s = s; + locks->share = curl_share_init(); + + apr_thread_mutex_create(&locks->share_mutex, APR_THREAD_MUTEX_DEFAULT, p); + apr_thread_mutex_create(&locks->dns_mutex, APR_THREAD_MUTEX_DEFAULT, p); + + curl_share_setopt(locks->share, CURLSHOPT_LOCKFUNC, lock_share); + curl_share_setopt(locks->share, CURLSHOPT_UNLOCKFUNC, unlock_share); + curl_share_setopt(locks->share, CURLSHOPT_USERDATA, (void *) locks); + curl_share_setopt(locks->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + + /* We have to associate our handle/locks with the process->pool otherwise + * we won't be able to get at it from the remote_fetch_image function. This + * pool doesn't seem to go away when the child process goes away so we + * have to register the clean up method below. + */ + apr_pool_userdata_set(locks, DIMS_CURL_SHARED_KEY, NULL, s->process->pool); + + /* Register cleanup with the 'p' pool so we can clean up the locks and + * shared curl handle when this process dies. + */ + apr_pool_cleanup_register(p, locks, dims_curl_cleanup, dims_curl_cleanup); +} \ No newline at end of file diff --git a/src/curl.h b/src/curl.h index 39e0734..b8336ff 100644 --- a/src/curl.h +++ b/src/curl.h @@ -25,7 +25,7 @@ typedef struct { } dims_image_data_t; CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); -void lock_share(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr); -void unlock_share(CURL *handle, curl_lock_data data, void *userptr); + +void dims_curl_init(apr_pool_t *p, server_rec *s); #endif \ No newline at end of file diff --git a/src/mod_dims.c b/src/mod_dims.c index 7940831..e4f6a6e 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -1049,17 +1049,6 @@ dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) static apr_status_t dims_child_cleanup(void *data) { - dims_curl_rec *locks = (dims_curl_rec *) data; - - curl_share_cleanup(locks->share); - curl_global_cleanup(); - - apr_thread_mutex_destroy(locks->share_mutex); - apr_thread_mutex_destroy(locks->dns_mutex); - - apr_pool_userdata_set(NULL, DIMS_CURL_SHARED_KEY, NULL, - locks->s->process->pool); - MagickWandTerminus(); return APR_SUCCESS; @@ -1068,32 +1057,8 @@ dims_child_cleanup(void *data) void dims_child_init(apr_pool_t *p, server_rec *s) { - MagickWandGenesis(); - curl_global_init(CURL_GLOBAL_ALL); - - dims_curl_rec *locks = - (dims_curl_rec *) apr_pcalloc(p, sizeof(dims_curl_rec)); - - locks->s = s; - locks->share = curl_share_init(); + dims_curl_init(p, s); - apr_thread_mutex_create(&locks->share_mutex, APR_THREAD_MUTEX_DEFAULT, p); - apr_thread_mutex_create(&locks->dns_mutex, APR_THREAD_MUTEX_DEFAULT, p); - - curl_share_setopt(locks->share, CURLSHOPT_LOCKFUNC, lock_share); - curl_share_setopt(locks->share, CURLSHOPT_UNLOCKFUNC, unlock_share); - curl_share_setopt(locks->share, CURLSHOPT_USERDATA, (void *) locks); - curl_share_setopt(locks->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); - - /* We have to associate our handle/locks with the process->pool otherwise - * we won't be able to get at it from the remote_fetch_image function. This - * pool doesn't seem to go away when the child process goes away so we - * have to register the clean up method below. - */ - apr_pool_userdata_set(locks, DIMS_CURL_SHARED_KEY, NULL, s->process->pool); - - /* Register cleanup with the 'p' pool so we can clean up the locks and - * shared curl handle when this process dies. - */ - apr_pool_cleanup_register(p, locks, dims_child_cleanup, dims_child_cleanup); + MagickWandGenesis(); + apr_pool_cleanup_register(p, NULL, dims_child_cleanup, dims_child_cleanup); } From 0a122379167cd0e0c5dcb242b4f2536fc64c4960 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 21:34:23 -0500 Subject: [PATCH 09/69] Remove support for local filesystem --- src/mod_dims.c | 156 +++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 103 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index e4f6a6e..3f1f0e5 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -112,108 +112,79 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Loading image from %s", fetch_url); - /* Allow file:/// references for NOIMAGE urls. */ - if(url == NULL && strncmp(fetch_url, "file:///", 8) == 0) { - char *filename = fetch_url + 7; - apr_finfo_t finfo; - apr_status_t status; - apr_time_t start_time; - - /* Read image from disk. */ - start_time = apr_time_now(); - status = apr_stat(&finfo, filename, APR_FINFO_SIZE, d->pool); - if(status != 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "mod_dims error, 'NOIMAGE image not found at %s', " - "on request: %s ", filename, d->r->uri); - return 1; - } - d->download_time = (apr_time_now() - start_time) / 1000; - d->original_image_size = finfo.size; + CURLcode code = dims_get_image_data(d, fetch_url, &image_data); - start_time = apr_time_now(); - if(MagickReadImage(d->wand, filename) == MagickFalse) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "mod_dims error, 'Failed to load NOIMAGE image from %s', " - "on request: %s ", filename, d->r->uri); - return 1; + start_time = apr_time_now(); + if(code != 0) { + if(image_data.data) { + free(image_data.data); } - d->imagemagick_time += (apr_time_now() - start_time) / 1000; - } else { - CURLcode code = dims_get_image_data(d, fetch_url, &image_data); - - start_time = apr_time_now(); - if(code != 0) { - if(image_data.data) { - free(image_data.data); - } - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "libcurl error, '%s', on request: %s ", - curl_easy_strerror(code), d->r->uri); - d->status = DIMS_FAILURE; - d->fetch_http_status = 500; - if(code == CURLE_OPERATION_TIMEDOUT) { - d->status = DIMS_DOWNLOAD_TIMEOUT; - } - - d->download_time = (apr_time_now() - start_time) / 1000; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, + "libcurl error, '%s', on request: %s ", + curl_easy_strerror(code), d->r->uri); - return 1; + d->status = DIMS_FAILURE; + d->fetch_http_status = 500; + if(code == CURLE_OPERATION_TIMEDOUT) { + d->status = DIMS_DOWNLOAD_TIMEOUT; } d->download_time = (apr_time_now() - start_time) / 1000; - // Don't set the fetch_http_status if we're downloading the NOIMAGE image. - if (url != NULL) { - d->fetch_http_status = image_data.response_code; - } + return 1; + } - if(image_data.response_code != 200) { - if(image_data.response_code == 404) { - d->status = DIMS_FILE_NOT_FOUND; - } + d->download_time = (apr_time_now() - start_time) / 1000; - if(image_data.data) { - free(image_data.data); - } - - return 1; - } + // Don't set the fetch_http_status if we're downloading the NOIMAGE image. + if (url != NULL) { + d->fetch_http_status = image_data.response_code; + } - char *actual_image_data = image_data.data; + if(image_data.response_code != 200) { + if(image_data.response_code == 404) { + d->status = DIMS_FILE_NOT_FOUND; + } - // Ensure SVGs have the appropriate XML header. - if (image_data.size >= 4 && strncmp(image_data.data, "pool, "\n", image_data.data, NULL); - image_data.used += 55; + if(image_data.data) { + free(image_data.data); } + + return 1; + } - start_time = apr_time_now(); - if(MagickReadImageBlob(d->wand, actual_image_data, image_data.used) - == MagickFalse) { - ExceptionType et; + char *actual_image_data = image_data.data; - if(image_data.data) { - free(image_data.data); - } + // Ensure SVGs have the appropriate XML header. + if (image_data.size >= 4 && strncmp(image_data.data, "pool, "\n", image_data.data, NULL); + image_data.used += 55; + } - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "ImageMagick error, '%s', on request: %s ", - MagickGetException(d->wand, &et), d->r->uri); + start_time = apr_time_now(); + if(MagickReadImageBlob(d->wand, actual_image_data, image_data.used) + == MagickFalse) { + ExceptionType et; - return 1; - } - d->imagemagick_time += (apr_time_now() - start_time) / 1000; + if(image_data.data) { + free(image_data.data); + } - if(d->status != DIMS_DOWNLOAD_TIMEOUT) { - d->original_image_size = image_data.used; - } + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, + "ImageMagick error, '%s', on request: %s ", + MagickGetException(d->wand, &et), d->r->uri); + + return 1; + } + d->imagemagick_time += (apr_time_now() - start_time) / 1000; - free(image_data.data); + if(d->status != DIMS_DOWNLOAD_TIMEOUT) { + d->original_image_size = image_data.used; } + free(image_data.data); + return 0; } @@ -861,28 +832,7 @@ dims_handle_request(dims_request_rec *d) } } - if(d->filename) { - /* Handle local images. */ - - apr_finfo_t finfo; - apr_status_t status; - apr_time_t start_time; - - /* Read image from disk. */ - start_time = apr_time_now(); - status = apr_stat(&finfo, d->filename, APR_FINFO_SIZE, d->pool); - if(status != 0) { - return dims_cleanup(d, "Unable to stat image file", DIMS_FILE_NOT_FOUND); - } - d->download_time = (apr_time_now() - start_time) / 1000; - d->original_image_size = finfo.size; - - start_time = apr_time_now(); - MAGICK_CHECK(MagickReadImage(d->wand, d->filename), d); - d->imagemagick_time += (apr_time_now() - start_time) / 1000; - - return dims_process_image(d); - } else if(d->image_url || d->no_image_url) { + if(d->image_url || d->no_image_url) { /* Handle remote images. */ char *fetch_url = NULL; From f31555d59ebb8ff2965d71a1341260dc68e232f2 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 21:38:11 -0500 Subject: [PATCH 10/69] Remove legacy dims support --- src/handler.c | 95 +-------------------------------------------------- 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/src/handler.c b/src/handler.c index 262b80b..852a18f 100644 --- a/src/handler.c +++ b/src/handler.c @@ -87,101 +87,8 @@ dims_handler(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Handler %s : %s", r->handler, r->uri); - /* Handle old-style DIMS parameters. */ - if(strcmp(r->handler, "dims-local") == 0 && - (r->path_info && strlen(r->path_info) != 0)) { - /* Handle local filesystem images w/DIMS parameters. */ - d->filename = r->canonical_filename; - d->unparsed_commands = r->path_info; - return dims_handle_request(d); - } else if(r->uri && strncmp(r->uri, "/dims/", 6) == 0) { - int status = 0; - char appid[50], b[10], w[10], h[10], q[10]; - char *fixed_url, *url; - - /* Translate provided parameters into new-style parameters. */ - b[0] = w[0] = h[0] = q[0] = '-'; - status = sscanf(r->uri + 5, - "/%49[^/]/%9[^/]/%9[^/]/%9[^/]/%9[^/]/", - (char *) &appid, (char *) &b, (char *) &w, (char *) &h, - (char *) &q); - - if(status != 5) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - int bitmap = (b[0] != '-') ? atoi(b) : -1; - double width = (w[0] != '-') ? atof(w) : 0; - double height = (h[0] != '-') ? atof(h) : 0; - int quality = (q[0] != '-') ? atoi(q) : 0; - - if(bitmap == -1) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - /* HACK: If URL has "http:/" instead of "http://", correct it. */ - url = strstr(r->uri, "http:/"); - if(url && *(url + 6) != '/') { - fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); - } else if(!url) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } else { - fixed_url = url; - } - - char *commands = apr_psprintf(r->pool, "%s", appid); - - if(bitmap & LEGACY_DIMS_RESIZE && bitmap & LEGACY_DIMS_CROP) { - if(!width && !height) { - return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); - } - commands = apr_psprintf(r->pool, "%s/legacy_thumbnail/%ldx%ld", - commands, (long) width, (long) height); - } else if(bitmap & LEGACY_DIMS_CROP || bitmap & LEGACY_DIMS_RESIZE) { - char *cmd = (bitmap & LEGACY_DIMS_RESIZE) ? "resize" : "legacy_crop"; - - if(width && !height) { - commands = apr_psprintf(r->pool, "%s/%s/%ld", - commands, cmd, (long) width); - } else if(height && !width) { - commands = apr_psprintf(r->pool, "%s/%s/x%ld", - commands, cmd, (long) height); - } else if(width && height) { - commands = apr_psprintf(r->pool, "%s/%s/%ldx%ld", - commands, cmd, (long) width, (long) height); - } else { - return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); - } - } - - if(bitmap & LEGACY_DIMS_JPG) { - commands = apr_psprintf(r->pool, "%s/format/jpg", - commands); - } else if(bitmap & LEGACY_DIMS_PNG) { - commands = apr_psprintf(r->pool, "%s/format/png", - commands); - } else if(bitmap & LEGACY_DIMS_GIF) { - commands = apr_psprintf(r->pool, "%s/format/gif", - commands); - } - - if(bitmap & LEGACY_DIMS_SHARPEN) { - commands = apr_psprintf(r->pool, "%s/sharpen/0.0x1.5", - commands); - } - - if(quality > 0 && quality <= 100) { - commands = apr_psprintf(r->pool, "%s/quality/%d", - commands, quality); - } - - /* Locate pointer to the image URL. */ - d->image_url = fixed_url; - d->unparsed_commands = commands; - - return dims_handle_request(d); - } else if ((strcmp(r->handler, "dims3") == 0) || + if ((strcmp(r->handler, "dims3") == 0) || (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || (strcmp(r->handler, "dims4") == 0 )) { /* Handle new-style DIMS parameters. */ From f85945debd2212db641e90158c5bca28eb32c8e5 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 9 Nov 2024 23:53:16 -0500 Subject: [PATCH 11/69] Partial refactor of handler logic The goal here is to split the dims3 and dims4 verification logic. --- src/handler.c | 108 +++++++++-------- src/mod_dims.c | 312 +++++++++++++++++++------------------------------ src/request.h | 5 + 3 files changed, 181 insertions(+), 244 deletions(-) diff --git a/src/handler.c b/src/handler.c index 852a18f..c4c8ba6 100644 --- a/src/handler.c +++ b/src/handler.c @@ -32,6 +32,38 @@ static void show_time(request_rec *r, apr_interval_time_t tsecs) ap_rprintf(r, "\n"); } +static dims_request_rec * +dims_create_request(request_rec *r) +{ + dims_request_rec *request = (dims_request_rec *) apr_palloc(r->pool, sizeof(dims_request_rec)); + dims_config_rec *config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); + + request->r = r; + request->pool = r->pool; + request->wand = NewMagickWand(); + request->config = config; + request->client_config = NULL; + request->no_image_url = request->config->no_image_url; + request->use_no_image = 0; + request->image_url = NULL; + request->filename = NULL; + request->cache_control = NULL; + request->edge_control = NULL; + request->etag = NULL; + request->last_modified = NULL; + request->request_hash = NULL; + request->status = APR_SUCCESS; + request->fetch_http_status = 0; + request->start_time = apr_time_now(); + request->download_time = 0; + request->imagemagick_time = 0; + request->use_secret_key=0; + request->optimize_resize = config->optimize_resize; + request->send_content_disposition = 0; + request->content_disposition_filename = NULL; + + return request; +} /** * The apache handler. Apache will call this method when a request @@ -50,34 +82,9 @@ static void show_time(request_rec *r, apr_interval_time_t tsecs) * with the commands (r->path_info) to dims_process_image. */ apr_status_t -dims_handler(request_rec *r) +dims_handler(request_rec *r) { - dims_request_rec *d = (dims_request_rec *) - apr_palloc(r->pool, sizeof(dims_request_rec)); - - d->r = r; - d->pool = r->pool; - d->wand = NULL; - d->config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); - d->client_config = NULL; - d->no_image_url = d->config->no_image_url; - d->use_no_image = 0; - d->image_url = NULL; - d->filename = NULL; - d->cache_control = NULL; - d->edge_control = NULL; - d->etag = NULL; - d->last_modified = NULL; - d->request_hash = NULL; - d->status = APR_SUCCESS; - d->fetch_http_status = 0; - d->start_time = apr_time_now(); - d->download_time = 0; - d->imagemagick_time = 0; - d->use_secret_key=0; - d->optimize_resize = d->config->optimize_resize; - d->send_content_disposition = 0; - d->content_disposition_filename = NULL; + dims_request_rec *d = dims_create_request(r); /* Set initial notes to be logged by mod_log_config. */ apr_table_setn(r->notes, "DIMS_STATUS", "0"); @@ -85,12 +92,11 @@ dims_handler(request_rec *r) apr_table_setn(r->notes, "DIMS_DL_TIME", "-"); apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Handler %s : %s", r->handler, r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Handler %s : %s", r->handler, r->uri); if ((strcmp(r->handler, "dims3") == 0) || - (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || - (strcmp(r->handler, "dims4") == 0 )) { + (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || (strcmp(r->handler, "dims4") == 0 )) { + /* Handle new-style DIMS parameters. */ char *p, *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; if (( strcmp( r->handler,"dims4") == 0)) { @@ -98,11 +104,23 @@ dims_handler(request_rec *r) } char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); + d->unparsed_commands = unparsed_commands; + d->client_id = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); + d->signature = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); + d->expiration = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); + d->commands = apr_pstrdup(d->pool, unparsed_commands); + char *s = d->commands; + while (*s) { + if (*s == ' ') { + *s = '+'; + } + + s++; + } - if(!(d->client_config = - apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Application ID is not valid", DIMS_BAD_CLIENT); + if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); } /* Check first if URL is passed as a query parameter. */ @@ -171,29 +189,9 @@ dims_handler(request_rec *r) } } - /* Parse out URL to image. - * HACK: If URL has "http:/" instead of "http://", correct it. - */ - commands = apr_pstrdup(r->pool, r->uri); - if(fixed_url == NULL) { - url = strstr(r->uri, "http:/"); - if(url && *(url + 6) != '/') { - fixed_url = apr_psprintf(r->pool, "http://%s", url + 6); - } else if(!url) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } else { - fixed_url = url; - } - - /* Strip URL off URI. This leaves only the tranformation parameters. */ - p = strstr(commands, "http:/"); - if(!p) return dims_cleanup(d, NULL, DIMS_BAD_URL); - *p = '\0'; - } - // Convert '+' in the fixed_url to ' '. char *image_url = apr_pstrdup(d->r->pool, fixed_url); - char *s = image_url; + s = image_url; while (*s) { if (*s == '+') { *s = ' '; diff --git a/src/mod_dims.c b/src/mod_dims.c index 3f1f0e5..3ac16a8 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -379,7 +379,6 @@ dims_send_image(dims_request_rec *d) MagickRelinquishMemory(blob); MagickRelinquishMemory(format); - DestroyMagickWand(d->wand); /* After the image is sent record stats about this request. */ if(d->status == DIMS_SUCCESS) { @@ -434,7 +433,6 @@ dims_cleanup(dims_request_rec *d, char *err_msg, int status) } MagickRelinquishMemory(msg); - DestroyMagickWand(d->wand); } if(err_msg) { @@ -444,19 +442,16 @@ dims_cleanup(dims_request_rec *d, char *err_msg, int status) } if(d->no_image_url) { - d->wand = NewMagickWand(); if(!dims_fetch_remote_image(d, NULL)) { return dims_send_image(d); - } - DestroyMagickWand(d->wand); + } } + if ( status != DIMS_SUCCESS ) { return HTTP_NOT_FOUND; + } else { + return DECLINED; } - else { - return DECLINED; - } - } /** @@ -478,14 +473,10 @@ dims_set_optimal_geometry(dims_request_rec *d) { MagickStatusType flags; RectangleInfo rec; - const char *cmds = d->unparsed_commands; - - if(!d->wand) { - d->wand = NewMagickWand(); - } + const char *cmds = d->commands; /* Process operations. */ - while(cmds < d->unparsed_commands + strlen(d->unparsed_commands)) { + while(cmds < d->commands + strlen(d->commands)) { char *command = ap_getword(d->pool, &cmds, '/'); if(strcmp(command, "resize") == 0 || @@ -592,8 +583,8 @@ dims_process_image(dims_request_rec *d) if (images == 1 || should_flatten) { bool output_format_provided = false; - const char *cmds = d->unparsed_commands; - while(cmds < d->unparsed_commands + strlen(d->unparsed_commands)) { + const char *cmds = d->commands; + while(cmds < d->commands + strlen(d->commands)) { char *command = ap_getword(d->pool, &cmds, '/'); if (strcmp(command, "format") == 0) { @@ -697,211 +688,154 @@ dims_process_image(dims_request_rec *d) */ SetImageProgressMonitor(GetImageFromMagickWand(d->wand), NULL, NULL); - return dims_send_image(d); + return DIMS_SUCCESS; } -apr_status_t -dims_handle_request(dims_request_rec *d) -{ - apr_time_t now_time; - d->wand = NewMagickWand(); +int +verify_dims4_signature(dims_request_rec *d) { + char *gen_hash; - /* Check to make sure the client id is valid. */ - if(*d->unparsed_commands == '/') { - d->unparsed_commands++; - } + // Throw all query params and their values into a hash table. + // This is used to derive additional signature params. + apr_hash_t *params = apr_hash_make(d->pool); - d->client_id = ap_getword(d->pool, (const char **) &d->unparsed_commands, '/'); + if (d->r->args) { + const size_t args_len = strlen(d->r->args) + 1; + char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); + char *token; + char *strtokstate; - if(!(d->client_config = - apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Application ID is not valid", DIMS_BAD_CLIENT); + token = apr_strtok(args, "&", &strtokstate); + while (token) { + char *param = strtok(token, "="); + apr_hash_set(params, param, APR_HASH_KEY_STRING, apr_pstrdup(d->r->pool, param + strlen(param) + 1)); + token = apr_strtok(NULL, "&", &strtokstate); + } } - if(d->client_config && d->client_config->no_image_url) { - d->no_image_url = d->client_config->no_image_url; - } + // Standard signature params. + char *signature_params = apr_pstrcat(d->pool, d->expiration, d->client_config->secret_key, d->commands, d->image_url, NULL); - now_time = apr_time_now(); - if ( d->use_secret_key == 1 ) { - char *hash; - char *expires_str; - long expires; - char *gen_hash; - long now; - hash = ap_getword(d->pool, (const char**)&d->unparsed_commands,'/'); - expires_str = ap_getword(d->pool, (const char**)&d->unparsed_commands,'/'); - expires = atol( expires_str); - now = apr_time_sec(now_time); - if ( expires - now < 0 ) { - ap_log_rerror( APLOG_MARK, APLOG_DEBUG,0, d->r, "Image expired: %s now=%ld", d->r->uri,now); - return dims_cleanup( d, "Image Key has expired", DIMS_BAD_URL); - } - if ( expires - now > d->config->max_expiry_period && d->config->max_expiry_period >0 ) { - ap_log_rerror( APLOG_MARK, APLOG_DEBUG,0, d->r, - "Image expiry too far in the future:%s %s now=%ld",expires_str, d->r->uri,now); - return dims_cleanup(d, "Image key too far in the future", DIMS_BAD_URL); - } + // Concatenate additional params. + char *token; + char *strtokstate; + token = apr_strtok(apr_hash_get(params, "_keys", APR_HASH_KEY_STRING), ",", &strtokstate); + while (token) { + signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(params, token, APR_HASH_KEY_STRING), NULL); + token = apr_strtok(NULL, ",", &strtokstate); + } - // Throw all query params and their values into a hash table. - // This is used to derive additional signature params. - apr_hash_t *params = apr_hash_make(d->pool); - - if (d->r->args) { - const size_t args_len = strlen(d->r->args) + 1; - char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); - char *token; - char *strtokstate; - - token = apr_strtok(args, "&", &strtokstate); - while (token) { - char *param = strtok(token, "="); - apr_hash_set(params, param, APR_HASH_KEY_STRING, apr_pstrdup(d->r->pool, param + strlen(param) + 1)); - token = apr_strtok(NULL, "&", &strtokstate); - } - } + // Hash. + gen_hash = ap_md5(d->pool, (unsigned char *) signature_params); + + if(d->client_config->secret_key == NULL) { + gen_hash[7] = '\0'; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, + "Developer key not set for client '%s'", d->client_config->id); + return dims_cleanup(d, "Missing Developer Key", DIMS_BAD_CLIENT); + } else if (strncasecmp(d->signature, gen_hash, 6) != 0) { + gen_hash[7] = '\0'; - // Convert %20 (space) back to '+' in commands. This fixes an issue with "+" being encoded as %20 by some clients. - char *commands = apr_pstrdup(d->r->pool, d->unparsed_commands); - char *s = commands; - while (*s) { - if (*s == ' ') { - *s = '+'; - } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, + "Key Mismatch: wanted %6s got %6s [%s?url=%s]", gen_hash, d->signature, d->r->uri, d->image_url); + return dims_cleanup(d, "Key mismatch", DIMS_BAD_URL); + } - s++; - } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, + "secret key (%s) to validated (%s:%s)", d->signature, d->commands, d->image_url); - // Standard signature params. - char *signature_params = apr_pstrcat(d->pool, expires_str, d->client_config->secret_key, commands, d->image_url, NULL); + return 1; +} - // Concatenate additional params. - char *token; - char *strtokstate; - token = apr_strtok(apr_hash_get(params, "_keys", APR_HASH_KEY_STRING), ",", &strtokstate); - while (token) { - signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(params, token, APR_HASH_KEY_STRING), NULL); - token = apr_strtok(NULL, ",", &strtokstate); - } +int +verify_dims3_allowlist(dims_request_rec *d) { + char *fetch_url = NULL; + char *hostname, *state = "exact"; + apr_uri_t uri; + int found = 0, done = 0; - // Hash. - gen_hash = ap_md5(d->pool, (unsigned char *) signature_params); - - if(d->client_config->secret_key == NULL) { - gen_hash[7] = '\0'; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Developer key not set for client '%s'", d->client_config->id); - return dims_cleanup(d, "Missing Developer Key", DIMS_BAD_CLIENT); - } else if (strncasecmp(hash, gen_hash, 6) != 0) { - gen_hash[7] = '\0'; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Key Mismatch: wanted %6s got %6s [%s?url=%s]", gen_hash, hash, d->r->uri, d->image_url); - return dims_cleanup(d, "Key mismatch", DIMS_BAD_URL); - } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "secret key (%s) to validated (%s:%s)", hash, d->unparsed_commands,d->image_url); + /* Check to make sure the URLs hostname is in the whitelist. Wildcards + * are handled by repeatedly checking the hash for a match after removing + * each part of the hostname until a match is found. If a match is found + * and it's value is set to "glob" the match will be accepted. + */ + if(apr_uri_parse(d->pool, d->image_url, &uri) != APR_SUCCESS) { + return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); } - d->request_hash = ap_md5(d->pool, - (unsigned char *) apr_pstrcat(d->pool, d->client_id, - d->unparsed_commands, d->image_url, NULL)); - - dims_set_optimal_geometry(d); + char *filename = strrchr(uri.path, '/'); + if (!filename || !uri.hostname) { + return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); + } - if (d->image_url && *d->image_url == '/') { - request_rec *sub_req = ap_sub_req_lookup_uri(d->image_url, d->r, NULL); + if (*filename == '/') { + d->filename = ++filename; + } - if (d->config->default_image_prefix != NULL) { - d->image_url = apr_pstrcat(d->r->pool, d->config->default_image_prefix, d->image_url, NULL); - } else if (sub_req && sub_req->canonical_filename) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Looking up image locally: %s", sub_req->canonical_filename); - d->filename = sub_req->canonical_filename; + hostname = uri.hostname; + while(!done) { + char *value = (char *) apr_table_get(d->config->whitelist, hostname); + if(value && strcmp(value, state) == 0) { + done = found = 1; } else { - const char *req_server; - char *req_port; - int port; - - port = ap_get_server_port(d->r); - req_server = ap_get_server_name_for_url(d->r); - req_port = ap_is_default_port(port, d->r) ? "" : apr_psprintf(d->r->pool, ":%u", port); - - d->image_url = apr_psprintf(d->r->pool, "%s://%s%s%s", - (char *) ap_http_scheme(d->r), req_server, req_port, d->image_url); - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Expanded relative URI to fully qualified URL since no local file existed: %s", d->image_url); + hostname = strstr(hostname, "."); + if(!hostname) { + done = 1; + } else { + hostname++; + } + state = "glob"; } } - if(d->image_url || d->no_image_url) { - /* Handle remote images. */ - - char *fetch_url = NULL; + return found; +} - char *hostname, *state = "exact"; - apr_uri_t uri; - int found = 0, done = 0; +apr_status_t +dims_handle_request(dims_request_rec *d) +{ + apr_time_t now_time = apr_time_now(); - /* Check to make sure the URLs hostname is in the whitelist. Wildcards - * are handled by repeatedly checking the hash for a match after removing - * each part of the hostname until a match is found. If a match is found - * and it's value is set to "glob" the match will be accepted. - */ - if(apr_uri_parse(d->pool, d->image_url, &uri) != APR_SUCCESS) { - return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); - } + // Verify client id. + if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); + } - char *filename = strrchr(uri.path, '/'); - if (!filename || !uri.hostname) { - return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); - } + if(d->client_config && d->client_config->no_image_url) { + d->no_image_url = d->client_config->no_image_url; + } - if (*filename == '/') { - d->filename = ++filename; - } + // Verify allowlist (dims3 only). + if (d->use_secret_key != 0 && !verify_dims3_allowlist(d)) { + return dims_cleanup(d, "Source image is being served from an non-allowed host.", DIMS_HOSTNAME_NOT_IN_WHITELIST); + } - hostname = uri.hostname; - if ( d->use_secret_key == 1 ) { - done = found = 1; - } - while(!done) { - char *value = (char *) apr_table_get(d->config->whitelist, hostname); - if(value && strcmp(value, state) == 0) { - done = found = 1; - } else { - hostname = strstr(hostname, "."); - if(!hostname) { - done = 1; - } else { - hostname++; - } - state = "glob"; - } - } + // Verify signature (dims4 only). + if (d->use_secret_key == 1 && !verify_dims4_signature(d)) { + return dims_cleanup(d, "Signature is invalid.", DIMS_BAD_CLIENT); + } - if(found) { - fetch_url = d->image_url; - } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "Requested URL has hostname that is not in the " - "whitelist. (%s)", uri.hostname); - return dims_cleanup(d, NULL, DIMS_HOSTNAME_NOT_IN_WHITELIST); - } + d->request_hash = ap_md5(d->pool, (unsigned char *) apr_pstrcat(d->pool, d->client_id, d->commands, d->image_url, NULL)); + + dims_set_optimal_geometry(d); - /* Fetch the image into a buffer. */ - if(fetch_url && dims_fetch_remote_image(d, fetch_url) != 0) { - /* If image failed to download replace it with - * the NOIMAGE image. - */ - if(dims_fetch_remote_image(d, NULL) != 0) { - return DECLINED; - } - d->use_no_image = 1; + // Download image. + if(dims_fetch_remote_image(d, d->image_url) != 0) { + // If image failed to download replace it with the NOIMAGE image. + if(dims_fetch_remote_image(d, NULL) != 0) { + return DECLINED; } - return dims_process_image(d); + d->use_no_image = 1; } - return dims_cleanup(d, NULL, DIMS_FAILURE); + // Execute Imagemagick commands. + dims_process_image(d); + + // Serve the image. + dims_send_image(d); + + return DIMS_SUCCESS; } int diff --git a/src/request.h b/src/request.h index 4bcafdb..38c2f43 100644 --- a/src/request.h +++ b/src/request.h @@ -31,6 +31,9 @@ struct dims_request_rec { /* The unparsed commands (resize, crop, etc). */ char *unparsed_commands; + /* The parsed commands with the signature and expiration timestamp removed. */ + char *commands; + /* The original image size in bytes. */ long original_image_size; @@ -67,6 +70,8 @@ struct dims_request_rec { /* Use a whitelist, or use a secret key passed on the URI */ int use_secret_key; + char *signature; + char *expiration; /* Should Content-Disposition header bet set. */ int send_content_disposition; From 7237a2cf47bebd29c9968eda7eb56301ee8950b5 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 11:10:40 -0500 Subject: [PATCH 12/69] Set PKG_CONFIG_PATH in devcontainer --- .devcontainer/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f275cba..439e00a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -22,6 +22,7 @@ ENV DIMS_SECRET="" ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV DIMS_WHITELIST="*.com *.org *.net *.io" +ENV PKG_CONFIG_PATH=/usr/local/imagemagick/lib/pkgconfig RUN apt-get -y update && \ apt-get install -y --no-install-recommends \ From 18b113cbb1b06283161ad37357c9fd65866b61d5 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 11:10:49 -0500 Subject: [PATCH 13/69] Add cpptools extension to devcontainer --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0f494b0..947fc89 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,6 +13,7 @@ "vscode": { "extensions": [ "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", "vscodevim.vim" ] } From a819f26c95ce982dcc49ce9dc45ba0c975ba7f43 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 11:23:17 -0500 Subject: [PATCH 14/69] Set includePath for VS Code --- .vscode/c_cpp_properties.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .vscode/c_cpp_properties.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c4e7420 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${default}", + "/usr/local/imagemagick/include/ImageMagick-7/", + "/usr/local/apache2/include", + "/usr/include/apr-1.0" + ], + "defines": [], + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-arm64" + } + ], + "version": 4 +} \ No newline at end of file From 0256a7fbe5ff5738beaf214e5ea176403ded5c41 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 11:25:18 -0500 Subject: [PATCH 15/69] Set DIMS_SECRET to non-empty value --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 439e00a..ae616f7 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -18,7 +18,7 @@ ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true ENV DIMS_SOURCE_CACHE=604800 ENV DIMS_MAX_SOURCE_CACHE=604800 -ENV DIMS_SECRET="" +ENV DIMS_SECRET="devmode" ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV DIMS_WHITELIST="*.com *.org *.net *.io" From bc327606c4db6e55ff5bb686455c7efda754352a Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 11:44:42 -0500 Subject: [PATCH 16/69] Set IntelliSense compiler --- .vscode/settings.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ad55af5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "*.templ": "templ", + "mod_dims.h": "c" + }, + "C_Cpp.default.compilerPath": "/usr/bin/gcc" +} \ No newline at end of file From 4054541e44f74829da48d534e0b5663abafd16ec Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sun, 10 Nov 2024 12:41:46 -0500 Subject: [PATCH 17/69] Separate out dims3/dims4 request logic Also starting the process of cleaning up error handling with a goal of centralizing it in the handlers. --- .vscode/settings.json | 3 +- src/Makefile.am | 1 + src/handler.c | 315 +++++++++++++++++++----------------------- src/mod_dims.c | 57 ++++---- src/mod_dims.h | 3 +- src/request.h | 1 - src/status.c | 64 +++++++++ src/status.h | 9 ++ 8 files changed, 244 insertions(+), 209 deletions(-) create mode 100644 src/status.c create mode 100644 src/status.h diff --git a/.vscode/settings.json b/.vscode/settings.json index ad55af5..0515ca4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { "files.associations": { "*.templ": "templ", - "mod_dims.h": "c" + "mod_dims.h": "c", + "scoreboard.h": "c" }, "C_Cpp.default.compilerPath": "/usr/bin/gcc" } \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index 31fe592..eb822d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ libmod_dims_la_SOURCES = \ mod_dims.c mod_dims.h \ mod_dims_ops.c mod_dims_ops.h \ module.c module.h \ + status.c status.h \ request.h libmod_dims_la_LDFLAGS = -module -avoid-version $(MagickCore_LIBS) $(MagickWand_LIBS) $(libcurl_LIBS) ${MODULE_LDFLAGS} libmod_dims_la_CFLAGS = -std=c99 -D_LARGEFILE64_SOURCE $(MagickCore_CFLAGS) $(MagickWand_CFLAGS) $(libcurl_CFLAGS) ${MODULE_CFLAGS} diff --git a/src/handler.c b/src/handler.c index c4c8ba6..33d6900 100644 --- a/src/handler.c +++ b/src/handler.c @@ -1,7 +1,9 @@ #include +#include #include #include +#include #include #include "mod_dims.h" @@ -10,27 +12,7 @@ #include "request.h" #include "module.h" #include "encryption.h" - -static void show_time(request_rec *r, apr_interval_time_t tsecs) -{ - int days, hrs, mins, secs; - - secs = (int)(tsecs % 60); - tsecs /= 60; - mins = (int)(tsecs % 60); - tsecs /= 60; - hrs = (int)(tsecs % 24); - days = (int)(tsecs / 24); - - ap_rprintf(r, "Uptime: "); - - if (days) ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); - if (hrs) ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); - if (mins) ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); - if (secs) ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); - - ap_rprintf(r, "\n"); -} +#include "status.h" static dims_request_rec * dims_create_request(request_rec *r) @@ -57,10 +39,123 @@ dims_create_request(request_rec *r) request->start_time = apr_time_now(); request->download_time = 0; request->imagemagick_time = 0; - request->use_secret_key=0; request->optimize_resize = config->optimize_resize; request->send_content_disposition = 0; request->content_disposition_filename = NULL; + + char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); + request->unparsed_commands = unparsed_commands; + + request->client_id = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); + request->signature = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); + request->expiration = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); + request->commands = apr_pstrdup(request->pool, unparsed_commands); + char *s = request->commands; + while (*s) { + if (*s == ' ') { + *s = '+'; + } + + s++; + } + + /* Check first if URL is passed as a query parameter. */ + char *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; + if(r->args) { + const size_t args_len = strlen(r->args) + 1; + char *args = apr_pstrndup(request->r->pool, request->r->args, args_len); + char *token; + char *strtokstate; + token = apr_strtok(args, "&", &strtokstate); + while (token) { + if(strncmp(token, "url=", 4) == 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "ARG: %s", token); + fixed_url = apr_pstrdup(r->pool, token + 4); + ap_unescape_url(fixed_url); + + if (strcmp(fixed_url, "") == 0) { + //return dims_cleanup(request, NULL, DIMS_BAD_URL); + return NULL; + } + } else if (strncmp(token, "download=1", 10) == 0) { + request->send_content_disposition = 1; + + } else if (strncmp(token, "eurl=", 4) == 0) { + eurl = apr_pstrdup(r->pool, token + 5); + + // Hash secret via SHA-1. + unsigned char *secret = (unsigned char *) request->client_config->secret_key; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1(secret, strlen((char *) secret), hash); + + // Convert to hex. + char hex[SHA_DIGEST_LENGTH * 2 + 1]; + if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { + //return dims_cleanup(request, "URL Decryption Failed", DIMS_FAILURE); + return NULL; + } + + // Use first 16 bytes. + unsigned char key[17]; + strncpy((char *) key, hex, 16); + key[16] = '\0'; + + // Force key to uppercase + unsigned char *s = key; + while (*s) { *s = toupper(*s); s++; } + + if (request->config->encryption_algorithm != NULL && + strncmp((char *)request->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) { + + fixed_url = aes_128_gcm_decrypt(r, key, eurl); + } else { + //Default is AES/ECB/PKCS5Padding + unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl)); + int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); + fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); + } + if (fixed_url == NULL) { + //return dims_cleanup(request, "URL Decryption Failed", DIMS_FAILURE); + return NULL; + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "Decrypted URL: %s", fixed_url); + break; + + } else if (strncmp(token, "optimizeResize=", 4) == 0) { + request->optimize_resize = atof(token + 15); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "Overriding optimize resize: %f", request->optimize_resize); + } + token = apr_strtok(NULL, "&", &strtokstate); + } + } + + // Convert '+' in the fixed_url to ' '. + char *image_url = apr_pstrdup(request->r->pool, fixed_url); + s = image_url; + while (*s) { + if (*s == '+') { + *s = ' '; + } + + s++; + } + + request->image_url = image_url; + request->unparsed_commands = commands + 6; + request->request_hash = ap_md5(request->pool, + apr_pstrcat(request->pool, request->client_id, request->commands, request->image_url, NULL)); + + /* Calculate image filename for use with content disposition. */ + apr_uri_t uri; + if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { + if (!uri.path) { + //return dims_cleanup(request, NULL, DIMS_BAD_URL); + return NULL; + } + + const char *path = apr_filepath_name_get(uri.path); + request->content_disposition_filename = apr_pstrdup(request->r->pool, path); + } return request; } @@ -84,7 +179,20 @@ dims_create_request(request_rec *r) apr_status_t dims_handler(request_rec *r) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handler %s : %s", r->handler, r->uri); + + if(strcmp(r->handler, "dims-status") == 0) { + return status_handler(r); + } + + if (!(strcmp(r->handler, "dims3") == 0 || strcmp(r->handler, "dims4") == 0)) { + return DECLINED; + } + dims_request_rec *d = dims_create_request(r); + if (d == NULL) { + return HTTP_INTERNAL_SERVER_ERROR; + } /* Set initial notes to be logged by mod_log_config. */ apr_table_setn(r->notes, "DIMS_STATUS", "0"); @@ -92,162 +200,19 @@ dims_handler(request_rec *r) apr_table_setn(r->notes, "DIMS_DL_TIME", "-"); apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Handler %s : %s", r->handler, r->uri); - - if ((strcmp(r->handler, "dims3") == 0) || - (r->uri && strncmp(r->uri, "/dims3/", 7) == 0) || (strcmp(r->handler, "dims4") == 0 )) { - - /* Handle new-style DIMS parameters. */ - char *p, *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; - if (( strcmp( r->handler,"dims4") == 0)) { - d->use_secret_key = 1; - } - - char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); - d->unparsed_commands = unparsed_commands; - - d->client_id = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); - d->signature = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); - d->expiration = ap_getword(d->pool, (const char **) &unparsed_commands, '/'); - d->commands = apr_pstrdup(d->pool, unparsed_commands); - char *s = d->commands; - while (*s) { - if (*s == ' ') { - *s = '+'; - } - - s++; - } - - if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); - } - - /* Check first if URL is passed as a query parameter. */ - if(r->args) { - const size_t args_len = strlen(r->args) + 1; - char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); - char *token; - char *strtokstate; - token = apr_strtok(args, "&", &strtokstate); - while (token) { - if(strncmp(token, "url=", 4) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "ARG: %s", token); - fixed_url = apr_pstrdup(r->pool, token + 4); - ap_unescape_url(fixed_url); - - if (strcmp(fixed_url, "") == 0) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - } else if (strncmp(token, "download=1", 10) == 0) { - d->send_content_disposition = 1; - - } else if (strncmp(token, "eurl=", 4) == 0) { - eurl = apr_pstrdup(r->pool, token + 5); - - // Hash secret via SHA-1. - unsigned char *secret = (unsigned char *) d->client_config->secret_key; - unsigned char hash[SHA_DIGEST_LENGTH]; - SHA1(secret, strlen((char *) secret), hash); - - // Convert to hex. - char hex[SHA_DIGEST_LENGTH * 2 + 1]; - if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { - return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); - } - - // Use first 16 bytes. - unsigned char key[17]; - strncpy((char *) key, hex, 16); - key[16] = '\0'; - - // Force key to uppercase - unsigned char *s = key; - while (*s) { *s = toupper(*s); s++; } - - if (d->config->encryption_algorithm != NULL && - strncmp((char *)d->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) { - - fixed_url = aes_128_gcm_decrypt(r, key, eurl); - } else { - //Default is AES/ECB/PKCS5Padding - unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl)); - int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); - fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); - } - if (fixed_url == NULL) { - return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE); - } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Decrypted URL: %s", fixed_url); - break; - - } else if (strncmp(token, "optimizeResize=", 4) == 0) { - d->optimize_resize = atof(token + 15); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Overriding optimize resize: %f", d->optimize_resize); - } - token = apr_strtok(NULL, "&", &strtokstate); - } - } - - // Convert '+' in the fixed_url to ' '. - char *image_url = apr_pstrdup(d->r->pool, fixed_url); - s = image_url; - while (*s) { - if (*s == '+') { - *s = ' '; - } - - s++; - } - - d->image_url = image_url; - d->unparsed_commands = commands + 6; - - /* Calculate image filename for use with content disposition. */ - apr_uri_t uri; - if (apr_uri_parse(r->pool, d->image_url, &uri) == APR_SUCCESS) { - if (!uri.path) { - return dims_cleanup(d, NULL, DIMS_BAD_URL); - } - - const char *path = apr_filepath_name_get(uri.path); - d->content_disposition_filename = apr_pstrdup(d->r->pool, path); - } - - return dims_handle_request(d); - } else if(strcmp(r->handler, "dims-status") == 0) { - apr_time_t uptime; - - ap_set_content_type(r, "text/plain"); - ap_rvputs(r, "ALIVE\n\n", NULL); - - uptime = (apr_uint32_t) apr_time_sec(apr_time_now() - - ap_scoreboard_image->global->restart_time); - - show_time(r, uptime); - - ap_rprintf(r, "Restart time: %s\n", - ap_ht_time(r->pool, - ap_scoreboard_image->global->restart_time, - "%A, %d-%b-%Y %H:%M:%S %Z", 0)); + if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); + } - ap_rprintf(r, "\nmod_dims version: %s (%s)\n", MODULE_VERSION, MODULE_RELEASE); - ap_rprintf(r, "ImageMagick version: %s\n", GetMagickVersion(NULL)); - ap_rprintf(r, "libcurl version: %s\n", curl_version()); + if(d->client_config && d->client_config->no_image_url) { + d->no_image_url = d->client_config->no_image_url; + } - ap_rprintf(r, "\nDetails\n-------\n"); - - ap_rprintf(r, "Successful requests: %d\n", - apr_atomic_read32(&stats->success_count)); - ap_rprintf(r, "Failed requests: %d\n\n", - apr_atomic_read32(&stats->failure_count)); - ap_rprintf(r, "Download timeouts: %d\n", - apr_atomic_read32(&stats->download_timeout_count)); - ap_rprintf(r, "Imagemagick Timeouts: %d\n", - apr_atomic_read32(&stats->imagemagick_timeout_count)); - ap_rflush(r); - return OK; + if ((strcmp(r->handler, "dims3") == 0)) { + return dims_handle_dims3(d); + } else if (strcmp(r->handler, "dims4") == 0) { + return dims_handle_dims4(d); } return DECLINED; diff --git a/src/mod_dims.c b/src/mod_dims.c index 3ac16a8..2646b19 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -355,7 +355,7 @@ dims_send_image(dims_request_rec *d) (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->etag, NULL)); } else if (d->last_modified) { etag = ap_md5(d->pool, - (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->last_modified, NULL)); + (unsigned char *)apr_pstrcat(d->pool, d->request_hash, d->last_modified, NULL)); } if (etag) { @@ -791,51 +791,46 @@ verify_dims3_allowlist(dims_request_rec *d) { return found; } -apr_status_t +static apr_status_t dims_handle_request(dims_request_rec *d) { - apr_time_t now_time = apr_time_now(); + // Download image. + int status; + if ((status = dims_fetch_remote_image(d, d->image_url)) != 0) { + return status; + } - // Verify client id. - if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); + // Execute Imagemagick commands. + if ((status = dims_process_image(d)) != 0) { + return status; } - if(d->client_config && d->client_config->no_image_url) { - d->no_image_url = d->client_config->no_image_url; + // Serve the image. + if ((status = dims_send_image(d)) != 0) { + return status; } +} +apr_status_t +dims_handle_dims3(dims_request_rec *d) +{ // Verify allowlist (dims3 only). - if (d->use_secret_key != 0 && !verify_dims3_allowlist(d)) { + if (!verify_dims3_allowlist(d)) { return dims_cleanup(d, "Source image is being served from an non-allowed host.", DIMS_HOSTNAME_NOT_IN_WHITELIST); } + return dims_handle_request(d); +} + +apr_status_t +dims_handle_dims4(dims_request_rec *d) +{ // Verify signature (dims4 only). - if (d->use_secret_key == 1 && !verify_dims4_signature(d)) { + if (!verify_dims4_signature(d)) { return dims_cleanup(d, "Signature is invalid.", DIMS_BAD_CLIENT); } - d->request_hash = ap_md5(d->pool, (unsigned char *) apr_pstrcat(d->pool, d->client_id, d->commands, d->image_url, NULL)); - - dims_set_optimal_geometry(d); - - // Download image. - if(dims_fetch_remote_image(d, d->image_url) != 0) { - // If image failed to download replace it with the NOIMAGE image. - if(dims_fetch_remote_image(d, NULL) != 0) { - return DECLINED; - } - - d->use_no_image = 1; - } - - // Execute Imagemagick commands. - dims_process_image(d); - - // Serve the image. - dims_send_image(d); - - return DIMS_SUCCESS; + return dims_handle_request(d); } int diff --git a/src/mod_dims.h b/src/mod_dims.h index d1f1b8b..b026168 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -65,7 +65,8 @@ int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) void dims_child_init(apr_pool_t *p, server_rec *s); void dims_register_hooks(apr_pool_t *p); -apr_status_t dims_handle_request(dims_request_rec *d); +apr_status_t dims_handle_dims3(dims_request_rec *d); +apr_status_t dims_handle_dims4(dims_request_rec *d); apr_status_t dims_cleanup(dims_request_rec *d, char *err_msg, int status); typedef struct { diff --git a/src/request.h b/src/request.h index 38c2f43..1e71d2e 100644 --- a/src/request.h +++ b/src/request.h @@ -69,7 +69,6 @@ struct dims_request_rec { apr_time_t imagemagick_time; /* Use a whitelist, or use a secret key passed on the URI */ - int use_secret_key; char *signature; char *expiration; diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..b9a6aeb --- /dev/null +++ b/src/status.c @@ -0,0 +1,64 @@ + +#include +#include +#include +#include +#include "mod_dims.h" + +static void show_time(request_rec *r, apr_interval_time_t tsecs) +{ + int days, hrs, mins, secs; + + secs = (int)(tsecs % 60); + tsecs /= 60; + mins = (int)(tsecs % 60); + tsecs /= 60; + hrs = (int)(tsecs % 24); + days = (int)(tsecs / 24); + + ap_rprintf(r, "Uptime: "); + + if (days) ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); + if (hrs) ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); + if (mins) ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); + if (secs) ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); + + ap_rprintf(r, "\n"); +} + +apr_status_t +status_handler(request_rec *r) { + apr_time_t uptime; + + ap_set_content_type(r, "text/plain"); + ap_rvputs(r, "ALIVE\n\n", NULL); + + uptime = (apr_uint32_t) apr_time_sec(apr_time_now() - + ap_scoreboard_image->global->restart_time); + + show_time(r, uptime); + + ap_rprintf(r, "Restart time: %s\n", + ap_ht_time(r->pool, + ap_scoreboard_image->global->restart_time, + "%A, %d-%b-%Y %H:%M:%S %Z", 0)); + + ap_rprintf(r, "\nmod_dims version: %s (%s)\n", MODULE_VERSION, MODULE_RELEASE); + ap_rprintf(r, "ImageMagick version: %s\n", GetMagickVersion(NULL)); + ap_rprintf(r, "libcurl version: %s\n", curl_version()); + + ap_rprintf(r, "\nDetails\n-------\n"); + + ap_rprintf(r, "Successful requests: %d\n", + apr_atomic_read32(&stats->success_count)); + ap_rprintf(r, "Failed requests: %d\n\n", + apr_atomic_read32(&stats->failure_count)); + ap_rprintf(r, "Download timeouts: %d\n", + apr_atomic_read32(&stats->download_timeout_count)); + ap_rprintf(r, "Imagemagick Timeouts: %d\n", + apr_atomic_read32(&stats->imagemagick_timeout_count)); + + ap_rflush(r); + + return OK; +} \ No newline at end of file diff --git a/src/status.h b/src/status.h new file mode 100644 index 0000000..0754504 --- /dev/null +++ b/src/status.h @@ -0,0 +1,9 @@ +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include +#include + +apr_status_t status_handler(request_rec *r); + +#endif \ No newline at end of file From 130ab826c1d485ed78a8fc786638d1c852d651f2 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Mon, 11 Nov 2024 08:14:12 -0500 Subject: [PATCH 18/69] Use Zig for build system rather than autoconf --- .gitignore | 7 +- AUTHORS | 0 ChangeLog | 13 ---- Makefile.am | 2 - NEWS | 0 autorun.sh | 46 ----------- build.zig | 39 ++++++++++ configure.ac | 48 ------------ m4/apache.m4 | 199 ------------------------------------------------ src/Makefile.am | 18 ----- 10 files changed, 45 insertions(+), 327 deletions(-) delete mode 100644 AUTHORS delete mode 100644 ChangeLog delete mode 100644 Makefile.am delete mode 100644 NEWS delete mode 100755 autorun.sh create mode 100644 build.zig delete mode 100644 configure.ac delete mode 100644 m4/apache.m4 delete mode 100644 src/Makefile.am diff --git a/.gitignore b/.gitignore index 3298f33..cd68f67 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,9 @@ stamp-h1 *~ *.dylib -*.so \ No newline at end of file +*.so + +.zig-out +.zig-cache +zig-out +zig-cache \ No newline at end of file diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index e69de29..0000000 diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 700c6cd..0000000 --- a/ChangeLog +++ /dev/null @@ -1,13 +0,0 @@ - -2012-11-14 Jeremy Collins - - - Added new image manipulation commands: brightness, flip/flop, sepia, - grayscale, rotate and invert. - - - Improved autorun.sh to work better on OS X with brew. - -2012-11-08 Jeremy Collins - - - Convert images with a CMYK colorspace to RGB. - - diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 0a7b929..0000000 --- a/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src diff --git a/NEWS b/NEWS deleted file mode 100644 index e69de29..0000000 diff --git a/autorun.sh b/autorun.sh deleted file mode 100755 index b834187..0000000 --- a/autorun.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# -# setup/re-init the autoconf files on a raw checkout -# minimalistic version -# - -fail() { - echo "FAIL: $@" >&2 - exit 1 -} - -# check location - -if [ ! -f ./configure.ac ]; then - fail "$0 needs to run fromt the top level directory where configure.ac resides." -fi - -# check tools we need - -AUTOCONF="${AUTOCONF:-autoconf}" -AUTORECONF="${AUTORECONF:-autoreconf}" -AUTOMAKE="${AUTOMAKE:-automake}" - -for tool in "$AUTOCONF" "$AUTORECONF" "$AUTOMAKE"; do - type "$tool" 2>&1 >/dev/null - if test $? -ne 0; then - fail "need ${tool} installed." - fi -done - -for i in .configured .deps compile aclocal.m4 autom4te.cache \ - autoscan.log config.guess config.status config.sub \ - config.h config.h.in config.h.in~ configure configure.scan \ - depcomp install-sh libtool ltmain.sh missing stamp-h1 \ - Makefile.in Makefile \ - src/Makefile.in src/Makefile \ - test/Makefile.in test/Makefile \ - ; do - test -z "$i" || rm -rf "$i" -done - -"$AUTORECONF" -i || exit $? -"$AUTOMAKE" || exit $? -"$AUTOCONF" || exit $?01 - -./configure $@ \ No newline at end of file diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..c710118 --- /dev/null +++ b/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const libmod_dims = b.addSharedLibrary(.{ + .name = "mod_dims", + .target = target, + .optimize = optimize, + .version = .{ .major = 4, .minor = 0, .patch = 0 }, + }); + + libmod_dims.linkSystemLibrary("curl"); + libmod_dims.linkSystemLibrary("apr-1"); + libmod_dims.linkSystemLibrary("MagickWand"); + libmod_dims.addCSourceFiles(.{ + .files = &.{ + "src/configuration.c", + "src/encryption.c", + "src/handler.c", + "src/mod_dims_ops.c", + "src/mod_dims.c", + "src/module.c", + "src/status.c", + }, + .flags = &.{ + "-I/usr/local/apache2/include/", + "-Wall", + "-W", + "-Wstrict-prototypes", + "-Wwrite-strings", + "-Wno-missing-field-initializers", + }, + } + ); + + b.installArtifact(libmod_dims); +} diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 0f85b05..0000000 --- a/configure.ac +++ /dev/null @@ -1,48 +0,0 @@ -AC_PREREQ(2.60) -AC_INIT(mod_dims, 4.0.0alpha, [jeremy.collins@beetlebug.org]) -AM_INIT_AUTOMAKE([no-define]) -AC_CONFIG_SRCDIR([src/mod_dims.c]) - -# Checks for programs. -AC_PROG_CC -AC_PROG_INSTALL -AC_PROG_LIBTOOL - -# Check for ImageMagick -PKG_CHECK_MODULES(MagickCore, MagickCore) -PKG_CHECK_MODULES(MagickWand, MagickWand) -PKG_CHECK_MODULES(libcurl, libcurl) - -AC_CONFIG_MACRO_DIRS([m4]) -AC_CONFIG_HEADERS([src/config.h]) - -AP_VERSION=2.4.0 -AP_CHECK_APACHE([$AP_VERSION], [ - LIBTOOL="`$APR_CONFIG --apr-libtool`" - AC_SUBST([LIBTOOL]) - - MODULE_CFLAGS="$AP_CFLAGS" - AC_SUBST([MODULE_CFLAGS]) - - MODULE_LDFLAGS="`$APR_CONFIG --link-libtool` `$APU_CONFIG --link-libtool`" - AC_SUBST([MODULE_LDFLAGS]) - - BIN_LDFLAGS="`$APR_CONFIG --link-libtool` `$APU_CONFIG --link-libtool` `$APR_CONFIG --ldflags --libs` `$APU_CONFIG --ldflags --libs`" - AC_SUBST([BIN_LDFLAGS]) - - prefix="$AP_PREFIX" -], AC_MSG_ERROR([*** Apache version $AP_VERSION not found!])) - -# Checks for header files. -AC_CHECK_HEADERS([stdlib.h sys/time.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_TYPE_SIZE_T - -# Checks for library functions. -AC_FUNC_REALLOC -AC_CHECK_FUNCS([memset sqrt strstr]) - -AC_CONFIG_FILES([Makefile src/Makefile]) -AC_OUTPUT - diff --git a/m4/apache.m4 b/m4/apache.m4 deleted file mode 100644 index de066f5..0000000 --- a/m4/apache.m4 +++ /dev/null @@ -1,199 +0,0 @@ -dnl -------------------------------------------------------- -*- autoconf -*- -dnl Licensed to the Apache Software Foundation (ASF) under one or more -dnl contributor license agreements. See the NOTICE file distributed with -dnl this work for additional information regarding copyright ownership. -dnl The ASF licenses this file to You under the Apache License, Version 2.0 -dnl (the "License"); you may not use this file except in compliance with -dnl the License. You may obtain a copy of the License at -dnl -dnl http://www.apache.org/licenses/LICENSE-2.0 -dnl -dnl Unless required by applicable law or agreed to in writing, software -dnl distributed under the License is distributed on an "AS IS" BASIS, -dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -dnl See the License for the specific language governing permissions and -dnl limitations under the License. - -dnl -dnl AP_TEST_APACHE_VERSION([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) -dnl - -AC_DEFUN([AP_TEST_APACHE_VERSION], [ - min_apache_version="$1" - no_apache="" - ac_save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $AP_CFLAGS $APR_INCLUDES $APU_INCLUDES" - AC_TRY_RUN([ -#include -#include -#include -#include "httpd.h" - -#ifndef AP_SERVER_BASEREVISION - #define AP_SERVER_BASEREVISION SERVER_BASEREVISION -#endif - -char* my_strdup (char *str) -{ - char *new_str; - - if (str) { - new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); - strcpy (new_str, str); - } else - new_str = NULL; - - return new_str; -} - -int main (int argc, char *argv[]) -{ - int major1, minor1, micro1; - int major2, minor2, micro2; - char *tmp_version; - - { FILE *fp = fopen("conf.apachetest", "a"); if ( fp ) fclose(fp); } - - // TODO: Support abbreviated versions, e.g. 2.2 instead of 2.2.0 - tmp_version = my_strdup("$min_apache_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major1, &minor1, µ1) != 3) { - printf("%s, bad version string\n", "$min_apache_version"); - exit(1); - } - tmp_version = my_strdup(AP_SERVER_BASEREVISION); - if (sscanf(tmp_version, "%d.%d.%d", &major2, &minor2, µ2) != 3) { - printf("%s, bad version string\n", AP_SERVER_BASEREVISION); - exit(1); - } - - if (major2 == major1 && - (minor2 > minor1 || - (minor2 == minor1 && micro2 >= micro1))) { - exit(0); - } else - exit(1); -} - ], [], [no_apache=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) - CFLAGS="$ac_save_CFLAGS" - - if test "x$no_apache" = x ; then - ifelse([$2], [], [:], [$2]) - else - if test -f conf.apachetest; then - : - else - AC_MSG_WARN([*** Could not run Apache test program, checking why...]) - CFLAGS="$CFLAGS $AP_CFLAGS $APR_INCLUDES $APU_INCLUDES" - AC_TRY_LINK([ -#include -#include "httpd.h" - -int main(int argc, char *argv[]) -{ return 0; } -#undef main -#define main K_and_R_C_main - ], [ return 0; ], - [AC_MSG_ERROR([*** The test program compiled, but failed to run. Check config.log])], - [AC_MSG_ERROR([*** The test program failed to compile or link. Check config.log])]) - CFLAGS="$ac_save_CFLAGS" - fi - ifelse([$3], [], :, [$3]) - fi - rm -f conf.apachetest -]) - -dnl -dnl AP_CHECK_APACHE([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) -dnl - -AC_DEFUN([AP_CHECK_APACHE], [ - AC_ARG_WITH([apxs], - [AC_HELP_STRING([--with-apxs=PATH], [Path to apxs])], - [apxs_prefix="$withval"], - [apxs_prefix="/usr"]) - - AC_ARG_ENABLE([apachetest], - [AC_HELP_STRING([--disable-apachetest], [Do not try to compile and run Apache version test program])], - [], - [enable_apachetest=yes]) - - # Find apxs - if test -x $apxs_prefix -a ! -d $apxs_prefix; then - APXS_BIN=$apxs_prefix - else - test_paths="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/local/apache2/bin" - - if test -d $apxs_prefix; then - test_paths="$apxs_prefix:$apxs_prefix/bin:$apxs_prefix/sbin:$test_paths" - apxs_prefix="apxs" - fi - - AC_PATH_PROG([APXS_BIN], [$apxs_prefix], [no], [$test_paths]) - fi - - if test "$APXS_BIN" = "no"; then - AC_MSG_ERROR([*** The apxs binary installed by Apache could not be found!]) - AC_MSG_ERROR([*** Use the --with-apxs option with the full path to apxs]) - else - # Set AP_ variables from apxs - - AP_INCLUDEDIR="`$APXS_BIN -q INCLUDEDIR 2>/dev/null`" - AP_INCLUDES="-I$AP_INCLUDEDIR" - - AP_PREFIX="`$APXS_BIN -q prefix 2>/dev/null`" - - AP_BINDIR="`$APXS_BIN -q bindir 2>/dev/null`" - AP_SBINDIR="`$APXS_BIN -q sbindir 2>/dev/null`" - - APXS_CFLAGS="" - for flag in CFLAGS EXTRA_CFLAGS NOTEST_CFLAGS; do - APXS_CFLAGS="$APXS_CFLAGS `$APXS_BIN -q $flag 2>/dev/null`" - done - - AP_CFLAGS="$APXS_CFLAGS $AP_INCLUDES" - - AP_LIBEXECDIR=`$APXS_BIN -q LIBEXECDIR 2>/dev/null` - - # Set APR_ variables from apr-config - APR_CONFIG="`$APXS_BIN -q APR_BINDIR 2>/dev/null`/apr-1-config" - if test ! -x $APR_CONFIG; then - APR_CONFIG="`$APXS_BIN -q APR_BINDIR 2>/dev/null`/apr-config" - fi - APR_INCLUDES=`$APR_CONFIG --includes 2>/dev/null` - APR_VERSION=`$APR_CONFIG --version 2>/dev/null` - - # Set APU_ variables from apu-config - APU_CONFIG="`$APXS_BIN -q APU_BINDIR 2>/dev/null`/apu-1-config" - if test ! -x $APU_CONFIG; then - APU_CONFIG="`$APXS_BIN -q APU_BINDIR 2>/dev/null`/apu-config" - fi - APU_INCLUDES=`$APU_CONFIG --includes 2>/dev/null` - APU_VERSION=`$APU_CONFIG --version 2>/dev/null` - - min_apache_version=ifelse([$1], [], [no], [$1]) - if test "x$enable_apachetest" = "xyes" -a "$min_apache_version" != "no"; then - AC_MSG_CHECKING([for Apache 2.0 version >= $min_apache_version]) - AP_TEST_APACHE_VERSION([$min_apache_version], - AC_MSG_RESULT([yes]) - AP_CFLAGS="$AP_CFLAGS $APU_INCLUDES $APR_INCLUDES" - AP_CPPFLAGS="$AP_CPPFLAGS $APU_INCLUDES $APR_INCLUDES" - ifelse([$2], [], [], [$2]), - AC_MSG_RESULT([no]) - ifelse([$3], [], [], [$3]) - ) - fi - AC_SUBST(AP_INCLUDEDIR) - AC_SUBST(AP_INCLUDES) - AC_SUBST(AP_PREFIX) - AC_SUBST(AP_BINDIR) - AC_SUBST(AP_SBINDIR) - AC_SUBST(AP_CFLAGS) - AC_SUBST(AP_LIBEXECDIR) - AC_SUBST(APR_CONFIG) - AC_SUBST(APR_INCLUDES) - AC_SUBST(APU_CONFIG) - AC_SUBST(APU_INCLUDES) - AC_SUBST(APXS_BIN) - AC_SUBST(APXS_CFLAGS) - fi -]) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index eb822d8..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -moddir = ${AP_LIBEXECDIR} -lib_LTLIBRARIES = libmod_dims.la - -libmod_dims_la_SOURCES = \ - curl.c curl.h \ - configuration.c configuration.h \ - encryption.c encryption.h \ - handler.c handler.h \ - mod_dims.c mod_dims.h \ - mod_dims_ops.c mod_dims_ops.h \ - module.c module.h \ - status.c status.h \ - request.h -libmod_dims_la_LDFLAGS = -module -avoid-version $(MagickCore_LIBS) $(MagickWand_LIBS) $(libcurl_LIBS) ${MODULE_LDFLAGS} -libmod_dims_la_CFLAGS = -std=c99 -D_LARGEFILE64_SOURCE $(MagickCore_CFLAGS) $(MagickWand_CFLAGS) $(libcurl_CFLAGS) ${MODULE_CFLAGS} - -install: libmod_dims.la - $(APXS_BIN) -i -a -n dims libmod_dims.la From 1b1f0067ff4751e05d56cc14ae0fff0ab4300b86 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Mon, 11 Nov 2024 11:19:47 -0500 Subject: [PATCH 19/69] Experimental zig build --- .devcontainer/Dockerfile | 76 +++++------------- README | 0 README.markdown => README.md | 42 ++++++---- build.zig | 18 ++--- docker/.dockerignore | 2 + docker/Dockerfile | 112 +++++++++----------------- docker/Dockerfile.builder | 148 +++++++++++++++++++++++++++++++++++ src/handler.c | 3 +- src/mod_dims.c | 13 --- src/mod_dims_ops.c | 2 - 10 files changed, 245 insertions(+), 171 deletions(-) delete mode 100644 README rename README.markdown => README.md (87%) create mode 100644 docker/.dockerignore create mode 100644 docker/Dockerfile.builder diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ae616f7..ffd76f3 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,12 +1,23 @@ ARG HTTPD_VERSION=2.4.62 -FROM httpd:${HTTPD_VERSION} as build-mod-dims +FROM ghcr.io/beetlebugorg/mod-dims:builder + +RUN apt-get update && \ + apt-get -y install \ + libpangocairo-1.0-0 libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ + libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 \ + liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates pkg-config \ + libapr1-dev libaprutil1-dev libcurl4-openssl-dev libssl-dev && \ + chown -R www-data:www-data /usr/local/apache2 && \ + sed "s|Listen 80|Listen 8000|" /usr/local/apache2/conf/httpd.conf -i && \ + sed "s|^#LoadModule authz_core_module|LoadModule authz_core_module|" /usr/local/apache2/conf/httpd.conf -i && \ + sed "s|^LogLevel warn|LogLevel debug|" /usr/local/apache2/conf/httpd.conf -i && \ + echo "Include conf/extra/dims.conf" >> /usr/local/apache2/conf/httpd.conf -ARG DIMS_VERSION=3.3.30 -ARG IMAGEMAGICK_VERSION=7.1.1-40 -ARG WEBP_VERSION=1.4.0 -ARG TIFF_VERSION=4.7.0 -ARG PREFIX=/usr/local/imagemagick +COPY . /build/mod-dims +WORKDIR /build/mod-dims + +RUN zig build ENV DIMS_DOWNLOAD_TIMEOUT=60000 ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 @@ -17,59 +28,12 @@ ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true ENV DIMS_SOURCE_CACHE=604800 +ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 -ENV DIMS_SECRET="devmode" ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 -ENV DIMS_WHITELIST="*.com *.org *.net *.io" -ENV PKG_CONFIG_PATH=/usr/local/imagemagick/lib/pkgconfig - -RUN apt-get -y update && \ - apt-get install -y --no-install-recommends \ - automake libtool autoconf build-essential \ - git ca-certificates \ - libapr1-dev libaprutil1-dev \ - curl \ - libcurl4-openssl-dev libfreetype6-dev libopenexr-dev libxml2-dev \ - libgif-dev libjpeg62-turbo-dev libpng-dev \ - liblcms2-dev pkg-config libssl-dev libpangocairo-1.0-0 wget - -# WEBP Library -RUN wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-${WEBP_VERSION}.tar.gz && \ - tar xzvf libwebp-${WEBP_VERSION}.tar.gz && \ - cd libwebp-${WEBP_VERSION} && \ - ./configure --prefix=/usr/local/imagemagick && \ - make -j4 && make install - -# TIFF Library -RUN wget http://download.osgeo.org/libtiff/tiff-${TIFF_VERSION}.tar.gz && \ - tar xzvf tiff-${TIFF_VERSION}.tar.gz && \ - cd tiff-${TIFF_VERSION} && \ - ./configure --prefix=$PREFIX --with-webp-include-dir=$PREFIX/include --with-webp-lib-dir=$PREFIX/lib && \ - make -j4 && make install - -# Imagemagick -RUN wget https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ - export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig && \ - tar xvf ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ - cd ImageMagick-${IMAGEMAGICK_VERSION} && \ - ./configure --without-x --with-quantum-depth=8 --prefix=$PREFIX && \ - make -j4 && make install - -RUN apt-get --no-install-recommends install -y apt-transport-https apt-utils \ - automake build-essential ccache cmake ca-certificates curl git \ - gcc g++ libc-ares-dev libc-ares2 libcurl4-openssl-dev libre2-dev \ - libssl-dev m4 make pkg-config tar wget zlib1g-dev - -WORKDIR /build - -RUN apt-get install -y vim && \ - chown -R www-data:www-data /usr/local/apache2 && \ - sed "s|Listen 80|Listen 8000|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^#LoadModule authz_core_module|LoadModule authz_core_module|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^LogLevel warn|LogLevel debug|" /usr/local/apache2/conf/httpd.conf -i && \ - echo "Include conf/extra/dims.conf" >> /usr/local/apache2/conf/httpd.conf +ENV DIMS_WHITELIST="" COPY docker/dims.conf /usr/local/apache2/conf/extra/dims.conf -ENV LC_ALL="C" \ No newline at end of file +EXPOSE 8000 \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index e69de29..0000000 diff --git a/README.markdown b/README.md similarity index 87% rename from README.markdown rename to README.md index 5485143..453a16f 100644 --- a/README.markdown +++ b/README.md @@ -1,42 +1,52 @@ -Dependencies -============ - -* Apache 2.2.x -* Imagemagick 6.6+ -* libcurl 7.18.0+ +mod-dims +======== -Compiling -========= +mod-dims is an HTTP microservice for dynamic image manipulation. It run as an Apache httpd module. -./autorun.sh --with-imagemagick=/path/to/imagemagick --with-apache=/path/to/apache +Dependencies +------------ -The paths provided above are prefix paths used to install those dependencies. If you installed -Imagemagick and Apache (including APR) in /usr/local you would run: +* Apache 2.4.x +* Imagemagick 7.1.x +* libcurl 7.x -./autorun.sh --with-imagemagick=/usr/local --with-apache=/usr/local +Compiling +--------- + +```bash +$ zig build +find zig-out/ +zig-out/ +zig-out/lib +zig-out/lib/libmod_dims.so.4 +zig-out/lib/libmod_dims.so.4.0.0 +zig-out/lib/libmod_dims.so +``` Installation -============ +------------ Add the following to the Apache configuration: +``` LoadModule dims_module modules/mod_dims.so AddHandler dims-local .gif .jpg - - SetHandler dims + + SetHandler dims3 - + SetHandler dims3 SetHandler dims-status +``` This assumes mod_dims.so has been installed in $HTTP_ROOT/modules. diff --git a/build.zig b/build.zig index c710118..1cf72ec 100644 --- a/build.zig +++ b/build.zig @@ -16,6 +16,7 @@ pub fn build(b: *std.Build) void { libmod_dims.linkSystemLibrary("MagickWand"); libmod_dims.addCSourceFiles(.{ .files = &.{ + "src/curl.c", "src/configuration.c", "src/encryption.c", "src/handler.c", @@ -23,17 +24,16 @@ pub fn build(b: *std.Build) void { "src/mod_dims.c", "src/module.c", "src/status.c", - }, + }, .flags = &.{ - "-I/usr/local/apache2/include/", - "-Wall", - "-W", - "-Wstrict-prototypes", - "-Wwrite-strings", - "-Wno-missing-field-initializers", + "-I/usr/local/apache2/include/", + "-Wall", + "-W", + "-Wstrict-prototypes", + "-Wwrite-strings", + "-Wno-missing-field-initializers", }, - } - ); + }); b.installArtifact(libmod_dims); } diff --git a/docker/.dockerignore b/docker/.dockerignore new file mode 100644 index 0000000..c26d4af --- /dev/null +++ b/docker/.dockerignore @@ -0,0 +1,2 @@ +zig-out +zig-cache diff --git a/docker/Dockerfile b/docker/Dockerfile index de2577f..c6dbca0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,72 +1,24 @@ -ARG HTTPD_VERSION=2.4.52 +ARG HTTPD_VERSION=2.4.62 -FROM httpd:${HTTPD_VERSION} as build-mod-dims +FROM ghcr.io/beetlebugorg/mod-dims:builder AS mod-dims -ARG DIMS_VERSION=3.3.26 -ARG IMAGEMAGICK_VERSION=6.9.12-34 -ARG WEBP_VERSION=1.2.1 -ARG TIFF_VERSION=4.3.0 -ARG PREFIX=/usr/local/imagemagick - -WORKDIR /build - -RUN apt-get -y update && \ - apt-get install -y --no-install-recommends \ - automake libtool autoconf build-essential \ - git ca-certificates \ - libapr1-dev libaprutil1-dev \ - curl \ - libcurl4-openssl-dev libfreetype6-dev libopenexr-dev libxml2-dev \ - libgif-dev libjpeg62-turbo-dev libpng-dev \ - liblcms2-dev pkg-config libssl-dev libpangocairo-1.0-0 wget - -# WEBP Library -RUN wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-${WEBP_VERSION}.tar.gz && \ - tar xzvf libwebp-${WEBP_VERSION}.tar.gz && \ - cd libwebp-${WEBP_VERSION} && \ - ./configure --prefix=/usr/local/imagemagick && \ - make -j4 && make install - -# TIFF Library -RUN wget http://download.osgeo.org/libtiff/tiff-${TIFF_VERSION}.tar.gz && \ - tar xzvf tiff-${TIFF_VERSION}.tar.gz && \ - cd tiff-${TIFF_VERSION} && \ - ./configure --prefix=$PREFIX --with-webp-include-dir=$PREFIX/include --with-webp-lib-dir=$PREFIX/lib && \ - make -j4 && make install - -# Imagemagick -RUN wget https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ - export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig && \ - tar xvf ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ - cd ImageMagick-${IMAGEMAGICK_VERSION} && \ - ./configure --without-x --with-quantum-depth=8 --prefix=$PREFIX && \ - make -j4 && make install - -# libcurl -RUN wget https://curl.se/download/curl-7.87.0.tar.xz && \ - export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig && \ - tar xvf curl-7.87.0.tar.xz && \ - cd curl-7.87.0 && \ - ./configure --with-openssl --prefix=$PREFIX && \ - make -j4 && make install - -RUN apt-get --no-install-recommends install -y apt-transport-https apt-utils \ - automake autoconf build-essential ccache cmake ca-certificates git \ - gcc g++ libc-ares-dev libc-ares2 libre2-dev \ - libssl-dev m4 make pkg-config tar wget zlib1g-dev +RUN apt-get update && \ + apt-get -y install \ + libpangocairo-1.0-0 libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ + libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 \ + liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates pkg-config \ + libapr1-dev libaprutil1-dev libcurl4-openssl-dev libssl-dev -ENV PKG_CONFIG_PATH=/usr/local/imagemagick/pkgconfig +COPY . /build/mod-dims +WORKDIR /build/mod-dims -COPY . /build/mod_dims -WORKDIR /build/mod_dims -RUN export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig && \ - ./autorun.sh && \ - ./configure && \ - make && \ - make install +RUN zig build FROM httpd:${HTTPD_VERSION} +ARG PREFIX=/usr/local/dims +ENV USER=dims +ENV UID=10001 ENV DIMS_DOWNLOAD_TIMEOUT=60000 ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 ENV DIMS_CLIENT=development @@ -75,31 +27,45 @@ ENV DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true -ENV DIMS_MIN_SOURCE_CACHE=604800 +ENV DIMS_SOURCE_CACHE=604800 +ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_SECRET="" ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 -ENV LC_ALL="C" - -USER root - -COPY --from=build-mod-dims /usr/local/apache2/modules/libmod_dims.so /usr/local/apache2/modules/ -COPY --from=build-mod-dims /usr/local/imagemagick /usr/local/imagemagick +ENV DIMS_WHITELIST="" + +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + "${USER}" + +COPY --from=mod-dims /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 /usr/local/apache2/modules/libmod_dims.so +COPY --from=mod-dims ${PREFIX}/libpng ${PREFIX}/libpng +COPY --from=mod-dims ${PREFIX}/libwebp ${PREFIX}/libwebp +COPY --from=mod-dims ${PREFIX}/libtiff ${PREFIX}/libtiff +COPY --from=mod-dims ${PREFIX}/imagemagick ${PREFIX}/imagemagick COPY docker/dims.conf /usr/local/apache2/conf/extra/dims.conf RUN apt-get update && \ apt-get -y install \ - libpangocairo-1.0-0 libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ - libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 \ - liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr25 ca-certificates && \ + libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ + libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 libjpeg62-turbo \ + liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates && \ rm -rf /usr/local/apache2/build \ /usr/local/apache2/cgi-bin \ /usr/local/apache2/include \ /usr/local/apache2/htdocs/index.html && \ + find /usr/local/dims | grep \.a$ | xargs rm && \ chown -R www-data:www-data /usr/local/apache2 && \ sed "s|Listen 80|Listen 8000|" /usr/local/apache2/conf/httpd.conf -i && \ sed "s|^#LoadModule authz_core_module|LoadModule authz_core_module|" /usr/local/apache2/conf/httpd.conf -i && \ + sed "s|^LogLevel warn|LogLevel debug|" /usr/local/apache2/conf/httpd.conf -i && \ echo "Include conf/extra/dims.conf" >> /usr/local/apache2/conf/httpd.conf -USER 33 +EXPOSE 8080 +#USER 10001:10001 \ No newline at end of file diff --git a/docker/Dockerfile.builder b/docker/Dockerfile.builder new file mode 100644 index 0000000..7bcc756 --- /dev/null +++ b/docker/Dockerfile.builder @@ -0,0 +1,148 @@ +ARG DEBIAN_VERSION=bookworm-slim +ARG HTTPD_VERSION=2.4.62 + +# -- Alpine Base +FROM debian:${DEBIAN_VERSION} AS debian-base + +RUN apt-get -y update && \ + apt-get install -y --no-install-recommends \ + automake libtool autoconf build-essential \ + git ca-certificates \ + libapr1-dev libaprutil1-dev \ + curl \ + libcurl4-openssl-dev libfreetype6-dev libopenexr-dev libxml2-dev \ + libgif-dev libjpeg62-turbo-dev libpng-dev \ + liblcms2-dev pkg-config libssl-dev wget + +# -- Build libpng +FROM debian-base AS libpng + +ARG PREFIX=/usr/local/dims/libpng +ARG PNG_VERSION=1.6.43 +ARG PNG_HASH="sha256:6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c" + +ENV PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig +ENV LD_LIBRARY_PATH=${PREFIX}/lib + +WORKDIR /build + +ADD --checksum="${PNG_HASH}" \ + https://versaweb.dl.sourceforge.net/project/libpng/libpng16/${PNG_VERSION}/libpng-${PNG_VERSION}.tar.xz \ + libpng-${PNG_VERSION}.tar.xz + +RUN tar xvf "libpng-${PNG_VERSION}.tar.xz" && \ + cd "libpng-${PNG_VERSION}" && \ + ./configure --prefix="${PREFIX}" --enable-static && \ + make -j"$(nproc)" && \ + make install + +# -- Build libwebp +FROM debian-base AS libwebp + +ARG PREFIX=/usr/local/dims/libwebp +ARG WEBP_VERSION=1.2.1 +ARG WEBP_HASH="sha256:808b98d2f5b84e9b27fdef6c5372dac769c3bda4502febbfa5031bd3c4d7d018" + +WORKDIR /build + +ADD --checksum="${WEBP_HASH}" \ + https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-${WEBP_VERSION}.tar.gz \ + libwebp-${WEBP_VERSION}.tar.gz + +RUN tar xzvf libwebp-${WEBP_VERSION}.tar.gz && \ + cd libwebp-${WEBP_VERSION} && \ + ./configure --prefix=${PREFIX} --enable-static && \ + make -j"$(nproc)" && \ + make install + +# -- Build libtiff +FROM debian-base AS libtiff + +ARG PREFIX=/usr/local/dims +ARG TIFF_VERSION=4.3.0 +ARG TIFF_HASH="sha256:0e46e5acb087ce7d1ac53cf4f56a09b221537fc86dfc5daaad1c2e89e1b37ac8" + +WORKDIR /build + +COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp + +ADD --checksum="${TIFF_HASH}" \ + https://download.osgeo.org/libtiff/tiff-${TIFF_VERSION}.tar.gz \ + tiff-${TIFF_VERSION}.tar.gz + +RUN tar xzvf tiff-${TIFF_VERSION}.tar.gz && \ + cd tiff-${TIFF_VERSION} && \ + ./configure --prefix=$PREFIX/libtiff --enable-static \ + --with-webp-include-dir=$PREFIX/libwebp/include \ + --with-webp-lib-dir=$PREFIX/libwebp/lib && \ + make -j"$(nproc)" && \ + make install + +# -- Build Imagemagick +FROM debian-base AS imagemagick + +ARG PREFIX=/usr/local/dims +ARG IMAGEMAGICK_VERSION=7.1.1-29 +ARG IMAGEMAGICK_HASH="sha256:f140465fbeb0b4724cba4394bc6f6fb32715731c1c62572d586f4f1c8b9b0685" + +WORKDIR /build + +COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp +COPY --from=libtiff ${PREFIX}/libtiff ${PREFIX}/libtiff +COPY --from=libpng ${PREFIX}/libpng ${PREFIX}/libpng + +ENV PKG_CONFIG_PATH=${PREFIX}/libwebp/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libtiff/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libpng/lib/pkgconfig + +ADD --checksum="${IMAGEMAGICK_HASH}" \ + https://imagemagick.org/archive/releases/ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz . + +RUN tar xvf ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ + cd ImageMagick-${IMAGEMAGICK_VERSION} && \ + ./configure --enable-opencl --with-openmp --with-magick-plus-plus=no \ + --with-modules=no --enable-hdri=no --without-utilities --disable-dpc \ + --enable-zero-configuration --with-threads --with-quantum-depth=8 \ + --disable-docs --without-openexr --without-lqr --without-x --without-jbig \ + --with-png=yes --with-jpeg=yes --with-xml=yes --with-webp=yes --with-tiff=yes \ + --prefix=${PREFIX}/imagemagick && \ + make -j"$(nproc)" && \ + make install && \ + rm -rf ${PREFIX}/imagemagick/bin && \ + rm -rf ${PREFIX}/imagemagick/etc && \ + rm -rf ${PREFIX}/imagemagick/share + +# -- Build base +FROM httpd:${HTTPD_VERSION} + +WORKDIR /build + +ARG PREFIX=/usr/local/dims + +RUN apt-get -y update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + libcurl4 libfreetype6 libopenexr-3-1-30 libxml2 \ + libgif7 libjpeg62-turbo wget xz-utils \ + liblcms2-2 libpangocairo-1.0-0 && \ + rm -rf /var/lib/apt/lists/* && \ + wget https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz && \ + tar xvf zig-linux-aarch64-0.13.0.tar.xz && \ + rm zig-linux-aarch64-0.13.0.tar.xz + +COPY --from=libpng ${PREFIX}/libpng ${PREFIX}/libpng +COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp +COPY --from=libtiff ${PREFIX}/libtiff ${PREFIX}/libtiff +COPY --from=imagemagick ${PREFIX}/imagemagick ${PREFIX}/imagemagick + +ENV PATH=/build/zig-linux-aarch64-0.13.0:$PATH + +ENV PKG_CONFIG_PATH=${PREFIX}/libwebp/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libpng/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libtiff/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/imagemagick/lib/pkgconfig + +ENV LD_CONFIG_PATH=${PREFIX}/libwebp/lib +ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libpng/lib +ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libtiff/lib +ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/imagemagick/lib diff --git a/src/handler.c b/src/handler.c index 33d6900..d3aa0fd 100644 --- a/src/handler.c +++ b/src/handler.c @@ -60,7 +60,7 @@ dims_create_request(request_rec *r) } /* Check first if URL is passed as a query parameter. */ - char *url = NULL, *fixed_url = NULL, *commands = NULL, *eurl = NULL; + char *url = NULL, *fixed_url = NULL, *eurl = NULL; if(r->args) { const size_t args_len = strlen(r->args) + 1; char *args = apr_pstrndup(request->r->pool, request->r->args, args_len); @@ -141,7 +141,6 @@ dims_create_request(request_rec *r) } request->image_url = image_url; - request->unparsed_commands = commands + 6; request->request_hash = ap_md5(request->pool, apr_pstrcat(request->pool, request->client_id, request->commands, request->image_url, NULL)); diff --git a/src/mod_dims.c b/src/mod_dims.c index 2646b19..de738af 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -873,19 +873,6 @@ dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) if (status != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; - /* If there was a memory block already assigned, destroy it */ - if (shm) { - status = apr_shm_destroy(shm); - if (status != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Couldn't destroy old memory block\n"); - return status; - } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_dims : Old Shared memory block, destroyed."); - } - } - /* Create shared memory block */ status = apr_shm_create(&shm, sizeof(dims_stats_rec), NULL, p); if (status != APR_SUCCESS) { diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 7fc8b77..0599269 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -54,8 +54,6 @@ static DimsGravity gravities[] = { {NULL, CenterGravity} }; -CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); - apr_status_t dims_strip_operation (dims_request_rec *d, char *args, char **err) { From f0d41b702f9cfe4d2528879c74b0e38f69db8167 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Mon, 11 Nov 2024 13:35:09 -0500 Subject: [PATCH 20/69] Remove examples/ directory --- examples/dims.conf | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 examples/dims.conf diff --git a/examples/dims.conf b/examples/dims.conf deleted file mode 100644 index 3731937..0000000 --- a/examples/dims.conf +++ /dev/null @@ -1,37 +0,0 @@ - - - LoadModule dims_module mod_dims/modules/mod_dims.so - - -DimsDownloadTimeout 60000 -DimsImagemagickTimeout 20000 - -# DimsClient [ ] -DimsAddClient TEST http://placehold.it/350x150 604800 604800 trust 604800 604800 t3st - -DimsDefaultImageURL http://placehold.it/350x150 -DimsCacheExpire 604800 -DimsNoImageCacheExpire 60 - -DimsAddWhitelist *.beetlebug.org *.aolcdn.com -DimsAddWhitelist www.google.com - -## Handler definitions. ## - -AddHandler dims-local .gif .jpg .png - - - SetHandler dims - - - - SetHandler dims3 - - - - SetHandler dims4 - - - - SetHandler dims-status - From 76cfd9f5a4c798ffb591c34c67684b0e9b6c69da Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Tue, 12 Nov 2024 07:18:00 -0500 Subject: [PATCH 21/69] Rename license file --- COPYING => LICENSE | 1 + 1 file changed, 1 insertion(+) rename COPYING => LICENSE (94%) diff --git a/COPYING b/LICENSE similarity index 94% rename from COPYING rename to LICENSE index 277ca3d..691c04d 100644 --- a/COPYING +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright 2009 AOL LLC +Copyright 2024 Jeremy Collins Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of From 90157c41de2afc35bef3fa9f576267f7bfe8a06c Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Tue, 12 Nov 2024 14:59:17 -0500 Subject: [PATCH 22/69] Fix build on macOS --- build.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 1cf72ec..d5ec03a 100644 --- a/build.zig +++ b/build.zig @@ -11,8 +11,11 @@ pub fn build(b: *std.Build) void { .version = .{ .major = 4, .minor = 0, .patch = 0 }, }); - libmod_dims.linkSystemLibrary("curl"); + libmod_dims.linker_allow_shlib_undefined = true; libmod_dims.linkSystemLibrary("apr-1"); + libmod_dims.linkSystemLibrary("apr-util-1"); + libmod_dims.linkSystemLibrary("curl"); + libmod_dims.linkSystemLibrary("openssl"); libmod_dims.linkSystemLibrary("MagickWand"); libmod_dims.addCSourceFiles(.{ .files = &.{ @@ -27,6 +30,7 @@ pub fn build(b: *std.Build) void { }, .flags = &.{ "-I/usr/local/apache2/include/", + "-I/opt/homebrew/include/httpd", "-Wall", "-W", "-Wstrict-prototypes", From f95569e9c070495a713cacccbb45091d45df7a7b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Tue, 12 Nov 2024 15:14:38 -0500 Subject: [PATCH 23/69] Move Dockerfiles to the project root --- docker/.dockerignore => .dockerignore | 0 docker/Dockerfile => Dockerfile | 3 +- .../Dockerfile.builder => Dockerfile.builder | 0 docker/dims.conf => dims.conf | 0 docker/README.md | 38 ------------------- 5 files changed, 1 insertion(+), 40 deletions(-) rename docker/.dockerignore => .dockerignore (100%) rename docker/Dockerfile => Dockerfile (96%) rename docker/Dockerfile.builder => Dockerfile.builder (100%) rename docker/dims.conf => dims.conf (100%) delete mode 100644 docker/README.md diff --git a/docker/.dockerignore b/.dockerignore similarity index 100% rename from docker/.dockerignore rename to .dockerignore diff --git a/docker/Dockerfile b/Dockerfile similarity index 96% rename from docker/Dockerfile rename to Dockerfile index c6dbca0..614aa2b 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -30,7 +30,6 @@ ENV DIMS_TRUST_SOURCE=true ENV DIMS_SOURCE_CACHE=604800 ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 -ENV DIMS_SECRET="" ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV DIMS_WHITELIST="" @@ -49,7 +48,7 @@ COPY --from=mod-dims ${PREFIX}/libpng ${PREFIX}/libpng COPY --from=mod-dims ${PREFIX}/libwebp ${PREFIX}/libwebp COPY --from=mod-dims ${PREFIX}/libtiff ${PREFIX}/libtiff COPY --from=mod-dims ${PREFIX}/imagemagick ${PREFIX}/imagemagick -COPY docker/dims.conf /usr/local/apache2/conf/extra/dims.conf +COPY dims.conf /usr/local/apache2/conf/extra/dims.conf RUN apt-get update && \ apt-get -y install \ diff --git a/docker/Dockerfile.builder b/Dockerfile.builder similarity index 100% rename from docker/Dockerfile.builder rename to Dockerfile.builder diff --git a/docker/dims.conf b/dims.conf similarity index 100% rename from docker/dims.conf rename to dims.conf diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index 56249f1..0000000 --- a/docker/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# mod-dims Docker Container - -# How to build this image - -``` -$ docker build -t mod-dims:latest -f Dockerfile .. -``` - -# How to use this image - -## Set DIMS_SECRET to use /dims4/ based signed URLs - -```shell -$ docker run -e DIMS_SECRET=mysecret mod-dims:latest -``` - -## Set DIMS_WHITELIST to use /dims3/ based URLs - -```shell -$ docker run -e DIMS_WHITELIST="images.pexels.com" mod-dims:latest -``` - -# Configuration - -| Environment Variables | Description | Default | -|-----------------------|-------------|---------| -| `DIMS_CLIENT` | Name of client | development | -| `DIMS_SECRET` | Shared secret for /dims4/ signatures | "" | -| `DIMS_DOWNLOAD_TIMEOUT` | Max time allowed for downloading source images, in milliseconds. | 60000 | -| `DIMS_IMAGEMAGICK_TIMEOUT` | Max time allowed for Imagemagick processing, in milliseconds. | 20000 | -| `DIMS_NO_IMAGE_URL` | URL (http(s):// or file:///) to an image displayed for errors | "http://placehold.it/350x150" | -| `DIMS_CACHE_CONTROL_MAX_AGE` | Cache control max age header setting, in seconds | 604800 | -| `DIMS_EDGE_CONTROL_DOWNSTREAM_TTL` | Edge control downstream TTL | 604800 | -| `DIMS_TRUST_SOURCE` | Whether or not to trust origin cache headers | true | -| `DIMS_MIN_SOURCE_CACHE` | Min max-age to accept from image origin, in seconds | 604800 | -| `DIMS_MAX_SOURCE_CACHE` | Max max-age to accept from image origin, in seconds | 604800 | -| `DIMS_CACHE_EXPIRE` | Default expire time when no cache headers are present on origin image, in seconds | 604800 | -| `DIMS_NO_IMAGE_CACHE_EXPIRE` | Time to cache "no image" (i.e. dims failures), in seconds | 60 | \ No newline at end of file From 69ffb765a57002e13d70cdf557055b883fdf5da2 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Tue, 12 Nov 2024 17:34:06 -0500 Subject: [PATCH 24/69] Convert main Dockerfile to Alpine --- Dockerfile | 68 ++++++++++++++------------------ Dockerfile.builder | 97 +++++++++++++++++++++++++++++++++------------- build.zig | 2 +- httpd.conf | 34 ++++++++++++++++ 4 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 httpd.conf diff --git a/Dockerfile b/Dockerfile index 614aa2b..a7bde11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,21 @@ -ARG HTTPD_VERSION=2.4.62 +ARG ALPINE_VERSION=3.20 -FROM ghcr.io/beetlebugorg/mod-dims:builder AS mod-dims +FROM --platform=linux/arm64 ghcr.io/beetlebugorg/mod-dims:builder AS mod-dims +ARG PREFIX=/usr/local/dims +ENV TARGETARCH=aarch64 + +ADD https://ziglang.org/download/0.13.0/zig-linux-${TARGETARCH}-0.13.0.tar.xz . -RUN apt-get update && \ - apt-get -y install \ - libpangocairo-1.0-0 libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ - libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 \ - liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates pkg-config \ - libapr1-dev libaprutil1-dev libcurl4-openssl-dev libssl-dev +RUN tar xf zig-linux-${TARGETARCH}-0.13.0.tar.xz +RUN apk update && apk add openssl-dev curl-dev expat-dev COPY . /build/mod-dims WORKDIR /build/mod-dims +RUN export PATH=$PATH:/build/mod-dims/zig-linux-${TARGETARCH}-0.13.0 && \ + zig build --verbose && \ + cp zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/apache2/modules/libmod_dims.so -RUN zig build - -FROM httpd:${HTTPD_VERSION} +FROM alpine:${ALPINE_VERSION} ARG PREFIX=/usr/local/dims ENV USER=dims @@ -33,6 +34,16 @@ ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV DIMS_WHITELIST="" +ENV PATH=${PREFIX}/apache2/bin:${PATH} + +COPY --from=mod-dims /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 /usr/local/apache2/modules/libmod_dims.so +COPY --from=mod-dims ${PREFIX}/libpng ${PREFIX}/libpng +COPY --from=mod-dims ${PREFIX}/libwebp ${PREFIX}/libwebp +COPY --from=mod-dims ${PREFIX}/libtiff ${PREFIX}/libtiff +COPY --from=mod-dims ${PREFIX}/imagemagick ${PREFIX}/imagemagick +COPY --from=mod-dims ${PREFIX}/apache2 ${PREFIX}/apache2 +COPY dims.conf /usr/local/dims/apache2/conf/extra/dims.conf +COPY httpd.conf /usr/local/dims/apache2/conf/httpd.conf RUN adduser \ --disabled-password \ @@ -41,30 +52,11 @@ RUN adduser \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ - "${USER}" - -COPY --from=mod-dims /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 /usr/local/apache2/modules/libmod_dims.so -COPY --from=mod-dims ${PREFIX}/libpng ${PREFIX}/libpng -COPY --from=mod-dims ${PREFIX}/libwebp ${PREFIX}/libwebp -COPY --from=mod-dims ${PREFIX}/libtiff ${PREFIX}/libtiff -COPY --from=mod-dims ${PREFIX}/imagemagick ${PREFIX}/imagemagick -COPY dims.conf /usr/local/apache2/conf/extra/dims.conf - -RUN apt-get update && \ - apt-get -y install \ - libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ - libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 libjpeg62-turbo \ - liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates && \ - rm -rf /usr/local/apache2/build \ - /usr/local/apache2/cgi-bin \ - /usr/local/apache2/include \ - /usr/local/apache2/htdocs/index.html && \ - find /usr/local/dims | grep \.a$ | xargs rm && \ - chown -R www-data:www-data /usr/local/apache2 && \ - sed "s|Listen 80|Listen 8000|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^#LoadModule authz_core_module|LoadModule authz_core_module|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^LogLevel warn|LogLevel debug|" /usr/local/apache2/conf/httpd.conf -i && \ - echo "Include conf/extra/dims.conf" >> /usr/local/apache2/conf/httpd.conf - -EXPOSE 8080 -#USER 10001:10001 \ No newline at end of file + "${USER}" && \ + chown -R ${USER}:${USER} ${PREFIX}/apache2/logs && \ + apk update && apk add pcre libexpat libcurl libgomp libgcc gcompat + +EXPOSE 80 +STOPSIGNAL SIGWINCH +USER 10001:10001 +CMD ["httpd", "-DFOREGROUND"] \ No newline at end of file diff --git a/Dockerfile.builder b/Dockerfile.builder index 7bcc756..95fa0c5 100644 --- a/Dockerfile.builder +++ b/Dockerfile.builder @@ -1,21 +1,13 @@ -ARG DEBIAN_VERSION=bookworm-slim +ARG ALPINE_VERSION=3.20 ARG HTTPD_VERSION=2.4.62 # -- Alpine Base -FROM debian:${DEBIAN_VERSION} AS debian-base - -RUN apt-get -y update && \ - apt-get install -y --no-install-recommends \ - automake libtool autoconf build-essential \ - git ca-certificates \ - libapr1-dev libaprutil1-dev \ - curl \ - libcurl4-openssl-dev libfreetype6-dev libopenexr-dev libxml2-dev \ - libgif-dev libjpeg62-turbo-dev libpng-dev \ - liblcms2-dev pkg-config libssl-dev wget +FROM alpine:${ALPINE_VERSION} AS build-essentals + +RUN apk add --no-cache alpine-sdk xz zlib-dev zlib-static vim expat-dev pcre-dev # -- Build libpng -FROM debian-base AS libpng +FROM build-essentals AS libpng ARG PREFIX=/usr/local/dims/libpng ARG PNG_VERSION=1.6.43 @@ -37,7 +29,7 @@ RUN tar xvf "libpng-${PNG_VERSION}.tar.xz" && \ make install # -- Build libwebp -FROM debian-base AS libwebp +FROM build-essentals AS libwebp ARG PREFIX=/usr/local/dims/libwebp ARG WEBP_VERSION=1.2.1 @@ -56,7 +48,7 @@ RUN tar xzvf libwebp-${WEBP_VERSION}.tar.gz && \ make install # -- Build libtiff -FROM debian-base AS libtiff +FROM build-essentals AS libtiff ARG PREFIX=/usr/local/dims ARG TIFF_VERSION=4.3.0 @@ -79,7 +71,7 @@ RUN tar xzvf tiff-${TIFF_VERSION}.tar.gz && \ make install # -- Build Imagemagick -FROM debian-base AS imagemagick +FROM build-essentals AS imagemagick ARG PREFIX=/usr/local/dims ARG IMAGEMAGICK_VERSION=7.1.1-29 @@ -112,28 +104,77 @@ RUN tar xvf ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ rm -rf ${PREFIX}/imagemagick/etc && \ rm -rf ${PREFIX}/imagemagick/share +# -- Build Imagemagick +FROM build-essentals AS apache2 + +ENV PREFIX=/usr/local/dims/apache2 +ENV PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig +ENV LD_LIBRARY_PATH=${PREFIX}/lib + +ENV HTTPD_VERSION=2.4.62 +ENV HTTPD_SHA256="sha256:3e2404d762a2da03560d7ada379ba1599d32f04a0d70ad6ff86f44325f2f062d" + +ENV APR_VERSION=1.7.5 +ENV APR_SHA256="sha256:3375fa365d67bcf945e52b52cba07abea57ef530f40b281ffbe977a9251361db" + +ENV APR_UTIL_VERSION=1.6.3 +ENV APR_UTIL_SHA256="sha256:2b74d8932703826862ca305b094eef2983c27b39d5c9414442e9976a9acf1983" + +ADD --checksum="${HTTPD_SHA256}" \ + https://dlcdn.apache.org/httpd/httpd-${HTTPD_VERSION}.tar.gz . + +ADD --checksum="${APR_SHA256}" \ + https://dlcdn.apache.org//apr/apr-${APR_VERSION}.tar.gz . + +ADD --checksum="${APR_UTIL_SHA256}" \ + https://dlcdn.apache.org//apr/apr-util-${APR_UTIL_VERSION}.tar.gz . + +RUN tar xzvf apr-${APR_VERSION}.tar.gz && \ + cd apr-${APR_VERSION} && \ + ./configure --prefix=${PREFIX} && \ + make -j"$(nproc)" && \ + make install + +RUN tar xzvf apr-util-${APR_UTIL_VERSION}.tar.gz && \ + cd apr-util-${APR_UTIL_VERSION} && \ + ./configure --prefix=${PREFIX} --with-apr=${PREFIX} && \ + make -j"$(nproc)" && \ + make install + +RUN tar xzvf httpd-${HTTPD_VERSION}.tar.gz && \ + cd httpd-${HTTPD_VERSION} && \ + ./configure --prefix=${PREFIX} --with-apr=${PREFIX} --with-apr-util=${PREFIX} \ + --enable-mods-static="few" -enable-mods-shared="log log_config" -with-mpm=event && \ + make -j"$(nproc)" && \ + make install && \ + rm -rf ${PREFIX}/manual ${PREFIX}/icons htdocs/* && \ + find ${PREFIX} -name "*.a" -exec rm -f {} \; + # -- Build base -FROM httpd:${HTTPD_VERSION} +FROM alpine:${ALPINE_VERSION} WORKDIR /build ARG PREFIX=/usr/local/dims -RUN apt-get -y update && \ - apt-get install -y --no-install-recommends \ - ca-certificates \ - libcurl4 libfreetype6 libopenexr-3-1-30 libxml2 \ - libgif7 libjpeg62-turbo wget xz-utils \ - liblcms2-2 libpangocairo-1.0-0 && \ - rm -rf /var/lib/apt/lists/* && \ - wget https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz && \ - tar xvf zig-linux-aarch64-0.13.0.tar.xz && \ - rm zig-linux-aarch64-0.13.0.tar.xz +RUN apk update && apk add pcre expat + +#RUN apt-get -y update && \ +# apt-get install -y --no-install-recommends \ +# ca-certificates \ +# libcurl4 libfreetype6 libopenexr-3-1-30 libxml2 \ +# libgif7 libjpeg62-turbo wget xz-utils \ +# liblcms2-2 libpangocairo-1.0-0 && \ +# rm -rf /var/lib/apt/lists/* && \ +# wget https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz && \ +# tar xvf zig-linux-aarch64-0.13.0.tar.xz && \ +# rm zig-linux-aarch64-0.13.0.tar.xz COPY --from=libpng ${PREFIX}/libpng ${PREFIX}/libpng COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp COPY --from=libtiff ${PREFIX}/libtiff ${PREFIX}/libtiff COPY --from=imagemagick ${PREFIX}/imagemagick ${PREFIX}/imagemagick +COPY --from=apache2 ${PREFIX}/apache2 ${PREFIX}/apache2 ENV PATH=/build/zig-linux-aarch64-0.13.0:$PATH @@ -141,8 +182,10 @@ ENV PKG_CONFIG_PATH=${PREFIX}/libwebp/lib/pkgconfig ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libpng/lib/pkgconfig ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libtiff/lib/pkgconfig ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/imagemagick/lib/pkgconfig +ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/apache2/lib/pkgconfig ENV LD_CONFIG_PATH=${PREFIX}/libwebp/lib ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libpng/lib ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libtiff/lib ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/imagemagick/lib +ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/apache2/lib diff --git a/build.zig b/build.zig index d5ec03a..bde8012 100644 --- a/build.zig +++ b/build.zig @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { "src/status.c", }, .flags = &.{ - "-I/usr/local/apache2/include/", + "-I/usr/local/dims/apache2/include/", "-I/opt/homebrew/include/httpd", "-Wall", "-W", diff --git a/httpd.conf b/httpd.conf new file mode 100644 index 0000000..550b70e --- /dev/null +++ b/httpd.conf @@ -0,0 +1,34 @@ +ServerRoot "/usr/local/dims/apache2" + +Listen 8000 + +LoadModule log_config_module modules/mod_log_config.so + + AllowOverride none + Require all denied + + +DocumentRoot "/usr/local/dims/apache2/htdocs" + + Options None + AllowOverride None + Require all granted + + +ErrorLog "logs/error_log" +LogLevel warn + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + CustomLog "logs/access_log" common + #CustomLog "logs/access_log" combined + + +Include conf/extra/dims.conf \ No newline at end of file From c9462eb73e24054b6d1132fd9a5b4a2524a28a9b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 06:47:55 -0500 Subject: [PATCH 25/69] Use Alpine packages --- .devcontainer/Dockerfile | 42 ++++----- .dockerignore | 1 + Dockerfile | 68 +++++--------- Dockerfile.builder | 191 --------------------------------------- build.zig | 2 +- dims.conf | 46 ++++++++-- httpd.conf | 34 ------- 7 files changed, 78 insertions(+), 306 deletions(-) delete mode 100644 Dockerfile.builder delete mode 100644 httpd.conf diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ffd76f3..2cb8d32 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,39 +1,29 @@ ARG HTTPD_VERSION=2.4.62 -FROM ghcr.io/beetlebugorg/mod-dims:builder - -RUN apt-get update && \ - apt-get -y install \ - libpangocairo-1.0-0 libgif7 libjpeg62-turbo libpng16-16 libgomp1 libjbig0 liblcms2-2 \ - libbz2-1.0 libfftw3-double3 libfontconfig1 libfreetype6 libheif1 \ - liblqr-1-0 libltdl7 liblzma5 libopenjp2-7 libopenexr-3-1-30 ca-certificates pkg-config \ - libapr1-dev libaprutil1-dev libcurl4-openssl-dev libssl-dev && \ - chown -R www-data:www-data /usr/local/apache2 && \ - sed "s|Listen 80|Listen 8000|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^#LoadModule authz_core_module|LoadModule authz_core_module|" /usr/local/apache2/conf/httpd.conf -i && \ - sed "s|^LogLevel warn|LogLevel debug|" /usr/local/apache2/conf/httpd.conf -i && \ - echo "Include conf/extra/dims.conf" >> /usr/local/apache2/conf/httpd.conf - -COPY . /build/mod-dims -WORKDIR /build/mod-dims - -RUN zig build +FROM httpd:${HTTPD_VERSION}-alpine ENV DIMS_DOWNLOAD_TIMEOUT=60000 ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 -ENV DIMS_CLIENT=development -ENV DIMS_NO_IMAGE_URL="http://placehold.it/350x150" -ENV DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true -ENV DIMS_SOURCE_CACHE=604800 -ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 -ENV DIMS_WHITELIST="" +ENV PREFIX=/usr/local/apache2 +ENV PATH=${PREFIX}/bin:${PATH} -COPY docker/dims.conf /usr/local/apache2/conf/extra/dims.conf +COPY . /build/mod-dims +WORKDIR /build/mod-dims + +RUN apk update && \ + apk add vim gdb zig apr-dev apr-util-dev imagemagick-dev curl-dev \ + imagemagick-jpeg imagemagick-webp imagemagick-tiff && \ + zig build || true; \ + ln -sf /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/modules/libmod_dims.so || true -EXPOSE 8000 \ No newline at end of file +COPY dims.conf /usr/local/apache2/conf/httpd.conf + +ENV DIMS_CLIENT=development +ENV DIMS_NO_IMAGE_URL="http://placehold.it/350x150" +ENV DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" \ No newline at end of file diff --git a/.dockerignore b/.dockerignore index c26d4af..55f1256 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ +.zig-cache zig-out zig-cache diff --git a/Dockerfile b/Dockerfile index a7bde11..3b83030 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,62 +1,36 @@ -ARG ALPINE_VERSION=3.20 +ARG HTTPD_VERSION=2.4.62 -FROM --platform=linux/arm64 ghcr.io/beetlebugorg/mod-dims:builder AS mod-dims -ARG PREFIX=/usr/local/dims -ENV TARGETARCH=aarch64 +FROM httpd:${HTTPD_VERSION}-alpine AS builder -ADD https://ziglang.org/download/0.13.0/zig-linux-${TARGETARCH}-0.13.0.tar.xz . - -RUN tar xf zig-linux-${TARGETARCH}-0.13.0.tar.xz -RUN apk update && apk add openssl-dev curl-dev expat-dev +ENV PREFIX=/usr/local/apache2 +ENV PATH=${PREFIX}/bin:${PATH} COPY . /build/mod-dims WORKDIR /build/mod-dims -RUN export PATH=$PATH:/build/mod-dims/zig-linux-${TARGETARCH}-0.13.0 && \ - zig build --verbose && \ - cp zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/apache2/modules/libmod_dims.so -FROM alpine:${ALPINE_VERSION} -ARG PREFIX=/usr/local/dims +RUN apk update && \ + apk add vim gdb zig apr-dev apr-util-dev imagemagick-dev curl-dev \ + imagemagick-jpeg imagemagick-webp imagemagick-tiff && \ + zig build || true; \ + ln -sf /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/modules/libmod_dims.so || true + +FROM httpd:${HTTPD_VERSION}-alpine AS final -ENV USER=dims -ENV UID=10001 ENV DIMS_DOWNLOAD_TIMEOUT=60000 ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 -ENV DIMS_CLIENT=development -ENV DIMS_NO_IMAGE_URL="http://placehold.it/350x150" -ENV DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true -ENV DIMS_SOURCE_CACHE=604800 -ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 -ENV DIMS_WHITELIST="" -ENV PATH=${PREFIX}/apache2/bin:${PATH} - -COPY --from=mod-dims /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 /usr/local/apache2/modules/libmod_dims.so -COPY --from=mod-dims ${PREFIX}/libpng ${PREFIX}/libpng -COPY --from=mod-dims ${PREFIX}/libwebp ${PREFIX}/libwebp -COPY --from=mod-dims ${PREFIX}/libtiff ${PREFIX}/libtiff -COPY --from=mod-dims ${PREFIX}/imagemagick ${PREFIX}/imagemagick -COPY --from=mod-dims ${PREFIX}/apache2 ${PREFIX}/apache2 -COPY dims.conf /usr/local/dims/apache2/conf/extra/dims.conf -COPY httpd.conf /usr/local/dims/apache2/conf/httpd.conf - -RUN adduser \ - --disabled-password \ - --gecos "" \ - --home "/nonexistent" \ - --shell "/sbin/nologin" \ - --no-create-home \ - --uid "${UID}" \ - "${USER}" && \ - chown -R ${USER}:${USER} ${PREFIX}/apache2/logs && \ - apk update && apk add pcre libexpat libcurl libgomp libgcc gcompat - -EXPOSE 80 -STOPSIGNAL SIGWINCH -USER 10001:10001 -CMD ["httpd", "-DFOREGROUND"] \ No newline at end of file +ENV PREFIX=/usr/local/apache2 +ENV PATH=${PREFIX}/bin:${PATH} + +RUN apk update && \ + apk add imagemagick imagemagick-jpeg imagemagick-webp imagemagick-tiff curl + +COPY --from=builder /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/modules/libmod_dims.so +COPY dims.conf /usr/local/apache2/conf/httpd.conf + +EXPOSE 8000 \ No newline at end of file diff --git a/Dockerfile.builder b/Dockerfile.builder deleted file mode 100644 index 95fa0c5..0000000 --- a/Dockerfile.builder +++ /dev/null @@ -1,191 +0,0 @@ -ARG ALPINE_VERSION=3.20 -ARG HTTPD_VERSION=2.4.62 - -# -- Alpine Base -FROM alpine:${ALPINE_VERSION} AS build-essentals - -RUN apk add --no-cache alpine-sdk xz zlib-dev zlib-static vim expat-dev pcre-dev - -# -- Build libpng -FROM build-essentals AS libpng - -ARG PREFIX=/usr/local/dims/libpng -ARG PNG_VERSION=1.6.43 -ARG PNG_HASH="sha256:6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c" - -ENV PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig -ENV LD_LIBRARY_PATH=${PREFIX}/lib - -WORKDIR /build - -ADD --checksum="${PNG_HASH}" \ - https://versaweb.dl.sourceforge.net/project/libpng/libpng16/${PNG_VERSION}/libpng-${PNG_VERSION}.tar.xz \ - libpng-${PNG_VERSION}.tar.xz - -RUN tar xvf "libpng-${PNG_VERSION}.tar.xz" && \ - cd "libpng-${PNG_VERSION}" && \ - ./configure --prefix="${PREFIX}" --enable-static && \ - make -j"$(nproc)" && \ - make install - -# -- Build libwebp -FROM build-essentals AS libwebp - -ARG PREFIX=/usr/local/dims/libwebp -ARG WEBP_VERSION=1.2.1 -ARG WEBP_HASH="sha256:808b98d2f5b84e9b27fdef6c5372dac769c3bda4502febbfa5031bd3c4d7d018" - -WORKDIR /build - -ADD --checksum="${WEBP_HASH}" \ - https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-${WEBP_VERSION}.tar.gz \ - libwebp-${WEBP_VERSION}.tar.gz - -RUN tar xzvf libwebp-${WEBP_VERSION}.tar.gz && \ - cd libwebp-${WEBP_VERSION} && \ - ./configure --prefix=${PREFIX} --enable-static && \ - make -j"$(nproc)" && \ - make install - -# -- Build libtiff -FROM build-essentals AS libtiff - -ARG PREFIX=/usr/local/dims -ARG TIFF_VERSION=4.3.0 -ARG TIFF_HASH="sha256:0e46e5acb087ce7d1ac53cf4f56a09b221537fc86dfc5daaad1c2e89e1b37ac8" - -WORKDIR /build - -COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp - -ADD --checksum="${TIFF_HASH}" \ - https://download.osgeo.org/libtiff/tiff-${TIFF_VERSION}.tar.gz \ - tiff-${TIFF_VERSION}.tar.gz - -RUN tar xzvf tiff-${TIFF_VERSION}.tar.gz && \ - cd tiff-${TIFF_VERSION} && \ - ./configure --prefix=$PREFIX/libtiff --enable-static \ - --with-webp-include-dir=$PREFIX/libwebp/include \ - --with-webp-lib-dir=$PREFIX/libwebp/lib && \ - make -j"$(nproc)" && \ - make install - -# -- Build Imagemagick -FROM build-essentals AS imagemagick - -ARG PREFIX=/usr/local/dims -ARG IMAGEMAGICK_VERSION=7.1.1-29 -ARG IMAGEMAGICK_HASH="sha256:f140465fbeb0b4724cba4394bc6f6fb32715731c1c62572d586f4f1c8b9b0685" - -WORKDIR /build - -COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp -COPY --from=libtiff ${PREFIX}/libtiff ${PREFIX}/libtiff -COPY --from=libpng ${PREFIX}/libpng ${PREFIX}/libpng - -ENV PKG_CONFIG_PATH=${PREFIX}/libwebp/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libtiff/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libpng/lib/pkgconfig - -ADD --checksum="${IMAGEMAGICK_HASH}" \ - https://imagemagick.org/archive/releases/ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz . - -RUN tar xvf ImageMagick-${IMAGEMAGICK_VERSION}.tar.xz && \ - cd ImageMagick-${IMAGEMAGICK_VERSION} && \ - ./configure --enable-opencl --with-openmp --with-magick-plus-plus=no \ - --with-modules=no --enable-hdri=no --without-utilities --disable-dpc \ - --enable-zero-configuration --with-threads --with-quantum-depth=8 \ - --disable-docs --without-openexr --without-lqr --without-x --without-jbig \ - --with-png=yes --with-jpeg=yes --with-xml=yes --with-webp=yes --with-tiff=yes \ - --prefix=${PREFIX}/imagemagick && \ - make -j"$(nproc)" && \ - make install && \ - rm -rf ${PREFIX}/imagemagick/bin && \ - rm -rf ${PREFIX}/imagemagick/etc && \ - rm -rf ${PREFIX}/imagemagick/share - -# -- Build Imagemagick -FROM build-essentals AS apache2 - -ENV PREFIX=/usr/local/dims/apache2 -ENV PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig -ENV LD_LIBRARY_PATH=${PREFIX}/lib - -ENV HTTPD_VERSION=2.4.62 -ENV HTTPD_SHA256="sha256:3e2404d762a2da03560d7ada379ba1599d32f04a0d70ad6ff86f44325f2f062d" - -ENV APR_VERSION=1.7.5 -ENV APR_SHA256="sha256:3375fa365d67bcf945e52b52cba07abea57ef530f40b281ffbe977a9251361db" - -ENV APR_UTIL_VERSION=1.6.3 -ENV APR_UTIL_SHA256="sha256:2b74d8932703826862ca305b094eef2983c27b39d5c9414442e9976a9acf1983" - -ADD --checksum="${HTTPD_SHA256}" \ - https://dlcdn.apache.org/httpd/httpd-${HTTPD_VERSION}.tar.gz . - -ADD --checksum="${APR_SHA256}" \ - https://dlcdn.apache.org//apr/apr-${APR_VERSION}.tar.gz . - -ADD --checksum="${APR_UTIL_SHA256}" \ - https://dlcdn.apache.org//apr/apr-util-${APR_UTIL_VERSION}.tar.gz . - -RUN tar xzvf apr-${APR_VERSION}.tar.gz && \ - cd apr-${APR_VERSION} && \ - ./configure --prefix=${PREFIX} && \ - make -j"$(nproc)" && \ - make install - -RUN tar xzvf apr-util-${APR_UTIL_VERSION}.tar.gz && \ - cd apr-util-${APR_UTIL_VERSION} && \ - ./configure --prefix=${PREFIX} --with-apr=${PREFIX} && \ - make -j"$(nproc)" && \ - make install - -RUN tar xzvf httpd-${HTTPD_VERSION}.tar.gz && \ - cd httpd-${HTTPD_VERSION} && \ - ./configure --prefix=${PREFIX} --with-apr=${PREFIX} --with-apr-util=${PREFIX} \ - --enable-mods-static="few" -enable-mods-shared="log log_config" -with-mpm=event && \ - make -j"$(nproc)" && \ - make install && \ - rm -rf ${PREFIX}/manual ${PREFIX}/icons htdocs/* && \ - find ${PREFIX} -name "*.a" -exec rm -f {} \; - -# -- Build base -FROM alpine:${ALPINE_VERSION} - -WORKDIR /build - -ARG PREFIX=/usr/local/dims - -RUN apk update && apk add pcre expat - -#RUN apt-get -y update && \ -# apt-get install -y --no-install-recommends \ -# ca-certificates \ -# libcurl4 libfreetype6 libopenexr-3-1-30 libxml2 \ -# libgif7 libjpeg62-turbo wget xz-utils \ -# liblcms2-2 libpangocairo-1.0-0 && \ -# rm -rf /var/lib/apt/lists/* && \ -# wget https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz && \ -# tar xvf zig-linux-aarch64-0.13.0.tar.xz && \ -# rm zig-linux-aarch64-0.13.0.tar.xz - -COPY --from=libpng ${PREFIX}/libpng ${PREFIX}/libpng -COPY --from=libwebp ${PREFIX}/libwebp ${PREFIX}/libwebp -COPY --from=libtiff ${PREFIX}/libtiff ${PREFIX}/libtiff -COPY --from=imagemagick ${PREFIX}/imagemagick ${PREFIX}/imagemagick -COPY --from=apache2 ${PREFIX}/apache2 ${PREFIX}/apache2 - -ENV PATH=/build/zig-linux-aarch64-0.13.0:$PATH - -ENV PKG_CONFIG_PATH=${PREFIX}/libwebp/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libpng/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/libtiff/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/imagemagick/lib/pkgconfig -ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX}/apache2/lib/pkgconfig - -ENV LD_CONFIG_PATH=${PREFIX}/libwebp/lib -ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libpng/lib -ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/libtiff/lib -ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/imagemagick/lib -ENV LD_CONFIG_PATH=$LD_CONFIG_PATH:${PREFIX}/apache2/lib diff --git a/build.zig b/build.zig index bde8012..d5ec03a 100644 --- a/build.zig +++ b/build.zig @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { "src/status.c", }, .flags = &.{ - "-I/usr/local/dims/apache2/include/", + "-I/usr/local/apache2/include/", "-I/opt/homebrew/include/httpd", "-Wall", "-W", diff --git a/dims.conf b/dims.conf index 7189891..bccdc60 100644 --- a/dims.conf +++ b/dims.conf @@ -1,9 +1,35 @@ - - LoadModule dims_module modules/libmod_dims.so +ServerRoot "/usr/local/apache2" +Listen 8000 +DocumentRoot "/usr/local/apache2/htdocs" + +LoadModule dims_module modules/libmod_dims.so +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_core_module modules/mod_authz_core.so + +LogLevel debug + +User www-data +Group www-data + +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio -DimsDownloadTimeout ${DIMS_DOWNLOAD_TIMEOUT} -DimsImagemagickTimeout ${DIMS_IMAGEMAGICK_TIMEOUT} +CustomLog /proc/self/fd/1 common +ErrorLog /proc/self/fd/2 + +ServerLimit 16 +StartServers 2 +MaxRequestWorkers 150 +MinSpareThreads 25 +MaxSpareThreads 75 +ThreadsPerChild 25 # DimsClient [ ] DimsAddClient ${DIMS_CLIENT} ${DIMS_NO_IMAGE_URL} ${DIMS_CACHE_CONTROL_MAX_AGE} ${DIMS_EDGE_CONTROL_DOWNSTREAM_TTL} ${DIMS_TRUST_SOURCE} ${DIMS_MIN_SOURCE_CACHE} ${DIMS_MAX_SOURCE_CACHE} ${DIMS_SECRET} @@ -12,18 +38,24 @@ DimsDefaultImageURL ${DIMS_DEFAULT_IMAGE_URL} DimsCacheExpire ${DIMS_CACHE_EXPIRE} DimsNoImageCacheExpire ${DIMS_NO_IMAGE_CACHE_EXPIRE} DimsUserAgentEnabled true - -## Handler definitions. ## - +DimsDownloadTimeout ${DIMS_DOWNLOAD_TIMEOUT} +DimsImagemagickTimeout ${DIMS_IMAGEMAGICK_TIMEOUT} DimsAddWhitelist ${DIMS_WHITELIST} + SetHandler dims3 + AuthType None + Require all granted SetHandler dims4 + AuthType None + Require all granted SetHandler dims-status + AuthType None + Require all granted \ No newline at end of file diff --git a/httpd.conf b/httpd.conf deleted file mode 100644 index 550b70e..0000000 --- a/httpd.conf +++ /dev/null @@ -1,34 +0,0 @@ -ServerRoot "/usr/local/dims/apache2" - -Listen 8000 - -LoadModule log_config_module modules/mod_log_config.so - - AllowOverride none - Require all denied - - -DocumentRoot "/usr/local/dims/apache2/htdocs" - - Options None - AllowOverride None - Require all granted - - -ErrorLog "logs/error_log" -LogLevel warn - - - LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined - LogFormat "%h %l %u %t \"%r\" %>s %b" common - - - # You need to enable mod_logio.c to use %I and %O - LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio - - - CustomLog "logs/access_log" common - #CustomLog "logs/access_log" combined - - -Include conf/extra/dims.conf \ No newline at end of file From 77487fe054a847296e9c6054531cd33f06ab3fd4 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 06:49:42 -0500 Subject: [PATCH 26/69] Add env file for local development --- .env | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..9c0152a --- /dev/null +++ b/.env @@ -0,0 +1,15 @@ +export DIMS_DOWNLOAD_TIMEOUT=60000 +export DIMS_IMAGEMAGICK_TIMEOUT=20000 +export DIMS_CLIENT=development +export DIMS_NO_IMAGE_URL="http://placehold.it/350x150" +export DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" +export DIMS_CACHE_CONTROL_MAX_AGE=604800 +export DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 +export DIMS_TRUST_SOURCE=true +export DIMS_SOURCE_CACHE=604800 +export DIMS_MIN_SOURCE_CACHE=0 +export DIMS_MAX_SOURCE_CACHE=604800 +export DIMS_SECRET="devmode" +export DIMS_CACHE_EXPIRE=604800 +export DIMS_NO_IMAGE_CACHE_EXPIRE=60 +export DIMS_WHITELIST="*.com *.net *.org *.io 127.0.0.1 localhost" From 8ddf3dc249720f7bc34fbe507b20e84a4de1875d Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 06:54:16 -0500 Subject: [PATCH 27/69] Fix case when _keys is empty --- src/mod_dims.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index de738af..69a35c1 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -717,12 +717,14 @@ verify_dims4_signature(dims_request_rec *d) { char *signature_params = apr_pstrcat(d->pool, d->expiration, d->client_config->secret_key, d->commands, d->image_url, NULL); // Concatenate additional params. - char *token; - char *strtokstate; - token = apr_strtok(apr_hash_get(params, "_keys", APR_HASH_KEY_STRING), ",", &strtokstate); - while (token) { - signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(params, token, APR_HASH_KEY_STRING), NULL); - token = apr_strtok(NULL, ",", &strtokstate); + char *strtokstate = NULL; + char *keys = apr_hash_get(params, "_keys", APR_HASH_KEY_STRING); + if (keys != NULL) { + char *token = apr_strtok(keys, ",", &strtokstate); + while (token) { + signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(params, token, APR_HASH_KEY_STRING), NULL); + token = apr_strtok(NULL, ",", &strtokstate); + } } // Hash. From ba79ce04c60df8a67bce052b35443ea06270fb2c Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 08:16:04 -0500 Subject: [PATCH 28/69] Error handling clean, first pass Removed dims_cleanup --- src/encryption.h | 2 +- src/handler.c | 36 +++++++++------ src/handler.h | 4 +- src/mod_dims.c | 117 ++++++++++++++--------------------------------- src/mod_dims.h | 30 +++++------- 5 files changed, 71 insertions(+), 118 deletions(-) diff --git a/src/encryption.h b/src/encryption.h index 7bbcd8c..5bb511d 100644 --- a/src/encryption.h +++ b/src/encryption.h @@ -1,5 +1,5 @@ #ifndef _ENCRYPTION_H_ -#define _ENCRYPTION_H +#define _ENCRYPTION_H_ int aes_errors(const char *message, size_t length, void *u); char *aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_text, int encrypted_length); diff --git a/src/handler.c b/src/handler.c index d3aa0fd..2e36a6b 100644 --- a/src/handler.c +++ b/src/handler.c @@ -42,7 +42,15 @@ dims_create_request(request_rec *r) request->optimize_resize = config->optimize_resize; request->send_content_disposition = 0; request->content_disposition_filename = NULL; - + + return request; +} + +static apr_status_t +dims_request_parse(dims_request_rec *request) +{ + request_rec *r = request->r; + char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); request->unparsed_commands = unparsed_commands; @@ -60,7 +68,7 @@ dims_create_request(request_rec *r) } /* Check first if URL is passed as a query parameter. */ - char *url = NULL, *fixed_url = NULL, *eurl = NULL; + char *fixed_url = NULL, *eurl = NULL; if(r->args) { const size_t args_len = strlen(r->args) + 1; char *args = apr_pstrndup(request->r->pool, request->r->args, args_len); @@ -74,8 +82,7 @@ dims_create_request(request_rec *r) ap_unescape_url(fixed_url); if (strcmp(fixed_url, "") == 0) { - //return dims_cleanup(request, NULL, DIMS_BAD_URL); - return NULL; + return DIMS_BAD_URL; } } else if (strncmp(token, "download=1", 10) == 0) { request->send_content_disposition = 1; @@ -91,8 +98,7 @@ dims_create_request(request_rec *r) // Convert to hex. char hex[SHA_DIGEST_LENGTH * 2 + 1]; if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { - //return dims_cleanup(request, "URL Decryption Failed", DIMS_FAILURE); - return NULL; + return DIMS_DECRYPTION_FAILURE; } // Use first 16 bytes. @@ -114,11 +120,13 @@ dims_create_request(request_rec *r) int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); } + if (fixed_url == NULL) { - //return dims_cleanup(request, "URL Decryption Failed", DIMS_FAILURE); - return NULL; + return DIMS_DECRYPTION_FAILURE; } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "Decrypted URL: %s", fixed_url); + break; } else if (strncmp(token, "optimizeResize=", 4) == 0) { @@ -148,15 +156,14 @@ dims_create_request(request_rec *r) apr_uri_t uri; if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { if (!uri.path) { - //return dims_cleanup(request, NULL, DIMS_BAD_URL); - return NULL; + return DIMS_BAD_URL; } const char *path = apr_filepath_name_get(uri.path); request->content_disposition_filename = apr_pstrdup(request->r->pool, path); } - return request; + return DIMS_SUCCESS; } /** @@ -189,8 +196,9 @@ dims_handler(request_rec *r) } dims_request_rec *d = dims_create_request(r); - if (d == NULL) { - return HTTP_INTERNAL_SERVER_ERROR; + int status = dims_request_parse(d); + if (status != DIMS_SUCCESS) { + return status; } /* Set initial notes to be logged by mod_log_config. */ @@ -200,7 +208,7 @@ dims_handler(request_rec *r) apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return dims_cleanup(d, "Client ID is not valid", DIMS_BAD_CLIENT); + return DIMS_BAD_CLIENT; } if(d->client_config && d->client_config->no_image_url) { diff --git a/src/handler.h b/src/handler.h index 6de9aa0..2db3f3e 100644 --- a/src/handler.h +++ b/src/handler.h @@ -1,5 +1,5 @@ -#ifndef _HANDLER_H -#define _HANDLER_ +#ifndef _HANDLER_H_ +#define _HANDLER_H_ apr_status_t dims_handler(request_rec *r); diff --git a/src/mod_dims.c b/src/mod_dims.c index 69a35c1..b6ac9c9 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -1,24 +1,8 @@ /** * mod_dims - Dynamic Image Manipulation Service * - * This module provides a webservice for dynamically manipulating - * images. Currently cropping, resizing, reformatting and - * thumbnail creation are supported. - * - * Code Flow Logic: - * - * dims_handler - called by apache, determines if request should be processed - * \ and does initial request setup. - * dims_handle_request - validates against whitelist, client list and loads image. - * \ - * dims_process_image - parses operations (resize, etc) and executes them - * \ using imagemagick api. - * dims_send_image - sends image to connection w/appropriate headers - * - * Any errors during processing will call 'dims_cleanup' which will free - * any memory and return the 'no image' image to the connection. - * * Copyright 2009 AOL LLC + * Copyright 2024 Jeremy Collins * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -51,9 +35,9 @@ #define MAGICK_CHECK(func, d) \ do {\ if(func == MagickFalse) \ - return dims_cleanup(d, NULL, DIMS_FAILURE); \ + return DIMS_FAILURE; \ if(d->status == DIMS_IMAGEMAGICK_TIMEOUT) \ - return dims_cleanup(d, NULL, d->status); \ + return d->status; \ } while(0); typedef struct { @@ -151,7 +135,7 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) free(image_data.data); } - return 1; + return image_data.response_code; } char *actual_image_data = image_data.data; @@ -163,8 +147,7 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) } start_time = apr_time_now(); - if(MagickReadImageBlob(d->wand, actual_image_data, image_data.used) - == MagickFalse) { + if(MagickReadImageBlob(d->wand, actual_image_data, image_data.used) == MagickFalse) { ExceptionType et; if(image_data.data) { @@ -175,7 +158,7 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) "ImageMagick error, '%s', on request: %s ", MagickGetException(d->wand, &et), d->r->uri); - return 1; + return HTTP_INTERNAL_SERVER_ERROR; } d->imagemagick_time += (apr_time_now() - start_time) / 1000; @@ -185,7 +168,7 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) free(image_data.data); - return 0; + return HTTP_OK; } static apr_status_t @@ -415,45 +398,6 @@ dims_send_image(dims_request_rec *d) return OK; } -apr_status_t -dims_cleanup(dims_request_rec *d, char *err_msg, int status) -{ - if(status != DIMS_IGNORE) { - d->status = status; - } - - if(d->wand) { - ExceptionType type; - char *msg = MagickGetException(d->wand, &type); - - if(type != UndefinedException && msg) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "Imagemagick error, '%s', on request: %s ", - msg, d->r->uri); - } - - MagickRelinquishMemory(msg); - } - - if(err_msg) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "mod_dims error, '%s', on request: %s ", - err_msg, d->r->uri); - } - - if(d->no_image_url) { - if(!dims_fetch_remote_image(d, NULL)) { - return dims_send_image(d); - } - } - - if ( status != DIMS_SUCCESS ) { - return HTTP_NOT_FOUND; - } else { - return DECLINED; - } -} - /** * Parse through the requested commands and set * the optimal image size on the MagicWand. @@ -614,7 +558,7 @@ dims_process_image(dims_request_rec *d) } else if(rec.width > 0 && rec.height > 0) { args = apr_psprintf(d->pool, "%ldx%ld", rec.width, rec.height); } else { - return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS); + return DIMS_BAD_ARGUMENTS; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, @@ -640,7 +584,7 @@ dims_process_image(dims_request_rec *d) command, args, d->r->uri); if((code = func(d, args, &err)) != DIMS_SUCCESS) { - return dims_cleanup(d, err, code); + return code; } } } @@ -657,7 +601,7 @@ dims_process_image(dims_request_rec *d) apr_status_t code; if((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { - return dims_cleanup(d, err, code); + return code; } } } @@ -676,7 +620,7 @@ dims_process_image(dims_request_rec *d) "Executing default strip command, on request %s", d->r->uri); if((code = strip_func(d, NULL, &err)) != DIMS_SUCCESS) { - return dims_cleanup(d, err, code); + return code; } } } @@ -732,21 +676,24 @@ verify_dims4_signature(dims_request_rec *d) { if(d->client_config->secret_key == NULL) { gen_hash[7] = '\0'; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Developer key not set for client '%s'", d->client_config->id); - return dims_cleanup(d, "Missing Developer Key", DIMS_BAD_CLIENT); + "Secret key not set for client '%s'", d->client_config->id); + + return DIMS_MISSING_SECRET; } else if (strncasecmp(d->signature, gen_hash, 6) != 0) { gen_hash[7] = '\0'; ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Key Mismatch: wanted %6s got %6s [%s?url=%s]", gen_hash, d->signature, d->r->uri, d->image_url); - return dims_cleanup(d, "Key mismatch", DIMS_BAD_URL); + "Signature invalid: wanted %6s got %6s [%s?url=%s]", gen_hash, d->signature, d->r->uri, d->image_url); + + return DIMS_INVALID_SIGNATURE; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "secret key (%s) to validated (%s:%s)", d->signature, d->commands, d->image_url); + "Signature valid: '%s' (%s:%s)", d->signature, d->commands, d->image_url); - return 1; + return OK; } int @@ -762,12 +709,12 @@ verify_dims3_allowlist(dims_request_rec *d) { * and it's value is set to "glob" the match will be accepted. */ if(apr_uri_parse(d->pool, d->image_url, &uri) != APR_SUCCESS) { - return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); + return DIMS_BAD_URL; } char *filename = strrchr(uri.path, '/'); if (!filename || !uri.hostname) { - return dims_cleanup(d, "Invalid URL in request.", DIMS_BAD_URL); + return DIMS_BAD_URL; } if (*filename == '/') { @@ -797,28 +744,32 @@ static apr_status_t dims_handle_request(dims_request_rec *d) { // Download image. - int status; - if ((status = dims_fetch_remote_image(d, d->image_url)) != 0) { + int status = dims_fetch_remote_image(d, d->image_url); + if (status != DIMS_SUCCESS) { return status; } // Execute Imagemagick commands. - if ((status = dims_process_image(d)) != 0) { + status = dims_process_image(d); + if (status != DIMS_SUCCESS) { return status; } // Serve the image. - if ((status = dims_send_image(d)) != 0) { + status = dims_send_image(d); + if (status != DIMS_SUCCESS) { return status; } + + return HTTP_OK; } apr_status_t dims_handle_dims3(dims_request_rec *d) { // Verify allowlist (dims3 only). - if (!verify_dims3_allowlist(d)) { - return dims_cleanup(d, "Source image is being served from an non-allowed host.", DIMS_HOSTNAME_NOT_IN_WHITELIST); + if (verify_dims3_allowlist(d)) { + return HTTP_UNAUTHORIZED; } return dims_handle_request(d); @@ -828,8 +779,8 @@ apr_status_t dims_handle_dims4(dims_request_rec *d) { // Verify signature (dims4 only). - if (!verify_dims4_signature(d)) { - return dims_cleanup(d, "Signature is invalid.", DIMS_BAD_CLIENT); + if (verify_dims4_signature(d)) { + return HTTP_UNAUTHORIZED; } return dims_handle_request(d); diff --git a/src/mod_dims.h b/src/mod_dims.h index b026168..2fb700c 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -42,24 +42,19 @@ #include "request.h" -#define LEGACY_DIMS_RESIZE 1 -#define LEGACY_DIMS_REFORMAT 2 -#define LEGACY_DIMS_CROP 4 -#define LEGACY_DIMS_SHARPEN 8 -#define LEGACY_DIMS_JPG 256 -#define LEGACY_DIMS_GIF 512 -#define LEGACY_DIMS_PNG 1024 - #define DIMS_IGNORE -1 -#define DIMS_SUCCESS 0 -#define DIMS_FAILURE 1 -#define DIMS_DOWNLOAD_TIMEOUT 2 -#define DIMS_IMAGEMAGICK_TIMEOUT 4 -#define DIMS_BAD_CLIENT 8 -#define DIMS_BAD_URL 16 -#define DIMS_BAD_ARGUMENTS 32 -#define DIMS_HOSTNAME_NOT_IN_WHITELIST 64 -#define DIMS_FILE_NOT_FOUND 128 +#define DIMS_SUCCESS 200 +#define DIMS_FAILURE 500 +#define DIMS_FILE_NOT_FOUND 404 +#define DIMS_DOWNLOAD_TIMEOUT 1000 +#define DIMS_IMAGEMAGICK_TIMEOUT 1001 +#define DIMS_BAD_CLIENT 1002 +#define DIMS_BAD_URL 1003 +#define DIMS_BAD_ARGUMENTS 1004 +#define DIMS_HOSTNAME_NOT_IN_WHITELIST 1005 +#define DIMS_INVALID_SIGNATURE 1006 +#define DIMS_MISSING_SECRET 1007 +#define DIMS_DECRYPTION_FAILURE 1008 int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); void dims_child_init(apr_pool_t *p, server_rec *s); @@ -67,7 +62,6 @@ void dims_register_hooks(apr_pool_t *p); apr_status_t dims_handle_dims3(dims_request_rec *d); apr_status_t dims_handle_dims4(dims_request_rec *d); -apr_status_t dims_cleanup(dims_request_rec *d, char *err_msg, int status); typedef struct { apr_uint32_t success_count; From 74263f2cbb4f5151de59102845f0a302499b902b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 08:16:20 -0500 Subject: [PATCH 29/69] Update Dockerfile --- .devcontainer/Dockerfile | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2cb8d32..07d1aab 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -13,17 +13,16 @@ ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV PREFIX=/usr/local/apache2 ENV PATH=${PREFIX}/bin:${PATH} -COPY . /build/mod-dims -WORKDIR /build/mod-dims - RUN apk update && \ apk add vim gdb zig apr-dev apr-util-dev imagemagick-dev curl-dev \ imagemagick-jpeg imagemagick-webp imagemagick-tiff && \ - zig build || true; \ - ln -sf /build/mod-dims/zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/modules/libmod_dims.so || true + zig build --verbose || true; \ + ln -sf /workspaces/mod_dims/zig-out/lib/libmod_dims.so.4.0.0 ${PREFIX}/modules/libmod_dims.so COPY dims.conf /usr/local/apache2/conf/httpd.conf ENV DIMS_CLIENT=development -ENV DIMS_NO_IMAGE_URL="http://placehold.it/350x150" -ENV DIMS_DEFAULT_IMAGE_URL="http://placehold.it/350x150" \ No newline at end of file +ENV DIMS_SECRET=devmode +ENV DIMS_WHITELIST="*.com *.net *.org *.io" +ENV DIMS_NO_IMAGE_URL="http://192.168.65.1:8081/mod-dims_00001_.jpg" +ENV DIMS_DEFAULT_IMAGE_URL="http://192.168.65.1:8081/mod-dims_00001_.jpg" \ No newline at end of file From b72a0d0eeac1bee5b5f74d93b7cdd54628992f4b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 11:32:43 -0500 Subject: [PATCH 30/69] Error handling clean, second pass --- .vscode/c_cpp_properties.json | 9 +- .vscode/settings.json | 8 -- dims.conf | 2 +- src/curl.c | 53 ++++++---- src/curl.h | 9 +- src/handler.c | 5 +- src/mod_dims.c | 186 ++++++++++++++++------------------ src/request.h | 13 ++- 8 files changed, 142 insertions(+), 143 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index c4e7420..eb945a6 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,9 +4,14 @@ "name": "Linux", "includePath": [ "${default}", - "/usr/local/imagemagick/include/ImageMagick-7/", "/usr/local/apache2/include", - "/usr/include/apr-1.0" + "/usr/lib/zig/libc/include", + "/usr/lib/zig/libcxx/include", + "/usr/lib/zig/libc/include/generic-musl", + "/usr/include", + "/usr/include/apr-1/", + "/usr/include/ImageMagick-7/", + "/usr/lib/zig/libc/include/aarch64-linux-musl" ], "defines": [], "cStandard": "c17", diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0515ca4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files.associations": { - "*.templ": "templ", - "mod_dims.h": "c", - "scoreboard.h": "c" - }, - "C_Cpp.default.compilerPath": "/usr/bin/gcc" -} \ No newline at end of file diff --git a/dims.conf b/dims.conf index bccdc60..7d690c4 100644 --- a/dims.conf +++ b/dims.conf @@ -58,4 +58,4 @@ DimsAddWhitelist ${DIMS_WHITELIST} SetHandler dims-status AuthType None Require all granted - \ No newline at end of file + diff --git a/src/curl.c b/src/curl.c index bc6ed88..023b7e4 100644 --- a/src/curl.c +++ b/src/curl.c @@ -33,8 +33,10 @@ static char *url_encode(char *str) { } void -lock_share(CURL *handle, curl_lock_data data, - curl_lock_access access, void *userptr) +lock_share(__attribute__ ((unused)) CURL *handle, + curl_lock_data data, + __attribute__ ((unused)) curl_lock_access access, + void *userptr) { dims_curl_rec *locks = (dims_curl_rec *) userptr; @@ -48,7 +50,9 @@ lock_share(CURL *handle, curl_lock_data data, } void -unlock_share(CURL *handle, curl_lock_data data, void *userptr) +unlock_share(__attribute__ ((unused)) CURL *handle, + curl_lock_data data, + void *userptr) { dims_curl_rec *locks = (dims_curl_rec *) userptr; @@ -62,10 +66,11 @@ unlock_share(CURL *handle, curl_lock_data data, void *userptr) } static int -dims_curl_debug_cb(CURL *handle, +dims_curl_debug_cb( + __attribute__ ((unused)) CURL *handle, curl_infotype type, char *data, - size_t size, + __attribute__ ((unused)) size_t size, void *clientp) { dims_request_rec *d = (dims_request_rec *) clientp; @@ -76,30 +81,28 @@ dims_curl_debug_cb(CURL *handle, default: break; } + + return 0; } /** - * This callback is called by the libcurl API to write data into - * memory as it's being downloaded. - * - * The memory allocated here must be freed manually as it's not - * allocated into an apache memory pool. + * This callback is called by the libcurl API to write data into memory as it's being downloaded. */ size_t -dims_write_image_cb(void *ptr, size_t size, size_t nmemb, void *data) +dims_write_image_cb(void *new_data, size_t size, size_t nmemb, void *data) { - dims_image_data_t *mem = (dims_image_data_t *) data; + dims_image_data_t *image = (dims_image_data_t *) data; size_t realsize = size * nmemb; /* Allocate more memory if needed. */ - if(mem->size - mem->used <= realsize) { - mem->size = mem->size == 0 ? realsize : (mem->size + realsize) * 1.25; - mem->data = (char *) realloc(mem->data, mem->size); + if(image->size - image->used <= realsize) { + image->size = image->size == 0 ? realsize : (image->size + realsize) * 1.25; + image->data = (char *) realloc(image->data, image->size); } - if (mem->data) { - memcpy(&(mem->data[mem->used]), ptr, realsize); - mem->used += realsize; + if (image->data) { + memcpy(&(image->data[image->used]), new_data, realsize); + image->used += realsize; } return realsize; @@ -140,7 +143,7 @@ dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) } CURLcode -dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data) +dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *source_image) { CURL *curl_handle; CURLcode code; @@ -198,15 +201,21 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *dat code = curl_easy_perform(curl_handle); - curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &image_data.response_code); + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &source_image->response_code); curl_easy_cleanup(curl_handle); - *data = image_data; - if (!d->config->disable_encoded_fetch) { free(fetch_url); } + if (source_image->response_code == 200) { + source_image->data = apr_pmemdup(d->pool, image_data.data, image_data.used); + source_image->size = image_data.used; + source_image->used = image_data.used; + } + + free(image_data.data); + return code; } diff --git a/src/curl.h b/src/curl.h index b8336ff..906fdff 100644 --- a/src/curl.h +++ b/src/curl.h @@ -1,5 +1,5 @@ #ifndef _CURL_H_ -#define _CURL_H +#define _CURL_H_ #include #include @@ -17,13 +17,6 @@ typedef struct { apr_thread_mutex_t *dns_mutex; } dims_curl_rec; -typedef struct { - char *data; - size_t size; - size_t used; - long response_code; -} dims_image_data_t; - CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); void dims_curl_init(apr_pool_t *p, server_rec *s); diff --git a/src/handler.c b/src/handler.c index 2e36a6b..2340871 100644 --- a/src/handler.c +++ b/src/handler.c @@ -22,7 +22,7 @@ dims_create_request(request_rec *r) request->r = r; request->pool = r->pool; - request->wand = NewMagickWand(); + request->wand = NULL; request->config = config; request->client_config = NULL; request->no_image_url = request->config->no_image_url; @@ -34,7 +34,7 @@ dims_create_request(request_rec *r) request->etag = NULL; request->last_modified = NULL; request->request_hash = NULL; - request->status = APR_SUCCESS; + request->status = DIMS_SUCCESS; request->fetch_http_status = 0; request->start_time = apr_time_now(); request->download_time = 0; @@ -215,7 +215,6 @@ dims_handler(request_rec *r) d->no_image_url = d->client_config->no_image_url; } - if ((strcmp(r->handler, "dims3") == 0)) { return dims_handle_dims3(d); } else if (strcmp(r->handler, "dims4") == 0) { diff --git a/src/mod_dims.c b/src/mod_dims.c index b6ac9c9..9720897 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -24,9 +24,9 @@ #include "curl.h" #include "request.h" #include "module.h" -#include "util_md5.h" #include "cmyk_icc.h" +#include #include #include #include @@ -45,6 +45,13 @@ typedef struct { apr_time_t start_time; } dims_progress_rec; +typedef struct { + size_t length; + unsigned char *bytes; + char *format; + apr_status_t error; +} dims_processed_image; + dims_stats_rec *stats; apr_shm_t *shm; apr_hash_t *ops; @@ -57,8 +64,10 @@ apr_hash_t *ops; * ImageMagick is busy loading up the pixel cache. */ MagickBooleanType -dims_imagemagick_progress_cb(const char *text, const MagickOffsetType offset, - const MagickSizeType span, void *client_data) +dims_imagemagick_progress_cb(const char *text, + __attribute__ ((unused)) const MagickOffsetType offset, + __attribute__ ((unused)) const MagickSizeType span, + void *client_data) { dims_progress_rec *p = (dims_progress_rec *) client_data; @@ -85,25 +94,20 @@ dims_imagemagick_progress_cb(const char *text, const MagickOffsetType offset, * Fetch remote image. If successful the MagicWand will * have the new image loaded. */ -static int +static apr_status_t dims_fetch_remote_image(dims_request_rec *d, const char *url) { - dims_image_data_t image_data; char *fetch_url = url ? (char *) url : d->no_image_url; - int extra_time = 0; apr_time_t start_time; + d->source_image = apr_palloc(d->pool, sizeof(dims_image_data_t)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Loading image from %s", fetch_url); - CURLcode code = dims_get_image_data(d, fetch_url, &image_data); + CURLcode code = dims_get_image_data(d, fetch_url, d->source_image); start_time = apr_time_now(); if(code != 0) { - if(image_data.data) { - free(image_data.data); - } - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "libcurl error, '%s', on request: %s ", curl_easy_strerror(code), d->r->uri); @@ -116,70 +120,40 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) d->download_time = (apr_time_now() - start_time) / 1000; - return 1; + return DIMS_FAILURE; } d->download_time = (apr_time_now() - start_time) / 1000; // Don't set the fetch_http_status if we're downloading the NOIMAGE image. if (url != NULL) { - d->fetch_http_status = image_data.response_code; + d->fetch_http_status = d->source_image->response_code; } - if(image_data.response_code != 200) { - if(image_data.response_code == 404) { + if(d->source_image->response_code != 200) { + if(d->source_image->response_code == 404) { d->status = DIMS_FILE_NOT_FOUND; } - if(image_data.data) { - free(image_data.data); - } - - return image_data.response_code; + return DIMS_FAILURE; } - char *actual_image_data = image_data.data; - // Ensure SVGs have the appropriate XML header. - if (image_data.size >= 4 && strncmp(image_data.data, "pool, "\n", image_data.data, NULL); - image_data.used += 55; + if (d->source_image->size >= 4 && strncmp(d->source_image->data, "source_image->data = + apr_pstrcat(d->pool, "\n", + d->source_image->data, NULL); + d->source_image->used += 55; } - start_time = apr_time_now(); - if(MagickReadImageBlob(d->wand, actual_image_data, image_data.used) == MagickFalse) { - ExceptionType et; - - if(image_data.data) { - free(image_data.data); - } - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "ImageMagick error, '%s', on request: %s ", - MagickGetException(d->wand, &et), d->r->uri); - - return HTTP_INTERNAL_SERVER_ERROR; - } - d->imagemagick_time += (apr_time_now() - start_time) / 1000; - - if(d->status != DIMS_DOWNLOAD_TIMEOUT) { - d->original_image_size = image_data.used; - } - - free(image_data.data); - - return HTTP_OK; + return DIMS_SUCCESS; } static apr_status_t -dims_send_image(dims_request_rec *d) +dims_send_image(dims_request_rec *d, dims_processed_image *image) { char buf[128]; - unsigned char *blob; - char *format; char *content_type; - size_t length; - apr_time_t start_time; int expire_time = 0; char *cache_control = NULL, @@ -195,13 +169,9 @@ dims_send_image(dims_request_rec *d) int trust_src_img = 0; - format = MagickGetImageFormat(d->wand); - - MagickResetIterator(d->wand); - - start_time = apr_time_now(); - blob = MagickGetImagesBlob(d->wand, &length); - d->imagemagick_time += (apr_time_now() - start_time) / 1000; + char *format = image->format; + unsigned char *blob = image->bytes; + size_t length = image->length; /* Set the Content-Type based on the image format. */ content_type = apr_psprintf(d->pool, "image/%s", format); @@ -345,12 +315,9 @@ dims_send_image(dims_request_rec *d) apr_table_set(d->r->headers_out, "ETag", etag); } - MagickSizeType image_size = 0; - MagickGetImageLength(d->wand, &image_size); - if (blob != NULL) { char content_length[256] = ""; - snprintf(content_length, sizeof(content_length), "%zu", (size_t)image_size); + snprintf(content_length, sizeof(content_length), "%zu", (size_t) image->length); apr_table_set(d->r->headers_out, "Content-Length", content_length); ap_rwrite(blob, length, d->r); @@ -360,9 +327,6 @@ dims_send_image(dims_request_rec *d) ap_rflush(d->r); - MagickRelinquishMemory(blob); - MagickRelinquishMemory(format); - /* After the image is sent record stats about this request. */ if(d->status == DIMS_SUCCESS) { apr_atomic_inc32(&stats->success_count); @@ -456,24 +420,37 @@ dims_set_optimal_geometry(dims_request_rec *d) * set the quality of the image to 70 before writing the image * to the connection. */ -static apr_status_t +static dims_processed_image * dims_process_image(dims_request_rec *d) { apr_time_t start_time = apr_time_now(); - /* Hook in the progress monitor. It gets passed a - * dims_progress_rec which keeps track of the start time. - */ - dims_progress_rec *progress_rec = (dims_progress_rec *) apr_palloc( - d->pool, sizeof(dims_progress_rec)); + d->wand = NewMagickWand(); + + if(MagickReadImageBlob(d->wand, d->source_image->data, d->source_image->used) == MagickFalse) { + ExceptionType et; + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, + "ImageMagick error, '%s', on request: %s ", + MagickGetException(d->wand, &et), d->r->uri); + + return NULL; + } + d->imagemagick_time += (apr_time_now() - start_time) / 1000; + + if(d->status != DIMS_DOWNLOAD_TIMEOUT) { + d->original_image_size = d->source_image->used; + } + + dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); + + /* Hook in the progress monitor. It gets passed a dims_progress_rec which keeps track of the start time. */ + dims_progress_rec *progress_rec = (dims_progress_rec *) apr_palloc(d->pool, sizeof(dims_progress_rec)); progress_rec->d = d; progress_rec->start_time = apr_time_now(); - /* Setting the progress monitor from the MagickWand API does not - * seem to work. The monitor never gets called. - */ - SetImageProgressMonitor(GetImageFromMagickWand(d->wand), dims_imagemagick_progress_cb, - (void *) progress_rec); + /* Setting the progress monitor from the MagickWand API does not seem to work. The monitor never gets called. */ + SetImageProgressMonitor(GetImageFromMagickWand(d->wand), dims_imagemagick_progress_cb, (void *) progress_rec); int exc_strip_cmd = 0; @@ -518,7 +495,7 @@ dims_process_image(dims_request_rec *d) } if (should_flatten) { - for (int i = 1; i <= images - 1; i++) { + for (size_t i = 1; i <= images - 1; i++) { MagickSetIteratorIndex(d->wand, i); MagickRemoveImage(d->wand); } @@ -546,10 +523,9 @@ dims_process_image(dims_request_rec *d) strcmp(command, "legacy_thumbnail") == 0 || strcmp(command, "legacy_crop") == 0 || strcmp(command, "thumbnail") == 0)) { - MagickStatusType flags; RectangleInfo rec; - flags = ParseAbsoluteGeometry(args, &rec); + ParseAbsoluteGeometry(args, &rec); if(rec.width > 0 && rec.height == 0) { args = apr_psprintf(d->pool, "%ld", rec.width); @@ -558,14 +534,16 @@ dims_process_image(dims_request_rec *d) } else if(rec.width > 0 && rec.height > 0) { args = apr_psprintf(d->pool, "%ldx%ld", rec.width, rec.height); } else { - return DIMS_BAD_ARGUMENTS; + DestroyMagickWand(d->wand); + image->error = DIMS_BAD_ARGUMENTS; + return image; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Rewriting command %s to 'resize' because a NOIMAGE " "image is being processed.", command); - command = "resize"; + command = apr_psprintf(d->pool, "%s", "resize"); } // Check if the command is present and set flag. @@ -584,7 +562,10 @@ dims_process_image(dims_request_rec *d) command, args, d->r->uri); if((code = func(d, args, &err)) != DIMS_SUCCESS) { - return code; + DestroyMagickWand(d->wand); + image->error = code; + return image; + ; } } } @@ -601,7 +582,9 @@ dims_process_image(dims_request_rec *d) apr_status_t code; if((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { - return code; + DestroyMagickWand(d->wand); + image->error = code; + return image; } } } @@ -620,7 +603,9 @@ dims_process_image(dims_request_rec *d) "Executing default strip command, on request %s", d->r->uri); if((code = strip_func(d, NULL, &err)) != DIMS_SUCCESS) { - return code; + DestroyMagickWand(d->wand); + image->error = code; + return image; } } } @@ -632,7 +617,15 @@ dims_process_image(dims_request_rec *d) */ SetImageProgressMonitor(GetImageFromMagickWand(d->wand), NULL, NULL); - return DIMS_SUCCESS; + MagickResetIterator(d->wand); + + image->error = DIMS_SUCCESS; + image->format = MagickGetImageFormat(d->wand); + image->bytes = MagickGetImagesBlob(d->wand, &image->length); + + DestroyMagickWand(d->wand); + + return image; } int @@ -698,8 +691,7 @@ verify_dims4_signature(dims_request_rec *d) { int verify_dims3_allowlist(dims_request_rec *d) { - char *fetch_url = NULL; - char *hostname, *state = "exact"; + char *hostname, *state = apr_pstrdup(d->pool, "exact"); apr_uri_t uri; int found = 0, done = 0; @@ -733,7 +725,7 @@ verify_dims3_allowlist(dims_request_rec *d) { } else { hostname++; } - state = "glob"; + state = apr_pstrdup(d->pool, "glob"); } } @@ -744,19 +736,21 @@ static apr_status_t dims_handle_request(dims_request_rec *d) { // Download image. - int status = dims_fetch_remote_image(d, d->image_url); - if (status != DIMS_SUCCESS) { + apr_status_t status = dims_fetch_remote_image(d, d->image_url); + if (status != DIMS_SUCCESS) { return status; } // Execute Imagemagick commands. - status = dims_process_image(d); - if (status != DIMS_SUCCESS) { - return status; + dims_processed_image *image = dims_process_image(d); + if (image != NULL && image->error != DIMS_SUCCESS) { + return image->error; + } else if (image == NULL) { + return DIMS_FAILURE; } // Serve the image. - status = dims_send_image(d); + status = dims_send_image(d, image); if (status != DIMS_SUCCESS) { return status; } diff --git a/src/request.h b/src/request.h index 1e71d2e..ab8379c 100644 --- a/src/request.h +++ b/src/request.h @@ -6,9 +6,15 @@ #include "configuration.h" -typedef struct dims_request_rec dims_request_rec; +typedef struct { + char *data; + size_t size; + size_t used; + long response_code; + apr_pool_t *pool; +} dims_image_data_t; -struct dims_request_rec { +typedef struct { request_rec *r; apr_pool_t *pool; @@ -36,6 +42,7 @@ struct dims_request_rec { /* The original image size in bytes. */ long original_image_size; + dims_image_data_t *source_image; /* The sample factor for optimizing resizing. */ float optimize_resize; @@ -75,6 +82,6 @@ struct dims_request_rec { /* Should Content-Disposition header bet set. */ int send_content_disposition; char *content_disposition_filename; -}; +} dims_request_rec; #endif \ No newline at end of file From 2d8d8846f852d9476f714dc7818a9bd9fe5684f4 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 14:14:55 -0500 Subject: [PATCH 31/69] Add build/debug configuration for vscode --- .vscode/launch.json | 30 ++++++++++++++++++++++++++++++ .vscode/tasks.json | 10 ++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..fbb6563 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "mod-dims - debug", + "type": "cppdbg", + "request": "launch", + "program": "/usr/local/apache2/bin/httpd", + "args": ["-X"], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "zig build" + }, + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..78193b4 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "zig build", + "type": "shell", + "command": "zig build --verbose" + } + ] +} \ No newline at end of file From 8a99a61e66f923107a441a53327174147bbd590d Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 14:16:14 -0500 Subject: [PATCH 32/69] Build using c17 standard --- build.zig | 1 + src/curl.c | 10 +++++----- src/mod_dims.c | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/build.zig b/build.zig index d5ec03a..ed0352d 100644 --- a/build.zig +++ b/build.zig @@ -29,6 +29,7 @@ pub fn build(b: *std.Build) void { "src/status.c", }, .flags = &.{ + "-std=c17", "-I/usr/local/apache2/include/", "-I/opt/homebrew/include/httpd", "-Wall", diff --git a/src/curl.c b/src/curl.c index 023b7e4..792578b 100644 --- a/src/curl.c +++ b/src/curl.c @@ -33,9 +33,9 @@ static char *url_encode(char *str) { } void -lock_share(__attribute__ ((unused)) CURL *handle, +lock_share(CURL *handle, curl_lock_data data, - __attribute__ ((unused)) curl_lock_access access, + curl_lock_access access, void *userptr) { dims_curl_rec *locks = (dims_curl_rec *) userptr; @@ -50,7 +50,7 @@ lock_share(__attribute__ ((unused)) CURL *handle, } void -unlock_share(__attribute__ ((unused)) CURL *handle, +unlock_share(CURL *handle, curl_lock_data data, void *userptr) { @@ -67,10 +67,10 @@ unlock_share(__attribute__ ((unused)) CURL *handle, static int dims_curl_debug_cb( - __attribute__ ((unused)) CURL *handle, + CURL *handle, curl_infotype type, char *data, - __attribute__ ((unused)) size_t size, + size_t size, void *clientp) { dims_request_rec *d = (dims_request_rec *) clientp; diff --git a/src/mod_dims.c b/src/mod_dims.c index 9720897..0d07f6b 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -65,8 +65,8 @@ apr_hash_t *ops; */ MagickBooleanType dims_imagemagick_progress_cb(const char *text, - __attribute__ ((unused)) const MagickOffsetType offset, - __attribute__ ((unused)) const MagickSizeType span, + const MagickOffsetType offset, + const MagickSizeType span, void *client_data) { dims_progress_rec *p = (dims_progress_rec *) client_data; From c75209b8808dd513b1b64d5430b78c13c51ac9c3 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 14:39:25 -0500 Subject: [PATCH 33/69] Clean up curl functions This removes "DimsDisableEncodedFetch" and "DimsUserAgentEnabled" configuration directives. These are both enabled by default. --- .vscode/launch.json | 2 +- dims.conf | 1 - src/configuration.c | 14 -------------- src/configuration.h | 2 -- src/curl.c | 37 +++++++++---------------------------- src/curl.h | 2 +- src/directives.h | 8 -------- src/mod_dims.c | 13 +++++-------- src/mod_dims_ops.c | 2 +- 9 files changed, 17 insertions(+), 64 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index fbb6563..790f2a9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "program": "/usr/local/apache2/bin/httpd", "args": ["-X"], "stopAtEntry": false, - "cwd": "${fileDirname}", + "cwd": ".", "environment": [], "externalConsole": false, "MIMode": "gdb", diff --git a/dims.conf b/dims.conf index 7d690c4..decd177 100644 --- a/dims.conf +++ b/dims.conf @@ -37,7 +37,6 @@ DimsAddClient ${DIMS_CLIENT} ${DIMS_NO_IMAGE_URL} ${DIMS_CACHE_CONTROL_MAX_AGE} DimsDefaultImageURL ${DIMS_DEFAULT_IMAGE_URL} DimsCacheExpire ${DIMS_CACHE_EXPIRE} DimsNoImageCacheExpire ${DIMS_NO_IMAGE_CACHE_EXPIRE} -DimsUserAgentEnabled true DimsDownloadTimeout ${DIMS_DOWNLOAD_TIMEOUT} DimsImagemagickTimeout ${DIMS_IMAGEMAGICK_TIMEOUT} DimsAddWhitelist ${DIMS_WHITELIST} diff --git a/src/configuration.c b/src/configuration.c index 322808b..70e2eac 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -203,20 +203,6 @@ dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg return NULL; } -const char * -dims_config_set_user_agent_enabled(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - if(strcmp(arg, "true") == 0) { - config->user_agent_enabled = 1; - } - else { - config->user_agent_enabled = 0; - } - return NULL; -} - const char * dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) { diff --git a/src/configuration.h b/src/configuration.h index 3483aac..3e644d5 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -23,7 +23,6 @@ const char *dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const cha const char *dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_user_agent_enabled(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]); const char *dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg); @@ -63,7 +62,6 @@ struct dims_config_rec { char *default_image_prefix; char *user_agent_override; - int user_agent_enabled; }; struct dims_client_config_rec { diff --git a/src/curl.c b/src/curl.c index 792578b..81b9dc3 100644 --- a/src/curl.c +++ b/src/curl.c @@ -143,7 +143,7 @@ dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) } CURLcode -dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *source_image) +dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) { CURL *curl_handle; CURLcode code; @@ -152,31 +152,14 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *sou image_data.data = NULL; image_data.size = 0; image_data.used = 0; - int extra_time = 0; - - /* Allow for some extra time to download the NOIMAGE image. */ - void *s = NULL; - - if (d->status == DIMS_DOWNLOAD_TIMEOUT) { - extra_time += 500; - } - - apr_pool_userdata_get((void *) &s, DIMS_CURL_SHARED_KEY, - d->r->server->process->pool); - - /* Encode the fetch URL before downloading */ - if (!d->config->disable_encoded_fetch) { - fetch_url = url_encode(fetch_url); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Encoded URL: %s ", fetch_url); - } curl_handle = curl_easy_init(); - curl_easy_setopt(curl_handle, CURLOPT_URL, fetch_url); + curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, dims_write_image_cb); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &image_data); curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dims_write_header_cb); curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) d); - curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, d->config->download_timeout + extra_time); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, d->config->download_timeout); curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); @@ -184,9 +167,9 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *sou curl_easy_setopt(curl_handle, CURLOPT_DEBUGDATA, d); /* Set the user agent to dims/ */ - if (d->config->user_agent_override != NULL && d->config->user_agent_enabled == 1) { + if (d->config->user_agent_override != NULL) { curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, d->config->user_agent_override); - } else if (d->config->user_agent_enabled == 1) { + } else { char *dims_useragent = apr_psprintf(d->r->pool, "mod_dims/%s", MODULE_VERSION); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, dims_useragent); } @@ -194,8 +177,10 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *sou /* The curl shared handle allows this process to share DNS cache * and prevents the DNS cache from going away after every request. */ - if (s) { - dims_curl_rec *locks = (dims_curl_rec *) s; + void *shared_locks = NULL; + apr_pool_userdata_get((void *) &shared_locks, DIMS_CURL_SHARED_KEY, d->r->server->process->pool); + if (shared_locks) { + dims_curl_rec *locks = (dims_curl_rec *) shared_locks; curl_easy_setopt(curl_handle, CURLOPT_SHARE, locks->share); } @@ -204,10 +189,6 @@ dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *sou curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &source_image->response_code); curl_easy_cleanup(curl_handle); - if (!d->config->disable_encoded_fetch) { - free(fetch_url); - } - if (source_image->response_code == 200) { source_image->data = apr_pmemdup(d->pool, image_data.data, image_data.used); source_image->size = image_data.used; diff --git a/src/curl.h b/src/curl.h index 906fdff..ab5fddb 100644 --- a/src/curl.h +++ b/src/curl.h @@ -17,7 +17,7 @@ typedef struct { apr_thread_mutex_t *dns_mutex; } dims_curl_rec; -CURLcode dims_get_image_data(dims_request_rec *d, char *fetch_url, dims_image_data_t *data); +CURLcode dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *data); void dims_curl_init(apr_pool_t *p, server_rec *s); diff --git a/src/directives.h b/src/directives.h index 17f0837..cddd39d 100644 --- a/src/directives.h +++ b/src/directives.h @@ -72,10 +72,6 @@ static const command_rec dims_directives[] = dims_config_set_optimize_resize, NULL, RSRC_CONF, "Should DIMS optimize resize operations. This has a slight impact on image quality. 0 = disabled" "The default is 0."), - AP_INIT_TAKE1("DimsDisableEncodedFetch", - dims_config_set_encoded_fetch, NULL, RSRC_CONF, - "Should DIMS encode image url before fetching it." - "The default is 0."), AP_INIT_TAKE1("DimsEncryptionAlgorithm", dims_config_set_encryption_algorithm, NULL, RSRC_CONF, "What algorithm should DIMS user to decrypt the 'eurl' parameter." @@ -83,10 +79,6 @@ static const command_rec dims_directives[] = AP_INIT_TAKE1("DimsDefaultOutputFormat", dims_config_set_default_output_format, NULL, RSRC_CONF, "Default output format if 'format' command is not present in the request."), - AP_INIT_TAKE1("DimsUserAgentEnabled", - dims_config_set_user_agent_enabled, NULL, RSRC_CONF, - "Enable DIMS User-Agent header ('dims/'), true OR false." - "The default is false."), AP_INIT_TAKE1("DimsUserAgentOverride", dims_config_set_user_agent_override, NULL, RSRC_CONF, "Override DIMS User-Agent header" diff --git a/src/mod_dims.c b/src/mod_dims.c index 0d07f6b..9d4aa08 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -95,16 +95,14 @@ dims_imagemagick_progress_cb(const char *text, * have the new image loaded. */ static apr_status_t -dims_fetch_remote_image(dims_request_rec *d, const char *url) +dims_download_source_image(dims_request_rec *d, const char *url) { - char *fetch_url = url ? (char *) url : d->no_image_url; apr_time_t start_time; d->source_image = apr_palloc(d->pool, sizeof(dims_image_data_t)); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Loading image from %s", fetch_url); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Loading image from %s", url); - CURLcode code = dims_get_image_data(d, fetch_url, d->source_image); + CURLcode code = dims_curl(d, url, d->source_image); start_time = apr_time_now(); if(code != 0) { @@ -112,8 +110,7 @@ dims_fetch_remote_image(dims_request_rec *d, const char *url) "libcurl error, '%s', on request: %s ", curl_easy_strerror(code), d->r->uri); - d->status = DIMS_FAILURE; - d->fetch_http_status = 500; + if(code == CURLE_OPERATION_TIMEDOUT) { d->status = DIMS_DOWNLOAD_TIMEOUT; } @@ -736,7 +733,7 @@ static apr_status_t dims_handle_request(dims_request_rec *d) { // Download image. - apr_status_t status = dims_fetch_remote_image(d, d->image_url); + apr_status_t status = dims_download_source_image(d, d->image_url); if (status != DIMS_SUCCESS) { return status; } diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 0599269..3999c39 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -409,7 +409,7 @@ dims_watermark_operation (dims_request_rec *d, char *args, char **err) { // Write to disk. } else { dims_image_data_t image_data; - CURLcode code = dims_get_image_data(d, overlay_url, &image_data); + CURLcode code = dims_curl(d, overlay_url, &image_data); if (MagickReadImageBlob(overlay_wand, image_data.data, image_data.used) == MagickFalse) { if (image_data.data) { From b359ecdcfbccf2fc5c9412609f766d2e8c4b254e Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 15:00:07 -0500 Subject: [PATCH 34/69] Clean up dims_request_rec --- .vscode/tasks.json | 7 ++++++- src/curl.c | 8 ++++---- src/handler.c | 6 ------ src/mod_dims.c | 42 +++++++++++++----------------------------- src/request.h | 23 ++++++++--------------- 5 files changed, 31 insertions(+), 55 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 78193b4..f6c5b06 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,12 @@ { "label": "zig build", "type": "shell", - "command": "zig build --verbose" + "command": "zig build --verbose", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } } ] } \ No newline at end of file diff --git a/src/curl.c b/src/curl.c index 81b9dc3..414da2c 100644 --- a/src/curl.c +++ b/src/curl.c @@ -130,13 +130,13 @@ dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) } if(key && value && strcmp(key, "Cache-Control") == 0) { - d->cache_control = value; + d->source_image->cache_control = value; } else if(key && value && strcmp(key, "Edge-Control") == 0) { - d->edge_control = value; + d->source_image->edge_control = value; } else if(key && value && strcmp(key, "Last-Modified") == 0) { - d->last_modified = value; + d->source_image->last_modified = value; } else if(key && value && strcmp(key, "ETag") == 0) { - d->etag = value; + d->source_image->etag = value; } return realsize; diff --git a/src/handler.c b/src/handler.c index 2340871..e5d85f7 100644 --- a/src/handler.c +++ b/src/handler.c @@ -28,14 +28,8 @@ dims_create_request(request_rec *r) request->no_image_url = request->config->no_image_url; request->use_no_image = 0; request->image_url = NULL; - request->filename = NULL; - request->cache_control = NULL; - request->edge_control = NULL; - request->etag = NULL; - request->last_modified = NULL; request->request_hash = NULL; request->status = DIMS_SUCCESS; - request->fetch_http_status = 0; request->start_time = apr_time_now(); request->download_time = 0; request->imagemagick_time = 0; diff --git a/src/mod_dims.c b/src/mod_dims.c index 9d4aa08..624fcb6 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -122,11 +122,6 @@ dims_download_source_image(dims_request_rec *d, const char *url) d->download_time = (apr_time_now() - start_time) / 1000; - // Don't set the fetch_http_status if we're downloading the NOIMAGE image. - if (url != NULL) { - d->fetch_http_status = d->source_image->response_code; - } - if(d->source_image->response_code != 200) { if(d->source_image->response_code == 404) { d->status = DIMS_FILE_NOT_FOUND; @@ -177,9 +172,9 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) if(d->status == DIMS_FILE_NOT_FOUND) { d->r->status = HTTP_NOT_FOUND; - } else if (d->fetch_http_status != 0) { - d->r->status = d->fetch_http_status; - } else if(d->status != DIMS_SUCCESS) { + } else if (d->source_image->response_code != 0) { + d->r->status = d->source_image->response_code; + } else if (d->status != DIMS_SUCCESS) { if (d->status == DIMS_BAD_URL || d->status == DIMS_BAD_ARGUMENTS) { d->r->status = HTTP_BAD_REQUEST; @@ -193,13 +188,13 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) d->r->status = HTTP_BAD_REQUEST; } - if(d->status == DIMS_SUCCESS && d->fetch_http_status == 200 && d->client_config) { + if(d->status == DIMS_SUCCESS && d->source_image->response_code == 200 && d->client_config) { // if the src image has a cache_control header, parse out the max-age - if(d->cache_control) { + if(d->source_image->cache_control) { // Ex. max-age=3600 - src_header = d->cache_control; + src_header = d->source_image->cache_control; src_start = src_header; src_len = strlen(src_header); @@ -262,7 +257,7 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) expire_time = d->client_config->cache_control_max_age; } - } else if(d->status == DIMS_SUCCESS && d->fetch_http_status == 200) { + } else if(d->status == DIMS_SUCCESS && d->source_image->response_code == 200) { expire_time = d->config->default_expire; cache_control = apr_psprintf(d->pool, "max-age=%d, public", expire_time); } else { @@ -278,10 +273,7 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) apr_table_set(d->r->headers_out, "Edge-Control", edge_control); } - if(d->filename && d->config->include_disposition) { - char *disposition = apr_psprintf(d->pool, "inline; filename=\"%s\"", d->filename); - apr_table_set(d->r->headers_out, "Content-Disposition", disposition); - } else if(d->content_disposition_filename && d->send_content_disposition) { + if(d->content_disposition_filename && d->send_content_disposition) { char *disposition = apr_psprintf(d->pool, "attachment; filename=\"%s\"", d->content_disposition_filename); apr_table_set(d->r->headers_out, "Content-Disposition", disposition); } @@ -300,12 +292,12 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) } char *etag = NULL; - if (d->etag) { + if (d->source_image->etag) { etag = ap_md5(d->pool, - (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->etag, NULL)); - } else if (d->last_modified) { + (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->source_image->etag, NULL)); + } else if (d->source_image->last_modified) { etag = ap_md5(d->pool, - (unsigned char *)apr_pstrcat(d->pool, d->request_hash, d->last_modified, NULL)); + (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->source_image->last_modified, NULL)); } if (etag) { @@ -341,7 +333,7 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) snprintf(buf, 128, "%d", d->status); apr_table_set(d->r->notes, "DIMS_STATUS", buf); - snprintf(buf, 128, "%ld", d->original_image_size); + snprintf(buf, 128, "%ld", d->source_image->used); apr_table_set(d->r->notes, "DIMS_ORIG_BYTES", buf); snprintf(buf, 128, "%ld", d->download_time); @@ -435,10 +427,6 @@ dims_process_image(dims_request_rec *d) } d->imagemagick_time += (apr_time_now() - start_time) / 1000; - if(d->status != DIMS_DOWNLOAD_TIMEOUT) { - d->original_image_size = d->source_image->used; - } - dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); /* Hook in the progress monitor. It gets passed a dims_progress_rec which keeps track of the start time. */ @@ -706,10 +694,6 @@ verify_dims3_allowlist(dims_request_rec *d) { return DIMS_BAD_URL; } - if (*filename == '/') { - d->filename = ++filename; - } - hostname = uri.hostname; while(!done) { char *value = (char *) apr_table_get(d->config->whitelist, hostname); diff --git a/src/request.h b/src/request.h index ab8379c..46fbdab 100644 --- a/src/request.h +++ b/src/request.h @@ -11,6 +11,12 @@ typedef struct { size_t size; size_t used; long response_code; + + // Cache control headers + char *cache_control; + char *edge_control; + char *last_modified; + char *etag; apr_pool_t *pool; } dims_image_data_t; @@ -23,6 +29,7 @@ typedef struct { /* Client ID of this request. */ char *client_id; + char *request_hash; /* The URL to the image being manipulated. */ char *image_url; @@ -31,17 +38,13 @@ typedef struct { /* The URL to the NOIMAGE image in case of failures. */ char *no_image_url; - /* The filename if this is a local request. */ - char *filename; - /* The unparsed commands (resize, crop, etc). */ char *unparsed_commands; /* The parsed commands with the signature and expiration timestamp removed. */ char *commands; - /* The original image size in bytes. */ - long original_image_size; + /* The source image. */ dims_image_data_t *source_image; /* The sample factor for optimizing resizing. */ @@ -53,13 +56,6 @@ typedef struct { /* The client specific configuration, if available. */ dims_client_config_rec *client_config; - /* The cache headers from the downloaded image. */ - char *cache_control; - char *edge_control; - char *last_modified; - char *etag; - char *request_hash; - /* The current status of this request. If downloading * or manipulating the image times out this will * be set to DIMS_*_TIMEOUT. If everything is ok it will @@ -67,9 +63,6 @@ typedef struct { */ apr_status_t status; - /* The HTTP status code from fetching the original image */ - apr_status_t fetch_http_status; - /* Time this request started. Used for statistics. */ apr_time_t start_time; apr_time_t download_time; From b06b0f7bede26c529f0cd90437a5afb2aacb7b37 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Wed, 13 Nov 2024 15:50:55 -0500 Subject: [PATCH 35/69] Move init methods to initialize.c|h --- .vscode/settings.json | 6 +++ build.zig | 1 + src/initialize.c | 113 ++++++++++++++++++++++++++++++++++++++++++ src/initialize.h | 10 ++++ src/mod_dims.c | 106 +++------------------------------------ src/mod_dims.h | 3 -- src/mod_dims_ops.c | 42 ++++++++++++++++ src/mod_dims_ops.h | 7 +++ src/module.c | 1 + 9 files changed, 186 insertions(+), 103 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/initialize.c create mode 100644 src/initialize.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4270e1b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.templ": "templ", + "strings.h": "c" + } +} \ No newline at end of file diff --git a/build.zig b/build.zig index ed0352d..f787f21 100644 --- a/build.zig +++ b/build.zig @@ -23,6 +23,7 @@ pub fn build(b: *std.Build) void { "src/configuration.c", "src/encryption.c", "src/handler.c", + "src/initialize.c", "src/mod_dims_ops.c", "src/mod_dims.c", "src/module.c", diff --git a/src/initialize.c b/src/initialize.c new file mode 100644 index 0000000..e0d8a3f --- /dev/null +++ b/src/initialize.c @@ -0,0 +1,113 @@ + +#include +#include + +#include "mod_dims.h" +#include "mod_dims_ops.h" +#include "curl.h" +#include "module.h" + +apr_shm_t *shm; + +typedef struct operations { + char *name; + dims_operation_func *func; +} operations; + +static operations ops[] = { + {"strip", dims_strip_operation}, + {"resize", dims_resize_operation}, + {"crop", dims_crop_operation}, + {"thumbnail", dims_thumbnail_operation}, + {"legacy_thumbnail", dims_legacy_thumbnail_operation}, + {"legacy_crop", dims_legacy_crop_operation}, + {"quality", dims_quality_operation}, + {"sharpen", dims_sharpen_operation}, + {"format", dims_format_operation}, + {"brightness", dims_brightness_operation}, + {"flipflop", dims_flipflop_operation}, + {"sepia", dims_sepia_operation}, + {"grayscale", dims_grayscale_operation}, + {"autolevel", dims_autolevel_operation}, + {"rotate", dims_rotate_operation}, + {"invert", dims_invert_operation}, + {"watermark", dims_watermark_operation}, + NULL +}; + +int +dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) +{ + dims_config_rec *config = (dims_config_rec *) + ap_get_module_config(s->module_config, &dims_module); + apr_status_t status; + apr_size_t retsize; + + ap_add_version_component(p, "mod_dims/" MODULE_VERSION); + + MagickWandGenesis(); + MagickSetResourceLimit(AreaResource, config->area_size); + MagickSetResourceLimit(DiskResource, config->disk_size); + MagickSetResourceLimit(MemoryResource, config->memory_size); + MagickSetResourceLimit(MapResource, config->map_size); + + /* Init APR's atomic functions */ + status = apr_atomic_init(p); + if (status != APR_SUCCESS) + return HTTP_INTERNAL_SERVER_ERROR; + + /* Create shared memory block */ + status = apr_shm_create(&shm, sizeof(dims_stats_rec), NULL, p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "mod_dims : Error creating shm block\n"); + return status; + } + + /* Check size of shared memory block */ + retsize = apr_shm_size_get(shm); + if (retsize != sizeof(dims_stats_rec)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "mod_dims : Error allocating shared memory block\n"); + return status; + } + + /* Init shm block */ + stats = apr_shm_baseaddr_get(shm); + if (stats == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "mod_dims : Error creating status block.\n"); + return status; + } + memset(stats, 0, retsize); + + if (retsize < sizeof(dims_stats_rec)) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, + "mod_dims : Not enough memory allocated!! Giving up"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + stats->success_count = 1; + stats->failure_count = 0; + stats->download_timeout_count = 0; + stats->imagemagick_timeout_count = 0; + + return OK; +} + +static apr_status_t +dims_child_cleanup(void *data) +{ + MagickWandTerminus(); + + return APR_SUCCESS; +} + +void +dims_child_init(apr_pool_t *p, server_rec *s) +{ + dims_curl_init(p, s); + + MagickWandGenesis(); + apr_pool_cleanup_register(p, NULL, dims_child_cleanup, dims_child_cleanup); +} \ No newline at end of file diff --git a/src/initialize.h b/src/initialize.h new file mode 100644 index 0000000..9df0551 --- /dev/null +++ b/src/initialize.h @@ -0,0 +1,10 @@ +#ifndef _INITIALIZE_H_ +#define _INITIALIZE_H_ + +#include + +int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); +void dims_child_init(apr_pool_t *p, server_rec *s); +void dims_register_hooks(apr_pool_t *p); + +#endif \ No newline at end of file diff --git a/src/mod_dims.c b/src/mod_dims.c index 624fcb6..6b451ea 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -53,7 +53,6 @@ typedef struct { } dims_processed_image; dims_stats_rec *stats; -apr_shm_t *shm; apr_hash_t *ops; /** @@ -99,6 +98,9 @@ dims_download_source_image(dims_request_rec *d, const char *url) { apr_time_t start_time; d->source_image = apr_palloc(d->pool, sizeof(dims_image_data_t)); + d->source_image->cache_control = NULL; + d->source_image->edge_control = NULL; + d->source_image->last_modified = NULL; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Loading image from %s", url); @@ -536,9 +538,9 @@ dims_process_image(dims_request_rec *d) exc_strip_cmd = 1; } - dims_operation_func *func = - apr_hash_get(ops, command, APR_HASH_KEY_STRING); - if(func != NULL) { + dims_operation_func *func = dims_operation_lookup(command); + if (func != NULL) + { char *err = NULL; apr_status_t code; @@ -760,99 +762,3 @@ dims_handle_dims4(dims_request_rec *d) return dims_handle_request(d); } - -int -dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) -{ - dims_config_rec *config = (dims_config_rec *) - ap_get_module_config(s->module_config, &dims_module); - apr_status_t status; - apr_size_t retsize; - - ap_add_version_component(p, "mod_dims/" MODULE_VERSION); - - MagickWandGenesis(); - MagickSetResourceLimit(AreaResource, config->area_size); - MagickSetResourceLimit(DiskResource, config->disk_size); - MagickSetResourceLimit(MemoryResource, config->memory_size); - MagickSetResourceLimit(MapResource, config->map_size); - - ops = apr_hash_make(p); - apr_hash_set(ops, "strip", APR_HASH_KEY_STRING, dims_strip_operation); - apr_hash_set(ops, "resize", APR_HASH_KEY_STRING, dims_resize_operation); - apr_hash_set(ops, "crop", APR_HASH_KEY_STRING, dims_crop_operation); - apr_hash_set(ops, "thumbnail", APR_HASH_KEY_STRING, dims_thumbnail_operation); - apr_hash_set(ops, "legacy_thumbnail", APR_HASH_KEY_STRING, dims_legacy_thumbnail_operation); - apr_hash_set(ops, "legacy_crop", APR_HASH_KEY_STRING, dims_legacy_crop_operation); - apr_hash_set(ops, "quality", APR_HASH_KEY_STRING, dims_quality_operation); - apr_hash_set(ops, "sharpen", APR_HASH_KEY_STRING, dims_sharpen_operation); - apr_hash_set(ops, "format", APR_HASH_KEY_STRING, dims_format_operation); - apr_hash_set(ops, "brightness", APR_HASH_KEY_STRING, dims_brightness_operation); - apr_hash_set(ops, "flipflop", APR_HASH_KEY_STRING, dims_flipflop_operation); - apr_hash_set(ops, "sepia", APR_HASH_KEY_STRING, dims_sepia_operation); - apr_hash_set(ops, "grayscale", APR_HASH_KEY_STRING, dims_grayscale_operation); - apr_hash_set(ops, "autolevel", APR_HASH_KEY_STRING, dims_autolevel_operation); - apr_hash_set(ops, "rotate", APR_HASH_KEY_STRING, dims_rotate_operation); - apr_hash_set(ops, "invert", APR_HASH_KEY_STRING, dims_invert_operation); - apr_hash_set(ops, "watermark", APR_HASH_KEY_STRING, dims_watermark_operation); - - /* Init APR's atomic functions */ - status = apr_atomic_init(p); - if (status != APR_SUCCESS) - return HTTP_INTERNAL_SERVER_ERROR; - - /* Create shared memory block */ - status = apr_shm_create(&shm, sizeof(dims_stats_rec), NULL, p); - if (status != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error creating shm block\n"); - return status; - } - - /* Check size of shared memory block */ - retsize = apr_shm_size_get(shm); - if (retsize != sizeof(dims_stats_rec)) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error allocating shared memory block\n"); - return status; - } - - /* Init shm block */ - stats = apr_shm_baseaddr_get(shm); - if (stats == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error creating status block.\n"); - return status; - } - memset(stats, 0, retsize); - - if (retsize < sizeof(dims_stats_rec)) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_dims : Not enough memory allocated!! Giving up"); - return HTTP_INTERNAL_SERVER_ERROR; - } - - stats->success_count = 1; - stats->failure_count = 0; - stats->download_timeout_count = 0; - stats->imagemagick_timeout_count = 0; - - return OK; -} - -static apr_status_t -dims_child_cleanup(void *data) -{ - MagickWandTerminus(); - - return APR_SUCCESS; -} - -void -dims_child_init(apr_pool_t *p, server_rec *s) -{ - dims_curl_init(p, s); - - MagickWandGenesis(); - apr_pool_cleanup_register(p, NULL, dims_child_cleanup, dims_child_cleanup); -} diff --git a/src/mod_dims.h b/src/mod_dims.h index 2fb700c..368b42d 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -56,9 +56,6 @@ #define DIMS_MISSING_SECRET 1007 #define DIMS_DECRYPTION_FAILURE 1008 -int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); -void dims_child_init(apr_pool_t *p, server_rec *s); -void dims_register_hooks(apr_pool_t *p); apr_status_t dims_handle_dims3(dims_request_rec *d); apr_status_t dims_handle_dims4(dims_request_rec *d); diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 3999c39..bc9e94a 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -15,6 +15,7 @@ */ #include "mod_dims.h" +#include "mod_dims_ops.h" #include "curl.h" #include "request.h" @@ -24,8 +25,49 @@ #include #include +#include #include +static operations_rec ops[] = { + {"strip", dims_strip_operation}, + {"resize", dims_resize_operation}, + {"crop", dims_crop_operation}, + {"thumbnail", dims_thumbnail_operation}, + {"legacy_thumbnail", dims_legacy_thumbnail_operation}, + {"legacy_crop", dims_legacy_crop_operation}, + {"quality", dims_quality_operation}, + {"sharpen", dims_sharpen_operation}, + {"format", dims_format_operation}, + {"brightness", dims_brightness_operation}, + {"flipflop", dims_flipflop_operation}, + {"sepia", dims_sepia_operation}, + {"grayscale", dims_grayscale_operation}, + {"autolevel", dims_autolevel_operation}, + {"rotate", dims_rotate_operation}, + {"invert", dims_invert_operation}, + {"watermark", dims_watermark_operation}, + {NULL, NULL} +}; + +dims_operation_func * +dims_operation_lookup(char *name) { + operations_rec *op_ptr = ops; + + if (name == NULL) { + return NULL; + } + + while (op_ptr->name != NULL) { + if (strcmp(name, op_ptr->name) == 0) { + return op_ptr->func; + } + + ++op_ptr; + } + + return NULL; +} + #define MAGICK_CHECK(func, rec) \ do { \ apr_status_t code = func; \ diff --git a/src/mod_dims_ops.h b/src/mod_dims_ops.h index 773ab3f..6c8c053 100644 --- a/src/mod_dims_ops.h +++ b/src/mod_dims_ops.h @@ -6,6 +6,11 @@ typedef apr_status_t(dims_operation_func) (dims_request_rec *, char *args, char **err); +typedef struct { + char *name; + dims_operation_func *func; +} operations_rec; + dims_operation_func dims_strip_operation, dims_resize_operation, @@ -26,4 +31,6 @@ dims_operation_func dims_watermark_operation, dims_legacy_crop_operation; +dims_operation_func *dims_operation_lookup(char *name); + #endif \ No newline at end of file diff --git a/src/module.c b/src/module.c index fa6027a..bedcbbb 100644 --- a/src/module.c +++ b/src/module.c @@ -3,6 +3,7 @@ #include "handler.h" #include "directives.h" #include "module.h" +#include "initialize.h" void dims_register_hooks(apr_pool_t *p) From 6cb82e29e642fac677414e0f78d6f1e127feba5d Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 10:18:21 -0500 Subject: [PATCH 36/69] Improve vscode devcontainer setup This ensures vscode is able to find all headers for Intellisense. --- .devcontainer/devcontainer.json | 20 ++++++++++++++++++-- .vscode/c_cpp_properties.json | 23 ----------------------- 2 files changed, 18 insertions(+), 25 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 947fc89..942b3de 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,12 +11,28 @@ // Add the IDs of extensions you want installed when the container is created. "customizations": { "vscode": { + "settings": { + "C_Cpp.default.compilerPath": "/usr/bin/zig", + "C_Cpp.default.includePath": [ + "${default}", + "/usr/local/apache2/include", + "/usr/lib/zig/libc/include", + "/usr/lib/zig/libcxx/include", + "/usr/lib/zig/libc/include/generic-musl", + "/usr/include", + "/usr/include/apr-1/", + "/usr/include/ImageMagick-7/", + "/usr/lib/zig/libc/include/aarch64-linux-musl" + ] + }, "extensions": [ "ms-vscode.cpptools", "ms-vscode.cpptools-extension-pack", - "vscodevim.vim" + "vscodevim.vim", + "ziglang.vscode-zig", + "GitHub.copilot" ] - } + }, }, // https://stackoverflow.com/questions/35860527/warning-error-disabling-address-space-randomization-operation-not-permitted diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index eb945a6..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${default}", - "/usr/local/apache2/include", - "/usr/lib/zig/libc/include", - "/usr/lib/zig/libcxx/include", - "/usr/lib/zig/libc/include/generic-musl", - "/usr/include", - "/usr/include/apr-1/", - "/usr/include/ImageMagick-7/", - "/usr/lib/zig/libc/include/aarch64-linux-musl" - ], - "defines": [], - "cStandard": "c17", - "cppStandard": "gnu++17", - "intelliSenseMode": "linux-gcc-arm64" - } - ], - "version": 4 -} \ No newline at end of file From cb8b74f8575401baaa77269843dddc06bb604121 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 15:28:39 -0500 Subject: [PATCH 37/69] Improve dims_request_parse logic Removes aes_128_decrypt as well. --- src/encryption.c | 41 ----------- src/handler.c | 185 ++++++++++++++++++++++++----------------------- src/mod_dims.c | 53 +++++--------- src/request.h | 1 + 4 files changed, 113 insertions(+), 167 deletions(-) diff --git a/src/encryption.c b/src/encryption.c index 6d145a4..14cf0b6 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -15,47 +15,6 @@ aes_errors(const char *message, size_t length, void *u) return 0; } -char * -aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_text, int encrypted_length) -{ - EVP_CIPHER_CTX *ctx; - - if (!(ctx = EVP_CIPHER_CTX_new())) { - ERR_print_errors_cb(aes_errors, r); - return NULL; - } - - if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - int decrypted_length; - int plaintext_length, out_length; - char *plaintext = apr_palloc(r->pool, encrypted_length * sizeof(char)); - if (EVP_DecryptUpdate(ctx, (unsigned char *) plaintext, &out_length, encrypted_text, encrypted_length)) { - plaintext_length = out_length; - - if (!EVP_DecryptFinal_ex(ctx, (unsigned char *) plaintext + out_length, &plaintext_length)) { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - plaintext_length += out_length; - plaintext[plaintext_length] = '\0'; - } else { - ERR_print_errors_cb(aes_errors, r); - EVP_CIPHER_CTX_free(ctx); - return NULL; - } - - EVP_CIPHER_CTX_free(ctx); - - return plaintext; -} - char * aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_encrypted_text) { EVP_CIPHER_CTX *ctx; diff --git a/src/handler.c b/src/handler.c index e5d85f7..39fd443 100644 --- a/src/handler.c +++ b/src/handler.c @@ -40,6 +40,68 @@ dims_create_request(request_rec *r) return request; } +static char * +dims_encode_spaces(apr_pool_t *pool, char *str) +{ + char *copy = apr_pstrdup(pool, str); + + char *s = copy; + while (*s) { + if (*s == ' ') { + *s = '+'; + } + + s++; + } + + return copy; +} + +static apr_hash_t * +dims_parse_args(request_rec *r) +{ + apr_hash_t *query_params = apr_hash_make(r->pool); + + const size_t args_len = strlen(r->args) + 1; + char *args = apr_pstrndup(r->pool, r->args, args_len); + char *token; + char *strtokstate; + + token = apr_strtok(args, "&", &strtokstate); + while (token) { + char *param = strtok(token, "="); + apr_hash_set(query_params, param, APR_HASH_KEY_STRING, apr_pstrdup(r->pool, param + strlen(param) + 1)); + token = apr_strtok(NULL, "&", &strtokstate); + } + + return query_params; +} + +static char * +dims_decrypt_eurl(request_rec *r, unsigned char *secret_key, char *eurl) +{ + // Hash secret via SHA-1. + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1(secret_key, strlen((char *) secret_key), hash); + + // Convert to hex. + char hex[SHA_DIGEST_LENGTH * 2 + 1]; + if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { + return NULL; + } + + // Use first 16 bytes. + unsigned char key[17]; + strncpy((char *) key, hex, 16); + key[16] = '\0'; + + // Force key to uppercase + unsigned char *s = key; + while (*s) { *s = toupper(*s); s++; } + + return aes_128_gcm_decrypt(r, key, eurl); +} + static apr_status_t dims_request_parse(dims_request_rec *request) { @@ -48,105 +110,48 @@ dims_request_parse(dims_request_rec *request) char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); request->unparsed_commands = unparsed_commands; - request->client_id = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); - request->signature = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); - request->expiration = ap_getword(request->pool, (const char **) &unparsed_commands, '/'); - request->commands = apr_pstrdup(request->pool, unparsed_commands); - char *s = request->commands; - while (*s) { - if (*s == ' ') { - *s = '+'; - } + request->client_id = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + request->signature = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + request->expiration = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + request->commands = dims_encode_spaces(r->pool, unparsed_commands); + request->query_params = dims_parse_args(r); - s++; + char *download = apr_hash_get(request->query_params, "download", APR_HASH_KEY_STRING); + if (download != NULL && *download == '1') { + request->send_content_disposition = 1; } - /* Check first if URL is passed as a query parameter. */ - char *fixed_url = NULL, *eurl = NULL; - if(r->args) { - const size_t args_len = strlen(r->args) + 1; - char *args = apr_pstrndup(request->r->pool, request->r->args, args_len); - char *token; - char *strtokstate; - token = apr_strtok(args, "&", &strtokstate); - while (token) { - if(strncmp(token, "url=", 4) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "ARG: %s", token); - fixed_url = apr_pstrdup(r->pool, token + 4); - ap_unescape_url(fixed_url); - - if (strcmp(fixed_url, "") == 0) { - return DIMS_BAD_URL; - } - } else if (strncmp(token, "download=1", 10) == 0) { - request->send_content_disposition = 1; - - } else if (strncmp(token, "eurl=", 4) == 0) { - eurl = apr_pstrdup(r->pool, token + 5); - - // Hash secret via SHA-1. - unsigned char *secret = (unsigned char *) request->client_config->secret_key; - unsigned char hash[SHA_DIGEST_LENGTH]; - SHA1(secret, strlen((char *) secret), hash); - - // Convert to hex. - char hex[SHA_DIGEST_LENGTH * 2 + 1]; - if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { - return DIMS_DECRYPTION_FAILURE; - } - - // Use first 16 bytes. - unsigned char key[17]; - strncpy((char *) key, hex, 16); - key[16] = '\0'; - - // Force key to uppercase - unsigned char *s = key; - while (*s) { *s = toupper(*s); s++; } - - if (request->config->encryption_algorithm != NULL && - strncmp((char *)request->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) { - - fixed_url = aes_128_gcm_decrypt(r, key, eurl); - } else { - //Default is AES/ECB/PKCS5Padding - unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl)); - int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl); - fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length); - } - - if (fixed_url == NULL) { - return DIMS_DECRYPTION_FAILURE; - } - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "Decrypted URL: %s", fixed_url); - - break; - - } else if (strncmp(token, "optimizeResize=", 4) == 0) { - request->optimize_resize = atof(token + 15); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, request->r, "Overriding optimize resize: %f", request->optimize_resize); - } - token = apr_strtok(NULL, "&", &strtokstate); + // Determine the source image URL. + char *url = apr_hash_get(request->query_params, "url", APR_HASH_KEY_STRING); + char *eurl = apr_hash_get(request->query_params, "eurl", APR_HASH_KEY_STRING); + if (eurl != NULL) { + request->image_url = dims_decrypt_eurl(r, request->config->secret_key, eurl); + + if (request->image_url == NULL) { + return DIMS_DECRYPTION_FAILURE; } + } else if (url != NULL) { + request->image_url = dims_encode_spaces(r->pool, url); + } else { + return DIMS_BAD_URL; } - // Convert '+' in the fixed_url to ' '. - char *image_url = apr_pstrdup(request->r->pool, fixed_url); - s = image_url; - while (*s) { - if (*s == '+') { - *s = ' '; - } + // Check for optimizeResize parameter. + char *optimize_resize = apr_hash_get(request->query_params, "optimizeResize", APR_HASH_KEY_STRING); + if (optimize_resize != NULL) { + request->optimize_resize = atof(optimize_resize); - s++; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Overriding optimize resize: %f", request->optimize_resize); } - request->image_url = image_url; - request->request_hash = ap_md5(request->pool, - apr_pstrcat(request->pool, request->client_id, request->commands, request->image_url, NULL)); + request->request_hash = ap_md5(r->pool, + apr_pstrcat(r->pool, + request->client_id, + request->commands, + request->image_url, + NULL)); - /* Calculate image filename for use with content disposition. */ + // Calculate image filename for use with content disposition. apr_uri_t uri; if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { if (!uri.path) { @@ -154,7 +159,7 @@ dims_request_parse(dims_request_rec *request) } const char *path = apr_filepath_name_get(uri.path); - request->content_disposition_filename = apr_pstrdup(request->r->pool, path); + request->content_disposition_filename = apr_pstrdup(r->pool, path); } return DIMS_SUCCESS; diff --git a/src/mod_dims.c b/src/mod_dims.c index 6b451ea..87ff912 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -617,62 +617,43 @@ dims_process_image(dims_request_rec *d) int verify_dims4_signature(dims_request_rec *d) { - char *gen_hash; - - // Throw all query params and their values into a hash table. - // This is used to derive additional signature params. - apr_hash_t *params = apr_hash_make(d->pool); - - if (d->r->args) { - const size_t args_len = strlen(d->r->args) + 1; - char *args = apr_pstrndup(d->r->pool, d->r->args, args_len); - char *token; - char *strtokstate; + if(d->client_config->secret_key == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, + "Secret key not set for client '%s'", d->client_config->id); - token = apr_strtok(args, "&", &strtokstate); - while (token) { - char *param = strtok(token, "="); - apr_hash_set(params, param, APR_HASH_KEY_STRING, apr_pstrdup(d->r->pool, param + strlen(param) + 1)); - token = apr_strtok(NULL, "&", &strtokstate); - } + return DIMS_MISSING_SECRET; } // Standard signature params. - char *signature_params = apr_pstrcat(d->pool, d->expiration, d->client_config->secret_key, d->commands, d->image_url, NULL); + char *signature_params = apr_pstrcat(d->pool, + d->expiration, + d->client_config->secret_key, + d->commands, + d->image_url, + NULL); // Concatenate additional params. char *strtokstate = NULL; - char *keys = apr_hash_get(params, "_keys", APR_HASH_KEY_STRING); + char *keys = apr_hash_get(d->query_params, "_keys", APR_HASH_KEY_STRING); if (keys != NULL) { char *token = apr_strtok(keys, ",", &strtokstate); while (token) { - signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(params, token, APR_HASH_KEY_STRING), NULL); + signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(d->query_params, token, APR_HASH_KEY_STRING), NULL); token = apr_strtok(NULL, ",", &strtokstate); } } - // Hash. - gen_hash = ap_md5(d->pool, (unsigned char *) signature_params); - - if(d->client_config->secret_key == NULL) { - gen_hash[7] = '\0'; - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Secret key not set for client '%s'", d->client_config->id); - - return DIMS_MISSING_SECRET; - } else if (strncasecmp(d->signature, gen_hash, 6) != 0) { - gen_hash[7] = '\0'; + // Calculate the signature. + char *signature = ap_md5(d->pool, (unsigned char *) signature_params); + if (strncasecmp(d->signature, signature, 6) != 0) { + signature[7] = '\0'; ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Signature invalid: wanted %6s got %6s [%s?url=%s]", gen_hash, d->signature, d->r->uri, d->image_url); + "Signature invalid: wanted %6s got %6s [%s?url=%s]", signature, d->signature, d->r->uri, d->image_url); return DIMS_INVALID_SIGNATURE; } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Signature valid: '%s' (%s:%s)", d->signature, d->commands, d->image_url); - return OK; } diff --git a/src/request.h b/src/request.h index 46fbdab..6a9855f 100644 --- a/src/request.h +++ b/src/request.h @@ -43,6 +43,7 @@ typedef struct { /* The parsed commands with the signature and expiration timestamp removed. */ char *commands; + apr_hash_t *query_params; /* The source image. */ dims_image_data_t *source_image; From c0d707bef3a8db44867f0a40af557acbfeaa1d46 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 15:32:47 -0500 Subject: [PATCH 38/69] Remove stats logic This is only displayed on the /dims-status/ page. The complexity doesn't seem worth it. --- src/initialize.c | 47 +---------------------------------------------- src/mod_dims.c | 14 -------------- src/mod_dims.h | 10 ---------- src/status.c | 11 ----------- 4 files changed, 1 insertion(+), 81 deletions(-) diff --git a/src/initialize.c b/src/initialize.c index e0d8a3f..b7f9051 100644 --- a/src/initialize.c +++ b/src/initialize.c @@ -7,8 +7,6 @@ #include "curl.h" #include "module.h" -apr_shm_t *shm; - typedef struct operations { char *name; dims_operation_func *func; @@ -38,9 +36,7 @@ static operations ops[] = { int dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) { - dims_config_rec *config = (dims_config_rec *) - ap_get_module_config(s->module_config, &dims_module); - apr_status_t status; + dims_config_rec *config = (dims_config_rec *) ap_get_module_config(s->module_config, &dims_module); apr_size_t retsize; ap_add_version_component(p, "mod_dims/" MODULE_VERSION); @@ -51,47 +47,6 @@ dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) MagickSetResourceLimit(MemoryResource, config->memory_size); MagickSetResourceLimit(MapResource, config->map_size); - /* Init APR's atomic functions */ - status = apr_atomic_init(p); - if (status != APR_SUCCESS) - return HTTP_INTERNAL_SERVER_ERROR; - - /* Create shared memory block */ - status = apr_shm_create(&shm, sizeof(dims_stats_rec), NULL, p); - if (status != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error creating shm block\n"); - return status; - } - - /* Check size of shared memory block */ - retsize = apr_shm_size_get(shm); - if (retsize != sizeof(dims_stats_rec)) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error allocating shared memory block\n"); - return status; - } - - /* Init shm block */ - stats = apr_shm_baseaddr_get(shm); - if (stats == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_dims : Error creating status block.\n"); - return status; - } - memset(stats, 0, retsize); - - if (retsize < sizeof(dims_stats_rec)) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_dims : Not enough memory allocated!! Giving up"); - return HTTP_INTERNAL_SERVER_ERROR; - } - - stats->success_count = 1; - stats->failure_count = 0; - stats->download_timeout_count = 0; - stats->imagemagick_timeout_count = 0; - return OK; } diff --git a/src/mod_dims.c b/src/mod_dims.c index 87ff912..6cb2fdb 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -52,7 +52,6 @@ typedef struct { apr_status_t error; } dims_processed_image; -dims_stats_rec *stats; apr_hash_t *ops; /** @@ -318,19 +317,6 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) ap_rflush(d->r); - /* After the image is sent record stats about this request. */ - if(d->status == DIMS_SUCCESS) { - apr_atomic_inc32(&stats->success_count); - } else { - apr_atomic_inc32(&stats->failure_count); - } - - if(d->status == DIMS_DOWNLOAD_TIMEOUT) { - apr_atomic_inc32(&stats->download_timeout_count); - } else if(d->status == DIMS_IMAGEMAGICK_TIMEOUT) { - apr_atomic_inc32(&stats->imagemagick_timeout_count); - } - /* Record metrics for logging. */ snprintf(buf, 128, "%d", d->status); apr_table_set(d->r->notes, "DIMS_STATUS", buf); diff --git a/src/mod_dims.h b/src/mod_dims.h index 368b42d..9a9ac78 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -56,17 +56,7 @@ #define DIMS_MISSING_SECRET 1007 #define DIMS_DECRYPTION_FAILURE 1008 - apr_status_t dims_handle_dims3(dims_request_rec *d); apr_status_t dims_handle_dims4(dims_request_rec *d); -typedef struct { - apr_uint32_t success_count; - apr_uint32_t failure_count; - apr_uint32_t download_timeout_count; - apr_uint32_t imagemagick_timeout_count; -} dims_stats_rec; - -extern dims_stats_rec *stats; - #endif diff --git a/src/status.c b/src/status.c index b9a6aeb..93f56dc 100644 --- a/src/status.c +++ b/src/status.c @@ -47,17 +47,6 @@ status_handler(request_rec *r) { ap_rprintf(r, "ImageMagick version: %s\n", GetMagickVersion(NULL)); ap_rprintf(r, "libcurl version: %s\n", curl_version()); - ap_rprintf(r, "\nDetails\n-------\n"); - - ap_rprintf(r, "Successful requests: %d\n", - apr_atomic_read32(&stats->success_count)); - ap_rprintf(r, "Failed requests: %d\n\n", - apr_atomic_read32(&stats->failure_count)); - ap_rprintf(r, "Download timeouts: %d\n", - apr_atomic_read32(&stats->download_timeout_count)); - ap_rprintf(r, "Imagemagick Timeouts: %d\n", - apr_atomic_read32(&stats->imagemagick_timeout_count)); - ap_rflush(r); return OK; From 78562ee6234ba1183ab56f7c4299244d131c6029 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 16:21:29 -0500 Subject: [PATCH 39/69] Fix global strip operation --- src/mod_dims.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 6cb2fdb..0cd5e3c 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -52,8 +52,6 @@ typedef struct { apr_status_t error; } dims_processed_image; -apr_hash_t *ops; - /** * This callback is called by the MagicWand API during transformation * operations. How often it's called is dependent on the operation @@ -567,7 +565,7 @@ dims_process_image(dims_request_rec *d) * If the strip command was not executed from the loop, call it anyway with NULL args */ if(!exc_strip_cmd) { - dims_operation_func *strip_func = apr_hash_get(ops, "strip", APR_HASH_KEY_STRING); + dims_operation_func *strip_func = dims_operation_lookup("strip"); if(strip_func != NULL) { char *err = NULL; apr_status_t code; From 6393b96b8ff470e97c49fe926d7e51ac6ac407e9 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 17:13:29 -0500 Subject: [PATCH 40/69] Preparse commands The command string was being parsed in several different places in the code. Now it's done up front during dims_request_rec setup. --- src/handler.c | 25 +++++++++++- src/mod_dims.c | 98 ++++++++++++---------------------------------- src/mod_dims_ops.c | 19 --------- src/request.h | 9 +++-- 4 files changed, 55 insertions(+), 96 deletions(-) diff --git a/src/handler.c b/src/handler.c index 39fd443..5da1729 100644 --- a/src/handler.c +++ b/src/handler.c @@ -36,6 +36,7 @@ dims_create_request(request_rec *r) request->optimize_resize = config->optimize_resize; request->send_content_disposition = 0; request->content_disposition_filename = NULL; + request->commands_list = apr_array_make(r->pool, 10, sizeof(dims_command_t)); return request; } @@ -102,19 +103,39 @@ dims_decrypt_eurl(request_rec *r, unsigned char *secret_key, char *eurl) return aes_128_gcm_decrypt(r, key, eurl); } +static apr_array_header_t * +dims_parse_commands(dims_request_rec *request, char *commands) { + apr_array_header_t *commands_list = apr_array_make(request->pool, 10, sizeof(dims_command_t)); + + const char *cmds = commands; + while(cmds < commands + strlen(commands)) { + char *command = ap_getword(request->pool, &cmds, '/'); + + if(strlen(command) > 0) { + char *args = ap_getword(request->pool, &cmds, '/'); + dims_command_t *cmd = (dims_command_t *) apr_palloc(request->pool, sizeof(dims_command_t)); + cmd->name = command; + cmd->arg = dims_encode_spaces(request->pool, args); + + APR_ARRAY_PUSH(commands_list, dims_command_t) = *cmd; + } + } + + return commands_list; +} + static apr_status_t dims_request_parse(dims_request_rec *request) { request_rec *r = request->r; char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); - request->unparsed_commands = unparsed_commands; - request->client_id = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); request->signature = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); request->expiration = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); request->commands = dims_encode_spaces(r->pool, unparsed_commands); request->query_params = dims_parse_args(r); + request->commands_list = dims_parse_commands(request, request->commands); char *download = apr_hash_get(request->query_params, "download", APR_HASH_KEY_STRING); if (download != NULL && *download == '1') { diff --git a/src/mod_dims.c b/src/mod_dims.c index 0cd5e3c..5f33063 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -356,26 +356,20 @@ dims_set_optimal_geometry(dims_request_rec *d) { MagickStatusType flags; RectangleInfo rec; - const char *cmds = d->commands; /* Process operations. */ - while(cmds < d->commands + strlen(d->commands)) { - char *command = ap_getword(d->pool, &cmds, '/'); + for (int i = 0; i < d->commands_list->nelts; i++) { + dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); - if(strcmp(command, "resize") == 0 || - strcmp(command, "legacy_thumbnail") == 0 || - strcmp(command, "thumbnail") == 0) { - char *args = ap_getword(d->pool, &cmds, '/'); + if(strcmp(cmd->name, "resize") == 0 || + strcmp(cmd->name, "legacy_thumbnail") == 0 || + strcmp(cmd->name, "thumbnail") == 0) { - flags = ParseAbsoluteGeometry(args, &rec); + flags = ParseAbsoluteGeometry(cmd->arg, &rec); if(flags & WidthValue && flags & HeightValue && !(flags & PercentValue)) { MagickSetSize(d->wand, rec.width, rec.height); return; } - } else { - if(strcmp(command, "") != 0) { - ap_getword(d->pool, &cmds, '/'); - } } } } @@ -449,11 +443,10 @@ dims_process_image(dims_request_rec *d) bool should_flatten = false; if (images > 1) { - const char *cmds = d->unparsed_commands; - while(cmds < d->unparsed_commands + strlen(d->unparsed_commands)) { - char *command = ap_getword(d->pool, &cmds, '/'); + for (int i = 0; i < d->commands_list->nelts; i++) { + dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); - if (strcmp(command, "watermark") == 0) { + if (strcmp(cmd->name, "watermark") == 0) { should_flatten = true; break; } @@ -475,69 +468,30 @@ dims_process_image(dims_request_rec *d) if (images == 1 || should_flatten) { bool output_format_provided = false; - const char *cmds = d->commands; - while(cmds < d->commands + strlen(d->commands)) { - char *command = ap_getword(d->pool, &cmds, '/'); - if (strcmp(command, "format") == 0) { + for (int i = 0; i < d->commands_list->nelts; i++) { + dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); + + if (strcmp(cmd->name, "format") == 0) { output_format_provided = true; } - - if(strlen(command) > 0) { - char *args = ap_getword(d->pool, &cmds, '/'); - - /* If the NOIMAGE image is being used for some reason then - * we don't want to crop it. - */ - if(d->use_no_image && - (strcmp(command, "crop") == 0 || - strcmp(command, "legacy_thumbnail") == 0 || - strcmp(command, "legacy_crop") == 0 || - strcmp(command, "thumbnail") == 0)) { - RectangleInfo rec; - - ParseAbsoluteGeometry(args, &rec); - - if(rec.width > 0 && rec.height == 0) { - args = apr_psprintf(d->pool, "%ld", rec.width); - } else if(rec.height > 0 && rec.width == 0) { - args = apr_psprintf(d->pool, "x%ld", rec.height); - } else if(rec.width > 0 && rec.height > 0) { - args = apr_psprintf(d->pool, "%ldx%ld", rec.width, rec.height); - } else { - DestroyMagickWand(d->wand); - image->error = DIMS_BAD_ARGUMENTS; - return image; - } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Rewriting command %s to 'resize' because a NOIMAGE " - "image is being processed.", command); + if(strcmp(cmd->name, "strip") == 0) { + exc_strip_cmd = 1; + } - command = apr_psprintf(d->pool, "%s", "resize"); - } + dims_operation_func *func = dims_operation_lookup(cmd->name); + if (func != NULL) { + char *err = NULL; + apr_status_t code; - // Check if the command is present and set flag. - if(strcmp(command, "strip") == 0) { - exc_strip_cmd = 1; - } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, + "Executing command %s(%s), on request %s", cmd->name, cmd->arg, d->r->uri); - dims_operation_func *func = dims_operation_lookup(command); - if (func != NULL) - { - char *err = NULL; - apr_status_t code; - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Executing command %s(%s), on request %s", - command, args, d->r->uri); - - if((code = func(d, args, &err)) != DIMS_SUCCESS) { - DestroyMagickWand(d->wand); - image->error = code; - return image; - ; - } + if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { + DestroyMagickWand(d->wand); + image->error = code; + return image; } } diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index bc9e94a..a7fb3ad 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -233,25 +233,6 @@ dims_crop_operation (dims_request_rec *d, char *args, char **err) { RectangleInfo rec; ExceptionInfo *ex_info = AcquireExceptionInfo(); - /* Replace spaces with '+'. This happens when some user agents inadvertantly - * escape the '+' as %20 which gets converted to a space. - * - * Example: - * - * 900x900%20350%200 is '900x900 350 0' which is an invalid, the following code - * coverts this to '900x900+350+0'. - * - */ - char *s = args; - while (*s) { - if (*s == ' ') { - *s = '+'; - } - - s++; - } - - flags = ParseGravityGeometry(GetImageFromMagickWand(d->wand), args, &rec, ex_info); if(!(flags & AllValues)) { DestroyExceptionInfo(ex_info); diff --git a/src/request.h b/src/request.h index 6a9855f..b3e1fda 100644 --- a/src/request.h +++ b/src/request.h @@ -20,6 +20,11 @@ typedef struct { apr_pool_t *pool; } dims_image_data_t; +typedef struct { + char *name; + char *arg; +} dims_command_t; + typedef struct { request_rec *r; @@ -38,12 +43,10 @@ typedef struct { /* The URL to the NOIMAGE image in case of failures. */ char *no_image_url; - /* The unparsed commands (resize, crop, etc). */ - char *unparsed_commands; - /* The parsed commands with the signature and expiration timestamp removed. */ char *commands; apr_hash_t *query_params; + apr_array_header_t *commands_list; /* The source image. */ dims_image_data_t *source_image; From 82748ddb43f43e9d3bb51369aaa409bab04027f8 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 18:28:14 -0500 Subject: [PATCH 41/69] Simplify handler.c This moves URL parsing logic into mod_dims.c leaving dims_handler() to do the bare minimum, call dims3, dims4, or dims-status. --- src/handler.c | 214 +---------------------------------------- src/mod_dims.c | 255 +++++++++++++++++++++++++++++++++++++++++++++---- src/mod_dims.h | 6 +- 3 files changed, 239 insertions(+), 236 deletions(-) diff --git a/src/handler.c b/src/handler.c index 5da1729..df1bace 100644 --- a/src/handler.c +++ b/src/handler.c @@ -4,7 +4,6 @@ #include #include #include -#include #include "mod_dims.h" #include "handler.h" @@ -14,194 +13,7 @@ #include "encryption.h" #include "status.h" -static dims_request_rec * -dims_create_request(request_rec *r) -{ - dims_request_rec *request = (dims_request_rec *) apr_palloc(r->pool, sizeof(dims_request_rec)); - dims_config_rec *config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); - - request->r = r; - request->pool = r->pool; - request->wand = NULL; - request->config = config; - request->client_config = NULL; - request->no_image_url = request->config->no_image_url; - request->use_no_image = 0; - request->image_url = NULL; - request->request_hash = NULL; - request->status = DIMS_SUCCESS; - request->start_time = apr_time_now(); - request->download_time = 0; - request->imagemagick_time = 0; - request->optimize_resize = config->optimize_resize; - request->send_content_disposition = 0; - request->content_disposition_filename = NULL; - request->commands_list = apr_array_make(r->pool, 10, sizeof(dims_command_t)); - - return request; -} - -static char * -dims_encode_spaces(apr_pool_t *pool, char *str) -{ - char *copy = apr_pstrdup(pool, str); - - char *s = copy; - while (*s) { - if (*s == ' ') { - *s = '+'; - } - - s++; - } - - return copy; -} - -static apr_hash_t * -dims_parse_args(request_rec *r) -{ - apr_hash_t *query_params = apr_hash_make(r->pool); - - const size_t args_len = strlen(r->args) + 1; - char *args = apr_pstrndup(r->pool, r->args, args_len); - char *token; - char *strtokstate; - - token = apr_strtok(args, "&", &strtokstate); - while (token) { - char *param = strtok(token, "="); - apr_hash_set(query_params, param, APR_HASH_KEY_STRING, apr_pstrdup(r->pool, param + strlen(param) + 1)); - token = apr_strtok(NULL, "&", &strtokstate); - } - - return query_params; -} - -static char * -dims_decrypt_eurl(request_rec *r, unsigned char *secret_key, char *eurl) -{ - // Hash secret via SHA-1. - unsigned char hash[SHA_DIGEST_LENGTH]; - SHA1(secret_key, strlen((char *) secret_key), hash); - - // Convert to hex. - char hex[SHA_DIGEST_LENGTH * 2 + 1]; - if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { - return NULL; - } - - // Use first 16 bytes. - unsigned char key[17]; - strncpy((char *) key, hex, 16); - key[16] = '\0'; - - // Force key to uppercase - unsigned char *s = key; - while (*s) { *s = toupper(*s); s++; } - - return aes_128_gcm_decrypt(r, key, eurl); -} - -static apr_array_header_t * -dims_parse_commands(dims_request_rec *request, char *commands) { - apr_array_header_t *commands_list = apr_array_make(request->pool, 10, sizeof(dims_command_t)); - - const char *cmds = commands; - while(cmds < commands + strlen(commands)) { - char *command = ap_getword(request->pool, &cmds, '/'); - - if(strlen(command) > 0) { - char *args = ap_getword(request->pool, &cmds, '/'); - dims_command_t *cmd = (dims_command_t *) apr_palloc(request->pool, sizeof(dims_command_t)); - cmd->name = command; - cmd->arg = dims_encode_spaces(request->pool, args); - - APR_ARRAY_PUSH(commands_list, dims_command_t) = *cmd; - } - } - - return commands_list; -} - -static apr_status_t -dims_request_parse(dims_request_rec *request) -{ - request_rec *r = request->r; - - char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); - request->client_id = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); - request->signature = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); - request->expiration = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); - request->commands = dims_encode_spaces(r->pool, unparsed_commands); - request->query_params = dims_parse_args(r); - request->commands_list = dims_parse_commands(request, request->commands); - - char *download = apr_hash_get(request->query_params, "download", APR_HASH_KEY_STRING); - if (download != NULL && *download == '1') { - request->send_content_disposition = 1; - } - - // Determine the source image URL. - char *url = apr_hash_get(request->query_params, "url", APR_HASH_KEY_STRING); - char *eurl = apr_hash_get(request->query_params, "eurl", APR_HASH_KEY_STRING); - if (eurl != NULL) { - request->image_url = dims_decrypt_eurl(r, request->config->secret_key, eurl); - - if (request->image_url == NULL) { - return DIMS_DECRYPTION_FAILURE; - } - } else if (url != NULL) { - request->image_url = dims_encode_spaces(r->pool, url); - } else { - return DIMS_BAD_URL; - } - - // Check for optimizeResize parameter. - char *optimize_resize = apr_hash_get(request->query_params, "optimizeResize", APR_HASH_KEY_STRING); - if (optimize_resize != NULL) { - request->optimize_resize = atof(optimize_resize); - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Overriding optimize resize: %f", request->optimize_resize); - } - - request->request_hash = ap_md5(r->pool, - apr_pstrcat(r->pool, - request->client_id, - request->commands, - request->image_url, - NULL)); - - // Calculate image filename for use with content disposition. - apr_uri_t uri; - if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { - if (!uri.path) { - return DIMS_BAD_URL; - } - - const char *path = apr_filepath_name_get(uri.path); - request->content_disposition_filename = apr_pstrdup(r->pool, path); - } - - return DIMS_SUCCESS; -} - -/** - * The apache handler. Apache will call this method when a request - * for /dims/, /dims3/, /dims4/ or an image is recieved. - * - * Depending on how this function is called it will do one of three - * things: - * - * 1) Transform old-style request into a new-style request and - * pass it along to the dims_handle_newstyle function. - * - * 2) Parse out the URL and commands and pass them along - * to the dims_handle_newstyle function. - * - * 3) Load the image from the filesystem and pass it along - * with the commands (r->path_info) to dims_process_image. - */ +// Called by Apache httpd per request. apr_status_t dims_handler(request_rec *r) { @@ -215,30 +27,10 @@ dims_handler(request_rec *r) return DECLINED; } - dims_request_rec *d = dims_create_request(r); - int status = dims_request_parse(d); - if (status != DIMS_SUCCESS) { - return status; - } - - /* Set initial notes to be logged by mod_log_config. */ - apr_table_setn(r->notes, "DIMS_STATUS", "0"); - apr_table_setn(r->notes, "DIMS_ORIG_BYTES", "-"); - apr_table_setn(r->notes, "DIMS_DL_TIME", "-"); - apr_table_setn(r->notes, "DIMS_IM_TIME", "-"); - - if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return DIMS_BAD_CLIENT; - } - - if(d->client_config && d->client_config->no_image_url) { - d->no_image_url = d->client_config->no_image_url; - } - if ((strcmp(r->handler, "dims3") == 0)) { - return dims_handle_dims3(d); + return dims_handle_dims3(r); } else if (strcmp(r->handler, "dims4") == 0) { - return dims_handle_dims4(d); + return dims_handle_dims4(r); } return DECLINED; diff --git a/src/mod_dims.c b/src/mod_dims.c index 5f33063..2bcb11d 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -31,6 +31,7 @@ #include #include #include +#include #define MAGICK_CHECK(func, d) \ do {\ @@ -315,25 +316,6 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) ap_rflush(d->r); - /* Record metrics for logging. */ - snprintf(buf, 128, "%d", d->status); - apr_table_set(d->r->notes, "DIMS_STATUS", buf); - - snprintf(buf, 128, "%ld", d->source_image->used); - apr_table_set(d->r->notes, "DIMS_ORIG_BYTES", buf); - - snprintf(buf, 128, "%ld", d->download_time); - apr_table_set(d->r->notes, "DIMS_DL_TIME", buf); - - snprintf(buf, 128, "%ld", (apr_time_now() - d->start_time) / 1000); - apr_table_set(d->r->notes, "DIMS_TOTAL_TIME", buf); - - if(d->status != DIMS_DOWNLOAD_TIMEOUT && - d->status != DIMS_IMAGEMAGICK_TIMEOUT) { - snprintf(buf, 128, "%ld", d->imagemagick_time); - apr_table_set(d->r->notes, "DIMS_IM_TIME", buf); - } - return OK; } @@ -637,6 +619,14 @@ verify_dims3_allowlist(dims_request_rec *d) { static apr_status_t dims_handle_request(dims_request_rec *d) { + char buf[128]; + + /* Set initial notes to be logged by mod_log_config. */ + apr_table_setn(d->r->notes, "DIMS_STATUS", "0"); + apr_table_setn(d->r->notes, "DIMS_ORIG_BYTES", "-"); + apr_table_setn(d->r->notes, "DIMS_DL_TIME", "-"); + apr_table_setn(d->r->notes, "DIMS_IM_TIME", "-"); + // Download image. apr_status_t status = dims_download_source_image(d, d->image_url); if (status != DIMS_SUCCESS) { @@ -657,12 +647,221 @@ dims_handle_request(dims_request_rec *d) return status; } + /* Record metrics for logging. */ + snprintf(buf, 128, "%d", d->status); + apr_table_set(d->r->notes, "DIMS_STATUS", buf); + + snprintf(buf, 128, "%ld", d->source_image->used); + apr_table_set(d->r->notes, "DIMS_ORIG_BYTES", buf); + + snprintf(buf, 128, "%ld", d->download_time); + apr_table_set(d->r->notes, "DIMS_DL_TIME", buf); + + snprintf(buf, 128, "%ld", (apr_time_now() - d->start_time) / 1000); + apr_table_set(d->r->notes, "DIMS_TOTAL_TIME", buf); + + if(d->status != DIMS_DOWNLOAD_TIMEOUT && + d->status != DIMS_IMAGEMAGICK_TIMEOUT) { + snprintf(buf, 128, "%ld", d->imagemagick_time); + apr_table_set(d->r->notes, "DIMS_IM_TIME", buf); + } + return HTTP_OK; } +static dims_request_rec * +dims_create_request(request_rec *r) +{ + dims_request_rec *request = (dims_request_rec *) apr_palloc(r->pool, sizeof(dims_request_rec)); + dims_config_rec *config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); + + request->r = r; + request->pool = r->pool; + request->wand = NULL; + request->config = config; + request->client_config = NULL; + request->no_image_url = request->config->no_image_url; + request->use_no_image = 0; + request->image_url = NULL; + request->request_hash = NULL; + request->status = DIMS_SUCCESS; + request->start_time = apr_time_now(); + request->download_time = 0; + request->imagemagick_time = 0; + request->optimize_resize = config->optimize_resize; + request->send_content_disposition = 0; + request->content_disposition_filename = NULL; + request->commands_list = apr_array_make(r->pool, 10, sizeof(dims_command_t)); + + return request; +} + +static char * +dims_encode_spaces(apr_pool_t *pool, char *str) +{ + char *copy = apr_pstrdup(pool, str); + + char *s = copy; + while (*s) { + if (*s == ' ') { + *s = '+'; + } + + s++; + } + + return copy; +} + +static apr_hash_t * +dims_parse_args(request_rec *r) +{ + apr_hash_t *query_params = apr_hash_make(r->pool); + + const size_t args_len = strlen(r->args) + 1; + char *args = apr_pstrndup(r->pool, r->args, args_len); + char *token; + char *strtokstate; + + token = apr_strtok(args, "&", &strtokstate); + while (token) { + char *param = strtok(token, "="); + apr_hash_set(query_params, param, APR_HASH_KEY_STRING, apr_pstrdup(r->pool, param + strlen(param) + 1)); + token = apr_strtok(NULL, "&", &strtokstate); + } + + return query_params; +} + +static apr_array_header_t * +dims_parse_commands(dims_request_rec *request, char *commands) { + apr_array_header_t *commands_list = apr_array_make(request->pool, 10, sizeof(dims_command_t)); + + const char *cmds = commands; + while(cmds < commands + strlen(commands)) { + char *command = ap_getword(request->pool, &cmds, '/'); + + if(strlen(command) > 0) { + char *args = ap_getword(request->pool, &cmds, '/'); + dims_command_t *cmd = (dims_command_t *) apr_palloc(request->pool, sizeof(dims_command_t)); + cmd->name = command; + cmd->arg = dims_encode_spaces(request->pool, args); + + APR_ARRAY_PUSH(commands_list, dims_command_t) = *cmd; + } + } + + return commands_list; +} + +static char * +dims_decrypt_eurl(request_rec *r, unsigned char *secret_key, char *eurl) +{ + // Hash secret via SHA-1. + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1(secret_key, strlen((char *) secret_key), hash); + + // Convert to hex. + char hex[SHA_DIGEST_LENGTH * 2 + 1]; + if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) { + return NULL; + } + + // Use first 16 bytes. + unsigned char key[17]; + strncpy((char *) key, hex, 16); + key[16] = '\0'; + + // Force key to uppercase + unsigned char *s = key; + while (*s) { *s = toupper(*s); s++; } + + return aes_128_gcm_decrypt(r, key, eurl); +} + +static apr_status_t +dims_request_parse(dims_request_rec *request, int dims4) +{ + request_rec *r = request->r; + + char *unparsed_commands = apr_pstrdup(r->pool, r->uri + 7); + request->client_id = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + request->query_params = dims_parse_args(r); + + if (dims4) { + request->signature = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + request->expiration = ap_getword(r->pool, (const char **) &unparsed_commands, '/'); + } + + request->commands = dims_encode_spaces(r->pool, unparsed_commands); + request->commands_list = dims_parse_commands(request, request->commands); + + char *download = apr_hash_get(request->query_params, "download", APR_HASH_KEY_STRING); + if (download != NULL && *download == '1') { + request->send_content_disposition = 1; + } + + // Determine the source image URL. + char *url = apr_hash_get(request->query_params, "url", APR_HASH_KEY_STRING); + char *eurl = apr_hash_get(request->query_params, "eurl", APR_HASH_KEY_STRING); + if (eurl != NULL) { + request->image_url = dims_decrypt_eurl(r, request->config->secret_key, eurl); + + if (request->image_url == NULL) { + return DIMS_DECRYPTION_FAILURE; + } + } else if (url != NULL) { + request->image_url = dims_encode_spaces(r->pool, url); + } else { + return DIMS_BAD_URL; + } + + // Check for optimizeResize parameter. + char *optimize_resize = apr_hash_get(request->query_params, "optimizeResize", APR_HASH_KEY_STRING); + if (optimize_resize != NULL) { + request->optimize_resize = atof(optimize_resize); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Overriding optimize resize: %f", request->optimize_resize); + } + + request->request_hash = ap_md5(r->pool, + apr_pstrcat(r->pool, + request->client_id, + request->commands, + request->image_url, + NULL)); + + // Calculate image filename for use with content disposition. + apr_uri_t uri; + if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { + if (!uri.path) { + return DIMS_BAD_URL; + } + + const char *path = apr_filepath_name_get(uri.path); + request->content_disposition_filename = apr_pstrdup(r->pool, path); + } + + return DIMS_SUCCESS; +} + apr_status_t -dims_handle_dims3(dims_request_rec *d) +dims_handle_dims3(request_rec *r) { + dims_request_rec *d = dims_create_request(r); + int status = dims_request_parse(d, 0); + if (status != DIMS_SUCCESS) { + return status; + } + + if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return DIMS_BAD_CLIENT; + } + + if(d->client_config && d->client_config->no_image_url) { + d->no_image_url = d->client_config->no_image_url; + } + // Verify allowlist (dims3 only). if (verify_dims3_allowlist(d)) { return HTTP_UNAUTHORIZED; @@ -672,8 +871,22 @@ dims_handle_dims3(dims_request_rec *d) } apr_status_t -dims_handle_dims4(dims_request_rec *d) +dims_handle_dims4(request_rec *r) { + dims_request_rec *d = dims_create_request(r); + int status = dims_request_parse(d, 1); + if (status != DIMS_SUCCESS) { + return status; + } + + if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + return DIMS_BAD_CLIENT; + } + + if(d->client_config && d->client_config->no_image_url) { + d->no_image_url = d->client_config->no_image_url; + } + // Verify signature (dims4 only). if (verify_dims4_signature(d)) { return HTTP_UNAUTHORIZED; diff --git a/src/mod_dims.h b/src/mod_dims.h index 9a9ac78..f9a3b84 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -40,8 +40,6 @@ #include -#include "request.h" - #define DIMS_IGNORE -1 #define DIMS_SUCCESS 200 #define DIMS_FAILURE 500 @@ -56,7 +54,7 @@ #define DIMS_MISSING_SECRET 1007 #define DIMS_DECRYPTION_FAILURE 1008 -apr_status_t dims_handle_dims3(dims_request_rec *d); -apr_status_t dims_handle_dims4(dims_request_rec *d); +apr_status_t dims_handle_dims3(request_rec *d); +apr_status_t dims_handle_dims4(request_rec *d); #endif From b34b6ce111c766c133f566e60fc875f1e63e3b96 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 18:33:30 -0500 Subject: [PATCH 42/69] Name structs --- src/mod_dims.c | 4 ++-- src/request.h | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 2bcb11d..c4fc6fa 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -41,12 +41,12 @@ return d->status; \ } while(0); -typedef struct { +typedef struct dims_progress_rec { dims_request_rec *d; apr_time_t start_time; } dims_progress_rec; -typedef struct { +typedef struct dims_processed_image { size_t length; unsigned char *bytes; char *format; diff --git a/src/request.h b/src/request.h index b3e1fda..c2b4798 100644 --- a/src/request.h +++ b/src/request.h @@ -6,7 +6,7 @@ #include "configuration.h" -typedef struct { +typedef struct dims_image_data_t { char *data; size_t size; size_t used; @@ -17,15 +17,14 @@ typedef struct { char *edge_control; char *last_modified; char *etag; - apr_pool_t *pool; } dims_image_data_t; -typedef struct { +typedef struct dims_command_t { char *name; char *arg; } dims_command_t; -typedef struct { +typedef struct dims_request_rec { request_rec *r; apr_pool_t *pool; From be91ef44bc78777c0f0979192342eb6dbf64b160 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 18:35:59 -0500 Subject: [PATCH 43/69] Remove config methods for encoded fetch This is no longer used. --- src/configuration.c | 10 ---------- src/configuration.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/src/configuration.c b/src/configuration.c index 70e2eac..b05b9fb 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -26,7 +26,6 @@ dims_create_config(apr_pool_t *p, server_rec *s) config->strip_metadata = 1; config->optimize_resize = 0; - config->disable_encoded_fetch = 0; config->default_output_format = NULL; config->area_size = 128 * 1024 * 1024; // 128mb max. @@ -163,15 +162,6 @@ dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg) return NULL; } -const char * -dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->disable_encoded_fetch = atoi(arg); - return NULL; -} - const char * dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg) { diff --git a/src/configuration.h b/src/configuration.h index 3e644d5..2bcbf14 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -19,7 +19,6 @@ const char *dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, con const char *dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg); @@ -46,7 +45,6 @@ struct dims_config_rec { int strip_metadata; float optimize_resize; int include_disposition; - int disable_encoded_fetch; char *default_output_format; MagickSizeType area_size; From 57f6dae06ff899ea417e44e7a97c01dff20a6d90 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 19:47:20 -0500 Subject: [PATCH 44/69] Improve dims_send_image This method is mostly calculating cache headers. Source image max-age parse logic was moved to source image loading. The http status logic was moved to a method and is returned by dims_handle_request. --- .devcontainer/Dockerfile | 1 + Dockerfile | 1 + src/configuration.c | 6 ++ src/curl.c | 17 ++++ src/mod_dims.c | 177 ++++++++++++--------------------------- src/request.h | 1 + 6 files changed, 78 insertions(+), 125 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 07d1aab..daa90a9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,6 +7,7 @@ ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true +ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 diff --git a/Dockerfile b/Dockerfile index 3b83030..5e93523 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ ENV DIMS_IMAGEMAGICK_TIMEOUT=20000 ENV DIMS_CACHE_CONTROL_MAX_AGE=604800 ENV DIMS_EDGE_CONTROL_DOWNSTREAM_TTL=604800 ENV DIMS_TRUST_SOURCE=true +ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 diff --git a/src/configuration.c b/src/configuration.c index b05b9fb..af6263a 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -263,6 +263,12 @@ dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) case 1: client_config->id = argv[0]; } + + // The min and max values for 'max-age' must be set in order to trust this client. + if(client_config->min_src_cache_control == -1 && client_config->max_src_cache_control == -1) { + client_config->trust_src = 0; + } + } apr_hash_set(config->clients, argv[0], APR_HASH_KEY_STRING, client_config); diff --git a/src/curl.c b/src/curl.c index 414da2c..e45eca2 100644 --- a/src/curl.c +++ b/src/curl.c @@ -131,6 +131,23 @@ dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) if(key && value && strcmp(key, "Cache-Control") == 0) { d->source_image->cache_control = value; + + char *src_header = value; + char *src_start = src_header; + int src_len = strlen(src_header); + + // Ex. max-age=3600 + while(src_header < (src_start + src_len)) { + if(*src_header == '=') { + src_header++; + while(*src_header == ' ') { + src_header++; + } + + d->source_image->max_age = atoi(src_header); + } + src_header++; + } } else if(key && value && strcmp(key, "Edge-Control") == 0) { d->source_image->edge_control = value; } else if(key && value && strcmp(key, "Last-Modified") == 0) { diff --git a/src/mod_dims.c b/src/mod_dims.c index c4fc6fa..9108403 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -141,135 +141,63 @@ dims_download_source_image(dims_request_rec *d, const char *url) return DIMS_SUCCESS; } -static apr_status_t -dims_send_image(dims_request_rec *d, dims_processed_image *image) +static apr_status_t +dims_status_to_http_code(dims_request_rec *d) { - char buf[128]; - char *content_type; - int expire_time = 0; - - char *cache_control = NULL, - *edge_control = NULL; - - // variables referring to the src image - char *src_header; - char *src_start; - int src_len; - - char *src_max_age_str; - int src_max_age = 0; - - int trust_src_img = 0; - - char *format = image->format; - unsigned char *blob = image->bytes; - size_t length = image->length; - - /* Set the Content-Type based on the image format. */ - content_type = apr_psprintf(d->pool, "image/%s", format); - ap_content_type_tolower(content_type); - ap_set_content_type(d->r, content_type); - if(d->status == DIMS_FILE_NOT_FOUND) { - d->r->status = HTTP_NOT_FOUND; + return HTTP_NOT_FOUND; } else if (d->source_image->response_code != 0) { - d->r->status = d->source_image->response_code; + return d->source_image->response_code; } else if (d->status != DIMS_SUCCESS) { - if (d->status == DIMS_BAD_URL - || d->status == DIMS_BAD_ARGUMENTS) { - d->r->status = HTTP_BAD_REQUEST; + if (d->status == DIMS_BAD_URL || d->status == DIMS_BAD_ARGUMENTS) { + return HTTP_BAD_REQUEST; } else { //Includes DIMS_BAD_CLIENT, DIMS_DOWNLOAD_TIMEOUT, DIMS_IMAGEMAGICK_TIMEOUT, DIMS_HOSTNAME_NOT_IN_WHITELIST - d->r->status = HTTP_INTERNAL_SERVER_ERROR; + return HTTP_INTERNAL_SERVER_ERROR; } } - if (blob == NULL) { - d->r->status = HTTP_BAD_REQUEST; - } - - if(d->status == DIMS_SUCCESS && d->source_image->response_code == 200 && d->client_config) { - - // if the src image has a cache_control header, parse out the max-age - if(d->source_image->cache_control) { - - // Ex. max-age=3600 - src_header = d->source_image->cache_control; - src_start = src_header; - src_len = strlen(src_header); - - while(src_header < (src_start + src_len)) { - if(*src_header == '=') { - src_header++; - while(*src_header == ' ') { - src_header++; - } - src_max_age_str = apr_pstrdup(d->pool, src_header); - src_max_age = atoi(src_max_age_str); - } - src_header++; - } - } - - // if we trust the src image and were able to parse its cache header - if(d->client_config->trust_src && src_max_age > 0) { - - // if the min and max config values were valid - if(d->client_config->min_src_cache_control >= -1 && - d->client_config->max_src_cache_control >= -1) { - - // if the max-age value is between the min and max, use the src value - if( (d->client_config->min_src_cache_control == -1 || - src_max_age >= d->client_config->min_src_cache_control) && - (d->client_config->max_src_cache_control == -1 || - src_max_age <= d->client_config->max_src_cache_control)) { + return OK; +} - trust_src_img = 1; - } - else { // use the client configred default - trust_src_img = 0; - } - } - else { // invalid max/min, use defaults - trust_src_img = 0; - } - } - else { // don't trust src, and use client configured default - trust_src_img = 0; - } +static apr_status_t +dims_send_image(dims_request_rec *d, dims_processed_image *image) +{ + request_rec *r = d->r; + // Set the Content-Type based on the image format. + char *format = image->format; + char *content_type = apr_psprintf(r->pool, "image/%s", format); + ap_content_type_tolower(content_type); + ap_set_content_type(r, content_type); - if(trust_src_img) { - cache_control = apr_psprintf(d->pool, "max-age=%d, public", src_max_age); - if(d->client_config->edge_control_downstream_ttl != -1) { - edge_control = apr_psprintf(d->pool, "downstream-ttl=%d", src_max_age); - } - expire_time = src_max_age; - } - else { - cache_control = apr_psprintf(d->pool, "max-age=%d, public", - d->client_config->cache_control_max_age); + // Calculate the cache control headers. + int max_age = d->client_config->cache_control_max_age; + int expire_time = d->client_config->cache_control_max_age; + int downstream_ttl = d->client_config->edge_control_downstream_ttl; + char *cache_control = NULL, + *edge_control = NULL; - if(d->client_config->edge_control_downstream_ttl != -1) { - edge_control = apr_psprintf(d->pool, "downstream-ttl=%d", - d->client_config->edge_control_downstream_ttl); - } - expire_time = d->client_config->cache_control_max_age; + // Use 'max-age' from the source image only if the client trusts the source, + // and the source image has a 'max-age' that falls within the configured min and max values. + int trust_src_img = 0; + if (d->client_config->trust_src && d->source_image->max_age > 0) { + int min = d->client_config->min_src_cache_control; + int max = d->client_config->max_src_cache_control; + + if((min == -1 || d->source_image->max_age >= d->client_config->min_src_cache_control) && + (max == -1 || d->source_image->max_age <= d->client_config->max_src_cache_control)) { + max_age = d->source_image->max_age; + expire_time = d->source_image->max_age; + downstream_ttl = d->source_image->max_age; } + } - } else if(d->status == DIMS_SUCCESS && d->source_image->response_code == 200) { - expire_time = d->config->default_expire; - cache_control = apr_psprintf(d->pool, "max-age=%d, public", expire_time); - } else { - expire_time = d->config->no_image_expire; - cache_control = apr_psprintf(d->pool, "max-age=%d, public", expire_time); - } - - if(cache_control) { - apr_table_set(d->r->headers_out, "Cache-Control", cache_control); - } + cache_control = apr_psprintf(d->pool, "max-age=%d, public", max_age); + apr_table_set(d->r->headers_out, "Cache-Control", cache_control); - if(edge_control) { + if(d->client_config->edge_control_downstream_ttl != -1) { + edge_control = apr_psprintf(d->pool, "downstream-ttl=%d", downstream_ttl); apr_table_set(d->r->headers_out, "Edge-Control", edge_control); } @@ -278,7 +206,7 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) apr_table_set(d->r->headers_out, "Content-Disposition", disposition); } - if(expire_time) { + if(expire_time > 0) { char buf[APR_RFC822_DATE_LEN]; apr_time_t e = apr_time_now() + ((long long) expire_time * 1000L * 1000L); apr_rfc822_date(buf, e); @@ -286,24 +214,25 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) } if(d->status == DIMS_SUCCESS) { + char buf[128]; snprintf(buf, 128, "DIMS_CLIENT_%s", d->client_id); apr_table_set(d->r->notes, "DIMS_CLIENT", d->client_id); - apr_table_set(d->r->subprocess_env, buf, d->client_id); } - char *etag = NULL; if (d->source_image->etag) { - etag = ap_md5(d->pool, - (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->source_image->etag, NULL)); + char *etag = ap_md5(d->pool, (unsigned char *) + apr_pstrcat(d->pool, d->request_hash, d->source_image->etag, NULL)); + + apr_table_set(d->r->headers_out, "ETag", etag); } else if (d->source_image->last_modified) { - etag = ap_md5(d->pool, - (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->source_image->last_modified, NULL)); - } + char *etag = ap_md5(d->pool, (unsigned char *) + apr_pstrcat(d->pool, d->request_hash, d->source_image->last_modified, NULL)); - if (etag) { apr_table_set(d->r->headers_out, "ETag", etag); } + unsigned char *blob = image->bytes; + size_t length = image->length; if (blob != NULL) { char content_length[256] = ""; snprintf(content_length, sizeof(content_length), "%zu", (size_t) image->length); @@ -314,8 +243,6 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) apr_table_set(d->r->headers_out, "Content-Length", "0"); } - ap_rflush(d->r); - return OK; } @@ -666,7 +593,7 @@ dims_handle_request(dims_request_rec *d) apr_table_set(d->r->notes, "DIMS_IM_TIME", buf); } - return HTTP_OK; + return dims_status_to_http_code(d); } static dims_request_rec * diff --git a/src/request.h b/src/request.h index c2b4798..ebcc9a4 100644 --- a/src/request.h +++ b/src/request.h @@ -17,6 +17,7 @@ typedef struct dims_image_data_t { char *edge_control; char *last_modified; char *etag; + int max_age; } dims_image_data_t; typedef struct dims_command_t { From cff16945e2193fda28a14f6ad4766a6a3417dd1c Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 19:53:18 -0500 Subject: [PATCH 45/69] Remove uptime from status --- src/status.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/status.c b/src/status.c index 93f56dc..17ef50c 100644 --- a/src/status.c +++ b/src/status.c @@ -5,27 +5,6 @@ #include #include "mod_dims.h" -static void show_time(request_rec *r, apr_interval_time_t tsecs) -{ - int days, hrs, mins, secs; - - secs = (int)(tsecs % 60); - tsecs /= 60; - mins = (int)(tsecs % 60); - tsecs /= 60; - hrs = (int)(tsecs % 24); - days = (int)(tsecs / 24); - - ap_rprintf(r, "Uptime: "); - - if (days) ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); - if (hrs) ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); - if (mins) ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); - if (secs) ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); - - ap_rprintf(r, "\n"); -} - apr_status_t status_handler(request_rec *r) { apr_time_t uptime; @@ -36,8 +15,6 @@ status_handler(request_rec *r) { uptime = (apr_uint32_t) apr_time_sec(apr_time_now() - ap_scoreboard_image->global->restart_time); - show_time(r, uptime); - ap_rprintf(r, "Restart time: %s\n", ap_ht_time(r->pool, ap_scoreboard_image->global->restart_time, From 469b94a00f983144e78dd7cb7c35fd0fc3bc0909 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 20:31:05 -0500 Subject: [PATCH 46/69] Improve dims_process_image --- src/mod_dims.c | 67 +++++++++++++++++++--------------------------- src/mod_dims_ops.c | 14 ++-------- 2 files changed, 30 insertions(+), 51 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 9108403..448596c 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -301,35 +301,34 @@ dims_set_optimal_geometry(dims_request_rec *d) static dims_processed_image * dims_process_image(dims_request_rec *d) { - apr_time_t start_time = apr_time_now(); + dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); + // Load the source image. + apr_time_t start_time = apr_time_now(); d->wand = NewMagickWand(); - - if(MagickReadImageBlob(d->wand, d->source_image->data, d->source_image->used) == MagickFalse) { + dims_set_optimal_geometry(d); + if (MagickReadImageBlob(d->wand, d->source_image->data, d->source_image->used) == MagickFalse) { ExceptionType et; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "ImageMagick error, '%s', on request: %s ", MagickGetException(d->wand, &et), d->r->uri); - return NULL; - } - d->imagemagick_time += (apr_time_now() - start_time) / 1000; + image->error = DIMS_FAILURE; - dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); + return image; + } - /* Hook in the progress monitor. It gets passed a dims_progress_rec which keeps track of the start time. */ + // Hook in the progress monitor. This will allow us to timeout. dims_progress_rec *progress_rec = (dims_progress_rec *) apr_palloc(d->pool, sizeof(dims_progress_rec)); progress_rec->d = d; progress_rec->start_time = apr_time_now(); - - /* Setting the progress monitor from the MagickWand API does not seem to work. The monitor never gets called. */ SetImageProgressMonitor(GetImageFromMagickWand(d->wand), dims_imagemagick_progress_cb, (void *) progress_rec); int exc_strip_cmd = 0; - /* Convert image to RGB from CMYK. */ - if(MagickGetImageColorspace(d->wand) == CMYKColorspace) { + // Convert image to RGB from CMYK. + if (MagickGetImageColorspace(d->wand) == CMYKColorspace) { size_t number_profiles; char **profiles; @@ -342,16 +341,14 @@ dims_process_image(dims_request_rec *d) MagickRelinquishMemory((void *)profiles); } - /* - * Flip image orientation, if needed. - */ MagickAutoOrientImage(d->wand); - /* Flatten images (i.e animated gif) if there's an overlay or file type is `psd`. Otherwise, pass through. */ + // Flatten images (i.e animated gif) if there's an overlay or file type is `psd`. Otherwise, pass through. size_t images = MagickGetNumberImages(d->wand); bool should_flatten = false; if (images > 1) { + // Always flatten if there's a watermark command. for (int i = 0; i < d->commands_list->nelts; i++) { dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); @@ -375,6 +372,7 @@ dims_process_image(dims_request_rec *d) } } + // Passthrough images contain multiple frames if (images == 1 || should_flatten) { bool output_format_provided = false; @@ -394,8 +392,7 @@ dims_process_image(dims_request_rec *d) char *err = NULL; apr_status_t code; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Executing command %s(%s), on request %s", cmd->name, cmd->arg, d->r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing command %s(%s)", cmd->name, cmd->arg); if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); @@ -424,31 +421,21 @@ dims_process_image(dims_request_rec *d) } } - /* - * If the strip command was not executed from the loop, call it anyway with NULL args - */ - if(!exc_strip_cmd) { - dims_operation_func *strip_func = dims_operation_lookup("strip"); - if(strip_func != NULL) { - char *err = NULL; - apr_status_t code; + // If the strip command was not executed from the loop + if(!exc_strip_cmd && d->config->strip_metadata) { + char *err = NULL; + apr_status_t code; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "Executing default strip command, on request %s", d->r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing default strip command, on request %s", d->r->uri); - if((code = strip_func(d, NULL, &err)) != DIMS_SUCCESS) { - DestroyMagickWand(d->wand); - image->error = code; - return image; - } - } + if((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { + DestroyMagickWand(d->wand); + image->error = code; + return image; + } } - d->imagemagick_time += (apr_time_now() - start_time) / 1000; - - /* Disable timeouts at this point since the only thing left - * to do is save the image. - */ + // Unregister the progress monitor so we don't timeout on writing the image. SetImageProgressMonitor(GetImageFromMagickWand(d->wand), NULL, NULL); MagickResetIterator(d->wand); @@ -459,6 +446,8 @@ dims_process_image(dims_request_rec *d) DestroyMagickWand(d->wand); + d->imagemagick_time += (apr_time_now() - start_time) / 1000; + return image; } diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index a7fb3ad..0923a36 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -98,21 +98,11 @@ static DimsGravity gravities[] = { apr_status_t dims_strip_operation (dims_request_rec *d, char *args, char **err) { - - /* If args is passed from the user and - * a) it equals true, strip the image. - * b) it equals false, don't strip the image. - * c) it is neither true/false, strip based on config value. - * If args is NULL, strip based on config value. - */ - if(args != NULL) { - if(strcmp(args, "true") == 0 || ( strcmp(args, "false") != 0 && d->config->strip_metadata )) { + if (args != NULL) { + if (strcmp(args, "true") == 0 || ( strcmp(args, "false") != 0 && d->config->strip_metadata)) { MAGICK_CHECK(MagickStripImage(d->wand), d); } } - else if(d->config->strip_metadata) { - MAGICK_CHECK(MagickStripImage(d->wand), d); - } return DIMS_SUCCESS; } From a7a4601ba67c5ede5a2648b56c420d554ccdc143 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 20:33:37 -0500 Subject: [PATCH 47/69] Fix segfault when no query params are set --- src/mod_dims.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mod_dims.c b/src/mod_dims.c index 448596c..a92b3b0 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -634,6 +634,10 @@ dims_parse_args(request_rec *r) { apr_hash_t *query_params = apr_hash_make(r->pool); + if (r->args == NULL) { + return query_params; + } + const size_t args_len = strlen(r->args) + 1; char *args = apr_pstrndup(r->pool, r->args, args_len); char *token; From 468110f538031f2ddff4a61a7506e9852b2599d7 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 21:23:00 -0500 Subject: [PATCH 48/69] Free image/format after sending --- src/mod_dims.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index a92b3b0..b85e20f 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -160,7 +160,7 @@ dims_status_to_http_code(dims_request_rec *d) return OK; } -static apr_status_t +static void dims_send_image(dims_request_rec *d, dims_processed_image *image) { request_rec *r = d->r; @@ -242,8 +242,6 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) } else { apr_table_set(d->r->headers_out, "Content-Length", "0"); } - - return OK; } /** @@ -271,8 +269,8 @@ dims_set_optimal_geometry(dims_request_rec *d) dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); if(strcmp(cmd->name, "resize") == 0 || - strcmp(cmd->name, "legacy_thumbnail") == 0 || - strcmp(cmd->name, "thumbnail") == 0) { + strcmp(cmd->name, "legacy_thumbnail") == 0 || + strcmp(cmd->name, "thumbnail") == 0) { flags = ParseAbsoluteGeometry(cmd->arg, &rec); if(flags & WidthValue && flags & HeightValue && !(flags & PercentValue)) { @@ -558,10 +556,10 @@ dims_handle_request(dims_request_rec *d) } // Serve the image. - status = dims_send_image(d, image); - if (status != DIMS_SUCCESS) { - return status; - } + dims_send_image(d, image); + + free(image->bytes); + free(image->format); /* Record metrics for logging. */ snprintf(buf, 128, "%d", d->status); From 492876ee1a5480a2e6c27438352dc642e67de408 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Thu, 14 Nov 2024 21:43:52 -0500 Subject: [PATCH 49/69] Update dims.conf to show notes usage --- dims.conf | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/dims.conf b/dims.conf index decd177..1623509 100644 --- a/dims.conf +++ b/dims.conf @@ -2,24 +2,20 @@ ServerRoot "/usr/local/apache2" Listen 8000 DocumentRoot "/usr/local/apache2/htdocs" -LoadModule dims_module modules/libmod_dims.so -LoadModule mpm_event_module modules/mod_mpm_event.so -LoadModule unixd_module modules/mod_unixd.so -LoadModule log_config_module modules/mod_log_config.so -LoadModule authn_core_module modules/mod_authn_core.so -LoadModule authz_core_module modules/mod_authz_core.so +LoadModule dims_module modules/libmod_dims.so +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_core_module modules/mod_authz_core.so LogLevel debug User www-data Group www-data -LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined -LogFormat "%h %l %u %t \"%r\" %>s %b" common - - # You need to enable mod_logio.c to use %I and %O - LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio - +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{DIMS_DL_TIME}n %{DIMS_IM_TIME}n %{DIMS_ORIG_BYTES}n %O" common CustomLog /proc/self/fd/1 common ErrorLog /proc/self/fd/2 From 12daef4244e0eb8785c7cf97f76794551b7b7518 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 07:44:31 -0500 Subject: [PATCH 50/69] Fix bug in downstream_ttl This was introduced by the rewrite. --- src/mod_dims.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index b85e20f..1e240ff 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -196,7 +196,7 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) cache_control = apr_psprintf(d->pool, "max-age=%d, public", max_age); apr_table_set(d->r->headers_out, "Cache-Control", cache_control); - if(d->client_config->edge_control_downstream_ttl != -1) { + if(downstream_ttl != -1) { edge_control = apr_psprintf(d->pool, "downstream-ttl=%d", downstream_ttl); apr_table_set(d->r->headers_out, "Edge-Control", edge_control); } From f171e3229cd5a819a5a30576a3cc3e6cfbf62606 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 08:19:32 -0500 Subject: [PATCH 51/69] Fix memory leak in dims_process_image --- src/mod_dims.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 1e240ff..20db15a 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -357,10 +357,10 @@ dims_process_image(dims_request_rec *d) } char *input_format = MagickGetImageFormat(d->wand); - if (strcmp(input_format, "PSD") == 0 || strcmp(input_format, "psd") == 0) { should_flatten = true; } + free(input_format); if (should_flatten) { for (size_t i = 1; i <= images - 1; i++) { From 8b338cbcc7a6285ff6630cecf69930aedef9b8d0 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 08:28:15 -0500 Subject: [PATCH 52/69] Remove optimizedResize feature --- src/configuration.c | 10 ---------- src/configuration.h | 2 -- src/directives.h | 4 ---- src/mod_dims.c | 9 --------- src/mod_dims_ops.c | 34 ---------------------------------- src/request.h | 3 --- 6 files changed, 62 deletions(-) diff --git a/src/configuration.c b/src/configuration.c index af6263a..51f4258 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -25,7 +25,6 @@ dims_create_config(apr_pool_t *p, server_rec *s) config->default_expire = 86400; config->strip_metadata = 1; - config->optimize_resize = 0; config->default_output_format = NULL; config->area_size = 128 * 1024 * 1024; // 128mb max. @@ -153,15 +152,6 @@ dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg return NULL; } -const char * -dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->optimize_resize = atof(arg); - return NULL; -} - const char * dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg) { diff --git a/src/configuration.h b/src/configuration.h index 2bcbf14..64e7472 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -18,7 +18,6 @@ const char *dims_config_set_download_timeout(cmd_parms *cmd, void *dummy, const const char *dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_include_disposition(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_optimize_resize(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg); @@ -43,7 +42,6 @@ struct dims_config_rec { long no_image_expire; long default_expire; int strip_metadata; - float optimize_resize; int include_disposition; char *default_output_format; diff --git a/src/directives.h b/src/directives.h index cddd39d..85944c9 100644 --- a/src/directives.h +++ b/src/directives.h @@ -68,10 +68,6 @@ static const command_rec dims_directives[] = dims_config_set_include_disposition, NULL, RSRC_CONF, "Should DIMS include Content-Disposition header, true OR false." "The default is false."), - AP_INIT_TAKE1("DimsOptimizeResize", - dims_config_set_optimize_resize, NULL, RSRC_CONF, - "Should DIMS optimize resize operations. This has a slight impact on image quality. 0 = disabled" - "The default is 0."), AP_INIT_TAKE1("DimsEncryptionAlgorithm", dims_config_set_encryption_algorithm, NULL, RSRC_CONF, "What algorithm should DIMS user to decrypt the 'eurl' parameter." diff --git a/src/mod_dims.c b/src/mod_dims.c index 20db15a..c8cc28a 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -602,7 +602,6 @@ dims_create_request(request_rec *r) request->start_time = apr_time_now(); request->download_time = 0; request->imagemagick_time = 0; - request->optimize_resize = config->optimize_resize; request->send_content_disposition = 0; request->content_disposition_filename = NULL; request->commands_list = apr_array_make(r->pool, 10, sizeof(dims_command_t)); @@ -734,14 +733,6 @@ dims_request_parse(dims_request_rec *request, int dims4) return DIMS_BAD_URL; } - // Check for optimizeResize parameter. - char *optimize_resize = apr_hash_get(request->query_params, "optimizeResize", APR_HASH_KEY_STRING); - if (optimize_resize != NULL) { - request->optimize_resize = atof(optimize_resize); - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Overriding optimize resize: %f", request->optimize_resize); - } - request->request_hash = ap_md5(r->pool, apr_pstrcat(r->pool, request->client_id, diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 0923a36..7210795 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -126,23 +126,6 @@ dims_resize_operation (dims_request_rec *d, char *args, char **err) { } MagickRelinquishMemory(format); - if (d->optimize_resize) { - size_t orig_width; - size_t orig_height; - - RectangleInfo sampleRec = rec; - sampleRec.width *= d->optimize_resize; - sampleRec.height *= d->optimize_resize; - - orig_width = MagickGetImageWidth(d->wand); - orig_height = MagickGetImageHeight(d->wand); - - if(sampleRec.width < orig_width && sampleRec.height < orig_height) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Sampling image down to %zdx%zd before resizing.", sampleRec.width, sampleRec.height); - MAGICK_CHECK(MagickSampleImage(d->wand, sampleRec.width, sampleRec.height), d); - } - } - MAGICK_CHECK(MagickScaleImage(d->wand, rec.width, rec.height), d); return DIMS_SUCCESS; @@ -183,23 +166,6 @@ dims_thumbnail_operation (dims_request_rec *d, char *args, char **err) { } MagickRelinquishMemory(format); - if (d->optimize_resize) { - size_t orig_width; - size_t orig_height; - - RectangleInfo sampleRec = rec; - sampleRec.width *= d->optimize_resize; - sampleRec.height *= d->optimize_resize; - - orig_width = MagickGetImageWidth(d->wand); - orig_height = MagickGetImageHeight(d->wand); - - if(sampleRec.width < orig_width && sampleRec.height < orig_height) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Sampling image down to %zdx%zd before resizing.", sampleRec.width, sampleRec.height); - MAGICK_CHECK(MagickSampleImage(d->wand, sampleRec.width, sampleRec.height), d); - } - } - MAGICK_CHECK(MagickThumbnailImage(d->wand, rec.width, rec.height), d); if(!(flags & PercentValue)) { diff --git a/src/request.h b/src/request.h index ebcc9a4..c113a24 100644 --- a/src/request.h +++ b/src/request.h @@ -51,9 +51,6 @@ typedef struct dims_request_rec { /* The source image. */ dims_image_data_t *source_image; - /* The sample factor for optimizing resizing. */ - float optimize_resize; - /* The global configuration. */ dims_config_rec *config; From 1de833b8b0f077c1f40177f6c57a22459294acf3 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 08:33:39 -0500 Subject: [PATCH 53/69] Clean up logging --- src/curl.c | 2 +- src/encryption.c | 16 ++++++++-------- src/handler.c | 2 +- src/mod_dims.c | 21 ++++++++------------- src/mod_dims_ops.c | 11 ----------- 5 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/curl.c b/src/curl.c index e45eca2..88ae988 100644 --- a/src/curl.c +++ b/src/curl.c @@ -76,7 +76,7 @@ dims_curl_debug_cb( dims_request_rec *d = (dims_request_rec *) clientp; switch(type) { case CURLINFO_HEADER_OUT: - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Curl request header data: %s ", data); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, d->r, "Curl request header data: %s ", data); break; default: break; diff --git a/src/encryption.c b/src/encryption.c index 14cf0b6..d15066a 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -36,14 +36,14 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en // Initialize the context if (!(ctx = EVP_CIPHER_CTX_new())) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to create new EVP_CIPHER_CTX"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: Failed to create new EVP_CIPHER_CTX"); ERR_print_errors_cb(aes_errors, r); return NULL; } // Initialize the decryption operation if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (1)"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_DecryptInit_ex failed (1)"); ERR_print_errors_cb(aes_errors, r); EVP_CIPHER_CTX_free(ctx); return NULL; @@ -51,7 +51,7 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en // Set the IV length, if necessary if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set IV length"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_CIPHER_CTX_ctrl failed to set IV length"); ERR_print_errors_cb(aes_errors, r); EVP_CIPHER_CTX_free(ctx); return NULL; @@ -59,7 +59,7 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en // Set the key and IV if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (2)"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_DecryptInit_ex failed (2)"); ERR_print_errors_cb(aes_errors, r); EVP_CIPHER_CTX_free(ctx); return NULL; @@ -67,14 +67,14 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en plaintext = apr_palloc(r->pool, ciphertext_length + 1); // +1 for null terminator if (!plaintext) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Memory allocation failed"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: Memory allocation failed"); EVP_CIPHER_CTX_free(ctx); return NULL; } // Provide the message to be decrypted and obtain the plaintext output if (!EVP_DecryptUpdate(ctx, (unsigned char *)plaintext, &out_length, encrypted_text, ciphertext_length)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptUpdate failed"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_DecryptUpdate failed"); ERR_print_errors_cb(aes_errors, r); EVP_CIPHER_CTX_free(ctx); return NULL; @@ -84,7 +84,7 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en // Set expected tag value (must be done after EVP_DecryptUpdate) if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set tag"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_CIPHER_CTX_ctrl failed to set tag"); ERR_print_errors_cb(aes_errors, r); EVP_CIPHER_CTX_free(ctx); return NULL; @@ -100,7 +100,7 @@ aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_en plaintext[plaintext_length] = '\0'; // Explicitly add the null terminator return plaintext; } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptFinal_ex failed"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Decrypting: EVP_DecryptFinal_ex failed"); ERR_print_errors_cb(aes_errors, r); return NULL; } diff --git a/src/handler.c b/src/handler.c index df1bace..56f39a6 100644 --- a/src/handler.c +++ b/src/handler.c @@ -17,7 +17,7 @@ apr_status_t dims_handler(request_rec *r) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handler %s : %s", r->handler, r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "URI: %s ARGS: %s", r->uri, r->args); if(strcmp(r->handler, "dims-status") == 0) { return status_handler(r); diff --git a/src/mod_dims.c b/src/mod_dims.c index c8cc28a..2cca363 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -75,7 +75,7 @@ dims_imagemagick_progress_cb(const char *text, if(diff > p->d->config->imagemagick_timeout) { p->d->status = DIMS_IMAGEMAGICK_TIMEOUT; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, p->d->r, - "Imagemagick operation, '%s', " + "Imagemagick: operation '%s', " "timed out after %d ms. " "(max: %d), on request: %s", text, (int) diff, @@ -100,16 +100,13 @@ dims_download_source_image(dims_request_rec *d, const char *url) d->source_image->edge_control = NULL; d->source_image->last_modified = NULL; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Loading image from %s", url); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Downloading: %s", url); CURLcode code = dims_curl(d, url, d->source_image); start_time = apr_time_now(); if(code != 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "libcurl error, '%s', on request: %s ", - curl_easy_strerror(code), d->r->uri); - + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "Downloading: libcurl error, '%s'", curl_easy_strerror(code)); if(code == CURLE_OPERATION_TIMEDOUT) { d->status = DIMS_DOWNLOAD_TIMEOUT; @@ -309,8 +306,7 @@ dims_process_image(dims_request_rec *d) ExceptionType et; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "ImageMagick error, '%s', on request: %s ", - MagickGetException(d->wand, &et), d->r->uri); + "ImageMagick: error reading image, '%s'", MagickGetException(d->wand, &et)); image->error = DIMS_FAILURE; @@ -390,7 +386,7 @@ dims_process_image(dims_request_rec *d) char *err = NULL; apr_status_t code; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing command %s(%s)", cmd->name, cmd->arg); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing: %s => %s", cmd->name, cmd->arg); if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); @@ -424,7 +420,7 @@ dims_process_image(dims_request_rec *d) char *err = NULL; apr_status_t code; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing default strip command, on request %s", d->r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing: strip => true (config default)"); if((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); @@ -453,7 +449,7 @@ int verify_dims4_signature(dims_request_rec *d) { if(d->client_config->secret_key == NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Secret key not set for client '%s'", d->client_config->id); + "Validating: Secret key not set for client '%s'", d->client_config->id); return DIMS_MISSING_SECRET; } @@ -482,8 +478,7 @@ verify_dims4_signature(dims_request_rec *d) { if (strncasecmp(d->signature, signature, 6) != 0) { signature[7] = '\0'; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, - "Signature invalid: wanted %6s got %6s [%s?url=%s]", signature, d->signature, d->r->uri, d->image_url); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: invalid dims4 signature, expected %6s got %6s", signature, d->signature); return DIMS_INVALID_SIGNATURE; } diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index 7210795..e20313e 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -324,7 +324,6 @@ dims_watermark_operation (dims_request_rec *d, char *args, char **err) { token = apr_strtok(args, "&", &strtokstate); while (token) { if (strncmp(token, "overlay=", 4) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "ARG: %s", token); overlay_url = apr_pstrdup(d->r->pool, token + 8); ap_unescape_url(overlay_url); } @@ -528,10 +527,6 @@ dims_legacy_crop_operation (dims_request_rec *d, char *args, char **err) { x = (width / 2) - (rec.width / 2); y = (height / 2) - (rec.height / 2); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "legacy_crop will crop to %ldx%ld+%d+%d", - rec.width, rec.height, x, y); - MAGICK_CHECK(MagickCropImage(d->wand, rec.width, rec.height, x, y), d); return DIMS_SUCCESS; @@ -565,9 +560,6 @@ dims_legacy_thumbnail_operation (dims_request_rec *d, char *args, char **err) { MAGICK_CHECK(MagickScaleImage(d->wand, rec.width, rec.height), d); } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "legacy_thumbnail will resize to %ldx%ld", rec.width, rec.height); - flags = ParseAbsoluteGeometry(args, &rec); if(!(flags & AllValues)) { *err = "Parsing thumbnail (crop) geometry failed"; @@ -579,9 +571,6 @@ dims_legacy_thumbnail_operation (dims_request_rec *d, char *args, char **err) { x = (width / 2) - (rec.width / 2); y = (height / 2) - (rec.height / 2); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, - "legacy_thumbnail will crop to %ldx%ld+%d+%d", rec.width, rec.height, x, y); - MAGICK_CHECK(MagickCropImage(d->wand, rec.width, rec.height, x, y), d); return DIMS_SUCCESS; From 07a4948625af75b4b4dca8af69081d344691f990 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 09:17:24 -0500 Subject: [PATCH 54/69] Replace imagemagick directives with envvars Since Imagemagick can be configured by environment variables it's easier just use those as we're exposing them as-is anyway. https://imagemagick.org/script/resources.php --- .devcontainer/Dockerfile | 14 ++++++++++++++ Dockerfile | 14 ++++++++++++++ src/configuration.c | 42 ---------------------------------------- src/configuration.h | 9 --------- src/directives.h | 16 --------------- src/initialize.c | 4 ---- 6 files changed, 28 insertions(+), 71 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index daa90a9..8f21684 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -14,6 +14,20 @@ ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV PREFIX=/usr/local/apache2 ENV PATH=${PREFIX}/bin:${PATH} +# Imagemagick configuration + +# 2GB disk limit +ENV MAGICK_DISK_LIMIT=2147483648 + +# 512MB memory limit +ENV MAGICK_MEMORY_LIMIT=536870912 + +# 512MB map limit +ENV MAGICK_MAP_LIMIT=536870912 + +# 128MB area limit +ENV MAGICK_AREA_LIMIT=134217728 + RUN apk update && \ apk add vim gdb zig apr-dev apr-util-dev imagemagick-dev curl-dev \ imagemagick-jpeg imagemagick-webp imagemagick-tiff && \ diff --git a/Dockerfile b/Dockerfile index 5e93523..b74cfd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,20 @@ ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 ENV PREFIX=/usr/local/apache2 ENV PATH=${PREFIX}/bin:${PATH} +# Imagemagick configuration + +# 2GB disk limit +ENV MAGICK_DISK_LIMIT=2147483648 + +# 512MB memory limit +ENV MAGICK_MEMORY_LIMIT=536870912 + +# 512MB map limit +ENV MAGICK_MAP_LIMIT=536870912 + +# 128MB area limit +ENV MAGICK_AREA_LIMIT=134217728 + RUN apk update && \ apk add imagemagick imagemagick-jpeg imagemagick-webp imagemagick-tiff curl diff --git a/src/configuration.c b/src/configuration.c index 51f4258..a95f880 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -27,11 +27,6 @@ dims_create_config(apr_pool_t *p, server_rec *s) config->strip_metadata = 1; config->default_output_format = NULL; - config->area_size = 128 * 1024 * 1024; // 128mb max. - config->memory_size = 512 * 1024 * 1024; // 512mb max. - config->map_size = 1024 * 1024 * 1024; // 1024mb max. - config->disk_size = 2048ul * 1024ul * 1024ul; // 2048mb max. - config->curl_queue_size = 10; config->cache_dir = NULL; config->secret_key = apr_pstrdup(p,"m0d1ms"); @@ -290,16 +285,6 @@ dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg) return NULL; } -const char * -dims_config_set_imagemagick_disk_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->disk_size = atol(arg) * 1024 * 1024; - - return NULL; -} - const char * dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg) { @@ -308,30 +293,3 @@ dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *a config->max_expiry_period = atol(arg); return NULL; } - -const char * -dims_config_set_imagemagick_area_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->area_size = atol(arg) * 1024 * 1024; - return NULL; -} - -const char * -dims_config_set_imagemagick_map_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->map_size = atol(arg) * 1024 * 1024; - return NULL; -} - -const char * -dims_config_set_imagemagick_memory_size(cmd_parms *cmd, void *dummy, const char *arg) -{ - dims_config_rec *config = (dims_config_rec *) ap_get_module_config( - cmd->server->module_config, &dims_module); - config->memory_size = atol(arg) * 1024 * 1024; - return NULL; -} diff --git a/src/configuration.h b/src/configuration.h index 64e7472..ef23ea6 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -24,11 +24,7 @@ const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, con const char *dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]); const char *dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_imagemagick_disk_size(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_imagemagick_area_size(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_imagemagick_map_size(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_imagemagick_memory_size(cmd_parms *cmd, void *dummy, const char *arg); struct dims_config_rec { int download_timeout; @@ -45,11 +41,6 @@ struct dims_config_rec { int include_disposition; char *default_output_format; - MagickSizeType area_size; - MagickSizeType memory_size; - MagickSizeType map_size; - MagickSizeType disk_size; - int curl_queue_size; char *secret_key; char *encryption_algorithm; diff --git a/src/directives.h b/src/directives.h index 85944c9..f0a18e2 100644 --- a/src/directives.h +++ b/src/directives.h @@ -40,22 +40,6 @@ static const command_rec dims_directives[] = dims_config_set_imagemagick_timeout, NULL, RSRC_CONF, "Timeout for processing images." "The default is 3000."), - AP_INIT_TAKE1("DimsImagemagickMemorySize", - dims_config_set_imagemagick_memory_size, NULL, RSRC_CONF, - "Maximum amount of memory in megabytes to use for pixel cache." - "The default is 512mb."), - AP_INIT_TAKE1("DimsImagemagickAreaSize", - dims_config_set_imagemagick_area_size, NULL, RSRC_CONF, - "Maximum amount of memory in megabytes that any one image can use." - "The default is 128mb."), - AP_INIT_TAKE1("DimsImagemagickMapSize", - dims_config_set_imagemagick_map_size, NULL, RSRC_CONF, - "Maximum amount of memory map in megabytes to use for the pixel cache." - "The default is 1024mb."), - AP_INIT_TAKE1("DimsImagemagickDiskSize", - dims_config_set_imagemagick_disk_size, NULL, RSRC_CONF, - "Maximum amount of disk space in megabytes to use for the pixel cache." - "The default is 1024mb."), AP_INIT_TAKE1("DimsSecretMaxExpiryPeriod", dims_config_set_secretkeyExpiryPeriod, NULL, RSRC_CONF, "How long in the future (in seconds) can the expiry date on the URL be requesting. 0 = forever" diff --git a/src/initialize.c b/src/initialize.c index b7f9051..6ca5fb6 100644 --- a/src/initialize.c +++ b/src/initialize.c @@ -42,10 +42,6 @@ dims_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t* ptemp, server_rec *s) ap_add_version_component(p, "mod_dims/" MODULE_VERSION); MagickWandGenesis(); - MagickSetResourceLimit(AreaResource, config->area_size); - MagickSetResourceLimit(DiskResource, config->disk_size); - MagickSetResourceLimit(MemoryResource, config->memory_size); - MagickSetResourceLimit(MapResource, config->map_size); return OK; } From 38933d3b4ba8a57e25d995b15686b0b95b57bacc Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 10:54:56 -0500 Subject: [PATCH 55/69] Replace "no_image" with error background color --- .devcontainer/Dockerfile | 3 +- Dockerfile | 3 +- dims.conf | 6 +- src/configuration.c | 18 +++--- src/configuration.h | 14 ++--- src/directives.h | 16 +++--- src/mod_dims.c | 119 +++++++++++++++++++++++++++++++++------ src/request.h | 4 -- 8 files changed, 132 insertions(+), 51 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 8f21684..1134010 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -10,7 +10,8 @@ ENV DIMS_TRUST_SOURCE=true ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 -ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 +ENV DIMS_ERROR_IMAGE_CACHE_EXPIRE=60 +ENV DIMS_ERROR_IMAGE_BACKGROUND="#5ADAFD" ENV PREFIX=/usr/local/apache2 ENV PATH=${PREFIX}/bin:${PATH} diff --git a/Dockerfile b/Dockerfile index b74cfd4..1110752 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,8 @@ ENV DIMS_TRUST_SOURCE=true ENV DIMS_MIN_SOURCE_CACHE=0 ENV DIMS_MAX_SOURCE_CACHE=604800 ENV DIMS_CACHE_EXPIRE=604800 -ENV DIMS_NO_IMAGE_CACHE_EXPIRE=60 +ENV DIMS_ERROR_IMAGE_CACHE_EXPIRE=60 +ENV DIMS_ERROR_IMAGE_BACKGROUND="#5ADAFD" ENV PREFIX=/usr/local/apache2 ENV PATH=${PREFIX}/bin:${PATH} diff --git a/dims.conf b/dims.conf index 1623509..177f6ea 100644 --- a/dims.conf +++ b/dims.conf @@ -28,11 +28,11 @@ MaxSpareThreads 75 ThreadsPerChild 25 # DimsClient [ ] -DimsAddClient ${DIMS_CLIENT} ${DIMS_NO_IMAGE_URL} ${DIMS_CACHE_CONTROL_MAX_AGE} ${DIMS_EDGE_CONTROL_DOWNSTREAM_TTL} ${DIMS_TRUST_SOURCE} ${DIMS_MIN_SOURCE_CACHE} ${DIMS_MAX_SOURCE_CACHE} ${DIMS_SECRET} +DimsAddClient ${DIMS_CLIENT} ${DIMS_ERROR_IMAGE_BACKGROUND} ${DIMS_CACHE_CONTROL_MAX_AGE} ${DIMS_EDGE_CONTROL_DOWNSTREAM_TTL} ${DIMS_TRUST_SOURCE} ${DIMS_MIN_SOURCE_CACHE} ${DIMS_MAX_SOURCE_CACHE} ${DIMS_SECRET} -DimsDefaultImageURL ${DIMS_DEFAULT_IMAGE_URL} +DimsDefaultImageBackground ${DIMS_ERROR_IMAGE_BACKGROUND} DimsCacheExpire ${DIMS_CACHE_EXPIRE} -DimsNoImageCacheExpire ${DIMS_NO_IMAGE_CACHE_EXPIRE} +DimsErrorImageCacheExpire ${DIMS_NO_IMAGE_CACHE_EXPIRE} DimsDownloadTimeout ${DIMS_DOWNLOAD_TIMEOUT} DimsImagemagickTimeout ${DIMS_IMAGEMAGICK_TIMEOUT} DimsAddWhitelist ${DIMS_WHITELIST} diff --git a/src/configuration.c b/src/configuration.c index a95f880..3846bfd 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -18,8 +18,8 @@ dims_create_config(apr_pool_t *p, server_rec *s) config->download_timeout = 3000; config->imagemagick_timeout = 3000; - config->no_image_url = NULL; - config->no_image_expire = 60; + config->error_image_background = "#5ADAFD"; + config->error_image_expire = 60; config->default_image_prefix = NULL; config->default_expire = 86400; @@ -92,11 +92,11 @@ dims_config_set_default_expire(cmd_parms *cmd, void *dummy, const char *arg) } const char * -dims_config_set_no_image_expire(cmd_parms *cmd, void *dummy, const char *arg) +dims_config_set_error_image_expire(cmd_parms *cmd, void *dummy, const char *arg) { dims_config_rec *config = (dims_config_rec *) ap_get_module_config( cmd->server->module_config, &dims_module); - config->no_image_expire = atol(arg); + config->error_image_expire = atol(arg); return NULL; } @@ -195,7 +195,7 @@ dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) apr_pcalloc(cmd->pool, sizeof(dims_client_config_rec)); - client_config->no_image_url = NULL; + client_config->error_image_background = config->error_image_background; client_config->cache_control_max_age = config->default_expire; client_config->edge_control_downstream_ttl = -1; client_config->trust_src = 0; @@ -243,7 +243,7 @@ dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) } case 2: if(strcmp(argv[1], "-") != 0) { - client_config->no_image_url = argv[1]; + client_config->error_image_background = argv[1]; } case 1: client_config->id = argv[0]; @@ -262,11 +262,11 @@ dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]) } const char * -dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg) +dims_config_set_error_image_background(cmd_parms *cmd, void *dummy, const char *arg) { dims_config_rec *config = (dims_config_rec *) ap_get_module_config( cmd->server->module_config, &dims_module); - config->no_image_url = (char *) arg; + config->error_image_background = (char *) arg; return NULL; } @@ -286,7 +286,7 @@ dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg) } const char * -dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg) +dims_config_set_secretkey_expiry_period(cmd_parms *cmd, void *dummy, const char *arg) { dims_config_rec *config = (dims_config_rec *) ap_get_module_config( cmd->server->module_config, &dims_module); diff --git a/src/configuration.h b/src/configuration.h index ef23ea6..b083b13 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -13,7 +13,6 @@ void *dims_create_config(apr_pool_t *p, server_rec *s); const char *dims_config_set_whitelist(cmd_parms *cmd, void *d, int argc, char *const argv[]); const char *dims_config_set_ignore_default_output_format(cmd_parms *cmd, void *d, int argc, char *const argv[]); const char *dims_config_set_default_expire(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_no_image_expire(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_download_timeout(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_imagemagick_timeout(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_strip_metadata(cmd_parms *cmd, void *dummy, const char *arg); @@ -22,9 +21,10 @@ const char *dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, co const char *dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_user_agent_override(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_client(cmd_parms *cmd, void *d, int argc, char *const argv[]); -const char *dims_config_set_no_image_url(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_error_image_background(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_error_image_expire(cmd_parms *cmd, void *dummy, const char *arg); const char *dims_config_set_image_prefix(cmd_parms *cmd, void *dummy, const char *arg); -const char *dims_config_set_secretkeyExpiryPeriod(cmd_parms *cmd, void *dummy, const char *arg); +const char *dims_config_set_secretkey_expiry_period(cmd_parms *cmd, void *dummy, const char *arg); struct dims_config_rec { int download_timeout; @@ -34,26 +34,24 @@ struct dims_config_rec { apr_hash_t *clients; apr_table_t *ignore_default_output_format; - char *no_image_url; - long no_image_expire; + char *error_image_background; + long error_image_expire; long default_expire; int strip_metadata; int include_disposition; char *default_output_format; - int curl_queue_size; char *secret_key; char *encryption_algorithm; long max_expiry_period; char *cache_dir; char *default_image_prefix; - char *user_agent_override; }; struct dims_client_config_rec { char *id; - char *no_image_url; + char *error_image_background; int cache_control_max_age; int edge_control_downstream_ttl; int trust_src; diff --git a/src/directives.h b/src/directives.h index f0a18e2..253ef04 100644 --- a/src/directives.h +++ b/src/directives.h @@ -18,19 +18,19 @@ static const command_rec dims_directives[] = AP_INIT_TAKE_ARGV("DimsIgnoreDefaultOutputFormat", dims_config_set_ignore_default_output_format, NULL, RSRC_CONF, "Add input formats that shouldn't be converted to the default output format."), - AP_INIT_TAKE1("DimsDefaultImageURL", - dims_config_set_no_image_url, NULL, RSRC_CONF, - "Default image if processing fails or original image doesn't exist."), + AP_INIT_TAKE1("DimsDefaultImageBackground", + dims_config_set_error_image_background, NULL, RSRC_CONF, + "Default image background color if processing fails."), AP_INIT_TAKE1("DimsDefaultImagePrefix", dims_config_set_image_prefix, NULL, RSRC_CONF, "Default image prefix if URL is relative."), AP_INIT_TAKE1("DimsCacheExpire", dims_config_set_default_expire, NULL, RSRC_CONF, - "Default expire time for Cache-Control/Expires/Edge-Control headers, in seconds." + "Default cache-control expire time headers, in seconds." "The default is 86400"), - AP_INIT_TAKE1("DimsNoImageCacheExpire", - dims_config_set_no_image_expire, NULL, RSRC_CONF, - "Default expire time for Cache-Control/Expires/Edge-Control headers for NOIMAGE image, in seconds." + AP_INIT_TAKE1("DimsErrorImageCacheExpire", + dims_config_set_error_image_expire, NULL, RSRC_CONF, + "Default cache-control expire time for error image, in seconds." "The default is 60"), AP_INIT_TAKE1("DimsDownloadTimeout", dims_config_set_download_timeout, NULL, RSRC_CONF, @@ -41,7 +41,7 @@ static const command_rec dims_directives[] = "Timeout for processing images." "The default is 3000."), AP_INIT_TAKE1("DimsSecretMaxExpiryPeriod", - dims_config_set_secretkeyExpiryPeriod, NULL, RSRC_CONF, + dims_config_set_secretkey_expiry_period, NULL, RSRC_CONF, "How long in the future (in seconds) can the expiry date on the URL be requesting. 0 = forever" "The default is 0."), AP_INIT_TAKE1("DimsStripMetadata", diff --git a/src/mod_dims.c b/src/mod_dims.c index 2cca363..729627b 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -120,11 +120,7 @@ dims_download_source_image(dims_request_rec *d, const char *url) d->download_time = (apr_time_now() - start_time) / 1000; if(d->source_image->response_code != 200) { - if(d->source_image->response_code == 404) { - d->status = DIMS_FILE_NOT_FOUND; - } - - return DIMS_FAILURE; + return d->source_image->response_code; } // Ensure SVGs have the appropriate XML header. @@ -135,7 +131,7 @@ dims_download_source_image(dims_request_rec *d, const char *url) d->source_image->used += 55; } - return DIMS_SUCCESS; + return d->source_image->response_code; } static apr_status_t @@ -157,6 +153,91 @@ dims_status_to_http_code(dims_request_rec *d) return OK; } +static void +dims_send_error(dims_request_rec *d, int status) +{ + request_rec *r = d->r; + + if (d->wand) { + DestroyMagickWand(d->wand); + } + d->wand = NewMagickWand(); + + PixelWand *background = NewPixelWand(); + PixelSetColor(background, d->client_config != NULL ? + d->client_config->error_image_background : + d->config->error_image_background); + + MagickNewImage(d->wand, 1024, 1024, background); + + MagickStatusType flags; + RectangleInfo rec; + + /* Process operations. */ + int output_format_provided = false; + for (int i = 0; i < d->commands_list->nelts; i++) { + dims_command_t *cmd = &APR_ARRAY_IDX(d->commands_list, i, dims_command_t); + + if (strcmp(cmd->name, "format") == 0) { + output_format_provided = true; + } + + dims_operation_func *func = dims_operation_lookup(cmd->name); + if (func != NULL) { + char *err = NULL; + apr_status_t code; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing: %s => %s", cmd->name, cmd->arg); + + if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { + continue; + } + } + + MagickMergeImageLayers(d->wand, TrimBoundsLayer); + } + + // Set the format for the error image. + if (!output_format_provided && d->config->default_output_format) { + MagickSetImageFormat(d->wand, d->config->default_output_format); + } else { + MagickSetImageFormat(d->wand, "JPEG"); + } + + MagickResetIterator(d->wand); + + size_t length; + unsigned char *blob = MagickGetImagesBlob(d->wand, &length); + char *format = MagickGetImageFormat(d->wand); + + // Set the Content-Type based on the image format. + char *content_type = apr_psprintf(r->pool, "image/%s", format); + ap_content_type_tolower(content_type); + ap_set_content_type(r, content_type); + + if (blob != NULL) { + char content_length[256] = ""; + snprintf(content_length, sizeof(content_length), "%zu", (size_t) length); + apr_table_set(d->r->headers_out, "Content-Length", content_length); + + ap_rwrite(blob, length, d->r); + } else { + apr_table_set(d->r->headers_out, "Content-Length", "0"); + } + + // Calculate the cache control headers. + char buf[APR_RFC822_DATE_LEN]; + apr_time_t e = apr_time_now() + ((long long) d->config->error_image_expire * 1000L * 1000L); + apr_rfc822_date(buf, e); + apr_table_set(d->r->headers_out, "Expires", buf); + + d->r->status = status; + ap_rflush(d->r); + + DestroyMagickWand(d->wand); + free(format); +} + static void dims_send_image(dims_request_rec *d, dims_processed_image *image) { @@ -539,14 +620,20 @@ dims_handle_request(dims_request_rec *d) // Download image. apr_status_t status = dims_download_source_image(d, d->image_url); if (status != DIMS_SUCCESS) { + dims_send_error(d, status); + return status; } // Execute Imagemagick commands. dims_processed_image *image = dims_process_image(d); if (image != NULL && image->error != DIMS_SUCCESS) { + dims_send_error(d, status); + return image->error; } else if (image == NULL) { + dims_send_error(d, status); + return DIMS_FAILURE; } @@ -589,8 +676,6 @@ dims_create_request(request_rec *r) request->wand = NULL; request->config = config; request->client_config = NULL; - request->no_image_url = request->config->no_image_url; - request->use_no_image = 0; request->image_url = NULL; request->request_hash = NULL; request->status = DIMS_SUCCESS; @@ -759,15 +844,15 @@ dims_handle_dims3(request_rec *r) } if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return DIMS_BAD_CLIENT; - } + dims_send_error(d, HTTP_UNAUTHORIZED); - if(d->client_config && d->client_config->no_image_url) { - d->no_image_url = d->client_config->no_image_url; + return DIMS_BAD_CLIENT; } // Verify allowlist (dims3 only). if (verify_dims3_allowlist(d)) { + dims_send_error(d, HTTP_UNAUTHORIZED); + return HTTP_UNAUTHORIZED; } @@ -784,16 +869,16 @@ dims_handle_dims4(request_rec *r) } if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { - return DIMS_BAD_CLIENT; - } + dims_send_error(d, HTTP_UNAUTHORIZED); - if(d->client_config && d->client_config->no_image_url) { - d->no_image_url = d->client_config->no_image_url; + return DIMS_BAD_CLIENT; } // Verify signature (dims4 only). if (verify_dims4_signature(d)) { - return HTTP_UNAUTHORIZED; + dims_send_error(d, HTTP_UNAUTHORIZED); + + return HTTP_BAD_REQUEST; } return dims_handle_request(d); diff --git a/src/request.h b/src/request.h index c113a24..b0a785f 100644 --- a/src/request.h +++ b/src/request.h @@ -38,10 +38,6 @@ typedef struct dims_request_rec { /* The URL to the image being manipulated. */ char *image_url; - int use_no_image; - - /* The URL to the NOIMAGE image in case of failures. */ - char *no_image_url; /* The parsed commands with the signature and expiration timestamp removed. */ char *commands; From abcd62533c5518dd07a855990aecc8b05e50133b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Fri, 15 Nov 2024 13:42:35 -0500 Subject: [PATCH 56/69] Simplify error codes --- src/mod_dims.c | 101 ++++++++++++++------------------------------- src/mod_dims.h | 15 +------ src/mod_dims_ops.c | 4 +- src/request.h | 7 ---- 4 files changed, 35 insertions(+), 92 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 729627b..a60cb8c 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -73,7 +73,6 @@ dims_imagemagick_progress_cb(const char *text, //long complete = (long) 100L * (offset / (span - 1)); if(diff > p->d->config->imagemagick_timeout) { - p->d->status = DIMS_IMAGEMAGICK_TIMEOUT; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, p->d->r, "Imagemagick: operation '%s', " "timed out after %d ms. " @@ -102,24 +101,19 @@ dims_download_source_image(dims_request_rec *d, const char *url) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Downloading: %s", url); + start_time = apr_time_now(); CURLcode code = dims_curl(d, url, d->source_image); + d->download_time = (apr_time_now() - start_time) / 1000; - start_time = apr_time_now(); - if(code != 0) { + if(code != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "Downloading: libcurl error, '%s'", curl_easy_strerror(code)); - if(code == CURLE_OPERATION_TIMEDOUT) { - d->status = DIMS_DOWNLOAD_TIMEOUT; - } - - d->download_time = (apr_time_now() - start_time) / 1000; - - return DIMS_FAILURE; + return HTTP_INTERNAL_SERVER_ERROR; } - d->download_time = (apr_time_now() - start_time) / 1000; - if(d->source_image->response_code != 200) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "Downloading: libcurl response code, '%d'", d->source_image->response_code); + return d->source_image->response_code; } @@ -134,33 +128,11 @@ dims_download_source_image(dims_request_rec *d, const char *url) return d->source_image->response_code; } -static apr_status_t -dims_status_to_http_code(dims_request_rec *d) -{ - if(d->status == DIMS_FILE_NOT_FOUND) { - return HTTP_NOT_FOUND; - } else if (d->source_image->response_code != 0) { - return d->source_image->response_code; - } else if (d->status != DIMS_SUCCESS) { - if (d->status == DIMS_BAD_URL || d->status == DIMS_BAD_ARGUMENTS) { - return HTTP_BAD_REQUEST; - } else { - //Includes DIMS_BAD_CLIENT, DIMS_DOWNLOAD_TIMEOUT, DIMS_IMAGEMAGICK_TIMEOUT, DIMS_HOSTNAME_NOT_IN_WHITELIST - return HTTP_INTERNAL_SERVER_ERROR; - } - } - - return OK; -} - static void dims_send_error(dims_request_rec *d, int status) { request_rec *r = d->r; - if (d->wand) { - DestroyMagickWand(d->wand); - } d->wand = NewMagickWand(); PixelWand *background = NewPixelWand(); @@ -291,12 +263,6 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) apr_table_set(d->r->headers_out, "Expires", buf); } - if(d->status == DIMS_SUCCESS) { - char buf[128]; - snprintf(buf, 128, "DIMS_CLIENT_%s", d->client_id); - apr_table_set(d->r->notes, "DIMS_CLIENT", d->client_id); - } - if (d->source_image->etag) { char *etag = ap_md5(d->pool, (unsigned char *) apr_pstrcat(d->pool, d->request_hash, d->source_image->etag, NULL)); @@ -389,7 +355,7 @@ dims_process_image(dims_request_rec *d) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "ImageMagick: error reading image, '%s'", MagickGetException(d->wand, &et)); - image->error = DIMS_FAILURE; + image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } @@ -471,7 +437,7 @@ dims_process_image(dims_request_rec *d) if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); - image->error = code; + image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } } @@ -489,7 +455,7 @@ dims_process_image(dims_request_rec *d) if((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); - image->error = code; + image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } } @@ -505,7 +471,7 @@ dims_process_image(dims_request_rec *d) if((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); - image->error = code; + image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } } @@ -529,10 +495,10 @@ dims_process_image(dims_request_rec *d) int verify_dims4_signature(dims_request_rec *d) { if(d->client_config->secret_key == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG,0, d->r, + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: Secret key not set for client '%s'", d->client_config->id); - return DIMS_MISSING_SECRET; + return DIMS_FAILURE; } // Standard signature params. @@ -561,10 +527,10 @@ verify_dims4_signature(dims_request_rec *d) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: invalid dims4 signature, expected %6s got %6s", signature, d->signature); - return DIMS_INVALID_SIGNATURE; + return DIMS_FAILURE; } - return OK; + return DIMS_SUCCESS; } int @@ -579,12 +545,12 @@ verify_dims3_allowlist(dims_request_rec *d) { * and it's value is set to "glob" the match will be accepted. */ if(apr_uri_parse(d->pool, d->image_url, &uri) != APR_SUCCESS) { - return DIMS_BAD_URL; + return DIMS_FAILURE; } char *filename = strrchr(uri.path, '/'); if (!filename || !uri.hostname) { - return DIMS_BAD_URL; + return DIMS_FAILURE; } hostname = uri.hostname; @@ -619,7 +585,7 @@ dims_handle_request(dims_request_rec *d) // Download image. apr_status_t status = dims_download_source_image(d, d->image_url); - if (status != DIMS_SUCCESS) { + if (status != 200) { dims_send_error(d, status); return status; @@ -628,13 +594,13 @@ dims_handle_request(dims_request_rec *d) // Execute Imagemagick commands. dims_processed_image *image = dims_process_image(d); if (image != NULL && image->error != DIMS_SUCCESS) { - dims_send_error(d, status); + dims_send_error(d, image->error); return image->error; } else if (image == NULL) { - dims_send_error(d, status); + dims_send_error(d, HTTP_INTERNAL_SERVER_ERROR); - return DIMS_FAILURE; + return HTTP_INTERNAL_SERVER_ERROR; } // Serve the image. @@ -644,9 +610,6 @@ dims_handle_request(dims_request_rec *d) free(image->format); /* Record metrics for logging. */ - snprintf(buf, 128, "%d", d->status); - apr_table_set(d->r->notes, "DIMS_STATUS", buf); - snprintf(buf, 128, "%ld", d->source_image->used); apr_table_set(d->r->notes, "DIMS_ORIG_BYTES", buf); @@ -656,13 +619,10 @@ dims_handle_request(dims_request_rec *d) snprintf(buf, 128, "%ld", (apr_time_now() - d->start_time) / 1000); apr_table_set(d->r->notes, "DIMS_TOTAL_TIME", buf); - if(d->status != DIMS_DOWNLOAD_TIMEOUT && - d->status != DIMS_IMAGEMAGICK_TIMEOUT) { - snprintf(buf, 128, "%ld", d->imagemagick_time); - apr_table_set(d->r->notes, "DIMS_IM_TIME", buf); - } + snprintf(buf, 128, "%ld", d->imagemagick_time); + apr_table_set(d->r->notes, "DIMS_IM_TIME", buf); - return dims_status_to_http_code(d); + return HTTP_OK; } static dims_request_rec * @@ -678,7 +638,6 @@ dims_create_request(request_rec *r) request->client_config = NULL; request->image_url = NULL; request->request_hash = NULL; - request->status = DIMS_SUCCESS; request->start_time = apr_time_now(); request->download_time = 0; request->imagemagick_time = 0; @@ -805,12 +764,12 @@ dims_request_parse(dims_request_rec *request, int dims4) request->image_url = dims_decrypt_eurl(r, request->config->secret_key, eurl); if (request->image_url == NULL) { - return DIMS_DECRYPTION_FAILURE; + return DIMS_FAILURE; } } else if (url != NULL) { request->image_url = dims_encode_spaces(r->pool, url); } else { - return DIMS_BAD_URL; + return DIMS_FAILURE; } request->request_hash = ap_md5(r->pool, @@ -824,7 +783,7 @@ dims_request_parse(dims_request_rec *request, int dims4) apr_uri_t uri; if (apr_uri_parse(r->pool, request->image_url, &uri) == APR_SUCCESS) { if (!uri.path) { - return DIMS_BAD_URL; + return DIMS_FAILURE; } const char *path = apr_filepath_name_get(uri.path); @@ -844,9 +803,11 @@ dims_handle_dims3(request_rec *r) } if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: Client '%s' not found", d->client_id); + dims_send_error(d, HTTP_UNAUTHORIZED); - return DIMS_BAD_CLIENT; + return DIMS_FAILURE; } // Verify allowlist (dims3 only). @@ -869,9 +830,11 @@ dims_handle_dims4(request_rec *r) } if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: Client '%s' not found", d->client_id); + dims_send_error(d, HTTP_UNAUTHORIZED); - return DIMS_BAD_CLIENT; + return DIMS_FAILURE; } // Verify signature (dims4 only). diff --git a/src/mod_dims.h b/src/mod_dims.h index f9a3b84..d269aa4 100644 --- a/src/mod_dims.h +++ b/src/mod_dims.h @@ -40,19 +40,8 @@ #include -#define DIMS_IGNORE -1 -#define DIMS_SUCCESS 200 -#define DIMS_FAILURE 500 -#define DIMS_FILE_NOT_FOUND 404 -#define DIMS_DOWNLOAD_TIMEOUT 1000 -#define DIMS_IMAGEMAGICK_TIMEOUT 1001 -#define DIMS_BAD_CLIENT 1002 -#define DIMS_BAD_URL 1003 -#define DIMS_BAD_ARGUMENTS 1004 -#define DIMS_HOSTNAME_NOT_IN_WHITELIST 1005 -#define DIMS_INVALID_SIGNATURE 1006 -#define DIMS_MISSING_SECRET 1007 -#define DIMS_DECRYPTION_FAILURE 1008 +#define DIMS_FAILURE 1 +#define DIMS_SUCCESS 0 apr_status_t dims_handle_dims3(request_rec *d); apr_status_t dims_handle_dims4(request_rec *d); diff --git a/src/mod_dims_ops.c b/src/mod_dims_ops.c index e20313e..b97ee0d 100644 --- a/src/mod_dims_ops.c +++ b/src/mod_dims_ops.c @@ -71,9 +71,7 @@ dims_operation_lookup(char *name) { #define MAGICK_CHECK(func, rec) \ do { \ apr_status_t code = func; \ - if(rec->status == DIMS_IMAGEMAGICK_TIMEOUT) {\ - return DIMS_IMAGEMAGICK_TIMEOUT; \ - } else if(code == MagickFalse) {\ + if(code == MagickFalse) {\ return DIMS_FAILURE; \ } \ } while(0) diff --git a/src/request.h b/src/request.h index b0a785f..fff5cad 100644 --- a/src/request.h +++ b/src/request.h @@ -53,13 +53,6 @@ typedef struct dims_request_rec { /* The client specific configuration, if available. */ dims_client_config_rec *client_config; - /* The current status of this request. If downloading - * or manipulating the image times out this will - * be set to DIMS_*_TIMEOUT. If everything is ok it will - * be set to DIMS_SUCCESS. - */ - apr_status_t status; - /* Time this request started. Used for statistics. */ apr_time_t start_time; apr_time_t download_time; From d79147b46b00b156137a50484098731682c668af Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 16 Nov 2024 09:14:49 -0500 Subject: [PATCH 57/69] Use curl_easy_header This simplifies the header logic. Requires curl 7.83.0 or newer. --- src/curl.c | 99 ++++++++++++++++++++++---------------------------- src/mod_dims.c | 17 +++++---- 2 files changed, 54 insertions(+), 62 deletions(-) diff --git a/src/curl.c b/src/curl.c index 88ae988..87ce6cb 100644 --- a/src/curl.c +++ b/src/curl.c @@ -108,57 +108,6 @@ dims_write_image_cb(void *new_data, size_t size, size_t nmemb, void *data) return realsize; } -static size_t -dims_write_header_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - dims_request_rec *d = (dims_request_rec *) data; - size_t realsize = size * nmemb; - char *start = (char *) ptr; - char *header = (char *) ptr; - char *key = NULL, *value = NULL; - - while(header < (start + realsize)) { - if(*header == ':') { - key = apr_pstrndup(d->pool, start, header - start); - while(*header == ' ') { - header++; - } - value = apr_pstrndup(d->pool, header, start + realsize - header - 2); - header = start + realsize; - } - header++; - } - - if(key && value && strcmp(key, "Cache-Control") == 0) { - d->source_image->cache_control = value; - - char *src_header = value; - char *src_start = src_header; - int src_len = strlen(src_header); - - // Ex. max-age=3600 - while(src_header < (src_start + src_len)) { - if(*src_header == '=') { - src_header++; - while(*src_header == ' ') { - src_header++; - } - - d->source_image->max_age = atoi(src_header); - } - src_header++; - } - } else if(key && value && strcmp(key, "Edge-Control") == 0) { - d->source_image->edge_control = value; - } else if(key && value && strcmp(key, "Last-Modified") == 0) { - d->source_image->last_modified = value; - } else if(key && value && strcmp(key, "ETag") == 0) { - d->source_image->etag = value; - } - - return realsize; -} - CURLcode dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) { @@ -174,8 +123,6 @@ dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, dims_write_image_cb); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &image_data); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dims_write_header_cb); - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) d); curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, d->config->download_timeout); curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); @@ -203,16 +150,58 @@ dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) code = curl_easy_perform(curl_handle); + // Grab cache headers using curl_easy_headers() + struct curl_header *header = NULL; + CURLHcode header_code = curl_easy_header(curl_handle, "cache-control", 0, CURLH_HEADER, -1, &header); + if (CURLHE_OK == header_code) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Cache-Control: %s", header->value); + source_image->cache_control = apr_pstrdup(d->pool, header->value); + + char *src_header = source_image->cache_control; + char *src_start = src_header; + int src_len = strlen(src_header); + + // Ex. max-age=3600 + while(src_header < (src_start + src_len)) { + if(*src_header++ == '=') { + while(*src_header == ' ') { + src_header++; + } + + d->source_image->max_age = atol(src_header); + break; + } + } + } + + header_code = curl_easy_header(curl_handle, "edge-control", 0, CURLH_HEADER, -1, &header); + if (CURLHE_OK == header_code) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Edge-Control: %s", header->value); + source_image->edge_control = apr_pstrdup(d->pool, header->value); + } + + header_code = curl_easy_header(curl_handle, "last-modified", 0, CURLH_HEADER, -1, &header); + if (CURLHE_OK == header_code) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Last-Modified: %s", header->value); + source_image->last_modified = apr_pstrdup(d->pool, header->value); + } + + header_code = curl_easy_header(curl_handle, "etag", 0, CURLH_HEADER, -1, &header); + if (CURLHE_OK == header_code) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "ETag: %s", header->value); + source_image->etag = apr_pstrdup(d->pool, header->value); + } + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &source_image->response_code); curl_easy_cleanup(curl_handle); if (source_image->response_code == 200) { source_image->data = apr_pmemdup(d->pool, image_data.data, image_data.used); - source_image->size = image_data.used; + source_image->size = image_data.size; source_image->used = image_data.used; } - free(image_data.data); + MagickRelinquishMemory(image_data.data); return code; } diff --git a/src/mod_dims.c b/src/mod_dims.c index a60cb8c..04f0a38 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -48,9 +48,9 @@ typedef struct dims_progress_rec { typedef struct dims_processed_image { size_t length; + apr_status_t error; unsigned char *bytes; char *format; - apr_status_t error; } dims_processed_image; /** @@ -207,7 +207,7 @@ dims_send_error(dims_request_rec *d, int status) ap_rflush(d->r); DestroyMagickWand(d->wand); - free(format); + MagickRelinquishMemory(format); } static void @@ -278,11 +278,11 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) unsigned char *blob = image->bytes; size_t length = image->length; if (blob != NULL) { - char content_length[256] = ""; - snprintf(content_length, sizeof(content_length), "%zu", (size_t) image->length); + char *content_length = apr_psprintf(d->pool, "%d", length); apr_table_set(d->r->headers_out, "Content-Length", content_length); ap_rwrite(blob, length, d->r); + ap_rflush(d->r); } else { apr_table_set(d->r->headers_out, "Content-Length", "0"); } @@ -403,7 +403,7 @@ dims_process_image(dims_request_rec *d) if (strcmp(input_format, "PSD") == 0 || strcmp(input_format, "psd") == 0) { should_flatten = true; } - free(input_format); + MagickRelinquishMemory(input_format); if (should_flatten) { for (size_t i = 1; i <= images - 1; i++) { @@ -437,6 +437,7 @@ dims_process_image(dims_request_rec *d) if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); + d->wand = NULL; image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } @@ -455,6 +456,7 @@ dims_process_image(dims_request_rec *d) if((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); + d->wand = NULL; image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } @@ -471,6 +473,7 @@ dims_process_image(dims_request_rec *d) if((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { DestroyMagickWand(d->wand); + d->wand = NULL; image->error = HTTP_INTERNAL_SERVER_ERROR; return image; } @@ -606,8 +609,8 @@ dims_handle_request(dims_request_rec *d) // Serve the image. dims_send_image(d, image); - free(image->bytes); - free(image->format); + MagickRelinquishMemory(image->bytes); + MagickRelinquishMemory(image->format); /* Record metrics for logging. */ snprintf(buf, 128, "%ld", d->source_image->used); From 99996b060ddb6ec986cca798970ec77d7c33176f Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 16 Nov 2024 09:15:41 -0500 Subject: [PATCH 58/69] Remove unused url_encode method --- src/curl.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/curl.c b/src/curl.c index 87ce6cb..4a64407 100644 --- a/src/curl.c +++ b/src/curl.c @@ -17,21 +17,6 @@ static char to_hex(char code) { return hex[code & 15]; } -/* Returns a url-encoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -static char *url_encode(char *str) { - char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf; - while (*pstr) { - if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr == ':' || *pstr == '/' || *pstr == '?' || *pstr == '=' || *pstr == '&') - *pbuf++ = *pstr; - else - *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); - pstr++; - } - *pbuf = '\0'; - return buf; -} - void lock_share(CURL *handle, curl_lock_data data, From 1d2c101bd04d9acec9f0f96735489d5d75103f53 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 16 Nov 2024 09:53:09 -0500 Subject: [PATCH 59/69] Fix memory leaks in dims_send_error --- src/mod_dims.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_dims.c b/src/mod_dims.c index 04f0a38..148eb8e 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -207,7 +207,9 @@ dims_send_error(dims_request_rec *d, int status) ap_rflush(d->r); DestroyMagickWand(d->wand); + DestroyPixelWand(background); MagickRelinquishMemory(format); + MagickRelinquishMemory(blob); } static void From 21119f5b9e1829295b73a48fc0efec5bce37b1e7 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 16 Nov 2024 09:53:58 -0500 Subject: [PATCH 60/69] Remove zig ext from devcontainer --- .devcontainer/devcontainer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 942b3de..0a12013 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -29,7 +29,6 @@ "ms-vscode.cpptools", "ms-vscode.cpptools-extension-pack", "vscodevim.vim", - "ziglang.vscode-zig", "GitHub.copilot" ] }, From cdb90d2ce6e74bd2839a0cf2f4a8a3920e7c137b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 10:45:25 -0500 Subject: [PATCH 61/69] Fix env var name --- dims.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dims.conf b/dims.conf index 177f6ea..f73b2d7 100644 --- a/dims.conf +++ b/dims.conf @@ -32,7 +32,7 @@ DimsAddClient ${DIMS_CLIENT} ${DIMS_ERROR_IMAGE_BACKGROUND} ${DIMS_CACHE_CONTROL DimsDefaultImageBackground ${DIMS_ERROR_IMAGE_BACKGROUND} DimsCacheExpire ${DIMS_CACHE_EXPIRE} -DimsErrorImageCacheExpire ${DIMS_NO_IMAGE_CACHE_EXPIRE} +DimsErrorImageCacheExpire ${DIMS_ERROR_IMAGE_CACHE_EXPIRE} DimsDownloadTimeout ${DIMS_DOWNLOAD_TIMEOUT} DimsImagemagickTimeout ${DIMS_IMAGEMAGICK_TIMEOUT} DimsAddWhitelist ${DIMS_WHITELIST} From 67ea5e24ff06ebba05e3fe4ff601755fc31bf44c Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 11:56:41 -0500 Subject: [PATCH 62/69] Refactor dims_write_image_cb --- src/curl.c | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/curl.c b/src/curl.c index 4a64407..7434973 100644 --- a/src/curl.c +++ b/src/curl.c @@ -71,26 +71,48 @@ dims_curl_debug_cb( } /** - * This callback is called by the libcurl API to write data into memory as it's being downloaded. + * dims_write_image_cb - Appends image data chunk to an existing buffer. + * + * @chunk: Pointer to the incoming data chunk. + * @size: The number of elements in the data chunk. + * @bytes_size: Size of each element in bytes. + * @data: Pointer to the image data structure (dims_image_data_t). + * + * This function writes a given image data chunk into an existing dynamically + * allocated buffer, reallocating memory as needed. + * + * Returns: The number of bytes successfully appended to the buffer, or 0 on failure. */ size_t -dims_write_image_cb(void *new_data, size_t size, size_t nmemb, void *data) +dims_write_image_cb(void *chunk, size_t size, size_t bytes_size, void *data) { - dims_image_data_t *image = (dims_image_data_t *) data; - size_t realsize = size * nmemb; + ap_assert(data != NULL); + ap_assert(chunk != NULL); - /* Allocate more memory if needed. */ - if(image->size - image->used <= realsize) { - image->size = image->size == 0 ? realsize : (image->size + realsize) * 1.25; - image->data = (char *) realloc(image->data, image->size); + // Prevent overflow ('size' is always 1 but just in case) + if (size != 1 && bytes_size > SIZE_MAX / size) { + return 0; } - if (image->data) { - memcpy(&(image->data[image->used]), new_data, realsize); - image->used += realsize; + dims_image_data_t *image = (dims_image_data_t *) data; + size_t chunk_size = size * bytes_size; + + // Allocate more memory if needed. + if(image->used + chunk_size > image->size) { + size_t new_size = (image->used + chunk_size) * 2; + char *new_data = (char *)realloc(image->data, new_size); + if (new_data == NULL) { + return 0; + } + + image->data = new_data; + image->size = new_size; } - return realsize; + memcpy(&(image->data[image->used]), chunk, chunk_size); + image->used += chunk_size; + + return chunk_size; } CURLcode From f816f2153f3be93a9dab28d951a4166144fabc5f Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 12:15:15 -0500 Subject: [PATCH 63/69] Improve header parsing --- src/curl.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/curl.c b/src/curl.c index 7434973..5d3f307 100644 --- a/src/curl.c +++ b/src/curl.c @@ -164,20 +164,26 @@ dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Cache-Control: %s", header->value); source_image->cache_control = apr_pstrdup(d->pool, header->value); - char *src_header = source_image->cache_control; - char *src_start = src_header; - int src_len = strlen(src_header); - - // Ex. max-age=3600 - while(src_header < (src_start + src_len)) { - if(*src_header++ == '=') { - while(*src_header == ' ') { - src_header++; - } - - d->source_image->max_age = atol(src_header); - break; + // Parse the Cache-Control header to extract "max-age" if present + char *cache_control = source_image->cache_control; + char *saveptr; + char *directive = strtok_r(cache_control, ",", &saveptr); + + while (directive != NULL) { + // Trim leading spaces + while (*directive == ' ') { + directive++; } + + // Check if the directive is "max-age" + if (strncmp(directive, "max-age=", 8) == 0) { + d->source_image->max_age = atol(directive + 8); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Max-Age: %d", d->source_image->max_age); + break; // Exit loop once max-age is found + } + + // Get the next directive + directive = strtok_r(NULL, ",", &saveptr); } } @@ -204,11 +210,11 @@ dims_curl(dims_request_rec *d, const char *url, dims_image_data_t *source_image) if (source_image->response_code == 200) { source_image->data = apr_pmemdup(d->pool, image_data.data, image_data.used); - source_image->size = image_data.size; + source_image->size = image_data.used; source_image->used = image_data.used; } - MagickRelinquishMemory(image_data.data); + free(image_data.data); return code; } From 7721c0d2bc5882b0b23aa285e397a5c25568e750 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 12:39:45 -0500 Subject: [PATCH 64/69] Refactor dims_process_image Return an error rather than the image. --- src/mod_dims.c | 94 ++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 148eb8e..3683397 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -41,6 +41,15 @@ return d->status; \ } while(0); +#define MAGICK_WAND_CLEANUP(wand_ptr, error_code) \ + do { \ + if ((wand_ptr) != NULL) { \ + DestroyMagickWand(wand_ptr); \ + (wand_ptr) = NULL; \ + } \ + return (error_code); \ + } while (0) + typedef struct dims_progress_rec { dims_request_rec *d; apr_time_t start_time; @@ -48,7 +57,6 @@ typedef struct dims_progress_rec { typedef struct dims_processed_image { size_t length; - apr_status_t error; unsigned char *bytes; char *format; } dims_processed_image; @@ -328,38 +336,35 @@ dims_set_optimal_geometry(dims_request_rec *d) } /** - * This is the main code for processing images. It will parse - * the command string into individual commands and execute them. - * When it's finished it will write the content type header and - * image data to connection and flush the connection. - * - * Commands should always come in pairs, the command name followed - * by the commands arguments delimited by '/'. Example: - * - * thumbnail/78x110/quality/70 - * - * This would first execute the thumbnail command then it would - * set the quality of the image to 70 before writing the image - * to the connection. + * dims_process_image + * + * Processes an image based on image manipulation commands in the request, + * applying each transformation sequentially. + * + * On success, populates `processed_image_out` with the result. + * + * @d: Image request context. + * @processed_image_out: Output pointer for the processed image. + * + * @returns: APR_SUCCESS on success, or http error code if processing fails. */ -static dims_processed_image * -dims_process_image(dims_request_rec *d) -{ +static apr_status_t dims_process_image(dims_request_rec *d, dims_processed_image **processed_image_out) { + if (!d || !processed_image_out) { + return APR_EINVAL; // Invalid argument + } + dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); + *processed_image_out = image; - // Load the source image. apr_time_t start_time = apr_time_now(); d->wand = NewMagickWand(); dims_set_optimal_geometry(d); + if (MagickReadImageBlob(d->wand, d->source_image->data, d->source_image->used) == MagickFalse) { ExceptionType et; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, "ImageMagick: error reading image, '%s'", MagickGetException(d->wand, &et)); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, d->r, - "ImageMagick: error reading image, '%s'", MagickGetException(d->wand, &et)); - - image->error = HTTP_INTERNAL_SERVER_ERROR; - - return image; + MAGICK_WAND_CLEANUP(d->wand, HTTP_INTERNAL_SERVER_ERROR); } // Hook in the progress monitor. This will allow us to timeout. @@ -426,7 +431,7 @@ dims_process_image(dims_request_rec *d) output_format_provided = true; } - if(strcmp(cmd->name, "strip") == 0) { + if (strcmp(cmd->name, "strip") == 0) { exc_strip_cmd = 1; } @@ -438,10 +443,7 @@ dims_process_image(dims_request_rec *d) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing: %s => %s", cmd->name, cmd->arg); if ((code = func(d, cmd->arg, &err)) != DIMS_SUCCESS) { - DestroyMagickWand(d->wand); - d->wand = NULL; - image->error = HTTP_INTERNAL_SERVER_ERROR; - return image; + MAGICK_WAND_CLEANUP(d->wand, HTTP_INTERNAL_SERVER_ERROR); } } @@ -456,28 +458,22 @@ dims_process_image(dims_request_rec *d) char *err = NULL; apr_status_t code; - if((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { - DestroyMagickWand(d->wand); - d->wand = NULL; - image->error = HTTP_INTERNAL_SERVER_ERROR; - return image; + if ((code = dims_format_operation(d, d->config->default_output_format, &err)) != DIMS_SUCCESS) { + MAGICK_WAND_CLEANUP(d->wand, HTTP_INTERNAL_SERVER_ERROR); } } } } // If the strip command was not executed from the loop - if(!exc_strip_cmd && d->config->strip_metadata) { + if (!exc_strip_cmd && d->config->strip_metadata) { char *err = NULL; apr_status_t code; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Executing: strip => true (config default)"); - if((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { - DestroyMagickWand(d->wand); - d->wand = NULL; - image->error = HTTP_INTERNAL_SERVER_ERROR; - return image; + if ((code = dims_strip_operation(d, NULL, &err)) != DIMS_SUCCESS) { + MAGICK_WAND_CLEANUP(d->wand, HTTP_INTERNAL_SERVER_ERROR); } } @@ -486,15 +482,12 @@ dims_process_image(dims_request_rec *d) MagickResetIterator(d->wand); - image->error = DIMS_SUCCESS; image->format = MagickGetImageFormat(d->wand); image->bytes = MagickGetImagesBlob(d->wand, &image->length); - DestroyMagickWand(d->wand); - d->imagemagick_time += (apr_time_now() - start_time) / 1000; - return image; + MAGICK_WAND_CLEANUP(d->wand, APR_SUCCESS); } int @@ -597,15 +590,12 @@ dims_handle_request(dims_request_rec *d) } // Execute Imagemagick commands. - dims_processed_image *image = dims_process_image(d); - if (image != NULL && image->error != DIMS_SUCCESS) { - dims_send_error(d, image->error); - - return image->error; - } else if (image == NULL) { - dims_send_error(d, HTTP_INTERNAL_SERVER_ERROR); + dims_processed_image *image; + status = dims_process_image(d, &image); + if (status != APR_SUCCESS) { + dims_send_error(d, status); - return HTTP_INTERNAL_SERVER_ERROR; + return status; } // Serve the image. From 7ec1fa4a53897fa3805919d2743175f909bfbf55 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 13:12:31 -0500 Subject: [PATCH 65/69] Set d->wand to NULL after freeing --- src/mod_dims.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_dims.c b/src/mod_dims.c index 3683397..2e461dd 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -215,6 +215,8 @@ dims_send_error(dims_request_rec *d, int status) ap_rflush(d->r); DestroyMagickWand(d->wand); + d->wand = NULL; + DestroyPixelWand(background); MagickRelinquishMemory(format); MagickRelinquishMemory(blob); From a8306f9d7f5239215cd2fbe31ec191e42dce3329 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 13:13:25 -0500 Subject: [PATCH 66/69] Use local vars --- src/mod_dims.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 2e461dd..55e5c12 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -240,15 +240,19 @@ dims_send_image(dims_request_rec *d, dims_processed_image *image) char *cache_control = NULL, *edge_control = NULL; - // Use 'max-age' from the source image only if the client trusts the source, - // and the source image has a 'max-age' that falls within the configured min and max values. + // Use 'max-age' from the source image only if the client trusts the source and the + // source image has a 'max-age' that falls within the configured min and max values. int trust_src_img = 0; if (d->client_config->trust_src && d->source_image->max_age > 0) { + // The source image domain is trusted and the source image has a max-age set. + int min = d->client_config->min_src_cache_control; int max = d->client_config->max_src_cache_control; - if((min == -1 || d->source_image->max_age >= d->client_config->min_src_cache_control) && - (max == -1 || d->source_image->max_age <= d->client_config->max_src_cache_control)) { + if ((min == -1 || d->source_image->max_age >= min) && + (max == -1 || d->source_image->max_age <= max)) { + // The source image max-age falls within the configured min and max values. + max_age = d->source_image->max_age; expire_time = d->source_image->max_age; downstream_ttl = d->source_image->max_age; From 2e0ea7978d473c67011c952a58fc24d6f571db78 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 13:17:18 -0500 Subject: [PATCH 67/69] Use asserts to validate arguments --- src/mod_dims.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 55e5c12..fdf833f 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -355,9 +355,8 @@ dims_set_optimal_geometry(dims_request_rec *d) * @returns: APR_SUCCESS on success, or http error code if processing fails. */ static apr_status_t dims_process_image(dims_request_rec *d, dims_processed_image **processed_image_out) { - if (!d || !processed_image_out) { - return APR_EINVAL; // Invalid argument - } + ap_assert(d != NULL); + ap_assert(processed_image_out != NULL); dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); *processed_image_out = image; From 3869cdf8a96a2877ff640a0b97940fdd1c34f547 Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 13:24:33 -0500 Subject: [PATCH 68/69] Explicitly check for NULL when fetching profiles --- src/mod_dims.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index fdf833f..149eb74 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -382,16 +382,16 @@ static apr_status_t dims_process_image(dims_request_rec *d, dims_processed_image // Convert image to RGB from CMYK. if (MagickGetImageColorspace(d->wand) == CMYKColorspace) { - size_t number_profiles; - char **profiles; + size_t number_profiles = 0; + char **profiles = NULL; profiles = MagickGetImageProfiles(d->wand, "icc", &number_profiles); - if (number_profiles == 0) { + if (profiles == NULL || number_profiles == 0) { MagickProfileImage(d->wand, "ICC", cmyk_icc, sizeof(cmyk_icc)); + } else { + MagickProfileImage(d->wand, "ICC", rgb_icc, sizeof(rgb_icc)); + MagickRelinquishMemory(profiles); } - MagickProfileImage(d->wand, "ICC", rgb_icc, sizeof(rgb_icc)); - - MagickRelinquishMemory((void *)profiles); } MagickAutoOrientImage(d->wand); From 2ae245417c0e273013167cdbc7f808636d52389b Mon Sep 17 00:00:00 2001 From: Jeremy Collins Date: Sat, 30 Nov 2024 13:46:42 -0500 Subject: [PATCH 69/69] Verify memory allocations succeed --- src/mod_dims.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/mod_dims.c b/src/mod_dims.c index 149eb74..c9b8af4 100755 --- a/src/mod_dims.c +++ b/src/mod_dims.c @@ -103,6 +103,10 @@ dims_download_source_image(dims_request_rec *d, const char *url) { apr_time_t start_time; d->source_image = apr_palloc(d->pool, sizeof(dims_image_data_t)); + if (d->source_image == NULL) { + return HTTP_INTERNAL_SERVER_ERROR; + } + d->source_image->cache_control = NULL; d->source_image->edge_control = NULL; d->source_image->last_modified = NULL; @@ -139,11 +143,17 @@ dims_download_source_image(dims_request_rec *d, const char *url) static void dims_send_error(dims_request_rec *d, int status) { + ap_assert(d != NULL); + request_rec *r = d->r; d->wand = NewMagickWand(); PixelWand *background = NewPixelWand(); + if (background == NULL) { + return; + } + PixelSetColor(background, d->client_config != NULL ? d->client_config->error_image_background : d->config->error_image_background); @@ -225,6 +235,11 @@ dims_send_error(dims_request_rec *d, int status) static void dims_send_image(dims_request_rec *d, dims_processed_image *image) { + ap_assert(d != NULL); + ap_assert(image != NULL); + ap_assert(image->bytes != NULL); + ap_assert(image->format != NULL); + request_rec *r = d->r; // Set the Content-Type based on the image format. @@ -359,6 +374,9 @@ static apr_status_t dims_process_image(dims_request_rec *d, dims_processed_image ap_assert(processed_image_out != NULL); dims_processed_image *image = (dims_processed_image *) apr_palloc(d->pool, sizeof(dims_processed_image)); + if (image == NULL) { + return HTTP_INTERNAL_SERVER_ERROR; + } *processed_image_out = image; apr_time_t start_time = apr_time_now(); @@ -374,6 +392,9 @@ static apr_status_t dims_process_image(dims_request_rec *d, dims_processed_image // Hook in the progress monitor. This will allow us to timeout. dims_progress_rec *progress_rec = (dims_progress_rec *) apr_palloc(d->pool, sizeof(dims_progress_rec)); + if (progress_rec == NULL) { + MAGICK_WAND_CLEANUP(d->wand, HTTP_INTERNAL_SERVER_ERROR); + } progress_rec->d = d; progress_rec->start_time = apr_time_now(); SetImageProgressMonitor(GetImageFromMagickWand(d->wand), dims_imagemagick_progress_cb, (void *) progress_rec); @@ -511,6 +532,9 @@ verify_dims4_signature(dims_request_rec *d) { d->commands, d->image_url, NULL); + if (signature_params == NULL) { + return DIMS_FAILURE; + } // Concatenate additional params. char *strtokstate = NULL; @@ -518,7 +542,12 @@ verify_dims4_signature(dims_request_rec *d) { if (keys != NULL) { char *token = apr_strtok(keys, ",", &strtokstate); while (token) { - signature_params = apr_pstrcat(d->pool, signature_params, apr_hash_get(d->query_params, token, APR_HASH_KEY_STRING), NULL); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: adding signature param '%s'", token); + + char *value = apr_hash_get(d->query_params, token, APR_HASH_KEY_STRING); + if (value != NULL) { + signature_params = apr_pstrcat(d->pool, signature_params, value, NULL); + } token = apr_strtok(NULL, ",", &strtokstate); } } @@ -533,6 +562,8 @@ verify_dims4_signature(dims_request_rec *d) { return DIMS_FAILURE; } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: signature valid -> '%s'", signature); + return DIMS_SUCCESS; } @@ -629,6 +660,10 @@ static dims_request_rec * dims_create_request(request_rec *r) { dims_request_rec *request = (dims_request_rec *) apr_palloc(r->pool, sizeof(dims_request_rec)); + if (request == NULL) { + return NULL; + } + dims_config_rec *config = (dims_config_rec *) ap_get_module_config(r->server->module_config, &dims_module); request->r = r; @@ -700,6 +735,10 @@ dims_parse_commands(dims_request_rec *request, char *commands) { if(strlen(command) > 0) { char *args = ap_getword(request->pool, &cmds, '/'); dims_command_t *cmd = (dims_command_t *) apr_palloc(request->pool, sizeof(dims_command_t)); + if (cmd == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, request->r, "Memory allocation failed for dims_command_t"); + return commands_list; + } cmd->name = command; cmd->arg = dims_encode_spaces(request->pool, args); @@ -824,12 +863,17 @@ apr_status_t dims_handle_dims4(request_rec *r) { dims_request_rec *d = dims_create_request(r); + if (d == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Memory allocation failed for dims_request_rec"); + return HTTP_INTERNAL_SERVER_ERROR; + } + int status = dims_request_parse(d, 1); if (status != DIMS_SUCCESS) { return status; } - if(!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { + if (!(d->client_config = apr_hash_get(d->config->clients, d->client_id, APR_HASH_KEY_STRING))) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Validating: Client '%s' not found", d->client_id); dims_send_error(d, HTTP_UNAUTHORIZED);