Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
62.40% |
78 / 125 |
|
50.00% |
4 / 8 |
CRAP | n/a |
0 / 0 |
|
| _get_log_version | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| _get_log_fields | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
1 | |||
| _read_last_line | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
3.10 | |||
| _log_fields_ini | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| _log_fields_end | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
2 | |||
| _write_log_line | |
22.22% |
2 / 9 |
|
0.00% |
0 / 1 |
16.76 | |||
| close_log_file | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
110 | |||
| append_logline | |
61.70% |
29 / 47 |
|
0.00% |
0 / 1 |
36.20 | |||
| LogEvent | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | n/a |
0 / 0 |
|||
| 1 | <?php |
| 2 | include_once __DIR__."/../../Legacy/common/session.php"; |
| 3 | |
| 4 | abstract class LogEvent { |
| 5 | const TicketUpdate = 'e0'; // ticket event |
| 6 | const Drawer = 'e1'; // drawer event |
| 7 | const TicketBAI = 'e3'; // ticket BAI event |
| 8 | const UsrLogin = 'l1'; // user place login event |
| 9 | const UsrLogout = 'l2'; // user place logout event |
| 10 | const GstLogin = 'l3'; // guest login event |
| 11 | const GstLogout = 'l4'; // guest logout event |
| 12 | } |
| 13 | |
| 14 | function _get_log_version(){ |
| 15 | return 'v6'; |
| 16 | } |
| 17 | |
| 18 | function _get_log_fields(){ |
| 19 | return [ |
| 20 | 'objid', // the ticket database objid |
| 21 | 'orderno', // the ticket orderno |
| 22 | 'series', // The ticket series |
| 23 | 'invceno', // the ticket invceno |
| 24 | 'status', // the ticket status |
| 25 | 'price', // the ticket price |
| 26 | 'refund', // the ticket refund value |
| 27 | 'payment', // the payment method |
| 28 | 'webpay', // objid for the payment entry for this ticket (if paid online) |
| 29 | 'account', // objid for the account entry for this ticket (if paid to account) |
| 30 | 'session', // session for this operation |
| 31 | 'paidby', // session of the user who paid the ticket |
| 32 | 'paidon', // date when ticket was paid |
| 33 | 'drawer', // the device name for drawer events |
| 34 | 'change', // change type for BAI events |
| 35 | 'error' // received error (if any) for SII/BAI events |
| 36 | ]; |
| 37 | } |
| 38 | |
| 39 | function _read_last_line($filepath){ |
| 40 | $file = fopen($filepath, 'r'); |
| 41 | if ($file !== false){ |
| 42 | if (fstat($file)['size'] == 0){ |
| 43 | return false; // empty file |
| 44 | } |
| 45 | else { |
| 46 | $offset = -1; |
| 47 | do { |
| 48 | fseek($file, --$offset, SEEK_END); |
| 49 | } while (fgetc($file) != PHP_EOL); |
| 50 | |
| 51 | return fread($file, 0 - $offset); |
| 52 | } |
| 53 | |
| 54 | fclose($file); |
| 55 | } |
| 56 | else { |
| 57 | return false; |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | function _log_fields_ini($timestamp, $type, &$logline){ |
| 62 | $line = ''; |
| 63 | $date = $timestamp; // $conx->now(); |
| 64 | $vers = _get_log_version(); |
| 65 | $evtp = $type; |
| 66 | |
| 67 | $line .= $date.";"; // current timestamp |
| 68 | $line .= $vers.";"; // current log version |
| 69 | $line .= $evtp.";"; // event type |
| 70 | |
| 71 | $logline .= $line; // update the log line |
| 72 | } |
| 73 | |
| 74 | function _log_fields_end($dstfile, &$updt, &$logline){ |
| 75 | $line = ''; |
| 76 | $vers = ''; |
| 77 | |
| 78 | $hash = '00000000000000000000000000000000'; |
| 79 | $last = _read_last_line($dstfile); |
| 80 | if ($last){ |
| 81 | $last = explode(';', $last, -1); |
| 82 | $vers = $last[1]; // read the last log version |
| 83 | $hash = $last[count($last)-2]; // read the last log hash |
| 84 | } |
| 85 | |
| 86 | $updt = (_get_log_version() != $vers); // check if log version is updated |
| 87 | |
| 88 | $line .= md5($logline).";"; // md5 for this line |
| 89 | $line .= $hash.";"; // md5 of the previous line |
| 90 | |
| 91 | $logline .= $line; // update the log line |
| 92 | } |
| 93 | |
| 94 | function _write_log_line($file, $updated, &$fields, &$logline){ |
| 95 | // write the file header (new file or version change) |
| 96 | if ((fstat($file)['size'] == 0) || $updated){ |
| 97 | $hdrline = 'date;version;event;'; |
| 98 | foreach($fields as $field){ |
| 99 | $hdrline .= $field.";"; |
| 100 | } |
| 101 | $hdrline .= 'hash;prev;'; |
| 102 | |
| 103 | if ($updated){ |
| 104 | fprintf($file, PHP_EOL); |
| 105 | } |
| 106 | fprintf($file, $hdrline.PHP_EOL); |
| 107 | } |
| 108 | |
| 109 | // write the log contents |
| 110 | fprintf($file, $logline.PHP_EOL); |
| 111 | } |
| 112 | |
| 113 | function close_log_file($logfile, $sigfile){ |
| 114 | if (!function_exists('openssl_sign')) { |
| 115 | addlog(__FILE__, LogLevel::Warning, "OpenSSL library extension is not available!"); |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | $logdata = file_exists($logfile) ? file_get_contents($logfile) : null; |
| 120 | if ($logdata){ |
| 121 | $privkeyfile = __DIR__."/../../Resources/certs/selfsigned.key"; |
| 122 | |
| 123 | $keycontents = file_exists($privkeyfile) ? file_get_contents($privkeyfile) : null; |
| 124 | if (!$keycontents){ |
| 125 | addlog(__FILE__, LogLevel::Error, "Could not read content for private key: '".$privkeyfile."'"); |
| 126 | return false; |
| 127 | } |
| 128 | else { |
| 129 | if (!openssl_sign($logdata, $sigdata, $keycontents, OPENSSL_ALGO_SHA256)){ |
| 130 | while ($msg = openssl_error_string()) { |
| 131 | addlog(__FILE__, LogLevel::Error, "OpenSSL error: ".$msg.""); |
| 132 | } |
| 133 | |
| 134 | return false; |
| 135 | } |
| 136 | |
| 137 | if (!file_put_contents($sigfile, $sigdata) || !chmod($sigfile, 0444)){ |
| 138 | addlog(__FILE__, LogLevel::Error, "Could not write signed contents into: '".$sigfile."'"); |
| 139 | return false; |
| 140 | } |
| 141 | |
| 142 | return true; // succesfully signed |
| 143 | } |
| 144 | } |
| 145 | else { |
| 146 | addlog(__FILE__, LogLevel::Error, "Could not read content for source file: '".$logfile."'"); |
| 147 | return false; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | function append_logline($filepath, $timestamp, $place, $type, $lineinfo){ |
| 152 | $logline = ''; |
| 153 | $fields = _get_log_fields(); |
| 154 | $updated = false; |
| 155 | |
| 156 | $file = fopen($filepath, 'a+'); |
| 157 | if (!$file){ |
| 158 | addlog(__FILE__, LogLevel::Error, "Could not open registry log file in '".$filepath."' (for append)"); |
| 159 | $error = error_get_last(); |
| 160 | if ($error) addlog(__FILE__, LogLevel::Debug, $error['message']); |
| 161 | } |
| 162 | else { |
| 163 | if (flock($file, LOCK_EX)){ // lock the file |
| 164 | // build the log line |
| 165 | _log_fields_ini($timestamp, $type, $logline); |
| 166 | foreach($fields as $field) { // add the log contents |
| 167 | $logline .= ((array_key_exists($field, $lineinfo)) ? $lineinfo[$field] : '').";"; |
| 168 | } |
| 169 | _log_fields_end($filepath, $updated, $logline); |
| 170 | |
| 171 | // append to the file |
| 172 | _write_log_line($file, $updated, $fields, $logline); |
| 173 | |
| 174 | flock($file, LOCK_UN); // unlock the file |
| 175 | } |
| 176 | else { |
| 177 | addlog(__FILE__, LogLevel::Error, "Could not lock the file '".$filepath."' for writing."); |
| 178 | $error = error_get_last(); |
| 179 | if ($error) addlog(__FILE__, LogLevel::Debug, $error['message']); |
| 180 | } |
| 181 | |
| 182 | fclose($file); |
| 183 | } |
| 184 | |
| 185 | // write the ticket line in the per date log file |
| 186 | $dstpath = sprintf("%s/%05d", AppConstants::RegLogPath(), $place); |
| 187 | if (!file_exists($dstpath) && !mkdir($dstpath, 0775)){ |
| 188 | addlog(__FILE__, LogLevel::Error, "Could not create local registry log folder in '".$dstpath."'"); |
| 189 | $error = error_get_last(); |
| 190 | if ($error) addlog(__FILE__, LogLevel::Debug, $error['message']); |
| 191 | } |
| 192 | |
| 193 | if (is_dir($dstpath)){ |
| 194 | $dstfile = sprintf('%s/%s_%s.csv', $dstpath, explode(' ', $timestamp)[0], $type); |
| 195 | $file = fopen($dstfile, 'a+'); |
| 196 | if (!$file){ |
| 197 | addlog(__FILE__, LogLevel::Error, "Could not open registry log file in '".$dstfile."' (for append)"); |
| 198 | $error = error_get_last(); |
| 199 | if ($error) addlog(__FILE__, LogLevel::Debug, $error['message']); |
| 200 | } |
| 201 | else { |
| 202 | _write_log_line($file, $updated, $fields, $logline); |
| 203 | fclose($file); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | // check if all the files (except the current one are signed) |
| 208 | if (is_dir($dstpath)){ |
| 209 | $dstdate = explode(' ', $timestamp)[0]; |
| 210 | |
| 211 | $logfiles = glob($dstpath . '/*.csv'); |
| 212 | foreach ($logfiles as $logfile) { |
| 213 | $filename = basename($logfile); |
| 214 | |
| 215 | $filedate = explode('_', explode('.', $filename)[0])[0]; |
| 216 | if ($filedate == $dstdate){ |
| 217 | continue; // do not verify signature for current file |
| 218 | } |
| 219 | |
| 220 | $sigfile = preg_replace('/\.csv$/', '.sig', $logfile); |
| 221 | if (!file_exists($sigfile)) { |
| 222 | addlog(__FILE__, LogLevel::Information, "Closing and signing logs file '".$logfile."'.."); |
| 223 | if (close_log_file($logfile, $sigfile)){ |
| 224 | addlog(__FILE__, LogLevel::Information, "Logs file signature written in'".$sigfile."'"); |
| 225 | } |
| 226 | else { |
| 227 | addlog(__FILE__, LogLevel::Error, "Error generating signed content in'".$sigfile."'"); |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | ?> |