WebP Express - Version 0.14.9

Version Description

(released: 22 jun 2019)

  • Tidied up code
Download this release

Release Info

Developer rosell.dk
Plugin Icon 128x128 WebP Express
Version 0.14.9
Comparing to
See all releases

Code changes from version 0.14.8 to 0.14.9

Files changed (39) hide show
  1. README.txt +9 -1
  2. composer.json +1 -1
  3. composer.lock +6 -6
  4. lib/classes/AdminInit.php +2 -2
  5. lib/classes/CachePurge.php +1 -1
  6. lib/classes/Config.php +2 -0
  7. lib/classes/Convert.php +70 -31
  8. lib/classes/ConvertHelperIndependent.php +2 -4
  9. lib/classes/ConvertLog.php +2 -2
  10. lib/classes/ConvertersHelper.php +8 -2
  11. lib/classes/DismissableMessages.php +3 -3
  12. lib/classes/Paths.php +1 -0
  13. lib/classes/Sanitize.php +19 -0
  14. lib/classes/Validate.php +167 -0
  15. lib/classes/ValidateException.php +7 -0
  16. lib/migrate/migrate10.php +68 -0
  17. lib/options/enqueue_scripts.php +2 -2
  18. lib/options/js/{0.14.5 → 0.14.9}/authorized_sites_bak.js +0 -0
  19. lib/options/js/{0.14.5 → 0.14.9}/bulk-convert.js +0 -0
  20. lib/options/js/{0.14.5 → 0.14.9}/converters.js +8 -3
  21. lib/options/js/{0.14.5 → 0.14.9}/das-popup.js +0 -0
  22. lib/options/js/{0.14.5 → 0.14.9}/escapeHTML.js +0 -0
  23. lib/options/js/{0.14.5 → 0.14.9}/image-comparison-slider.js +0 -0
  24. lib/options/js/{0.14.5 → 0.14.9}/page.js +0 -0
  25. lib/options/js/{0.14.5 → 0.14.9}/purge-cache.js +0 -0
  26. lib/options/js/{0.14.5 → 0.14.9}/sortable.min.js +0 -0
  27. lib/options/js/{0.14.5 → 0.14.9}/test-convert.js +0 -0
  28. lib/options/js/{0.14.5 → 0.14.9}/whitelist.js +0 -0
  29. lib/options/page-messages.php +15 -5
  30. lib/options/submit.php +3 -6
  31. vendor/composer/installed.json +6 -6
  32. vendor/rosell-dk/webp-convert/composer.json +1 -0
  33. vendor/rosell-dk/webp-convert/docs/v2.0/converting/options.md +24 -3
  34. vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php +3 -1
  35. vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php +6 -0
  36. vendor/rosell-dk/webp-convert/src/Serve/Report.php +1 -1
  37. webp-express.php +7 -6
  38. wod/webp-on-demand.php +239 -192
  39. wod/webp-realizer.php +237 -172
README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://ko-fi.com/rosell
4
Tags: webp, images, performance
5
Requires at least: 4.0
6
Tested up to: 5.2
7
- Stable tag: 0.14.8
8
Requires PHP: 5.6
9
License: GPLv3
10
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@@ -605,6 +605,11 @@ Easy enough! - [Go here!](https://ko-fi.com/rosell). Or [here](https://buymeacof
605
606
== Changelog ==
607
608
= 0.14.8 =
609
*(released: 21 jun 2019)*
610
@@ -839,6 +844,9 @@ For older releases, check out changelog.txt
839
840
== Upgrade Notice ==
841
842
= 0.14.8 =
843
Tidied up code
844
4
Tags: webp, images, performance
5
Requires at least: 4.0
6
Tested up to: 5.2
7
+ Stable tag: 0.14.9
8
Requires PHP: 5.6
9
License: GPLv3
10
License URI: https://www.gnu.org/licenses/gpl-3.0.html
605
606
== Changelog ==
607
608
+ = 0.14.9 =
609
+ *(released: 22 jun 2019)*
610
+
611
+ * Tidied up code
612
+
613
= 0.14.8 =
614
*(released: 21 jun 2019)*
615
844
845
== Upgrade Notice ==
846
847
+ = 0.14.9 =
848
+ Tidied up code
849
+
850
= 0.14.8 =
851
Tidied up code
852
composer.json CHANGED
@@ -4,7 +4,7 @@
4
"type": "project",
5
"license": "MIT",
6
"require": {
7
- "rosell-dk/webp-convert": "^2.1.1",
8
"rosell-dk/webp-convert-cloud-service": "^2.0.0",
9
"rosell-dk/dom-util-for-webp": "^0.3.0"
10
},
4
"type": "project",
5
"license": "MIT",
6
"require": {
7
+ "rosell-dk/webp-convert": "^2.1.3",
8
"rosell-dk/webp-convert-cloud-service": "^2.0.0",
9
"rosell-dk/dom-util-for-webp": "^0.3.0"
10
},
composer.lock CHANGED
@@ -4,7 +4,7 @@
4
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
"This file is @generated automatically"
6
],
7
- "content-hash": "d012f5aa2f2575b563cddf193216562a",
8
"packages": [
9
{
10
"name": "rosell-dk/dom-util-for-webp",
@@ -118,16 +118,16 @@
118
},
119
{
120
"name": "rosell-dk/webp-convert",
121
- "version": "2.1.1",
122
"source": {
123
"type": "git",
124
"url": "https://github.com/rosell-dk/webp-convert.git",
125
- "reference": "cc0ceb81193a0b930b2e7c58886a7f3662fb45c6"
126
},
127
"dist": {
128
"type": "zip",
129
- "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/cc0ceb81193a0b930b2e7c58886a7f3662fb45c6",
130
- "reference": "cc0ceb81193a0b930b2e7c58886a7f3662fb45c6",
131
"shasum": ""
132
},
133
"require": {
@@ -190,7 +190,7 @@
190
"png",
191
"png2webp"
192
],
193
- "time": "2019-06-21T11:15:37+00:00"
194
},
195
{
196
"name": "rosell-dk/webp-convert-cloud-service",
4
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
"This file is @generated automatically"
6
],
7
+ "content-hash": "4b326e154cd42a9b5e0182756cab2642",
8
"packages": [
9
{
10
"name": "rosell-dk/dom-util-for-webp",
118
},
119
{
120
"name": "rosell-dk/webp-convert",
121
+ "version": "2.1.3",
122
"source": {
123
"type": "git",
124
"url": "https://github.com/rosell-dk/webp-convert.git",
125
+ "reference": "959f0515b79a785fa1015c0023c22619f55d6392"
126
},
127
"dist": {
128
"type": "zip",
129
+ "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/959f0515b79a785fa1015c0023c22619f55d6392",
130
+ "reference": "959f0515b79a785fa1015c0023c22619f55d6392",
131
"shasum": ""
132
},
133
"require": {
190
"png",
191
"png2webp"
192
],
193
+ "time": "2019-06-22T22:24:30+00:00"
194
},
195
{
196
"name": "rosell-dk/webp-convert-cloud-service",
lib/classes/AdminInit.php CHANGED
@@ -30,7 +30,7 @@ class AdminInit
30
public static function runMigrationIfNeeded()
31
{
32
// When an update requires a migration, the number should be increased
33
- define('WEBPEXPRESS_MIGRATION_VERSION', '9');
34
35
if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
36
// run migration logic
@@ -38,7 +38,7 @@ class AdminInit
38
}
39
40
// uncomment next line to test-run a migration
41
- //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate9.php';
42
}
43
44
public static function adminInitHandler()
30
public static function runMigrationIfNeeded()
31
{
32
// When an update requires a migration, the number should be increased
33
+ define('WEBPEXPRESS_MIGRATION_VERSION', '10');
34
35
if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
36
// run migration logic
38
}
39
40
// uncomment next line to test-run a migration
41
+ //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate10.php';
42
}
43
44
public static function adminInitHandler()
lib/classes/CachePurge.php CHANGED
@@ -152,7 +152,7 @@ class CachePurge
152
wp_die();
153
}
154
155
- $onlyPng = ($_POST['only-png'] == 'true');
156
157
$config = Config::loadConfigAndFix();
158
$result = self::purge($config, $onlyPng);
152
wp_die();
153
}
154
155
+ $onlyPng = (sanitize_text_field($_POST['only-png']) == 'true');
156
157
$config = Config::loadConfigAndFix();
158
$result = self::purge($config, $onlyPng);
lib/classes/Config.php CHANGED
@@ -602,6 +602,8 @@ class Config
602
// WOD options
603
// -------------
604
$wod = [
605
'base-htaccess-on-these-capability-tests' => $config['base-htaccess-on-these-capability-tests'],
606
'destination-extension' => $config['destination-extension'],
607
'destination-folder' => $config['destination-folder'],
602
// WOD options
603
// -------------
604
$wod = [
605
+ 'enable-redirection-to-converter' => $config['enable-redirection-to-converter'],
606
+ 'enable-redirection-to-webp-realizer' => $config['enable-redirection-to-webp-realizer'],
607
'base-htaccess-on-these-capability-tests' => $config['base-htaccess-on-these-capability-tests'],
608
'destination-extension' => $config['destination-extension'],
609
'destination-folder' => $config['destination-folder'],
lib/classes/Convert.php CHANGED
@@ -1,14 +1,13 @@
1
<?php
2
3
- /*
4
- This class is made to be independent of other classes, and must be kept like that.
5
- It is used by webp-on-demand.php, which does not register an auto loader. It is also used for bulk conversion.
6
- */
7
namespace WebPExpress;
8
9
use \WebPExpress\ConvertHelperIndependent;
10
use \WebPExpress\Config;
11
- use \WebpExpress\ConvertersHelper;
12
13
class Convert
14
{
@@ -74,47 +73,87 @@ class Convert
74
75
public static function processAjaxConvertFile()
76
{
77
if (!check_ajax_referer('webpexpress-ajax-convert-nonce', 'nonce', false)) {
78
wp_send_json_error('Invalid security nonce (it has probably expired - try refreshing)');
79
wp_die();
80
}
81
82
- // No need to sanitize filename. self::convertFile does that for us. And the WebPConvert library also does.
83
- // Also, no need to check mime type as the WebPConvert library also does that (it only allows image/jpeg and image/png)
84
- $filename = $_POST['filename'];
85
86
- if (isset($_POST['config-overrides'])) {
87
- $config = Config::loadConfigAndFix();
88
89
- // overrides
90
- $overrides = $_POST['config-overrides'];
91
- $overrides = preg_replace('/\\\\"/', '"', $overrides); // We got crazy encoding, perhaps by jQuery. This cleans it up
92
- $overrides = json_decode($overrides, true);
93
94
- $config = array_merge($config, $overrides);
95
96
- // single converter
97
- $converter = null;
98
- $convertOptions = null;
99
- if (isset($_POST['converter'])) {
100
- $converter = $_POST['converter'];
101
102
- // find converter
103
- $c = ConvertersHelper::getConverterById($config, $converter);
104
- if ($c !== false) {
105
106
- $convertOptions = Config::generateWodOptionsFromConfigObj($config)['webp-convert']['convert'];
107
- $convertOptions = array_merge($convertOptions, $c['options']);
108
- unset($convertOptions['converters']);
109
110
- $config = array_merge($config, $c['options']);
111
- //echo 'options: <pre>' . print_r($convertOptions, true) . '</pre>'; exit;
112
- //echo 'options: <pre>' . print_r($c['options'], true) . '</pre>'; exit;
113
}
114
- }
115
116
- $result = self::convertFile($filename, $config, $convertOptions, $converter);
117
118
} else {
119
$result = self::convertFile($filename);
120
}
1
<?php
2
3
namespace WebPExpress;
4
5
use \WebPExpress\ConvertHelperIndependent;
6
use \WebPExpress\Config;
7
+ use \WebPExpress\ConvertersHelper;
8
+ use \WebPExpress\Sanitize;
9
+ use \WebPExpress\Validate;
10
+ use \WebPExpress\ValidateException;
11
12
class Convert
13
{
73
74
public static function processAjaxConvertFile()
75
{
76
+
77
if (!check_ajax_referer('webpexpress-ajax-convert-nonce', 'nonce', false)) {
78
wp_send_json_error('Invalid security nonce (it has probably expired - try refreshing)');
79
wp_die();
80
}
81
82
+ // Validate input
83
+ // ---------------------------
84
+ try {
85
+ // validate "filename"
86
+ $validating = '"filename" argument';
87
+ Validate::postHasKey('filename');
88
+ $filename = sanitize_text_field($_POST['filename']);
89
+ Validate::absPathLooksSaneExistsAndIsNotDir($filename);
90
91
92
+ // validate converter id
93
+ // ---------------------
94
+ $validating = '"converter" argument';
95
+ if (isset($_POST['converter'])) {
96
+ $converterId = sanitize_text_field($_POST['converter']);
97
+ Validate::isConverterId($converterId);
98
+ }
99
100
101
+ // validate "config-overrides"
102
+ // ---------------------------
103
+ $validating = '"config-overrides" argument';
104
+ if (isset($_POST['config-overrides'])) {
105
+ $configOverridesJSON = Sanitize::removeNUL($_POST['config-overrides']);
106
+ $configOverridesJSON = preg_replace('/\\\\"/', '"', $configOverridesJSON); // We got crazy encoding, perhaps by jQuery. This cleans it up
107
+
108
+ Validate::isJSONObject($configOverridesJSON, $configOverridesJSON);
109
+ $configOverrides = json_decode($configOverridesJSON, true);
110
+
111
+ // PS: We do not need to validate the overrides.
112
+ // webp-convert checks all options. Nothing can be passed to webp-convert which causes harm.
113
+ }
114
+
115
+ } catch (ValidateException $e) {
116
+ wp_send_json_error('failed validating ' . $validating . ': '. $e->getMessage());
117
+ wp_die();
118
+ }
119
+
120
+
121
+ // Input has been processed, now lets get to work!
122
+ // -----------------------------------------------
123
+ if (isset($configOverrides)) {
124
+ $config = Config::loadConfigAndFix();
125
+
126
127
+ // convert using specific converter
128
+ if (!is_null($converterId)) {
129
130
+ // Merge in the config-overrides (config-overrides only have effect when using a specific converter)
131
+ $config = array_merge($config, $configOverrides);
132
133
+ $converter = ConvertersHelper::getConverterById($config, $converterId);
134
+ if ($converter === false) {
135
+ wp_send_json_error('Converter could not be loaded');
136
+ wp_die();
137
}
138
139
+ // the converter options stored in config.json is not precisely the same as the ones
140
+ // we send to webp-convert.
141
+ // We need to "regenerate" webp-convert options in order to use the ones specified in the config-overrides
142
+ // And we need to merge the general options (such as quality etc) into the option for the specific converter
143
+
144
+ $generalWebpConvertOptions = Config::generateWodOptionsFromConfigObj($config)['webp-convert']['convert'];
145
+ $converterSpecificWebpConvertOptions = $converter['options'];
146
+
147
+ $webpConvertOptions = array_merge($generalWebpConvertOptions, $converterSpecificWebpConvertOptions);
148
+ unset($webpConvertOptions['converters']);
149
150
+ // what is this? - I forgot why!
151
+ //$config = array_merge($config, $converter['options']);
152
+ $result = self::convertFile($filename, $config, $webpConvertOptions, $converterId);
153
+
154
+ } else {
155
+ $result = self::convertFile($filename, $config);
156
+ }
157
} else {
158
$result = self::convertFile($filename);
159
}
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -182,12 +182,10 @@ class ConvertHelperIndependent
182
*/
183
private static function findSourceMingled($destination, $destinationExt)
184
{
185
- global $options;
186
- global $destination;
187
if ($destinationExt == 'append') {
188
$source = preg_replace('/\\.(webp)#x2F;', '', $destination);
189
} else {
190
- $source = preg_replace('/\\.webp#x2F;', '.jpg', $destination);
191
if (!@file_exists($source)) {
192
$source = preg_replace('/\\.webp#x2F;', '.jpeg', $destination);
193
}
@@ -270,7 +268,7 @@ APACHE
270
271
$text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
272
273
- $text = 'WebP Express 0.14.8. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
274
275
$logFile = self::getLogFilename($source, $logDir);
276
182
*/
183
private static function findSourceMingled($destination, $destinationExt)
184
{
185
if ($destinationExt == 'append') {
186
$source = preg_replace('/\\.(webp)#x2F;', '', $destination);
187
} else {
188
+ $source = preg_replace('#\\.webp$#', '.jpg', $destination);
189
if (!@file_exists($source)) {
190
$source = preg_replace('/\\.webp#x2F;', '.jpeg', $destination);
191
}
268
269
$text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
270
271
+ $text = 'WebP Express 0.14.9. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
272
273
$logFile = self::getLogFilename($source, $logDir);
274
lib/classes/ConvertLog.php CHANGED
@@ -13,14 +13,14 @@ class ConvertLog
13
wp_send_json_error('Invalid security nonce (it has probably expired - try refreshing)');
14
wp_die();
15
}
16
- $source = $_POST['source'];
17
18
- // We need to be absolute certain that this feature can be misused.
19
// - so disabling until I get the time...
20
21
$msg = 'This feature is on the road map...';
22
echo json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
23
/*
24
$logFile = ConvertHelperIndependent::getLogFilename($source, Paths::getLogDirAbs());
25
$msg = 'Log file: <i>' . $logFile . '</i><br><br><hr>';
26
13
wp_send_json_error('Invalid security nonce (it has probably expired - try refreshing)');
14
wp_die();
15
}
16
17
+ // We need to be absolute certain that this feature cannot be misused.
18
// - so disabling until I get the time...
19
20
$msg = 'This feature is on the road map...';
21
echo json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
22
/*
23
+ $source = sanitize_text_field($_POST['source']);
24
$logFile = ConvertHelperIndependent::getLogFilename($source, Paths::getLogDirAbs());
25
$msg = 'Log file: <i>' . $logFile . '</i><br><br><hr>';
26
lib/classes/ConvertersHelper.php CHANGED
@@ -10,7 +10,6 @@ class ConvertersHelper
10
'try-common-system-paths' => true,
11
'try-supplied-binary-for-os' => true,
12
'method' => 6,
13
- 'size-in-percentage' => null,
14
'low-memory' => true,
15
'command-line-options' => '',
16
]],
@@ -35,7 +34,14 @@ class ConvertersHelper
35
36
public static function getDefaultConverterNames()
37
{
38
- return array_column(self::$defaultConverters, 'converter');
39
}
40
41
public static function getConverterNames($converters)
10
'try-common-system-paths' => true,
11
'try-supplied-binary-for-os' => true,
12
'method' => 6,
13
'low-memory' => true,
14
'command-line-options' => '',
15
]],
34
35
public static function getDefaultConverterNames()
36
{
37
+ $availableConverterIDs = [];
38
+ foreach (self::$defaultConverters as $converter) {
39
+ $availableConverterIDs[] = $converter['converter'];
40
+ }
41
+ return $availableConverterIDs;
42
+
43
+ // PS: In a couple of years:
44
+ //return array_column(self::$defaultConverters, 'converter');
45
}
46
47
public static function getConverterNames($converters)
lib/classes/DismissableMessages.php CHANGED
@@ -75,10 +75,10 @@ class DismissableMessages
75
76
public static function processAjaxDismissMessage() {
77
/*
78
- We have no security nonce here. Dismissing a message is not harmful and dismissMessage($id) do anything harmful, no matter what you
79
- send in the "id"
80
*/
81
- $id = $_POST['id'];
82
self::dismissMessage($id);
83
}
84
75
76
public static function processAjaxDismissMessage() {
77
/*
78
+ We have no security nonce here
79
+ Dismissing a message is not harmful and dismissMessage($id) do anything harmful, no matter what you send in the "id"
80
*/
81
+ $id = sanitize_text_field($_POST['id']);
82
self::dismissMessage($id);
83
}
84
lib/classes/Paths.php CHANGED
@@ -400,6 +400,7 @@ APACHE
400
public static function getWodUrlPath()
401
{
402
return self::getPluginUrlPath() . '/wod/webp-on-demand.php';
403
}
404
405
public static function getWebPRealizerUrlPath()
400
public static function getWodUrlPath()
401
{
402
return self::getPluginUrlPath() . '/wod/webp-on-demand.php';
403
+ //return self::getHomeUrlPath() . '/webp-on-demand';
404
}
405
406
public static function getWebPRealizerUrlPath()
lib/classes/Sanitize.php ADDED
@@ -0,0 +1,19 @@
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ class Sanitize
6
+ {
7
+
8
+ /**
9
+ * The NUL character is a demon, because it can be used to bypass other tests
10
+ * See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
11
+ *
12
+ * @param string $string string remove NUL characters in
13
+ */
14
+ public static function removeNUL($string)
15
+ {
16
+ return str_replace(chr(0), '', $string);
17
+ }
18
+
19
+ }
lib/classes/Validate.php ADDED
@@ -0,0 +1,167 @@
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ use \WebPExpress\Sanitize;
6
+ use \WebPExpress\ValidateException;
7
+
8
+ class Validate
9
+ {
10
+
11
+ public static function postHasKey($key)
12
+ {
13
+ if (!isset($_POST[$key])) {
14
+ throw new ValidateException('Expected parameter in POST missing: ' . $key);
15
+ }
16
+ }
17
+
18
+ /**
19
+ * The NUL character is a demon, because it can be used to bypass other tests
20
+ * See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
21
+ *
22
+ * @param string $string string to test for NUL char
23
+ */
24
+ public static function noNUL($string)
25
+ {
26
+ if (strpos($string, chr(0)) !== false) {
27
+ throw new ValidateException('NUL character is not allowed');
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Prevent control chararters (#00 - #20).
33
+ *
34
+ * This prevents line feed, new line, tab, charater return, tab, ets.
35
+ * https://www.rapidtables.com/code/text/ascii-table.html
36
+ *
37
+ * @param string $string string to test for control characters
38
+ */
39
+ public static function noControlChars($string)
40
+ {
41
+ if (preg_match('#[\x{0}-\x{1f}]#', $string)) {
42
+ throw new ValidateException('Control characters are not allowed');
43
+ }
44
+ }
45
+
46
+ /**
47
+ *
48
+ * @param mixed $mixed something that may not be empty
49
+ */
50
+ public static function notEmpty($mixed)
51
+ {
52
+ if (empty($mixed)) {
53
+ throw new ValidateException('Must be non-empty');
54
+ }
55
+ }
56
+
57
+ public static function noDirectoryTraversal($path)
58
+ {
59
+ if (preg_match('#\.\.\/#', Sanitize::removeNUL($path))) {
60
+ throw new ValidateException('Directory traversal is not allowed');
61
+ }
62
+ }
63
+
64
+ public static function noStreamWrappers($path)
65
+ {
66
+ // Prevent stream wrappers ("phar://", "php://" and the like)
67
+ // https://www.php.net/manual/en/wrappers.phar.php
68
+ if (preg_match('#^\\w+://#', Sanitize::removeNUL($path))) {
69
+ throw new ValidateException('Stream wrappers are not allowed');
70
+ }
71
+ }
72
+
73
+ public static function pathLooksSane($path)
74
+ {
75
+ self::notEmpty($path);
76
+ self::noControlChars($path);
77
+ self::noDirectoryTraversal($path);
78
+ self::noStreamWrappers($path);
79
+ }
80
+
81
+ public static function absPathLooksSane($path)
82
+ {
83
+ self::pathLooksSane($path);
84
+ }
85
+
86
+
87
+ public static function absPathLooksSaneAndExists($path, $errorMsg = 'Path does not exist')
88
+ {
89
+ self::absPathLooksSane($path);
90
+ if (@!file_exists($path)) {
91
+ throw new ValidateException($errorMsg);
92
+ }
93
+ }
94
+
95
+ public static function absPathLooksSaneExistsAndIsDir(
96
+ $path,
97
+ $errorMsg = 'Path points to a file (it should point to a directory)'
98
+ ) {
99
+ self::absPathLooksSaneAndExists($path);
100
+ if (!is_dir($path)) {
101
+ throw new ValidateException($errorMsg);
102
+ }
103
+ }
104
+
105
+ public static function absPathLooksSaneExistsAndIsFile(
106
+ $path,
107
+ $errorMsg = 'Path points to a directory (it should not do that)'
108
+ ) {
109
+ self::absPathLooksSaneAndExists($path, 'File does not exist');
110
+ if (@is_dir($path)) {
111
+ throw new ValidateException($errorMsg);
112
+ }
113
+ }
114
+
115
+ public static function absPathLooksSaneExistsAndIsNotDir(
116
+ $path,
117
+ $errorMsg = 'Path points to a directory (it should point to a file)'
118
+ ) {
119
+ self::absPathLooksSaneExistsAndIsFile($path, $errorMsg);
120
+ }
121
+
122
+
123
+ public static function isString($string, $errorMsg = 'Not a string')
124
+ {
125
+ if (!is_string($string)) {
126
+ throw new ValidateException($errorMsg);
127
+ }
128
+ }
129
+
130
+ public static function pregMatch($pattern, $subject, $errorMsg = 'Does not match expected pattern')
131
+ {
132
+ self::noNUL($subject);
133
+ self::isString($subject);
134
+ if (!preg_match($pattern, $subject)) {
135
+ throw new ValidateException($errorMsg);
136
+ }
137
+ }
138
+
139
+ public static function isJSONArray($json, $errorMsg = 'Not a JSON array')
140
+ {
141
+ self::noNUL($json);
142
+ self::isString($json);
143
+ self::notEmpty($json);
144
+ if ((strpos($json, '[') !== 0) || (!is_array(json_decode($json)))) {
145
+ throw new ValidateException($errorMsg);
146
+ }
147
+ }
148
+
149
+ public static function isJSONObject($json, $errorMsg = 'Not a JSON object')
150
+ {
151
+ self::noNUL($json);
152
+ self::isString($json);
153
+ self::notEmpty($json);
154
+ if ((strpos($json, '{') !== 0) || (!is_object(json_decode($json)))) {
155
+ throw new ValidateException($errorMsg);
156
+ }
157
+ }
158
+
159
+ public static function isConverterId($converterId, $errorMsg = 'Not a valid converter id')
160
+ {
161
+ self::pregMatch('#^[a-z]+$#', $converterId, $errorMsg);
162
+ if (!in_array($converterId, \WebPExpress\ConvertersHelper::getDefaultConverterNames())) {
163
+ throw new ValidateException($errorMsg);
164
+ }
165
+ }
166
+
167
+ }
lib/classes/ValidateException.php ADDED
@@ -0,0 +1,7 @@
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ class ValidateException extends \Exception
6
+ {
7
+ }
lib/migrate/migrate10.php ADDED
@@ -0,0 +1,68 @@
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ use \WebPExpress\Config;
6
+ use \WebPExpress\Messenger;
7
+
8
+ function webpexpress_migrate10() {
9
+
10
+ $config = Config::loadConfigAndFix(false); // false, because we do not need to test if quality detection is working
11
+
12
+ $converters = &$config['converters'];
13
+ if (is_array($converters)) {
14
+
15
+ // Change specific converter options
16
+ foreach ($converters as &$converter) {
17
+ if (!isset($converter['converter'])) {
18
+ continue;
19
+ }
20
+ $options = &$converter['options'];
21
+
22
+ switch ($converter['converter']) {
23
+ case 'wpc':
24
+ if (isset($options['api-version'])) {
25
+ $options['api-version'] = intval($options['api-version']);
26
+ } else {
27
+ $options['api-version'] = 1;
28
+ }
29
+ break;
30
+ case 'cwebp':
31
+ if (isset($options['method'])) {
32
+ $options['method'] = intval($options['method']);
33
+ } else {
34
+ $options['method'] = 6;
35
+ }
36
+ if (isset($options['size-in-percentage'])) {
37
+ $options['size-in-percentage'] = intval($options['size-in-percentage']);
38
+ }
39
+ break;
40
+ }
41
+ }
42
+ }
43
+
44
+
45
+ // Save both configs.
46
+ // The reason we do it is that we need to update wod-options.json, so the scripts can access the newly available
47
+ // "enable-redirection-to-converter" and "enable-redirection-to-webp-realizer" options
48
+
49
+ $forceHtaccessRegeneration = false;
50
+ $result = Config::saveConfigurationAndHTAccess($config, $forceHtaccessRegeneration);
51
+
52
+ if ($result['saved-both-config']) {
53
+ Messenger::addMessage(
54
+ 'info',
55
+ 'Successfully migrated <i>WebP Express</i> options for 0.14.9.'
56
+ );
57
+ Option::updateOption('webp-express-migration-version', '10');
58
+
59
+ } else {
60
+ Messenger::addMessage(
61
+ 'error',
62
+ 'Failed migrating webp express options to 0.14.9. Probably you need to grant write permissions in your wp-content folder.'
63
+ );
64
+ }
65
+
66
+ }
67
+
68
+ webpexpress_migrate10();
lib/options/enqueue_scripts.php CHANGED
@@ -8,8 +8,8 @@ use \WebPExpress\Paths;
8
include_once __DIR__ . '/../classes/Config.php';
9
use \WebPExpress\Config;
10
11
- $ver = '2'; // note: Minimum 1
12
- $jsDir = 'js/0.14.5'; // We change dir when it is critical that no-one gets the cached version (there is a plugin that strips version strings out there...)
13
14
if (!function_exists('webp_express_add_inline_script')) {
15
function webp_express_add_inline_script($id, $script, $position) {
8
include_once __DIR__ . '/../classes/Config.php';
9
use \WebPExpress\Config;
10
11
+ $ver = '1'; // note: Minimum 1
12
+ $jsDir = 'js/0.14.9'; // We change dir when it is critical that no-one gets the cached version (there is a plugin that strips version strings out there...)
13
14
if (!function_exists('webp_express_add_inline_script')) {
15
function webp_express_add_inline_script($id, $script, $position) {
lib/options/js/{0.14.5 → 0.14.9}/authorized_sites_bak.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/bulk-convert.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/converters.js RENAMED
@@ -342,7 +342,7 @@ function updateConverterOptions() {
342
//setConverterOption(converter, 'url-2', document.getElementById('wpc_url_2').value);
343
//setConverterOption(converter, 'secret-2', document.getElementById('wpc_secret_2').value);*/
344
345
- var apiVersion = document.getElementById('wpc_api_version').value;
346
setConverterOption(converter, 'api-version', apiVersion);
347
348
if (apiVersion == '0') {
@@ -367,11 +367,16 @@ function updateConverterOptions() {
367
break;
368
case 'cwebp':
369
setConverterOption(converter, 'use-nice', document.getElementById('cwebp_use_nice').checked);
370
- setConverterOption(converter, 'method', document.getElementById('cwebp_method').value);
371
setConverterOption(converter, 'try-common-system-paths', document.getElementById('cwebp_try_common_system_paths').checked);
372
setConverterOption(converter, 'try-supplied-binary-for-os', document.getElementById('cwebp_try_supplied_binary').checked);
373
setConverterOption(converter, 'set-size', document.getElementById('cwebp_set_size').checked);
374
- setConverterOption(converter, 'size-in-percentage', document.getElementById('cwebp_size_in_percentage').value);
375
setConverterOption(converter, 'command-line-options', document.getElementById('cwebp_command_line_options').value);
376
break;
377
case 'imagemagick':
342
//setConverterOption(converter, 'url-2', document.getElementById('wpc_url_2').value);
343
//setConverterOption(converter, 'secret-2', document.getElementById('wpc_secret_2').value);*/
344
345
+ var apiVersion = parseInt(document.getElementById('wpc_api_version').value, 10);
346
setConverterOption(converter, 'api-version', apiVersion);
347
348
if (apiVersion == '0') {
367
break;
368
case 'cwebp':
369
setConverterOption(converter, 'use-nice', document.getElementById('cwebp_use_nice').checked);
370
+ var methodString = document.getElementById('cwebp_method').value;
371
+ var methodNum = (methodString == '') ? 6 : parseInt(methodString, 10);
372
+ setConverterOption(converter, 'method', methodNum);
373
setConverterOption(converter, 'try-common-system-paths', document.getElementById('cwebp_try_common_system_paths').checked);
374
setConverterOption(converter, 'try-supplied-binary-for-os', document.getElementById('cwebp_try_supplied_binary').checked);
375
setConverterOption(converter, 'set-size', document.getElementById('cwebp_set_size').checked);
376
+
377
+ var sizeInPercentageString = document.getElementById('cwebp_size_in_percentage').value;
378
+ var sizeInPercentageNumber = (sizeInPercentageString == '') ? '' : parseInt(sizeInPercentageString, 10);
379
+ setConverterOption(converter, 'size-in-percentage', sizeInPercentageNumber);
380
setConverterOption(converter, 'command-line-options', document.getElementById('cwebp_command_line_options').value);
381
break;
382
case 'imagemagick':
lib/options/js/{0.14.5 → 0.14.9}/das-popup.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/escapeHTML.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/image-comparison-slider.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/page.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/purge-cache.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/sortable.min.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/test-convert.js RENAMED
File without changes
lib/options/js/{0.14.5 → 0.14.9}/whitelist.js RENAMED
File without changes
lib/options/page-messages.php CHANGED
@@ -205,11 +205,21 @@ if (!Paths::createContentDirIfMissing()) {
205
206
if (Config::isConfigFileThere()) {
207
if (!Config::isConfigFileThereAndOk()) {
208
- Messenger::printMessage(
209
- 'warning',
210
- 'Warning: The configuration file is not ok! (cant be read, or not valid json).<br>' .
211
- 'file: "' . esc_html(Paths::getConfigFileName()) . '"'
212
- );
213
} else {
214
if (HTAccess::arePathsUsedInHTAccessOutdated()) {
215
Messenger::printMessage(
205
206
if (Config::isConfigFileThere()) {
207
if (!Config::isConfigFileThereAndOk()) {
208
+ $json = FileHelper::loadFile(Paths::getConfigFileName());
209
+ if ($json === false) {
210
+ Messenger::printMessage(
211
+ 'warning',
212
+ 'Warning: The configuration file is not ok! (cant be read).<br>' .
213
+ 'file: "' . esc_html(Paths::getConfigFileName()) . '"'
214
+ );
215
+ } else {
216
+ Messenger::printMessage(
217
+ 'warning',
218
+ 'Warning: The configuration file is not ok! (not valid json).<br>' .
219
+ 'file: "' . esc_html(Paths::getConfigFileName()) . '"'
220
+ );
221
+ }
222
+
223
} else {
224
if (HTAccess::arePathsUsedInHTAccessOutdated()) {
225
Messenger::printMessage(
lib/options/submit.php CHANGED
@@ -169,10 +169,7 @@ function webpexpress_getSanitizedConverters() {
169
$convertersSanitized = [];
170
171
// Get list of possible converter ids.
172
- $availableConverterIDs = [];
173
- foreach (ConvertersHelper::$defaultConverters as $converter) {
174
- $availableConverterIDs[] = $converter['converter'];
175
- }
176
177
// Add converters one at the time.
178
foreach ($convertersPosted as $unsanitizedConverter) {
@@ -211,8 +208,8 @@ function webpexpress_getSanitizedConverters() {
211
// cwebp
212
"try-common-system-paths" => 'boolean',
213
"try-supplied-binary-for-os" => 'boolean',
214
- "method" => 'string', // 0-6, // TODO: make a migration so we use integer instead
215
- "size-in-percentage" => 'string',
216
"low-memory" => 'boolean',
217
"command-line-options" => 'string', // webp-convert takes care of sanitizing this very carefully!
218
"set-size" => 'boolean',
169
$convertersSanitized = [];
170
171
// Get list of possible converter ids.
172
+ $availableConverterIDs = ConvertersHelper::getDefaultConverterNames();
173
174
// Add converters one at the time.
175
foreach ($convertersPosted as $unsanitizedConverter) {
208
// cwebp
209
"try-common-system-paths" => 'boolean',
210
"try-supplied-binary-for-os" => 'boolean',
211
+ "method" => 'integer', // 0-6,
212
+ "size-in-percentage" => 'integer', // 0-100
213
"low-memory" => 'boolean',
214
"command-line-options" => 'string', // webp-convert takes care of sanitizing this very carefully!
215
"set-size" => 'boolean',
vendor/composer/installed.json CHANGED
@@ -115,17 +115,17 @@
115
},
116
{
117
"name": "rosell-dk/webp-convert",
118
- "version": "2.1.1",
119
- "version_normalized": "2.1.1.0",
120
"source": {
121
"type": "git",
122
"url": "https://github.com/rosell-dk/webp-convert.git",
123
- "reference": "cc0ceb81193a0b930b2e7c58886a7f3662fb45c6"
124
},
125
"dist": {
126
"type": "zip",
127
- "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/cc0ceb81193a0b930b2e7c58886a7f3662fb45c6",
128
- "reference": "cc0ceb81193a0b930b2e7c58886a7f3662fb45c6",
129
"shasum": ""
130
},
131
"require": {
@@ -143,7 +143,7 @@
143
"ext-vips": "to use Vips extension for converting.",
144
"php-stan/php-stan": "Suggested for dev, in order to analyse code before committing"
145
},
146
- "time": "2019-06-21T11:15:37+00:00",
147
"type": "library",
148
"extra": {
149
"scripts-descriptions": {
115
},
116
{
117
"name": "rosell-dk/webp-convert",
118
+ "version": "2.1.3",
119
+ "version_normalized": "2.1.3.0",
120
"source": {
121
"type": "git",
122
"url": "https://github.com/rosell-dk/webp-convert.git",
123
+ "reference": "959f0515b79a785fa1015c0023c22619f55d6392"
124
},
125
"dist": {
126
"type": "zip",
127
+ "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/959f0515b79a785fa1015c0023c22619f55d6392",
128
+ "reference": "959f0515b79a785fa1015c0023c22619f55d6392",
129
"shasum": ""
130
},
131
"require": {
143
"ext-vips": "to use Vips extension for converting.",
144
"php-stan/php-stan": "Suggested for dev, in order to analyse code before committing"
145
},
146
+ "time": "2019-06-22T22:24:30+00:00",
147
"type": "library",
148
"extra": {
149
"scripts-descriptions": {
vendor/rosell-dk/webp-convert/composer.json CHANGED
@@ -31,6 +31,7 @@
31
],
32
"test-src": "phpunit --coverage-text",
33
"phpunit": "phpunit --coverage-text",
34
"cs-fix-all": [
35
"php-cs-fixer fix src"
36
],
31
],
32
"test-src": "phpunit --coverage-text",
33
"phpunit": "phpunit --coverage-text",
34
+ "test-no-cov": "phpunit --no-coverage",
35
"cs-fix-all": [
36
"php-cs-fixer fix src"
37
],
vendor/rosell-dk/webp-convert/docs/v2.0/converting/options.md CHANGED
@@ -28,6 +28,30 @@ Supported by: cwebp
28
```
29
This allows you to set any parameter available for cwebp in the same way as you would do when executing *cwebp*. You could ie set it to "-sharpness 5 -mt -crop 10 10 40 40". Read more about all the available parameters in [the docs](https://developers.google.com/speed/webp/docs/cwebp).<br><br>
30
31
### `default-quality`
32
```
33
Type: integer (0-100)
@@ -169,9 +193,6 @@ $options = [
169
'gmagick-skip' => true,
170
];
171
```
172
-
173
-
174
-
175
<br>
176
177
### `stack-converters`
28
```
29
This allows you to set any parameter available for cwebp in the same way as you would do when executing *cwebp*. You could ie set it to "-sharpness 5 -mt -crop 10 10 40 40". Read more about all the available parameters in [the docs](https://developers.google.com/speed/webp/docs/cwebp).<br><br>
30
31
+ ### `cwebp-rel-path-to-precompiled-binaries`
32
+ ```
33
+ Type: string
34
+ Default: './Binaries'
35
+ Supported by: cwebp
36
+ ```
37
+ Allows you to change where to look for the precompiled binaries. While this may look as a risk, it is completely safe, as the binaries are hash-checked before being executed. The option is needed when you are using two-file version of webp-on-demand.
38
+
39
+ ### `cwebp-try-common-system-paths`
40
+ ```
41
+ Type: boolean
42
+ Default: true
43
+ Supported by: cwebp
44
+ ```
45
+ If set, the converter will try to look for cwebp in locations such as `/usr/bin/cwebp`.
46
+
47
+ ### `cwebp-try-supplied-binary-for-os`
48
+ ```
49
+ Type: boolean
50
+ Default: true
51
+ Supported by: cwebp
52
+ ```
53
+ If set, the converter will try the precompiled cwebp binary that are located in `src/Convert/Converters/Binaries`, for the current OS. The binaries are hash-checked before executed.
54
+
55
### `default-quality`
56
```
57
Type: integer (0-100)
193
'gmagick-skip' => true,
194
];
195
```
196
<br>
197
198
### `stack-converters`
vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php CHANGED
@@ -125,7 +125,9 @@ class Cwebp extends AbstractConverter
125
private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
126
{
127
if (!ctype_print($commandLineOptions)) {
128
- throw new ConversionFailedException('Non-printable characters are not allowed in the extra command line options');
129
}
130
131
if (preg_match('#[^a-zA-Z0-9_\s\-]#', $commandLineOptions)) {
125
private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
126
{
127
if (!ctype_print($commandLineOptions)) {
128
+ throw new ConversionFailedException(
129
+ 'Non-printable characters are not allowed in the extra command line options'
130
+ );
131
}
132
133
if (preg_match('#[^a-zA-Z0-9_\s\-]#', $commandLineOptions)) {
vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php CHANGED
@@ -38,8 +38,14 @@ class PathChecker
38
}
39
40
// Prevent non printable characters
41
if (!ctype_print($absFilePath)) {
42
throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
43
}
44
45
// Prevent directory traversal
38
}
39
40
// Prevent non printable characters
41
+ /*
42
if (!ctype_print($absFilePath)) {
43
throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
44
+ }*/
45
+
46
+ // Prevent control characters (at least the first 32 (#0 - #1f)
47
+ if (preg_match('#[\x{0}-\x{1f}]#', $absFilePath)) {
48
+ throw new InvalidInputException('Non-printable characters are not allowed');
49
}
50
51
// Prevent directory traversal
vendor/rosell-dk/webp-convert/src/Serve/Report.php CHANGED
@@ -38,7 +38,7 @@ class Report
38
try {
39
$echoLogger = new EchoLogger();
40
$options['log-call-arguments'] = true;
41
- WebPConvert::convert($source, $destination, $options, $echoLogger);
42
} catch (\Exception $e) {
43
$msg = $e->getMessage();
44
echo '<b>' . $msg . '</b>';
38
try {
39
$echoLogger = new EchoLogger();
40
$options['log-call-arguments'] = true;
41
+ WebPConvert::convert($source, $destination, $options['convert'], $echoLogger);
42
} catch (\Exception $e) {
43
$msg = $e->getMessage();
44
echo '<b>' . $msg . '</b>';
webp-express.php CHANGED
@@ -3,7 +3,7 @@
3
* Plugin Name: WebP Express
4
* Plugin URI: https://github.com/rosell-dk/webp-express
5
* Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
- * Version: 0.14.8
7
* Author: Bjørn Rosell
8
* Author URI: https://www.bitwise-it.dk
9
* License: GPL2
@@ -22,7 +22,7 @@ use \WebPExpress\Option;
22
define('WEBPEXPRESS_PLUGIN', __FILE__);
23
define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
24
25
- // Autoloading rules!
26
spl_autoload_register('webpexpress_autoload');
27
function webpexpress_autoload($class) {
28
if (strpos($class, 'WebPExpress\\') === 0) {
@@ -37,14 +37,15 @@ if (is_admin()) {
37
function webp_express_process_post() {
38
// strip query string
39
$requestUriNoQS = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
40
41
- if (!preg_match('/webp-express-web-service#x2F;', $requestUriNoQS)) {
42
- return;
43
}
44
- include __DIR__ . '/web-service/wpc.php';
45
- die();
46
}
47
add_action( 'init', 'webp_express_process_post' );
48
49
if (Option::getOption('webp-express-alter-html', false)) {
50
require_once __DIR__ . '/lib/classes/AlterHtmlInit.php';
3
* Plugin Name: WebP Express
4
* Plugin URI: https://github.com/rosell-dk/webp-express
5
* Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
+ * Version: 0.14.9
7
* Author: Bjørn Rosell
8
* Author URI: https://www.bitwise-it.dk
9
* License: GPL2
22
define('WEBPEXPRESS_PLUGIN', __FILE__);
23
define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
24
25
+ // Autoload WebPExpress classes
26
spl_autoload_register('webpexpress_autoload');
27
function webpexpress_autoload($class) {
28
if (strpos($class, 'WebPExpress\\') === 0) {
37
function webp_express_process_post() {
38
// strip query string
39
$requestUriNoQS = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
40
+ //echo '<pre>' . print_r($_SERVER, true) . '</pre>'; die();
41
42
+ if (preg_match('/webp-express-web-service#x2F;', $requestUriNoQS)) {
43
+ include __DIR__ . '/web-service/wpc.php';
44
+ die();
45
}
46
}
47
add_action( 'init', 'webp_express_process_post' );
48
+ //add_action( 'parse_request', 'webp_express_process_post' );
49
50
if (Option::getOption('webp-express-alter-html', false)) {
51
require_once __DIR__ . '/lib/classes/AlterHtmlInit.php';
wod/webp-on-demand.php CHANGED
@@ -1,238 +1,285 @@
1
<?php
2
3
use \WebPConvert\WebPConvert;
4
use \WebPConvert\Serve\ServeConvertedWebP;
5
-
6
- include_once "../lib/classes/ConvertHelperIndependent.php";
7
use \WebPExpress\ConvertHelperIndependent;
8
9
- function exitWithError($msg) {
10
- header('X-WebP-Express-Error: ' . $msg, true);
11
- echo $msg;
12
- exit;
13
- }
14
15
- //echo $_SERVER["SERVER_SOFTWARE"]; exit;
16
- //stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false
17
18
- // Protect against directly accessing webp-on-demand.php
19
- // Only protect on Apache. We know for sure that the method is not reliable on nginx. We have not tested on litespeed yet, so we dare not.
20
- if (stripos($_SERVER["SERVER_SOFTWARE"], 'apache') !== false && stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') === false) {
21
- if (strpos($_SERVER['REQUEST_URI'], 'webp-on-demand.php') !== false) {
22
- exitWithError('It seems you are visiting this file (plugins/webp-express/wod/webp-on-demand.php) directly. We do not allow this.');
23
exit;
24
}
25
- }
26
27
-
28
- /**
29
- * Get environment variable set with mod_rewrite module
30
- * Return false if the environment variable isn't found
31
- */
32
- function getEnvPassedInRewriteRule($envName) {
33
- // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
34
- // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
35
- // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
36
- // We simply look for an environment variable that ends with what we are looking for.
37
- // (so make sure to make it unique)
38
- $len = strlen($envName);
39
- foreach ($_SERVER as $key => $item) {
40
- if (substr($key, -$len) == $envName) {
41
- return $item;
42
}
43
}
44
- return false;
45
- }
46
47
- function loadConfig($configFilename) {
48
- if (!file_exists($configFilename)) {
49
- header('X-WebP-Express-Error: Configuration file not found!', true);
50
- echo 'Configuration file not found!';
51
- //WebPConvert::convertAndServe($source, $destination, []);
52
- exit;
53
- }
54
55
- // TODO: Handle read error / json error
56
- $handle = @fopen($configFilename, "r");
57
- $json = fread($handle, filesize($configFilename));
58
- fclose($handle);
59
- return json_decode($json, true);
60
- }
61
62
- /**
63
- * Get absolute path to source file.
64
- *
65
- * The path can be passed to this file from the .htaccess file / nginx config in various ways.
66
- *
67
- * @return string Absolute path to source (unsanitized! - call sanitizeAbsFilePath immidiately after calling this method)
68
- */
69
- function getSourceUnsanitized() {
70
- global $wodOptions;
71
- global $docRoot;
72
-
73
- // First check if it is in an environment variable - thats the safest way
74
- $source = getEnvPassedInRewriteRule('REQFN');
75
- if ($source !== false) {
76
- return $source;
77
- }
78
79
- // Then header
80
- if (isset($wodOptions['base-htaccess-on-these-capability-tests'])) {
81
- $capTests = $wodOptions['base-htaccess-on-these-capability-tests'];
82
- $passThroughHeaderDefinitelyUnavailable = ($capTests['passThroughHeaderWorking'] === false);
83
- $passThrougEnvVarDefinitelyAvailable =($capTests['passThroughEnvWorking'] === true);
84
- } else {
85
- $passThroughHeaderDefinitelyUnavailable = false;
86
- $passThrougEnvVarDefinitelyAvailable = false;
87
- }
88
- if ((!$passThrougEnvVarDefinitelyAvailable) && (!$passThroughHeaderDefinitelyUnavailable)) {
89
- if (isset($_SERVER['HTTP_REQFN'])) {
90
- return $_SERVER['HTTP_REQFN'];
91
}
92
- }
93
94
- // Then querystring (full path).
95
- // PS: The parameters in $_GET are already url decoded. Sanitizing happens on the result
96
97
- // TODO: perhaps only allow passing absolute path on nginx?
98
- if (isset($_GET['xsource'])) {
99
- return substr($_GET['xsource'], 1); // No url decoding needed as $_GET is already decoded
100
- } elseif (isset($_GET['source'])) {
101
- return $_GET['source'];
102
}
103
104
- // Then querystring (relative path)
105
- $srcRel = '';
106
- if (isset($_GET['xsource-rel'])) {
107
- $srcRel = substr($_GET['xsource-rel'], 1);
108
- } elseif (isset($_GET['source-rel'])) {
109
- $srcRel = $_GET['source-rel'];
110
}
111
- if ($srcRel != '') {
112
- if (isset($_GET['source-rel-filter'])) {
113
- if ($_GET['source-rel-filter'] == 'discard-parts-before-wp-content') {
114
- $parts = explode('/', $srcRel);
115
- $wp_content = isset($_GET['wp-content']) ? $_GET['wp-content'] : 'wp-content';
116
-
117
- if (in_array($wp_content, $parts)) {
118
- foreach($parts as $index => $part) {
119
- if($part !== $wp_content) {
120
- unset($parts[$index]);
121
- } else {
122
- break;
123
- }
124
- }
125
- $srcRel = implode('/', $parts);
126
- }
127
}
128
}
129
- return $docRoot . '/' . $srcRel;
130
- }
131
132
- // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
133
- // correct result in all setups (ie "folder method 1")
134
- $requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
135
- //$docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
136
- $source = $docRoot . urldecode($requestUriNoQS);
137
- if (file_exists($source)) {
138
- return $source;
139
- }
140
141
- // No luck whatsoever!
142
- exitWithError('webp-on-demand.php was not passed any filename to convert');
143
- }
144
145
- function getSource() {
146
- return ConvertHelperIndependent::sanitizeAbsFilePath(
147
- getSourceUnsanitized()
148
- );
149
- }
150
151
- function getWpContentRel() {
152
- // Passed in env variable?
153
- $wpContentDirRel = getEnvPassedInRewriteRule('WPCONTENT');
154
- if ($wpContentDirRel !== false) {
155
- return $wpContentDirRel;
156
- }
157
158
- // Passed in QS?
159
- if (isset($_GET['wp-content'])) {
160
- return $_GET['wp-content'];
161
- }
162
163
- // In case above fails, fall back to standard location
164
- return 'wp-content';
165
- }
166
167
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
168
169
- // PS: the following sanitizing will remove any "../" (which is good)
170
- $webExpressContentDirAbs = ConvertHelperIndependent::sanitizeAbsFilePath($docRoot . '/' . getWpContentRel() . '/webp-express');
171
- $options = loadConfig($webExpressContentDirAbs . '/config/wod-options.json');
172
173
- // TODO:
174
- // Exit here, if not configured to redirect to conversion script
175
176
- $wodOptions = $options['wod'];
177
- $serveOptions = $options['webp-convert'];
178
- $convertOptions = &$serveOptions['convert'];
179
180
181
- $source = getSource();
182
- //$source = getSource(false, false);
183
184
- //echo $source; exit;
185
186
- if (!file_exists($source)) {
187
- header('X-WebP-Express-Error: Source file not found!', true);
188
- echo 'Source file not found!';
189
- exit;
190
- }
191
192
- $destination = ConvertHelperIndependent::getDestination(
193
- $source,
194
- $wodOptions['destination-folder'],
195
- $wodOptions['destination-extension'],
196
- $webExpressContentDirAbs,
197
- $docRoot . '/' . $wodOptions['paths']['uploadDirRel']
198
- );
199
-
200
- //echo $destination; exit;
201
- //echo '<pre>' . print_r($wodOptions, true) . '</pre>'; exit;
202
-
203
- foreach ($convertOptions['converters'] as &$converter) {
204
- if (isset($converter['converter'])) {
205
- $converterId = $converter['converter'];
206
- } else {
207
- $converterId = $converter;
208
- }
209
- if ($converterId == 'cwebp') {
210
- $converter['options']['rel-path-to-precompiled-binaries'] = '../src/Converters/Binaries';
211
- }
212
- }
213
214
- if ($wodOptions['forward-query-string']) {
215
- if (isset($_GET['debug'])) {
216
- $serveOptions['show-report'] = true;
217
- }
218
- if (isset($_GET['reconvert'])) {
219
- $serveOptions['reconvert'] = true;
220
}
221
}
222
223
-
224
- if (isset($wodOptions['success-response']) && ($wodOptions['success-response'] == 'original')) {
225
- $serveOptions['serve-original'] = true;
226
- $serveOptions['serve-image']['headers']['vary-accept'] = false;
227
- } else {
228
- $serveOptions['serve-image']['headers']['vary-accept'] = true;
229
}
230
231
-
232
- ConvertHelperIndependent::serveConverted(
233
- $source,
234
- $destination,
235
- $serveOptions,
236
- $webExpressContentDirAbs . '/log',
237
- 'Conversion triggered with the conversion script (wod/webp-on-demand.php)'
238
- );
1
<?php
2
+ namespace WebPExpress;
3
4
use \WebPConvert\WebPConvert;
5
use \WebPConvert\Serve\ServeConvertedWebP;
6
use \WebPExpress\ConvertHelperIndependent;
7
+ use \WebPExpress\Sanitize;
8
+ use \WebPExpress\ValidateException;
9
10
+ class WebPOnDempand
11
+ {
12
13
+ private static $docRoot;
14
15
+ public static function exitWithError($msg) {
16
+ header('X-WebP-Express-Error: ' . $msg, true);
17
+ echo $msg;
18
exit;
19
}
20
21
+ //echo $_SERVER["SERVER_SOFTWARE"]; exit;
22
+ //stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false
23
+
24
+
25
+ /**
26
+ * Get environment variable set with mod_rewrite module
27
+ * Return false if the environment variable isn't found
28
+ */
29
+ static function getEnvPassedInRewriteRule($envName) {
30
+ // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
31
+ // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
32
+ // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
33
+ // We simply look for an environment variable that ends with what we are looking for.
34
+ // (so make sure to make it unique)
35
+ $len = strlen($envName);
36
+ foreach ($_SERVER as $key => $item) {
37
+ if (substr($key, -$len) == $envName) {
38
+ return $item;
39
+ }
40
}
41
+ return false;
42
}
43
44
+ /**
45
+ * Get absolute path to source file.
46
+ *
47
+ * The path can be passed to this file from the .htaccess file / nginx config in various ways.
48
+ *
49
+ * @return string Absolute path to source (unsanitized! - call sanitizeAbsFilePath immidiately after calling this method)
50
+ */
51
+ static function getSourceUnsanitized($docRoot, $wodOptions) {
52
+
53
+ // First check if it is in an environment variable - thats the safest way
54
+ $source = self::getEnvPassedInRewriteRule('REQFN');
55
+ if ($source !== false) {
56
+ return $source;
57
+ }
58
59
+ // Then header
60
+ if (isset($wodOptions['base-htaccess-on-these-capability-tests'])) {
61
+ $capTests = $wodOptions['base-htaccess-on-these-capability-tests'];
62
+ $passThroughHeaderDefinitelyUnavailable = ($capTests['passThroughHeaderWorking'] === false);
63
+ $passThrougEnvVarDefinitelyAvailable =($capTests['passThroughEnvWorking'] === true);
64
+ } else {
65
+ $passThroughHeaderDefinitelyUnavailable = false;
66
+ $passThrougEnvVarDefinitelyAvailable = false;
67
+ }
68
+ if ((!$passThrougEnvVarDefinitelyAvailable) && (!$passThroughHeaderDefinitelyUnavailable)) {
69
+ if (isset($_SERVER['HTTP_REQFN'])) {
70
+ return $_SERVER['HTTP_REQFN'];
71
+ }
72
+ }
73
74
+ // Then querystring (relative path)
75
+ $srcRel = '';
76
+ if (isset($_GET['xsource-rel'])) {
77
+ $srcRel = substr($_GET['xsource-rel'], 1);
78
+ } elseif (isset($_GET['source-rel'])) {
79
+ $srcRel = $_GET['source-rel'];
80
+ }
81
+ if ($srcRel != '') {
82
+ /*
83
+ if (isset($_GET['source-rel-filter'])) {
84
+ if ($_GET['source-rel-filter'] == 'discard-parts-before-wp-content') {
85
+ $parts = explode('/', $srcRel);
86
+ $wp_content = isset($_GET['wp-content']) ? $_GET['wp-content'] : 'wp-content';
87
+
88
+ if (in_array($wp_content, $parts)) {
89
+ foreach($parts as $index => $part) {
90
+ if($part !== $wp_content) {
91
+ unset($parts[$index]);
92
+ } else {
93
+ break;
94
+ }
95
+ }
96
+ $srcRel = implode('/', $parts);
97
+ }
98
+ }
99
+ }*/
100
+ return $docRoot . '/' . $srcRel;
101
+ }
102
+
103
+ // Then querystring (full path) - But only on Nginx (our Apache .htaccess rules never passes absolute url)
104
+ if (stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false) {
105
+
106
+ // On Nginx, we allow passing absolute path
107
+ if (isset($_GET['xsource'])) {
108
+ return substr($_GET['xsource'], 1); // No url decoding needed as $_GET is already decoded
109
+ } elseif (isset($_GET['source'])) {
110
+ return $_GET['source'];
111
+ }
112
113
}
114
115
+ // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
116
+ // correct result in all setups (ie "folder method 1")
117
+ $requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
118
+ //$docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
119
+ $source = Sanitize::removeNUL($docRoot . urldecode($requestUriNoQS));
120
+ if (@file_exists($source)) {
121
+ return $source;
122
+ }
123
124
+ // No luck whatsoever!
125
+ self::exitWithError('webp-on-demand.php was not passed any filename to convert');
126
}
127
128
+ private static function getWpContentRelUnsanitized() {
129
+ // Passed in env variable?
130
+ $wpContentDirRel = self::getEnvPassedInRewriteRule('WPCONTENT');
131
+ if ($wpContentDirRel !== false) {
132
+ return $wpContentDirRel;
133
+ }
134
+
135
+ // Passed in QS?
136
+ if (isset($_GET['wp-content'])) {
137
+ return $_GET['wp-content'];
138
+ }
139
+
140
+ // In case above fails, fall back to standard location
141
+ return 'wp-content';
142
}
143
+
144
+ /*
145
+ static function registerAutoload()
146
+ {
147
+ define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
148
+
149
+ // Autoload WebPExpress classes
150
+ spl_autoload_register('webpexpress_autoload');
151
+ function webpexpress_autoload($class) {
152
+ if (strpos($class, 'WebPExpress\\') === 0) {
153
+ require_once WEBPEXPRESS_PLUGIN_DIR . '/lib/classes/' . substr($class, 12) . '.php';
154
}
155
}
156
+ }*/
157
158
+ static function process() {
159
160
+ include_once "../lib/classes/ConvertHelperIndependent.php";
161
+ include_once __DIR__ . '/../lib/classes/Sanitize.php';
162
+ include_once __DIR__ . '/../lib/classes/Validate.php';
163
+ include_once __DIR__ . '/../lib/classes/ValidateException.php';
164
165
+ // Validate!
166
+ // ----------
167
168
+ try {
169
170
+ // Validate DOCUMENT_ROOT
171
+ // ----------------------
172
+ $validating = 'DOCUMENT_ROOT';
173
+ $realPathResult = realpath(Sanitize::removeNUL($_SERVER["DOCUMENT_ROOT"]));
174
+ if ($realPathResult === false) {
175
+ throw new ValidateException('Cannot find document root');
176
+ }
177
+ $docRoot = rtrim($realPathResult, '/');
178
+ Validate::absPathLooksSaneExistsAndIsDir($docRoot);
179
+ $docRoot = $docRoot;
180
181
182
+ // Validate WebP Express content dir
183
+ // ---------------------------------
184
+ $validating = 'WebP Express content dir';
185
+ $webExpressContentDirAbs = ConvertHelperIndependent::sanitizeAbsFilePath(
186
+ $docRoot . '/' . self::getWpContentRelUnsanitized() . '/webp-express'
187
+ );
188
+ Validate::absPathLooksSaneExistsAndIsDir($webExpressContentDirAbs);
189
190
191
+ // Validate config file name
192
+ // ---------------------------------
193
+ $validating = 'config file';
194
+ $configFilename = $webExpressContentDirAbs . '/config/wod-options.json';
195
+ Validate::absPathLooksSaneExistsAndIsFile($configFilename);
196
197
198
+ // Validate config file
199
+ // --------------------
200
+ $configLoadResult = file_get_contents($configFilename);
201
+ if ($configLoadResult === false) {
202
+ throw new ValidateException('Cannot open config file');
203
+ }
204
+ Validate::isJSONObject($configLoadResult);
205
+ $json = $configLoadResult;
206
+ $options = json_decode($json, true);
207
+ $wodOptions = $options['wod'];
208
+ $serveOptions = $options['webp-convert'];
209
+ $convertOptions = &$serveOptions['convert'];
210
+ //echo '<pre>' . print_r($wodOptions, true) . '</pre>'; exit;
211
+
212
+
213
+ // Validate that WebPExpress was configured to redirect to this conversion script
214
+ // ------------------------------------------------------------------------------
215
+ $validating = 'settings';
216
+ if (!isset($wodOptions['enable-redirection-to-converter']) || ($wodOptions['enable-redirection-to-converter'] === false)) {
217
+ throw new ValidateException('Redirection to conversion script is not enabled');
218
+ }
219
220
221
+ // Validate source (the image to be converted)
222
+ // --------------------------------------------
223
+ $validating = 'source';
224
+ $source = Sanitize::removeNUL(self::getSourceUnsanitized($docRoot, $options['wod']));
225
+ Validate::absPathLooksSaneExistsAndIsFile($source);
226
+ //echo $source; exit;
227
+
228
+
229
+ // Validate destination path
230
+ // --------------------------------------------
231
+ $validating = 'destination path';
232
+ $destination = ConvertHelperIndependent::getDestination(
233
+ $source,
234
+ $wodOptions['destination-folder'],
235
+ $wodOptions['destination-extension'],
236
+ $webExpressContentDirAbs,
237
+ $docRoot . '/' . $wodOptions['paths']['uploadDirRel']
238
+ );
239
+ Validate::absPathLooksSane($destination);
240
+ //echo $destination; exit;
241
+
242
+ } catch (ValidateException $e) {
243
+ self::exitWithError('failed validating ' . $validating . ': '. $e->getMessage());
244
+ }
245
246
+ /*
247
+ if ($wodOptions['forward-query-string']) {
248
+ if (isset($_GET['debug'])) {
249
+ $serveOptions['show-report'] = true;
250
+ }
251
+ if (isset($_GET['reconvert'])) {
252
+ $serveOptions['reconvert'] = true;
253
+ }
254
+ }*/
255
256
+ if (isset($wodOptions['success-response']) && ($wodOptions['success-response'] == 'original')) {
257
+ $serveOptions['serve-original'] = true;
258
+ $serveOptions['serve-image']['headers']['vary-accept'] = false;
259
+ } else {
260
+ $serveOptions['serve-image']['headers']['vary-accept'] = true;
261
+ }
262
263
+ //echo '<pre>' . print_r($serveOptions, true) . '</pre>'; exit;
264
+ //$serveOptions['show-report'] = true;
265
+
266
+ ConvertHelperIndependent::serveConverted(
267
+ $source,
268
+ $destination,
269
+ $serveOptions,
270
+ $webExpressContentDirAbs . '/log',
271
+ 'Conversion triggered with the conversion script (wod/webp-on-demand.php)'
272
+ );
273
}
274
}
275
276
+ // Protect against directly accessing webp-on-demand.php
277
+ // Only protect on Apache. We know for sure that the method is not reliable on nginx. We have not tested on litespeed yet, so we dare not.
278
+ if (stripos($_SERVER["SERVER_SOFTWARE"], 'apache') !== false && stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') === false) {
279
+ if (strpos($_SERVER['REQUEST_URI'], 'webp-on-demand.php') !== false) {
280
+ WebPOnDempand::exitWithError('It seems you are visiting this file (plugins/webp-express/wod/webp-on-demand.php) directly. We do not allow this.');
281
+ exit;
282
+ }
283
}
284
285
+ WebPOnDempand::process();
wod/webp-realizer.php CHANGED
@@ -1,213 +1,278 @@
1
<?php
2
3
use \WebPConvert\WebPConvert;
4
- use \WebPConvert\ServeExistingOrHandOver;
5
-
6
- include_once "../lib/classes/ConvertHelperIndependent.php";
7
use \WebPExpress\ConvertHelperIndependent;
8
9
- function exitWithError($msg) {
10
- header('X-WebP-Express-Error: ' . $msg, true);
11
- echo $msg;
12
- exit;
13
- }
14
15
- // Protect against directly accessing webp-on-demand.php
16
- // Only protect on Apache. We know for sure that the method is not reliable on nginx. We have not tested on litespeed yet, so we dare not.
17
- if (stripos($_SERVER["SERVER_SOFTWARE"], 'apache') !== false && stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') === false) {
18
- if (strpos($_SERVER['REQUEST_URI'], 'webp-realizer.php') !== false) {
19
- exitWithError('It seems you are visiting this file (plugins/webp-express/wod/webp-realizer.php) directly. We do not allow this.');
20
exit;
21
}
22
- }
23
24
- /**
25
- * Get environment variable set with mod_rewrite module
26
- * Return false if the environment variable isn't found
27
- */
28
- function getEnvPassedInRewriteRule($envName) {
29
- // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
30
- // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
31
- // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
32
- // We simply look for an environment variable that ends with what we are looking for.
33
- // (so make sure to make it unique)
34
- $len = strlen($envName);
35
- foreach ($_SERVER as $key => $item) {
36
- if (substr($key, -$len) == $envName) {
37
- return $item;
38
}
39
}
40
- return false;
41
- }
42
43
- function loadConfig($configFilename) {
44
- if (!file_exists($configFilename)) {
45
- header('X-WebP-Express-Error: Configuration file not found!', true);
46
- echo 'Configuration file not found!';
47
- //WebPConvert::convertAndServe($source, $destination, []);
48
- exit;
49
- }
50
51
- // TODO: Handle read error / json error
52
- $handle = @fopen($configFilename, "r");
53
- $json = fread($handle, filesize($configFilename));
54
- fclose($handle);
55
- return json_decode($json, true);
56
- }
57
58
- /*
59
- function getDestinationRealPath($dest) {
60
- //echo $_SERVER["DOCUMENT_ROOT"] . '<br>' . $dest . '<br>';
61
- if (strpos($dest, $_SERVER["DOCUMENT_ROOT"]) === 0) {
62
- return realpath($_SERVER["DOCUMENT_ROOT"]) . substr($dest, strlen($_SERVER["DOCUMENT_ROOT"]));
63
- } else {
64
return $dest;
65
}
66
- }*/
67
68
- function getDestinationUnsanitized() {
69
- global $docRoot;
70
71
- // First check if it is in an environment variable - thats the safest way
72
- $destinationRel = getEnvPassedInRewriteRule('DESTINATIONREL');
73
- if ($destinationRel !== false) {
74
- return $docRoot . '/' . $destinationRel;
75
- }
76
77
- // Next, check querystring (full path)
78
- if (isset($_GET['xdestination'])) {
79
- return substr($_GET['xdestination'], 1); // No url decoding needed as $_GET is already decoded
80
- } elseif (isset($_GET['destination'])) {
81
- return $_GET['destination'];
82
- }
83
84
- // Next, check querystring (relative path)
85
- $destinationRel = '';
86
- if (isset($_GET['xdestination-rel'])) {
87
- $destinationRel = substr($_GET['xdestination-rel'], 1);
88
- } elseif (isset($_GET['destination-rel'])) {
89
- $destinationRel = $_GET['destination-rel'];
90
}
91
- if ($destinationRel != '') {
92
- if (isset($_GET['source-rel-filter'])) {
93
- if ($_GET['source-rel-filter'] == 'discard-parts-before-wp-content') {
94
- $parts = explode('/', $destinationRel);
95
- $wp_content = isset($_GET['wp-content']) ? $_GET['wp-content'] : 'wp-content';
96
-
97
- if (in_array($wp_content, $parts)) {
98
- foreach($parts as $index => $part) {
99
- if($part !== $wp_content) {
100
- unset($parts[$index]);
101
- } else {
102
- break;
103
- }
104
- }
105
- $destinationRel = implode('/', $parts);
106
- }
107
}
108
}
109
- return $docRoot . '/' . $destinationRel;
110
- }
111
112
- // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
113
- // correct result in all setups (ie "folder method 1")
114
- $requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
115
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
116
- $dest = $docRoot . urldecode($requestUriNoQS);
117
- return $dest;
118
- }
119
120
- function getDestination() {
121
- return ConvertHelperIndependent::sanitizeAbsFilePath(
122
- getDestinationUnsanitized()
123
- );
124
- }
125
126
- function getWpContentRel() {
127
- // Passed in env variable?
128
- $wpContentDirRel = getEnvPassedInRewriteRule('WPCONTENT');
129
- if ($wpContentDirRel !== false) {
130
- return $wpContentDirRel;
131
- }
132
133
- // Passed in QS?
134
- if (isset($_GET['wp-content'])) {
135
- return $_GET['wp-content'];
136
- }
137
138
- // In case above fails, fall back to standard location
139
- return 'wp-content';
140
- }
141
142
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
143
- $webExpressContentDirAbs = $docRoot . '/' . getWpContentRel() . '/webp-express';
144
- $options = loadConfig($webExpressContentDirAbs . '/config/wod-options.json');
145
- $wodOptions = $options['wod'];
146
- $serveOptions = $options['webp-convert'];
147
- $convertOptions = &$serveOptions['convert'];
148
149
- $destination = getDestination();
150
151
- //echo 'destination: ' . $destination; exit;
152
153
- $source = ConvertHelperIndependent::findSource(
154
- $destination,
155
- $wodOptions['destination-folder'],
156
- $wodOptions['destination-extension'],
157
- $webExpressContentDirAbs
158
- );
159
160
- if ($source === false) {
161
- header('X-WebP-Express-Error: webp-realizer.php could not find an existing jpg or png that corresponds to the webp requested', true);
162
163
- $protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
164
- header($protocol . " 404 Not Found");
165
- //echo '<p>webp-realizer.php could not find an existing jpg or png that corresponds to the webp requested!</p>';
166
- //echo 'destination requested:<br><i>' . $destination . '</i>';
167
- }
168
169
- foreach ($convertOptions['converters'] as &$converter) {
170
- if (isset($converter['converter'])) {
171
- $converterId = $converter['converter'];
172
- } else {
173
- $converterId = $converter;
174
- }
175
- if ($converterId == 'cwebp') {
176
- $converter['options']['rel-path-to-precompiled-binaries'] = '../src/Converters/Binaries';
177
- }
178
- }
179
180
- if ($wodOptions['forward-query-string']) {
181
- if (isset($_GET['debug'])) {
182
- $serveOptions['show-report'] = true;
183
- }
184
- if (isset($_GET['reconvert'])) {
185
- $options['reconvert'] = true;
186
- }
187
- }
188
189
- $serveOptions['add-vary-header'] = false;
190
- $serveOptions['fail'] = '404';
191
- $serveOptions['fail-when-fail-fails'] = '404';
192
- //$options['show-report'] = true;
193
- $serveOptions['serve-image']['headers']['vary-accept'] = false;
194
-
195
- /*
196
- function aboutToServeImageCallBack($servingWhat, $whyServingThis, $obj) {
197
- // Redirect to same location.
198
- header('Location: ?fresh' , 302);
199
- return false; // tell webp-convert not to serve!
200
- }
201
- */
202
203
- ConvertHelperIndependent::serveConverted(
204
- $source,
205
- $destination,
206
- $serveOptions,
207
- $webExpressContentDirAbs . '/log',
208
- 'Conversion triggered with the conversion script (wod/webp-realizer.php)'
209
- );
210
211
212
213
- //echo "<pre>source: $source \ndestination: $destination \n\noptions:" . print_r($options, true) . '</pre>'; exit;
1
<?php
2
+ namespace WebPExpress;
3
4
use \WebPConvert\WebPConvert;
5
+ use \WebPConvert\Serve\ServeConvertedWebP;
6
use \WebPExpress\ConvertHelperIndependent;
7
+ use \WebPExpress\Sanitize;
8
+ use \WebPExpress\ValidateException;
9
10
+ class WebPRealizer
11
+ {
12
13
+ private static $docRoot;
14
+
15
+ public static function exitWithError($msg) {
16
+ header('X-WebP-Express-Error: ' . $msg, true);
17
+ echo $msg;
18
exit;
19
}
20
21
+ //echo $_SERVER["SERVER_SOFTWARE"]; exit;
22
+ //stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false
23
+
24
+
25
+ /**
26
+ * Get environment variable set with mod_rewrite module
27
+ * Return false if the environment variable isn't found
28
+ */
29
+ static function getEnvPassedInRewriteRule($envName) {
30
+ // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
31
+ // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
32
+ // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
33
+ // We simply look for an environment variable that ends with what we are looking for.
34
+ // (so make sure to make it unique)
35
+ $len = strlen($envName);
36
+ foreach ($_SERVER as $key => $item) {
37
+ if (substr($key, -$len) == $envName) {
38
+ return $item;
39
+ }
40
}
41
+ return false;
42
}
43
44
+ /**
45
+ * Get absolute path to destination file.
46
+ *
47
+ * The path can be passed to this file from the .htaccess file / nginx config in various ways.
48
+ *
49
+ * @return string Absolute path to destination (unsanitized! - call sanitizeAbsFilePath immidiately after calling this method)
50
+ */
51
+ static function getDestinationUnsanitized($docRoot) {
52
+
53
+ // First check if it is in an environment variable - thats the safest way
54
+ $destinationRel = self::getEnvPassedInRewriteRule('DESTINATIONREL');
55
+ if ($destinationRel !== false) {
56
+ return $docRoot . '/' . $destinationRel;
57
+ }
58
59
+ // Next, check querystring (relative path)
60
+ $destinationRel = '';
61
+ if (isset($_GET['xdestination-rel'])) {
62
+ $destinationRel = substr(Sanitize::removeNUL($_GET['xdestination-rel']), 1);
63
+ } elseif (isset($_GET['destination-rel'])) {
64
+ $destinationRel = Sanitize::removeNUL($_GET['destination-rel']);
65
+ }
66
+ if ($destinationRel != '') {
67
+ /*
68
+ if (isset($_GET['source-rel-filter'])) {
69
+ if (Sanitize::removeNUL($_GET['source-rel-filter']) == 'discard-parts-before-wp-content') {
70
+ $parts = explode('/', $destinationRel);
71
+ $wp_content = isset($_GET['wp-content']) ? Sanitize::removeNUL($_GET['wp-content']) : 'wp-content';
72
+
73
+ if (in_array($wp_content, $parts)) {
74
+ foreach($parts as $index => $part) {
75
+ if($part !== $wp_content) {
76
+ unset($parts[$index]);
77
+ } else {
78
+ break;
79
+ }
80
+ }
81
+ $destinationRel = implode('/', $parts);
82
+ }
83
+ }
84
+ }*/
85
+ return $docRoot . '/' . $destinationRel;
86
+ }
87
+
88
+ // Then querystring (full path) - But only on Nginx (our Apache .htaccess rules never passes absolute url)
89
+ if (stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false) {
90
+ if (isset($_GET['xdestination'])) {
91
+ return substr($_GET['xdestination'], 1); // No url decoding needed as $_GET is already decoded
92
+ } elseif (isset($_GET['destination'])) {
93
+ return $_GET['destination'];
94
+ }
95
+ }
96
97
+ // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
98
+ // correct result in all setups (ie "folder method 1")
99
+ $requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
100
+ $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
101
+ $dest = $docRoot . urldecode($requestUriNoQS);
102
return $dest;
103
}
104
105
106
+ static function getWpContentRelUnsanitized() {
107
+ // Passed in env variable?
108
+ $wpContentDirRel = self::getEnvPassedInRewriteRule('WPCONTENT');
109
+ if ($wpContentDirRel !== false) {
110
+ return $wpContentDirRel;
111
+ }
112
113
+ // Passed in QS?
114
+ if (isset($_GET['wp-content'])) {
115
+ return $_GET['wp-content'];
116
+ }
117
118
+ // In case above fails, fall back to standard location
119
+ return 'wp-content';
120
}
121
+
122
+ /*
123
+ static function registerAutoload()
124
+ {
125
+ define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
126
+
127
+ // Autoload WebPExpress classes
128
+ spl_autoload_register('webpexpress_autoload');
129
+ function webpexpress_autoload($class) {
130
+ if (strpos($class, 'WebPExpress\\') === 0) {
131
+ require_once WEBPEXPRESS_PLUGIN_DIR . '/lib/classes/' . substr($class, 12) . '.php';
132
}
133
}
134
+ }*/
135
136
+ static function process() {
137
138
+ include_once "../lib/classes/ConvertHelperIndependent.php";
139
+ include_once __DIR__ . '/../lib/classes/Sanitize.php';
140
+ include_once __DIR__ . '/../lib/classes/Validate.php';
141
+ include_once __DIR__ . '/../lib/classes/ValidateException.php';
142
143
+ // Validate!
144
+ // ----------
145
146
+ try {
147
148
+ // Validate DOCUMENT_ROOT
149
+ // ----------------------
150
+ $validating = 'DOCUMENT_ROOT';
151
+ $realPathResult = realpath(Sanitize::removeNUL($_SERVER["DOCUMENT_ROOT"]));
152
+ if ($realPathResult === false) {
153
+ throw new ValidateException('Cannot find document root');
154
+ }
155
+ $docRoot = rtrim($realPathResult, '/');
156
+ Validate::absPathLooksSaneExistsAndIsDir($docRoot);
157
+ $docRoot = $docRoot;
158
159
160
+ // Validate WebP Express content dir
161
+ // ---------------------------------
162
+ $validating = 'WebP Express content dir';
163
+ $webExpressContentDirAbs = ConvertHelperIndependent::sanitizeAbsFilePath(
164
+ $docRoot . '/' . self::getWpContentRelUnsanitized() . '/webp-express'
165
+ );
166
+ Validate::absPathLooksSaneExistsAndIsDir($webExpressContentDirAbs);
167
168
169
+ // Validate config file name
170
+ // ---------------------------------
171
+ $validating = 'config file';
172
+ $configFilename = $webExpressContentDirAbs . '/config/wod-options.json';
173
+ Validate::absPathLooksSaneExistsAndIsFile($configFilename);
174
175
176
+ // Validate config file
177
+ // --------------------
178
+ $configLoadResult = file_get_contents($configFilename);
179
+ if ($configLoadResult === false) {
180
+ throw new ValidateException('Cannot open config file');
181
+ }
182
+ Validate::isJSONObject($configLoadResult);
183
+ $json = $configLoadResult;
184
+ $options = json_decode($json, true);
185
+ $wodOptions = $options['wod'];
186
+ $serveOptions = $options['webp-convert'];
187
+ $convertOptions = &$serveOptions['convert'];
188
+ //echo '<pre>' . print_r($wodOptions, true) . '</pre>'; exit;
189
+
190
+
191
+ // Validate that WebPExpress was configured to redirect to this conversion script
192
+ // ------------------------------------------------------------------------------
193
+ $validating = 'settings';
194
+ if (!isset($wodOptions['enable-redirection-to-webp-realizer']) || ($wodOptions['enable-redirection-to-webp-realizer'] === false)) {
195
+ throw new ValidateException('Redirection to conversion script is not enabled');
196
+ }
197
198
199
+ // Validate destination (the image that was requested, but has not been converted yet)
200
+ // ------------------------------------------------------------------------------------
201
+ $validating = 'destination path';
202
+ $destination = Sanitize::removeNUL(self::getDestinationUnsanitized($docRoot));
203
+ Validate::absPathLooksSane($destination);
204
+ //echo $destination; exit;
205
206
207
+ // Validate source path
208
+ // --------------------------------------------
209
+ $validating = 'source path';
210
+ $source = ConvertHelperIndependent::findSource(
211
+ $destination,
212
+ $wodOptions['destination-folder'],
213
+ $wodOptions['destination-extension'],
214
+ $webExpressContentDirAbs
215
+ );
216
+
217
+ if ($source === false) {
218
+ header('X-WebP-Express-Error: webp-realizer.php could not find an existing jpg or png that corresponds to the webp requested', true);
219
+
220
+ $protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
221
+ header($protocol . " 404 Not Found");
222
+ die();
223
+ //echo 'destination requested:<br><i>' . $destination . '</i>';
224
+ }
225
+ Validate::absPathLooksSaneExistsAndIsFile($source);
226
+ //echo $source; exit;
227
+
228
+ } catch (ValidateException $e) {
229
+ self::exitWithError('failed validating ' . $validating . ': '. $e->getMessage());
230
+ }
231
232
+ /*
233
+ if ($wodOptions['forward-query-string']) {
234
+ if (isset($_GET['debug'])) {
235
+ $serveOptions['show-report'] = true;
236
+ }
237
+ if (isset($_GET['reconvert'])) {
238
+ $serveOptions['reconvert'] = true;
239
+ }
240
+ }*/
241
+
242
+ $serveOptions['add-vary-header'] = false;
243
+ $serveOptions['fail'] = '404';
244
+ $serveOptions['fail-when-fail-fails'] = '404';
245
+ $serveOptions['serve-image']['headers']['vary-accept'] = false;
246
+
247
+ /*
248
+ function aboutToServeImageCallBack($servingWhat, $whyServingThis, $obj) {
249
+ // Redirect to same location.
250
+ header('Location: ?fresh' , 302);
251
+ return false; // tell webp-convert not to serve!
252
+ }
253
+ */
254
255
+ //echo '<pre>' . print_r($serveOptions, true) . '</pre>'; exit;
256
+ //$serveOptions['show-report'] = true;
257
+
258
+ ConvertHelperIndependent::serveConverted(
259
+ $source,
260
+ $destination,
261
+ $serveOptions,
262
+ $webExpressContentDirAbs . '/log',
263
+ 'Conversion triggered with the conversion script (wod/webp-realizer.php)'
264
+ );
265
+
266
+ }
267
+ }
268
+
269
+ // Protect against directly accessing webp-on-demand.php
270
+ // Only protect on Apache. We know for sure that the method is not reliable on nginx. We have not tested on litespeed yet, so we dare not.
271
+ if (stripos($_SERVER["SERVER_SOFTWARE"], 'apache') !== false && stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') === false) {
272
+ if (strpos($_SERVER['REQUEST_URI'], 'webp-realizer.php') !== false) {
273
+ WebPRealizer::exitWithError('It seems you are visiting this file (plugins/webp-express/wod/webp-realizer.php) directly. We do not allow this.');
274
+ exit;
275
+ }
276
+ }
277
278
+ WebPRealizer::process();