Advertisement:

Navigation

Readme

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

./index.php

Operation #1
Find: [Select]
* @version 2.0.14
Replace With: [Select]
* @version 2.0.15
Operation #2
Find: [Select]
$forum_version = 'SMF 2.0.14';
Replace With: [Select]
$forum_version = 'SMF 2.0.15';

./proxy.php

Operation #1
Find: [Select]
if (empty(response)) {
Replace With: [Select]
if (empty($response)) {
This operation isn't vital to the installation of this mod.

Operation #2
Find: [Select]
if (is_int($response)) {
Replace With: [Select]
if (!$response) {
Operation #3
Find: [Select]
return $request->result('code');
Replace With: [Select]
return false;
Operation #4
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

Operation #1
Find: [Select]
* @version 2.0.10
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.14
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.14
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.14
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.12
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.12
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.14
Replace With: [Select]
* @version 2.0.15
Operation #2
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. */

Operation #3
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),
);

Operation #4
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);

Operation #5
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;
}

Operation #6
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,
);

Operation #7
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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();

Operation #3
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'],

Operation #4
Find: [Select]
'database_type' => strtolower($serverVersions['db_server']['title']),
Replace With: [Select]
'database_type' => strtolower($serverVersions['db_engine']['version']),
Operation #5
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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',
);

Operation #3
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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',
);

Operation #3
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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',
);

Operation #3
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.12
Replace With: [Select]
* @version 2.0.15
Operation #2
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',
);

Operation #3
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]) : '');
Operation #4
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

Operation #1
Find: [Select]
* @version 2.0.11
Replace With: [Select]
* @version 2.0.15
Operation #2
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']);
}
}

Operation #3
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

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
Find: [Select]
$logBoardInserts = '';
Replace With: [Select]
$logBoardInserts = array();

./Themes/default/Admin.template.php

Operation #1
Find: [Select]
* @version 2.0
Replace With: [Select]
* @version 2.0.15
Operation #2
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

Operation #1
Find: [Select]
* @version 2.0.12
Replace With: [Select]
* @version 2.0.15
Operation #2
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 = '';

Operation #3
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

Operation #1
Find: [Select]
* @version 2.0.11
Replace With: [Select]
* @version 2.0.15
Operation #2
Find: [Select]
$maybe_email = false;
foreach ($names as $i => $name)
{
Replace With: [Select]
$maybe_email = false;
$names_list = array();
foreach ($names as $i => $name)
{

Operation #3
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];
}

Operation #4
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,
))
);