Space to separate "&&" so that all "&$var" can be made into "$var" later
[openemr.git] / library / direct_message_check.inc
blobc2a93cca9f2c37970fa9a20af7ce93b39ddd2b25
1 <?php
2 /**
3  * Background receive function for phiMail Direct Messaging service.
4  *
5  * This script is called by the background service manager
6  * at /library/ajax/execute_background_services.php
7  *
8  * Copyright (C) 2013 EMR Direct <http://www.emrdirect.com/>
9  *
10  * LICENSE: This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
20  *
21  * @package OpenEMR
22  * @author  EMR Direct <http://www.emrdirect.com/>
23  * @link    http://www.open-emr.org
24  */
26 require_once(dirname(__FILE__) . "/log.inc");
27 require_once(dirname(__FILE__) . "/sql.inc");
28 require_once(dirname(__FILE__) . "/pnotes.inc");
29 require_once(dirname(__FILE__) . "/../controllers/C_Document.class.php");
31 /**
32  * Connect to a phiMail Direct Messaging server and check for any incoming status
33  * messages related to previously transmitted messages or any new messages received.
34  */
36 function phimail_check() {
37    if ($GLOBALS['phimail_enable']==false) return; //for safety
39    $phimail_server=@parse_url($GLOBALS['phimail_server_address']);
40    $phimail_username=$GLOBALS['phimail_username'];
41    $phimail_password=$GLOBALS['phimail_password'];
42    switch ($phimail_server['scheme']) {
43        case "http": $server="tcp://".$phimail_server['host'];
44                break;
45        case "https": $server="ssl://".$phimail_server['host'];
46                break;
47        default: return;
48    }
49    $fp=@fsockopen($server,$phimail_server['port']);
50    if ($fp===false) {
51       phimail_logit(0,"could not connect to server");
52       return;
53    }
55    $ret = phimail_write_expect_OK($fp,"AUTH $phimail_username $phimail_password\n");
56    if($ret!==TRUE) return; //authentication error
58    while (1) {
59       phimail_write($fp,"CHECK\n");
60       $ret=fgets($fp,512);
62       if($ret=="NONE\n") { //nothing to process
63           phimail_close($fp);
64           phimail_logit(1,"message check completed");
65           return;
66       }
67       else if(substr($ret,0,6)=="STATUS") {
68           //Format STATUS message-id status-code [additional-information]
69           $val=explode(" ",trim($ret),4);
70           $sql='SELECT * from direct_message_log WHERE msg_id = ?';
71           $res = sqlStatementNoLog($sql,array($val[1]));
72           if ($res===FALSE) { //database problem
73              phimail_close($fp);
74              phimail_logit(0,"database problem");
75              return;
76           }
77           if (($msg=sqlFetchArray($res))===FALSE) {
78              //no match, so log it and move on (should never happen)
79              phimail_logit(0,"NO MATCH: ".$ret);
80              $ret = phimail_write_expect_OK($fp,"OK\n"); 
81              if($ret!==TRUE) return; else continue; 
82           }
84           //if we get here, $msg contains the matching outgoing message record
85           if($val[2]=='failed') {
86              $success=0;
87              $status='F';
88           } else if ($val[2]=='dispatched') {
89              $success=1;
90              $status='D';
91           } else {
92              //unrecognized status, log it and move on (should never happen)
93              $ret = "UNKNOWN STATUS: ".$ret;
94              $success=0;
95              $status='U';
96           }
98           phimail_logit($success,$ret,$msg['patient_id']);
100           if (!isset($val[3])) $val[3]="";
101           $sql = "UPDATE direct_message_log SET status=?, status_ts=NOW(), status_info=? WHERE msg_type='S' AND msg_id=?";
102           $res = sqlStatementNoLog($sql,array($status,$val[3],$val[1]));
103           if ($res===FALSE) { //database problem
104              phimail_close($fp);
105              phimail_logit(0,"database problem updating: ".$val[1]);
106              return;
107           }
109           if(!$success) {
110              phimail_notify( xl('Direct Messaging Send Failure.'), $ret);
111           }
113           //done with this status message
114           $ret = phimail_write_expect_OK($fp,"OK\n");
115           if($ret!==TRUE) {
116              phimail_close($fp);
117              return;
118           } 
120       }
122       else if(substr($ret,0,4)=="MAIL") {
124          $val = explode(" ",trim($ret),5); // MAIL recipient sender #attachments msg-id
125          $recipient=$val[1];
126          $sender=$val[2];
127          $att=(int)$val[3];
128          $msg_id=$val[4];
130          //request main message
131          $ret2 = phimail_write_expect_OK($fp,"SHOW 0\n");
132          if($ret2!==TRUE) {
133             phimail_close($fp);
134             return;
135          }
137          //get message headers
138          $hdrs="";
139          while (($next_hdr = fgets($fp,1024)) != "\n") 
140             $hdrs .= $next_hdr;
142          $mime_type=fgets($fp,512);
143          $mime_info=explode(";",$mime_type);
144          $mime_type_main=strtolower($mime_info[0]);
146          //get main message body
147          $body_len=fgets($fp,256);
148          $body=phimail_read_blob($fp,$body_len);
149          if ($body===FALSE) {
150            phimail_close($fp);
151            return;
152          }
154          $att2=fgets($fp,256);
155          if($att2!=$att) { //safety for mismatch on attachments
156             phimail_close($fp);
157             return;
158          }
160          //get attachment info
161          if($att>0) {
162             for ($attnum=0;$attnum<$att;$attnum++) {
163                 if(  ($attinfo[$attnum]['name']=fgets($fp,1024)) === FALSE
164                   || ($attinfo[$attnum]['mime']=fgets($fp,1024)) === FALSE
165                   || ($attinfo[$attnum]['desc']=fgets($fp,1024)) === FALSE) {
166                      phimail_close($fp);
167                      return;
168                 }
169              }
170           }
172           //main part gets stored as document if not plain text content
173           //(if plain text it will be the body of the final pnote)
174           $doc_id=0;
175           $att_detail="";
176           if ($mime_type_main != "text/plain") {
177              $name = uniqid("dm-message-") . phimail_extension($mime_type_main);
178              $doc_id = phimail_store($name,$mime_type_main,$body);
179              if (!$doc_id) { 
180                 phimail_close($fp);
181                 return;
182              }
183              $idnum=$doc_id['doc_id']; 
184              $url=$doc_id['url']; 
185              $url=substr($url,strrpos($url,"/")+1);
186              $att_detail = "\n" . xl ("Document") . " $idnum (\"$url\"; $mime_type_main; " . 
187                 filesize($body) . " bytes) Main message body";
188           }
190           //download and store attachments
191           for($attnum=0;$attnum<$att;$attnum++) {
192              $ret2 = phimail_write_expect_OK($fp,"SHOW " . ($attnum+1) . "\n");
193              if ($ret2!==TRUE) {
194                 phimail_close($fp);
195                 return;
196              }
198              //we can ignore next two lines (repeat of name and mime-type)
199              if ( ($a1=fgets($fp,512))===FALSE || ($a2=fgets($fp,512))===FALSE ) {
200                 phimail_close($fp);
201                 return;
202              }
204              $att_len = fgets($fp,256); //length of file
205              $attdata = phimail_read_blob($fp,$att_len);
206              if ($attdata===FALSE) {
207                 phimail_close($fp);
208                 return;
209              }
210              $attinfo[$attnum]['file']=$attdata;
212              $req_name = trim($attinfo[$attnum]['name']);
213              $req_name = (empty($req_name) ? $attdata : "dm-") . $req_name;
214              $attinfo[$attnum]['mime'] = explode(";",trim($attinfo[$attnum]['mime']));
215              $attmime = strtolower($attinfo[$attnum]['mime'][0]);
216              $att_doc_id = phimail_store($req_name, $attmime, $attdata);
217              if (!$att_doc_id) { 
218                 phimail_close($fp);
219                 return;
220              }
221              $attinfo[$attnum]['doc_id']=$att_doc_id;
222              $idnum=$att_doc_id['doc_id']; 
223              $url=$att_doc_id['url']; 
224              $url=substr($url,strrpos($url,"/")+1);
225              $att_detail = $att_detail . "\n" . xl ("Document") . " $idnum (\"$url\"; $attmime; " . 
226                  filesize($attdata) . " bytes) " . trim($attinfo[$attnum]['desc']);
227          }
229          if ($att_detail != "") 
230            $att_detail = "\n\n" . xl("The following documents were attached to this Direct message:") . $att_detail;
232          $ret2 = phimail_write_expect_OK($fp,"DONE\n"); //we'll check for failure after logging.
234          //logging only after succesful download, storage, and acknowledgement of message
235          $sql = "INSERT INTO direct_message_log (msg_type,msg_id,sender,recipient,status,status_ts,user_id) " .
236             "VALUES ('R', ?, ?, ?, 'R', NOW(), ?)";
237          $res = sqlStatementNoLog($sql,array($msg_id,$sender,$recipient,phimail_service_userID()));
239          phimail_logit(1,$ret);
241          if(!($notifyUsername = $GLOBALS['phimail_notify'])) $notifyUsername='admin'; //fallback
242          
243          //alert appointed user about new message
244          switch($mime_type_main) {
246            case "text/plain":
247              $body_text = @file_get_contents($body); //this was not uploaded as a document
248              unlink($body);
249              $pnote_id = addPnote(0, xl("Direct Message Received.") . "\n$hdrs\n$body_text$att_detail",
250                0, 1, "Unassigned", $notifyUsername, "", "New", "phimail-service");
251              break;
253            default:
254              $note = xl("Direct Message Received.") . "\n$hdrs\n"
255                 . xl("Message content is not plain text so it has been stored as a document.") . $att_detail;
256              $pnote_id = addPnote(0, $note, 0, 1, "Unassigned", $notifyUsername, "", "New", "phimail-service");
257              break;
259          }
261          if ($ret2!==TRUE) { 
262             phimail_close();
263             return; 
264          }
266       }
267       else { //unrecognized or FAIL response
268           phimail_close($fp);
269           return;
270       } 
271    }
275  * Helper functions
276  */
277 function phimail_write($fp,$text) {
278    fwrite($fp,$text);
279    fflush($fp);
282 function phimail_write_expect_OK($fp,$text) {
283    phimail_write($fp,$text);
284    $ret = fgets($fp,256);
285    if($ret!="OK\n") { //unexpected error
286       phimail_close($fp);
287       return $ret;
288    }
289    return TRUE;
292 function phimail_close($fp) {
293    fwrite($fp,"BYE\n");
294    fflush($fp);
295    fclose($fp);
298 function phimail_logit($success,$text,$pid=0,$event="direct-message-check") {
299    newEvent($event,"phimail-service",0,$success,$text,$pid);
303  * Read a blob of data into a local temporary file
304  * @param $len number of bytes to read
305  * @return the temp filename, or FALSE if failure
306  */
307 function phimail_read_blob($fp,$len) {
309    $fpath=$GLOBALS['temporary_files_dir'];
310    if(!@file_exists($fpath)) {
311      return FALSE;
312    }
313    $name = uniqid("direct-");
314    $fn = $fpath . "/" . $name . ".dat";
315    $dup = 1;
316    while(file_exists($fn)) {
317      $fn = $fpath . "/" . $name . "." . $dup++ . ".dat";
318    }
320    $ff = @fopen ($fn, "w");
321    if(!$ff) return FALSE;
323    $bytes_left=$len;
324    $chunk_size=1024;
325    while (!feof($fp) && $bytes_left>0) {
326       if ($bytes_left < $chunk_size ) $chunk_size = $bytes_left;
327       $chunk = fread($fp,$chunk_size);
328       if($chunk===FALSE || @fwrite($ff,$chunk)===FALSE) {
329          @fclose($ff);
330          @unlink($fn);
331          return FALSE;
332       }
333       $bytes_left -= strlen($chunk);
334    }
335    @fclose($ff);
336    return($fn);
340  * Return a suitable filename extension based on MIME-type
341  * (very limited, default is .dat)
342  */
343 function phimail_extension($mime) {
344   $m=explode("/",$mime);
345   switch($mime) {
346         case 'text/plain': 
347                 return (".txt");
348         default:
349   }
350   switch($m[1]) {
351         case 'html':
352         case 'xml':
353         case 'pdf':
354                 return (".".$m[1]);
355         default:
356                 return (".dat");
357   }
360 function phimail_service_userID($name='phimail-service') {
361    $sql = "SELECT id FROM users WHERE username=?";
362    if (($r = sqlStatementNoLog($sql,array($name))) === FALSE ||
363        ($u = sqlFetchArray($r)) === FALSE) {
364       $user=1; //default if we don't have a service user
365    } else {
366       $user = $u['id'];
367    }
368    return ($user);
373  * Registers an attachment or non-text message file using the existing Document structure
374  * @return Array(doc_id,URL) of the file as stored in documents table, false = failure
375  */
376 function phimail_store($name,$mime_type,$fn) {
378    //upload using existing controller framework
379    $_FILES['file']['name'][0]=$name;
380    $_FILES['file']['type'][0]=$mime_type;
381    $_FILES['file']['tmp_name'][0]=$fn;
382    $_FILES['file']['error'][0]=0;
383    $_FILES['file']['size'][0]=filesize($fn);
384    $_GET['patient_id']='direct'; //new documents get put into in [fileroot]/sites/[site]/documents/direct/
385    $_POST['destination']='';
386    $_POST['submit']='Upload';
387    $_POST['patient_id']='00'; //workaround because '0' becomes a Null and not a zero in documents.foreign_id
388    $_POST['category_id']='1'; //since we don't know what kind of document; these show up in root Documents folder
389    $_POST['process']='true';
391    $user = phimail_service_userID();
392    
393    $cd = new C_Document();
394    $cd->upload_action_process($user); 
395    @unlink($fn);
396    $v = $cd->get_template_vars("file");
397    if (!isset($v) || !$v) return false;
398    return array ("doc_id" => $v[0]->id, "url" => $v[0]->url);
402  * Send an error notification or other alert to the notification address specified in globals.
403  * (notification email function modified from interface/drugs/dispense_drug.php)
404  * @return true if notificaiton successfully sent, false otherwise
405  */
406 function phimail_notify($subj,$body) {
407   $recipient = $GLOBALS['practice_return_email_path'];
408   if (empty($recipient)) return false;
409   $mail = new PHPMailer();
410   $mail->SetLanguage("en", $GLOBALS['fileroot'] . "/library/" );
411   $mail->From = $recipient;
412   $mail->FromName = 'phiMail Gateway';
413   $mail->isMail();
414   $mail->Host = "localhost";
415   $mail->Mailer = "mail";
416   $mail->Body = $body;
417   $mail->Subject = $subject;
418   $mail->AddAddress($recipient);
419   return ($mail->Send());