Feat openemr fix 7480 7494 email prescription (#7495)
[openemr.git] / library / edihistory / test_edih_sftp_files.php
blobaa1dcf74cbf186f5c7618d4ac05fc78be2204594
1 <?php
3 /* ********** flow pattern for using sftp with edi_history
5 * 1. database query for "x12 Partners"
6 * -- get the partner name
7 * -- populate a "select" list with names
8 * -- "submit" runs script which gets url, password, etc and the sftp
9 * transfer for selected x12 partner
10 * -- put downloaded files in a designated directory e.g. history/sftp/
12 * 2 (alt) create php script to use most of edih_uploads.php function edih_upload_files()
13 * -- but skip the $_FILES array rewrite and testing, just test, match type and store
14 * -- maybe call it edih_sftp_upload()
16 * 3. add if stanza to edih_main.php in section:
17 * if (strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
18 * -- calls edih_io.php function edih_disp_sftp_upload()
19 * -- remove the files from the download directory
20 * (alt) -- this is where you would call maybe edih_sftp_upload()
22 * 4. At this point the sftp files will be in the edi/history/[type] directories
23 * and the "Process" button will run the script to parse the files and create csv table rows
27 // comment out below exit when need to use this script
28 exit;
30 /* ** add this function to edih_uploads.php
31 * -- or work it into edih_upload_files(), since it is almost a direct copy
34 function edih_upload_sftp()
37 $html_str = '';
39 $sftp_dir = 'path/to/dir';
40 // if ($_FILES) ) {
41 //csv_edihist_log('Error: upload files indicated, but none received.');
42 //return false;
43 //} elseif
44 if (is_dir($sftp_dir) && is_readable($sftp_dir)) {
45 $sftp_dir = realpath($sftp_dir);
46 $fn_ar = scandir($sftp_dir);
47 } else {
48 $html_str = 'unable to read the directory for sftp file uploads <br />';
49 csv_edihist_log('unable to read the directory for sftp file uploads');
50 return $html_str;
54 $m_types = array('application/octet-stream', 'text/plain', 'application/zip', 'application/x-zip-compressed');
56 // some unwanted file extensions that might be accidentally included in upload files
57 $ext_types = 'sh|asp|html|htm|cm|js|xml|jpg|png|tif|xpm|pdf|php|py|pl|tcl|doc|pub|ppt|xls|xla|vsd|rtf|odt|ods|odp';
58 // we get the parameters here to send to ibr_upload_match_file()
59 $param_ar = csv_parameters();
60 //if ( class_exists('finfo') )
61 foreach ($fn_ar as $idx => $fn) {
62 $fa = array();
63 $fa['tmp_name'] = tempnam($sftp_dir . DS . $fn, 'x12_');
64 $fa['name'] = $sftp_dir . DS . $fn;
65 $fa['type'] = mime_content_type($sftp_dir . DS . $fn);
66 $fa['size'] = filesize($sftp_dir . DS . $fn);
67 // now do verifications
68 if (!in_array($fa['type'], $m_types)) {
69 //$html_str .= "Error: mime-type {$fa['type']} not accepted for {$fa['name']} <br />" . PHP_EOL;
70 $f_ar['reject'][] = array('name' => $fa['name'],'comment' => 'mime-type ' . $fa['type']);
71 csv_edihist_log('edih_upload_sftp: error mime-type ' . $fa['name'] . ' mime-type ' . $fa['type']);
72 unset($fn_ar[$idx]);
73 continue;
76 // verify that we have a usable name
77 $fext = (strpos($fa['name'], '.')) ? pathinfo($fa['name'], PATHINFO_EXTENSION) : '';
78 if ($fext && preg_match('/' . $ext_types . '\?/i', $fext)) {
79 //$html_str .= 'Error: uploaded_file error for '.$fa['name'].' extension '.$fext.'<br />'. PHP_EOL;
80 $f_ar['reject'][] = array('name' => $fa['name'],'comment' => 'extension ' . $fext);
81 csv_edihist_log('edih_upload_sftp: _FILES error name ' . $fa['name'] . ' extension ' . $fext);
82 unset($fn_ar[$idx]);
83 continue;
86 if (is_string($fa['name'])) {
87 // check for null byte in file name, linux hidden file, directory
88 if (strpos($fa['name'], '.') === 0 || strpos($fa['name'], "\0") !== false || strpos($fa['name'], "./") !== false) {
89 //$html_str .= "Error: uploaded_file error for " . $fa['name'] . "<br />". PHP_EOL;
90 $fname = preg_replace("/[^a-zA-Z0-9_.-]/", "_", $fa['name']);
91 $f_ar['reject'][] = array('name' => $fname,'comment' => 'null byte, hidden, invalid');
92 csv_edihist_log('edih_upload_sftp: null byte, hidden, invalid ' . $fname);
93 unset($fn_ar[$idx]);
94 continue;
97 // replace spaces in file names -- should not happen, but response files from payers might have spaces
98 // $fname = preg_replace("/[^a-zA-Z0-9_.-]/","_",$fname);
99 $fa['name'] = str_replace(' ', '_', $fa['name']);
100 } else {
101 // name is not a string
102 //$html_str .= "Error: uploaded_file error for " . $fa['tmp_name'] . "<br />". PHP_EOL;
103 $f_ar['reject'][] = array('name' => (string)$fa['name'],'comment' => 'invalid name');
104 unset($fn_ar[$idx]);
105 continue;
108 if (!$fa['tmp_name'] || !$fa['size']) {
109 //$html_str .= "Error: file name or size error <br />" . PHP_EOL;
110 $f_ar['reject'][] = array('name' => (string)$fa['name'],'comment' => 'php file upload error');
111 unset($files[$uplkey][$idx]);
112 continue;
116 if (strpos(strtolower($fa['name']), '.zip') || strpos($fa['type'], 'zip')) {
118 $f_upl = edih_ziptoarray($fa['tmp_name'], $param_ar, false);
120 // put them in the correct type array
121 if (is_array($f_upl) && count($f_upl)) {
122 foreach ($f_upl as $tp => $fz) {
123 if ($tp == 'reject') {
124 if (isset($f_ar['reject']) && is_array($fz)) {
125 array_merge($f_ar['reject'], $fz);
126 } else {
127 $f_ar['reject'] = (is_array($fz)) ? $fz : array();
129 } else {
130 // expect $fz to be an array of file names
131 foreach ($fz as $zf) {
132 $f_ar[$tp][] = $zf;
133 $p_ct++;
137 } else {
138 // nothing good from edih_ziptoarray()
139 // $html_str .= "error with zip file or no files accepted for " . $fa['name'] . "<br />" .PHP_EOL;
140 $f_ar['reject'][] = array('name' => $fa['name'],'comment' => 'error with zip archive');
141 unset($files[$uplkey][$idx]);
144 // continue, since we have done everything that would happen below
145 continue;
149 $f_upl = edih_upload_match_file($param_ar, $fa);
151 if (is_array($f_upl) && count($f_upl) > 0) {
152 $f_ar[$f_upl['type']][] = $f_upl['name'];
153 $p_ct++;
154 } else {
155 // verification failed
156 csv_edihist_log('edih_upload_file: verification failed for ' . $fa['name']);
157 $f_ar['reject'][] = array('name' => $fa['name'], 'comment' => 'verification failed');
158 unset($fn_ar[$idx]);
160 } // end foreach($files[$uplkey] as $idx=>$fa)
162 $f_ar['remark'][] = "Received $f_ct files, accepted $p_ct" . PHP_EOL;
163 return $f_ar;
166 /* ** add this function to edih_io.php */
167 function edih_disp_sftp_upload()
169 // sftp file upload
170 // imaginary form and POST values
171 $str_html = '';
173 if (isset($_POST['post_sftp'])) {
174 $la = (isset($_POST['post_sftp'])) ? filter_input(INPUT_POST, 'post_sftp', FILTER_DEFAULT) : '';
175 $x12ptnr = (isset($_POST['sftp_select'])) ? filter_input(INPUT_POST, 'sftp_select', FILTER_DEFAULT) : '';
177 if (($la == 'get_sftp') && $x12ptnr) {
178 // yet to be written -- gets x12 partner info and does sftp download
179 $is_sftp = edih_sftp_connect($x12ptnr);
181 $f_array = ($is_sftp) ? edih_upload_sftp() : false;
182 if (is_array($f_array) && count($f_array)) {
183 $str_html .= edih_sort_upload($f_array);
184 } else {
185 $str_html .= "sftp connection did not get any files <br />" . PHP_EOL;
187 } else {
188 $str_html .= "sftp file transfer invalid input <br />" . PHP_EOL;
192 return $str_html;
195 /* ** edih_view.php needs a form to give user control over sftp uploads */
196 // a select list of x12 partners and submit button and process button
197 // append output of edih_disp_sftp_upload() to displayed file list
200 * Unattended SFTP host operations using phpseclib.
202 * Copyright (C) 2014 MD Support <mdsupport@users.sourceforge.net>
204 * LICENSE: This program is free software; you can redistribute it and/or
205 * modify it under the terms of the GNU General Public License
206 * as published by the Free Software Foundation; either version 2
207 * of the License, or (at your option) any later version.
208 * This program is distributed in the hope that it will be useful,
209 * but WITHOUT ANY WARRANTY; without even the implied warranty of
210 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
211 * GNU General Public License for more details.
212 * You should have received a copy of the GNU General Public License
213 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
215 * @package OpenEMR
216 * @author MD Support <mdsupport@users.sourceforge.net>
218 * Following parameters may be provided :
219 * 1. actn - get or put
220 * 2. Interface with Procedure providers : ppid
221 * 3. Single host definition : host, port, user, pass, fdir, pdir
222 * 4. ldir - local directory
223 * 5. sub - 'ppid'/'host'/'fdir'/'pdir' to be used as subdir of ldir
227 if (php_sapi_name() == 'cli') {
228 parse_str(implode('&', array_slice($argv, 1)), $_GET);
229 $_SERVER['REQUEST_URI'] = $_SERVER['PHP_SELF'];
230 $_SERVER['SERVER_NAME'] = 'localhost';
231 $backpic = "";
232 $ignoreAuth = 1;
233 // Since from command line, set $sessionAllowWrite since need to set site_id session and no benefit to set to false
234 $sessionAllowWrite = true;
237 $get_count = extract($_GET, EXTR_OVERWRITE);
238 // Following breaks link to OpenEMR structure dependency - assumes phpseclib is subdir
239 $script_dir = dirname(__FILE__);
240 ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . "$script_dir/phpseclib");
241 require_once("$script_dir/phpseclib/Net/SFTP.php");
242 function get_openemr_globals($libdir)
244 if (!isset($site)) {
245 $_GET['site'] = 'default';
248 require_once("$libdir/../interface/globals.php");
250 function sftp_status($msg, $val)
252 if (php_sapi_name() == 'cli') {
253 fwrite(STDOUT, xl($msg) . ' ' . $val . PHP_EOL);
256 $exitmsgs = array (
257 0 => ''
258 ,1 => 'Missing/Invalid parameter(s) - SFTP host definition, actn, ldir'
259 ,2 => 'File Transfer error(s)'
261 // OpenEMR specific mappings. $sub works with procedure_providers fields
262 $submap = array(
263 'ppid' => 'ppid'
264 ,'host' => 'remote_host'
265 ,'fdir' => 'results_path'
266 ,'pdir' => 'orders_path'
268 $pathmap = array(
269 'get' => 'results_path'
270 ,'put' => 'orders_path'
272 $exitcd = 0;
273 // Perform parameter-driven actions
274 if (isset($ppid)) {
275 if (!isset($srcdir)) {
276 get_openemr_globals($script_dir);
279 $rsql = "SELECT * FROM procedure_providers WHERE protocol=? ";
280 $rprm = array('SFTP');
281 if ($ppid != "*") {
282 $rsql .= " AND ppid=?";
283 $rprm[] = $ppid;
286 $rs = sqlStatement($rsql, $rprm);
287 while ($rsrec = sqlFetchArray($rs)) {
288 $sftp_hosts[] = $rsrec;
290 } else { // fill in host detais from parameters
291 if (isset($fhost) && isset($user) && (isset($fdir) || isset($pdir))) {
292 $sftp_hosts[] = array (
293 'remote_host' => $host
294 ,'port' => $port
295 ,'login' => $user
296 ,'password' => $pass
297 ,'results_path' => $fdir
298 ,'orders_path' => $pdir
303 if (
304 (!isset($sftp_hosts)) || (!isset($ldir)) ||
305 (((!isset($actn)) || (!(in_array($actn, array_keys($pathmap)))))) ||
306 (((isset($sub)) && (!(in_array($sub, array_keys($submap))))))
308 $exitcd = 1;
311 if (!$exitcd) {
312 foreach ($sftp_hosts as $sftp_host) {
313 $wrk = explode(':', $sftp_host['remote_host']);
314 $sftp_host['remote_host'] = $wrk[0];
315 if (!isset($sftp_host['port'])) {
316 $sftp_host['port'] = (isset($wrk[1]) ? $wrk[1] : '22');
319 $cn = new \phpseclib3\Net\SFTP($sftp_host['remote_host'], $sftp_host['port']);
320 if (!$cn->login($sftp_host['login'], $sftp_host['password'])) {
321 sftp_status('Login error', $sftp_host['remote_host'] . ':' . $sftp_host['port']);
322 } else {
323 $sdir = '';
324 if ((isset($sub)) && (isset($sftp_host[$submap[$sub]]))) {
325 $sdir = '/' . $sftp_host[$submap[$sub]];
328 // Get the list of files. TBD: Overwrite protection.
329 if ($actn == 'get') {
330 $dir_from = $sftp_host[$pathmap[$actn]];
331 $dir_to = ($ldir . $sdir);
332 $full_list = $cn->rawlist($dir_from);
333 foreach ($full_list as $file_name => $file_rec) {
334 if ($file_rec['type'] == NET_SFTP_TYPE_REGULAR) {
335 $dir_list[] = $file_name;
338 } else {
339 $dir_to = $sftp_host[$pathmap[$actn]];
340 $dir_from = ($ldir . $sdir);
341 $full_list = new DirectoryIterator($dir_from);
342 foreach ($full_list as $fileinfo) {
343 if ($fileinfo->isFile()) {
344 $dir_list[] = $fileinfo->getFilename();
349 // Transfer each file
350 if (isset($dir_list)) {
351 foreach ($dir_list as $dir_file) {
352 // Skip directories
353 // mdsupport - now $dir_list should have only valid file names
354 // if (($dir_file == '.') || ($dir_file == '..')) {}
355 // else {
356 if ($actn == 'get') {
357 $sftp_ok = $cn->get(($dir_from . '/' . $dir_file), ($dir_to . '/' . $dir_file));
358 if ($sftp_ok) {
359 $sftp_del = $cn->delete(($dir_from . '/' . $dir_file));
361 } else {
362 $sftp_ok = $cn->put(($dir_to . '/' . $dir_file), ($dir_from . '/' . $dir_file), NET_SFTP_LOCAL_FILE);
363 if ($sftp_ok) {
364 $sftp_del = unlink(($dir_from . '/' . $dir_file));
368 sftp_status('File transfer ' . ($sftp_ok ? 'ok' : 'error'), ($dir_from . '/' . $dir_file));
369 if (isset($sftp_del) && (!$sftp_del)) {
370 sftp_status('File deletion error', $dir_file);
373 if ((!$sftp_ok) || (isset($sftp_del) && (!$sftp_del))) {
374 $exitcd = 2;
377 // }
382 sftp_status('Host action complete', " : $actn files from " . $sftp_host['remote_host'] . ':' . $sftp_host['port'] . " $dir_to");
386 if (php_sapi_name() == 'cli') {
387 fwrite(($exitcd ? STDERR : STDOUT), xl($exitmsgs[$exitcd]) . PHP_EOL);
388 exit($exitcd);