PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.0.12
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.0.12
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.12, at vendor-prefixed/trustedlogin/client/src/Logging.php

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