' . $this->make_caution($faulting) . $line_desc . '' . $e . "$desc
\n"; return; } function make_caution($faulting) { /* * Syntax: make_caution($faulting) * * Return a blurb when an error message may be an artifact. * * Input: * $faulting Boolean. When TRUE input data was ignored. * * Returns: * A htmlized string describing the problem. * * Side Effects: */ if ($faulting) { // We already had one error, so subsequent problems may or // may not be real. $prefix = 'CAUTION -- This error may not be real; prior row(s) of the uploaded file were rejected:File supplied: ' . htmlentities($_FILES['datafile']['name'], ENT_QUOTES) . '
'; } print $error; print "Error uploading file: No file uploaded
'); return FALSE; default: // Got an error on the file upload. // Let the user try again. $upload_form->show('' . explain_error('Error uploading file', $datafile_err) . '
'); return FALSE; } if (array_key_exists(UPLOADED_FILE, $_SESSION) && array_key_exists($upload_form->db , $_SESSION[UPLOADED_FILE]) && $this->filename == $_SESSION[UPLOADED_FILE][$upload_form->db]) { // The user is trying to upload a file (with the same name as) // the file which was last successfully put into the database. $upload_form->show('Upload aborted. A file with this name was the last file successfully loaded into the ' . $upload_form->db . ' database.
'); return FALSE; } if ($_FILES['datafile']['size'] == 0) { // Let the user try again. $upload_form->show('The file uploaded' . " ($datafile) is empty.
"); return FALSE; } // Get rid of OS specific cruft. $tmpf = '/tmp/upcen.' . getmypid(); if (move_and_fix_eol($datafile_tmp, $tmpf) != 0) { // Let the user try again. $upload_form->show('Unexpected failure trying' . 'to move uploaded file.
'); return FALSE; } // Read the file $content = file($tmpf); unlink($tmpf); // cleanup if ($content === FALSE) { // Let the user try again. $upload_form->show('Unexpected failure trying' . ' to read uploaded file.
'); return FALSE; } // Get rid of any stupid MS Windows eof character $last = count($content) - 1; if (substr($content[$last], strlen($content[$last]) - 1, 1) == chr(26)) { // File ends in a Ctrl-Z, the DOS EOF character. // Remove it. $content[$last] = substr($content[$last] , 0 , strlen($content[$last]) - 1); } $this->data = $content; return TRUE; } } ///////////////////////////////////////////////////////////////////// class Upload_connection { /* * Abstracts database operations over a postgresql connection. */ /* * Bugs: * It does not seem (php-4.3.9-3.22) that you can cache * a query result resource in an object. So we've two * different functions sql() and sql_result(). * This could just be the result of php4 stupidness with * objects and references. I've not thought it through. */ // Private variables. var $upload_form; // An Upload_form instance. var $pg; // The postgresql connection handle. var $db; // The database connected to. var $user; // The user used to authenticate. var $password; // The password used to authenticate. var $last_pg_error; // The last error from postgres // Set to '' by any function that might cause a pg // error, except when we know that we're going to // do operations that might trash pg_last_error(). function Upload_connection($db, $user, $password, &$upload_form) { /* * Class constructor. * * Input: * $db The database to connect to. * $user The username to login with. * $password The password to login with. * $upload_form The HTML form used to get info from the user. * * Returns: A class instance. * * Side Effects: none * * Remarks: */ // Save the instance varaibles.. $this->upload_form =& $upload_form; $this->db = $db; $this->user = $user; $this->password = $password; unset($this->last_pg_error); return; } function connect() { /* * Connect to the database * * Input: * * Returns: * TRUE on success, FALSE otherwise. * * Globals: * $last_pg_error Cleared. * * Side Effects: * Connects to the db with the username and password. * On error re-displays html form with error. */ // // Login to postgres. // $this->pg = pg_connect('host=' . PG_HOST . ' port=' . PG_PORT . " dbname=$this->db user=$this->user password='" . str_replace("'" , "\\'" , str_replace('\\', '\\' . '\\', $this->password)) . "'"); if ($this->pg === FALSE) { // Let the user try again. // Allow user to login again by calling with blank password. $this->upload_form->show('Login failed.' . ' Is the database, user,' . ' and password correct?
' , ''); return FALSE; } $this->sloppy = FALSE; unset($this->last_pg_error); return TRUE; } function begin() { /* * Begin a transation. * * Syntax: begin() * begin(sloppy_errors) * * Input: * sloppy_errors (boolean, optional, defaults to FALSE) * When TRUE simulate the ability to continue executing statements * within a transaction after an error, reporting a new error * for each error that occurs. Queries can still fail but * subsequent queries within the simulated transaction can * appear to succeed, although they will never be committed * to the database. * * Note that this works by rolling back on error and starting a * new transaction that will never be committed. So all statements * executed after a failure will not see the effects of statements * executed before the failure. This can produce errors that would * not otherwise exist. * * Returns: * TRUE on success, FALSE otherwise. * * Globals: * $last_pg_error Cleared. * * Side Effects: * Begins a db transaction. * Can establish a savepoint. * On error re-displays html form with error. */ if (func_num_args() > 0) { $sloppy_errors = func_get_arg(0); } else { $sloppy_errors = FALSE; } $this->sloppy = $sloppy_errors; $this->faulting = FALSE; unset($this->last_pg_error); // // Start the transaction. // if (pg_query($this->pg, 'BEGIN;') === FALSE) { // Let the user try again. $this->upload_form->show('Failed to begin transaction:'
. '
'
. htmlentities(pg_last_error($this->pg))
. '
Failed to establish savepoint at'
. ' transaction start:
'
. htmlentities(pg_last_error($this->pg))
. '
Failed to rollback transaction:'
. '
'
. htmlentities(pg_last_error($this->pg))
. '
Program aborted: Internal error:' . ' Failed to rollback to savepoint
'); exit(); } return; } function redo_savepoint() { /* * Redo the savepoint we use for sloppy transactions. * * Input: * * Returns: n/a/ * * Side Effects: * May send html with an error to the user and abort. * Re-establishes the savepoint. * * Remarks: * $last_pg_error not altered because we only call this after * we know that the last query succeeded. */ if (pg_query($this->pg, 'RELEASE SAVEPOINT ' . SP_NAME . ';') === FALSE) { error_log('Program aborted: Internal error: Failed to release savepoint'); $this->upload_form->show('Internal error:' . ' Failed to release savepoint'); exit(); } if (pg_query($this->pg, SP_COMMAND) === FALSE) { error_log('Program aborted: Internal error: Failed to establish savepoint'); $this->upload_form->show('
Internal error:' . ' Failed to establish savepoint'); exit(); } return; } function sql($sql) { /* * Submit some SQL to the db. * * Syntax: sql($sql) * sql($sql, $side_effects) * * Input: * $sql The SQL to send to the db. * $side_effects Boolean. Optional. Defaults to TRUE. * When true the sql may alter the db. * * Returns: * TRUE if the SQL executed successfully, FALSE otherwise. * * Globals: * $last_pg_error Cleared. * * Side Effects: * SQL may alter db. */ if (func_num_args() > 1) { $side_effects = func_get_arg(1); } else { $side_effects = TRUE; } unset($this->last_pg_error); if (pg_query($this->pg, $sql) === FALSE) { error_log("Failed query: $sql"); if ($this->sloppy === TRUE) { // In a sloppy transaction. $this->sloppy_restart(); } return FAlSE; } if ($this->sloppy === TRUE && $side_effects) { // Get rid of the old savepoint and make a new one. $this->redo_savepoint(); } return TRUE; } function sql_result($sql) { /* * Submit some SQL to the db and get a result. * * Syntax: sql_result($sql) * sql_result($sql, $side_effects) * * Input: * $sql The SQL to send to the db. * $side_effects Boolean. Optional. Defaults to FALSE. * When true the sql may alter the db. * * Returns: * The first column of the first row of the most recent * query result set or FALSE on error or the integer 0 * if there is no result. * * Globals: * $last_pg_error Cleared. * * Side Effects: * SQL may alter db. * * Remarks: * Values are returned as strings. Distingush no rows from the string * "0" using ===. * * Bugs: * Booleans are returned as the strings "t" and "f" and NULL as * the string "NULL". */ if (func_num_args() > 1) { $side_effects = func_get_arg(1); } else { $side_effects = FALSE; } unset($this->last_pg_error); $qr = pg_query($this->pg, $sql); if ($qr === FALSE) { error_log("Failed query: $sql"); if ($this->sloppy === TRUE) { // In a sloppy transaction. $this->sloppy_restart(); } return FALSE; } if (pg_num_rows($qr) == 0) { return 0; } // Stash the result. $r = pg_fetch_result($qr, 0, 0); if ($this->sloppy === TRUE && $side_effects) { // Get rid of the old savepoint and make a new one. $this->redo_savepoint(); } return $r; } function last_error() { /* * Return string describing the last error on the connection. * * Input: * * Returns: * String describing last error on the connection. * * Globals: * $last_pg_error Referenced. * * Side Effects: */ if (isset($this->last_pg_error)) { return $this->last_pg_error; } return pg_last_error($this->pg); } function sloppy_rollback() { /* * Private function, for internal use only. * * Rollback the current (sloppy) transaction. * * Input: * * Returns: n/a * * Globals: * $last_pg_error Cleared. * * Side Effects: * Rolls back the current transaction. * * Remarks: * Called only when in a sloppy transaction that has been continued * and has 'completed' without further error. * */ // The sloppy transaction had a failure. // Rollback the 'continued' transaction. if (pg_query($this->pg, 'ROLLBACK;') === FALSE) { error_log('The sloppy transaction could not be rolled back'); // This is kinda bad, we don't want to commit things that we shouldn't. exit('Internal error: Failed to rollback a sloppy transaction'); } unset($this->last_pg_error); return; } function commit() { /* * Commit the current transaction. * * Input: * * Returns: * TRUE on success or FALSE on failure. * * Globals: * $last_pg_error Cleared. * * Side Effects: * Completes the current transaction. * */ if ($this->sloppy && $this->faulting) { // The sloppy transaction had a failure. $this->sloppy_rollback(); return FALSE; // The transaction did not commit. } unset($this->last_pg_error); if (pg_query($this->pg, 'COMMIT;') === FALSE) { return FALSE; } return TRUE; } function complete() { /* * Commit the current transaction, if possible. * * Input: * * Returns: * TRUE if the transaction completed or FALSE if it did not. * * Globals: * $last_pg_error Cleared. * * Side Effects: * Completes the current transaction. * * Remarks: * If the transaction won't commit, roll it back instead. */ if ($this->sloppy && $this->faulting) { // The sloppy transaction had a failure. $this->sloppy_rollback(); return FALSE; // The sloppy transaction did not commit. } if (!$this->commit()) { $this->rollback('
Failed to commit transaction:'
. '
'
. htmlentities(pg_last_error($this->pg))
. '