WebP Express - Version 0.14.10

Version Description

(released: 24 jun 2019)

  • Tidied up code
Download this release

Release Info

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

Code changes from version 0.14.9 to 0.14.10

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.9
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.9 =
609
  *(released: 22 jun 2019)*
610
 
@@ -844,6 +849,9 @@ For older releases, check out changelog.txt
844
 
845
  == Upgrade Notice ==
846
 
 
 
 
847
  = 0.14.9 =
848
  Tidied up code
849
 
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
+ Stable tag: 0.14.10
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.10 =
609
+ *(released: 24 jun 2019)*
610
+
611
+ * Tidied up code
612
+
613
  = 0.14.9 =
614
  *(released: 22 jun 2019)*
615
 
849
 
850
  == Upgrade Notice ==
851
 
852
+ = 0.14.10 =
853
+ Tidied up code
854
+
855
  = 0.14.9 =
856
  Tidied up code
857
 
lib/classes/Convert.php CHANGED
@@ -5,7 +5,8 @@ namespace WebPExpress;
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
 
@@ -79,41 +80,44 @@ class Convert
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
 
5
  use \WebPExpress\ConvertHelperIndependent;
6
  use \WebPExpress\Config;
7
  use \WebPExpress\ConvertersHelper;
8
+ use \WebPExpress\SanityCheck;
9
+ use \WebPExpress\SanityException;
10
  use \WebPExpress\Validate;
11
  use \WebPExpress\ValidateException;
12
 
80
  wp_die();
81
  }
82
 
83
+ // Check input
84
+ // --------------
85
  try {
86
+ // Check "filename"
87
+ $checking = '"filename" argument';
88
  Validate::postHasKey('filename');
89
  $filename = sanitize_text_field($_POST['filename']);
90
+ $filename = SanityCheck::absPathExistsAndIsNotDir($filename);
91
 
92
 
93
+ // Check converter id
94
  // ---------------------
95
+ $checking = '"converter" argument';
96
  if (isset($_POST['converter'])) {
97
  $converterId = sanitize_text_field($_POST['converter']);
98
  Validate::isConverterId($converterId);
99
  }
100
 
101
 
102
+ // Check "config-overrides"
103
  // ---------------------------
104
+ $checking = '"config-overrides" argument';
105
  if (isset($_POST['config-overrides'])) {
106
+ $configOverridesJSON = SanityCheck::noControlChars($_POST['config-overrides']);
107
  $configOverridesJSON = preg_replace('/\\\\"/', '"', $configOverridesJSON); // We got crazy encoding, perhaps by jQuery. This cleans it up
108
 
109
+ $configOverridesJSON = SanityCheck::isJSONObject($configOverridesJSON);
110
  $configOverrides = json_decode($configOverridesJSON, true);
111
 
112
  // PS: We do not need to validate the overrides.
113
  // webp-convert checks all options. Nothing can be passed to webp-convert which causes harm.
114
  }
115
 
116
+ } catch (SanityException $e) {
117
+ wp_send_json_error('Sanitation check failed for ' . $checking . ': '. $e->getMessage());
118
+ wp_die();
119
  } catch (ValidateException $e) {
120
+ wp_send_json_error('Validation failed for ' . $checking . ': '. $e->getMessage());
121
  wp_die();
122
  }
123
 
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -268,7 +268,7 @@ APACHE
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
 
268
 
269
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
270
 
271
+ $text = 'WebP Express 0.14.10. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
272
 
273
  $logFile = self::getLogFilename($source, $logDir);
274
 
lib/classes/SanityCheck.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ use \WebPExpress\Sanitize;
6
+ use \WebPExpress\SanityException;
7
+
8
+ class SanityCheck
9
+ {
10
+
11
+ /**
12
+ *
13
+ * @param string $input string to test for NUL char
14
+ */
15
+ public static function mustBeString($input, $errorMsg = 'String expected')
16
+ {
17
+ if (gettype($input) !== 'string') {
18
+ throw new SanityException($errorMsg);
19
+ }
20
+ return $input;
21
+ }
22
+
23
+ /**
24
+ * The NUL character is a demon, because it can be used to bypass other tests
25
+ * See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
26
+ *
27
+ * @param string $input string to test for NUL char
28
+ */
29
+ public static function noNUL($input, $errorMsg = 'NUL character is not allowed')
30
+ {
31
+ self::mustBeString($input);
32
+ if (strpos($input, chr(0)) !== false) {
33
+ throw new SanityException($errorMsg);
34
+ }
35
+ return $input;
36
+ }
37
+
38
+ /**
39
+ * Prevent control chararters (#00 - #20).
40
+ *
41
+ * This prevents line feed, new line, tab, charater return, tab, ets.
42
+ * https://www.rapidtables.com/code/text/ascii-table.html
43
+ *
44
+ * @param string $input string to test for control characters
45
+ */
46
+ public static function noControlChars($input)
47
+ {
48
+ self::mustBeString($input);
49
+ self::noNUL($input);
50
+ if (preg_match('#[\x{0}-\x{1f}]#', $input)) {
51
+ throw new SanityException('Control characters are not allowed');
52
+ }
53
+ return $input;
54
+ }
55
+
56
+
57
+ /**
58
+ *
59
+ * @param mixed $input something that may not be empty
60
+ */
61
+ public static function notEmpty($input, $errorMsg = 'Must be non-empty')
62
+ {
63
+ if (empty($input)) {
64
+ throw new SanityException($input);
65
+ }
66
+ return $input;
67
+ }
68
+
69
+
70
+
71
+ public static function noDirectoryTraversal($input, $errorMsg = 'Directory traversal is not allowed')
72
+ {
73
+ self::mustBeString($input);
74
+ self::noControlChars($input);
75
+ if (preg_match('#\.\.\/#', $input)) {
76
+ throw new SanityException($errorMsg);
77
+ }
78
+ return $input;
79
+ }
80
+
81
+ public static function noStreamWrappers($input, $errorMsg = 'Stream wrappers are not allowed')
82
+ {
83
+ self::mustBeString($input);
84
+ self::noControlChars($input);
85
+
86
+ // Prevent stream wrappers ("phar://", "php://" and the like)
87
+ // https://www.php.net/manual/en/wrappers.phar.php
88
+ if (preg_match('#^\\w+://#', Sanitize::removeNUL($input))) {
89
+ throw new SanityException($errorMsg);
90
+ }
91
+ return $input;
92
+ }
93
+
94
+ public static function path($input)
95
+ {
96
+ self::notEmpty($input);
97
+ self::mustBeString($input);
98
+ self::noControlChars($input);
99
+ self::noDirectoryTraversal($input);
100
+ self::noStreamWrappers($input);
101
+ return $input;
102
+ }
103
+
104
+ public static function pathWithoutDirectoryTraversal($input)
105
+ {
106
+ return self::path($input);
107
+ }
108
+
109
+ public static function pathBeginsWith($input, $beginsWith, $errorMsg = 'Path is outside allowed path')
110
+ {
111
+ self::path($input);
112
+ if (!(strpos($input, $beginsWith) === 0)) {
113
+ throw new SanityException($errorMsg);
114
+ }
115
+ return $input;
116
+ }
117
+
118
+ public static function absPath($input)
119
+ {
120
+ return self::path($input);
121
+ }
122
+
123
+ public static function absPathExists($input, $errorMsg = 'Path does not exist')
124
+ {
125
+ self::absPath($input);
126
+ if (@!file_exists($input)) {
127
+ throw new SanityException($errorMsg);
128
+ }
129
+ return $input;
130
+ }
131
+
132
+ public static function absPathExistsAndIsDir(
133
+ $input,
134
+ $errorMsg = 'Path points to a file (it should point to a directory)'
135
+ ) {
136
+ self::absPathExists($input);
137
+ if (!is_dir($input)) {
138
+ throw new SanityException($errorMsg);
139
+ }
140
+ return $input;
141
+ }
142
+
143
+ public static function absPathExistsAndIsFile(
144
+ $input,
145
+ $errorMsg = 'Path points to a directory (it should not do that)'
146
+ ) {
147
+ self::absPathExists($input, 'File does not exist');
148
+ if (@is_dir($input)) {
149
+ throw new SanityException($errorMsg);
150
+ }
151
+ return $input;
152
+ }
153
+
154
+ public static function absPathExistsAndIsNotDir(
155
+ $input,
156
+ $errorMsg = 'Path points to a directory (it should point to a file)'
157
+ ) {
158
+ self::absPathExistsAndIsFile($input, $errorMsg);
159
+ return $input;
160
+ }
161
+
162
+
163
+ public static function pregMatch($pattern, $input, $errorMsg = 'Does not match expected pattern')
164
+ {
165
+ self::noNUL($input);
166
+ self::mustBeString($input);
167
+ if (!preg_match($pattern, $input)) {
168
+ throw new SanityException($errorMsg);
169
+ }
170
+ return $input;
171
+ }
172
+
173
+ public static function isJSONArray($input, $errorMsg = 'Not a JSON array')
174
+ {
175
+ self::noNUL($input);
176
+ self::mustBeString($input);
177
+ self::notEmpty($input);
178
+ if ((strpos($input, '[') !== 0) || (!is_array(json_decode($input)))) {
179
+ throw new SanityException($errorMsg);
180
+ }
181
+ return $input;
182
+ }
183
+
184
+ public static function isJSONObject($input, $errorMsg = 'Not a JSON object')
185
+ {
186
+ self::noNUL($input);
187
+ self::mustBeString($input);
188
+ self::notEmpty($input);
189
+ if ((strpos($input, '{') !== 0) || (!is_object(json_decode($input)))) {
190
+ throw new SanityException($errorMsg);
191
+ }
192
+ return $input;
193
+ }
194
+
195
+ }
lib/classes/SanityException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ class SanityException extends \Exception
6
+ {
7
+ }
lib/classes/Validate.php CHANGED
@@ -2,8 +2,9 @@
2
 
3
  namespace WebPExpress;
4
 
5
- use \WebPExpress\Sanitize;
6
  use \WebPExpress\ValidateException;
 
7
 
8
  class Validate
9
  {
@@ -15,151 +16,10 @@ class Validate
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
  }
2
 
3
  namespace WebPExpress;
4
 
5
+ use \WebPExpress\ConvertersHelper;
6
  use \WebPExpress\ValidateException;
7
+ use \WebPExpress\SanityCheck;
8
 
9
  class Validate
10
  {
16
  }
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  public static function isConverterId($converterId, $errorMsg = 'Not a valid converter id')
20
  {
21
+ SanityCheck::pregMatch('#^[a-z]+$#', $converterId, $errorMsg);
22
+ if (!in_array($converterId, ConvertersHelper::getDefaultConverterNames())) {
23
  throw new ValidateException($errorMsg);
24
  }
25
  }
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.9
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
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.10
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
wod/webp-on-demand.php CHANGED
@@ -5,28 +5,25 @@ 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.
@@ -41,168 +38,67 @@ class WebPOnDempand
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'];
@@ -212,23 +108,68 @@ class WebPOnDempand
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'],
@@ -236,23 +177,18 @@ class WebPOnDempand
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;
@@ -260,9 +196,6 @@ class WebPOnDempand
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,
5
  use \WebPConvert\Serve\ServeConvertedWebP;
6
  use \WebPExpress\ConvertHelperIndependent;
7
  use \WebPExpress\Sanitize;
8
+ use \WebPExpress\SanityCheck;
9
+ use \WebPExpress\SanityException;
10
 
11
  class WebPOnDempand
12
  {
13
 
14
  private static $docRoot;
15
 
16
+ private static function exitWithError($msg) {
17
  header('X-WebP-Express-Error: ' . $msg, true);
18
  echo $msg;
19
  exit;
20
  }
21
 
 
 
 
 
22
  /**
23
  * Get environment variable set with mod_rewrite module
24
  * Return false if the environment variable isn't found
25
  */
26
+ private static function getEnvPassedInRewriteRule($envName) {
27
  // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
28
  // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
29
  // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
38
  return false;
39
  }
40
 
41
+ public static function process() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ include_once __DIR__ . "/../lib/classes/ConvertHelperIndependent.php";
44
+ include_once __DIR__ . '/../lib/classes/Sanitize.php';
45
+ include_once __DIR__ . '/../lib/classes/SanityCheck.php';
46
+ include_once __DIR__ . '/../lib/classes/SanityException.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ // Check input
49
+ // --------------
 
 
50
 
51
+ try {
 
 
52
 
53
+ // Check DOCUMENT_ROOT
54
+ // ----------------------
55
+ $checking = 'DOCUMENT_ROOT';
56
+ $docRoot = SanityCheck::absPath($_SERVER["DOCUMENT_ROOT"]);
57
 
58
+ // Use realpath to expand symbolic links and check if it exists
59
+ $docRoot = realpath($docRoot);
60
+ if ($docRoot === false) {
61
+ throw new SanityException('Cannot find document root');
 
62
  }
63
+ $docRoot = rtrim($docRoot, '/');
64
+ $docRoot = SanityCheck::absPathExistsAndIsDir($docRoot);
65
 
66
+ // Check wp-content
67
+ // ----------------------
 
 
 
 
 
 
 
68
 
69
+ // Passed in env variable?
70
+ $wpContentDirRel = self::getEnvPassedInRewriteRule('WPCONTENT');
71
+ if ($wpContentDirRel === false) {
72
 
73
+ // Passed in QS?
74
+ if (isset($_GET['wp-content'])) {
75
+ $wpContentDirRel = SanityCheck::pathWithoutDirectoryTraversal($_GET['wp-content']);
76
+ } else {
77
+ // In case above fails, fall back to standard location
78
+ $wpContentDirRel = 'wp-content';
79
+ }
80
  }
 
 
 
 
81
 
82
+ // Check WebP Express content dir
83
  // ---------------------------------
84
+ $checking = 'WebP Express content dir';
85
+ $webExpressContentDirAbs = SanityCheck::absPathExistsAndIsDir($docRoot . '/' . $wpContentDirRel . '/webp-express');
 
 
 
86
 
87
 
88
+ // Check config file name
89
  // ---------------------------------
90
+ $checking = 'config file';
91
+ $configFilename = SanityCheck::absPathExistsAndIsFile($webExpressContentDirAbs . '/config/wod-options.json');
 
92
 
93
 
94
+ // Check config file
95
  // --------------------
96
  $configLoadResult = file_get_contents($configFilename);
97
  if ($configLoadResult === false) {
98
  throw new ValidateException('Cannot open config file');
99
  }
100
+ $json = SanityCheck::isJSONObject($configLoadResult);
101
+
102
  $options = json_decode($json, true);
103
  $wodOptions = $options['wod'];
104
  $serveOptions = $options['webp-convert'];
108
 
109
  // Validate that WebPExpress was configured to redirect to this conversion script
110
  // ------------------------------------------------------------------------------
111
+ $checking = 'settings';
112
  if (!isset($wodOptions['enable-redirection-to-converter']) || ($wodOptions['enable-redirection-to-converter'] === false)) {
113
  throw new ValidateException('Redirection to conversion script is not enabled');
114
  }
115
 
116
 
117
+ // Check source (the image to be converted)
118
  // --------------------------------------------
119
+ $checking = 'source';
120
+
121
+ // Check if it is in an environment variable
122
+ $source = self::getEnvPassedInRewriteRule('REQFN');
123
+ if ($source !== false) {
124
+ $source = SanityCheck::absPathExistsAndIsFile($source);
125
+ } else {
126
+ // Check if it is in header (but only if .htaccess was configured to send in header)
127
+ if (isset($wodOptions['base-htaccess-on-these-capability-tests'])) {
128
+ $capTests = $wodOptions['base-htaccess-on-these-capability-tests'];
129
+ $passThroughHeaderDefinitelyUnavailable = ($capTests['passThroughHeaderWorking'] === false);
130
+ $passThrougEnvVarDefinitelyAvailable =($capTests['passThroughEnvWorking'] === true);
131
+ } else {
132
+ $passThroughHeaderDefinitelyUnavailable = false;
133
+ $passThrougEnvVarDefinitelyAvailable = false;
134
+ }
135
+ if ((!$passThrougEnvVarDefinitelyAvailable) && (!$passThroughHeaderDefinitelyUnavailable)) {
136
+ if (isset($_SERVER['HTTP_REQFN'])) {
137
+ $source = SanityCheck::absPathExistsAndIsFile($_SERVER['HTTP_REQFN']);
138
+ }
139
+ } else {
140
+ // Check querystring (relative path)
141
+ $srcRel = '';
142
+ if (isset($_GET['xsource-rel'])) {
143
+ $xsrcRel = SanityCheck::noControlChars($_GET['xsource-rel']);
144
+ $srcRel = SanityCheck::pathWithoutDirectoryTraversal(substr($xsrcRel, 1));
145
+ $source = SanityCheck::absPathExistsAndIsFile($docRoot . '/' . $srcRel);
146
+ } else {
147
+ // Then querystring (full path)
148
+ // - But only on Nginx (our Apache .htaccess rules never passes absolute url)
149
+ if (
150
+ (stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false) &&
151
+ (isset($_GET['source']) || isset($_GET['xsource']))
152
+ ) {
153
+ if (isset($_GET['source'])) {
154
+ $source = SanityCheck::absPathExistsAndIsFile($_GET['source']);
155
+ } else {
156
+ $xsrc = SanityCheck::noControlChars($_GET['xsource']);
157
+ $source = SanityCheck::absPathExistsAndIsFile(substr($xsrc, 1));
158
+ }
159
+ } else {
160
+ // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
161
+ // correct result in all setups (ie "folder method 1")
162
+ $srcRel = SanityCheck::pathWithoutDirectoryTraversal(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
163
+ $source = SanityCheck::absPathExistsAndIsFile($docRoot . $srcRel);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ $source = SanityCheck::pathBeginsWith($source, $docRoot . '/');
169
 
170
+ // Check destination path
171
  // --------------------------------------------
172
+ $checking = 'destination path';
173
  $destination = ConvertHelperIndependent::getDestination(
174
  $source,
175
  $wodOptions['destination-folder'],
177
  $webExpressContentDirAbs,
178
  $docRoot . '/' . $wodOptions['paths']['uploadDirRel']
179
  );
180
+ $destination = SanityCheck::absPath($destination);
181
+ $destination = SanityCheck::pregMatch('#\.webp$#', $destination, 'Does not end with .webp');
182
+ $destination = SanityCheck::pathBeginsWith($destination, $docRoot . '/');
183
 
184
+ } catch (SanityException $e) {
185
+ self::exitWithError('Sanity check failed for ' . $checking . ': '. $e->getMessage());
186
  } catch (ValidateException $e) {
187
+ self::exitWithError('Validation failed for ' . $checking . ': '. $e->getMessage());
188
  }
189
 
190
+ // Done with sanitizing, lets get to work!
191
+ // ---------------------------------------
 
 
 
 
 
 
 
 
192
  if (isset($wodOptions['success-response']) && ($wodOptions['success-response'] == 'original')) {
193
  $serveOptions['serve-original'] = true;
194
  $serveOptions['serve-image']['headers']['vary-accept'] = false;
196
  $serveOptions['serve-image']['headers']['vary-accept'] = true;
197
  }
198
 
 
 
 
199
  ConvertHelperIndependent::serveConverted(
200
  $source,
201
  $destination,
wod/webp-realizer-old.php ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ SanityCheck::absPathExistsAndIsDir($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
+ SanityCheck::absPathExistsAndIsDir($webExpressContentDirAbs);
167
+
168
+
169
+ // Validate config file name
170
+ // ---------------------------------
171
+ $validating = 'config file';
172
+ $configFilename = $webExpressContentDirAbs . '/config/wod-options.json';
173
+ SanityCheck::absPathExistsAndIsFile($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
+ SanityCheck::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
+ SanityCheck::absPath($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
+ SanityCheck::absPathExistsAndIsFile($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();
wod/webp-realizer.php CHANGED
@@ -5,28 +5,25 @@ 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.
@@ -41,146 +38,67 @@ class WebPRealizer
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'];
@@ -190,23 +108,56 @@ class WebPRealizer
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'],
@@ -215,46 +166,30 @@ class WebPRealizer
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,
@@ -262,7 +197,6 @@ class WebPRealizer
262
  $webExpressContentDirAbs . '/log',
263
  'Conversion triggered with the conversion script (wod/webp-realizer.php)'
264
  );
265
-
266
  }
267
  }
268
 
5
  use \WebPConvert\Serve\ServeConvertedWebP;
6
  use \WebPExpress\ConvertHelperIndependent;
7
  use \WebPExpress\Sanitize;
8
+ use \WebPExpress\SanityCheck;
9
+ use \WebPExpress\SanityException;
10
 
11
  class WebPRealizer
12
  {
13
 
14
  private static $docRoot;
15
 
16
+ private static function exitWithError($msg) {
17
  header('X-WebP-Express-Error: ' . $msg, true);
18
  echo $msg;
19
  exit;
20
  }
21
 
 
 
 
 
22
  /**
23
  * Get environment variable set with mod_rewrite module
24
  * Return false if the environment variable isn't found
25
  */
26
+ private static function getEnvPassedInRewriteRule($envName) {
27
  // Envirenment variables passed through the REWRITE module have "REWRITE_" as a prefix (in Apache, not Litespeed, if I recall correctly)
28
  // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
29
  // Multiple iterations causes multiple REWRITE_ prefixes, and we get many environment variables set.
38
  return false;
39
  }
40
 
41
+ public static function process() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ include_once __DIR__ . "/../lib/classes/ConvertHelperIndependent.php";
44
+ include_once __DIR__ . '/../lib/classes/Sanitize.php';
45
+ include_once __DIR__ . '/../lib/classes/SanityCheck.php';
46
+ include_once __DIR__ . '/../lib/classes/SanityException.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ // Check input
49
+ // --------------
 
 
50
 
51
+ try {
 
 
52
 
53
+ // Check DOCUMENT_ROOT
54
+ // ----------------------
55
+ $checking = 'DOCUMENT_ROOT';
56
+ $docRoot = SanityCheck::absPath($_SERVER["DOCUMENT_ROOT"]);
57
 
58
+ // Use realpath to expand symbolic links and check if it exists
59
+ $docRoot = realpath($docRoot);
60
+ if ($docRoot === false) {
61
+ throw new SanityException('Cannot find document root');
 
62
  }
63
+ $docRoot = rtrim($docRoot, '/');
64
+ $docRoot = SanityCheck::absPathExistsAndIsDir($docRoot);
 
 
65
 
66
+ // Check wp-content
67
+ // ----------------------
 
 
 
 
 
68
 
69
+ // Passed in env variable?
70
+ $wpContentDirRel = self::getEnvPassedInRewriteRule('WPCONTENT');
71
+ if ($wpContentDirRel === false) {
72
 
73
+ // Passed in QS?
74
+ if (isset($_GET['wp-content'])) {
75
+ $wpContentDirRel = SanityCheck::pathWithoutDirectoryTraversal($_GET['wp-content']);
76
+ } else {
77
+ // In case above fails, fall back to standard location
78
+ $wpContentDirRel = 'wp-content';
79
+ }
80
  }
 
 
 
81
 
82
+ // Check WebP Express content dir
 
83
  // ---------------------------------
84
+ $checking = 'WebP Express content dir';
85
+ $webExpressContentDirAbs = SanityCheck::absPathExistsAndIsDir($docRoot . '/' . $wpContentDirRel . '/webp-express');
 
 
 
86
 
87
 
88
+ // Check config file name
89
  // ---------------------------------
90
+ $checking = 'config file';
91
+ $configFilename = SanityCheck::absPathExistsAndIsFile($webExpressContentDirAbs . '/config/wod-options.json');
 
92
 
93
 
94
+ // Check config file
95
  // --------------------
96
  $configLoadResult = file_get_contents($configFilename);
97
  if ($configLoadResult === false) {
98
  throw new ValidateException('Cannot open config file');
99
  }
100
+ $json = SanityCheck::isJSONObject($configLoadResult);
101
+
102
  $options = json_decode($json, true);
103
  $wodOptions = $options['wod'];
104
  $serveOptions = $options['webp-convert'];
108
 
109
  // Validate that WebPExpress was configured to redirect to this conversion script
110
  // ------------------------------------------------------------------------------
111
+ $checking = 'settings';
112
+ if (!isset($wodOptions['enable-redirection-to-converter']) || ($wodOptions['enable-redirection-to-converter'] === false)) {
113
  throw new ValidateException('Redirection to conversion script is not enabled');
114
  }
115
 
116
 
117
+ // Check destination (the image that was requested, but has not been converted yet)
118
  // ------------------------------------------------------------------------------------
119
+ $checking = 'destination path';
120
+
121
+ // Check if it is in an environment variable
122
+ $destRel = self::getEnvPassedInRewriteRule('DESTINATIONREL');
123
+ if ($destRel !== false) {
124
+ $destination = SanityCheck::absPath($docRoot . '/' . $destRel);
125
+ } else {
126
+ // Check querystring (relative path)
127
+ if (isset($_GET['xdestination-rel'])) {
128
+ $xdestRel = SanityCheck::noControlChars($_GET['xdestination-rel']);
129
+ $destRel = SanityCheck::pathWithoutDirectoryTraversal(substr($xdestRel, 1));
130
+ $destination = SanityCheck::absPath($docRoot . '/' . $destRel);
131
+ } else {
132
+
133
+ // Then querystring (full path)
134
+ // - But only on Nginx (our Apache .htaccess rules never passes absolute url)
135
+ if (
136
+ (stripos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false) &&
137
+ (isset($_GET['destination']) || isset($_GET['xdestination']))
138
+ ) {
139
+ if (isset($_GET['destination'])) {
140
+ $destination = SanityCheck::absPath($_GET['destination']);
141
+ } else {
142
+ $xdest = SanityCheck::noControlChars($_GET['xdestination']);
143
+ $destination = SanityCheck::absPath(substr($xdest, 1));
144
+ }
145
+ } else {
146
+ // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
147
+ // correct result in all setups (ie "folder method 1")
148
+ $destRel = SanityCheck::pathWithoutDirectoryTraversal(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
149
+ $destination = SanityCheck::absPath($docRoot . $destRel);
150
+ }
151
+ }
152
+ }
153
+
154
+ $destination = SanityCheck::pregMatch('#\.webp$#', $destination, 'Does not end with .webp');
155
+ $destination = SanityCheck::pathBeginsWith($destination, $docRoot . '/');
156
 
157
 
158
  // Validate source path
159
  // --------------------------------------------
160
+ $checking = 'source path';
161
  $source = ConvertHelperIndependent::findSource(
162
  $destination,
163
  $wodOptions['destination-folder'],
166
  );
167
 
168
  if ($source === false) {
169
+ header('X-WebP-Express-Error: webp-realizer.php could not find an existing jpg/png that corresponds to the webp requested', true);
170
 
171
  $protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
172
  header($protocol . " 404 Not Found");
173
  die();
174
  //echo 'destination requested:<br><i>' . $destination . '</i>';
175
  }
176
+ $source = SanityCheck::absPathExistsAndIsFile($source);
177
+ $source = SanityCheck::pathBeginsWith($source, $docRoot . '/');
178
 
179
+ } catch (SanityException $e) {
180
+ self::exitWithError('Sanity check failed for ' . $checking . ': '. $e->getMessage());
181
  } catch (ValidateException $e) {
182
+ self::exitWithError('Validation failed for ' . $checking . ': '. $e->getMessage());
183
  }
184
 
 
 
 
 
 
 
 
 
 
185
 
186
+ // Done with sanitizing, lets get to work!
187
+ // ---------------------------------------
188
  $serveOptions['add-vary-header'] = false;
189
  $serveOptions['fail'] = '404';
190
  $serveOptions['fail-when-fail-fails'] = '404';
191
  $serveOptions['serve-image']['headers']['vary-accept'] = false;
192
 
 
 
 
 
 
 
 
 
 
 
 
193
  ConvertHelperIndependent::serveConverted(
194
  $source,
195
  $destination,
197
  $webExpressContentDirAbs . '/log',
198
  'Conversion triggered with the conversion script (wod/webp-realizer.php)'
199
  );
 
200
  }
201
  }
202