PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.0.2
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.0.2
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / vendor-prefixed / trustedlogin / client / src / Logging.php

Logging.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.0.2, at vendor-prefixed/trustedlogin/client/src/Logging.php

343 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Logging
4 *
5 * @package ContentControl\Vendor\TrustedLogin\Client
6 *
7 * @copyright 2021 Katz Web Services, Inc.
8 *
9 * @license GPL-2.0-or-later
10 * Modified by code-atlantic on 18-September-2023 using Strauss.
11 * @see https://github.com/BrianHenryIE/strauss
12 */
13 namespace ContentControl\Vendor\TrustedLogin;
14
15
16 class Logging {
17
18 /**
19 * Path to logging directory (inside the WP Uploads base dir)
20 */
21 const DIRECTORY_PATH = 'trustedlogin-logs/';
22
23 /**
24 * @var string Namespace for the vendor
25 */
26 private $ns;
27
28 /**
29 * @var bool $logging_enabled
30 */
31 private $logging_enabled = false;
32
33 /**
34 * @var Logger|null|false Null: not instantiated; False: failed to instantiate.
35 */
36 private $klogger = null;
37
38 /**
39 * Logger constructor.
40 */
41 public function __construct( Config $config ) {
42
43 $this->ns = $config->ns();
44
45 $this->logging_enabled = $config->get_setting( 'logging/enabled', false );
46
47 $this->klogger = $this->setup_klogger( $config );
48 }
49
50 /**
51 * Attempts to initialize KLogger logging
52 *
53 * @param Config $config
54 *
55 * @return false|Logger
56 */
57 private function setup_klogger( $config ) {
58
59
60 $logging_directory = null;
61
62 $configured_logging_dir = $config->get_setting( 'logging/directory', '' );
63
64 if( ! empty( $configured_logging_dir ) ) {
65
66 $logging_directory = $this->check_directory( $configured_logging_dir );
67
68 if ( ! $logging_directory ) {
69 return false;
70 }
71 }
72
73 if ( ! $logging_directory ) {
74 $logging_directory = $this->maybe_make_logging_directory();
75 }
76
77 // Directory cannot be found or created. Cannot log.
78 if( ! $logging_directory ) {
79 return false;
80 }
81
82 // Directory cannot be written to
83 if( ! $this->check_directory( $logging_directory ) ) {
84 return false;
85 }
86
87 try {
88
89 $DateTime = new \DateTime( '@' . time() );
90
91 // Filename hash changes every day, make it harder to guess
92 $filename_hash_data = $this->ns . home_url( '/' ) . $DateTime->format( 'z' );
93
94 $default_options = array(
95 'extension' => 'log',
96 'dateFormat' => 'Y-m-d G:i:s.u',
97 'filename' => sprintf( 'trustedlogin-client-debug-%s-%s', $DateTime->format( 'Y-m-d' ), \hash( 'sha256', $filename_hash_data ) ),
98 'flushFrequency' => false,
99 'logFormat' => false,
100 'appendContext' => true,
101 );
102
103 $settings_options = $config->get_setting( 'logging/options', $default_options );
104
105 $options = wp_parse_args( $settings_options, $default_options );
106
107 $klogger = new Logger (
108 $logging_directory,
109 $config->get_setting( 'logging/threshold', 'notice' ),
110 $options
111 );
112
113 } catch ( \RuntimeException $exception ) {
114
115 $this->log( 'Could not initialize KLogger: ' . $exception->getMessage(), __METHOD__, 'error' );
116
117 return false;
118
119 } catch ( \Exception $exception ) {
120
121 $this->log( 'DateTime could not be created: ' . $exception->getMessage(), __METHOD__, 'error' );
122
123 return false;
124 }
125
126 return $klogger;
127 }
128
129 /**
130 * Returns the full path to the log file
131 *
132 * @since 1.5.0
133 *
134 * @return null|string Path to log file, if exists; null if not instantiated.
135 */
136 public function get_log_file_path() {
137
138 if ( ! $this->klogger instanceof Logger ) {
139 return null;
140 }
141
142 return $this->klogger->getLogFilePath();
143 }
144
145 /**
146 * Checks whether a path exists and is writable
147 *
148 * @param string $dirpath Path to directory
149 *
150 * @return bool|string If exists and writable, returns original string. Otherwise, returns false.
151 */
152 private function check_directory( $dirpath ) {
153
154 $dirpath = (string) $dirpath;
155 $file_exists = file_exists( $dirpath );
156 $is_writable = wp_is_writable( $dirpath );
157
158 // If the configured setting path exists and is writeable, use it.
159 if( $file_exists && $is_writable ) {
160 return $dirpath;
161 }
162
163 // Otherwise, try and log default errors
164 if( ! $file_exists ) {
165 $this->log( 'The defined logging directory does not exist: ' . $dirpath, __METHOD__, 'error' );
166 }
167
168 if( ! $is_writable ) {
169 $this->log( 'The defined logging directory exists but could not be written to: ' . $dirpath, __METHOD__, 'error' );
170 }
171
172 return false;
173 }
174
175 /**
176 * Returns the directory to use for logging if not defined by Config. Creates one if it doesn't exist.
177 *
178 * Note: Created directories are protected by an index.html file to prevent browsing.
179 *
180 * @return false|string Directory path, if exists; False if failure.
181 */
182 private function maybe_make_logging_directory() {
183
184 $upload_dir = wp_upload_dir();
185
186 $log_dir = trailingslashit( $upload_dir['basedir'] ) . self::DIRECTORY_PATH;
187
188 // Directory exists; return early
189 if( file_exists( $log_dir ) ) {
190
191 $this->prevent_directory_browsing( $log_dir );
192
193 return $log_dir;
194 }
195
196 // Create the folder using wp_mkdir_p() instead of relying on KLogger
197 $folder_created = wp_mkdir_p( $log_dir );
198
199 // Something went wrong mapping the directory
200 if( ! $folder_created ) {
201 $this->log( 'The log directory could not be created: ' . $log_dir, __METHOD__, 'error' );
202 return false;
203 }
204
205 // Protect directory from being browsed by adding index.html
206 $this->prevent_directory_browsing( $log_dir );
207
208 // Make sure the new log directory can be written to
209 return $log_dir;
210 }
211
212 /**
213 * Prevent browsing a directory by adding an index.html file to it
214 *
215 * Code inspired by @see wp_privacy_generate_personal_data_export_file()
216 *
217 * @param string $dirpath Path to directory to protect (in this case, logging)
218 *
219 * @return bool True: File exists or was created; False: file could not be created.
220 */
221 private function prevent_directory_browsing( $dirpath ) {
222
223 // Protect export folder from browsing.
224 $index_pathname = $dirpath . 'index.html';
225
226 if ( file_exists( $index_pathname ) ) {
227 return true;
228 }
229
230 $file = fopen( $index_pathname, 'w' );
231
232 if ( false === $file ) {
233 $this->log( 'Unable to protect directory from browsing.', __METHOD__, 'error' );
234 return false;
235 }
236
237 fwrite( $file, '<!-- Silence is golden. TrustedLogin is also pretty great. -->' );
238 fclose( $file );
239
240 return true;
241 }
242
243 /**
244 * Returns whether logging is enabled
245 *
246 * @return bool
247 */
248 public function is_enabled() {
249
250 $is_enabled = ! empty( $this->logging_enabled );
251
252 /**
253 * Filter: Whether debug logging is enabled in TrustedLogin Client
254 *
255 * @since 1.0.0
256 *
257 * @param bool $debug_mode Default: false
258 */
259 $is_enabled = apply_filters( 'trustedlogin/' . $this->ns . '/logging/enabled', $is_enabled );
260
261 return (bool) $is_enabled;
262 }
263
264 /**
265 * @see https://github.com/php-fig/log/blob/master/Psr/Log/LogLevel.php for log levels
266 *
267 * @param string|\WP_Error $message Message or error to log. If a WP_Error is passed, $data is ignored.
268 * @param string $method Method where the log was called
269 * @param string $level PSR-3 log level
270 * @param \WP_Error|\Exception|mixed $data Optional. Error data. Ignored if $message is WP_Error.
271 *
272 */
273 public function log( $message = '', $method = '', $level = 'debug', $data = array() ) {
274
275 if ( ! $this->is_enabled() ) {
276 return;
277 }
278
279 $levels = array( 'emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug' );
280
281 if ( ! in_array( $level, $levels ) ) {
282
283 $this->log( sprintf( 'Invalid level passed by %s method: %s', $method, $level ), __METHOD__, 'error' );
284
285 $level = 'debug'; // Continue processing original log
286 }
287
288 $log_message = $message;
289
290 if ( is_wp_error( $log_message ) ) {
291 $data = $log_message; // Store WP_Error as extra data.
292 $log_message = ''; // The message will be constructed below.
293 }
294
295 if ( ! is_string( $log_message ) ) {
296 $log_message = print_r( $log_message, true );
297 }
298
299 if ( is_wp_error( $data ) ) {
300 $log_message .= sprintf( '[%s] %s', $data->get_error_code(), $data->get_error_message() );
301 }
302
303 if ( $data instanceof \Exception ) {
304 $log_message .= sprintf( '[%s] %s', $data->getCode(), $data->getMessage() );
305 }
306
307 // Keep PSR-4 compatible
308 if ( $data && ! is_array( $data ) ) {
309 $data = array( $data );
310 }
311
312 do_action( 'trustedlogin/' . $this->ns . '/logging/log', $message, $method, $level, $data );
313 do_action( 'trustedlogin/' . $this->ns . '/logging/log_' . $level, $message, $method, $data );
314
315 // If logging is in place, don't use the error_log
316 if ( has_action( 'trustedlogin/' . $this->ns . '/logging/log' ) || has_action( 'trustedlogin/' . $this->ns . '/logging/log_' . $level ) ) {
317 return;
318 }
319
320 // The logger class didn't load for some reason
321 if ( ! $this->klogger ) {
322
323 $wp_debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
324 $wp_debug_log = defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG;
325
326 // If WP_DEBUG and WP_DEBUG_LOG are enabled, log errors to that file.
327 if ( $wp_debug && $wp_debug_log ) {
328
329 if ( ! empty( $data ) ) {
330 $log_message .= ' Error data: ' . print_r( $data, true );
331 }
332
333 error_log( $method . ' (' . $level . '): ' . $log_message );
334 }
335
336 return;
337 }
338
339 $this->klogger->{$level}( $log_message, (array) $data );
340 }
341
342 }
343