September 25, 2021, 08:12:10 AM

News:

SMF 2.0.18 has been released! Please update. Read more.


Changelog
=========
  • A security issue reported by Daniel Le Gall, SCRT SA
  • Various bug fixes with Proxy handler
  • Login fixes for SSI and Maintenance Mode
  • Various Search fixes
  • Fixes email handling issue when using SendTopic
  • Fixed SM statistics collection and added enrollment option to admin panel

This release requires at least PHP 5.4 to function. Most hosts should have it or something newer. You can check which version of PHP that you are running by visiting the "Support and Credits" section of the Administration Center.

File Edits ALT + Click to collapse all the operations

./index.php

Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.15
Find: Select
$forum_version = 'SMF 2.0.14';
Replace With: Select
$forum_version = 'SMF 2.0.15';

./proxy.php

This operation isn't vital to the installation of this mod.
Find: Select
if (empty(response)) {
Replace With: Select
if (empty($response)) {
Find: Select
if (is_int($response)) {
Replace With: Select
if (!$response) {
Find: Select
return $request->result('code');
Replace With: Select
return false;
Find: Select
return file_put_contents($dest, json_encode(array(
'content_type' => $headers['content-type'],
'size' => $response['size'],
'time' => time(),
'body' => base64_encode($response['body']),
)))
Replace With: Select
return file_put_contents($dest, json_encode(array(
'content_type' => $headers['content-type'],
'size' => $response['size'],
'time' => time(),
'body' => base64_encode($response['body']),
))) === false ? 1 : null;

./SSI.php

Find: Select
* @version 2.0.10
Replace With: Select
* @version 2.0.15
Find: Select
<td><input type="submit" value="', $txt['login'], '" class="button_submit" /></td>
Replace With: Select
<td><input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" /><input type="submit" value="', $txt['login'], '" class="button_submit" /></td>

./Themes/default/Login.template.php

Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.15
Find: Select
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" /><p class="centertext"><input type="submit" value="', $txt['login'], '" class="button_submit" /><input type="hidden" name="hash_passwrd" value="" />
Replace With: Select
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" /><input type="hidden" name="hash_passwrd" value="" />

./Sources/Class-CurlFetchWeb.php

Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.15
Find: Select
$this->headers[strtolower($temp[0])] = strtolower(trim($temp[1]));
Replace With: Select
$this->headers[strtolower($temp[0])] = trim($temp[1]);

./Sources/SendTopic.php

Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.15
Find: Select
if (filter_var($_POST['ryemail'], FILTER_VALIDATE_EMAIL) === false)
Replace With: Select
if (filter_var($_POST['y_email'], FILTER_VALIDATE_EMAIL) === false)

./Sources/DbPackages-mysql.php

Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.15
Find: Select
$unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float')) && !empty($column_info['unsigned']) ? 'unsigned ' : '';
Replace With: Select
$unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float')) && !empty($column['unsigned']) ? 'unsigned ' : '';

./Sources/SearchAPI-Custom.php

Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.15
Find: Select
// Excluded phrases don't benefit from being split into subwords.
if (count($subwords) > 1 && $isExcluded)
continue;
Replace With: Select
// Excluded phrases don't benefit from being split into subwords.
if (count($subwords) > 1 && $isExcluded)
return;

./Sources/SearchAPI-Fulltext.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
public function prepareIndexes($word, &$wordsSearch, &$wordsExclude, $isExcluded)
{
global $modSettings;
Replace With: Select
public function prepareIndexes($word, &$wordsSearch, &$wordsExclude, $isExcluded)
{
global $modSettings, $smcFunc;

./Sources/ManageServer.php

Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.15
Find: Select
global $scripturl, $context, $txt;

/* If you're writing a mod, it's a bad idea to add things here....
For each option:
variable name, description, type (constant), size/possible values, helptext.
OR an empty string for a horizontal rule.
OR a string for a titled section. */
Replace With: Select
global $scripturl, $context, $txt;

loadLanguage('Install');

/* If you're writing a mod, it's a bad idea to add things here....
For each option:
variable name, description, type (constant), size/possible values, helptext.
OR an empty string for a horizontal rule.
OR a string for a titled section. */
Find: Select
array('image_proxy_maxsize', $txt['image_proxy_maxsize'], 'file', 'int', null, 'image_proxy_maxsize', 'postinput' => $txt['image_proxy_maxsize_postinput'], 'subtext' => $txt['image_proxy_maxsize_desc']),
);
Replace With: Select
array('image_proxy_maxsize', $txt['image_proxy_maxsize'], 'file', 'int', null, 'image_proxy_maxsize', 'postinput' => $txt['image_proxy_maxsize_postinput'], 'subtext' => $txt['image_proxy_maxsize_desc']),
'',
array('enable_sm_stats', $txt['install_settings_stats'], 'db', 'check', null),
);
Find: Select
// Saving settings?
if (isset($_REQUEST['save']))
{
saveSettings($config_vars);
Replace With: Select
// Saving settings?
if (isset($_REQUEST['save']))
{
// Are we saving the stat collection?
if (!empty($_POST['enable_sm_stats']) && empty($modSettings['sm_stats_key']))
{
$registerSMStats = registerSMStats();

// Failed to register, disable it again.
if (empty($registerSMStats))
$_POST['enable_sm_stats'] = 0;
}

saveSettings($config_vars);
Find (at the end of the file): Select
?>
Add Before: Select


/**
* Registers the site with the Simple Machines Stat collection. This function
* purposely does not use updateSettings.php as it will be called shortly after
* this process completes by the saveSettings() function.
*
* @see Stats.php SMStats() for more information.
* @link https://www.simplemachines.org/about/stats.php for more info.
*
*/
function registerSMStats()
{
global $modSettings, $boardurl, $smcFunc;

// Already have a key? Can't register again.
if (!empty($modSettings['sm_stats_key']))
return true;

$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
if ($fp)
{
$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
$out .= 'Host: www.simplemachines.org' . "\r\n";
$out .= 'Connection: Close' . "\r\n\r\n";
fwrite($fp, $out);

$return_data = '';
while (!feof($fp))
$return_data .= fgets($fp, 128);

fclose($fp);

// Get the unique site ID.
preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);

if (!empty($ID[1]))
{
$smcFunc['db_insert']('replace',
'{db_prefix}settings',
array('variable' => 'string', 'value' => 'string'),
array('sm_stats_key', $ID[1]),
array('variable')
);
return true;
}
}

return false;
}
Find: Select
'subtext' => isset($config_var['subtext']) ? $config_var['subtext'] : '',
);
Replace With: Select
'subtext' => isset($config_var['subtext']) ? $config_var['subtext'] : '',
'needs_default' => !empty($config_var['needs_default']) || in_array($config_var[0], array('db_persist', 'db_error_send', 'maintenance', 'image_proxy_enabled')) ? true : false,
);
Find: Select
foreach ($config_bools as $key)
{
if (!empty($_POST[$key]))
$new_settings[$key] = '1';
else
$new_settings[$key] = '0';
}
Replace With: Select
foreach ($config_bools as $key)
{
if (!empty($_POST[$key]))
$new_settings[$key] = '1';
elseif (isset($_POST[$key]))
$new_settings[$key] = '0';
}

./Sources/Stats.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
global $modSettings, $user_info, $forum_version, $sourcedir;

// First, is it disabled?
if (empty($modSettings['allow_sm_stats']))
die();

// Are we saying who we are, and are we right? (OR an admin)
if (!$user_info['is_admin'] && (!isset($_GET['sid']) || $_GET['sid'] != $modSettings['allow_sm_stats']))
die();
Replace With: Select
global $modSettings, $user_info, $forum_version, $sourcedir;

// Is this the old settings (upgrade failure)?
if (!empty($modSettings['allow_sm_stats']))
{
$modSettings['enable_sm_stats'] = 1;
$modSettings['sm_stats_key'] = $modSettings['allow_sm_stats'];
unset($modSettings['allow_sm_stats']);
}

// First, is it disabled?
if (empty($modSettings['enable_sm_stats']) || empty($modSettings['sm_stats_key']))
die();

// Are we saying who we are, and are we right? (OR an admin)
if (!$user_info['is_admin'] && (!isset($_GET['sid']) || $_GET['sid'] != $modSettings['sm_stats_key']))
die();
Find: Select
// Get the actual stats.
$stats_to_send = array(
'UID' => $modSettings['allow_sm_stats'],
Replace With: Select
// Get the actual stats.
$stats_to_send = array(
'UID' => $modSettings['sm_stats_key'],
Find: Select
'database_type' => strtolower($serverVersions['db_server']['title']),
Replace With: Select
'database_type' => strtolower($serverVersions['db_engine']['version']),
Find: Select
$out = 'POST /smf/stats/collect_stats.php HTTP/1.1' . "\r\n";
$out .= 'Host: www.simplemachines.org' . "\r\n";
$out .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
$out .= 'Content-Length: ' . $length . "\r\n\r\n";
$out .= $stats_to_send . "\r\n";
$out .= 'Connection: Close' . "\r\n\r\n";
fwrite($fp, $out);
Replace With: Select
$out = 'POST /smf/stats/collect_stats.php HTTP/1.1' . "\r\n";
$out .= 'Host: www.simplemachines.org' . "\r\n";
$out .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
$out .= 'Connection: Close' . "\r\n";
$out .= 'Content-Length: ' . $length . "\r\n\r\n";
$out .= $stats_to_send . "\r\n";
fwrite($fp, $out);

./Sources/DbExtra-mysql.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
'db_get_version' => 'smf_db_get_version',
);
Replace With: Select
'db_get_version' => 'smf_db_get_version',
'db_get_engine' => 'smf_db_get_engine',
);
Find (at the end of the file): Select
?>
Add Before: Select

/**
* Figures out if we are using MySQL, Percona or MariaDB
*
* @return string The database engine we are using
*/
function smf_db_get_engine()
{
global $smcFunc;
static $db_type;

if (!empty($db_type))
return $db_type;

$request = $smcFunc['db_query']('', 'SELECT @@version_comment');
list ($comment) = $smcFunc['db_fetch_row']($request);
$smcFunc['db_free_result']($request);

// Skip these if we don't have a comment.
if (!empty($comment))
{
if (stripos($comment, 'percona') !== false)
return 'Percona';
if (stripos($comment, 'mariadb') !== false)
return 'MariaDB';
}
else
return 'fail';

return 'MySQL';
}

./Sources/DbExtra-postgresql.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
'db_get_version' => 'smf_db_get_version',
);
Replace With: Select
'db_get_version' => 'smf_db_get_version',
'db_get_engine' => 'smf_db_get_engine',
);
Find (at the end of the file): Select
?>
Add Before: Select

/**
* Return PostgreSQL
*
* @return string The database engine we are using
*/
function smf_db_get_engine()
{
return 'PostgreSQL';
}

./Sources/DbExtra-sqlite.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
'db_get_version' => 'smf_db_get_version',
);
Replace With: Select
'db_get_version' => 'smf_db_get_version',
'db_get_engine' => 'smf_db_get_engine',
);
Find (at the end of the file): Select
?>
Add Before: Select

/**
* Return SQLite
*
* @return string The database engine we are using
*/
function smf_db_get_engine()
{
return 'SQLite';
}

./Sources/Subs-Admin.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
else
{
$versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => '');
$versions['db_server']['version'] = $smcFunc['db_get_version']();
}
Replace With: Select
else
{
$versions['db_engine'] = array('title' => sprintf($txt['database_server'], $smcFunc['db_title']), 'version' => '');
$versions['db_engine']['version'] = $smcFunc['db_get_engine']();

$versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => '');
$versions['db_server']['version'] = $smcFunc['db_get_version']();
}

./Sources/Admin.php

Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.15
Find: Select
// Load a lot of language files.
$language_files = array(
'Help', 'ManageMail', 'ManageSettings', 'ManageCalendar', 'ManageBoards', 'ManagePaid', 'ManagePermissions', 'Search',
'Login', 'ManageSmileys',
);
Replace With: Select
// Load a lot of language files.
$language_files = array(
'Help', 'ManageMail', 'ManageSettings', 'ManageCalendar', 'ManageBoards', 'ManagePaid', 'ManagePermissions', 'Search',
'Login', 'ManageSmileys', 'Install',
);
Find: Select
$search_data['settings'][] = array($var[(isset($var[2]) && in_array($var[2], array('file', 'db'))) ? 0 : 1], $setting_area[1]);
Replace With: Select
$search_data['settings'][] = array($var[(isset($var[2]) && in_array($var[2], array('file', 'db'))) ? 0 : 1], $setting_area[1], 'alttxt' => (isset($var[2]) && in_array($var[2], array('file', 'db'))) || isset($var[3]) ? (in_array($var[2], array('file', 'db')) ? $var[1] : $var[3]) : '');
Find: Select
// Format the name - and remove any descriptions the entry may have.
$name = isset($txt[$found]) ? $txt[$found] : (isset($txt['setting_' . $found]) ? $txt['setting_' . $found] : $found);
Replace With: Select
// Format the name - and remove any descriptions the entry may have.
$name = isset($txt[$found]) ? $txt[$found] : (isset($txt['setting_' . $found]) ? $txt['setting_' . $found] : (!empty($item['alttxt']) ? $item['alttxt'] : $found));

./Sources/ManageMembers.php

Find: Select
* @version 2.0.11
Replace With: Select
* @version 2.0.15
Find: Select
if ($context['sub_action'] == 'query' && !empty($_REQUEST['params']) && empty($_POST))
$_POST += safe_unserialize(base64_decode($_REQUEST['params']));
Replace With: Select
if ($context['sub_action'] == 'query' && empty($_POST))
{
if (!empty($_REQUEST['params']))
{
$_POST += safe_unserialize(base64_decode($_REQUEST['params']));
}
elseif ($context['browser']['is_ie'] && !empty($_SESSION['params']))
{
$_POST += $_SESSION['params'];
unset($_SESSION['params']);
}
}
Find: Select
$search_params = base64_encode(serialize($_POST));
Replace With: Select
if ($context['browser']['is_ie'])
{
// Cache the results in the session and avoid IE's 2083 character limit.
$_SESSION['params'] = $_POST;
$search_params = null;
}
else
$search_params = base64_encode(serialize($_POST));

./Sources/Subs-Boards.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
$logBoardInserts = '';
Replace With: Select
$logBoardInserts = array();

./Themes/default/Admin.template.php

Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.15
Find: Select
// Show a check box.
if ($config_var['type'] == 'check')
echo '
<input type="checkbox"', $javascript, $disabled, ' name="', $config_var['name'], '" id="', $config_var['name'], '"', ($config_var['value'] ? ' checked="checked"' : ''), ' value="1" class="input_check" />';
Replace With: Select
// Show a check box.
if ($config_var['type'] == 'check')
{
if (!empty($config_var['needs_default']))
echo '
<input type="hidden" name="', $config_var['name'], '" value="0" />';

echo '
<input type="checkbox"', $javascript, $disabled, ' name="', $config_var['name'], '" id="', $config_var['name'], '"', ($config_var['value'] ? ' checked="checked"' : ''), ' value="1" class="input_check" />';
}

./Sources/PersonalMessage.php

Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.15
Find: Select
// Who matches those criteria?
// !!! This doesn't support sent item searching.
$request = $smcFunc['db_query']('', '
SELECT id_member
FROM {db_prefix}members
WHERE real_name LIKE {raw:real_name_implode}',
array(
'real_name_implode' => '\'' . implode('\' OR real_name LIKE \'', $possible_users) . '\'',
)
);
// Simply do nothing if there're too many members matching the criteria.
if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch)
$userQuery = '';
elseif ($smcFunc['db_num_rows']($request) == 0)
{
$userQuery = 'AND pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})';
$searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\'';
}
else
{
$memberlist = array();
while ($row = $smcFunc['db_fetch_assoc']($request))
$memberlist[] = $row['id_member'];
$userQuery = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})))';
$searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\'';
$searchq_parameters['member_list'] = $memberlist;
}
$smcFunc['db_free_result']($request);
Replace With: Select
if (!empty($possible_users))
{
// We need to bring this into the query and do it nice and cleanly.
$where_params = array();
$where_clause = array();
foreach ($possible_users as $k => $v)
{
$where_params['name_' . $k] = $v;
$where_clause[] = '{raw:real_name} LIKE {string:name_' . $k . '}';
if (!isset($where_params['real_name']))
$where_params['real_name'] = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name';
}

// Who matches those criteria?
// !!! This doesn't support sent item searching.
$request = $smcFunc['db_query']('', '
SELECT id_member
FROM {db_prefix}members
WHERE ' . implode(' OR ', $where_clause),
$where_params
);

// Simply do nothing if there're too many members matching the criteria.
if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch)
$userQuery = '';
elseif ($smcFunc['db_num_rows']($request) == 0)
{
$where_params['real_name'] = 'pm.from_name';
$searchq_parameters = array_merge($searchq_parameters, $where_params);
$userQuery = 'AND pm.id_member_from = 0 AND (' . implode(' OR ', $where_clause) . ')';
}
else
{
$memberlist = array();
while ($row = $smcFunc['db_fetch_assoc']($request))
$memberlist[] = $row['id_member'];

$where_params['real_name'] = 'pm.from_name';
$searchq_parameters = array_merge($searchq_parameters, $where_params);
$searchq_parameters['member_list'] = $memberlist;
$userQuery = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND (' . implode(' OR ', $where_clause) . ')))';
}
$smcFunc['db_free_result']($request);
}
else
$userQuery = '';
Find: Select
WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {raw:current_member}
Replace With: Select
WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member}

./Sources/Subs-Auth.php

Find: Select
* @version 2.0.11
Replace With: Select
* @version 2.0.15
Find: Select
$maybe_email = false;
foreach ($names as $i => $name)
{
Replace With: Select
$maybe_email = false;
$names_list = array();
foreach ($names as $i => $name)
{
Find: Select
$names[$i] = strtr($names[$i], array('\'' => '&#039;'));
}
Replace With: Select
$names[$i] = strtr($names[$i], array('\'' => '&#039;'));

$names_list[] = '{string:lookup_name_' . $i . '}';
$where_params['lookup_name_' . $i] = $names[$i];
}
Find: Select
$real_name = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name';

// Search by username, display name, and email address.
$request = $smcFunc['db_query']('', '
SELECT id_member, member_name, real_name, email_address, hide_email
FROM {db_prefix}members
WHERE ({raw:member_name_search}
OR {raw:real_name_search} {raw:email_condition})
' . ($buddies_only ? 'AND id_member IN ({array_int:buddy_list})' : '') . '
AND is_activated IN (1, 11)
LIMIT {int:limit}',
array(
'buddy_list' => $user_info['buddies'],
'member_name_search' => $member_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $member_name . ' ' . $comparison . ' \'', $names) . '\'',
'real_name_search' => $real_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $real_name . ' ' . $comparison . ' \'', $names) . '\'',
'email_condition' => $email_condition,
'limit' => $max,
)
);
Replace With: Select
$real_name = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name';

// Searches.
$member_name_search = $member_name . ' ' . $comparison . ' ' . implode( ' OR ' . $member_name . ' ' . $comparison . ' ', $names_list);
$real_name_search = $real_name . ' ' . $comparison . ' ' . implode( ' OR ' . $real_name . ' ' . $comparison . ' ', $names_list);

// Search by username, display name, and email address.
$request = $smcFunc['db_query']('', '
SELECT id_member, member_name, real_name, email_address, hide_email
FROM {db_prefix}members
WHERE (' . $member_name_search . '
OR ' . $real_name_search . ' ' . $email_condition . ')
' . ($buddies_only ? 'AND id_member IN ({array_int:buddy_list})' : '') . '
AND is_activated IN (1, 11)
LIMIT {int:limit}',
array_merge($where_params, array(
'buddy_list' => $user_info['buddies'],
'limit' => $max,
))
);
Advertisement: