WebP Express - Version 0.14.11

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.11
Comparing to
See all releases

Code changes from version 0.14.10 to 0.14.11

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.10
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.10 =
609
  *(released: 24 jun 2019)*
610
 
@@ -849,6 +854,9 @@ For older releases, check out changelog.txt
849
 
850
  == Upgrade Notice ==
851
 
 
 
 
852
  = 0.14.10 =
853
  Tidied up code
854
 
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
+ Stable tag: 0.14.11
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.11 =
609
+ *(released: 24 jun 2019)*
610
+
611
+ * Tidied up code
612
+
613
  = 0.14.10 =
614
  *(released: 24 jun 2019)*
615
 
854
 
855
  == Upgrade Notice ==
856
 
857
+ = 0.14.11 =
858
+ Tidied up code
859
+
860
  = 0.14.10 =
861
  Tidied up code
862
 
lib/classes/CachePurge.php CHANGED
@@ -55,7 +55,7 @@ class CachePurge
55
  *
56
  * @return [num files deleted, num files failed to delete]
57
  */
58
- public static function purgeWebPFilesInDir($dir, &$filter, &$config)
59
  {
60
  if (!@file_exists($dir) || !@is_dir($dir)) {
61
  return [0, 0];
55
  *
56
  * @return [num files deleted, num files failed to delete]
57
  */
58
+ private static function purgeWebPFilesInDir($dir, &$filter, &$config)
59
  {
60
  if (!@file_exists($dir) || !@is_dir($dir)) {
61
  return [0, 0];
lib/classes/Convert.php CHANGED
@@ -29,28 +29,62 @@ class Convert
29
 
30
  public static function convertFile($source, $config = null, $convertOptions = null, $converter = null)
31
  {
32
- // PS: No need to check mime type as the WebPConvert library does that (it only accepts image/jpeg and image/png)
33
- $source = ConvertHelperIndependent::sanitizeAbsFilePath($source);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- if (is_null($config)) {
36
- $config = Config::loadConfigAndFix();
37
- }
38
- if (is_null($convertOptions)) {
39
- $convertOptions = Config::generateWodOptionsFromConfigObj($config)['webp-convert']['convert'];
40
- }
41
- /*
42
- if (isset($config['converter'])) {
43
- $options['convert']['converter'] = $config['converter'];
44
- }*/
45
 
46
- $destination = self::getDestination($source, $config);
47
- $destination = ConvertHelperIndependent::sanitizeAbsFilePath($destination);
 
 
48
 
49
- $logDir = Paths::getWebPExpressContentDirAbs() . '/log';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  $result = ConvertHelperIndependent::convert($source, $destination, $convertOptions, $logDir, $converter);
52
 
53
- //$result['destination'] = $destination;
54
  if ($result['success'] === true) {
55
  $result['filesize-original'] = @filesize($source);
56
  $result['filesize-webp'] = @filesize($destination);
@@ -58,12 +92,33 @@ class Convert
58
  return $result;
59
  }
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  public static function findSource($destination, &$config = null)
62
  {
63
- $destination = ConvertHelperIndependent::sanitizeAbsFilePath($destination);
 
 
 
 
 
 
 
64
  if (is_null($config)) {
65
  $config = Config::loadConfigAndFix();
66
  }
 
67
  return ConvertHelperIndependent::findSource(
68
  $destination,
69
  $config['destination-folder'],
@@ -87,7 +142,8 @@ class Convert
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
29
 
30
  public static function convertFile($source, $config = null, $convertOptions = null, $converter = null)
31
  {
32
+ try {
33
+ // Check source
34
+ // ---------------
35
+ $checking = 'filename';
36
+ $filename = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
37
+ // PS: No need to check mime type as the WebPConvert library does that (it only accepts image/jpeg and image/png)
38
+
39
+
40
+ // Check config
41
+ // --------------
42
+ $checking = 'configuration file';
43
+ if (is_null($config)) {
44
+ $config = Config::loadConfigAndFix(); // ps: if this fails to load, default config is returned.
45
+ }
46
+ if (!is_array($config)) {
47
+ throw new SanityException('file is corrupt');
48
+ }
49
+
50
+ // Check convert options
51
+ // -------------------------------
52
+ if (is_null($convertOptions)) {
53
+ $wodOptions = Config::generateWodOptionsFromConfigObj($config);
54
+ if (!isset($wodOptions['webp-convert']['convert'])) {
55
+ throw new SanityException('conversion options are missing');
56
+ }
57
+ $convertOptions = $wodOptions['webp-convert']['convert'];
58
+ }
59
+ if (!is_array($convertOptions)) {
60
+ throw new SanityException('conversion options are missing');
61
+ }
62
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ // Check destination
65
+ // -------------------------------
66
+ $destination = self::getDestination($source, $config);
67
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
68
 
69
+
70
+ // Check log dir
71
+ // -------------------------------
72
+ $logDir = SanityCheck::absPathIsInDocRoot(Paths::getWebPExpressContentDirAbs() . '/log');
73
+
74
+
75
+ } catch (SanityException $e) {
76
+ return [
77
+ 'success' => false,
78
+ 'msg' => 'Sanitation check failed for ' . $checking . ': '. $e->getMessage(),
79
+ 'log' => '',
80
+ ];
81
+ }
82
+
83
+ // Done with sanitizing, lets get to work!
84
+ // ---------------------------------------
85
 
86
  $result = ConvertHelperIndependent::convert($source, $destination, $convertOptions, $logDir, $converter);
87
 
 
88
  if ($result['success'] === true) {
89
  $result['filesize-original'] = @filesize($source);
90
  $result['filesize-webp'] = @filesize($destination);
92
  return $result;
93
  }
94
 
95
+ /**
96
+ * Determine the location of a source from the location of a destination.
97
+ *
98
+ * If for example Operation mode is set to "mingled" and extension is set to "Append .webp",
99
+ * the result of looking passing "/path/to/logo.jpg.webp" will be "/path/to/logo.jpg".
100
+ *
101
+ * Additionally, it is tested if the source exists. If not, false is returned.
102
+ * The destination does not have to exist.
103
+ *
104
+ * @return string|null The source path corresponding to a destination path
105
+ * - or false on failure (if the source does not exist or $destination is not sane)
106
+ *
107
+ */
108
  public static function findSource($destination, &$config = null)
109
  {
110
+ try {
111
+ // Check that destination path is sane and inside document root
112
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
113
+ } catch (SanityException $e) {
114
+ return false;
115
+ }
116
+
117
+ // Load config if not already loaded
118
  if (is_null($config)) {
119
  $config = Config::loadConfigAndFix();
120
  }
121
+
122
  return ConvertHelperIndependent::findSource(
123
  $destination,
124
  $config['destination-folder'],
142
  $checking = '"filename" argument';
143
  Validate::postHasKey('filename');
144
  $filename = sanitize_text_field($_POST['filename']);
145
+ $filename = SanityCheck::absPathExistsAndIsFileInDocRoot($filename);
146
+ // PS: No need to check mime version as webp-convert does that.
147
 
148
 
149
  // Check converter id
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -1,22 +1,27 @@
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 \WebPConvert\WebPConvert;
10
  use \WebPConvert\Convert\ConverterFactory;
 
11
  use \WebPConvert\Loggers\BufferLogger;
12
  use \WebPExpress\FileHelper;
13
- use WebPConvert\Exceptions\WebPConvertException;
14
-
15
 
16
  class ConvertHelperIndependent
17
  {
18
 
19
- public static function storeMingledOrNot($source, $destinationFolder, $uploadDirAbs)
 
 
 
 
20
  {
21
  if ($destinationFolder != 'mingled') {
22
  return false;
@@ -42,169 +47,175 @@ class ConvertHelperIndependent
42
  return strpos($normalizedSource, $normalizedDocRoot) === 0;
43
  }
44
 
45
- /*
46
- public static function getDestinationFolder($sourceDir, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
47
- {
48
- if (self::storeMingledOrNot($sourceDir, $destinationFolder, $uploadDirAbs)) {
49
- return $sourceDir;
50
- } else {
51
-
52
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
53
- $imageRoot = $webExpressContentDirAbs . '/webp-images';
54
-
55
- // Check if source dir is residing inside document root.
56
- // (it is, if path starts with document root + '/')
57
- if (substr($sourceDir, 0, strlen($docRoot) + 1) === $docRoot . '/') {
58
-
59
- // We store relative to document root.
60
- // "Eat" the left part off the source parameter which contains the document root.
61
- // and also eat the slash (+1)
62
- $sourceDirRel = substr($sourceDir, strlen($docRoot) + 1);
63
- return $imageRoot . '/doc-root/' . $sourceDirRel;
64
- } else {
65
- // Source file is residing outside document root.
66
- // we must add complete path to structure
67
- return $imageRoot . '/abs' . $sourceDir;
68
- }
69
- }
70
- }*/
71
 
72
  /**
73
- * Get destination from source (and some configurations)
 
 
 
 
 
 
 
 
 
 
 
74
  */
75
- private static function getDestinationUnsanitized($source, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
76
  {
77
- if (self::storeMingledOrNot($source, $destinationFolder, $uploadDirAbs)) {
78
- if ($destinationExt == 'append') {
79
- return $source . '.webp';
80
- } else {
81
- return preg_replace('/\\.(jpe?g|png)$/', '', $source) . '.webp';
82
- }
83
- } else {
84
-
85
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
86
- $imageRoot = $webExpressContentDirAbs . '/webp-images';
87
 
88
- // Check if source is residing inside document root.
89
- // (it is, if path starts with document root + '/')
90
- if (self::sourceIsInsideDocRoot($source, $docRoot) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- // We store relative to document root.
93
- // "Eat" the left part off the source parameter which contains the document root.
94
- // and also eat the slash (+1)
95
  $sourceRel = substr($source, strlen($docRoot) + 1);
96
- return $imageRoot . '/doc-root/' . $sourceRel . '.webp';
97
- } else {
98
- // Source file is residing outside document root.
99
- // we must add complete path to structure
100
- return $imageRoot . '/abs' . $source . '.webp';
101
  }
 
 
 
102
  }
 
 
103
  }
104
 
 
105
  /**
106
- * Sanitize absolute file path.
107
  *
108
- * Make sure that file path is not a stream wrapper.
109
- * This protects against Phar Deserialization and possibly other nasty tricks
110
- * https://blog.ripstech.com/2018/new-php-exploitation-technique/
111
- * https://www.php.net/manual/en/wrappers.phar.php
112
  *
113
- * We also prevent directory traversal, as we do not expect any traversal in our absolute paths.
 
114
  *
115
- * @param string $absFilePath
116
- * @return string sanitized file path
117
  */
118
- public static function sanitizeAbsFilePath($absFilePath) {
119
- // Remove NUL characters (https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP)
120
- $absFilePath = str_replace(chr(0), '', $absFilePath);
121
 
122
- // remove "../"
123
- $absFilePath = preg_replace('#\.\.\/#', '', $absFilePath);
 
124
 
125
- // remove "phar://", "php://" and the like from the beginning of the string
126
- // important that this is done after removing "../" - otherwise one could send "../phar://"
127
- $absFilePath = preg_replace('#^\\w+://#', '', $absFilePath);
128
 
129
- return $absFilePath;
130
- }
 
131
 
132
- /**
133
- * Get destination from source (and some configurations)
134
- */
135
- public static function getDestination($source, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
136
- {
137
- return self::sanitizeAbsFilePath(
138
- self::getDestinationUnsanitized($source, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
139
- );
140
- }
141
 
142
- /**
143
- * Find source corresponding to destination, separate
144
- * We can rely on destinationExt being "append" for separate
145
- * Returns false if not found. Otherwise returns path to source
146
- */
147
- private static function findSourceSeparate($destination, $webExpressContentDirAbs)
148
- {
149
- $imageRoot = $webExpressContentDirAbs . '/webp-images';
150
 
151
- // Check if destination is residing inside "doc-root" folder
152
- // TODO: This does not work on Windows yet.
153
- // NOTE: WE CANNOT DO AS WITH sourceIsInsideDocRoot, because it relies on realpath, which only translates EXISTING paths.
154
- // $destination does not exist yet, when this method is called from webp-realizer.php
155
- if (strpos($destination, $imageRoot . '/doc-root/') === 0) {
156
 
157
- $imageRoot .= '/doc-root';
158
- // "Eat" the left part off the $destination parameter. $destination is for example:
159
- // "/var/www/webp-express-tests/we0/wp-content-moved/webp-express/webp-images/doc-root/wordpress/uploads-moved/2018/12/tegning5-300x265.jpg.webp"
160
- // We also eat the slash (+1)
161
- $sourceRel = substr($destination, strlen($imageRoot) + 1);
162
 
163
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
164
- $source = $docRoot . '/' . $sourceRel;
165
- $source = preg_replace('/\\.(webp)$/', '', $source);
166
- } else {
167
- $imageRoot .= '/abs';
168
- $sourceRel = substr($destination, strlen($imageRoot) + 1);
169
- $source = $sourceRel;
170
- $source = preg_replace('/\\.(webp)$/', '', $source);
171
- }
172
- $source = self::sanitizeAbsFilePath($source);
173
- if (!@file_exists($source)) {
174
  return false;
175
  }
 
176
  return $source;
177
  }
178
 
179
  /**
180
- * Find source corresponding to destination (mingled)
181
- * Returns false if not found. Otherwise returns path to source
 
 
 
 
 
182
  */
183
  private static function findSourceMingled($destination, $destinationExt)
184
  {
185
- if ($destinationExt == 'append') {
186
- $source = preg_replace('/\\.(webp)$/', '', $destination);
187
- } else {
188
- $source = preg_replace('#\\.webp$#', '.jpg', $destination);
189
- if (!@file_exists($source)) {
190
- $source = preg_replace('/\\.webp$/', '.jpeg', $destination);
191
- }
192
- if (!@file_exists($source)) {
193
- $source = preg_replace('/\\.webp$/', '.png', $destination);
 
 
 
 
 
 
 
 
 
 
194
  }
195
- }
196
- if (!@file_exists($source)) {
 
 
197
  return false;
198
  }
 
199
  return $source;
200
  }
201
 
202
  /**
203
- * Get source from destination (and some configurations)
204
- * Returns false if not found. Otherwise returns path to source
 
 
 
 
 
 
 
205
  */
206
  public static function findSource($destination, $destinationFolder, $destinationExt, $webExpressContentDirAbs)
207
  {
 
 
 
 
 
 
 
 
 
 
208
  if ($destinationFolder == 'mingled') {
209
  $result = self::findSourceMingled($destination, $destinationExt);
210
  if ($result === false) {
@@ -216,31 +227,58 @@ class ConvertHelperIndependent
216
  }
217
  }
218
 
 
 
 
 
 
 
 
 
219
  public static function getLogFilename($source, $logDir)
220
  {
221
- // Calculate path for log file
222
- // ---------------------------
223
- $logDir .= '/conversions';
224
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
 
 
225
 
226
- // Check if source is residing inside document root.
227
- // (it is, if path starts with document root + '/')
228
- if (self::sourceIsInsideDocRoot($source, $docRoot) ) {
 
 
 
 
 
229
 
230
  // We store relative to document root.
231
  // "Eat" the left part off the source parameter which contains the document root.
232
  // and also eat the slash (+1)
 
 
233
  $sourceRel = substr($source, strlen($docRoot) + 1);
234
- return $logDir . '/doc-root/' . $sourceRel . '.md';
235
- } else {
236
- // Source file is residing outside document root.
237
- // we must add complete path to structure
238
- return $logDir . '/abs' . $source . '.md';
239
  }
 
240
 
241
  }
242
 
243
- public static function createLogDir($logDir)
 
 
 
 
 
 
 
 
 
244
  {
245
  if (!is_dir($logDir)) {
246
  @mkdir($logDir, 0775, true);
@@ -260,18 +298,33 @@ APACHE
260
  return is_dir($logDir);
261
  }
262
 
263
- public static function saveLog($source, $logDir, $text, $msgTop)
 
 
 
 
 
 
 
 
 
 
264
  {
 
265
  if (!file_exists($logDir)) {
266
  self::createLogDir($logDir);
267
  }
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
 
 
 
 
 
275
  $logFolder = @dirname($logFile);
276
  if (!@file_exists($logFolder)) {
277
  mkdir($logFolder, 0777, true);
@@ -282,11 +335,50 @@ APACHE
282
  }
283
 
284
  /**
285
- * To convert with a specific converter, set it in the $converter param.
 
 
 
 
 
 
 
 
286
  */
287
  public static function convert($source, $destination, $convertOptions, $logDir, $converter = null) {
288
  include_once __DIR__ . '/../../vendor/autoload.php';
289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  $success = false;
291
  $msg = '';
292
  $logger = new BufferLogger();
@@ -322,15 +414,44 @@ APACHE
322
 
323
  }
324
 
 
 
 
 
325
  public static function serveConverted($source, $destination, $serveOptions, $logDir, $logMsgTop = '')
326
  {
327
  include_once __DIR__ . '/../../vendor/autoload.php';
328
 
329
- // TODO: error_log()
330
- //ini_set('display_errors', 0);
331
- //error_reporting(0);
 
 
 
 
 
 
332
 
333
- //echo '<pre>' . print_r($serveOptions, true) . '</pre>'; exit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  $convertLogger = new BufferLogger();
336
  WebPConvert::serveConverted($source, $destination, $serveOptions, null, $convertLogger);
1
  <?php
2
 
3
  /*
4
+ This class is made to be dependent only on a few WebPExpress 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 \WebPConvert\WebPConvert;
10
  use \WebPConvert\Convert\ConverterFactory;
11
+ use \WebPConvert\Exceptions\WebPConvertException;
12
  use \WebPConvert\Loggers\BufferLogger;
13
  use \WebPExpress\FileHelper;
14
+ use \WebPExpress\SanityCheck;
15
+ use \WebPExpress\SanityException;
16
 
17
  class ConvertHelperIndependent
18
  {
19
 
20
+ /**
21
+ *
22
+ * @return boolean Whether or not the destination corresponding to a given source should be stored in the same folder or the separate (in wp-content/webp-express)
23
+ */
24
+ private static function storeMingledOrNot($source, $destinationFolder, $uploadDirAbs)
25
  {
26
  if ($destinationFolder != 'mingled') {
27
  return false;
47
  return strpos($normalizedSource, $normalizedDocRoot) === 0;
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  /**
52
+ * Get destination path corresponding to the source path given (and some configurations)
53
+ *
54
+ * If for example Operation mode is set to "mingled" and extension is set to "Append .webp",
55
+ * the result of finding the destination path that corresponds to "/path/to/logo.jpg" will be "/path/to/logo.jpg.webp".
56
+ *
57
+ * @param string $source Path to source file
58
+ * @param string $destinationFolder 'mingled' or 'separate'
59
+ * @param string $destinationExt Extension ('append' or 'set')
60
+ * @param string $webExpressContentDirAbs
61
+ * @param string $uploadDirAbs
62
+ *
63
+ * @return string|false Returns path to destination corresponding to source, or false on failure
64
  */
65
+ public static function getDestination($source, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
66
  {
67
+ // At this point, everything has already been checked for sanity. But for good meassure, lets
68
+ // check the most important parts again. This is after all a public method.
69
+ // ------------------------------------------------------------------
 
 
 
 
 
 
 
70
 
71
+ try {
72
+ // Check source
73
+ // --------------
74
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
75
+
76
+ // Calculate destination and check that the result is sane
77
+ // -------------------------------------------------------
78
+ if (self::storeMingledOrNot($source, $destinationFolder, $uploadDirAbs)) {
79
+ if ($destinationExt == 'append') {
80
+ $destination = SanityCheck::absPathIsInDocRoot($source . '.webp');
81
+ } else {
82
+ $destination = preg_replace('/\\.(jpe?g|png)$/', '', $source) . '.webp';
83
+ $destination = SanityCheck::absPathIsInDocRoot($source . '.webp');
84
+ }
85
+ } else {
86
+ $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
87
+ $imageRoot = $webExpressContentDirAbs . '/webp-images';
88
+ SanityCheck::absPathIsInDocRoot($imageRoot);
89
 
 
 
 
90
  $sourceRel = substr($source, strlen($docRoot) + 1);
91
+ $destination = $imageRoot . '/doc-root/' . $sourceRel . '.webp';
92
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
 
 
 
93
  }
94
+
95
+ } catch (SanityException $e) {
96
+ return false;
97
  }
98
+
99
+ return $destination;
100
  }
101
 
102
+
103
  /**
104
+ * Find source corresponding to destination, separate.
105
  *
106
+ * We can rely on destinationExt being "append" for separate.
107
+ * Returns false if source file is not found or if a path is not sane. Otherwise returns path to source
108
+ * destination does not have to exist.
 
109
  *
110
+ * @param string $destination Path to destination file (does not have to exist)
111
+ * @param string $webExpressContentDirAbs
112
  *
113
+ * @return string|false Returns path to source, if found. If not - or a path is not sane, false is returned
 
114
  */
115
+ private static function findSourceSeparate($destination, $webExpressContentDirAbs)
116
+ {
117
+ try {
118
 
119
+ // Check that destination path is sane and inside document root
120
+ // --------------------------
121
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
122
 
 
 
 
123
 
124
+ // Check that calculated image root is sane and inside document root
125
+ // --------------------------
126
+ $imageRoot = SanityCheck::absPathIsInDocRoot($webExpressContentDirAbs . '/webp-images/doc-root');
127
 
 
 
 
 
 
 
 
 
 
128
 
129
+ // Calculate source and check that it is sane and exists
130
+ // -----------------------------------------------------
 
 
 
 
 
 
131
 
132
+ // TODO: This does not work on Windows yet.
133
+ // NOTE: WE CANNOT DO AS WITH sourceIsInsideDocRoot, because it relies on realpath, which only translates EXISTING paths.
134
+ // $destination does not exist yet, when this method is called from webp-realizer.php
135
+ if (strpos($destination, $imageRoot . '/doc-root/') === 0) {
 
136
 
137
+ // "Eat" the left part off the $destination parameter. $destination is for example:
138
+ // "/var/www/webp-express-tests/we0/wp-content-moved/webp-express/webp-images/doc-root/wordpress/uploads-moved/2018/12/tegning5-300x265.jpg.webp"
139
+ // We also eat the slash (+1)
140
+ $sourceRel = substr($destination, strlen($imageRoot) + 1);
 
141
 
142
+ $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
143
+ $source = $docRoot . '/' . $sourceRel;
144
+ $source = preg_replace('/\\.(webp)$/', '', $source);
145
+ }
146
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
147
+
148
+ } catch (SanityException $e) {
 
 
 
 
149
  return false;
150
  }
151
+
152
  return $source;
153
  }
154
 
155
  /**
156
+ * Find source corresponding to destination (mingled)
157
+ * Returns false if not found. Otherwise returns path to source
158
+ *
159
+ * @param string $destination Path to destination file (does not have to exist)
160
+ * @param string $destinationExt Extension ('append' or 'set')
161
+ *
162
+ * @return string|false Returns path to source, if found. If not - or a path is not sane, false is returned
163
  */
164
  private static function findSourceMingled($destination, $destinationExt)
165
  {
166
+ try {
167
+
168
+ // Check that destination path is sane and inside document root
169
+ // --------------------------
170
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
171
+
172
+
173
+ // Calculate source and check that it is sane and exists
174
+ // -----------------------------------------------------
175
+ if ($destinationExt == 'append') {
176
+ $source = preg_replace('/\\.(webp)$/', '', $destination);
177
+ } else {
178
+ $source = preg_replace('#\\.webp$#', '.jpg', $destination);
179
+ if (!@file_exists($source)) {
180
+ $source = preg_replace('/\\.webp$/', '.jpeg', $destination);
181
+ }
182
+ if (!@file_exists($source)) {
183
+ $source = preg_replace('/\\.webp$/', '.png', $destination);
184
+ }
185
  }
186
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
187
+
188
+
189
+ } catch (SanityException $e) {
190
  return false;
191
  }
192
+
193
  return $source;
194
  }
195
 
196
  /**
197
+ * Get source from destination (and some configurations)
198
+ * Returns false if not found. Otherwise returns path to source
199
+ *
200
+ * @param string $destination Path to destination file (does not have to exist)
201
+ * @param string $destinationFolder 'mingled' or 'separate'
202
+ * @param string $destinationExt Extension ('append' or 'set')
203
+ * @param string $webExpressContentDirAbs
204
+ *
205
+ * @return string|false Returns path to source, if found. If not - or a path is not sane, false is returned
206
  */
207
  public static function findSource($destination, $destinationFolder, $destinationExt, $webExpressContentDirAbs)
208
  {
209
+ try {
210
+
211
+ // Check that destination path is sane and inside document root
212
+ // --------------------------
213
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
214
+
215
+ } catch (SanityException $e) {
216
+ return false;
217
+ }
218
+
219
  if ($destinationFolder == 'mingled') {
220
  $result = self::findSourceMingled($destination, $destinationExt);
221
  if ($result === false) {
227
  }
228
  }
229
 
230
+ /**
231
+ *
232
+ * @param string $source Path to source file
233
+ * @param string $logDir The folder where log files are kept
234
+ *
235
+ * @return string|false Returns computed filename of log - or false if a path is not sane
236
+ *
237
+ */
238
  public static function getLogFilename($source, $logDir)
239
  {
240
+ try {
241
+
242
+ // Check that source path is sane and inside document root
243
+ // -------------------------------------------------------
244
+ $source = SanityCheck::absPathIsInDocRoot($source);
245
+
246
 
247
+ // Check that log path is sane and inside document root
248
+ // -------------------------------------------------------
249
+ $logDir = SanityCheck::absPathIsInDocRoot($logDir);
250
+
251
+
252
+ // Compute and check log path
253
+ // --------------------------
254
+ $logDirForConversions = $logDir .= '/conversions';
255
 
256
  // We store relative to document root.
257
  // "Eat" the left part off the source parameter which contains the document root.
258
  // and also eat the slash (+1)
259
+
260
+ $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
261
  $sourceRel = substr($source, strlen($docRoot) + 1);
262
+ $logFileName = $logDir . '/doc-root/' . $sourceRel . '.md';
263
+ SanityCheck::absPathIsInDocRoot($logFileName);
264
+
265
+ } catch (SanityException $e) {
266
+ return false;
267
  }
268
+ return $logFileName;
269
 
270
  }
271
 
272
+ /**
273
+ * Create the directory for log files and put a .htaccess file into it, which prevents
274
+ * it to be viewed from the outside (not that it contains any sensitive information btw, but for good measure).
275
+ *
276
+ * @param string $logDir The folder where log files are kept
277
+ *
278
+ * @return boolean Whether it was created successfully or not.
279
+ *
280
+ */
281
+ private static function createLogDir($logDir)
282
  {
283
  if (!is_dir($logDir)) {
284
  @mkdir($logDir, 0775, true);
298
  return is_dir($logDir);
299
  }
300
 
301
+ /**
302
+ * Saves the log file corresponding to a conversion.
303
+ *
304
+ * @param string $source Path to the source file that was converted
305
+ * @param string $logDir The folder where log files are kept
306
+ * @param string $text Content of the log file
307
+ * @param string $msgTop A message that is printed before the conversion log (containing version info)
308
+ *
309
+ *
310
+ */
311
+ private static function saveLog($source, $logDir, $text, $msgTop)
312
  {
313
+
314
  if (!file_exists($logDir)) {
315
  self::createLogDir($logDir);
316
  }
317
 
318
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
319
 
320
+ $text = 'WebP Express 0.14.11. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
321
 
322
  $logFile = self::getLogFilename($source, $logDir);
323
 
324
+ if ($logFile === false) {
325
+ return;
326
+ }
327
+
328
  $logFolder = @dirname($logFile);
329
  if (!@file_exists($logFolder)) {
330
  mkdir($logFolder, 0777, true);
335
  }
336
 
337
  /**
338
+ * Trigger an actual conversion with webp-convert.
339
+ *
340
+ * PS: To convert with a specific converter, set it in the $converter param.
341
+ *
342
+ * @param string $source Path to the source file that was converted.
343
+ * @param string $destination Path to the destination file (may exist or not).
344
+ * @param array $convertOptions Conversion options.
345
+ * @param string $logDir The folder where log files are kept.
346
+ * @param string $converter (optional) Set it to convert with a specific converter.
347
  */
348
  public static function convert($source, $destination, $convertOptions, $logDir, $converter = null) {
349
  include_once __DIR__ . '/../../vendor/autoload.php';
350
 
351
+ // At this point, everything has already been checked for sanity. But for good meassure, lets
352
+ // check the most important parts again. This is after all a public method.
353
+ // ------------------------------------------------------------------
354
+ try {
355
+
356
+ // Check that source path is sane, exists, is a file and is inside document root
357
+ // -------------------------------------------------------
358
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
359
+
360
+
361
+ // Check that destination path is sane and is inside document root
362
+ // -------------------------------------------------------
363
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
364
+ $destination = SanityCheck::pregMatch('#\.webp$#', $destination, 'Destination does not end with .webp');
365
+
366
+
367
+ // Check that log path is sane and inside document root
368
+ // -------------------------------------------------------
369
+ $logDir = SanityCheck::absPathIsInDocRoot($logDir);
370
+
371
+
372
+ // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
373
+
374
+ } catch (SanityException $e) {
375
+ return [
376
+ 'success' => false,
377
+ 'msg' => $e->getMessage(),
378
+ 'log' => '',
379
+ ];
380
+ }
381
+
382
  $success = false;
383
  $msg = '';
384
  $logger = new BufferLogger();
414
 
415
  }
416
 
417
+ /**
418
+ * Serve a converted file (if it does not already exist, a conversion is triggered - all handled in webp-convert).
419
+ *
420
+ */
421
  public static function serveConverted($source, $destination, $serveOptions, $logDir, $logMsgTop = '')
422
  {
423
  include_once __DIR__ . '/../../vendor/autoload.php';
424
 
425
+ // At this point, everything has already been checked for sanity. But for good meassure, lets
426
+ // check again. This is after all a public method.
427
+ // ---------------------------------------------
428
+ try {
429
+
430
+ // Check that source path is sane, exists, is a file and is inside document root
431
+ // -------------------------------------------------------
432
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
433
+
434
 
435
+ // Check that destination path is sane and is inside document root
436
+ // -------------------------------------------------------
437
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
438
+ $destination = SanityCheck::pregMatch('#\.webp$#', $destination, 'Destination does not end with .webp');
439
+
440
+
441
+ // Check that log path is sane and inside document root
442
+ // -------------------------------------------------------
443
+ $logDir = SanityCheck::absPathIsInDocRoot($logDir);
444
+
445
+
446
+ // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
447
+
448
+ } catch (SanityException $e) {
449
+ $msg = $e->getMessage();
450
+ echo $msg;
451
+ header('X-WebP-Express-Error: ' . $msg, true);
452
+ // TODO: error_log() ?
453
+ exit;
454
+ }
455
 
456
  $convertLogger = new BufferLogger();
457
  WebPConvert::serveConverted($source, $destination, $serveOptions, null, $convertLogger);
lib/classes/HandleUploadHooks.php CHANGED
@@ -5,6 +5,8 @@ namespace WebPExpress;
5
  use \WebPExpress\Config;
6
  use \WebPExpress\Convert;
7
  use \WebPExpress\Mime;
 
 
8
 
9
  class HandleUploadHooks
10
  {
@@ -58,9 +60,14 @@ class HandleUploadHooks
58
  */
59
  public static function handleUpload($filearray, $overrides = false, $ignore = false)
60
  {
61
- $filename = $filearray['file'];
62
- self::convertIf($filename);
63
-
 
 
 
 
 
64
  return $filearray;
65
  }
66
 
@@ -70,7 +77,14 @@ class HandleUploadHooks
70
  */
71
  public static function handleMakeIntermediateSize($filename)
72
  {
73
- self::convertIf($filename);
 
 
 
 
 
 
 
74
  return $filename;
75
  }
76
  }
5
  use \WebPExpress\Config;
6
  use \WebPExpress\Convert;
7
  use \WebPExpress\Mime;
8
+ use \WebPExpress\SanityCheck;
9
+ use \WebPExpress\SanityException;
10
 
11
  class HandleUploadHooks
12
  {
60
  */
61
  public static function handleUpload($filearray, $overrides = false, $ignore = false)
62
  {
63
+ if (isset($filearray['file'])) {
64
+ try {
65
+ $filename = SanityCheck::absPathExistsAndIsFileInDocRoot($filearray['file']);
66
+ self::convertIf($filename);
67
+ } catch (SanityException $e) {
68
+ // fail silently. (maybe we should write to debug log instead?)
69
+ }
70
+ }
71
  return $filearray;
72
  }
73
 
77
  */
78
  public static function handleMakeIntermediateSize($filename)
79
  {
80
+ if (!is_null($filename)) {
81
+ try {
82
+ $filenameToConvert = SanityCheck::absPathExistsAndIsFileInDocRoot($filearray['file']);
83
+ self::convertIf($filenameToConvert);
84
+ } catch (SanityException $e) {
85
+ // fail silently. (maybe we should write to debug log instead?)
86
+ }
87
+ }
88
  return $filename;
89
  }
90
  }
lib/classes/SanityCheck.php CHANGED
@@ -115,6 +115,23 @@ class SanityCheck
115
  return $input;
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  public static function absPath($input)
119
  {
120
  return self::path($input);
@@ -151,6 +168,13 @@ class SanityCheck
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)'
115
  return $input;
116
  }
117
 
118
+ public static function absPathIsInDocRoot($input, $errorMsg = 'Path is outside allowed path')
119
+ {
120
+ $docRoot = self::absPath($_SERVER["DOCUMENT_ROOT"]);
121
+
122
+ // Use realpath to expand symbolic links and check if it exists
123
+ $docRoot = realpath($docRoot);
124
+ if ($docRoot === false) {
125
+ throw new SanityException('Cannot find document root');
126
+ }
127
+ $docRoot = rtrim($docRoot, '/');
128
+ $docRoot = self::absPathExists($docRoot, 'Document root does not exist!');
129
+ $docRoot = self::absPathExistsAndIsDir($docRoot, 'Document root is not a directory!');
130
+
131
+ self::pathBeginsWith($input, $docRoot . '/', $errorMsg);
132
+ return $input;
133
+ }
134
+
135
  public static function absPath($input)
136
  {
137
  return self::path($input);
168
  return $input;
169
  }
170
 
171
+ public static function absPathExistsAndIsFileInDocRoot($input)
172
+ {
173
+ self::absPathExistsAndIsFile($input);
174
+ self::absPathIsInDocRoot($input);
175
+ return $input;
176
+ }
177
+
178
  public static function absPathExistsAndIsNotDir(
179
  $input,
180
  $errorMsg = 'Path points to a directory (it should point to a file)'
lib/options/enqueue_scripts.php CHANGED
@@ -8,7 +8,7 @@ use \WebPExpress\Paths;
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')) {
8
  include_once __DIR__ . '/../classes/Config.php';
9
  use \WebPExpress\Config;
10
 
11
+ $ver = '2'; // 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')) {
lib/options/page-messages.php CHANGED
@@ -13,15 +13,10 @@ use \WebPExpress\Paths;
13
  use \WebPExpress\PlatformInfo;
14
  use \WebPExpress\State;
15
 
16
- //use \WebPExpress\BulkConvert;
17
- //echo '<pre>' . print_r(BulkConvert::getList($config), true) . "</pre>";
18
- //echo '<pre>' . print_r(BulkConvert::convertFile('/var/www/webp-express-tests/we0/wordpress/uploads-moved/space in name.jpg'), true) . "</pre>";
19
-
20
  if ((!State::getState('configured', false))) {
21
  include __DIR__ . "/page-welcome.php";
22
  }
23
 
24
-
25
  /*
26
  if (CapabilityTest::modRewriteWorking()) {
27
  echo 'mod rewrite works. that is nice';
13
  use \WebPExpress\PlatformInfo;
14
  use \WebPExpress\State;
15
 
 
 
 
 
16
  if ((!State::getState('configured', false))) {
17
  include __DIR__ . "/page-welcome.php";
18
  }
19
 
 
20
  /*
21
  if (CapabilityTest::modRewriteWorking()) {
22
  echo 'mod rewrite works. that is nice';
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.10
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.11
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
wod/webp-on-demand.php CHANGED
@@ -121,7 +121,8 @@ class WebPOnDempand
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'])) {
@@ -134,7 +135,7 @@ class WebPOnDempand
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)
@@ -151,10 +152,10 @@ class WebPOnDempand
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
121
  // Check if it is in an environment variable
122
  $source = self::getEnvPassedInRewriteRule('REQFN');
123
  if ($source !== false) {
124
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
125
+ echo $source; exit;
126
  } else {
127
  // Check if it is in header (but only if .htaccess was configured to send in header)
128
  if (isset($wodOptions['base-htaccess-on-these-capability-tests'])) {
135
  }
136
  if ((!$passThrougEnvVarDefinitelyAvailable) && (!$passThroughHeaderDefinitelyUnavailable)) {
137
  if (isset($_SERVER['HTTP_REQFN'])) {
138
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($_SERVER['HTTP_REQFN']);
139
  }
140
  } else {
141
  // Check querystring (relative path)
152
  (isset($_GET['source']) || isset($_GET['xsource']))
153
  ) {
154
  if (isset($_GET['source'])) {
155
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot($_GET['source']);
156
  } else {
157
  $xsrc = SanityCheck::noControlChars($_GET['xsource']);
158
+ $source = SanityCheck::absPathExistsAndIsFileInDocRoot(substr($xsrc, 1));
159
  }
160
  } else {
161
  // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
wod/webp-realizer-old.php DELETED
@@ -1,278 +0,0 @@
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
@@ -88,7 +88,7 @@ class WebPRealizer
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
@@ -137,10 +137,10 @@ class WebPRealizer
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
@@ -152,7 +152,7 @@ class WebPRealizer
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
88
  // Check config file name
89
  // ---------------------------------
90
  $checking = 'config file';
91
+ $configFilename = SanityCheck::absPathExistsAndIsFileInDocRoot($webExpressContentDirAbs . '/config/wod-options.json');
92
 
93
 
94
  // Check config file
137
  (isset($_GET['destination']) || isset($_GET['xdestination']))
138
  ) {
139
  if (isset($_GET['destination'])) {
140
+ $destination = SanityCheck::absPathIsInDocRoot($_GET['destination']);
141
  } else {
142
  $xdest = SanityCheck::noControlChars($_GET['xdestination']);
143
+ $destination = SanityCheck::absPathIsInDocRoot(substr($xdest, 1));
144
  }
145
  } else {
146
  // Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
152
  }
153
 
154
  $destination = SanityCheck::pregMatch('#\.webp$#', $destination, 'Does not end with .webp');
155
+ $destination = SanityCheck::absPathIsInDocRoot($destination);
156
 
157
 
158
  // Validate source path