Support new security model in the formSubmit function - bug fix
[openemr.git] / library / log.inc
blob263c26c5d7efc6e622f84a675b047a0a34a73cf4
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="") {
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) " .
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) .  ")";
16     $ret = sqlInsertClean_audit($sql);
17     send_atna_audit_msg($user, $groupname, $event, 0, $success, $comments);
20 function getEventByDate($date, $user="", $cols="DISTINCT date, event, user, groupname, patient_id, success, comments, checksum")
22     $sql = "SELECT $cols FROM log WHERE date >= '$date 00:00:00' AND date <= '$date 23:59:59'";
23     if ($user) $sql .= " AND user LIKE '$user'";
24     $sql .= " ORDER BY date DESC LIMIT 5000";
25     $res = sqlStatement($sql);
26     for($iter=0; $row=sqlFetchArray($res); $iter++) {
27         $all[$iter] = $row;
28     }
29     return $all;
32 /******************
33  * Get records from the LOG and Extended_Log table
34  * using the optional parameters:
35  *   date : a specific date  (defaults to today)
36  *   user : a specific user  (defaults to none)
37  *   cols : gather specific columns  (defaults to date,event,user,groupname,comments)
38  *   sortby : sort the results by  (defaults to none)
39  * RETURNS:
40  *   array of results
41  ******************/
42 function getEvents($params) 
44     // parse the parameters
45     $cols = "DISTINCT date, event, user, groupname, patient_id, success, comments,checksum,crt_user";
46     if (isset($params['cols']) && $params['cols'] != "") $cols = $params['cols'];
48     $date1 = date("Y-m-d", time());
49     if (isset($params['sdate']) && $params['sdate'] != "") $date1= $params['sdate'];
50     
51     $date2 = date("Y-m-d", time());
52     if (isset($params['edate']) && $params['edate'] != "") $date2= $params['edate'];
53     
54     $user = "";
55     if (isset($params['user']) && $params['user'] != "") $user= $params['user'];
57     //VicarePlus :: For Generating log with patient id.
58     $patient = "";
59     if (isset($params['patient']) && $params['patient'] != "") $patient= $params['patient'];
60     
61     $sortby = "";
62     if (isset($params['sortby']) && $params['sortby'] != "") $sortby = $params['sortby'];
63     
64     $levent = "";
65     if (isset($params['levent']) && $params['levent'] != "") $levent = $params['levent'];
66     
67      $tevent = "";
68     if (isset($params['tevent']) && $params['tevent'] != "") $tevent = $params['tevent'];
69     
70      $event = "";
71     if (isset($params['event']) && $params['event'] != "") $event = $params['event'];
72     if ($event!=""){
73     if ($sortby == "comments") $sortby = "description";
74     if ($sortby == "groupname") $sortby = ""; //VicarePlus :: since there is no groupname in extended_log
75     if ($sortby == "success") $sortby = "";   //VicarePlus :: since there is no success field in extended_log
76     if ($sortby == "checksum") $sortby = "";  //VicarePlus :: since there is no checksum field in extended_log
77     $columns = "DISTINCT date, event, user, recipient,patient_id,description";
78     $sql = "SELECT $columns FROM extended_log WHERE date >= '$date1 00:00:00' AND date <= '$date2 23:59:59'";
79     if ($user != "") $sql .= " AND user LIKE '$user'";
80     if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
81     if ($levent != "") $sql .= " AND event LIKE '$levent%'";
82     if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
83     $sql .= " LIMIT 5000";
84     }
85     else
86     {
87     // do the query
88     $sql = "SELECT $cols FROM log WHERE date >= '$date1 00:00:00' AND date <= '$date2 23:59:59'";
89     if ($user != "") $sql .= " AND user LIKE '$user'";
90     if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
91     if ($levent != "") $sql .= " AND event LIKE '$levent%'";
92     if ($tevent != "") $sql .= " AND event LIKE '%$tevent'";
93     if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
94     $sql .= " LIMIT 5000";
95     }
96     $res = sqlStatement($sql);
97     for($iter=0; $row=sqlFetchArray($res); $iter++) {
98         $all[$iter] = $row;
99     }
100     return $all;
103 /* Given an SQL insert/update that was just performeds:
104  * - Find the table and primary id of the row that was created/modified
105  * - Calculate the SHA1 checksum of that row (with all the
106  *   column values concatenated together).
107  * - Return the SHA1 checksum as a 40 char hex string.
108  * If this is not an insert/update query, return "".
109  * If multiple rows were modified, return "".
110  * If we're unable to determine the row modified, return "".
112  * TODO: May need to incorporate the binded stuff (still analyzing)
114  */
115 function sql_checksum_of_modified_row($statement)
117     $table = "";
118     $rid = "";
120     $tokens = preg_split("/[\s,(\'\"]+/", $statement);
121     /* Identifying the id for insert/replace statements for calculating the checksum */
122         if((strcasecmp($tokens[0],"INSERT")==0) || (strcasecmp($tokens[0],"REPLACE")==0)){
123         $table = $tokens[2];
124         $rid = mysql_insert_id($GLOBALS['dbh']);
125         /* For handling the table that doesn't have auto-increment column */
126         if ($rid === 0 || $rid === FALSE) {
127           if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map")
128            $id="acl_id";
129           else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map")
130           $id="group_id";
131           else
132            $id="id";
133           /* To handle insert statements */
134           if($tokens[3] == $id){
135              for($i=4;$i<count($tokens);$i++){
136                  if(strcasecmp($tokens[$i],"VALUES")==0){
137                   $rid=$tokens[$i+1];
138                      break;
139                 }// if close
140               }//for close
141             }//if close
142         /* To handle replace statements */
143           else if(strcasecmp($tokens[3],"SET")==0){
144                  if((strcasecmp($tokens[4],"ID")==0) || (strcasecmp($tokens[4],"`ID`")==0)){
145                   $rid=$tokens[6];
146            }// if close
147         }
149         else {          
150             return "";
151           }
152         }
153     }
154      /* Identifying the id for update statements for calculating the checksum */
155        else if(strcasecmp($tokens[0],"UPDATE")==0){
156         $table = $tokens[1];
158         $offset = 3;
159         $total = count($tokens);
161         /* Identifying the primary key column for the updated record */ 
162         if ($table == "form_physical_exam") {
163             $id = "forms_id";
164         }
165         else if ($table == "claims"){
166             $id = "patient_id";
167         }
168         else if ($table == "openemr_postcalendar_events") {
169             $id = "pc_eid";
170         }
171          else if ($table == "lang_languages"){
172             $id = "lang_id";
173          }
174          else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
175             $id = "pc_catid";
176          }
177          else if ($table == "openemr_postcalendar_limits"){
178             $id = "pc_limitid";
179          }
180          else if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
181            $id="acl_id";
182           }
183           else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
184           $id="group_id";
185           }
186            else {
187             $id = "id";
188            }
189         
190          /* Identifying the primary key value for the updated record */
191         while ($offset < $total) {
192             /* There are 4 possible ways that the id=123 can be parsed:
193              * ('id', '=', '123')
194              * ('id=', '123')
195              * ('id=123')
196              * ('id', '=123')
197              */
198             $rid = "";
199            /*id=', '123'*/
200             if (($tokens[$offset] == "$id=") && ($offset + 1 < $total)) {
201                 $rid = $tokens[$offset+1];
202                 break;
203             }
204            /* 'id', '=', '123' */
205             else if ($tokens[$offset] == "$id" && $tokens[$offset+1] == "=" && ($offset+2 < $total)) {
206                 $rid = $tokens[$offset+2];
207                 break;
208              }
209             /*id=123*/
210             else if (strpos($tokens[$offset], "$id=") === 0) {
211                 $tid = substr($tokens[$offset], strlen($id)+1);
212                 if(is_numeric($tid))
213                  $rid=$tid;
214                  break;
215              }
216            /*'id', '=123' */
217              else if($tokens[$offset] == "$id") {
218                 $tid = substr($tokens[$offset+1],1);
219                 if(is_numeric($tid))
220                  $rid=$tid;
221                 break;
222               } 
223             $offset += 1;
224         }//while ($offset < $total)
225     }// else if ($tokens[0] == 'update' || $tokens[0] == 'UPDATE' )
227     if ($table == "" || $rid == "") {
228         return "";
229     }
230    /* Framing sql statements for calculating checksum */ 
231    if ($table == "form_physical_exam") {
232         $sql = "select * from $table where forms_id = $rid";
233     }
234    else if ($table == "claims"){
235                 $sql = "select * from $table where patient_id = $rid";
236         }
237    else if ($table == "openemr_postcalendar_events") {
238             $sql = "select * from $table where pc_eid = $rid";
239         }
240     else if ($table == "lang_languages") {
241             $sql = "select * from $table where lang_id = $rid";
242     }
243     else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
244             $sql = "select * from $table where pc_catid = $rid";
245          }
246     else if ($table == "openemr_postcalendar_limits"){
247            $sql = "select * from $table where pc_limitid = $rid";
248          }
249     else if ($table ==  "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
250            $sql = "select * from $table where acl_id = $rid";
251          }
252      else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
253            $sql = "select * from $table where group_id = $rid";
254       }
255      else {
256         $sql = "select * from $table where id = $rid";
257     }
258     // When this function is working perfectly, can then shift to the
259     // sqlQueryNoLog() function.
260     $results = sqlQueryNoLogIgnoreError($sql);    
261     $column_values = "";
262    /* Concatenating the column values for the row inserted/updated */
263     if (is_array($results)) {
264         foreach ($results as $field_name => $field) {
265             $column_values .= $field;
266         }
267     }
268     // ViCarePlus: As per NIST standard, the encryption algorithm SHA1 is used
270     //error_log("COLUMN_VALUES: ".$column_values,0);
271     return sha1($column_values);
274 /* Create an XML audit record corresponding to RFC 3881.
275  * The parameters passed are the column values (from table 'log')
276  * for a single audit record.
277  */
278 function create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments)
281     /* Event action codes indicate whether the event is read/write.
282      * C = create, R = read, U = update, D = delete, E = execute
283      */
284     $eventActionCode = 'E';
285     if (substr($event, -7) == "-create") {
286         $eventActionCode = 'C';
287     }
288     else if (substr($event, -7) == "-insert") {
289         $eventActionCode = 'C';
290     }
291     else if (substr($event, -7) == "-select") {
292         $eventActionCode = 'R';
293     }
294     else if (substr($event, -7) == "-update") {
295         $eventActionCode = 'U';
296     }
297     else if (substr($event, -7) == "-delete") {
298         $eventActionCode = 'D';
299     }
301     $date_obj = new DateTime();
302     $eventDateTime = $date_obj->format(DATE_ATOM);
304     /* For EventOutcomeIndicator, 0 = success and 4 = minor error */
305     $eventOutcome = ($outcome === 1) ? 0 : 4;
307     /* The choice of event codes is up to OpenEMR.
308      * We're using the same event codes as
309      * https://iheprofiles.projects.openhealthtools.org/
310      */
311     $eventIDcodeSystemName = "DCM";
312     $eventIDcode = 0;
313     $eventIDdisplayName = $event;
315     if (strpos($event, 'patient-record') !== FALSE) {
316         $eventIDcode = 110110;
317         $eventIDdisplayName = 'Patient Record';
318     }
319     else if (strpos($event, 'view') !== FALSE) {
320         $eventIDCode = 110110;
321         $eventIDdisplayName = 'Patient Record';
322     }
323     else if (strpos($event, 'login') !== FALSE) {
324         $eventIDcode = 110122;
325         $eventIDdisplayName = 'Login';
326     }
327     else if (strpos($event, 'logout') !== FALSE) {
328         $eventIDcode = 110123;
329         $eventIDdisplayName = 'Logout';
330     }
331     else if (strpos($event, 'scheduling') !== FALSE) {
332         $eventIDcode = 110111;
333         $eventIDdisplayName = 'Patient Care Assignment';
334     }
335     else if (strpos($event, 'security-administration') !== FALSE) {
336         $eventIDcode = 110129;
337         $eventIDdisplayName = 'Security Administration';
338     }
341    
343     /* Variables used in ActiveParticipant section, which identifies
344      * the IP address and application of the source and destination.
345      */
346     $srcUserID = $_SERVER['SERVER_NAME'] . '|OpenEMR';
347     $srcNetwork = $_SERVER['SERVER_ADDR'];
348     $destUserID = $GLOBALS['atna_audit_host'];
349     $destNetwork = $GLOBALS['atna_audit_host'];
351     $userID = $user;
352     $userTypeCode = 1;
353     $userRole = 6;
354     $userCode = 11;
355     $userDisplayName = 'User Identifier';
357     $patientID = "";
358     $patientTypeCode = "";
359     $patientRole = "";
360     $patientCode = "";
361     $patientDisplayName = "";
363     if ($eventIdDisplayName == 'Patient Record') {
364         $patientID = $patient_id;
365         $pattientTypeCode = 1;
366         $patientRole = 1;
367         $patientCode = 2;
368         $patientDisplayName = 'Patient Number';
369     }
371     /* Construct the XML audit message, and save to $msg */
372     $msg =  '<?xml version="1.0" encoding="ASCII"?>';
373     $msg .= '<AuditMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ';
374     $msg .= 'xsi:noNamespaceSchemaLocation="healthcare-security-audit.xsd">';
376     /* Indicate the event code, text name, read/write type, and date/time */
377     $msg .= "<EventIdentification EventActionCode=\"$eventActionCode\" ";
378     $msg .= "EventDateTime=\"$eventDateTime\" ";
379     $msg .= "EventOutcomeIndicator=\"$eventOutcome\">";
380     $msg .= "<EventID code=\"eventIDcode\" displayName=\"$eventIDdisplayName\" ";
381     $msg .= "codeSystemName=\"DCM\" />";
382     $msg .= "</EventIdentification>";
384     /* Indicate the IP address and application of the source and destination */
385     $msg .= "<ActiveParticipant UserID=\"$srcUserID\" UserIsRequestor=\"true\" ";
386     $msg .= "NetworkAccessPointID=\"$srcNetwork\" NetworkAccessPointTypeCode=\"2\" >";
387     $msg .= "<RoleIDCode code=\"110153\" displayName=\"Source\" codeSystemName=\"DCM\" />";
388     $msg .= "</ActiveParticipant>";
389     $msg .= "<ActiveParticipant UserID=\"$destUserID\" UserIsRequestor=\"false\" ";
390     $msg .= "NetworkAccessPointID=\"$destNetwork\" NetworkAccessPointTypeCode=\"2\" >";
391     $msg .= "<RoleIDCode code=\"110152\" displayName=\"Destination\" codeSystemName=\"DCM\" />";
392     $msg .= "</ActiveParticipant>";
394     $msg .= "<AuditSourceIdentification AuditSourceID=\"$srcUserID\" />";
396     /* Indicate the username who generated this audit record */
397     $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$user\" ";
398     $msg .= "ParticipantObjectTypeCode=\"1\" ";
399     $msg .= "ParticipantObjectTypeCodeRole=\"6\" >";
400     $msg .= "<ParticipantObjectIDTypeCode code=\"11\" ";
401     $msg .= "displayName=\"User Identifier\" ";
402     $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
404     if ($eventIDdisplayName == 'Patient Record' && $patient_id != 0) {
405         $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$patient_id\" ";
406         $msg .= "ParticipantObjectTypeCode=\"1\" ";
407         $msg .= "ParticipantObjectTypeCodeRole=\"1\" >";
408         $msg .= "<ParticipantObjectIDTypeCode code=\"2\" ";
409         $msg .= "displayName=\"Patient Number\" ";
410         $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
411     }
412     $msg .= "</AuditMessage>";
414     /* Add the syslog header */
415     $date_obj = new DateTime($date);
416     $datestr= $date_obj->format(DATE_ATOM);
417     $msg = "<13> " . $datestr . " " . $_SERVER['SERVER_NAME'] . " " . $msg;
418     return $msg;
422 /* Create a TLS (SSLv3) connection to the given host/port.
423  * $localcert is the path to a PEM file with a client certificate and private key.
424  * $cafile is the path to the CA certificate file, for
425  *  authenticating the remote machine's certificate.
426  * If $cafile is "", the remote machine's certificate is not verified.
427  * If $localcert is "", we don't pass a client certificate in the connection.
429  * Return a stream resource that can be used with fwrite(), fread(), etc.
430  * Returns FALSE on error.
431  */
432 function create_tls_conn($host, $port, $localcert, $cafile) {
433     $sslopts = array();
434     if ($cafile !== null && $cafile != "") {
435         $sslopts['cafile'] = $cafile;
436         $sslopts['verify_peer'] = TRUE;
437         $sslopts['verify_depth'] = 10;
438     }
439     if ($localcert !== null && $localcert != "") {
440         $sslopts['local_cert'] = $localcert;
441     }
442     $opts = array('tls' => $sslopts, 'ssl' => $sslopts);
443     $ctx = stream_context_create($opts);
444     $timeout = 60;
445     $flags = STREAM_CLIENT_CONNECT;
447     $olderr = error_reporting(0);
448     $conn = stream_socket_client('tls://' . $host . ":" . $port, $errno, $errstr,
449                                  $timeout, $flags, $ctx);
450     error_reporting($olderr);
451     return $conn;
455 /* This function is used to send audit records to an Audit Repository Server,
456  * as described in the Audit Trail and Node Authentication (ATNA) standard.
457  * Given the fields in a single audit record:
458  * - Create an XML audit message according to RFC 3881, including the RFC5425 syslog header.
459  * - Create a TLS connection that performs bi-directions certificate authentication,
460  *   according to RFC 5425.
461  * - Send the XML message on the TLS connection.
462  */
463 function send_atna_audit_msg($user, $group, $event, $patient_id, $outcome, $comments)
465     /* If no ATNA repository server is configured, return */
466     if ($GLOBALS['atna_audit_host'] === null || $GLOBALS['atna_audit_host'] == "" || !($GLOBALS['enable_atna_audit'])) {
467         return;
468     }
469     $host = $GLOBALS['atna_audit_host'];
470     $port = $GLOBALS['atna_audit_port'];
471     $localcert = $GLOBALS['atna_audit_localcert'];
472     $cacert = $GLOBALS['atna_audit_cacert'];
473     $conn = create_tls_conn($host, $port, $localcert, $cacert);
474     if ($conn !== FALSE) {
475         $msg = create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments);
476         $len = strlen($msg);
477         fwrite($conn, $msg);
478         fclose($conn);
479     }
483 /* Add an entry into the audit log table, indicating that an
484  * SQL query was performed. $outcome is true if the statement
485  * successfully completed.  Determine the event type based on
486  * the tables present in the SQL query.
487  */
488 function auditSQLEvent($statement, $outcome, $binds=NULL)
491     $user =  isset($_SESSION['authUser']) ? $_SESSION['authUser'] : "";
492         /* Don't log anything if the audit logging is not enabled. Exception for "emergency" users */
493    if (!isset($GLOBALS['enable_auditlog']) || !($GLOBALS['enable_auditlog']))
494    {
495             if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
496             return;
497    }
500    $statement = trim($statement);
502     /* Don't audit SQL statements done to the audit log,
503      * or we'll have an infinite loop.
504      */
505     if ((stripos($statement, "insert into log") !== FALSE) ||
506         (stripos($statement, "FROM log ") !== FALSE) ) {
507         return;
508     }
510     $group = isset($_SESSION['authGroup']) ?  $_SESSION['authGroup'] : "";
511     $comments = $statement;
513     $processed_binds = "";
514     if (is_array($binds)) {
515         // Need to include the binded variable elements in the logging
516         $first_loop=true;
517         foreach ($binds as $value_bind) {
518             if ($first_loop) {
519                 //no comma
520                 $processed_binds .= "'" . add_escape_custom($value_bind) . "'";
521                 $first_loop=false;
522             }
523             else {
524                 //add a comma
525                 $processed_binds .= ",'" . add_escape_custom($value_bind) . "'";
526             }
527         }
528         if (!empty($processed_binds)) {
529             $processed_binds = "(" . $processed_binds . ")";
530             $comments .= " " . $processed_binds;
531         }
532     }
534     $success = 1;
535     $checksum = "";
536     if ($outcome === FALSE) {
537         $success = 0;
538     }
539     if ($outcome !== FALSE) {
540         // Should use the $statement rather than the processed
541         // variables, which includes the binded stuff. If do
542         // indeed need the binded values, then will need
543         // to include this as a separate array.
545         //error_log("STATEMENT: ".$statement,0);
546         //error_log("BINDS: ".$processed_binds,0);
547         $checksum = sql_checksum_of_modified_row($statement);
548         //error_log("CHECKSUM: ".$checksum,0);
549     }
550     /* Determine the query type (select, update, insert, delete) */
551     $querytype = "select";
552     $querytypes = array("select", "update", "insert", "delete","replace");
553     foreach ($querytypes as $qtype) {
554         if (stripos($statement, $qtype) === 0) {
555             $querytype = $qtype;
556         }
557     }
559     /* Determine the audit event based on the database tables */
560     $event = "other";
561     $tables = array("billing" => "patient-record",
562                     "claims" => "patient-record",
563                     "employer_data" => "patient-record",
564                     "forms" => "patient-record",
565                     "form_encounter" => "patient-record",
566                     "form_dictation" => "patient-record",
567                     "form_misc_billing_options" => "patient-record",
568                     "form_reviewofs" => "patient-record",
569                     "form_ros" => "patient-record",
570                     "form_soap" => "patient-record",
571                     "form_vitals" => "patient-record",
572                     "history_data" => "patient-record",
573                     "immunizations" => "patient-record",
574                     "insurance_data" => "patient-record",
575                     "issue_encounter" => "patient-record",
576                     "lists" => "patient-record",
577                     "patient_data" => "patient-record",
578                     "payments" => "patient-record",
579                     "pnotes" => "patient-record",
580                     "onotes" => "patient-record",
581                     "prescriptions" => "order",
582                     "transactions" => "patient-record",
583                     "facility" => "security-administration",
584                     "pharmacies" => "security-administration",
585                     "addresses" => "security-administration",
586                     "phone_numbers" => "security-administration",
587                     "x12_partners" => "security-administration",
588                     "insurance_companies" => "security-administration",
589                     "codes" => "security-administration",
590                     "registry" => "security-administration", 
591                     "users" => "security-administration",
592                     "groups" => "security-administration",
593                     "openemr_postcalendar_events" => "scheduling",
594                                 "openemr_postcalendar_categories" => "security-administration",
595                                 "openemr_postcalendar_limits" => "security-administration",
596                                 "openemr_postcalendar_topics" => "security-administration",
597                                 "gacl_acl" => "security-administration",
598                                 "gacl_acl_sections" => "security-administration",
599                                 "gacl_acl_seq" => "security-administration",
600                                 "gacl_aco" => "security-administration",
601                                 "gacl_aco_map" => "security-administration",
602                                 "gacl_aco_sections" => "security-administration",
603                                 "gacl_aco_sections_seq" => "security-administration",
604                                 "gacl_aco_seq" => "security-administration",
605                                 "gacl_aro" => "security-administration",
606                                 "gacl_aro_groups" => "security-administration",                                 
607                                 "gacl_aro_groups_id_seq" => "security-administration",
608                                 "gacl_aro_groups_map" => "security-administration",
609                                 "gacl_aro_map" => "security-administration",
610                                 "gacl_aro_sections" => "security-administration",
611                                 "gacl_aro_sections_seq" => "security-administration",
612                                 "gacl_aro_seq" => "security-administration",
613                                 "gacl_axo" => "security-administration",
614                                 "gacl_axo_groups" => "security-administration",
615                                 "gacl_axo_groups_map" => "security-administration",
616                                 "gacl_axo_map" => "security-administration",
617                                 "gacl_axo_sections" => "security-administration",
618                                 "gacl_groups_aro_map" => "security-administration",
619                                 "gacl_groups_axo_map" => "security-administration",
620                                 "gacl_phpgacl" => "security-administration"                             
621                   );
623     /* When searching for table names, truncate the SQL statement,
624      * removing any WHERE, SET, or VALUE clauses.
625      */
626         $truncated_sql = $statement;
627         $truncated_sql = str_replace("\n", " ", $truncated_sql);
628         if ($querytype == "select") {
629         $startwhere = stripos($truncated_sql, " where ");
630         if ($startwhere > 0) {
631         $truncated_sql = substr($truncated_sql, 0, $startwhere);
632     }
634         else {
635      $startparen = stripos($truncated_sql, "(" );
636      $startset = stripos($truncated_sql, " set ");
637      $startvalues = stripos($truncated_sql, " values ");
639         if ($startparen > 0) {
640             $truncated_sql = substr($truncated_sql, 0, $startparen);
641         }
642         if ($startvalues > 0) {
643             $truncated_sql = substr($truncated_sql, 0, $startvalues);
644         }
645         if ($startset > 0) {
646             $truncated_sql = substr($truncated_sql, 0, $startset);
647         }
648     }
649     foreach ($tables as $table => $value) {
650         if (strpos($truncated_sql, $table) !== FALSE) {
651             $event = $value;
652              break;
653         }
654       else if (strpos($truncated_sql, "form_") !== FALSE) {
655             $event = "patient-record";
656              break;
657         }
658     }
660     /* Avoid filling the audit log with trivial SELECT statements.
661      * Skip SELECTs from unknown tables.  
662      * Skip SELECT count() statements.
663      * Skip the SELECT made by the authCheckSession() function.
664      */
665     if ($querytype == "select") {
666         if ($event == "other")
667             return;
668         if (stripos($statement, "SELECT count(" ) === 0)
669             return;
670         if (stripos($statement, "select username, password from users") === 0)
671             return;
672     }
675     /* If the event is a patient-record, then note the patient id */
676     $pid = 0;
677     if ($event == "patient-record") {
678         if (array_key_exists('pid', $_SESSION) && $_SESSION['pid'] != '') {
679             $pid = $_SESSION['pid'];
680         }
681     }
683     /* If query events are not enabled, don't log them */
684     if (($querytype == "select") && !($GLOBALS['audit_events_query']))
685     { 
686        if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
687        return;
688     }
690     if (!($GLOBALS["audit_events_${event}"])) 
691     {
692         if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
693         return;
694     }
695            
697     $event = $event . "-" . $querytype;
699     $adodb = $GLOBALS['adodb']['db'];
700    
701     // ViSolve : Don't log sequences - to avoid the affect due to GenID calls
702     if (strpos($comments, "sequences") !== FALSE) return;
704     $sql = "insert into log (date, event, user, groupname, comments, patient_id, success, checksum,crt_user) " .
705          "values ( NOW(), " . 
706          $adodb->qstr($event) . ", " .
707          $adodb->qstr($user) . "," . 
708          $adodb->qstr($group) . "," .
709          $adodb->qstr($comments) . "," .
710          $adodb->qstr($pid) . "," .
711          $adodb->qstr($success) . "," . 
712          $adodb->qstr($checksum) . "," .
713          $adodb->qstr($_SERVER['SSL_CLIENT_S_DN_CN']) .")";
715     sqlInsertClean_audit($sql);
716     send_atna_audit_msg($user, $group, $event, $pid, $success, $comments);
717     //return $ret;
720  * Record the patient disclosures.
721  * @param $dates    - The date when the disclosures are sent to the thrid party.
722  * @param $event    - The type of the disclosure.
723  * @param $pid      - The id of the patient for whom the disclosures are recorded.
724  * @param $comment  - The recipient name and description of the disclosure.
725  * @uname           - The username who is recording the disclosure.
726  */
727 function recordDisclosure($dates,$event,$pid,$recipient,$description,$user)
729         $adodb = $GLOBALS['adodb']['db'];
730         $crt_user= $_SERVER['SSL_CLIENT_S_DN_CN'];
731         $groupname=$_SESSION['authProvider'];
732         $success=1;
733         $sql = "insert into extended_log ( date, event, user, recipient, patient_id, description) " .
734             "values (" . $adodb->qstr($dates) . "," . $adodb->qstr($event) . "," . $adodb->qstr($user) .
735             "," . $adodb->qstr($recipient) . ",".
736             $adodb->qstr($pid) ."," .
737             $adodb->qstr($description) .")";
738         $ret = sqlInsertClean_audit($sql);
741  * Edit the disclosures that is recorded.
742  * @param $dates  - The date when the disclosures are sent to the thrid party.
743  * @param $event  - The type of the disclosure.
744  * param $comment - The recipient and the description of the disclosure are appended.
745  * $logeventid    - The id of the record which is to be edited.
746  */
747 function updateRecordedDisclosure($dates,$event,$recipient,$description,$disclosure_id)
749          $adodb = $GLOBALS['adodb']['db'];
750          $sql="update extended_log set
751                 event=" . $adodb->qstr($event) . ",
752                 date=" .  $adodb->qstr($dates) . ",
753                 recipient=" . $adodb->qstr($recipient) . ",
754                 description=" . $adodb->qstr($description) . "
755                 where id=" . $adodb->qstr($disclosure_id) . "";
756           $ret = sqlInsertClean_audit($sql);
759  * Delete the disclosures that is recorded.
760  * $deleteid - The id of the record which is to be deleted.
761  */
762 function deleteDisclosure($deletelid)
764         $sql="delete from extended_log where id='$deletelid'";
765         $ret = sqlInsertClean_audit($sql);