PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.5
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.5
3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 All 198 releases
← All changes | vendor/convertkit/convertkit-wordpress-libraries/src/class-convertkit-log.php +138 -21 2.2.2 → 3.4.5 View file →
@@ -13,8 +13,17 @@
13 13 */
14 14 class ConvertKit_Log {
15 15
16 16 /**
17 + * The path to the directory that will contain the log file.
18 + *
19 + * @since 1.4.2
20 + *
21 + * @var string
22 + */
23 + private $path = '';
24 +
25 + /**
17 26 * The path and filename of the log file.
18 27 *
19 28 * @since 1.0.0
20 29 *
@@ -19,9 +28,9 @@
19 28 * @since 1.0.0
20 29 *
21 30 * @var string
22 31 */
23 - private $log_file;
32 + private $log_file = '';
24 33
25 34 /**
26 35 * Constructor. Defines the log file location.
27 36 *
@@ -30,18 +39,93 @@
30 39 * @param string $path Path to where log file should be created/edited/read.
31 40 */
32 41 public function __construct( $path ) {
33 42
43 + // If legacy log files exist in the Plugin's directory, delete them now.
44 + $this->maybe_delete_legacy_log_files( $path );
45 +
46 + // Fetch the uploads directory.
47 + $upload_dir = wp_upload_dir();
48 +
49 + // Bail if the uploads directory is unavailable.
50 + if ( ! empty( $upload_dir['error'] ) || empty( $upload_dir['basedir'] ) ) {
51 + return;
52 + }
53 +
34 54 // Define location of log file.
35 - $this->log_file = trailingslashit( $path ) . 'log.txt';
55 + $this->path = trailingslashit( $upload_dir['basedir'] ) . 'kit-logs/';
56 + $this->log_file = $this->path . $this->get_log_file_name( $path );
36 57
37 - // Initialize WP_Filesystem.
38 - require_once ABSPATH . 'wp-admin/includes/file.php';
39 - WP_Filesystem();
58 + // If the secure log directory does not exist, create it now.
59 + $this->maybe_create_secure_log_directory();
40 60
41 61 }
42 62
43 63 /**
64 + * Deletes log files stored in the Plugin's directory by earlier versions of
65 + * this class.
66 + *
67 + * Deletes:
68 + * - `log.txt`, used prior to 1.4.2, which has no .htaccess or index.html protection,
69 + * - `log` directory and its contents, used from 1.4.2 to 2.6.0.
70 + *
71 + * @since 1.4.2
72 + *
73 + * @param string $path Path to the Plugin.
74 + */
75 + private function maybe_delete_legacy_log_files( $path ) {
76 +
77 + // If a log.txt file exists in the Plugin's directory (i.e. from 1.4.2 or earlier), delete it.
78 + $legacy_file = trailingslashit( $path ) . 'log.txt';
79 + if ( file_exists( $legacy_file ) ) {
80 + wp_delete_file( $legacy_file );
81 + }
82 +
83 + // If a log directory exists in the Plugin's directory (i.e. from 1.4.2 to 2.6.0), delete it and its contents.
84 + $legacy_path = trailingslashit( $path ) . 'log';
85 + if ( is_dir( $legacy_path ) ) {
86 + // Delete the files this class created in the log directory.
87 + foreach ( array( 'log.txt', '.htaccess', 'index.html' ) as $file ) {
88 + if ( file_exists( trailingslashit( $legacy_path ) . $file ) ) {
89 + wp_delete_file( trailingslashit( $legacy_path ) . $file );
90 + }
91 + }
92 +
93 + // Delete the log directory.
94 + rmdir( trailingslashit( $path ) . 'log' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
95 + }
96 +
97 + }
98 +
99 + /**
100 + * Creates a directory to store the log file, with .htaccess and index.html
101 + * files to protect the log file, as WooCommerce does.
102 + *
103 + * Disables logging if the directory could not be created, or isn't writable.
104 + *
105 + * @since 1.4.2
106 + */
107 + private function maybe_create_secure_log_directory() {
108 +
109 + // Create directory.
110 + wp_mkdir_p( $this->path );
111 +
112 + // Disable logging if the directory doesn't exist or isn't writable.
113 + if ( ! is_dir( $this->path ) || ! wp_is_writable( $this->path ) ) {
114 + $this->path = '';
115 + $this->log_file = '';
116 + return;
117 + }
118 +
119 + // Define files to protect the directory.
120 + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
121 + file_put_contents( $this->path . '.htaccess', 'deny from all' );
122 + file_put_contents( $this->path . 'index.html', '' );
123 + // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
124 +
125 + }
126 +
127 + /**
44 128 * Returns the path and filename of the log file.
45 129 *
46 130 * @since 1.0.0
47 131 *
@@ -61,8 +145,13 @@
61 145 * @return bool
62 146 */
63 147 public function exists() {
64 148
149 + // Bail if logging is disabled.
150 + if ( ! $this->log_file ) {
151 + return false;
152 + }
153 +
65 154 return file_exists( $this->get_filename() );
66 155
67 156 }
68 157
@@ -74,23 +163,29 @@
74 163 * @param string $entry Log Line Entry.
75 164 */
76 165 public function add( $entry ) {
77 166
78 - // Initialize WordPress file system.
79 - global $wp_filesystem;
167 + // Bail if logging is disabled.
168 + if ( ! $this->log_file ) {
169 + return;
170 + }
80 171
81 172 // Prefix the entry with a date and time.
82 173 $entry = '(' . gmdate( 'Y-m-d H:i:s' ) . ') ' . $entry . "\n";
83 174
84 - // Get any existing log file contents.
85 - $contents = $wp_filesystem->get_contents( $this->get_filename() );
175 + // Mask email addresses that may be contained within the entry.
176 + $entry = preg_replace_callback(
177 + '^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})^',
178 + function ( $matches ) {
179 + return preg_replace( '/\B[^@.]/', '*', $matches[0] );
180 + },
181 + $entry
182 + );
86 183
87 184 // Append entry.
88 - $contents .= $entry;
185 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
186 + file_put_contents( $this->get_filename(), $entry, FILE_APPEND );
89 187
90 - // Write contents.
91 - $wp_filesystem->put_contents( $this->get_filename(), $contents );
92 -
93 188 }
94 189
95 190 /**
96 191 * Reads the given number of lines from the log file.
@@ -101,11 +196,8 @@
101 196 * @return string Log file data
102 197 */
103 198 public function read( $number_of_lines = 500 ) {
104 199
105 - // Initialize WordPress file system.
106 - global $wp_filesystem;
107 -
108 200 // Bail if the log file does not exist.
109 201 if ( ! $this->exists() ) {
110 202 return '';
111 203 }
@@ -110,9 +202,10 @@
110 202 return '';
111 203 }
112 204
113 205 // Open log file.
114 - $log = $wp_filesystem->get_contents_array( $this->get_filename() );
206 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file
207 + $log = file( $this->get_filename() );
115 208
116 209 // Bail if the log file is empty.
117 210 if ( ! is_array( $log ) || ! count( $log ) ) {
118 211 return '';
@@ -129,12 +222,15 @@
129 222 * @since 1.0.0
130 223 */
131 224 public function clear() {
132 225
133 - // Initialize WordPress file system.
134 - global $wp_filesystem;
226 + // Bail if logging is disabled.
227 + if ( ! $this->log_file ) {
228 + return;
229 + }
135 230
136 - $wp_filesystem->put_contents( $this->get_filename(), '' );
231 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
232 + file_put_contents( $this->get_filename(), '' );
137 233
138 234 }
139 235
140 236 /**
@@ -143,9 +239,30 @@
143 239 * @since 1.0.0
144 240 */
145 241 public function delete() {
146 242
147 - unlink( $this->get_filename() );
243 + // Bail if logging is disabled.
244 + if ( ! $this->log_file ) {
245 + return;
246 + }
247 +
248 + wp_delete_file( $this->get_filename() );
249 +
250 + }
251 +
252 + /**
253 + * Returns the log file's name for the Plugin at the given path.
254 + *
255 + * @since 2.6.1
256 + *
257 + * @param string $path Path to the Plugin.
258 + * @return string
259 + */
260 + private function get_log_file_name( $path ) {
261 +
262 + $slug = sanitize_key( basename( untrailingslashit( $path ) ) );
263 +
264 + return $slug . '-' . wp_hash( $slug ) . '.log';
148 265
149 266 }
150 267
151 268 }