PluginProbe ʕ •ᴥ•ʔ
Tracking Code Manager / 2.7.0
Tracking Code Manager v2.7.0
2.8.0 2.7.0 trunk 1.11.8 1.11.9 1.12.0 1.12.1 1.12.2 1.12.3 1.4 1.5 2.0.0 2.0.1 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0
tracking-code-manager / includes / classes / utils / Logger.php
tracking-code-manager / includes / classes / utils Last commit date
Cron.php 4 years ago Ecommerce.php 4 years ago Language.php 4 years ago Logger.php 1 month ago MobileDetect.php 1 month ago Options.php 1 month ago Plugin.php 1 month ago Properties.php 4 years ago Tracking.php 1 month ago Utils.php 1 month ago
Logger.php
156 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 class TCMP_Logger {
9 private $name;
10 private $context = array();
11
12 public function __construct( $name = 'TCMP' ) {
13 if ( '' == $name ) {
14 $name = 'TCMP';
15 }
16 $this->name = $name;
17 }
18
19 public function pushContext( $context ) {
20 array_push( $this->context, $context );
21 }
22 public function popContext() {
23 array_pop( $this->context );
24 }
25
26 public function fatal( $message, $v1 = null, $v2 = null, $v3 = null, $v4 = null, $v5 = null, $v6 = null ) {
27 $what = $this->write( '[FATAL]', $message, $v1, $v2, $v3, $v4, $v5, $v6 );
28 // The message can interpolate snippet names and other stored values, so
29 // escape before it reaches the browser.
30 die( esc_html( $what ) );
31 }
32 public function debug( $message, $v1 = null, $v2 = null, $v3 = null, $v4 = null, $v5 = null, $v6 = null ) {
33 $this->write( '[DEBUG]', $message, $v1, $v2, $v3, $v4, $v5, $v6 );
34 }
35 public function info( $message, $v1 = null, $v2 = null, $v3 = null, $v4 = null, $v5 = null, $v6 = null ) {
36 $this->write( '[INFO] ', $message, $v1, $v2, $v3, $v4, $v5, $v6 );
37 }
38 public function error( $message, $v1 = null, $v2 = null, $v3 = null, $v4 = null, $v5 = null, $v6 = null ) {
39 $this->write( '[ERROR]', $message, $v1, $v2, $v3, $v4, $v5, $v6 );
40 }
41 private function dump( $v ) {
42 if ( is_array( $v ) && 0 == count( $v ) ) {
43 $v = '[]';
44 }
45 if ( null != $v ) {
46 if ( is_array( $v ) || is_object( $v ) ) {
47 $v = print_r( $v, true );
48 }
49 }
50 if ( is_bool( $v ) ) {
51 $v = ( $v ? 'TRUE' : 'FALSE' );
52 }
53 return $v;
54 }
55 private function write( $verbosity, $message, $v1 = null, $v2 = null, $v3 = null, $v4 = null, $v5 = null, $v6 = null ) {
56 global $tcmp;
57
58 $text = sprintf(
59 $message,
60 $this->dump( $v1 ),
61 $this->dump( $v2 ),
62 $this->dump( $v3 ),
63 $this->dump( $v4 ),
64 $this->dump( $v5 ),
65 $this->dump( $v6 )
66 );
67 $message = date( 'd/m/Y H:i:s' ) . ' ' . $verbosity . ' ';
68 if ( count( $this->context ) > 0 ) {
69 $message .= '{' . $this->context[ count( $this->context ) - 1 ] . '} ';
70 }
71 $message = "\n" . $message . $text;
72 if ( ! $tcmp->options->isLoggerEnable() ) {
73 return $message;
74 }
75
76 $hasErrors = false;
77 $dir = $this->get_log_dir();
78 if ( '' == $dir ) {
79 return $message;
80 }
81 // The filename carries a site-specific, unguessable suffix so the log is
82 // not fetchable by guessing the URL, even on servers where the deny
83 // rules below are not honoured (e.g. nginx ignores .htaccess). See F-07.
84 $name = sanitize_file_name( $this->name );
85 $filename = $dir . $name . '_' . gmdate( 'Ym' ) . '_' . $this->log_token() . '.txt';
86 if ( ! $handle = fopen( $filename, 'a' ) ) {
87 $hasErrors = true;
88 }
89
90 if ( ! $hasErrors && fwrite( $handle, $message ) === false ) {
91 $hasErrors = true;
92 }
93
94 if ( ! $hasErrors ) {
95 fclose( $handle );
96 }
97 return $message;
98 }
99
100 // Resolve (and lazily create + protect) the directory logs are written to.
101 // Logs live under wp-content/uploads/ rather than inside the plugin folder,
102 // and the directory is hardened with deny rules so it is not web-readable
103 // (see F-07).
104 private function get_log_dir() {
105 if ( ! function_exists( 'wp_upload_dir' ) ) {
106 return '';
107 }
108 $upload = wp_upload_dir();
109 if ( ! is_array( $upload ) || empty( $upload['basedir'] ) ) {
110 return '';
111 }
112 $dir = trailingslashit( $upload['basedir'] ) . 'tcm-logs/';
113 if ( ! is_dir( $dir ) && function_exists( 'wp_mkdir_p' ) ) {
114 wp_mkdir_p( $dir );
115 }
116 if ( ! is_dir( $dir ) ) {
117 return '';
118 }
119 $this->protect_dir( $dir );
120 return $dir;
121 }
122
123 // Drop the standard "deny all" guard files into the log directory so the
124 // logs cannot be downloaded over HTTP on either Apache or IIS, plus a blank
125 // index.php to prevent directory listing.
126 private function protect_dir( $dir ) {
127 $guards = array(
128 'index.php' => "<?php\n// Silence is golden.\n",
129 '.htaccess' => "Order Allow,Deny\nDeny from all\n",
130 'web.config' => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n\t<system.webServer>\n\t\t<authorization>\n\t\t\t<deny users=\"*\" />\n\t\t</authorization>\n\t</system.webServer>\n</configuration>\n",
131 );
132 foreach ( $guards as $file => $contents ) {
133 $path = $dir . $file;
134 if ( ! file_exists( $path ) ) {
135 @file_put_contents( $path, $contents );
136 }
137 }
138 }
139
140 // Short, deterministic, site-specific token used only to obfuscate the log
141 // filename. Derived from WordPress salts when available; this is filename
142 // obfuscation, not a security-sensitive digest.
143 private function log_token() {
144 $seed = 'tcm-log';
145 if ( defined( 'AUTH_SALT' ) && '' != AUTH_SALT ) {
146 $seed .= AUTH_SALT;
147 } elseif ( defined( 'ABSPATH' ) ) {
148 $seed .= ABSPATH;
149 }
150 if ( function_exists( 'wp_hash' ) ) {
151 return substr( wp_hash( $seed ), 0, 12 );
152 }
153 return substr( md5( $seed ), 0, 12 );
154 }
155 }
156