예제 #1
0
/**
 * Process new batch files for csv data and html output
 * 
 * The html output is only a file summary, no claim detail is created
 * 
 * 
 * @uses csv_verify_file()
 * @uses ibr_batch_csv_data()
 * @uses csv_write_record() 
 * @uses csv_newfile_list()
 * @param array $file_array   optional default is NULL 
 * @param boolean $html_out   whether to generate html files summary table
 * @return string             html output only for files table
 */
function ibr_batch_process_new($file_array = NULL, $html_out = TRUE)
{
    //
    // 'mtime', 'dirname', 'fname', 'trace' 'payer' 'claims'
    $html_str = "";
    $chars1 = 0;
    $chars2 = 0;
    //$chars2 = 0;
    $idx = 0;
    $ar_batchf = array();
    //$need_dir = TRUE;
    // get the list of new files
    if ($file_array === NULL || !is_array($file_array) || count($file_array) == 0) {
        $ar_newfiles = csv_newfile_list("batch");
    } else {
        $ar_newfiles = $file_array;
        //$need_dir = FALSE;
    }
    //
    if (count($ar_newfiles) == 0) {
        if ($html_out) {
            $html_str .= "<p>ibr_batch_process_new: no new batch files <br />";
            return $html_str;
        } else {
            return false;
        }
    } else {
        $btfcount = count($ar_newfiles);
    }
    // we have some new ones
    // verify and get complete path
    foreach ($ar_newfiles as $fbt) {
        $fp = csv_verify_file($fbt, 'batch', false);
        if ($fp) {
            $ar_batchf[] = $fp;
        }
    }
    $p = csv_parameters("batch");
    $b_dir = $p['directory'];
    //
    if ($html_out) {
        $html_str .= "<table cols=5 class=\"batch\">\n\t\t  <caption>Batch Files CSV</caption>\n\t\t  <thead>\n\t\t\t<tr>\n\t\t\t  <th>File Time</th><th>File Name</th>\n\t\t\t  <th>Control</th><th>Claims</th><th>x12_Partner</th>\n\t\t\t</tr>\n\t\t  </thead>\n\t\t  <tbody>";
    }
    foreach ($ar_batchf as $f_batch) {
        //
        // increment counter for alternating html backgrounds
        $bgc = $idx % 2 == 1 ? 'odd' : 'even';
        $idx++;
        //
        //$full_path = ($need_dir) ? $b_dir.DIRECTORY_SEPARATOR.$f_batch : $f_batch;
        // get the file data for csv output
        //$ar_csv_data = ibr_batch_csv_data($full_path);
        $ar_csv_data = ibr_batch_csv_data($f_batch);
        //
        // write to the files_batch csv record
        $chars1 += csv_write_record($ar_csv_data['file'], 'batch', 'file');
        $chars2 += csv_write_record($ar_csv_data['claim'], 'batch', 'claim');
        //
        // link for viewing file <a href=\"edi_view_file.php?\'fvkey\'=$dta\" target=\"_blank\">$dta</a></td>";
        if ($html_out) {
            $html_str .= "<tr class=\"{$bgc}\">\n                 <td>{$ar_csv_data['file'][0]}</td>\n                 <td><a target='_blank' href='edi_history_main.php?fvkey={$ar_csv_data['file'][1]}'>{$ar_csv_data['file'][1]}</a></td>\n                 <td>{$ar_csv_data['file'][2]}</td>\n                 <td>{$ar_csv_data['file'][3]}</td>\n                 <td>{$ar_csv_data['file'][4]}</td>\n               </tr>";
        }
    }
    if ($html_out) {
        $html_str .= "</tbody>\n\t\t   </table>\n\t\t <p></p>";
    }
    //
    csv_edihist_log("ibr_batch_process_new: {$chars1} characters written to files_batch.csv");
    csv_edihist_log("ibr_batch_process_new: {$chars2} characters written to claims_batch.csv");
    //
    if ($html_out) {
        return $html_str;
    } else {
        return "<p>Batch files: processed {$btfcount} files </p>";
    }
}
예제 #2
0
/**
 * Parse x12 file into array of segments.
 * 
 * This function relies on csv_x12_delimiters() to get the delimiters array 
 * and on csv_verify_file() to supply the next_segment value.
 * There are some basic verifications and failures are noted in the log.
 * <pre>
 *  'path'=>pathtofile
 *  'delimiters'=>array from x12_delimiters()
 *  'segments'=>segments[i]=>segment text
 *     if $seg_array is true then segments[i]=>array of elements
 * </pre>
 * 
 * @uses csv_x12_delimiters() 
 * @uses csv_verify_file()
 * @param string $file_path  the full path for the file
 * @param string $type       one of  batch|837|era|835|997|999|277|271
 * @param bool $seg_array    whether each segment should be made into an array of elements
 * @return array|bool        array['delimiters']['segments']['path'], or false on error
 */
function csv_x12_segments($file_path, $type, $seg_array = FALSE)
{
    // do verifications
    $fp_ar = csv_verify_file($file_path, $type, TRUE);
    //
    if (!$fp_ar || !$fp_ar[0]) {
        csv_edihist_log("csv_x12_segments: verification failed for {$file_path}");
        return FALSE;
    }
    //
    if ($fp_ar) {
        //
        $fp = $fp_ar[0];
        $next_segment = $fp_ar[1];
        $f_str = file_get_contents($fp);
        //
        // verify $delimiters
        $delimiters = csv_x12_delimiters(substr($f_str, 0, 126), $next_segment);
        $dlm_ok = FALSE;
        // here, expect $delimiters['t'] ['e'] ['s'] ['r']
        if (is_array($delimiters) && array_keys($delimiters) == array('t', 'e', 's', 'r')) {
            $dlm_ok = TRUE;
            $seg_d = $delimiters['t'];
            $elem_d = $delimiters['e'];
            $subelem_d = $delimiters['s'];
            $rep_d = $delimiters['r'];
        } else {
            csv_edihist_log("csv_x12_segments: invalid delimiters or delimiters wrong for {$fp}");
            return FALSE;
        }
    }
    // OK, now initialize variables
    $fn = basename($fp);
    $ar_seg = array();
    $ar_seg['path'] = $fp;
    $ar_seg['delimiters'] = $delimiters;
    $ar_seg['segments'] = array();
    $seg_pos = 0;
    // position where segment begins
    $st_segs_ct = 0;
    // segments in ST-SE envelope
    $se_seg_ct = "";
    // segment count from SE segment
    $isa_segs_ct = 0;
    // segments in ISA envelope
    $isa_ct = 0;
    // ISA envelope count
    $iea_ct = 0;
    // IEA count
    $trnset_seg_ct = 0;
    // segments by sum of isa segment count
    //
    $isa_str = "ISA" . $elem_d;
    // to reduce evaluations
    $iea_str = "IEA" . $elem_d;
    $st_str = "ST" . $elem_d;
    $se_str = "SE" . $elem_d;
    //
    $idx = -1;
    $moresegs = true;
    while ($moresegs) {
        $idx++;
        // extract each segment from the file text
        $seg_end = strpos($f_str, $seg_d, $seg_pos);
        $moresegs = strpos($f_str, $seg_d, $seg_end + 1);
        //
        $seg_text = substr($f_str, $seg_pos, $seg_end - $seg_pos);
        $seg_pos = $seg_end + 1;
        //
        // we trim in case there are line or carriage returns
        $seg_text = trim($seg_text);
        //
        // check for non ASCII basic characters.  Note reg_ex '/[^\x20-\xFF]/' allows extended ASCII characters
        // this is partly file syntax and partly protection -- we don't want to process an imposter
        // We are mostly concerned with \x00 to \x19, the "control" characters, but apparently some are allowed
        //
        if (preg_match_all('/[^\\x20-\\x7E]/', $seg_text, $matches)) {
            csv_edihist_log("csv_x12_segments: Non-basic ASCII character in segment ({$idx}) in file {$fn}");
            // quit here? return false;  -- actually files have probably been scanned before in upload.php
            // also x12 files have more allowed characters than these
        }
        if ($seg_array) {
            $ar_seg['segments'][] = explode($elem_d, $seg_text);
        } else {
            $ar_seg['segments'][] = $seg_text;
        }
        $st_segs_ct++;
        $isa_segs_ct++;
        //
        // some checks, if wanted
        if (substr($seg_text, 0, 4) == $isa_str) {
            $isa_seg = explode($elem_d, $seg_text);
            $isa_id = $isa_seg[13];
            $isa_segs_ct = 1;
            $isa_ct++;
            continue;
        }
        //
        if (substr($seg_text, 0, 3) == $st_str) {
            // $e = strpos($seg_text, $elem_d, 8);  // ST*835* is 7 characters
            // $st02 = substr($seg_text, 7, $e-7);
            $st_ar = explode($elem_d, $seg_text);
            $st_num = $st_ar[2];
            $st_segs_ct = 1;
            continue;
        }
        if (substr($seg_text, 0, 3) == $se_str) {
            $se_ar = explode($elem_d, $seg_text);
            $se_seg_ct = $se_ar[1];
            $se_num = $se_ar[2];
            if ($se_num != $st_num) {
                csv_edihist_log("csv_x12_segments: ST-SE number mismatch {$st_num} {$se_num} in {$fn}");
            }
            if (intval($se_seg_ct) != $st_segs_ct) {
                csv_edihist_log("csv_x12_segments: ST-SE segment count mismatch {$st_segs_ct} {$se_seg_ct} in {$fn}");
            }
            continue;
        }
        if (substr($seg_text, 0, 4) == $iea_str) {
            $iea_seg = explode($elem_d, $seg_text);
            $iea_id = $iea_seg[2];
            $iea_ct++;
            //
            if ($isa_id != $iea_id) {
                csv_edihist_log("csv_x12_segments: ISA-IEA identifier mismatch set {$iea_ct} in {$fn}");
            }
            if ($iea_ct == $isa_ct) {
                $trnset_seg_ct += $isa_segs_ct;
                if ($idx + 1 != $trnset_seg_ct) {
                    //
                    csv_edihist_log("csv_x12_segments: IEA segment count error ({idx+1}:{$trnset_seg_ct} set) {$iea_ct} in {$fn}");
                }
            } else {
                csv_edihist_log("csv_x12_segments: ISA-IEA count mismatch set {$isa_ct} {$iea_ct} in {$fn}");
            }
        }
        //
    }
    //
    return $ar_seg;
}
예제 #3
0
/**
 * copy the substring of text containing claim payment information for a claim
 * 
 * The filename must be found in the files.csv table, i.e. previously processed
 * 
 * @uses csv_verify_file()
 * @uses csv_x12_delimiters()
 * @param string $clp_clm_num  the pid-encounter value
 * @param string $era_file     the filename
 * @return string 			   newline characters are added to each segment end
 */
function ibr_era_get_clp_text($clp_clm_num, $era_file, $html_out = true)
{
    // @param string $clp_clm_num -- CLP01 value, pid-encounter
    // @param string $era_file path to 835 file containing the $clp_clm_num
    // segment block CLP to CLP, SE, LX
    // get the substring of the era file containing the ST number
    //
    $fp = csv_verify_file($era_file, "era");
    if (!$fp) {
        csv_edihist_log("ibr_era_get_clp_text: failed to read {$era_file}");
        $str_clp .= "failed to read {$era_file}";
        return $str_clp;
    } else {
        //
        $bstr = file_get_contents($fp);
        if (!$bstr) {
            csv_edihist_log("ibr_era_get_clp_text: failed to get contents of {$era_file}");
            $str_clp .= "failed to read {$era_file}";
            return $str_clp;
        }
        // get the delimiters
        $str_isa = substr($bstr, 0, 126);
        //
        $ar_delim = csv_x12_delimiters($str_isa, "GS");
        $seg_delim = $ar_delim['t'];
        //
        $seg_clp = "CLP*" . $clp_clm_num;
        // CLP segment that begins remittance detail
        //
        $clp_pos = strpos($bstr, $seg_clp, 0);
        // break it off if $st_pos is not found
        if ($clp_pos == FALSE) {
            csv_edihist_log("Error: {$clp_clm_num} not found in {$era_file}");
            $str_clp .= "Error: {$clp_clm_num} not found in {$era_file}";
            return $str_clp;
        }
        //
        $seg_se = "SE*";
        $seg_lx = "LX*";
        $seg_clpx = "CLP*";
        // see if we can find a closing segment
        $pos_ar[] = strpos($bstr, $seg_se, $clp_pos);
        // $se_pos =
        $pos_ar[] = strpos($bstr, $seg_lx, $clp_pos);
        // $lx_pos =
        $pos_ar[] = strpos($bstr, $seg_clpx, $clp_pos + 10);
        //$clpx_pos =
        //
        // choose the best closing position, closest to $clp_pos
        asort($pos_ar);
        foreach ($pos_ar as $p) {
            // echo "clp_pos $clp_pos  pos_ar $p". PHP_EOL;
            if ($p > $clp_pos) {
                $end_pos = $p;
                break;
            }
        }
        //
        $str_clp = substr($bstr, $clp_pos, $end_pos - $clp_pos);
        //
        // add newlines so each segment is on its own line
        if (strpos($str_clp, $seg_delim . PHP_EOL)) {
            // if true, assume the file has newlines ending segments
        } else {
            // we could get fancy and make an html table or add line numbers
            $str_clp = str_replace($seg_delim, $seg_delim . PHP_EOL, $str_clp);
        }
    }
    //
    if ($html_out) {
        $str_html = "<div class=\"filetext\">";
        $str_html .= "<p>{$pe} &nbsp;&nbsp;" . basename($fp) . " </p>" . PHP_EOL . "<pre><code>";
        $str_html .= $str_clp;
        $str_html .= PHP_EOL . "</code></pre>" . PHP_EOL . "</div>" . PHP_EOL;
        return $str_html;
    } else {
        return $str_clp;
    }
}
예제 #4
0
/**
 * Create html output for a 277 file, same as the process new output
 * 
 * @uses csv_verify_file()
 * @uses csv_x12_segments()
 * @uses ibr_277_parse()
 * @uses ibr_277_csv_claim_data()
 * @uses ibr_277_html()
 * @param string $filepath -- filename or full path to file
 * @return string
 */
function ibr_277_filetohtml($filepath, $err_only = false)
{
    //
    // simply create an html output for the file like processing new
    //
    $html_str = "";
    //
    $fp = csv_verify_file($filepath, "f277");
    if ($fp) {
        $ar_277_seg = csv_x12_segments($fp, "f277", false);
        if (is_array($ar_277_seg) && count($ar_277_seg['segments'])) {
            $ar_277_vals = ibr_277_parse($ar_277_segs);
        } else {
            $html_str .= "failed to get segments for {$fp} <br />" . PHP_EOL;
            continue;
        }
        $idx = 0;
        foreach ($ar_277_vals as $isa) {
            $ar_csvf = $isa['file'];
            $ar_csvc = ibr_277_csv_claim_data($isa['claim']);
            //
            $ar_h[$idx]['file'] = $ar_csvf;
            $ar_h[$idx]['claim'] = $ar_csvc;
            $idx++;
        }
        $html_str .= ibr_277_html($ar_h, $err_only);
    } else {
        csv_edihist_log("ibr_277_filetohtml: verification failed {$filepath}");
        $html_str .= "ibr_277_filetohtml: Error, validation failed {$filepath} <br />";
    }
    //
    return $html_str;
}
예제 #5
0
/**
 * process new 997/999 files
 * 
 * @uses csv_newfile_list()
 * @uses csv_verify_file()
 * @uses csv_parameters()
 * @param array $file_array -- optional, this array is sent from the ibr_io.php script
 * @param bool $html_out -- whether to produce and return html output
 * @param bool $err_only -- whether to generate claim information only for errors (ignored)
 * @return string
 */
function ibr_997_process_new($file_array = NULL, $html_out = TRUE, $err_only = TRUE)
{
    //
    $ret_str = "";
    $chr_ct1 = 0;
    $chr_ct2 = 0;
    $new_997 = array();
    $need_dir = TRUE;
    //
    if (is_null($file_array) || empty($file_array)) {
        // directory will need to be prepended to name
        $new_997 = csv_newfile_list('f997');
    } elseif (is_array($file_array) && count($file_array)) {
        // files that are not verified will just be ignored
        foreach ($file_array as $finp) {
            $fp = csv_verify_file($finp, 'f997');
            if ($fp) {
                $new_997[] = $fp;
            }
        }
        $need_dir = FALSE;
    }
    //
    if (count($new_997) == 0) {
        $ret_str = "<p>ibr_997_process_new: no new 997/999 files. </p>";
        return $ret_str;
    } else {
        $f997count = count($new_997);
    }
    //
    $ar_htm = array();
    //
    $params = csv_parameters("f997");
    $tdir = dirname(__FILE__) . $params['directory'];
    //
    // get batch files parameters, we need the batch_dir
    $bp = csv_parameters("batch");
    $batch_dir = dirname(__FILE__) . $bp['directory'];
    //
    foreach ($new_997 as $f997) {
        // make the file path
        $fpath = $need_dir ? $tdir . DIRECTORY_SEPARATOR . $f997 : $f997;
        // get file m-time
        //$ftime = date('Ymd:His', filemtime($fpath));
        // read file into string
        $f_str = file_get_contents($fpath);
        if ($f_str) {
            // transform file contents into segment arrays
            $ar_seg = csv_x12_segments($fpath, "f997", FALSE);
            //
            if (!$ar_seg) {
                // file was rejected
                csv_edihist_log("ibr_997_process_new: failed to get segments for {$fpath}");
                $ret_str .= "ibr_997_process_new: failed to get segments for {$fpath}</p>" . PHP_EOL;
                continue;
            }
            // parse arrays into data arrays
            $ar_data = ibr_997_parse($ar_seg);
            // $ar_data = array("file" => $ar_997file, "rejects" => $ar_reject);
            //
            $csv_claims = ibr_997_data_csv($ar_data['file'], 'file');
            if ($csv_claims) {
                $chr_ct1 += csv_write_record($csv_claims, "f997", "file");
            } else {
                csv_edihist_log("ibr_997_process_new: error with files csv array");
            }
            //
            if (isset($ar_data['claims']) && count($ar_data['claims'])) {
                // only add to claims_997.csv if there are rejected claims
                $csv_claims = ibr_997_data_csv($ar_data['claims'], 'claim');
                if ($csv_claims) {
                    $chr_ct2 += csv_write_record($csv_claims, "f997", "claim");
                } else {
                    csv_edihist_log("ibr_997_process_new: error with claims csv array");
                }
                //
            }
            //
            // save all the ar_datas for html output
            if ($html_out) {
                $ar_htm[] = $ar_data;
            }
            //
        } else {
            $ret_str .= "<p>ibr_997_process_new: failed to read {$fpath} </p>" . PHP_EOL;
        }
    }
    //
    csv_edihist_log("ibr_997_process_new: {$chr_ct1} characters written to files_997.csv");
    csv_edihist_log("ibr_997_process_new: {$chr_ct2} characters written to claims_997.csv");
    //
    if ($html_out) {
        // generate html output and return that
        $ret_str .= ibr_997_file_data_html($ar_htm, $err_only);
    } else {
        $ret_str .= "x12_999 files: processed {$f997count} x12-999 files <br />";
    }
    //
    return $ret_str;
}
예제 #6
0
/**
 * process new .ta1 files
 * 
 * 
 * @uses ibr_process_ta1()
 * @uses ibr_ack_html()
 * @uses csv_newfile_list()
 * @uses csv_verify_file()
 * @param array|string -- optional array of filenames or filename
 * @param bool --whether to produce html table
 * @return string
 */
function ibr_ta1_process_new($ta1_files, $html_out = TRUE)
{
    //
    $str_html = "";
    //
    if (is_array($ta1_files)) {
        $new_files = $ta1_files;
    } elseif (is_string($ta1_files) && strlen($ta1_files)) {
        $new_files[] = $ta1_files;
    } else {
        $new_files = csv_newfile_list("ta1");
    }
    //
    if (is_array($new_files) && count($new_files) > 0) {
        $fcount = count($new_files);
        foreach ($new_files as $ta) {
            $fp = csv_verify_file($ta, 'ta1');
            if ($fp) {
                $ar_d[] = ibr_process_ta1($fp);
            } else {
                $str_html .= "TA1 failed to verify file: {$ta} <br />" . PHP_EOL;
                csv_edihist_log("ibr_ta1_process_new: failed to verify file: {$ta}");
            }
        }
    }
    // write a line to csv file -- put in file_997.csv
    if (is_array($ar_d) && count($ar_d)) {
        foreach ($ar_d as $arec) {
            // do not put message, batch, or text file in csv record
            $ar_csv[] = array_slice($arec, 0, count($arec) - 3);
        }
        $rslt = csv_write_record($ar_csv, "ta1", "file");
        csv_edihist_log("ibr_ta1_process_new: {$rslt} characters written to files_997.csv");
        if ($html_out) {
            $str_html .= ibr_ack_html($ar_d);
        } else {
            $str_html .= "TA1 files: processed {$fcount} TA1 files <br />" . PHP_EOL;
        }
    } else {
        $str_html .= "No new TA1 files found. <br />" . PHP_EOL;
    }
    //
    return $str_html;
}
예제 #7
0
/**
 * generate output as if file is being processed
 * 
 * @param string  filename
 * @param bool    display errors only 
 * @return string
 */
function ibr_ebr_filetohtml($filepath, $err_only = false)
{
    // simply create an html output for the file
    $html_str = "";
    $data_ar = array();
    $fp = false;
    $ext = substr($filepath, -3);
    if (strpos('|ibr|ebr', $ext)) {
        $fp = csv_verify_file($filepath, "ebr");
    }
    if ($fp) {
        if ($ext == 'ebr') {
            $data_ar = ibr_ebr_values($fp);
        } elseif ($ext == 'ibr') {
            $data_ar = ibr_ibr_values($fp);
        } else {
            csv_edihist_log("ibr_ebr_filetohtml: invalid extension {$ext} " . basename($fp));
            return "<p>invalid extension {$ext} </p>" . PHP_EOL;
        }
        //
        $html_str .= ibr_ebr_html($data_ar, $err_only);
    } else {
        csv_edihist_log("ibr_ebr_filetohtml: verification failed {$filepath}");
        $html_str .= "Error, validation failed {$filepath} <br />";
    }
    //
    return $html_str;
}
예제 #8
0
/**
 * Categorize and check uploaded files
 * 
 * Files are typed and scanned, if they pass scan, then the file is moved
 * to the IBR_UPLOADS_DIR and an array('type', 'name') is returned 
 * If an error occurs, FALSE is returned
 * 
 * @uses csv_verify_file()
 * @param array $param_ar -- the csv_parameters("ALL") array (so we don't have to keep creating it)
 * @param array $fidx -- individual file array from $files[$i] array
 * @param string &$html_str -- the html output that is passed around
 * @return array|bool
 */
function ibr_upload_match_file($param_ar, $fidx, &$html_str)
{
    //
    if (is_array($fidx) && isset($fidx['name'])) {
        $fn = basename($fidx['name']);
        $ftmp = $fidx['tmp_name'];
    } else {
        $html_str .= "Error: invalid file argument <br />" . PHP_EOL;
        return FALSE;
    }
    //
    if (is_array($param_ar) && count($param_ar)) {
        $p = $param_ar;
        // csv_parameters("ALL");
    } else {
        $html_str .= "Error: invalid parameters <br />" . PHP_EOL;
        return FALSE;
    }
    //
    $ibr_upldir = csv_edih_tmpdir();
    //
    $ar_fn = array();
    //
    foreach ($p as $ky => $par) {
        //
        if (!$p[$ky]['regex']) {
            continue;
        }
        if (!preg_match($p[$ky]['regex'], $fn)) {
            continue;
        }
        // file name has matched an allowed extension; now, we scan the file
        //$is_tp = TRUE;
        //
        $fstr = file_get_contents($ftmp);
        if (!$fstr) {
            $html_str .= "Error: could not read {$fn} <br />" . PHP_EOL;
            return FALSE;
        }
        // check for Non-basic ASCII character and <%, <asp:, <?, ${, #!, <?, <scr (any other evil script indicators?)
        // basically allows A-Z a-z 0-9 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and newline carriage_return
        // this may need to be adapted to the allowed x12 characters, but <%, <asp:, <?, ${, and #! should never occur
        //  any other bad stuff indicators should be added
        //
        //$fltr = filter_var ( $fstr, FILTER_SANITIZE_STRING ); FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH,
        if (preg_match('/[^\\x20-\\x7E\\x0A\\x0D]|(<\\?)|(<%)|(<asp)|(<ASP)|(#!)|(\\$\\{)|(<scr)|(<SCR)/', $fstr, $matches, PREG_OFFSET_CAPTURE)) {
            //
            $html_str .= "Filtered character in file {$fn} -- not accepted <br />" . PHP_EOL;
            $html_str .= " character: " . $matches[0][0] . "  position: " . $matches[0][1] . "<br />" . PHP_EOL;
            //
            return FALSE;
        } else {
            // file matches extension and contains only allowed characters
            $newname = $ibr_upldir . DIRECTORY_SEPARATOR . $fn;
            //
            $is_mv = rename($ftmp, $newname);
            if (!$is_mv) {
                // moving file failed, check permissions for directory, since this may be a problem at start
                if (!is_writable($ibr_upldir)) {
                    $html_str .= "write permission denied for {$ibr_upldir}<br />" . PHP_EOL;
                }
                $html_str .= "Error: unable to move {$fn} to uploads directory<br />" . PHP_EOL;
                return FALSE;
            } else {
                // file was moved, now set permissions to be -rw-------
                $iscm = chmod($newname, 0600);
                if (!$iscm) {
                    $html_str .= "Error: failed to set permissions for {$fa['name']} -- trying to remove<br />" . PHP_EOL;
                    unlink($newname);
                    return FALSE;
                }
            }
            if ($is_mv && $iscm) {
                // verify file by checking for expected parts -- not extensive
                // function csv_verify_file() is in csv_record_include.php
                if (csv_verify_file($newname, $ky)) {
                    //
                    $ar_fn['type'] = (string) $ky;
                    $ar_fn['name'] = $newname;
                } else {
                    //
                    //$html_str .= "verify failed for $newname <br />" .PHP_EOL;
                    return FALSE;
                }
            }
        }
        // quit the extension check loop
        break;
    }
    return $ar_fn;
}