Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.40% covered (warning)
62.40%
78 / 125
50.00% covered (danger)
50.00%
4 / 8
CRAP
n/a
0 / 0
_get_log_version
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
_get_log_fields
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
_read_last_line
77.78% covered (warning)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
3.10
_log_fields_ini
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
_log_fields_end
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
_write_log_line
22.22% covered (danger)
22.22%
2 / 9
0.00% covered (danger)
0.00%
0 / 1
16.76
close_log_file
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
110
append_logline
61.70% covered (warning)
61.70%
29 / 47
0.00% covered (danger)
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?>