Esempio n. 1
0
/**
 * This function can be used to safely write a file to disk.
 *
 * For writing the file, a swap file is used during the write phase.
 * Only if writing the swap file fully succeeded without running
 * into errors, the swap file is moved to its final location.
 * This way, we cannot get broken data because of scripts that crash
 * during writing or because of disks that are full.
 *
 * @param string $file
 *     The name of the file to write to.
 *
 * @param string $data
 *     The data to put in the file.
 *
 * @return boolean
 *     TRUE in case the file was written successfully to disk.
 *     FALSE if writing the file failed. The function
 *     {@link phorum_api_error_message()} can be used to retrieve
 *     information about the error that occurred.
 */
function phorum_api_write_file($file, $data)
{
    global $PHORUM;
    // Reset error storage.
    $PHORUM['API']['errno'] = NULL;
    $PHORUM['API']['error'] = NULL;
    ini_set('track_errors', 1);
    // Generate the swap file name.
    $stamp = preg_replace('/\\D/', '', microtime());
    $swpfile = $file . '.swp' . $stamp;
    // Open the swap file.
    $fp = @fopen($swpfile, 'w');
    if (!$fp) {
        @unlink($swpfile);
        return phorum_api_error(PHORUM_ERRNO_ERROR, "Cannot create swap file \"{$swpfile}\": {$php_errormsg}");
    }
    // Write file data to disk.
    @fputs($fp, $data);
    // Close the swap file.
    if (!@fclose($fp)) {
        @unlink($swpfile);
        return phorum_api_error(PHORUM_ERRNO_ERROR, "Error on closing swap file \"{$swpfile}\": disk full?");
    }
    // A special check on the created outputfile. We have seen strange
    // things happen on Windows2000 where the webserver could not read
    // the file it just had written :-/
    if (!($fp = @fopen($swpfile, 'r'))) {
        @unlink($swpfile);
        return phorum_api_error(PHORUM_ERRNO_ERROR, "Cannot read swap file \"{$swpfile}\", although it was just " . "written to disk. This is probably due to a problem with " . "the file permissions for the storage directory.");
    }
    @fclose($fp);
    // Move the swap file to its final location.
    if (!@rename($swpfile, $file)) {
        @unlink($swpfile);
        return phorum_api_error(PHORUM_ERRNO_ERROR, "Cannot move swap file \"{$swpfile}\": {$php_errormsg}");
    }
    return TRUE;
}
Esempio n. 2
0
 /**
  * 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)
 {
     static $conn;
     static $querytrack;
     // Close the database connection.
     if ($return == DB_CLOSE_CONN) {
         if (!empty($conn)) {
             mysqli_close($conn);
             $conn = null;
         }
         return;
     }
     $debug = empty($GLOBALS['PHORUM']['DBCONFIG']['dbdebug']) ? 0 : $GLOBALS['PHORUM']['DBCONFIG']['dbdebug'];
     if (!empty($debug)) {
         if (!isset($querytrack) || !is_array($querytrack)) {
             $querytrack = array('count' => 0, 'time' => 0, 'queries' => array());
         }
     }
     // Setup a database connection if no database connection is
     // available yet.
     if (empty($conn)) {
         global $PHORUM;
         // we suppress errors from the mysqli_connect command as errors
         // are catched differently.
         $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_api_error(PHORUM_ERRNO_DATABASE, 'Failed to connect to the database.');
             exit;
         }
         if (!empty($PHORUM['DBCONFIG']['charset'])) {
             $set_names = "SET NAMES '{$PHORUM['DBCONFIG']['charset']}'";
             mysqli_query($conn, $set_names);
             if ($debug) {
                 $querytrack['count'] += 2;
                 if ($debug > 1) {
                     $querytrack['queries'][] = array('number' => '001', 'query' => htmlspecialchars($set_names), 'raw_query' => $set_names, 'time' => '0.000');
                 }
             }
         }
         // 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: 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(__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}";
     }
     // Execute the SQL query.
     $tries = 0;
     $res = FALSE;
     while ($res === FALSE && $tries < 3) {
         // Time the query for debug level 2 and up.
         if ($debug > 1) {
             $t1 = microtime(TRUE);
         }
         // 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);
         }
         if ($debug) {
             $querytrack['count']++;
             if ($debug > 1) {
                 $t2 = microtime(TRUE);
                 $time = sprintf("%0.3f", $t2 - $t1);
                 $querytrack['time'] += $time;
                 $querytrack['queries'][] = array('number' => sprintf("%03d", $querytrack['count']), 'query' => htmlspecialchars($sql), 'raw_query' => $sql, 'time' => $time);
             }
             $GLOBALS['PHORUM']['DATA']['DBDEBUG'] = $querytrack;
         }
         // Handle errors.
         if ($res === FALSE) {
             $errno = mysqli_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 = mysqli_error($conn);
                     // RETURN: error message.
                     if ($return === DB_RETURN_ERROR) {
                         return $err;
                     }
                     // Trigger an error.
                     phorum_api_error(PHORUM_ERRNO_DATABASE, "{$err} ({$errno}): {$sql}");
                     exit;
                 }
                 // break while
                 break;
             }
         }
     }
     // 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) {
         return $res ? mysqli_num_rows($res) : 0;
     }
     // 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 = 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];
         }
     }
     // 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 = 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];
             }
         }
     }
     // RETURN: new id after inserting a new record
     if ($return === DB_RETURN_NEWID) {
         return mysqli_insert_id($conn);
     }
     trigger_error(__METHOD__ . ': Internal error: ' . 'illegal return type specified!', E_USER_ERROR);
 }
Esempio n. 3
0
File: db.php Progetto: netovs/Core
/**
 * 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)
{
    global $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;
    }
    $loginfo = phorum_api_hook("event_logging_writelog", $loginfo);
    // 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_api_error(PHORUM_ERRNO_DATABASE, "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);
}
Esempio n. 4
0
 /**
  * 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) {
         if (!empty($conn)) {
             pg_close($conn);
             $conn = null;
         }
         return;
     }
     // Setup a database connection if no database connection is
     // available yet.
     if (empty($conn)) {
         // Format the connection string for pg_connect.
         $conn_string = '';
         if ($PHORUM['DBCONFIG']['server']) {
             $conn_string .= ' host=' . $PHORUM['DBCONFIG']['server'];
         }
         if ($PHORUM['DBCONFIG']['user']) {
             $conn_string .= ' user='******'DBCONFIG']['user'];
         }
         if ($PHORUM['DBCONFIG']['password']) {
             $conn_string .= ' password='******'DBCONFIG']['password'];
         }
         if ($PHORUM['DBCONFIG']['name']) {
             $conn_string .= ' dbname=' . $PHORUM['DBCONFIG']['name'];
         }
         // Try to setup a connection to the database.
         $conn = @pg_connect($conn_string, PGSQL_CONNECT_FORCE_NEW);
         if ($conn === FALSE) {
             if ($flags & DB_NOCONNECTOK) {
                 return FALSE;
             }
             phorum_api_error(PHORUM_ERRNO_DATABASE, 'Failed to connect to the database.');
             exit;
         }
         if (!empty($PHORUM['DBCONFIG']['charset'])) {
             $charset = $PHORUM['DBCONFIG']['charset'];
             pg_query($conn, "SET CLIENT_ENCODING TO '{$charset}'");
         }
     }
     // RETURN: quoted parameter.
     if ($return === DB_RETURN_QUOTED) {
         return pg_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(__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}";
     }
     // Execute the SQL query.
     if (!@pg_send_query($conn, $sql)) {
         trigger_error(__METHOD__ . ': Internal error: ' . 'pg_send_query() failed!', E_USER_ERROR);
     }
     // Check if an error occurred.
     $res = pg_get_result($conn);
     $errno = pg_result_error_field($res, PGSQL_DIAG_SQLSTATE);
     if ($errno != 0) {
         // See if the $flags tell us to ignore the error.
         $ignore_error = FALSE;
         switch ($errno) {
             // Table does not exist.
             case '42P01':
                 if ($flags & DB_MISSINGTABLEOK) {
                     $ignore_error = TRUE;
                 }
                 break;
                 // Table already exists or duplicate key name.
                 // These two cases use the same error code.
             // Table already exists or duplicate key name.
             // These two cases use the same error code.
             case '42P07':
                 if ($flags & DB_TABLEEXISTSOK) {
                     $ignore_error = TRUE;
                 }
                 if ($flags & DB_DUPKEYNAMEOK) {
                     $ignore_error = TRUE;
                 }
                 break;
                 // Duplicate column name.
             // Duplicate column name.
             case '42701':
                 if ($flags & DB_DUPFIELDNAMEOK) {
                     $ignore_error = TRUE;
                 }
                 break;
                 // Duplicate entry for key.
             // 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) {
             $errmsg = pg_result_error($res);
             // RETURN: error message
             if ($return === DB_RETURN_ERROR) {
                 return $errmsg;
             }
             // Trigger an error.
             phorum_api_error(PHORUM_ERRNO_DATABASE, "{$errmsg} ({$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) {
         return $res ? pg_num_rows($res) : 0;
     }
     // 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 = pg_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];
         }
     }
     // 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 = pg_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];
             }
         }
     }
     // RETURN: new id after inserting a new record
     if ($return === DB_RETURN_NEWID) {
         $res = pg_exec($conn, "SELECT lastval()");
         if ($res === FALSE) {
             phorum_api_error(PHORUM_ERRNO_DATABASE, 'Failed to get a lastval() result.');
         }
         $row = pg_fetch_row($res);
         if ($row === FALSE) {
             phorum_api_error(PHORUM_ERRNO_DATABASE, 'No rows returned from LASTVAL().');
         }
         return $row[0];
     }
     trigger_error(__METHOD__ . ': Internal error: ' . 'illegal return type specified!', E_USER_ERROR);
 }
Esempio n. 5
0
/**
 * @deprecated Replaced by {@link phorum_api_error()}.
 */
function phorum_api_error_set($errno, $error = NULL)
{
    return phorum_api_error($errno, $error);
}
Esempio n. 6
0
/**
 * Retrieve a Phorum file.
 *
 * This function can handle Phorum file retrieval in multiple ways:
 * either return the file to the caller or send it directly to the user's
 * browser (based on the $flags parameter). Sending it directly to the
 * browser allows for the implementation of modules that don't have to buffer
 * the full file data before sending it (a.k.a. streaming, which provides the
 * advantage of using less memory for sending files).
 *
 * @param mixed $file
 *     This is either an array containing at least the fields "file_id"
 *     and "filename" or a numerical file_id value. Note that you can
 *     use the return value of the function
 *     {@link phorum_api_file_check_read_access()} as input for this function.
 *
 * @param integer $flags
 *     These are flags that influence aspects of the function call. It is
 *     a bitflag value, so you can OR multiple flags together. Available
 *     flags for this function are: {@link PHORUM_FLAG_IGNORE_PERMS},
 *     {@link PHORUM_FLAG_GET}, {@link PHORUM_FLAG_SEND} and
 *     {@link PHORUM_FLAG_FORCE_DOWNLOAD}. The SEND flag has precedence
 *     over the GET flag.
 *
 * @return mixed
 *     On error, this function will return FALSE.
 *     The functions {@link phorum_api_error_message()} and
 *     {@link phorum_api_error_code()} can be used to retrieve information
 *     about the error that occurred.
 *
 *     If the {@link PHORUM_FLAG_SEND} flag is used, then the function will
 *     return NULL.
 *
 *     If the {@link PHORUM_FLAG_GET} flag is used, then the function
 *     will return a file description array, containing the fields "file_id",
 *     "username", "file_data", "mime_type".
 *     If the {@link $file} parameter was an array, then all fields from that
 *     array will be included as well.
 */
function phorum_api_file_retrieve($file, $flags = PHORUM_FLAG_GET)
{
    global $PHORUM;
    // Reset error storage.
    $PHORUM["API"]["errno"] = NULL;
    $PHORUM["API"]["error"] = NULL;
    // If $file is not an array, we are handling a numerical file_id.
    // In that case, first retrieve the file data through the access check
    // function. All the function flags are passed on to that function,
    // so the PHORUM_FLAG_IGNORE_PERMS flag can be set for ignoring access
    // permissions.
    if (!is_array($file)) {
        $file_id = (int) $file;
        $file = phorum_api_file_check_read_access($file_id, $flags);
        // Return in case of errors.
        if ($file === FALSE) {
            return FALSE;
        }
    }
    // A small basic check to see if we have a proper $file array.
    if (!isset($file["file_id"])) {
        trigger_error("phorum_api_file_get(): \$file parameter needs a \"file_id\" field.", E_USER_ERROR);
    }
    if (!isset($file["filename"])) {
        trigger_error("phorum_api_file_get(): \$file parameter needs a \"filename\" field.", E_USER_ERROR);
    }
    settype($file["file_id"], "int");
    /*
     * [hook]
     *     file_retrieve
     *
     * [description]
     *     This hook allows modules to handle the file data retrieval.
     *     The hook can use <literal>phorum_api_error()</literal>
     *     to return an error. Hooks should be aware that their input might
     *     not be <literal>$file</literal>, but <literal>FALSE</literal>
     *     instead, in which case they should immediately return
     *     <literal>FALSE</literal> themselves.
     *
     * [category]
     *     File storage
     *
     * [when]
     *     In
     *     <filename>include/api/file.php</filename>,
     *     right before a file attachment is retrieved from the database.
     *
     * [input]
     *     Two part array where the first element is an empty file array
     *     and the second element is the flags variable.
     *
     * [output]
     *     Same as input with file_data filled in.
     *
     */
    $file["result"] = 0;
    $file["mime_type"] = NULL;
    $file["file_data"] = NULL;
    if (isset($PHORUM["hooks"]["file_retrieve"])) {
        list($file, $flags) = phorum_api_hook("file_retrieve", array($file, $flags));
        if ($file === FALSE) {
            return FALSE;
        }
        // If a module sent the file data to the browser, then we are done.
        if ($file["result"] == PHORUM_FLAG_SEND) {
            return NULL;
        }
    }
    // If no module handled file retrieval, we will retrieve the
    // file from the Phorum database.
    if ($file["file_data"] === NULL) {
        $dbfile = $PHORUM['DB']->file_get($file["file_id"], TRUE);
        if (empty($dbfile)) {
            return phorum_api_error(PHORUM_ERRNO_NOTFOUND, "Phorum file (id {$file["file_id"]}) could not be " . "retrieved from the database.");
        }
        // Phorum stores the files in base64 format in the database, to
        // prevent problems with dumping and restoring databases.
        $file["file_data"] = base64_decode($dbfile["file_data"]);
    }
    // Set the MIME type information if it was not set by a module.
    $mime_type_verified = FALSE;
    if ($file["mime_type"] === NULL) {
        // Determine the MIME type based on the file extension using
        // the MIME types as defined in the Phorum API code.
        $extension_mime_type = phorum_api_file_get_mimetype($file["filename"]);
        // The MIME magic file to use for the fileinfo extension.
        if (!empty($PHORUM['mime_magic_file'])) {
            $mime_magic_file = $PHORUM['mime_magic_file'];
        } else {
            $mime_magic_file = NULL;
        }
        // Retrieve the MIME-type using the fileinfo extension if
        // it is available and enabled.
        if (function_exists("finfo_open") && (!isset($PHORUM['file_fileinfo_ext']) || !empty($PHORUM['file_fileinfo_ext'])) && ($finfo = @finfo_open(FILEINFO_MIME, $mime_magic_file))) {
            $file["mime_type"] = finfo_buffer($finfo, $file['file_data']);
            finfo_close($finfo);
            if ($file["mime_type"] === FALSE) {
                return phorum_api_error(PHORUM_ERRNO_ERROR, "The mime-type of file {$file["file_id"]} couldn't be " . "determined through the fileinfo-extension");
            }
            // The MIME-type for the file extension doesn't fit the
            // MIME-type as determined by the file's magic signature.
            // Because it is not safe to view this file in a browser,
            // we force a download.
            if ($extension_mime_type != $file["mime_type"]) {
                $flags = $flags | PHORUM_FLAG_FORCE_DOWNLOAD;
            }
            $mime_type_verified = TRUE;
        } else {
            $file["mime_type"] = $extension_mime_type;
        }
    }
    // If the file is not requested for downloading, then check if it is
    // safe for the browser to view the file inline. If it is not, then
    // enable the force download flag to make sure that the browser will
    // download the file instead.
    $safe_to_cache = TRUE;
    $safe_to_view = TRUE;
    if (!($flags & PHORUM_FLAG_FORCE_DOWNLOAD) && !$mime_type_verified) {
        list($safe_to_view, $safe_to_cache) = phorum_api_file_safe_to_view($file);
        if (!$safe_to_view) {
            $flags = $flags | PHORUM_FLAG_FORCE_DOWNLOAD;
        }
    }
    // Allow for post processing on the retrieved file.
    /**
     * @todo document the file_after_retrieve hook.
     */
    if (!empty($PHORUM['hooks']['file_after_retrieve'])) {
        list($file, $flags) = phorum_api_hook("file_after_retrieve", array($file, $flags));
    }
    // In "send" mode, we directly send the file contents to the browser.
    if ($flags & PHORUM_FLAG_SEND) {
        // Avoid using any output compression or handling on the sent data.
        ini_set('zlib.output_compression', '0');
        ini_set('output_handler', '');
        // Get rid of any buffered output so far (there shouldn't be any).
        phorum_api_buffer_clear();
        $time = (int) $file['add_datetime'];
        // Handle client side caching.
        if ($safe_to_cache) {
            // Check if an If-Modified-Since header is in the request. If yes,
            // then check if the file has changed, based on the date from
            // the file data. If nothing changed, then we return a 304 header,
            // to tell the browser to use the cached data.
            phorum_api_output_last_modify_time($time);
            // Send caching headers, so files can be cached by the browser.
            phorum_api_output_cache_max_age(3600 * 24 * 60);
        } else {
            phorum_api_output_cache_disable();
        }
        if ($flags & PHORUM_FLAG_FORCE_DOWNLOAD) {
            $disposition = 'attachment; ';
            $type = 'application/octet-stream';
        } else {
            $disposition = '';
            $type = $file['mime_type'];
        }
        header("Content-Disposition: " . "{$disposition}filename=\"{$file['filename']}\"");
        header("Content-Type: {$type}");
        header('Content-Length: ' . strlen($file['file_data']));
        print $file['file_data'];
        return NULL;
    } elseif ($flags & PHORUM_FLAG_GET) {
        return $file;
    } else {
        trigger_error("phorum_api_file_retrieve(): no retrieve mode specified in the " . "flags (either use PHORUM_FLAG_GET or PHORUM_FLAG_SEND).", E_USER_ERROR);
    }
}
Esempio n. 7
0
File: user.php Progetto: netovs/Core
/**
 * Create a Phorum user session.
 *
 * Before calling this function, the variable $PHORUM['use_cookies']
 * should be set to one of {@link PHORUM_NO_COOKIES},
 * {@link PHORUM_USE_COOKIES} or {@link PHORUM_REQUIRE_COOKIES}.
 *
 * Phorum does not use PHP sessions. Instead, it uses its own session
 * management system for remembering logged in users. There are
 * multiple reasons for that, amongst which are:
 *
 * - the lack of session support (on some PHP installs);
 * - missing out of the box load balancing support (sessions are normally
 *   written to local session state files, so multiple machines would not
 *   work well together);
 * - file I/O problems (both performance and file system permissions can
 *   be a problem);
 * - the amount of unneeded overhead that is caused by the PHP session system;
 * - the fact that Phorum also supports URI based sessions (without cookie).
 *
 * This function can be used to create or maintain a login session for a
 * Phorum user. A prerequisite is that an active Phorum user is set through
 * the {@link phorum_api_user_set_active_user()} function, before calling
 * this function.
 *
 * There are two session types available: {@link PHORUM_FORUM_SESSION}
 * (used for the front end application) and {@link PHORUM_ADMIN_SESSION}
 * (used for the administrative back end).
 *
 * Admin sessions are used for the administrative back end system. For
 * security reasons, the back end does not share the front end session,
 * but uses a fully separate session instead. This session does not
 * have a timeout restriction, but it does not survive closing the
 * browser. It is always tracked using a cookie, never using URI
 * authentication (for security reasons).
 *
 * The forum sessions can be split up into long term and short term sessions:
 *
 * - Long term session:
 *   The standard Phorum user session. This session is long lasting and will
 *   survive after closing the browser (unless the long term session timeout
 *   is set to zero). If tighter security is not enabled, then this session
 *   is all a user needs to fully use all forum options. This session is
 *   tracked using either a cookie or URI authentication.
 *
 * - Short term session:
 *   This session has a limited life time and will not survive closing the
 *   browser. If tighter security is enabled, then the user will not be able
 *   to use all forum functions, unless there is a short term session active
 *   (e.g. posting forum messages and reading/writing private messages are
 *   restricted). This session is tracked using a cookie. If URI authentication
 *   is in use (because of admin config or cookie-less browsers) Phorum will
 *   only look at the long term session (even in tighter security mode), since
 *   URI authentication can be considered to be short term by nature.
 *
 * @example user_login.php Handle a user forum login
 *
 * @param string $type
 *     The type of session to initialize. This must be one of
 *     {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
 *
 * @param integer $reset
 *     If it is set to 0 (zero, the default), then existing session_ids
 *     will be reused if possible.
 *
 *     If this parameter is set to PHORUM_SESSID_RESET_LOGIN, then a new
 *     session id will be generated for short term forum sessions and if
 *     cookies are disabled for some reason, for long term forum sessions
 *     as well (to prevent accidental distribution of URLs with auth info
 *     in them). This is the type of session id reset that is appropriate
 *     after handling a login action.
 *
 *     If this parameter is set to PHORUM_SESSID_RESET_ALL, then all session
 *     ids will be reset to new values. This is for example
 *     appropriate after a user changed the password (so active sessions on
 *     other computers or browsers will be ended).
 *
 * @return boolean
 *     TRUE in case the session was initialized successfully.
 *     Otherwise, FALSE will be returned. The functions
 *     {@link phorum_api_error_message()} and {@link phorum_api_error_code()}
 *     can be used to retrieve information about the error which occurred.
 */
function phorum_api_user_session_create($type, $reset = 0)
{
    global $PHORUM;
    /**
     * [hook]
     *     user_session_create
     *
     * [description]
     *     Allow modules to override Phorum's session create management or
     *     to even fully omit creating a session (for example useful
     *     if the hook <hook>user_session_restore</hook> is used
     *     to inherit an external session from some 3rd party application).
     *
     * [category]
     *     User authentication and session handling
     *
     * [when]
     *     Just before Phorum runs its own session initialization code
     *     in the user API function
     *     <literal>phorum_api_user_session_create()</literal>.
     *
     * [input]
     *     The session type for which a session must be created.
     *     This can be either <literal>PHORUM_FORUM_SESSION</literal>
     *     or <literal>PHORUM_ADMIN_SESSION</literal>.
     *
     * [output]
     *     Same as input if Phorum has to run its standard session
     *     initialization code or NULL if that code should be fully skipped.
     *
     * [example]
     *     <hookcode>
     *     function phorum_mod_foo_user_session_create($type)
     *     {
     *         global $PHORUM;
     *
     *         // Let Phorum handle admin sessions on its own.
     *         if ($type == PHORUM_ADMIN_SESSION) return $type;
     *
     *         // Override the session handling for front end forum sessions.
     *         // We could for example put the session in a standard PHP
     *         // session by first starting a PHP session if that was
     *         // not done yet...
     *         if (!session_id()) session_start();
     *
     *         // ...and then storing the user_id of the current user in the
     *         // PHP session data. The user_id is really the only thing
     *         // that needs to be remembered for a Phorum session, because
     *         // all other data for the user is stored in the database.
     *         $phorum_user_id = $PHORUM["user"]["user_id"];
     *         $_SESSION['phorum_user_id'] = $phorum_user_id;
     *
     *         // Tell Phorum not to run its own session initialization code.
     *         return NULL;
     *     }
     *     </hookcode>
     *
     *     See the <hook>user_session_restore</hook> hook for an example
     *     of how to let Phorum pick up this PHP based session.
     */
    if (isset($PHORUM['hooks']['user_session_create'])) {
        if (phorum_api_hook('user_session_create', $type) === NULL) {
            return TRUE;
        }
    }
    // Reset error storage.
    $PHORUM['API']['errno'] = NULL;
    $PHORUM['API']['error'] = NULL;
    // Check if we have a valid session type.
    if ($type != PHORUM_FORUM_SESSION && $type != PHORUM_ADMIN_SESSION) {
        trigger_error('phorum_api_user_session_create(): Illegal session type: ' . htmlspecialchars($type), E_USER_ERROR);
        return NULL;
    }
    // Check if the active Phorum user was set.
    if (empty($PHORUM['user']) || empty($PHORUM['user']['user_id'])) {
        trigger_error('phorum_api_user_session_create(): Missing user in environment', E_USER_ERROR);
        return NULL;
    }
    // Check if the user is activated.
    if ($PHORUM['user']['active'] != PHORUM_USER_ACTIVE) {
        return phorum_api_error(PHORUM_ERRNO_NOACCESS, 'The user is not (yet) activated (user id ' . $PHORUM['user']['user_id'] . ')');
    }
    // For admin sessions, check if the user has administrator rights.
    // This is also checked from phorum_api_user_set_active_user(), but
    // one can never be too sure about this.
    if ($type == PHORUM_ADMIN_SESSION && empty($PHORUM['user']['admin'])) {
        return phorum_api_error(PHORUM_ERRNO_NOACCESS, 'The user is not an administrator (user id ' . $PHORUM['user']['user_id'] . ')');
    }
    // Shortcut for checking if session ids are stored in cookies.
    // Note that the software that uses this function is responsible for
    // setting $PHORUM["use_cookies"] to PHORUM_NO_COOKIES if the client
    // does not support cookies.
    $use_cookies = isset($PHORUM['use_cookies']) && $PHORUM['use_cookies'] > PHORUM_NO_COOKIES;
    // ----------------------------------------------------------------------
    // Retrieve or generate required session id(s).
    // ----------------------------------------------------------------------
    $user = $PHORUM['user'];
    // Generate a long term session id. This one is used by all session types.
    // Create a new long term session id if no session id is available yet or
    // if a refresh was requested and cookies are disabled (with cookies
    // enabled, we always reuse the existing long term session, so the session
    // can be remembered and shared between multiple browsers / computers).
    $refresh_sessid_lt = empty($user['sessid_lt']) || !$use_cookies && $reset == PHORUM_SESSID_RESET_LOGIN || $reset == PHORUM_SESSID_RESET_ALL;
    if ($refresh_sessid_lt) {
        $sessid_lt = md5($user['username'] . microtime() . $user['password']);
        phorum_api_user_save_raw(array('user_id' => $user['user_id'], 'sessid_lt' => $sessid_lt));
        $PHORUM['user']['sessid_lt'] = $sessid_lt;
    } else {
        $sessid_lt = $user['sessid_lt'];
    }
    // For forum sessions, generate a short term session id if tight
    // security is enabled in the configuration and cookies are enabled
    // (with URI authentication, the tight security system is bypassed
    // since the user will have to login on every visit already).
    $refresh_sessid_st = FALSE;
    if ($type == PHORUM_FORUM_SESSION && !empty($PHORUM['tight_security']) && $use_cookies) {
        // How much longer is the existing short term session id valid?
        $timeleft = empty($user['sessid_st_timeout']) ? 0 : $user['sessid_st_timeout'] - time();
        // Create a new short term session id if ..
        if (empty($user['sessid_st']) || $reset) {
            // .. any type of reset was requested
            $sessid_st = md5($user['username'] . microtime() . $user['password']);
            $refresh_sessid_st = TRUE;
        } else {
            // Reuse the existing short term session id
            $sessid_st = $user['sessid_st'];
            // Have the session timeout reset if more than one third of the
            // session's life time has passed and if the session has not
            // yet expired.
            if ($timeleft > 0 && $timeleft < $PHORUM['short_session_timeout'] * 60 / 2) {
                $refresh_sessid_st = TRUE;
            }
        }
        // The session data needs updating.
        if ($refresh_sessid_st) {
            $timeout = time() + $PHORUM['short_session_timeout'] * 60;
            phorum_api_user_save_raw(array('user_id' => $user['user_id'], 'sessid_st' => $sessid_st, 'sessid_st_timeout' => $timeout));
            $PHORUM['user']['sessid_st'] = $sessid_st;
            $PHORUM['user']['sessid_st_timeout'] = $timeout;
        }
    }
    // For admin sessions, the session id is computed using the long term
    // session id and some random data that was generated at install time.
    if ($type == PHORUM_ADMIN_SESSION) {
        $sessid_admin = md5($sessid_lt . $PHORUM['admin_session_salt']);
    }
    // ----------------------------------------------------------------------
    // Route the required session id(s) to the user.
    // ----------------------------------------------------------------------
    $user = $PHORUM['user'];
    if ($type == PHORUM_FORUM_SESSION) {
        // The long term session can be stored in either a cookie or
        // URL / form posting data.
        if ($use_cookies) {
            $timeout = empty($PHORUM['session_timeout']) ? 0 : time() + 86400 * $PHORUM['session_timeout'];
            setcookie(PHORUM_SESSION_LONG_TERM, $user['user_id'] . ':' . $sessid_lt, $timeout, $PHORUM['session_path'], $PHORUM['session_domain']);
        } else {
            // Add the session id to the URL building GET variables.
            $PHORUM['DATA']['GET_VARS'][PHORUM_SESSION_LONG_TERM] = PHORUM_SESSION_LONG_TERM . '=' . urlencode($user['user_id'] . ':' . $sessid_lt);
            // Add the session id to the form POST variables.
            $PHORUM['DATA']['POST_VARS'] .= '<input type="hidden" name="' . PHORUM_SESSION_LONG_TERM . '" ' . 'value="' . $user['user_id'] . ':' . $sessid_lt . '" />';
        }
        // The short term session id is always put in a cookie.
        if ($refresh_sessid_st) {
            setcookie(PHORUM_SESSION_SHORT_TERM, $user['user_id'] . ':' . $user['sessid_st'], $user['sessid_st_timeout'], $PHORUM['session_path'], $PHORUM['session_domain']);
        }
    } elseif ($type == PHORUM_ADMIN_SESSION) {
        setcookie(PHORUM_SESSION_ADMIN, $user['user_id'] . ':' . $sessid_admin, 0, $PHORUM['session_path'], $PHORUM['session_domain']);
    }
    return TRUE;
}
Esempio n. 8
0
/**
 * Restore a previously deleted custom field.
 *
 * If a custom field is deleted, it's settings and data are not deleted.
 * The field is only flagged as deleted. This function can be used for
 * reverting the delete action.
 *
 * @param int $id
 *     The id of the custom field to restore.
 *
 * @return bool
 *     TRUE if the restore was successfull or FALSE if there was an error.
 *     The functions {@link phorum_api_error_message()} and
 *     {@link phorum_api_error_code()} can be used to retrieve information
 *     about the error that occurred.
 */
function phorum_api_custom_field_restore($id)
{
    global $PHORUM;
    settype($id, "int");
    $field = $PHORUM['DB']->custom_field_config_get($id);
    if ($field !== NULL) {
        $field['deleted'] = FALSE;
        $PHORUM['DB']->custom_field_config_set($field);
        phorum_api_custom_field_rebuild_cache();
    } else {
        return phorum_api_error(PHORUM_ERRNO_NOTFOUND, "Unable to restore custom field {$id}: no configuration found.");
    }
    return TRUE;
}
Esempio n. 9
0
/**
 * Clip a part from an image and optionally, resize it.
 *
 * This function can be used to clip a part of an image. You can specifiy
 * width and/or a height to which to scale the clipped image.
 *
 * Below, you see an image of the way in which a clip is defined by the
 * function parameters. $clip_x, $clip_y, $clip_w and $clip_h.
 * <code>
 * +------------------------------------------+
 * |                       ^                  |
 * |                       |                  |
 * |                     clip_y               |
 * |                       |                  |
 * |                       v                  |
 * |            +-------------------+   ^     |
 * |            |                   |   |     |
 * |<--clip_x-->|                   |   |     |
 * |            |      CLIPPED      |   |     |
 * |            |       IMAGE       | clip_h  |
 * |            |                   |   |     |
 * |            |                   |   |     |
 * |            +-------------------+   v     |
 * |                                          |
 * |            <-------clip_w------>         |
 * |                                          |
 * +------------------------------------------+
 * </code>
 *
 * @param string $image
 *     The raw binary image data.
 *
 * @param integer $clip_x
 *     The X-offset for the clip, where 0 indicates the left of the image.
 * @param integer $clip_y
 *     The Y-offset for the clip, where 0 indicates the top of the image.
 * @param integer $clip_w
 *     The width of the clip to take out of the image.
 * @param integer $clip_h
 *     The height of the clip to take out of the image.
 *
 * @param integer $dst_w
 *     The width for the created clip image in pixels.
 *     Use NULL or 0 (zero) to indicate that the width should be
 *     the same as the $clip_w parameter.
 * @param integer $dst_h
 *     The height for the created clip image in pixels.
 *     Use NULL or 0 (zero) to indicate that the height should be
 *     the same as the $clip_h parameter.
 *
 * @param string $method
 *     The method to use for scaling the image. By default, this function
 *     will try to autodetect a working method. Providing a $method parameter
 *     is mostly useful for debugging purposes. Available methods (in the
 *     order in which they are probed in the code) are:
 *     - imagick : using the ImageMagick library (requires extension "imagick")
 *     - gd      : using the GD library (requires extension "gd")
 *     - convert : using the ImageMagick "convert" tool (requires the
 *                 ImageMagick package to be installed on the server and does
 *                 not work in combination with some PHP safety restrictions).
 *
 * @return mixed
 *     FALSE is returned in case creating the clip image failed. The function
 *     {@link phorum_api_error_message()} can be used to retrieve information
 *     about the error that occurred.
 *
 *     An array is returned in case creating the clip image did work.
 *     This array contains the following fields:
 *     - image    : The scaled down image. NULL if no scaling was needed.
 *     - method   : The method that was used to create the thumbnail.
 *     - cur_w    : The width of the original $image.
 *     - cur_h    : The height of the original $image.
 *     - cur_mime : The MIME type of the original $image.
 *     - new_w    : The width of the scaled down image.
 *     - new_h    : The height of the scaled down image.
 *     - new_mime : The MIME type of the scaled down image,
 */
function phorum_api_image_clip($image, $clip_x = 0, $clip_y = 0, $clip_w = NULL, $clip_h = NULL, $dst_w = NULL, $dst_h = NULL, $method = NULL)
{
    global $PHORUM;
    settype($clip_x, 'int');
    settype($clip_y, 'int');
    settype($clip_w, 'int');
    settype($clip_h, 'int');
    settype($dst_w, 'int');
    settype($dst_h, 'int');
    // Reset error storage.
    $PHORUM['API']['errno'] = NULL;
    $PHORUM['API']['error'] = NULL;
    $error = NULL;
    // Initialize the return array.
    $img = array('image' => NULL, 'new_mime' => NULL);
    // Retrieve image info.
    $image_info = phorum_api_image_info($image);
    if ($image_info === FALSE) {
        return FALSE;
    }
    // Derive a name for the image type.
    switch ($image_info['type']) {
        case IMAGETYPE_JPEG:
            $type = 'jpeg';
            break;
        case IMAGETYPE_GIF:
            $type = 'gif';
            break;
        case IMAGETYPE_PNG:
            $type = 'png';
            break;
        default:
            $type = 'unknown';
            // should not occur
    }
    // The clip width and height are inherited from the image
    // width and height, unless they are set.
    if (empty($clip_w)) {
        $clip_w = $image_info['width'] - $clip_x;
    }
    if (empty($clip_h)) {
        $clip_h = $image_info['height'] - $clip_y;
    }
    // The target image width and height are inherited from the clip
    // width and height, unless they are set.
    if (empty($dst_w)) {
        $dst_w = $clip_w;
    }
    if (empty($dst_h)) {
        $dst_h = $clip_h;
    }
    // Add the image data to the return array.
    $img['cur_w'] = $image_info['width'];
    $img['cur_h'] = $image_info['height'];
    $img['new_w'] = $dst_w;
    $img['new_h'] = $dst_h;
    $img['cur_mime'] = $img['new_mime'] = $image_info['mime'];
    // Check if the requested clip fits the source image size.
    if ($clip_x + $clip_w > $img['cur_w']) {
        return phorum_api_error(PHROM_ERRNO_ERROR, "The clip X offset {$clip_x} + clip width {$clip_w} exceeds " . "the source image width {$img['cur_w']}");
    }
    if ($clip_y + $clip_h > $img['cur_h']) {
        return phorum_api_error(PHROM_ERRNO_ERROR, "The clip Y offset {$clip_y} + clip height {$clip_h} exceeds " . "the source image height {$img['cur_h']}");
    }
    // -----------------------------------------------------------------
    // Try to use the imagick library tools
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'imagick') && extension_loaded('imagick') && class_exists('Imagick')) {
        $method = NULL;
        $imagick = new Imagick();
        $imagick->readImageBlob($image);
        $imagick->flattenImages();
        $tmp = new Imagick();
        $tmp->newPseudoImage($img['cur_w'], $img['cur_h'], 'xc:white');
        $tmp->compositeImage($imagick, imagick::COMPOSITE_OVER, 0, 0);
        $imagick = $tmp;
        $imagick->cropImage($clip_w, $clip_h, $clip_x, $clip_y);
        $imagick->thumbnailImage($dst_w, $dst_h, FALSE);
        $imagick->setImagePage($clip_w, $clip_h, 0, 0);
        $imagick->setFormat("jpg");
        $img['image'] = $imagick->getImagesBlob();
        $img['new_mime'] = 'image/jpeg';
        $img['method'] = 'imagick';
        return $img;
    }
    // -----------------------------------------------------------------
    // Try to use the GD library tools
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'gd') && extension_loaded('gd') && function_exists('gd_info')) {
        // We need gd_info() to check whether GD has the required
        // image support for the type of image that we are handling.
        $gd = gd_info();
        // We always need JPEG support for the scaled down image.
        if (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) {
            $error = "GD: no JPEG support available for processing images";
        } elseif ($type == 'gif' && empty($gd['GIF Read Support']) || $type == 'jpeg' && (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) || $type == 'png' && empty($gd['PNG Support'])) {
            $error = "GD: no support available for image type \"{$type}\"";
        } else {
            // Create a GD image handler based on the image data.
            // imagecreatefromstring() spawns PHP warnings if the file cannot
            // be processed. We do not care to see that in the event logging,
            // so if event logging is loaded, we suspend it here.
            // Instead, we catch error output and try to mangle it into a
            // usable error message.
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_suspend();
            }
            ob_start();
            $original = @imagecreatefromstring($image);
            $error = ob_get_contents();
            ob_end_clean();
            $error = trim(preg_replace('!(^(?:\\w+:\\s*).*?:|in /.*$)!', '', trim($error)));
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_resume();
            }
            if (!$original) {
                if ($error == '') {
                    $error = "GD: Cannot process the {$type} image using GD";
                } else {
                    $error = 'GD: ' . $error;
                }
            } else {
                // Create the scaled image.
                $scaled = imagecreatetruecolor($dst_w, $dst_h);
                // Fill the image to have a background for transparent pixels.
                $white = imagecolorallocate($scaled, 255, 255, 255);
                imagefill($scaled, 0, 0, $white);
                // Scale the image.
                imagecopyresampled($scaled, $original, 0, 0, $clip_x, $clip_y, $dst_w, $dst_h, $clip_w, $clip_h);
                // Create the jpeg output data for the scaled image.
                ob_start();
                imagejpeg($scaled);
                $image = ob_get_contents();
                $size = ob_get_length();
                ob_end_clean();
                imagedestroy($original);
                imagedestroy($scaled);
                $img['image'] = $image;
                $img['new_mime'] = 'image/jpeg';
                $img['method'] = 'gd';
                return $img;
            }
        }
    }
    // -----------------------------------------------------------------
    // Try to use the ImageMagick "convert" tool
    // -----------------------------------------------------------------
    if ($method === NULL || $method == 'convert') {
        // Try to find the "convert" utility.
        // First, check if it is configured in the Phorum settings.
        $convert = NULL;
        if (isset($PHORUM['imagemagick_convert_path'])) {
            $path = $PHORUM['imagemagick_convert_path'];
            if (is_executable($path)) {
                $convert = $path;
            }
        }
        // Not found? Then simply use "convert" and hope that it
        // can be found in the OS' path.
        if ($convert === NULL) {
            $convert = 'convert';
        }
        // Build the command line.
        $cmd = escapeshellcmd($convert) . ' ' . '- ' . '-background white -flatten ' . "-crop {$clip_w}x{$clip_h}+{$clip_x}+{$clip_y} " . '+repage ' . '-thumbnail ' . $dst_w . 'x' . $dst_h . '\\! ' . 'jpeg:-';
        // Run the command.
        $descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
        $process = proc_open($cmd, $descriptors, $pipes);
        if ($process == FALSE) {
            $error = 'Failed to execute "convert".';
        } else {
            // Feed convert the image data on STDIN.
            fwrite($pipes[0], $image);
            fclose($pipes[0]);
            // Read the scaled image from STDOUT.
            $scaled = stream_get_contents($pipes[1]);
            fclose($pipes[1]);
            // Read errors.
            $errors = trim(stream_get_contents($pipes[2]));
            fclose($pipes[2]);
            $exit = proc_close($process);
            if ($exit == 0) {
                $img['image'] = $scaled;
                $img['new_mime'] = 'image/jpeg';
                $img['method'] = 'convert';
                return $img;
            }
            // Some error occurred.
            if ($errors == '') {
                $error = 'Got exit code ' . $exit . ' from "convert".';
            } else {
                $error = $errors;
            }
        }
    }
    // -----------------------------------------------------------------
    // Safety nets.
    // -----------------------------------------------------------------
    // Return error if one was set.
    if ($error) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, $error);
    }
    // Catch illegal methods
    if ($method !== NULL) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, 'Illegal scaling method: ' . $method);
    }
    // If we get here, then we were totally out of luck.
    return phorum_api_error(PHORUM_ERRNO_ERROR, 'No working image scaling method found');
}
Esempio n. 10
0
/**
 * This function can be used to retrieve data from a URL using
 * an HTTP GET request.
 *
 * The way in which data can be retrieved for URLs, depends a lot on
 * the features and the configuration for PHP on the system. This
 * function will try to autodetect a way that will work for the
 * running system automatically.
 *
 * @param string $url
 *     The URL to retrieve.
 *
 * @param string $method
 *     The method to use for retrieving the data. By default, this function
 *     will try to autodetect a working method. Providing a $method parameter
 *     is mostly useful for debugging purposes. Available methods (in the
 *     order in which they are probed in the code) are:
 *     - curl: using the curl library (requires extension "curl")
 *     - socket: using PHP socket programming (requires extension "sockets")
 *     - file: using fopen() (requires option "allow_url_fopen" to be enabled)
 *
 * @return string $data
 *     The data that was loaded from the URL or NULL if an error occurred.
 *     The function {@link phorum_api_error_message()} can be used to retrieve
 *     information about the error that occurred.
 */
function phorum_api_http_get($url, $method = NULL)
{
    global $PHORUM;
    // Reset error storage.
    $PHORUM['API']['errno'] = NULL;
    $PHORUM['API']['error'] = NULL;
    // For keeping track of errors in this function.
    $error = NULL;
    $fatal = FALSE;
    // -----------------------------------------------------------------
    // Try to use the CURL library tools
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'curl') && extension_loaded('curl')) {
        $method = NULL;
        $curl = @curl_init();
        if ($curl === FALSE) {
            $error = 'Failed to initialize curl request';
        } else {
            // We don't care a lot about certificates for retrieving data.
            @curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
            @curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
            // Return the header and content when doing a request.
            @curl_setopt($curl, CURLOPT_HEADER, 1);
            @curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            // We do not let the curl library follow Location: redirects.
            // Because (at the time of writing) PHP in safe mode disables
            // redirect following (to prevent redirection to file://),
            // we implement our own redirect handling here.
            @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
            // The maximum number of redirects that we want to follow.
            // This has to be limited to prevent looping.
            $max_redirects = 10;
            // Also, track URLs that we have already seen as an extra
            // anti looping system.
            $seen = array();
            $work_url = $url;
            for (;;) {
                // Only HTTP allowed.
                if (!preg_match('!^https?://!i', $work_url)) {
                    $error = "Denying non-HTTP URL: {$work_url}";
                    $fatal = TRUE;
                    break;
                }
                // Looping prevention.
                if ($max_redirects-- == 0) {
                    $error = "Bailed out after too many page redirects.";
                    $fatal = TRUE;
                    break;
                }
                if (isset($seen[$work_url])) {
                    $error = "Following the URL results in a loop.";
                    $fatal = TRUE;
                    break;
                }
                $seen[$work_url] = 1;
                // Retrieve the page.
                @curl_setopt($curl, CURLOPT_URL, $work_url);
                $data = @curl_exec($curl);
                if ($data == FALSE) {
                    $error = "The curl HTTP request failed.";
                    break;
                }
                list($header, $body) = explode("\r\n\r\n", $data, 2);
                $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
                $requested_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
                // If we got the data, we can return it.
                if ($code == 200) {
                    return $body;
                }
                // Analyze the result data to see what we should to next.
                $res = phorum_api_http_get_analyze($requested_url, $code, $header);
                // An error was returned. Bail out.
                if (isset($res['error'])) {
                    $fatal = empty($res['fatal']) ? 0 : 1;
                    $error = $res['error'];
                    break;
                }
                // A redirect was returned.
                if (isset($res['redirect'])) {
                    $work_url = $res['redirect'];
                    continue;
                }
                // Shouldn't get here.
                trigger_error('phorum_api_http_get_nalyze() returned an ' . 'unexpected result.', E_USER_ERROR);
            }
        }
    }
    // Return fatal errors. For non fatal errors, we fall through
    // to the next method.
    if ($error !== NULL && $fatal) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, $error);
    }
    // -----------------------------------------------------------------
    // Try to use a direct fsockopen call.
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'socket') && extension_loaded('sockets')) {
        $method = NULL;
        // The maximum number of redirects that we want to follow.
        // This has to be limited to prevent looping.
        $max_redirects = 10;
        // Also, track URLs that we have already seen as an extra
        // anti looping system.
        $seen = array();
        $work_url = $url;
        for (;;) {
            // Looping prevention.
            if ($max_redirects-- == 0) {
                $error = "Bailed out after too many page redirects.";
                $fatal = TRUE;
                break;
            }
            if (isset($seen[$work_url])) {
                $error = "Following the URL results in a loop.";
                $fatal = TRUE;
                break;
            }
            $seen[$work_url] = 1;
            // Format the HTTP request for retrieving the URL.
            $parsed = @parse_url(strtoupper($work_url));
            if (!isset($parsed['host'])) {
                $error = "Cannot parse URL";
                $fatal = TRUE;
                break;
            }
            $uri = preg_replace('!^\\w+://[^/]+!', '', $work_url);
            if ($uri == '') {
                $uri = '/';
            }
            $req = "GET {$uri} HTTP/1.1\r\n" . "Connection: close\r\n" . "Host: " . strtolower($parsed['host']) . "\r\n" . "\r\n";
            // Determine protocol and port for the request.
            $port = NULL;
            $proto = '';
            if (!empty($parsed['port'])) {
                $port = (int) $parsed['port'];
            }
            if (!empty($parsed['scheme'])) {
                $s = strtolower($parsed['scheme']);
                if ($s == 'http') {
                    $proto = '';
                    if ($port === NULL) {
                        $port = 80;
                    }
                } elseif ($s == 'https') {
                    if (!extension_loaded('openssl')) {
                        $error = "PHP lacks SSL support";
                        $fatal = TRUE;
                        break;
                    }
                    $proto = 'ssl://';
                    if ($port === NULL) {
                        $port = 443;
                    }
                }
            }
            if ($port === NULL) {
                $port = 80;
            }
            // Connect to the webserver.
            $fp = @fsockopen($proto . $parsed['host'], $port, $errno, $errstr, 10);
            if (!$fp) {
                $error = "Connection to server failed ({$errstr})";
                $fatal = TRUE;
                break;
            }
            // Send the HTTP request.
            fwrite($fp, $req);
            // Read the HTTP response.
            $response = '';
            while (is_resource($fp) && !feof($fp)) {
                $response .= fread($fp, 1024);
            }
            fclose($fp);
            if ($response == '') {
                $error = "No data retrieved from server";
                $fatal = TRUE;
                break;
            }
            // Parse the response.
            list($header, $body) = explode("\r\n\r\n", $response, 2);
            list($status, $header) = explode("\r\n", $header, 2);
            if (preg_match('!^HTTP/\\d+\\.\\d+\\s+(\\d+)\\s!', $status, $m)) {
                $code = $m[1];
            } else {
                $error = "Unexpected status from server ({$status})";
                $fatal = TRUE;
                break;
            }
            // If we got the data, we can return it.
            if ($code == 200) {
                // Check if we need to handle chunked transfers
                // (see RFC 2616, section 3.6.1: Chunked Transfer Coding).
                if (preg_match('/^Transfer-Encoding:\\s*chunked\\s*$/m', $header)) {
                    $unchunked = '';
                    for (;;) {
                        // Check if there is another chunk.
                        // There should be, but let's protect against
                        // bad chunked data.
                        if (strstr($body, "\r\n") === FALSE) {
                            break;
                        }
                        // Get the size of the next chunk.
                        list($sz, $rest) = explode("\r\n", $body, 2);
                        $sz = preg_replace('/;.*$/', '', $sz);
                        $sz = hexdec($sz);
                        // Size 0 indicates end of body data.
                        if ($sz == 0) {
                            break;
                        }
                        // Add the chunk to the unchunked body data.
                        $unchunked .= substr($rest, 0, $sz);
                        $body = substr($rest, $sz + 2);
                        // +2 = skip \r\n
                    }
                    // Return the unchunked body content.
                    return $unchunked;
                }
                // Return the body content.
                return $body;
            }
            // Analyze the result data to see what we should to next.
            $res = phorum_api_http_get_analyze($work_url, $code, $header);
            // An error was returned. Bail out.
            if (isset($res['error'])) {
                $fatal = empty($res['fatal']) ? 0 : 1;
                $error = $res['error'];
                break;
            }
            // A redirect was returned.
            if (isset($res['redirect'])) {
                $work_url = $res['redirect'];
                continue;
            }
            // Shouldn't get here.
            trigger_error('phorum_api_http_get_analyze() returned an ' . 'unexpected result.', E_USER_ERROR);
        }
    }
    // Return fatal errors. For non fatal errors, we fall through
    // to the next method.
    if ($error !== NULL && $fatal) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, $error);
    }
    // -----------------------------------------------------------------
    // Try to use file_get_contents
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'fopen') && ini_get('allow_url_fopen')) {
        $method = NULL;
        $track = ini_get('track_errors');
        ini_set('track_errors', TRUE);
        $php_errormsg = '';
        $contents = @file_get_contents($url);
        ini_set('track_errors', $track);
        if ($contents === FALSE || $php_errormsg != '') {
            $error = preg_replace('/(^.*?\\:\\s+|[\\r\\n])/', '', $php_errormsg);
            $error = "[{$error}]";
        } else {
            return $contents;
        }
    }
    // Return errors.
    if ($error !== NULL) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, $error);
    }
    // Catch illegal methods
    if ($method !== NULL) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, 'Illegal method: ' . $method);
    }
    return phorum_api_error(PHORUM_ERRNO_ERROR, 'No working HTTP request method found');
}