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