aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/acp/acp_database.php54
-rw-r--r--phpBB/includes/acp/acp_groups.php9
-rw-r--r--phpBB/includes/db/mssqlnative.php20
-rw-r--r--phpBB/includes/functions_download.php168
-rw-r--r--phpBB/includes/functions_messenger.php87
-rw-r--r--phpBB/includes/search/fulltext_mysql.php8
6 files changed, 299 insertions, 47 deletions
diff --git a/phpBB/includes/acp/acp_database.php b/phpBB/includes/acp/acp_database.php
index 0582d6204e..193dd001c0 100644
--- a/phpBB/includes/acp/acp_database.php
+++ b/phpBB/includes/acp/acp_database.php
@@ -1619,41 +1619,48 @@ class mssql_extractor extends base_extractor
function write_data_mssqlnative($table_name)
{
global $db;
- $ary_type = $ary_name = $meta_array = array();
+ $ary_type = $ary_name = array();
$ident_set = false;
$sql_data = '';
// Grab all of the data from current table.
$sql = "SELECT * FROM $table_name";
+ $db->mssqlnative_set_query_options(array('Scrollable' => SQLSRV_CURSOR_STATIC));
$result = $db->sql_query($sql);
- $retrieved_data = $db->mssqlnative_num_rows($result);
+ $retrieved_data = $db->mssqlnative_num_rows($result);
- $meta_array = sqlsrv_field_metadata($result);
- $i_num_fields = sqlsrv_num_fields($result);
-
+ if (!$retrieved_data)
+ {
+ $db->sql_freeresult($result);
+ return;
+ }
+
+ $sql = "SELECT * FROM $table_name";
+ $result_fields = $db->sql_query_limit($sql, 1);
+
+ $row = new result_mssqlnative($result_fields);
+ $i_num_fields = $row->num_fields();
+
for ($i = 0; $i < $i_num_fields; $i++)
{
- $info = $db->mssqlnative_fieldInfo($table_name, $meta_array[$i]['Name']);
- $ary_type[$i] = $info->type();
- $ary_name[$i] = $info->name();
+ $ary_type[$i] = $row->field_type($i);
+ $ary_name[$i] = $row->field_name($i);
}
+ $db->sql_freeresult($result_fields);
+
+ $sql = "SELECT 1 as has_identity
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
+ $result2 = $db->sql_query($sql);
+ $row2 = $db->sql_fetchrow($result2);
- if ($retrieved_data)
+ if (!empty($row2['has_identity']))
{
- $sql = "SELECT 1 as has_identity
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
- $result2 = $db->sql_query($sql);
- $row2 = $db->sql_fetchrow($result2);
-
- if (!empty($row2['has_identity']))
- {
- $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
- $ident_set = true;
- }
- $db->sql_freeresult($result2);
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
+ $ident_set = true;
}
+ $db->sql_freeresult($result2);
while ($row = $db->sql_fetchrow($result))
{
@@ -1664,7 +1671,8 @@ class mssql_extractor extends base_extractor
{
$str_val = $row[$ary_name[$i]];
- if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
+ // defaults to type number - better quote just to be safe, so check for is_int too
+ if (is_int($ary_type[$i]) || preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
{
$str_quote = '';
$str_empty = "''";
@@ -1705,7 +1713,7 @@ class mssql_extractor extends base_extractor
}
$db->sql_freeresult($result);
- if ($retrieved_data && $ident_set)
+ if ($ident_set)
{
$sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n";
}
diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php
index 60512c67b8..486616c33d 100644
--- a/phpBB/includes/acp/acp_groups.php
+++ b/phpBB/includes/acp/acp_groups.php
@@ -394,6 +394,15 @@ class acp_groups
}
}
+ // Validate the length of "Maximum number of allowed recipients per private message" setting.
+ // We use 16777215 as a maximum because it matches MySQL unsigned mediumint maximum value
+ // which is the lowest amongst DBMSes supported by phpBB3
+ if ($max_recipients_error = validate_data($submit_ary, array('max_recipients' => array('num', false, 0, 16777215))))
+ {
+ // Replace "error" string with its real, localised form
+ $error = array_merge($error, array_map(array(&$user, 'lang'), $max_recipients_error));
+ }
+
if (!sizeof($error))
{
// Only set the rank, colour, etc. if it's changed or if we're adding a new
diff --git a/phpBB/includes/db/mssqlnative.php b/phpBB/includes/db/mssqlnative.php
index 44d5722e4f..d6ac3b3acc 100644
--- a/phpBB/includes/db/mssqlnative.php
+++ b/phpBB/includes/db/mssqlnative.php
@@ -51,7 +51,6 @@ class result_mssqlnative
}
$this->m_row_count = count($this->m_rows);
- sqlsrv_free_stmt($queryresult);
}
private function array_to_obj($array, &$obj)
@@ -199,6 +198,7 @@ class dbal_mssqlnative extends dbal
{
var $m_insert_id = NULL;
var $last_query_text = '';
+ var $query_options = array();
/**
* Connect to server
@@ -308,10 +308,12 @@ class dbal_mssqlnative extends dbal
if ($this->query_result === false)
{
- if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query)) === false)
+ if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false)
{
$this->sql_error($query);
}
+ // reset options for next query
+ $this->query_options = array();
if (defined('DEBUG_EXTRA'))
{
@@ -598,20 +600,28 @@ class dbal_mssqlnative extends dbal
* Utility method used to retrieve number of rows
* Emulates mysql_num_rows
* Used in acp_database.php -> write_data_mssqlnative()
+ * Requires a static or keyset cursor to be definde via
+ * mssqlnative_set_query_options()
*/
function mssqlnative_num_rows($res)
{
if ($res !== false)
{
- $row = new result_mssqlnative($res);
- $num_rows = $row->num_rows();
- return $num_rows;
+ return sqlsrv_num_rows($res);
}
else
{
return false;
}
}
+
+ /**
+ * Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter.
+ */
+ function mssqlnative_set_query_options($options)
+ {
+ $this->query_options = $options;
+ }
}
?> \ No newline at end of file
diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php
index 87bf7a91a6..94d851e383 100644
--- a/phpBB/includes/functions_download.php
+++ b/phpBB/includes/functions_download.php
@@ -157,6 +157,16 @@ function send_file_to_browser($attachment, $upload_dir, $category)
trigger_error('UNABLE_TO_DELIVER_FILE');
}
+ // Make sure the database record for the filesize is correct
+ if ($size > 0 && $size != $attachment['filesize'])
+ {
+ // Update database record
+ $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
+ SET filesize = ' . (int) $size . '
+ WHERE attach_id = ' . (int) $attachment['attach_id'];
+ $db->sql_query($sql);
+ }
+
// Now the tricky part... let's dance
header('Pragma: public');
@@ -226,6 +236,16 @@ function send_file_to_browser($attachment, $upload_dir, $category)
if ($fp !== false)
{
+ // Deliver file partially if requested
+ if ($range = phpbb_http_byte_range($size))
+ {
+ fseek($fp, $range['byte_pos_start']);
+
+ send_status_line(206, 'Partial Content');
+ header('Content-Range: bytes ' . $range['byte_pos_start'] . '-' . $range['byte_pos_end'] . '/' . $range['bytes_total']);
+ header('Content-Length: ' . $range['bytes_requested']);
+ }
+
while (!feof($fp))
{
echo fread($fp, 8192);
@@ -407,3 +427,151 @@ function file_gc()
$db->sql_close();
exit;
}
+
+/**
+* HTTP range support (RFC 2616 Section 14.35)
+*
+* Allows browsers to request partial file content
+* in case a download has been interrupted.
+*
+* @param int $filesize the size of the file in bytes we are about to deliver
+*
+* @return mixed false if the whole file has to be delivered
+* associative array on success
+*/
+function phpbb_http_byte_range($filesize)
+{
+ // Only call find_range_request() once.
+ static $request_array;
+
+ if (!$filesize)
+ {
+ return false;
+ }
+
+ if (!isset($request_array))
+ {
+ $request_array = phpbb_find_range_request();
+ }
+
+ return (empty($request_array)) ? false : phpbb_parse_range_request($request_array, $filesize);
+}
+
+/**
+* Searches for HTTP range request in super globals.
+*
+* @return mixed false if no request found
+* array of strings containing the requested ranges otherwise
+* e.g. array(0 => '0-0', 1 => '123-125')
+*/
+function phpbb_find_range_request()
+{
+ $globals = array(
+ array('_SERVER', 'HTTP_RANGE'),
+ array('_ENV', 'HTTP_RANGE'),
+ );
+
+ foreach ($globals as $array)
+ {
+ $global = $array[0];
+ $key = $array[1];
+
+ // Make sure range request starts with "bytes="
+ if (isset($GLOBALS[$global][$key]) && strpos($GLOBALS[$global][$key], 'bytes=') === 0)
+ {
+ // Strip leading 'bytes='
+ // Multiple ranges can be separated by a comma
+ return explode(',', substr($GLOBALS[$global][$key], 6));
+ }
+ }
+
+ return false;
+}
+
+/**
+* Analyses a range request array.
+*
+* A range request can contain multiple ranges,
+* we however only handle the first request and
+* only support requests from a given byte to the end of the file.
+*
+* @param array $request_array array of strings containing the requested ranges
+* @param int $filesize the full size of the file in bytes that has been requested
+*
+* @return mixed false if the whole file has to be delivered
+* associative array on success
+* byte_pos_start the first byte position, can be passed to fseek()
+* byte_pos_end the last byte position
+* bytes_requested the number of bytes requested
+* bytes_total the full size of the file
+*/
+function phpbb_parse_range_request($request_array, $filesize)
+{
+ // Go through all ranges
+ foreach ($request_array as $range_string)
+ {
+ $range = explode('-', trim($range_string));
+
+ // "-" is invalid, "0-0" however is valid and means the very first byte.
+ if (sizeof($range) != 2 || $range[0] === '' && $range[1] === '')
+ {
+ continue;
+ }
+
+ if ($range[0] === '')
+ {
+ // Return last $range[1] bytes.
+
+ if (!$range[1])
+ {
+ continue;
+ }
+
+ if ($range[1] >= $filesize)
+ {
+ return false;
+ }
+
+ $first_byte_pos = $filesize - (int) $range[1];
+ $last_byte_pos = $filesize - 1;
+ }
+ else
+ {
+ // Return bytes from $range[0] to $range[1]
+
+ $first_byte_pos = (int) $range[0];
+ $last_byte_pos = (int) $range[1];
+
+ if ($last_byte_pos && $last_byte_pos < $first_byte_pos)
+ {
+ // The requested range contains 0 bytes.
+ continue;
+ }
+
+ if ($first_byte_pos >= $filesize)
+ {
+ // Requested range not satisfiable
+ return false;
+ }
+
+ // Adjust last-byte-pos if it is absent or greater than the content.
+ if ($range[1] === '' || $last_byte_pos >= $filesize)
+ {
+ $last_byte_pos = $filesize - 1;
+ }
+ }
+
+ // We currently do not support range requests that end before the end of the file
+ if ($last_byte_pos != $filesize - 1)
+ {
+ continue;
+ }
+
+ return array(
+ 'byte_pos_start' => $first_byte_pos,
+ 'byte_pos_end' => $last_byte_pos,
+ 'bytes_requested' => $last_byte_pos - $first_byte_pos + 1,
+ 'bytes_total' => $filesize,
+ );
+ }
+}
diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php
index bb0d88ec1b..b5c87094c0 100644
--- a/phpBB/includes/functions_messenger.php
+++ b/phpBB/includes/functions_messenger.php
@@ -632,6 +632,64 @@ class queue
}
/**
+ * Obtains exclusive lock on queue cache file.
+ * Returns resource representing the lock
+ */
+ function lock()
+ {
+ // For systems that can't have two processes opening
+ // one file for writing simultaneously
+ if (file_exists($this->cache_file . '.lock'))
+ {
+ $mode = 'rb';
+ }
+ else
+ {
+ $mode = 'wb';
+ }
+
+ $lock_fp = @fopen($this->cache_file . '.lock', $mode);
+
+ if ($mode == 'wb')
+ {
+ if (!$lock_fp)
+ {
+ // Two processes may attempt to create lock file at the same time.
+ // Have the losing process try opening the lock file again for reading
+ // on the assumption that the winning process created it
+ $mode = 'rb';
+ $lock_fp = @fopen($this->cache_file . '.lock', $mode);
+ }
+ else
+ {
+ // Only need to set mode when the lock file is written
+ @chmod($this->cache_file . '.lock', 0666);
+ }
+ }
+
+ if ($lock_fp)
+ {
+ @flock($lock_fp, LOCK_EX);
+ }
+
+ return $lock_fp;
+ }
+
+ /**
+ * Releases lock on queue cache file, using resource obtained from lock()
+ */
+ function unlock($lock_fp)
+ {
+ // lock() will return null if opening lock file, and thus locking, failed.
+ // Accept null values here so that client code does not need to check them
+ if ($lock_fp)
+ {
+ @flock($lock_fp, LOCK_UN);
+ fclose($lock_fp);
+ }
+ }
+
+ /**
* Process queue
* Using lock file
*/
@@ -639,24 +697,16 @@ class queue
{
global $db, $config, $phpEx, $phpbb_root_path, $user;
- set_config('last_queue_run', time(), true);
+ $lock_fp = $this->lock();
- // Delete stale lock file
- if (file_exists($this->cache_file . '.lock') && !file_exists($this->cache_file))
- {
- @unlink($this->cache_file . '.lock');
- return;
- }
+ set_config('last_queue_run', time(), true);
- if (!file_exists($this->cache_file) || (file_exists($this->cache_file . '.lock') && filemtime($this->cache_file) > time() - $config['queue_interval']))
+ if (!file_exists($this->cache_file) || filemtime($this->cache_file) > time() - $config['queue_interval'])
{
+ $this->unlock($lock_fp);
return;
}
- $fp = @fopen($this->cache_file . '.lock', 'wb');
- fclose($fp);
- @chmod($this->cache_file . '.lock', 0777);
-
include($this->cache_file);
foreach ($this->queue_data as $object => $data_ary)
@@ -720,6 +770,7 @@ class queue
break;
default:
+ $this->unlock($lock_fp);
return;
}
@@ -745,8 +796,6 @@ class queue
if (!$result)
{
- @unlink($this->cache_file . '.lock');
-
messenger::error('EMAIL', $err_msg);
continue 2;
}
@@ -790,16 +839,14 @@ class queue
{
if ($fp = @fopen($this->cache_file, 'wb'))
{
- @flock($fp, LOCK_EX);
fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>");
- @flock($fp, LOCK_UN);
fclose($fp);
phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
}
}
- @unlink($this->cache_file . '.lock');
+ $this->unlock($lock_fp);
}
/**
@@ -812,6 +859,8 @@ class queue
return;
}
+ $lock_fp = $this->lock();
+
if (file_exists($this->cache_file))
{
include($this->cache_file);
@@ -831,13 +880,13 @@ class queue
if ($fp = @fopen($this->cache_file, 'w'))
{
- @flock($fp, LOCK_EX);
fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>");
- @flock($fp, LOCK_UN);
fclose($fp);
phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
}
+
+ $this->unlock($lock_fp);
}
}
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 0be3a10e5f..29cdd8ee9a 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -919,6 +919,14 @@ class fulltext_mysql extends search_backend
<dt><label>' . $user->lang['FULLTEXT_MYSQL_MBSTRING'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MBSTRING_EXPLAIN'] . '</span></dt>
<dd>' . (($this->mbstring_regex) ? $user->lang['YES'] : $user->lang['NO']). '</dd>
</dl>
+ <dl>
+ <dt><label>' . $user->lang['MIN_SEARCH_CHARS'] . ':</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
+ <dd>' . $config['fulltext_mysql_min_word_len'] . '</dd>
+ </dl>
+ <dl>
+ <dt><label>' . $user->lang['MAX_SEARCH_CHARS'] . ':</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MAX_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
+ <dd>' . $config['fulltext_mysql_max_word_len'] . '</dd>
+ </dl>
';
// These are fields required in the config table