コード例 #1
0
ファイル: mysqli.php プロジェクト: sheldon/dejavu
/**
 * This function is the central function for handling database interaction.
 * The function can be used for setting up a database connection, for running
 * a SQL query and for returning query rows. Which of these actions the
 * function will handle and what the function return data will be, is
 * determined by the $return function parameter.
 *
 * @param $return   - What to return. Options are the following constants:
 *                    DB_RETURN_CONN      a db connection handle
 *                    DB_RETURN_QUOTED    a quoted parameter
 *                    DB_RETURN_RES       result resource handle
 *                    DB_RETURN_ROW       single row as array
 *                    DB_RETURN_ROWS      all rows as arrays
 *                    DB_RETURN_ASSOC     single row as associative array
 *                    DB_RETURN_ASSOCS    all rows as associative arrays
 *                    DB_RETURN_VALUE     single row, single column
 *                    DB_RETURN_ROWCOUNT  number of selected rows
 *                    DB_RETURN_NEWID     new row id for insert query
 *                    DB_RETURN_ERROR     an error message if the query
 *                                        failed or NULL if there was no error
 *                    DB_CLOSE_CONN       close the connection, no return data
 *
 * @param $sql      - The SQL query to run or the parameter to quote if
 *                    DB_RETURN_QUOTED is used.
 *
 * @param $keyfield - When returning an array of rows, the indexes are
 *                    numerical by default (0, 1, 2, etc.). However, if
 *                    the $keyfield parameter is set, then from each
 *                    row the $keyfield index is taken as the key for the
 *                    return array. This way, you can create a direct
 *                    mapping between some id field and its row in the
 *                    return data. Mind that there is no error checking
 *                    at all, so you have to make sure that you provide
 *                    a valid $keyfield here!
 *
 * @param $flags    - Special flags for modifying the function's behavior.
 *                    These flags can be OR'ed if multiple flags are needed.
 *                    DB_NOCONNECTOK     Failure to connect is not fatal but
 *                                       lets the call return FALSE (useful
 *                                       in combination with DB_RETURN_CONN).
 *                    DB_MISSINGTABLEOK  Missing table errors not fatal.
 *                    DB_DUPFIELDNAMEOK  Duplicate field errors not fatal.
 *                    DB_DUPKEYNAMEOK    Duplicate key name errors not fatal.
 *                    DB_DUPKEYOK        Duplicate key errors not fatal.
 *
 * @return $res     - The result of the query, based on the $return parameter.
 */
function phorum_db_interact($return, $sql = NULL, $keyfield = NULL, $flags = 0)
{
    static $conn;
    // Close the database connection.
    if ($return == DB_CLOSE_CONN) {
        if (!empty($conn)) {
            mysqli_close($conn);
            $conn = null;
        }
        return;
    }
    // Setup a database connection if no database connection is available yet.
    if (empty($conn)) {
        $PHORUM = $GLOBALS['PHORUM'];
        $conn = mysqli_connect($PHORUM['DBCONFIG']['server'], $PHORUM['DBCONFIG']['user'], $PHORUM['DBCONFIG']['password'], $PHORUM['DBCONFIG']['name'], $PHORUM['DBCONFIG']['port'], $PHORUM['DBCONFIG']['socket']);
        if ($conn === FALSE) {
            if ($flags & DB_NOCONNECTOK) {
                return FALSE;
            }
            phorum_database_error('Failed to connect to the database.');
            exit;
        }
        if (!empty($PHORUM['DBCONFIG']['charset'])) {
            mysqli_query($conn, "SET NAMES '{$PHORUM['DBCONFIG']['charset']}'");
        }
        // putting this here for testing mainly
        // All of Phorum should work in strict mode
        if (!empty($PHORUM["DBCONFIG"]["strict_mode"])) {
            mysqli_query($conn, "SET SESSION sql_mode='STRICT_ALL_TABLES'");
        }
    }
    // Return a quoted parameter.
    if ($return === DB_RETURN_QUOTED) {
        return mysqli_real_escape_string($conn, $sql);
    }
    // RETURN: database connection handle
    if ($return === DB_RETURN_CONN) {
        return $conn;
    }
    // By now, we really need a SQL query.
    if ($sql === NULL) {
        trigger_error('Internal error: phorum_db_interact(): ' . 'missing sql query statement!', E_USER_ERROR);
    }
    // Execute the SQL query.
    // For queries where we are going to retrieve multiple rows, we
    // use an unuffered query result.
    if ($return === DB_RETURN_ASSOCS || $return === DB_RETURN_ROWS) {
        $res = FALSE;
        if (mysqli_real_query($conn, $sql) !== FALSE) {
            $res = mysqli_use_result($conn);
        }
    } else {
        $res = mysqli_query($conn, $sql);
    }
    // Execute the SQL query.
    if ($res === FALSE) {
        // See if the $flags tell us to ignore the error.
        $ignore_error = FALSE;
        $errno = mysqli_errno($conn);
        switch ($errno) {
            // Table does not exist.
            case 1146:
                if ($flags & DB_MISSINGTABLEOK) {
                    $ignore_error = TRUE;
                }
                break;
                // Table already exists.
            // Table already exists.
            case 1050:
                if ($flags & DB_TABLEEXISTSOK) {
                    $ignore_error = TRUE;
                }
                break;
                // Duplicate column name.
            // Duplicate column name.
            case 1060:
                if ($flags & DB_DUPFIELDNAMEOK) {
                    $ignore_error = TRUE;
                }
                break;
                // Duplicate key name.
            // Duplicate key name.
            case 1061:
                if ($flags & DB_DUPKEYNAMEOK) {
                    $ignore_error = TRUE;
                }
                break;
                // Duplicate entry for key.
            // Duplicate entry for key.
            case 1062:
                // For MySQL server versions 5.1.15 up to 5.1.20.
                // See bug #28842 (http://bugs.mysql.com/bug.php?id=28842)
            // For MySQL server versions 5.1.15 up to 5.1.20.
            // See bug #28842 (http://bugs.mysql.com/bug.php?id=28842)
            case 1582:
                if ($flags & DB_DUPKEYOK) {
                    $ignore_error = TRUE;
                }
                break;
        }
        // Handle this error if it's not to be ignored.
        if (!$ignore_error) {
            $err = mysqli_error($conn);
            // RETURN: error message or NULL
            if ($return === DB_RETURN_ERROR) {
                return $err;
            }
            // Trigger an error.
            phorum_database_error("{$err} ({$errno}): {$sql}");
            exit;
        }
    }
    // RETURN: error message or NULL
    if ($return === DB_RETURN_ERROR) {
        return NULL;
    }
    // RETURN: query resource handle
    if ($return === DB_RETURN_RES) {
        return $res;
    } elseif ($return === DB_RETURN_ROWCOUNT) {
        return $res ? mysqli_num_rows($res) : 0;
    } elseif ($return === DB_RETURN_ROW || $return === DB_RETURN_ROWS || $return === DB_RETURN_VALUE) {
        // Keyfields are only valid for DB_RETURN_ROWS.
        if ($return !== DB_RETURN_ROWS) {
            $keyfield = NULL;
        }
        $rows = array();
        if ($res) {
            while ($row = mysqli_fetch_row($res)) {
                if ($keyfield === NULL) {
                    $rows[] = $row;
                } else {
                    $rows[$row[$keyfield]] = $row;
                }
            }
        }
        // Return all rows.
        if ($return === DB_RETURN_ROWS) {
            /* Might be FALSE in case of ignored errors. */
            if (!is_bool($res)) {
                mysqli_free_result($res);
            }
            return $rows;
        }
        // Return a single row.
        if ($return === DB_RETURN_ROW) {
            if (count($rows) == 0) {
                return NULL;
            } else {
                return $rows[0];
            }
        }
        // Return a single value.
        if (count($rows) == 0) {
            return NULL;
        } else {
            return $rows[0][0];
        }
    } elseif ($return === DB_RETURN_ASSOC || $return === DB_RETURN_ASSOCS) {
        // Keyfields are only valid for DB_RETURN_ASSOCS.
        if ($return !== DB_RETURN_ASSOCS) {
            $keyfield = NULL;
        }
        $rows = array();
        if ($res) {
            while ($row = mysqli_fetch_assoc($res)) {
                if ($keyfield === NULL) {
                    $rows[] = $row;
                } else {
                    $rows[$row[$keyfield]] = $row;
                }
            }
        }
        // Return all rows.
        if ($return === DB_RETURN_ASSOCS) {
            /* Might be FALSE in case of ignored errors. */
            if (!is_bool($res)) {
                mysqli_free_result($res);
            }
            return $rows;
        }
        // Return a single row.
        if ($return === DB_RETURN_ASSOC) {
            if (count($rows) == 0) {
                return NULL;
            } else {
                return $rows[0];
            }
        }
    } elseif ($return === DB_RETURN_NEWID) {
        return mysqli_insert_id($conn);
    }
    trigger_error('Internal error: phorum_db_interact(): ' . 'illegal return type specified!', E_USER_ERROR);
}
コード例 #2
0
ファイル: db.php プロジェクト: sleepy909/cpassman
/**
 * Write a new message to the event logging table.
 *
 * This function will automatically fill the log information with
 * user_id, ip, hostname (if hostname resolving is enabled for the log module)
 * datestamp and vroot information. Other log info can be provided throught
 * the $loginfo argument.
 *
 * @param $loginfo - An array containing logging information. This array
 *                   can contain the following fields:
 *
 *                   message     A short log message on one line.
 *                   details     Details about the log message, which can
 *                               span multiple lines. This could for example
 *                               be used for providing a debug backtrace.
 *                   source      The source of the log message. This is a
 *                               free 32 char text field, which can be used
 *                               to specifiy what part of Phorum generated the
 *                               log message (e.g. "mod_smileys"). If no
 *                               source is provided, the "phorum_page"
 *                               constant will be used instead.
 *                   category    A high level category for the message.
 *                               Options for this field are:
 *                               EVENTLOG_CAT_APPLICATION (default)
 *                               EVENTLOG_CAT_DATABASE
 *                               EVENTLOG_CAT_SECURITY
 *                               EVENTLOG_CAT_SYSTEM
 *                               EVENTLOG_CAT_MODULE
 *                   loglevel    This indicates the severety of the message.
 *                               Options for this field are:
 *                               EVENTLOG_LVL_DEBUG
 *                                 Messages that are used by programmers
 *                                 for tracking low level Phorum operation.
 *                               EVENTLOG_LVL_INFO
 *                                 Messages that provide logging for events
 *                                 that occur during normal operation. These
 *                                 messages could be harvested for usage
 *                                 reporting and other types of reports.
 *                               EVENTLOG_LVL_WARNING
 *                                 Warning messages do not indicate errors,
 *                                 but they do report events that are not
 *                                 considered to belong to normal operation
 *                                 (e.g. a user which enters a wrong password
 *                                 or a duplicate message being posted).
 *                               EVENTLOG_LVL_ERROR
 *                                 Error messages indicate non urgent failures
 *                                 in Phorum operation. These should be
 *                                 relayed to administrators and/or developers
 *                                 to have them solved.
 *                               EVENTLOG_LVL_ALERT
 *                                 Alert messages indicate errors which should
 *                                 be corrected as soon as possible (e.g. loss
 *                                 of network connectivity or a full disk).
 *                                 These should be relayed to the system
 *                                 administrator).
 *
 *                   vroot       vroot for which a message is generated.
 *                   forum_id    forum_id for which a message is generated.
 *                   thread_id   thread_id for which a message is generated
 *                   message_id  message_id for which a message is generated
 *
 *                   user_id     Filled automatically, but can be overridden
 *                   ip          Filled automatically, but can be overridden
 *                   hostname    Filled automatically, but can be overridden
 *                   datestamp   Filled automatically, but can be overridden
 */
function event_logging_writelog($loginfo)
{
    $PHORUM = $GLOBALS["PHORUM"];
    // Check the minimum log level. Only write to the log if the
    // log level of the event is at or above the configured minimum.
    $lvl = isset($loginfo["loglevel"]) ? (int) $loginfo["loglevel"] : 0;
    if ($lvl < $PHORUM["mod_event_logging"]["min_log_level"]) {
        return;
    }
    // The record that we will insert in the database.
    $record = array();
    // Handle messages that exceed the maximum message length.
    if ($loginfo["message"] !== NULL && strlen($loginfo["message"]) > 255) {
        if (!isset($loginfo["details"])) {
            $loginfo["details"] = '';
        }
        $loginfo["details"] = "Message:\n\n{$loginfo["message"]}\n\n" . $loginfo["details"];
        $loginfo["message"] = substr($loginfo["message"], 0, 100) . "... (see event details for the full message)\n";
    } elseif (isset($loginfo["details"])) {
        $loginfo["details"] = "Message:\n\n{$loginfo["message"]}\n\n" . $loginfo["details"];
    }
    // Add the fields from the $loginfo argument.
    foreach ($loginfo as $key => $val) {
        switch ($key) {
            case "datestamp":
            case "user_id":
            case "vroot":
            case "forum_id":
            case "thread_id":
            case "message_id":
            case "category":
            case "loglevel":
                settype($val, "int");
                $record[$key] = $val;
                break;
            case "message":
            case "details":
            case "source":
            case "ip":
            case "hostname":
                $record[$key] = "'" . phorum_db_interact(DB_RETURN_QUOTED, $val) . "'";
                break;
            default:
                phorum_database_error("event_logging_log(): Illegal key " . "field \"{$key}\" in the \$loginfo argument");
        }
    }
    // Add the message source.
    $from_module = FALSE;
    if (!isset($record["source"])) {
        list($source, $from_module) = event_logging_find_source(1);
        $record["source"] = "'" . phorum_db_interact(DB_RETURN_QUOTED, $source) . "'";
    }
    // Add the category.
    if (!isset($record["category"])) {
        $record["category"] = $from_module ? EVENTLOG_CAT_MODULE : EVENTLOG_CAT_APPLICATION;
    }
    // Add the datestamp.
    if (!isset($record["datestamp"])) {
        $record["datestamp"] = time();
    }
    // Add the IP address for the current visitor.
    if (!isset($record["ip"]) && isset($_SERVER["REMOTE_ADDR"])) {
        $ip = $_SERVER["REMOTE_ADDR"];
        $record["ip"] = "'" . phorum_db_interact(DB_RETURN_QUOTED, $ip) . "'";
    }
    // Add the hostname for the current visitor.
    if (!isset($record["hostname"]) && isset($record["ip"]) && $PHORUM["mod_event_logging"]["resolve_hostnames"]) {
        $hostname = gethostbyaddr($ip);
        if ($hostname != $ip) {
            $record["hostname"] = "'" . phorum_db_interact(DB_RETURN_QUOTED, $hostname) . "'";
        }
    }
    // Add the user_id in case the visitor is an authenticated user.
    if (!isset($record["user_id"]) && isset($PHORUM["user"]["user_id"]) && $PHORUM["user"]["user_id"]) {
        $record["user_id"] = $PHORUM["user"]["user_id"];
    }
    // Add the current vroot.
    if (!isset($record["vroot"]) && isset($PHORUM["vroot"])) {
        $record["vroot"] = $PHORUM["vroot"];
    }
    // Insert the logging record in the database.
    phorum_db_interact(DB_RETURN_RES, "INSERT INTO {$PHORUM["event_logging_table"]}\n                (" . implode(', ', array_keys($record)) . ")\n         VALUES (" . implode(', ', $record) . ")", NULL, DB_MASTERQUERY);
}
コード例 #3
0
ファイル: PhorumSqliteDB.php プロジェクト: samuell/Core
 /**
  * This method is the central method for handling database
  * interaction. The method can be used for setting up a database
  * connection, for running a SQL query and for returning query rows.
  * Which of these actions the method will handle and what the method
  * return data will be, is determined by the $return method parameter.
  *
  * @param $return   - What to return. Options are the following constants:
  *                    DB_RETURN_CONN      a db connection handle
  *                    DB_RETURN_QUOTED    a quoted parameter
  *                    DB_RETURN_RES       result resource handle
  *                    DB_RETURN_ROW       single row as array
  *                    DB_RETURN_ROWS      all rows as arrays
  *                    DB_RETURN_ASSOC     single row as associative array
  *                    DB_RETURN_ASSOCS    all rows as associative arrays
  *                    DB_RETURN_VALUE     single row, single column
  *                    DB_RETURN_ROWCOUNT  number of selected rows
  *                    DB_RETURN_NEWID     new row id for insert query
  *                    DB_RETURN_ERROR     an error message if the query
  *                                        failed or NULL if there was
  *                                        no error
  *                    DB_CLOSE_CONN       close the connection, no
  *                                        return data
  *
  * @param $sql      - The SQL query to run or the parameter to quote if
  *                    DB_RETURN_QUOTED is used.
  *
  * @param $keyfield - When returning an array of rows, the indexes are
  *                    numerical by default (0, 1, 2, etc.). However, if
  *                    the $keyfield parameter is set, then from each
  *                    row the $keyfield index is taken as the key for the
  *                    return array. This way, you can create a direct
  *                    mapping between some id field and its row in the
  *                    return data. Mind that there is no error checking
  *                    at all, so you have to make sure that you provide
  *                    a valid $keyfield here!
  *
  * @param $flags    - Special flags for modifying the method's behavior.
  *                    These flags can be OR'ed if multiple flags are needed.
  *                    DB_NOCONNECTOK     Failure to connect is not fatal
  *                                       but lets the call return FALSE
  *                                       (useful in combination with
  *                                       DB_RETURN_CONN).
  *                    DB_MISSINGTABLEOK  Missing table errors not fatal.
  *                    DB_DUPFIELDNAMEOK  Duplicate field errors not fatal.
  *                    DB_DUPKEYNAMEOK    Duplicate key name errors
  *                                       not fatal.
  *                    DB_DUPKEYOK        Duplicate key errors not fatal.
  *
  * @param $limit    - The maximum number of rows to return.
  * @param $offset   - The number of rows to skip in the result set,
  *                    before returning rows to the caller.
  *
  * @return $res     - The result of the query, based on the $return
  *                    parameter.
  */
 public function interact($return, $sql = NULL, $keyfield = NULL, $flags = 0, $limit = 0, $offset = 0)
 {
     global $PHORUM;
     static $conn;
     // Close the database connection.
     if ($return == DB_CLOSE_CONN) {
         // PDO disconnects when the PDO object is destroyed.
         $conn = NULL;
         return;
     }
     // Setup a database connection if no database connection is
     // available yet.
     if (empty($conn)) {
         if (empty($PHORUM['DBCONFIG']['dbfile'])) {
             print 'The Phorum database configuration does not define ' . 'the path for the sqlite database file in the ' . '"dbfile" option.';
             exit;
         }
         try {
             $conn = new PDO('sqlite:' . $PHORUM['DBCONFIG']['dbfile']);
         } catch (PDOException $e) {
             if ($flags & DB_NOCONNECTOK) {
                 return FALSE;
             }
             phorum_database_error('Failed to open the database: ' . $e->getMessage());
             exit;
         }
     }
     // RETURN: database connection handle.
     if ($return === DB_RETURN_CONN) {
         return $conn;
     }
     // Return a quoted parameter.
     if ($return === DB_RETURN_QUOTED) {
         $quoted = $conn->quote($sql);
         // PDO adds the outer single quotes, but our DB layer adds those
         // on its own. Strip the quotes from the quoted string.
         $quoted = preg_replace('/(?:^\'|\'$)/', '', $quoted);
         return $quoted;
     }
     // By now, we really need a SQL query.
     if ($sql === NULL) {
         trigger_error(__METHOD__ . ': Internal error: ' . 'missing sql query statement!', E_USER_ERROR);
     }
     // Apply limit and offset to the query.
     settype($limit, 'int');
     settype($offset, 'int');
     if ($limit > 0) {
         $sql .= " LIMIT {$limit}";
     }
     if ($offset > 0) {
         $sql .= " OFFSET {$offset}";
     }
     // Tweak the syntax of the queries.
     $sql = preg_replace('/^(INSERT|UPDATE)\\s+IGNORE\\s/i', '\\1 OR IGNORE ', $sql);
     // Execute the SQL query.
     $res = $conn->query($sql);
     if ($res === FALSE) {
         // An error has occurred. Fetch the error code and message.
         $error_info = $conn->errorInfo();
         $errno = $error_info[1];
         $error = $error_info[2];
         // See if the $flags tell us to ignore the error.
         $ignore_error = FALSE;
         if (strstr($error, "no such table") && $flags & DB_MISSINGTABLEOK) {
             $ignore_error = TRUE;
         }
         if ($errno == 19 && $flags & DB_DUPKEYOK) {
             $ignore_error = TRUE;
         }
         #
         #            if (strstr($exec_error, "already exists") &&
         #                substr($exec_error, 0, 5) == 'table' &&
         #                ($flags & DB_TABLEEXISTSOK)) {
         #                $ignore_error = TRUE;
         #            }
         #
         #            if (strstr($exec_error, "already exists") &&
         #                substr($exec_error, 0, 5) == 'index' &&
         #                ($flags & DB_DUPKEYNAMEOK)) {
         #                $ignore_error = TRUE;
         #            }
         ##### HURDLE: no alter table for Sqlite2, which is the version supported
         ##### by php5-sqlite. Maybe PDO can be used, which seems to support Sqlite3.
         #####  // Duplicate column name.
         #####  case '42701':
         #####    if ($flags & DB_DUPFIELDNAMEOK) $ignore_error = TRUE;
         #####    break;
         #####
         #####  // Duplicate entry for key.
         #####  case '23505':
         #####    if ($flags & DB_DUPKEYOK) {
         #####        $ignore_error = TRUE;
         #####
         #####        # the code expects res to have no value upon error
         #####        $res = NULL;
         #####    }
         #####    break;
         // Handle this error if it's not to be ignored.
         if (!$ignore_error) {
             // RETURN: error message or NULL
             if ($return === DB_RETURN_ERROR) {
                 return $error;
             }
             // Trigger an error.
             phorum_database_error("{$error} ({$errno}): {$sql}");
             exit;
         }
     }
     // RETURN: NULL (no error)
     if ($return === DB_RETURN_ERROR) {
         return NULL;
     }
     // RETURN: query resource handle
     if ($return === DB_RETURN_RES) {
         return $res;
     }
     // RETURN: number of rows
     if ($return === DB_RETURN_ROWCOUNT) {
         if (!$res) {
             return 0;
         }
         return $res->rowCount();
     }
     // RETURN: array rows or single value
     if ($return === DB_RETURN_ROW || $return === DB_RETURN_ROWS || $return === DB_RETURN_VALUE) {
         // Keyfields are only valid for DB_RETURN_ROWS.
         if ($return !== DB_RETURN_ROWS) {
             $keyfield = NULL;
         }
         $rows = array();
         if ($res) {
             while ($row = $res->fetch(PDO::FETCH_NUM)) {
                 if ($keyfield === NULL) {
                     $rows[] = $row;
                 } else {
                     $rows[$row[$keyfield]] = $row;
                 }
             }
         }
         // Return all rows.
         if ($return === DB_RETURN_ROWS) {
             return $rows;
         }
         // Return a single row.
         if ($return === DB_RETURN_ROW) {
             if (count($rows) == 0) {
                 return NULL;
             } else {
                 return $rows[0];
             }
         }
         // Return a single value.
         if (count($rows) == 0) {
             return NULL;
         } else {
             return $rows[0][0];
         }
     }
     // RETURN: associative array rows
     if ($return === DB_RETURN_ASSOC || $return === DB_RETURN_ASSOCS) {
         // Keyfields are only valid for DB_RETURN_ASSOCS.
         if ($return !== DB_RETURN_ASSOCS) {
             $keyfield = NULL;
         }
         $rows = array();
         if ($res) {
             while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
                 if ($keyfield === NULL) {
                     $rows[] = $row;
                 } else {
                     $rows[$row[$keyfield]] = $row;
                 }
             }
         }
         // Return all rows.
         if ($return === DB_RETURN_ASSOCS) {
             return $rows;
         }
         // Return a single row.
         if ($return === DB_RETURN_ASSOC) {
             if (count($rows) == 0) {
                 return NULL;
             } else {
                 return $rows[0];
             }
         }
     }
     // RETURN: new id after inserting a new record
     if ($return === DB_RETURN_NEWID) {
         return $conn->lastInsertId();
     }
     trigger_error(__METHOD__ . ': Internal error: ' . 'illegal return type specified!', E_USER_ERROR);
 }
コード例 #4
0
ファイル: mysql.php プロジェクト: sleepy909/cpassman
/**
 * This function is the central function for handling database interaction.
 * The function can be used for setting up a database connection, for running
 * a SQL query and for returning query rows. Which of these actions the
 * function will handle and what the function return data will be, is
 * determined by the $return function parameter.
 *
 * @param $return   - What to return. Options are the following constants:
 *                    DB_RETURN_CONN      a db connection handle
 *                    DB_RETURN_QUOTED    a quoted parameter
 *                    DB_RETURN_RES       result resource handle
 *                    DB_RETURN_ROW       single row as array
 *                    DB_RETURN_ROWS      all rows as arrays
 *                    DB_RETURN_ASSOC     single row as associative array
 *                    DB_RETURN_ASSOCS    all rows as associative arrays
 *                    DB_RETURN_VALUE     single row, single column
 *                    DB_RETURN_ROWCOUNT  number of selected rows
 *                    DB_RETURN_NEWID     new row id for insert query
 *                    DB_RETURN_ERROR     an error message if the query
 *                                        failed or NULL if there was no error
 *                    DB_CLOSE_CONN       close the connection, no return data
 *
 * @param $sql      - The SQL query to run or the parameter to quote if
 *                    DB_RETURN_QUOTED is used.
 *
 * @param $keyfield - When returning an array of rows, the indexes are
 *                    numerical by default (0, 1, 2, etc.). However, if
 *                    the $keyfield parameter is set, then from each
 *                    row the $keyfield index is taken as the key for the
 *                    return array. This way, you can create a direct
 *                    mapping between some id field and its row in the
 *                    return data. Mind that there is no error checking
 *                    at all, so you have to make sure that you provide
 *                    a valid $keyfield here!
 *
 * @param $flags    - Special flags for modifying the function's behavior.
 *                    These flags can be OR'ed if multiple flags are needed.
 *                    DB_NOCONNECTOK     Failure to connect is not fatal but
 *                                       lets the call return FALSE (useful
 *                                       in combination with DB_RETURN_CONN).
 *                    DB_MISSINGTABLEOK  Missing table errors not fatal.
 *                    DB_DUPFIELDNAMEOK  Duplicate field errors not fatal.
 *                    DB_DUPKEYNAMEOK    Duplicate key name errors not fatal.
 *                    DB_DUPKEYOK        Duplicate key errors not fatal.
 *
 * @return $res     - The result of the query, based on the $return parameter.
 */
function phorum_db_interact($return, $sql = NULL, $keyfield = NULL, $flags = 0)
{
    static $conn;
    // Close the database connection.
    if ($return == DB_CLOSE_CONN) {
        if (!empty($conn)) {
            mysql_close($conn);
            $conn = null;
        }
        return;
    }
    // Setup a database connection if no database connection is available yet.
    if (empty($conn)) {
        $PHORUM = $GLOBALS['PHORUM'];
        $conn = mysql_connect($PHORUM['DBCONFIG']['server'], $PHORUM['DBCONFIG']['user'], $PHORUM['DBCONFIG']['password'], TRUE);
        if ($conn === FALSE) {
            if ($flags & DB_NOCONNECTOK) {
                return FALSE;
            }
            phorum_database_error('Failed to connect to the database.');
            exit;
        }
        if (mysql_select_db($PHORUM['DBCONFIG']['name'], $conn) === FALSE) {
            if ($flags & DB_NOCONNECTOK) {
                return FALSE;
            }
            phorum_database_error('Failed to select the database.');
            exit;
        }
        if (!empty($PHORUM['DBCONFIG']['charset'])) {
            mysql_query("SET NAMES '{$PHORUM['DBCONFIG']['charset']}'", $conn);
        }
    }
    // RETURN: database connection handle
    if ($return === DB_RETURN_CONN) {
        return $conn;
    }
    // Return a quoted parameter.
    if ($return === DB_RETURN_QUOTED) {
        return mysql_real_escape_string($sql, $conn);
    }
    // By now, we really need a SQL query.
    if ($sql === NULL) {
        trigger_error('Internal error: phorum_db_interact(): ' . 'missing sql query statement!', E_USER_ERROR);
    }
    // Execute the SQL query.
    $tries = 0;
    $res = false;
    while ($res === FALSE) {
        // For queries where we are going to retrieve multiple rows, we
        // use an unuffered query result.
        $res = $return === DB_RETURN_ASSOCS || $return === DB_RETURN_ROWS ? mysql_unbuffered_query($sql, $conn) : mysql_query($sql, $conn);
        if ($res === FALSE) {
            $errno = mysql_errno($conn);
            // if we have an error due to a transactional storage engine,
            // retry the query for those errors up to 2 more times
            if ($tries < 3 && ($errno == 1422 || $errno == 1213 || $errno == 1205)) {
                // 1205 Lock wait timeout
                $tries++;
            } else {
                // See if the $flags tell us to ignore the error.
                $ignore_error = FALSE;
                switch ($errno) {
                    // Table does not exist.
                    case 1146:
                        if ($flags & DB_MISSINGTABLEOK) {
                            $ignore_error = TRUE;
                        }
                        break;
                        // Table already exists.
                    // Table already exists.
                    case 1050:
                        if ($flags & DB_TABLEEXISTSOK) {
                            $ignore_error = TRUE;
                        }
                        break;
                        // Duplicate column name.
                    // Duplicate column name.
                    case 1060:
                        if ($flags & DB_DUPFIELDNAMEOK) {
                            $ignore_error = TRUE;
                        }
                        break;
                        // Duplicate key name.
                    // Duplicate key name.
                    case 1061:
                        if ($flags & DB_DUPKEYNAMEOK) {
                            $ignore_error = TRUE;
                        }
                        break;
                        // Duplicate entry for key.
                    // Duplicate entry for key.
                    case 1062:
                        // For MySQL server versions 5.1.15 up to 5.1.20.
                        // See bug #28842 (http://bugs.mysql.com/bug.php?id=28842)
                    // For MySQL server versions 5.1.15 up to 5.1.20.
                    // See bug #28842 (http://bugs.mysql.com/bug.php?id=28842)
                    case 1582:
                        if ($flags & DB_DUPKEYOK) {
                            $ignore_error = TRUE;
                        }
                        break;
                }
                // Handle this error if it's not to be ignored.
                if (!$ignore_error) {
                    $err = mysql_error($conn);
                    // RETURN: error message or NULL
                    if ($return === DB_RETURN_ERROR) {
                        return $err;
                    }
                    // Trigger an error.
                    phorum_database_error("{$err} ({$errno}): {$sql}");
                    exit;
                }
                // break while
                break;
            }
        }
    }
    if ($res === FALSE) {
    }
    // RETURN: error message or NULL
    if ($return === DB_RETURN_ERROR) {
        return NULL;
    }
    // RETURN: query resource handle
    if ($return === DB_RETURN_RES) {
        return $res;
    } elseif ($return === DB_RETURN_ROWCOUNT) {
        return $res ? mysql_num_rows($res) : 0;
    } elseif ($return === DB_RETURN_ROW || $return === DB_RETURN_ROWS || $return === DB_RETURN_VALUE) {
        // Keyfields are only valid for DB_RETURN_ROWS.
        if ($return !== DB_RETURN_ROWS) {
            $keyfield = NULL;
        }
        $rows = array();
        if ($res) {
            while ($row = mysql_fetch_row($res)) {
                if ($keyfield === NULL) {
                    $rows[] = $row;
                } else {
                    $rows[$row[$keyfield]] = $row;
                }
            }
        }
        // Return all rows.
        if ($return === DB_RETURN_ROWS) {
            return $rows;
        }
        // Return a single row.
        if ($return === DB_RETURN_ROW) {
            if (count($rows) == 0) {
                return NULL;
            } else {
                return $rows[0];
            }
        }
        // Return a single value.
        if (count($rows) == 0) {
            return NULL;
        } else {
            return $rows[0][0];
        }
    } elseif ($return === DB_RETURN_ASSOC || $return === DB_RETURN_ASSOCS) {
        // Keyfields are only valid for DB_RETURN_ASSOCS.
        if ($return !== DB_RETURN_ASSOCS) {
            $keyfield = NULL;
        }
        $rows = array();
        if ($res) {
            while ($row = mysql_fetch_assoc($res)) {
                if ($keyfield === NULL) {
                    $rows[] = $row;
                } else {
                    $rows[$row[$keyfield]] = $row;
                }
            }
        }
        // Return all rows.
        if ($return === DB_RETURN_ASSOCS) {
            return $rows;
        }
        // Return a single row.
        if ($return === DB_RETURN_ASSOC) {
            if (count($rows) == 0) {
                return NULL;
            } else {
                return $rows[0];
            }
        }
    } elseif ($return === DB_RETURN_NEWID) {
        return mysql_insert_id($conn);
    }
    trigger_error('Internal error: phorum_db_interact(): ' . 'illegal return type specified!', E_USER_ERROR);
}