2 /* ********** flow pattern for using sftp with edi_history
4 * 1. database query for "x12 Partners"
5 * -- get the partner name
6 * -- populate a "select" list with names
7 * -- "submit" runs script which gets url, password, etc and the sftp
8 * transfer for selected x12 partner
9 * -- put downloaded files in a designated directory e.g. history/sftp/
11 * 2 (alt) create php script to use most of edih_uploads.php function edih_upload_files()
12 * -- but skip the $_FILES array rewrite and testing, just test, match type and store
13 * -- maybe call it edih_sftp_upload()
15 * 3. add if stanza to edih_main.php in section:
16 * if (strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
17 * -- calls edih_io.php function edih_disp_sftp_upload()
18 * -- remove the files from the download directory
19 * (alt) -- this is where you would call maybe edih_sftp_upload()
21 * 4. At this point the sftp files will be in the edi/history/[type] directories
22 * and the "Process" button will run the script to parse the files and create csv table rows
26 /* ** add this function to edih_uploads.php
27 * -- or work it into edih_upload_files(), since it is almost a direct copy
30 function edih_upload_sftp() {
34 $sftp_dir = 'path/to/dir';
36 //csv_edihist_log('Error: upload files indicated, but none received.');
39 if (is_dir($sftp_dir) && is_readable($sftp_dir)) {
40 $sftp_dir = realpath($sftp_dir);
41 $fn_ar = scandir($sftp_dir);
43 $html_str = 'unable to read the directory for sftp file uploads <br />';
44 csv_edihist_log('unable to read the directory for sftp file uploads');
48 $m_types = array('application/octet-stream', 'text/plain', 'application/zip', 'application/x-zip-compressed');
50 // some unwanted file extensions that might be accidentally included in upload files
51 $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';
52 // we get the parameters here to send to ibr_upload_match_file()
53 $param_ar = csv_parameters();
54 //if ( class_exists('finfo') )
55 foreach($fn_ar as $idx=>$fn) {
57 $fa['tmp_name'] = tempnam($sftp_dir.DS
.$fn, 'x12_');
58 $fa['name'] = $sftp_dir.DS
.$fn;
59 $fa['type'] = mime_content_type($sftp_dir.DS
.$fn);
60 $fa['size'] = filesize($sftp_dir.DS
.$fn);
61 // now do verifications
62 if ( !in_array($fa['type'], $m_types) ) {
63 //$html_str .= "Error: mime-type {$fa['type']} not accepted for {$fa['name']} <br />" . PHP_EOL;
64 $f_ar['reject'][] = array('name'=>$fa['name'],'comment'=>'mime-type '.$fa['type']);
65 csv_edihist_log('edih_upload_sftp: error mime-type '.$fa['name'].' mime-type '.$fa['type']);
69 // verify that we have a usable name
70 $fext = (strpos($fa['name'], '.')) ?
pathinfo($fa['name'], PATHINFO_EXTENSION
) : '';
71 if( $fext && preg_match('/'.$ext_types.'\?/i',$fext) ) {
72 //$html_str .= 'Error: uploaded_file error for '.$fa['name'].' extension '.$fext.'<br />'. PHP_EOL;
73 $f_ar['reject'][] = array('name'=>$fa['name'],'comment'=>'extension '.$fext);
74 csv_edihist_log('edih_upload_sftp: _FILES error name '.$fa['name'].' extension '.$fext);
78 if (is_string($fa['name'])) {
79 // check for null byte in file name, linux hidden file, directory
80 if (strpos($fa['name'], '.') === 0 ||
strpos($fa['name'], "\0") !== false ||
strpos($fa['name'], "./") !== false ) {
81 //$html_str .= "Error: uploaded_file error for " . $fa['name'] . "<br />". PHP_EOL;
82 $fname = preg_replace( "/[^a-zA-Z0-9_.-]/","_", $fa['name'] );
83 $f_ar['reject'][] = array('name'=>$fname,'comment'=>'null byte, hidden, invalid');
84 csv_edihist_log('edih_upload_sftp: null byte, hidden, invalid '.$fname);
88 // replace spaces in file names -- should not happen, but response files from payers might have spaces
89 // $fname = preg_replace("/[^a-zA-Z0-9_.-]/","_",$fname);
90 $fa['name'] = str_replace(' ', '_', $fa['name']);
92 // name is not a string
93 //$html_str .= "Error: uploaded_file error for " . $fa['tmp_name'] . "<br />". PHP_EOL;
94 $f_ar['reject'][] = array('name'=>(string)$fa['name'],'comment'=>'invalid name');
98 if ( !$fa['tmp_name'] ||
!$fa['size'] ) {
99 //$html_str .= "Error: file name or size error <br />" . PHP_EOL;
100 $f_ar['reject'][] = array('name'=>(string)$fa['name'],'comment'=>'php file upload error');
101 unset($files[$uplkey][$idx]);
105 if ( strpos(strtolower($fa['name']), '.zip') ||
strpos($fa['type'], 'zip') ) {
107 $f_upl = edih_ziptoarray($fa['tmp_name'], $param_ar, false);
109 // put them in the correct type array
110 if (is_array($f_upl) && count($f_upl)) {
111 foreach($f_upl as $tp=>$fz) {
112 if ( $tp == 'reject' ) {
113 if ( isset($f_ar['reject']) && is_array($fz) ) {
114 array_merge($f_ar['reject'], $fz);
116 $f_ar['reject'] = (is_array($fz)) ?
$fz : array();
119 // expect $fz to be an array of file names
120 foreach($fz as $zf) {
127 // nothing good from edih_ziptoarray()
128 // $html_str .= "error with zip file or no files accepted for " . $fa['name'] . "<br />" .PHP_EOL;
129 $f_ar['reject'][] = array('name'=>$fa['name'],'comment'=>'error with zip archive');
130 unset($files[$uplkey][$idx]);
132 // continue, since we have done everything that would happen below
136 $f_upl = edih_upload_match_file($param_ar, $fa);
138 if (is_array($f_upl) && count($f_upl) > 0 ) {
139 $f_ar[$f_upl['type']][] = $f_upl['name'];
142 // verification failed
143 csv_edihist_log('edih_upload_file: verification failed for '. $fa['name']);
144 $f_ar['reject'][] = array('name'=>$fa['name'], 'comment'=>'verification failed');
147 } // end foreach($files[$uplkey] as $idx=>$fa)
149 $f_ar['remark'][] = "Received $f_ct files, accepted $p_ct" . PHP_EOL
;
153 /* ** add this function to edih_io.php */
154 function edih_disp_sftp_upload() {
156 // imaginary form and POST values
158 if (isset($_POST['post_sftp'])) {
159 $la = (isset($_POST['post_sftp'])) ?
filter_input(INPUT_POST
, 'post_sftp', FILTER_SANITIZE_STRING
) : ;
160 $x12ptnr = (isset($_POST['sftp_select'])) ?
filter_input(INPUT_POST
, 'sftp_select', FILTER_SANITIZE_STRING
) :;
162 if (($la == 'get_sftp') && $x12ptnr) {
163 // yet to be written -- gets x12 partner info and does sftp download
164 $is_sftp = edih_sftp_connect($x12ptnr);
166 $f_array = ($is_sftp) ?
edih_upload_sftp() : false;
167 if ( is_array($f_array) && count($f_array) ) {
168 $str_html .= edih_sort_upload($f_array);
170 $str_html .= "sftp connection did not get any files <br />".PHP_EOL
;
173 $str_html .= "sftp file transfer invalid input <br />" . PHP_EOL
;
179 /* ** edih_view.php needs a form to give user control over sftp uploads */
180 // a select list of x12 partners and submit button and process button
181 // append output of edih_disp_sftp_upload() to displayed file list
184 * Unattended SFTP host operations using phpseclib.
186 * Copyright (C) 2014 MD Support <mdsupport@users.sourceforge.net>
188 * LICENSE: This program is free software; you can redistribute it and/or
189 * modify it under the terms of the GNU General Public License
190 * as published by the Free Software Foundation; either version 2
191 * of the License, or (at your option) any later version.
192 * This program is distributed in the hope that it will be useful,
193 * but WITHOUT ANY WARRANTY; without even the implied warranty of
194 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
195 * GNU General Public License for more details.
196 * You should have received a copy of the GNU General Public License
197 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
200 * @author MD Support <mdsupport@users.sourceforge.net>
202 * Following parameters may be provided :
203 * 1. actn - get or put
204 * 2. Interface with Procedure providers : ppid
205 * 3. Single host definition : host, port, user, pass, fdir, pdir
206 * 4. ldir - local directory
207 * 5. sub - 'ppid'/'host'/'fdir'/'pdir' to be used as subdir of ldir
211 if (php_sapi_name() == 'cli') {
212 parse_str(implode('&', array_slice($argv, 1)), $_GET);
213 $_SERVER['REQUEST_URI']=$_SERVER['PHP_SELF'];
214 $_SERVER['SERVER_NAME']='localhost';
218 $get_count = extract( $_GET, EXTR_OVERWRITE
);
219 // Following breaks link to OpenEMR structure dependency - assumes phpseclib is subdir
220 $script_dir = dirname(__FILE__
);
221 ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR
. "$script_dir/phpseclib");
222 require_once ("$script_dir/phpseclib/Net/SFTP.php");
223 function get_openemr_globals ($libdir) {
224 if (!isset($site)) $_GET['site'] = 'default';
225 require_once ("$libdir/../interface/globals.php");
227 function sftp_status($msg, $val) {
228 if (php_sapi_name() == 'cli') {
229 fwrite(STDOUT
, xl($msg).' '.$val.PHP_EOL
);
234 ,1 => 'Missing/Invalid parameter(s) - SFTP host definition, actn, ldir'
235 ,2 => 'File Transfer error(s)'
237 // OpenEMR specific mappings. $sub works with procedure_providers fields
240 ,'host' => 'remote_host'
241 ,'fdir' => 'results_path'
242 ,'pdir' => 'orders_path'
245 'get' => 'results_path'
246 ,'put' => 'orders_path'
249 // Perform parameter-driven actions
251 if (!isset($srcdir)) get_openemr_globals ($script_dir);
252 $rsql = "SELECT * FROM procedure_providers WHERE protocol=? ";
253 $rprm = array('SFTP');
255 $rsql .= " AND ppid=?";
258 $rs = sqlStatement($rsql, $rprm);
259 while ($rsrec = sqlFetchArray($rs)) {
260 $sftp_hosts[] = $rsrec;
262 } else { // fill in host detais from parameters
263 if (isset($fhost) && isset($user) && (isset($fdir) ||
isset($pdir)))
264 $sftp_hosts[] = array (
265 'remote_host' => $host
269 ,'results_path' => $fdir
270 ,'orders_path' => $pdir
273 if ((!isset($sftp_hosts)) ||
(!isset($ldir)) ||
274 (((!isset($actn)) ||
(!(in_array($actn, array_keys($pathmap)))))) ||
275 (((isset($sub)) && (!(in_array($sub, array_keys($submap))))))
279 if (!$exitcd) foreach ($sftp_hosts as $sftp_host) {
280 $wrk = explode(':', $sftp_host['remote_host']);
281 $sftp_host['remote_host'] = $wrk[0];
282 if (!isset($sftp_host['port'])) {
283 $sftp_host['port'] = (isset($wrk[1]) ?
$wrk[1] : '22');
285 $cn = new \phpseclib\Net\
SFTP($sftp_host['remote_host'], $sftp_host['port']);
286 if (!$cn->login($sftp_host['login'], $sftp_host['password'])) {
287 sftp_status('Login error', $sftp_host['remote_host'].':'.$sftp_host['port']);
290 if ((isset($sub)) && (isset($sftp_host[$submap[$sub]]))) $sdir = '/'.$sftp_host[$submap[$sub]];
291 // Get the list of files. TBD: Overwrite protection.
292 if ($actn == 'get') {
293 $dir_from = $sftp_host[$pathmap[$actn]];
294 $dir_to = ($ldir.$sdir);
295 $full_list = $cn->rawlist($dir_from);
296 foreach ($full_list as $file_name=>$file_rec) {
297 if ($file_rec['type'] == NET_SFTP_TYPE_REGULAR
) {
298 $dir_list[] = $file_name;
302 $dir_to = $sftp_host[$pathmap[$actn]];
303 $dir_from = ($ldir.$sdir);
304 $full_list = new DirectoryIterator($dir_from);
305 foreach ($full_list as $fileinfo) {
306 if ($fileinfo->isFile()) {
307 $dir_list[] = $fileinfo->getFilename();
311 // Transfer each file
312 if (isset($dir_list)) foreach ($dir_list as $dir_file) {
314 // mdsupport - now $dir_list should have only valid file names
315 // if (($dir_file == '.') || ($dir_file == '..')) {}
317 if ($actn == 'get') {
318 $sftp_ok = $cn->get(($dir_from.'/'.$dir_file), ($dir_to.'/'.$dir_file));
319 if ($sftp_ok) $sftp_del = $cn->delete(($dir_from.'/'.$dir_file));
321 $sftp_ok = $cn->put(($dir_to.'/'.$dir_file), ($dir_from.'/'.$dir_file), NET_SFTP_LOCAL_FILE
);
322 if ($sftp_ok) $sftp_del = unlink(($dir_from.'/'.$dir_file));
324 sftp_status('File transfer '.($sftp_ok ?
'ok' : 'error'), ($dir_from.'/'.$dir_file));
325 if (isset($sftp_del) && (!$sftp_del)) sftp_status('File deletion error', $dir_file);
326 if ((!$sftp_ok) ||
(isset($sftp_del) && (!$sftp_del))) $exitcd = 2;
330 sftp_status('Host action complete', " : $actn files from ".$sftp_host['remote_host'].':'.$sftp_host['port']. " $dir_to");
332 if (php_sapi_name() == 'cli') {
333 fwrite(($exitcd ? STDERR
: STDOUT
), xl($exitmsgs[$exitcd]).PHP_EOL
);