A small fix for the missing double quotes in custom_report file.
[openemr.git] / library / log.inc
blobf0f2f18f20cddc084a2798e7c0cf4c432ae4b569
1 <?php
2 #require_once("{$GLOBALS['srcdir']}/sql.inc");
3 require_once(dirname(__FILE__). "/sql.inc");
4 require_once(dirname(__FILE__). "/formdata.inc.php");
6 function newEvent($event, $user, $groupname, $success, $comments="", $patient_id=null) {
7     $adodb = $GLOBALS['adodb']['db'];
8     $crt_user=isset($_SERVER['SSL_CLIENT_S_DN_CN']) ?  $_SERVER['SSL_CLIENT_S_DN_CN'] : null;
9     /* More details added to the log */
10     $sql = "insert into log ( date, event, user, groupname, success, comments, crt_user, patient_id) " .
11             "values ( NOW(), " . $adodb->qstr($event) . "," . $adodb->qstr($user) .
12             "," . $adodb->qstr($groupname) . "," . $adodb->qstr($success) . "," .
13             $adodb->qstr($comments) ."," .
14             $adodb->qstr($crt_user) ."," . $adodb->qstr($patient_id). ")";
16     $ret = sqlInsertClean_audit($sql);
17     if(($patient_id=="NULL") || ($patient_id==null))$patient_id=0;
19     send_atna_audit_msg($user, $groupname, $event, $patient_id, $success, $comments);
22 function getEventByDate($date, $user="", $cols="DISTINCT date, event, user, groupname, patient_id, success, comments, checksum")
24     $sql = "SELECT $cols FROM log WHERE date >= '$date 00:00:00' AND date <= '$date 23:59:59'";
25     if ($user) $sql .= " AND user LIKE '$user'";
26     $sql .= " ORDER BY date DESC LIMIT 5000";
27     $res = sqlStatement($sql);
28     for($iter=0; $row=sqlFetchArray($res); $iter++) {
29         $all[$iter] = $row;
30     }
31     return $all;
34 /******************
35  * Get records from the LOG and Extended_Log table
36  * using the optional parameters:
37  *   date : a specific date  (defaults to today)
38  *   user : a specific user  (defaults to none)
39  *   cols : gather specific columns  (defaults to date,event,user,groupname,comments)
40  *   sortby : sort the results by  (defaults to none)
41  * RETURNS:
42  *   array of results
43  ******************/
44 function getEvents($params) 
46     // parse the parameters
47     $cols = "DISTINCT date, event, user, groupname, patient_id, success, comments,checksum,crt_user, id ";
48     if (isset($params['cols']) && $params['cols'] != "") $cols = $params['cols'];
50     $date1 = date("Y-m-d H:i:s", time());
51     if (isset($params['sdate']) && $params['sdate'] != "") $date1= $params['sdate'];
52     
53     $date2 = date("Y-m-d H:i:s", time());
54     if (isset($params['edate']) && $params['edate'] != "") $date2= $params['edate'];
55     
56     $user = "";
57     if (isset($params['user']) && $params['user'] != "") $user= $params['user'];
59     //VicarePlus :: For Generating log with patient id.
60     $patient = "";
61     if (isset($params['patient']) && $params['patient'] != "") $patient= $params['patient'];
62     
63     $sortby = "";
64     if (isset($params['sortby']) && $params['sortby'] != "") $sortby = $params['sortby'];
65     
66     $levent = "";
67     if (isset($params['levent']) && $params['levent'] != "") $levent = $params['levent'];
68     
69      $tevent = "";
70     if (isset($params['tevent']) && $params['tevent'] != "") $tevent = $params['tevent'];
71     
72      $event = "";
73     if (isset($params['event']) && $params['event'] != "") $event = $params['event'];
74     if ($event!=""){
75     if ($sortby == "comments") $sortby = "description";
76     if ($sortby == "groupname") $sortby = ""; //VicarePlus :: since there is no groupname in extended_log
77     if ($sortby == "success") $sortby = "";   //VicarePlus :: since there is no success field in extended_log
78     if ($sortby == "checksum") $sortby = "";  //VicarePlus :: since there is no checksum field in extended_log
79     $columns = "DISTINCT date, event, user, recipient,patient_id,description";
80     $sql = "SELECT $columns FROM extended_log WHERE date >= '$date1' AND date <= '$date2'";
81     if ($user != "") $sql .= " AND user LIKE '$user'";
82     if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
83     if ($levent != "") $sql .= " AND event LIKE '$levent%'";
84     if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
85     $sql .= " LIMIT 5000";
86     }
87     else
88     {
89     // do the query
90     $sql = "SELECT $cols FROM log WHERE date >= '$date1' AND date <= '$date2'";
91     if ($user != "") $sql .= " AND user LIKE '$user'";
92     if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
93     if ($levent != "") $sql .= " AND event LIKE '$levent%'";
94     if ($tevent != "") $sql .= " AND event LIKE '%$tevent'";
95     if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
96     $sql .= " LIMIT 5000";
97     }
98     $res = sqlStatement($sql);
99     for($iter=0; $row=sqlFetchArray($res); $iter++) {
100         $all[$iter] = $row;
101     }
102     return $all;
105 /* Given an SQL insert/update that was just performeds:
106  * - Find the table and primary id of the row that was created/modified
107  * - Calculate the SHA1 checksum of that row (with all the
108  *   column values concatenated together).
109  * - Return the SHA1 checksum as a 40 char hex string.
110  * If this is not an insert/update query, return "".
111  * If multiple rows were modified, return "".
112  * If we're unable to determine the row modified, return "".
114  * TODO: May need to incorporate the binded stuff (still analyzing)
116  */
117 function sql_checksum_of_modified_row($statement)
119     $table = "";
120     $rid = "";
122     $tokens = preg_split("/[\s,(\'\"]+/", $statement);
123     /* Identifying the id for insert/replace statements for calculating the checksum */
124         if((strcasecmp($tokens[0],"INSERT")==0) || (strcasecmp($tokens[0],"REPLACE")==0)){
125         $table = $tokens[2];
126         $rid = mysql_insert_id($GLOBALS['dbh']);
127         /* For handling the table that doesn't have auto-increment column */
128         if ($rid === 0 || $rid === FALSE) {
129           if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map")
130            $id="acl_id";
131           else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map")
132           $id="group_id";
133           else
134            $id="id";
135           /* To handle insert statements */
136           if($tokens[3] == $id){
137              for($i=4;$i<count($tokens);$i++){
138                  if(strcasecmp($tokens[$i],"VALUES")==0){
139                   $rid=$tokens[$i+1];
140                      break;
141                 }// if close
142               }//for close
143             }//if close
144         /* To handle replace statements */
145           else if(strcasecmp($tokens[3],"SET")==0){
146                  if((strcasecmp($tokens[4],"ID")==0) || (strcasecmp($tokens[4],"`ID`")==0)){
147                   $rid=$tokens[6];
148            }// if close
149         }
151         else {          
152             return "";
153           }
154         }
155     }
156      /* Identifying the id for update statements for calculating the checksum */
157        else if(strcasecmp($tokens[0],"UPDATE")==0){
158         $table = $tokens[1];
160         $offset = 3;
161         $total = count($tokens);
163         /* Identifying the primary key column for the updated record */ 
164         if ($table == "form_physical_exam") {
165             $id = "forms_id";
166         }
167         else if ($table == "claims"){
168             $id = "patient_id";
169         }
170         else if ($table == "openemr_postcalendar_events") {
171             $id = "pc_eid";
172         }
173          else if ($table == "lang_languages"){
174             $id = "lang_id";
175          }
176          else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
177             $id = "pc_catid";
178          }
179          else if ($table == "openemr_postcalendar_limits"){
180             $id = "pc_limitid";
181          }
182          else if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
183            $id="acl_id";
184           }
185           else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
186           $id="group_id";
187           }
188            else {
189             $id = "id";
190            }
191         
192          /* Identifying the primary key value for the updated record */
193         while ($offset < $total) {
194             /* There are 4 possible ways that the id=123 can be parsed:
195              * ('id', '=', '123')
196              * ('id=', '123')
197              * ('id=123')
198              * ('id', '=123')
199              */
200             $rid = "";
201            /*id=', '123'*/
202             if (($tokens[$offset] == "$id=") && ($offset + 1 < $total)) {
203                 $rid = $tokens[$offset+1];
204                 break;
205             }
206            /* 'id', '=', '123' */
207             else if ($tokens[$offset] == "$id" && $tokens[$offset+1] == "=" && ($offset+2 < $total)) {
208                 $rid = $tokens[$offset+2];
209                 break;
210              }
211             /*id=123*/
212             else if (strpos($tokens[$offset], "$id=") === 0) {
213                 $tid = substr($tokens[$offset], strlen($id)+1);
214                 if(is_numeric($tid))
215                  $rid=$tid;
216                  break;
217              }
218            /*'id', '=123' */
219              else if($tokens[$offset] == "$id") {
220                 $tid = substr($tokens[$offset+1],1);
221                 if(is_numeric($tid))
222                  $rid=$tid;
223                 break;
224               } 
225             $offset += 1;
226         }//while ($offset < $total)
227     }// else if ($tokens[0] == 'update' || $tokens[0] == 'UPDATE' )
229     if ($table == "" || $rid == "") {
230         return "";
231     }
232    /* Framing sql statements for calculating checksum */ 
233    if ($table == "form_physical_exam") {
234         $sql = "select * from $table where forms_id = $rid";
235     }
236    else if ($table == "claims"){
237                 $sql = "select * from $table where patient_id = $rid";
238         }
239    else if ($table == "openemr_postcalendar_events") {
240             $sql = "select * from $table where pc_eid = $rid";
241         }
242     else if ($table == "lang_languages") {
243             $sql = "select * from $table where lang_id = $rid";
244     }
245     else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
246             $sql = "select * from $table where pc_catid = $rid";
247          }
248     else if ($table == "openemr_postcalendar_limits"){
249            $sql = "select * from $table where pc_limitid = $rid";
250          }
251     else if ($table ==  "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
252            $sql = "select * from $table where acl_id = $rid";
253          }
254      else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
255            $sql = "select * from $table where group_id = $rid";
256       }
257      else {
258         $sql = "select * from $table where id = $rid";
259     }
260     // When this function is working perfectly, can then shift to the
261     // sqlQueryNoLog() function.
262     $results = sqlQueryNoLogIgnoreError($sql);    
263     $column_values = "";
264    /* Concatenating the column values for the row inserted/updated */
265     if (is_array($results)) {
266         foreach ($results as $field_name => $field) {
267             $column_values .= $field;
268         }
269     }
270     // ViCarePlus: As per NIST standard, the encryption algorithm SHA1 is used
272     //error_log("COLUMN_VALUES: ".$column_values,0);
273     return sha1($column_values);
276 /* Create an XML audit record corresponding to RFC 3881.
277  * The parameters passed are the column values (from table 'log')
278  * for a single audit record.
279  */
280 function create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments)
283     /* Event action codes indicate whether the event is read/write.
284      * C = create, R = read, U = update, D = delete, E = execute
285      */
286     $eventActionCode = 'E';
287     if (substr($event, -7) == "-create") {
288         $eventActionCode = 'C';
289     }
290     else if (substr($event, -7) == "-insert") {
291         $eventActionCode = 'C';
292     }
293     else if (substr($event, -7) == "-select") {
294         $eventActionCode = 'R';
295     }
296     else if (substr($event, -7) == "-update") {
297         $eventActionCode = 'U';
298     }
299     else if (substr($event, -7) == "-delete") {
300         $eventActionCode = 'D';
301     }
303     $date_obj = new DateTime();
304     $eventDateTime = $date_obj->format(DATE_ATOM);
306     /* For EventOutcomeIndicator, 0 = success and 4 = minor error */
307     $eventOutcome = ($outcome === 1) ? 0 : 4;
309     /* The choice of event codes is up to OpenEMR.
310      * We're using the same event codes as
311      * https://iheprofiles.projects.openhealthtools.org/
312      */
313     $eventIDcodeSystemName = "DCM";
314     $eventIDcode = 0;
315     $eventIDdisplayName = $event;
317     if (strpos($event, 'patient-record') !== FALSE) {
318         $eventIDcode = 110110;
319         $eventIDdisplayName = 'Patient Record';
320     }
321     else if (strpos($event, 'view') !== FALSE) {
322         $eventIDCode = 110110;
323         $eventIDdisplayName = 'Patient Record';
324     }
325     else if (strpos($event, 'login') !== FALSE) {
326         $eventIDcode = 110122;
327         $eventIDdisplayName = 'Login';
328     }
329     else if (strpos($event, 'logout') !== FALSE) {
330         $eventIDcode = 110123;
331         $eventIDdisplayName = 'Logout';
332     }
333     else if (strpos($event, 'scheduling') !== FALSE) {
334         $eventIDcode = 110111;
335         $eventIDdisplayName = 'Patient Care Assignment';
336     }
337     else if (strpos($event, 'security-administration') !== FALSE) {
338         $eventIDcode = 110129;
339         $eventIDdisplayName = 'Security Administration';
340     }
343    
345     /* Variables used in ActiveParticipant section, which identifies
346      * the IP address and application of the source and destination.
347      */
348     $srcUserID = $_SERVER['SERVER_NAME'] . '|OpenEMR';
349     $srcNetwork = $_SERVER['SERVER_ADDR'];
350     $destUserID = $GLOBALS['atna_audit_host'];
351     $destNetwork = $GLOBALS['atna_audit_host'];
353     $userID = $user;
354     $userTypeCode = 1;
355     $userRole = 6;
356     $userCode = 11;
357     $userDisplayName = 'User Identifier';
359     $patientID = "";
360     $patientTypeCode = "";
361     $patientRole = "";
362     $patientCode = "";
363     $patientDisplayName = "";
365     if ($eventIdDisplayName == 'Patient Record') {
366         $patientID = $patient_id;
367         $pattientTypeCode = 1;
368         $patientRole = 1;
369         $patientCode = 2;
370         $patientDisplayName = 'Patient Number';
371     }
373     /* Construct the XML audit message, and save to $msg */
374     $msg =  '<?xml version="1.0" encoding="ASCII"?>';
375     $msg .= '<AuditMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ';
376     $msg .= 'xsi:noNamespaceSchemaLocation="healthcare-security-audit.xsd">';
378     /* Indicate the event code, text name, read/write type, and date/time */
379     $msg .= "<EventIdentification EventActionCode=\"$eventActionCode\" ";
380     $msg .= "EventDateTime=\"$eventDateTime\" ";
381     $msg .= "EventOutcomeIndicator=\"$eventOutcome\">";
382     $msg .= "<EventID code=\"eventIDcode\" displayName=\"$eventIDdisplayName\" ";
383     $msg .= "codeSystemName=\"DCM\" />";
384     $msg .= "</EventIdentification>";
386     /* Indicate the IP address and application of the source and destination */
387     $msg .= "<ActiveParticipant UserID=\"$srcUserID\" UserIsRequestor=\"true\" ";
388     $msg .= "NetworkAccessPointID=\"$srcNetwork\" NetworkAccessPointTypeCode=\"2\" >";
389     $msg .= "<RoleIDCode code=\"110153\" displayName=\"Source\" codeSystemName=\"DCM\" />";
390     $msg .= "</ActiveParticipant>";
391     $msg .= "<ActiveParticipant UserID=\"$destUserID\" UserIsRequestor=\"false\" ";
392     $msg .= "NetworkAccessPointID=\"$destNetwork\" NetworkAccessPointTypeCode=\"2\" >";
393     $msg .= "<RoleIDCode code=\"110152\" displayName=\"Destination\" codeSystemName=\"DCM\" />";
394     $msg .= "</ActiveParticipant>";
396     $msg .= "<AuditSourceIdentification AuditSourceID=\"$srcUserID\" />";
398     /* Indicate the username who generated this audit record */
399     $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$user\" ";
400     $msg .= "ParticipantObjectTypeCode=\"1\" ";
401     $msg .= "ParticipantObjectTypeCodeRole=\"6\" >";
402     $msg .= "<ParticipantObjectIDTypeCode code=\"11\" ";
403     $msg .= "displayName=\"User Identifier\" ";
404     $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
406     if ($eventIDdisplayName == 'Patient Record' && $patient_id != 0) {
407         $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$patient_id\" ";
408         $msg .= "ParticipantObjectTypeCode=\"1\" ";
409         $msg .= "ParticipantObjectTypeCodeRole=\"1\" >";
410         $msg .= "<ParticipantObjectIDTypeCode code=\"2\" ";
411         $msg .= "displayName=\"Patient Number\" ";
412         $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
413     }
414     $msg .= "</AuditMessage>";
416     /* Add the syslog header */
417     $date_obj = new DateTime($date);
418     $datestr= $date_obj->format(DATE_ATOM);
419     $msg = "<13> " . $datestr . " " . $_SERVER['SERVER_NAME'] . " " . $msg;
420     return $msg;
424 /* Create a TLS (SSLv3) connection to the given host/port.
425  * $localcert is the path to a PEM file with a client certificate and private key.
426  * $cafile is the path to the CA certificate file, for
427  *  authenticating the remote machine's certificate.
428  * If $cafile is "", the remote machine's certificate is not verified.
429  * If $localcert is "", we don't pass a client certificate in the connection.
431  * Return a stream resource that can be used with fwrite(), fread(), etc.
432  * Returns FALSE on error.
433  */
434 function create_tls_conn($host, $port, $localcert, $cafile) {
435     $sslopts = array();
436     if ($cafile !== null && $cafile != "") {
437         $sslopts['cafile'] = $cafile;
438         $sslopts['verify_peer'] = TRUE;
439         $sslopts['verify_depth'] = 10;
440     }
441     if ($localcert !== null && $localcert != "") {
442         $sslopts['local_cert'] = $localcert;
443     }
444     $opts = array('tls' => $sslopts, 'ssl' => $sslopts);
445     $ctx = stream_context_create($opts);
446     $timeout = 60;
447     $flags = STREAM_CLIENT_CONNECT;
449     $olderr = error_reporting(0);
450     $conn = stream_socket_client('tls://' . $host . ":" . $port, $errno, $errstr,
451                                  $timeout, $flags, $ctx);
452     error_reporting($olderr);
453     return $conn;
457 /* This function is used to send audit records to an Audit Repository Server,
458  * as described in the Audit Trail and Node Authentication (ATNA) standard.
459  * Given the fields in a single audit record:
460  * - Create an XML audit message according to RFC 3881, including the RFC5425 syslog header.
461  * - Create a TLS connection that performs bi-directions certificate authentication,
462  *   according to RFC 5425.
463  * - Send the XML message on the TLS connection.
464  */
465 function send_atna_audit_msg($user, $group, $event, $patient_id, $outcome, $comments)
467     /* If no ATNA repository server is configured, return */
468     if ($GLOBALS['atna_audit_host'] === null || $GLOBALS['atna_audit_host'] == "" || !($GLOBALS['enable_atna_audit'])) {
469         return;
470     }
471     $host = $GLOBALS['atna_audit_host'];
472     $port = $GLOBALS['atna_audit_port'];
473     $localcert = $GLOBALS['atna_audit_localcert'];
474     $cacert = $GLOBALS['atna_audit_cacert'];
475     $conn = create_tls_conn($host, $port, $localcert, $cacert);
476     if ($conn !== FALSE) {
477         $msg = create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments);
478         $len = strlen($msg);
479         fwrite($conn, $msg);
480         fclose($conn);
481     }
485 /* Add an entry into the audit log table, indicating that an
486  * SQL query was performed. $outcome is true if the statement
487  * successfully completed.  Determine the event type based on
488  * the tables present in the SQL query.
489  */
490 function auditSQLEvent($statement, $outcome, $binds=NULL)
493     $user =  isset($_SESSION['authUser']) ? $_SESSION['authUser'] : "";
494         /* Don't log anything if the audit logging is not enabled. Exception for "emergency" users */
495    if (!isset($GLOBALS['enable_auditlog']) || !($GLOBALS['enable_auditlog']))
496    {
497             if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
498             return;
499    }
502    $statement = trim($statement);
504     /* Don't audit SQL statements done to the audit log,
505      * or we'll have an infinite loop.
506      */
507     if ((stripos($statement, "insert into log") !== FALSE) ||
508         (stripos($statement, "FROM log ") !== FALSE) ) {
509         return;
510     }
512     $group = isset($_SESSION['authGroup']) ?  $_SESSION['authGroup'] : "";
513     $comments = $statement;
515     $processed_binds = "";
516     if (is_array($binds)) {
517         // Need to include the binded variable elements in the logging
518         $first_loop=true;
519         foreach ($binds as $value_bind) {
520             if ($first_loop) {
521                 //no comma
522                 $processed_binds .= "'" . add_escape_custom($value_bind) . "'";
523                 $first_loop=false;
524             }
525             else {
526                 //add a comma
527                 $processed_binds .= ",'" . add_escape_custom($value_bind) . "'";
528             }
529         }
530         if (!empty($processed_binds)) {
531             $processed_binds = "(" . $processed_binds . ")";
532             $comments .= " " . $processed_binds;
533         }
534     }
536     $success = 1;
537     $checksum = "";
538     if ($outcome === FALSE) {
539         $success = 0;
540     }
541     if ($outcome !== FALSE) {
542         // Should use the $statement rather than the processed
543         // variables, which includes the binded stuff. If do
544         // indeed need the binded values, then will need
545         // to include this as a separate array.
547         //error_log("STATEMENT: ".$statement,0);
548         //error_log("BINDS: ".$processed_binds,0);
549         $checksum = sql_checksum_of_modified_row($statement);
550         //error_log("CHECKSUM: ".$checksum,0);
551     }
552     /* Determine the query type (select, update, insert, delete) */
553     $querytype = "select";
554     $querytypes = array("select", "update", "insert", "delete","replace");
555     foreach ($querytypes as $qtype) {
556         if (stripos($statement, $qtype) === 0) {
557             $querytype = $qtype;
558         }
559     }
561     /* Determine the audit event based on the database tables */
562     $event = "other";
563     $tables = array("billing" => "patient-record",
564                     "claims" => "patient-record",
565                     "employer_data" => "patient-record",
566                     "forms" => "patient-record",
567                     "form_encounter" => "patient-record",
568                     "form_dictation" => "patient-record",
569                     "form_misc_billing_options" => "patient-record",
570                     "form_reviewofs" => "patient-record",
571                     "form_ros" => "patient-record",
572                     "form_soap" => "patient-record",
573                     "form_vitals" => "patient-record",
574                     "history_data" => "patient-record",
575                     "immunizations" => "patient-record",
576                     "insurance_data" => "patient-record",
577                     "issue_encounter" => "patient-record",
578                     "lists" => "patient-record",
579                     "patient_data" => "patient-record",
580                     "payments" => "patient-record",
581                     "pnotes" => "patient-record",
582                     "onotes" => "patient-record",
583                     "prescriptions" => "order",
584                     "transactions" => "patient-record",
585                                         "amendments" => "patient-record",
586                                         "amendments_history" => "patient-record",
587                     "facility" => "security-administration",
588                     "pharmacies" => "security-administration",
589                     "addresses" => "security-administration",
590                     "phone_numbers" => "security-administration",
591                     "x12_partners" => "security-administration",
592                     "insurance_companies" => "security-administration",
593                     "codes" => "security-administration",
594                     "registry" => "security-administration", 
595                     "users" => "security-administration",
596                     "groups" => "security-administration",
597                     "openemr_postcalendar_events" => "scheduling",
598                                 "openemr_postcalendar_categories" => "security-administration",
599                                 "openemr_postcalendar_limits" => "security-administration",
600                                 "openemr_postcalendar_topics" => "security-administration",
601                                 "gacl_acl" => "security-administration",
602                                 "gacl_acl_sections" => "security-administration",
603                                 "gacl_acl_seq" => "security-administration",
604                                 "gacl_aco" => "security-administration",
605                                 "gacl_aco_map" => "security-administration",
606                                 "gacl_aco_sections" => "security-administration",
607                                 "gacl_aco_sections_seq" => "security-administration",
608                                 "gacl_aco_seq" => "security-administration",
609                                 "gacl_aro" => "security-administration",
610                                 "gacl_aro_groups" => "security-administration",                                 
611                                 "gacl_aro_groups_id_seq" => "security-administration",
612                                 "gacl_aro_groups_map" => "security-administration",
613                                 "gacl_aro_map" => "security-administration",
614                                 "gacl_aro_sections" => "security-administration",
615                                 "gacl_aro_sections_seq" => "security-administration",
616                                 "gacl_aro_seq" => "security-administration",
617                                 "gacl_axo" => "security-administration",
618                                 "gacl_axo_groups" => "security-administration",
619                                 "gacl_axo_groups_map" => "security-administration",
620                                 "gacl_axo_map" => "security-administration",
621                                 "gacl_axo_sections" => "security-administration",
622                                 "gacl_groups_aro_map" => "security-administration",
623                                 "gacl_groups_axo_map" => "security-administration",
624                                 "gacl_phpgacl" => "security-administration"                             
625                   );
627     /* When searching for table names, truncate the SQL statement,
628      * removing any WHERE, SET, or VALUE clauses.
629      */
630         $truncated_sql = $statement;
631         $truncated_sql = str_replace("\n", " ", $truncated_sql);
632         if ($querytype == "select") {
633         $startwhere = stripos($truncated_sql, " where ");
634         if ($startwhere > 0) {
635         $truncated_sql = substr($truncated_sql, 0, $startwhere);
636     }
638         else {
639      $startparen = stripos($truncated_sql, "(" );
640      $startset = stripos($truncated_sql, " set ");
641      $startvalues = stripos($truncated_sql, " values ");
643         if ($startparen > 0) {
644             $truncated_sql = substr($truncated_sql, 0, $startparen);
645         }
646         if ($startvalues > 0) {
647             $truncated_sql = substr($truncated_sql, 0, $startvalues);
648         }
649         if ($startset > 0) {
650             $truncated_sql = substr($truncated_sql, 0, $startset);
651         }
652     }
653     foreach ($tables as $table => $value) {
654         if (strpos($truncated_sql, $table) !== FALSE) {
655             $event = $value;
656              break;
657         }
658       else if (strpos($truncated_sql, "form_") !== FALSE) {
659             $event = "patient-record";
660              break;
661         }
662     }
664     /* Avoid filling the audit log with trivial SELECT statements.
665      * Skip SELECTs from unknown tables.  
666      * Skip SELECT count() statements.
667      * Skip the SELECT made by the authCheckSession() function.
668      */
669     if ($querytype == "select") {
670         if ($event == "other")
671             return;
672         if (stripos($statement, "SELECT count(" ) === 0)
673             return;
674         if (stripos($statement, "select username, password from users") === 0)
675             return;
676     }
679     /* If the event is a patient-record, then note the patient id */
680     $pid = 0;
681     if ($event == "patient-record") {
682         if (array_key_exists('pid', $_SESSION) && $_SESSION['pid'] != '') {
683             $pid = $_SESSION['pid'];
684         }
685     }
687     /* If query events are not enabled, don't log them */
688     if (($querytype == "select") && !($GLOBALS['audit_events_query']))
689     { 
690        if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
691        return;
692     }
694     if (!($GLOBALS["audit_events_${event}"])) 
695     {
696         if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
697         return;
698     }
699            
701     $event = $event . "-" . $querytype;
703     $adodb = $GLOBALS['adodb']['db'];
704    
705     // ViSolve : Don't log sequences - to avoid the affect due to GenID calls
706     if (strpos($comments, "sequences") !== FALSE) return;
707         
708         $encrypt_comment = 'No';
709         //July 1, 2014: Ensoftek: Check and encrypt audit logging
710         if ($GLOBALS["enable_auditlog_encryption"]) {
711                 $comments =  aes256Encrypt($comments);
712                 $encrypt_comment = 'Yes';
713         }
714         
715         $current_datetime = date("Y-m-d H:i:s");
716     $SSL_CLIENT_S_DN_CN=isset($_SERVER['SSL_CLIENT_S_DN_CN']) ? $_SERVER['SSL_CLIENT_S_DN_CN'] : '';
717     $sql = "insert into log (date, event, user, groupname, comments, patient_id, success, checksum,crt_user) " .
718          "values ( ".
719                  $adodb->qstr($current_datetime). ", ".
720          $adodb->qstr($event) . ", " .
721          $adodb->qstr($user) . "," . 
722          $adodb->qstr($group) . "," .
723          $adodb->qstr($comments) . "," .
724          $adodb->qstr($pid) . "," .
725          $adodb->qstr($success) . "," . 
726          $adodb->qstr($checksum) . "," .
727          $adodb->qstr($SSL_CLIENT_S_DN_CN) .")";
728         sqlInsertClean_audit($sql);
729         
730         $last_log_id = $GLOBALS['adodb']['db']->Insert_ID();
731         $checksumGenerate = '';
732         //July 1, 2014: Ensoftek: Record the encryption checksum in a secondary table(log_comment_encrypt)
733         if ($querytype == 'update') {
734                 $concatLogColumns = $current_datetime.$event.$user.$group.$comments.$pid.$success.$checksum.$SSL_CLIENT_S_DN_CN;
735                 $checksumGenerate = sha1($concatLogColumns);
736         }
737         $encryptLogQry = "INSERT INTO log_comment_encrypt (log_id, encrypt, checksum) ".
738                                          " VALUES ( ".
739                                           $adodb->qstr($last_log_id) . "," .
740                                           $adodb->qstr($encrypt_comment) . "," .
741                                           $adodb->qstr($checksumGenerate) .")"; 
742         sqlInsertClean_audit($encryptLogQry);
743         
744     send_atna_audit_msg($user, $group, $event, $pid, $success, $comments);
745     //return $ret;
748 // May-29-2014: Ensoftek: For Auditable events and tamper-resistance (MU2)
749 // Insert Audit Logging Status into the LOG table.
750 function auditSQLAuditTamper($enable)
752     $user =  isset($_SESSION['authUser']) ? $_SESSION['authUser'] : "";
753     $group = isset($_SESSION['authGroup']) ?  $_SESSION['authGroup'] : "";
754     $pid = 0;
755     $checksum = "";
756     $success = 1;
757         $event = "security-administration" . "-" . "insert";
760     $adodb = $GLOBALS['adodb']['db'];
761    
762         if ($enable == "1")
763         {
764                 $comments = "Audit Logging Enabled.";
765         }
766         else
767         {
768                 $comments = "Audit Logging Disabled.";
769         }
770            
771     $SSL_CLIENT_S_DN_CN=isset($_SERVER['SSL_CLIENT_S_DN_CN']) ? $_SERVER['SSL_CLIENT_S_DN_CN'] : '';
772     $sql = "insert into log (date, event, user, groupname, comments, patient_id, success, checksum,crt_user) " .
773          "values ( NOW(), " . 
774          $adodb->qstr($event) . ", " .
775          $adodb->qstr($user) . "," . 
776          $adodb->qstr($group) . "," .
777          $adodb->qstr($comments) . "," .
778          $adodb->qstr($pid) . "," .
779          $adodb->qstr($success) . "," . 
780          $adodb->qstr($checksum) . "," .
781          $adodb->qstr($SSL_CLIENT_S_DN_CN) .")";
783     sqlInsertClean_audit($sql);
784     send_atna_audit_msg($user, $group, $event, $pid, $success, $comments);
788  * Record the patient disclosures.
789  * @param $dates    - The date when the disclosures are sent to the thrid party.
790  * @param $event    - The type of the disclosure.
791  * @param $pid      - The id of the patient for whom the disclosures are recorded.
792  * @param $comment  - The recipient name and description of the disclosure.
793  * @uname           - The username who is recording the disclosure.
794  */
795 function recordDisclosure($dates,$event,$pid,$recipient,$description,$user)
797         $adodb = $GLOBALS['adodb']['db'];
798         $crt_user= $_SERVER['SSL_CLIENT_S_DN_CN'];
799         $groupname=$_SESSION['authProvider'];
800         $success=1;
801         $sql = "insert into extended_log ( date, event, user, recipient, patient_id, description) " .
802             "values (" . $adodb->qstr($dates) . "," . $adodb->qstr($event) . "," . $adodb->qstr($user) .
803             "," . $adodb->qstr($recipient) . ",".
804             $adodb->qstr($pid) ."," .
805             $adodb->qstr($description) .")";
806         $ret = sqlInsertClean_audit($sql);
809  * Edit the disclosures that is recorded.
810  * @param $dates  - The date when the disclosures are sent to the thrid party.
811  * @param $event  - The type of the disclosure.
812  * param $comment - The recipient and the description of the disclosure are appended.
813  * $logeventid    - The id of the record which is to be edited.
814  */
815 function updateRecordedDisclosure($dates,$event,$recipient,$description,$disclosure_id)
817          $adodb = $GLOBALS['adodb']['db'];
818          $sql="update extended_log set
819                 event=" . $adodb->qstr($event) . ",
820                 date=" .  $adodb->qstr($dates) . ",
821                 recipient=" . $adodb->qstr($recipient) . ",
822                 description=" . $adodb->qstr($description) . "
823                 where id=" . $adodb->qstr($disclosure_id) . "";
824           $ret = sqlInsertClean_audit($sql);
827  * Delete the disclosures that is recorded.
828  * $deleteid - The id of the record which is to be deleted.
829  */
830 function deleteDisclosure($deletelid)
832         $sql="delete from extended_log where id='" . add_escape_custom($deletelid) . "'";
833         $ret = sqlInsertClean_audit($sql);
836 //July 1, 2014: Ensoftek: Function to AES256 encrypt a given string
837 function aes256Encrypt($sValue){
838         $sSecretKey = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
839     return rtrim(
840         base64_encode(
841             mcrypt_encrypt(
842                 MCRYPT_RIJNDAEL_256,
843                 $sSecretKey, $sValue, 
844                 MCRYPT_MODE_ECB, 
845                 mcrypt_create_iv(
846                     mcrypt_get_iv_size(
847                         MCRYPT_RIJNDAEL_256, 
848                         MCRYPT_MODE_ECB
849                     ), 
850                     MCRYPT_RAND)
851                 )
852             ), "\0"
853         );
856 //July 1, 2014: Ensoftek: Function to AES256 decrypt a given string
857 function aes256Decrypt($sValue){
858         $sSecretKey = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
859     return rtrim(
860         mcrypt_decrypt(
861             MCRYPT_RIJNDAEL_256, 
862             $sSecretKey, 
863             base64_decode($sValue), 
864             MCRYPT_MODE_ECB,
865             mcrypt_create_iv(
866                 mcrypt_get_iv_size(
867                     MCRYPT_RIJNDAEL_256,
868                     MCRYPT_MODE_ECB
869                 ), 
870                 MCRYPT_RAND
871             )
872         ), "\0"
873     );
876 //July 1, 2014: Ensoftek: Utility function to get data from table(log_comment_encrypt)
877 function logCommentEncryptData($log_id){
878         $encryptRow = array();
879         $logRes = sqlStatement("SELECT * FROM log_comment_encrypt WHERE log_id=?", array($log_id));
880         while($logRow = sqlFetchArray($logRes)){
881                 $encryptRow['encrypt'] = $logRow['encrypt'];
882                 $encryptRow['checksum'] = $logRow['checksum'];
883         }
884         return $encryptRow;