Completed
Push — release-2.1 ( 43f77f...fe5234 )
by Michael
08:11
created

News.php ➔ getXmlNews()   F

Complexity

Conditions 36
Paths 700

Size

Total Lines 381
Code Lines 240

Duplication

Lines 104
Ratio 27.3 %

Importance

Changes 0
Metric Value
cc 36
eloc 240
nc 700
nop 1
dl 104
loc 381
rs 2.0567
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains the files necessary to display news as an XML feed.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Outputs xml data representing recent information or a profile.
21
 * Can be passed 4 subactions which decide what is output:
22
 *  'recent' for recent posts,
23
 *  'news' for news topics,
24
 *  'members' for recently registered members,
25
 *  'profile' for a member's profile.
26
 * To display a member's profile, a user id has to be given. (;u=1)
27
 * Outputs an rss feed instead of a proprietary one if the 'type' $_GET
28
 * parameter is 'rss' or 'rss2'.
29
 * Accessed via ?action=.xml.
30
 * Does not use any templates, sub templates, or template layers.
31
 *
32
 * @uses Stats language file.
33
 */
34
function ShowXmlFeed()
35
{
36
	global $board, $board_info, $context, $scripturl, $boardurl, $txt, $modSettings, $user_info;
37
	global $query_this_board, $smcFunc, $forum_version;
38
39
	// If it's not enabled, die.
40
	if (empty($modSettings['xmlnews_enable']))
41
		obExit(false);
42
43
	loadLanguage('Stats');
44
45
	// Default to latest 5.  No more than 255, please.
46
	$_GET['limit'] = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? 5 : min((int) $_GET['limit'], 255);
47
48
	// Some general metadata for this feed. We'll change some of these values below.
49
	$feed_meta = array(
50
		'title' => '',
51
		'desc' => $txt['xml_rss_desc'],
52
		'author' => $context['forum_name'],
53
		'source' => $scripturl,
54
		'id' => $scripturl,
55
		'icon' => $boardurl . '/favicon.ico',
56
	);
57
58
	// Handle the cases where a board, boards, or category is asked for.
59
	$query_this_board = 1;
60
	$context['optimize_msg'] = array(
61
		'highest' => 'm.id_msg <= b.id_last_msg',
62
	);
63
	if (!empty($_REQUEST['c']) && empty($board))
64
	{
65
		$_REQUEST['c'] = explode(',', $_REQUEST['c']);
66
		foreach ($_REQUEST['c'] as $i => $c)
67
			$_REQUEST['c'][$i] = (int) $c;
68
69
		if (count($_REQUEST['c']) == 1)
70
		{
71
			$request = $smcFunc['db_query']('', '
72
				SELECT name
73
				FROM {db_prefix}categories
74
				WHERE id_cat = {int:current_category}',
75
				array(
76
					'current_category' => (int) $_REQUEST['c'][0],
77
				)
78
			);
79
			list ($feed_meta['title']) = $smcFunc['db_fetch_row']($request);
80
			$smcFunc['db_free_result']($request);
81
82
			$feed_meta['title'] = ' - ' . strip_tags($feed_meta['title']);
83
		}
84
85
		$request = $smcFunc['db_query']('', '
86
			SELECT b.id_board, b.num_posts
87
			FROM {db_prefix}boards AS b
88
			WHERE b.id_cat IN ({array_int:current_category_list})
89
				AND {query_see_board}',
90
			array(
91
				'current_category_list' => $_REQUEST['c'],
92
			)
93
		);
94
		$total_cat_posts = 0;
95
		$boards = array();
96
		while ($row = $smcFunc['db_fetch_assoc']($request))
97
		{
98
			$boards[] = $row['id_board'];
99
			$total_cat_posts += $row['num_posts'];
100
		}
101
		$smcFunc['db_free_result']($request);
102
103
		if (!empty($boards))
104
			$query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
105
106
		// Try to limit the number of messages we look through.
107 View Code Duplication
		if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15)
108
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5);
109
	}
110
	elseif (!empty($_REQUEST['boards']))
111
	{
112
		$_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
113
		foreach ($_REQUEST['boards'] as $i => $b)
114
			$_REQUEST['boards'][$i] = (int) $b;
115
116
		$request = $smcFunc['db_query']('', '
117
			SELECT b.id_board, b.num_posts, b.name
118
			FROM {db_prefix}boards AS b
119
			WHERE b.id_board IN ({array_int:board_list})
120
				AND {query_see_board}
121
			LIMIT {int:limit}',
122
			array(
123
				'board_list' => $_REQUEST['boards'],
124
				'limit' => count($_REQUEST['boards']),
125
			)
126
		);
127
128
		// Either the board specified doesn't exist or you have no access.
129
		$num_boards = $smcFunc['db_num_rows']($request);
130
		if ($num_boards == 0)
131
			fatal_lang_error('no_board');
132
133
		$total_posts = 0;
134
		$boards = array();
135
		while ($row = $smcFunc['db_fetch_assoc']($request))
136
		{
137
			if ($num_boards == 1)
138
				$feed_meta['title'] = ' - ' . strip_tags($row['name']);
139
140
			$boards[] = $row['id_board'];
141
			$total_posts += $row['num_posts'];
142
		}
143
		$smcFunc['db_free_result']($request);
144
145
		if (!empty($boards))
146
			$query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
147
148
		// The more boards, the more we're going to look through...
149 View Code Duplication
		if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12)
150
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5);
151
	}
152
	elseif (!empty($board))
153
	{
154
		$request = $smcFunc['db_query']('', '
155
			SELECT num_posts
156
			FROM {db_prefix}boards
157
			WHERE id_board = {int:current_board}
158
			LIMIT 1',
159
			array(
160
				'current_board' => $board,
161
			)
162
		);
163
		list ($total_posts) = $smcFunc['db_fetch_row']($request);
164
		$smcFunc['db_free_result']($request);
165
166
		$feed_meta['title'] = ' - ' . strip_tags($board_info['name']);
167
168
		$query_this_board = 'b.id_board = ' . $board;
169
170
		// Try to look through just a few messages, if at all possible.
171 View Code Duplication
		if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10)
172
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5);
173
	}
174
	else
175
	{
176
		$query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
177
			AND b.id_board != ' . $modSettings['recycle_board'] : '');
178
		$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5);
179
	}
180
181
	// Show in rss or proprietary format?
182
	$xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf')) ? $_GET['type'] : 'smf';
183
184
	// @todo Birthdays?
185
186
	// List all the different types of data they can pull.
187
	$subActions = array(
188
		'recent' => array('getXmlRecent', 'recent-post'),
189
		'news' => array('getXmlNews', 'article'),
190
		'members' => array('getXmlMembers', 'member'),
191
		'profile' => array('getXmlProfile', null),
192
	);
193
194
	// Easy adding of sub actions
195
 	call_integration_hook('integrate_xmlfeeds', array(&$subActions));
196
197
	if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']]))
198
		$_GET['sa'] = 'recent';
199
200
	// We only want some information, not all of it.
201
	$cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']);
202
	foreach (array('board', 'boards', 'c') as $var)
203
		if (isset($_REQUEST[$var]))
204
			$cachekey[] = $_REQUEST[$var];
205
	$cachekey = md5(json_encode($cachekey) . (!empty($query_this_board) ? $query_this_board : ''));
206
	$cache_t = microtime();
207
208
	// Get the associative array representing the xml.
209
	if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3))
210
		$xml = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240);
211
	if (empty($xml))
212
	{
213
		$call = call_helper($subActions[$_GET['sa']][0], true);
214
215
		if (!empty($call))
216
			$xml = call_user_func($call, $xml_format);
217
218
		if (!empty($modSettings['cache_enable']) && (($user_info['is_guest'] && $modSettings['cache_enable'] >= 3)
219
		|| (!$user_info['is_guest'] && (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.2))))
220
			cache_put_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240);
0 ignored issues
show
Bug introduced by
The variable $xml does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
221
	}
222
223
	$feed_meta['title'] = $smcFunc['htmlspecialchars'](strip_tags($context['forum_name'])) . (isset($feed_meta['title']) ? $feed_meta['title'] : '');
224
225
	// Allow mods to add extra namespaces and tags to the feed/channel
226
	$namespaces = array(
227
		'rss' => array(),
228
		'rss2' => array('atom' => 'http://www.w3.org/2005/Atom'),
229
		'atom' => array('' => 'http://www.w3.org/2005/Atom'),
230
		'rdf' => array(
231
			'' => 'http://purl.org/rss/1.0/',
232
			'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
233
			'dc' => 'http://purl.org/dc/elements/1.1/',
234
		),
235
		'smf' => array(
236
			'' => 'http://www.simplemachines.org/xml/' . $_GET['sa'],
237
			'smf' => 'http://www.simplemachines.org/',
238
		),
239
	);
240
	$extraFeedTags = array(
241
		'rss' => array(),
242
		'rss2' => array(),
243
		'atom' => array(),
244
		'rdf' => array(),
245
		'smf' => array(),
246
	);
247
248
	// Allow mods to specify any keys that need special handling
249
	$forceCdataKeys = array();
250
	$nsKeys = array();
251
252
	// If mods want to do somthing with this feed, let them do that now.
253
	// Provide the feed's data, title, format, content type, keys that need special handling, etc.
254
	call_integration_hook('integrate_xml_data', array(&$xml, &$feed_meta, &$namespaces, &$extraFeedTags, &$forceCdataKeys, &$nsKeys, $xml_format, $_GET['sa']));
255
256
	$ns_string = '';
257
	if (!empty($namespaces[$xml_format]))
258
	{
259
		foreach ($namespaces[$xml_format] as $nsprefix => $nsurl)
260
			$ns_string .= ' xmlns' . ($nsprefix !== '' ? ':' : '') . $nsprefix . '="' . $nsurl . '"';
261
	}
262
263
	$extraFeedTags_string = '';
264
	if (!empty($extraFeedTags[$xml_format]))
265
	{
266
		foreach ($extraFeedTags[$xml_format] as $extraTag)
267
			$extraFeedTags_string .= "\n\t\t" . $extraTag;
268
	}
269
270
	// This is an xml file....
271
	ob_end_clean();
272
	if (!empty($modSettings['enableCompressedOutput']))
273
		@ob_start('ob_gzhandler');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
274
	else
275
		ob_start();
276
277
	if ($xml_format == 'smf' || isset($_REQUEST['debug']))
278
		header('Content-Type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
279 View Code Duplication
	elseif ($xml_format == 'rss' || $xml_format == 'rss2')
280
		header('Content-Type: application/rss+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
281 View Code Duplication
	elseif ($xml_format == 'atom')
282
		header('Content-Type: application/atom+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
283
	elseif ($xml_format == 'rdf')
284
		header('Content-Type: ' . (isBrowser('ie') ? 'text/xml' : 'application/rdf+xml') . '; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
285
286
	// First, output the xml header.
287
	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?' . '>';
288
289
	// Are we outputting an rss feed or one with more information?
290
	if ($xml_format == 'rss' || $xml_format == 'rss2')
291
	{
292
		if ($xml_format == 'rss2')
293 View Code Duplication
			foreach ($_REQUEST as $var => $val)
294
				if (in_array($var, array('action', 'sa', 'type', 'board', 'boards', 'c', 'u', 'limit')))
295
					$url_parts[] = $var . '=' . (is_array($val) ? implode(',', $val) : $val);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$url_parts was never initialized. Although not strictly required by PHP, it is generally a good practice to add $url_parts = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
296
297
		// Start with an RSS 2.0 header.
298
		echo '
299
<rss version=', $xml_format == 'rss2' ? '"2.0"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>
300
	<channel>
301
		<title>', $feed_meta['title'], '</title>
302
		<link>', $feed_meta['source'], '</link>
303
		<description>', cdata_parse(strip_tags($feed_meta['desc'])), '</description>';
304
305
		// RSS2 calls for this.
306
		if ($xml_format == 'rss2')
307
			echo '
308
		<atom:link rel="self" type="application/rss+xml" href="', $scripturl, !empty($url_parts) ? '?' . implode(';', $url_parts) : '', '" />';
0 ignored issues
show
Security Cross-Site Scripting introduced by
!empty($url_parts) ? '?'...e(';', $url_parts) : '' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST, and $val is assigned
    in Sources/News.php on line 293
  2. $url_parts is assigned
    in Sources/News.php on line 295
  3. $url_parts is passed through implode()
    in Sources/News.php on line 308

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
309
310
		echo $extraFeedTags_string;
311
312
		// Output all of the associative array, start indenting with 2 tabs, and name everything "item".
313
		dumpTags($xml, 2, null, $xml_format, $forceCdataKeys, $nsKeys);
314
315
		// Output the footer of the xml.
316
		echo '
317
	</channel>
318
</rss>';
319
	}
320
	elseif ($xml_format == 'atom')
321
	{
322 View Code Duplication
		foreach ($_REQUEST as $var => $val)
323
			if (in_array($var, array('action', 'sa', 'type', 'board', 'boards', 'c', 'u', 'limit')))
324
				$url_parts[] = $var . '=' . (is_array($val) ? implode(',', $val) : $val);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$url_parts was never initialized. Although not strictly required by PHP, it is generally a good practice to add $url_parts = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
325
326
		echo '
327
<feed', $ns_string, '>
328
	<title>', $feed_meta['title'], '</title>
329
	<link rel="alternate" type="text/html" href="', $feed_meta['source'], '" />
330
	<link rel="self" type="application/atom+xml" href="', $scripturl, !empty($url_parts) ? '?' . implode(';', $url_parts) : '', '" />
0 ignored issues
show
Security Cross-Site Scripting introduced by
!empty($url_parts) ? '?'...e(';', $url_parts) : '' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST, and $val is assigned
    in Sources/News.php on line 322
  2. $url_parts is assigned
    in Sources/News.php on line 324
  3. $url_parts is passed through implode()
    in Sources/News.php on line 330

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
331
	<id>', $feed_meta['id'], '</id>
332
	<icon>', $feed_meta['icon'], '</icon>
333
	<updated>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</updated>
334
	<subtitle>', cdata_parse(strip_tags($feed_meta['desc'])), '</subtitle>
335
	<generator uri="http://www.simplemachines.org" version="', strtr($forum_version, array('SMF' => '')), '">SMF</generator>
336
	<author>
337
		<name>', cdata_parse(strip_tags($feed_meta['author'])), '</name>
338
	</author>';
339
340
		echo $extraFeedTags_string;
341
342
		dumpTags($xml, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
343
344
		echo '
345
</feed>';
346
	}
347
	elseif ($xml_format == 'rdf')
348
	{
349
		echo '
350
<rdf:RDF', $ns_string, '>
351
	<channel rdf:about="', $scripturl, '">
352
		<title>', $feed_meta['title'], '</title>
353
		<link>', $feed_meta['source'], '</link>
354
		<description>', cdata_parse(strip_tags($feed_meta['desc'])), '</description>';
355
356
		echo $extraFeedTags_string;
357
358
		echo '
359
		<items>
360
			<rdf:Seq>';
361
362
		foreach ($xml as $item)
363
		{
364
			$link = array_filter($item['content'], function ($e) { return ($e['tag'] == 'link'); });
365
			$link = array_pop($link);
366
367
			echo '
368
				<rdf:li rdf:resource="', $link['content'], '" />';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$link['content'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $item is assigned
    in Sources/News.php on line 362
  4. $item['content'] is passed through array_filter(), and $link is assigned
    in Sources/News.php on line 364
  5. $link is passed through array_pop(), and $link is assigned
    in Sources/News.php on line 365

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
369
		}
370
371
		echo '
372
			</rdf:Seq>
373
		</items>
374
	</channel>
375
';
376
377
		dumpTags($xml, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
378
379
		echo '
380
</rdf:RDF>';
381
	}
382
	// Otherwise, we're using our proprietary formats - they give more data, though.
383
	else
384
	{
385
		echo '
386
<smf:xml-feed xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>';
387
388
		// Hard to imagine anyone wanting to add these for the proprietary format, but just in case...
389
		echo $extraFeedTags_string;
390
391
		// Dump out that associative array.  Indent properly.... and use the right names for the base elements.
392
		dumpTags($xml, 1, $subActions[$_GET['sa']][1], $xml_format, $forceCdataKeys, $nsKeys);
393
394
		echo '
395
</smf:xml-feed>';
396
}
397
398
	obExit(false);
399
}
400
401
/**
402
 * Called from dumpTags to convert data to xml
403
 * Finds urls for local site and sanitizes them
404
 *
405
 * @param string $val A string containing a possible URL
406
 * @return string $val The string with any possible URLs sanitized
407
 */
408
function fix_possible_url($val)
409
{
410
	global $modSettings, $context, $scripturl;
411
412
	if (substr($val, 0, strlen($scripturl)) != $scripturl)
413
		return $val;
414
415
	call_integration_hook('integrate_fix_url', array(&$val));
416
417
	if (empty($modSettings['queryless_urls']) || ($context['server']['is_cgi'] && ini_get('cgi.fix_pathinfo') == 0 && @get_cfg_var('cgi.fix_pathinfo') == 0) || (!$context['server']['is_apache'] && !$context['server']['is_lighttpd']))
418
		return $val;
419
420
	$val = preg_replace_callback('~^' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function($m) use ($scripturl)
421
		{
422
			return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "");
423
		}, $val);
424
	return $val;
425
}
426
427
/**
428
 * Ensures supplied data is properly encapsulated in cdata xml tags
429
 * Called from getXmlProfile in News.php
430
 *
431
 * @param string $data XML data
432
 * @param string $ns A namespace prefix for the XML data elements (used by mods, maybe)
433
 * @param boolean $force If true, enclose the XML data in cdata tags no matter what (used by mods, maybe)
434
 * @return string The XML data enclosed in cdata tags when necessary
435
 */
436
function cdata_parse($data, $ns = '', $force = false)
437
{
438
	global $smcFunc;
439
440
	// Do we even need to do this?
441
	if (strpbrk($data, '<>&') == false && $force !== true)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpbrk($data, '<>&') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
442
		return $data;
443
444
	$cdata = '<![CDATA[';
445
446
	for ($pos = 0, $n = $smcFunc['strlen']($data); $pos < $n; null)
447
	{
448
		$positions = array(
449
			$smcFunc['strpos']($data, '&', $pos),
450
			$smcFunc['strpos']($data, ']', $pos),
451
		);
452
		if ($ns != '')
453
			$positions[] = $smcFunc['strpos']($data, '<', $pos);
454
		foreach ($positions as $k => $dummy)
455
		{
456
			if ($dummy === false)
457
				unset($positions[$k]);
458
		}
459
460
		$old = $pos;
461
		$pos = empty($positions) ? $n : min($positions);
462
463
		if ($pos - $old > 0)
464
			$cdata .= $smcFunc['substr']($data, $old, $pos - $old);
465
		if ($pos >= $n)
466
			break;
467
468
		if ($smcFunc['substr']($data, $pos, 1) == '<')
469
		{
470
			$pos2 = $smcFunc['strpos']($data, '>', $pos);
471
			if ($pos2 === false)
472
				$pos2 = $n;
473
			if ($smcFunc['substr']($data, $pos + 1, 1) == '/')
474
				$cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
475
			else
476
				$cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
477
			$pos = $pos2 + 1;
478
		}
479
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
480
		{
481
			$cdata .= ']]>&#093;<![CDATA[';
482
			$pos++;
483
		}
484
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
485
		{
486
			$pos2 = $smcFunc['strpos']($data, ';', $pos);
487
			if ($pos2 === false)
488
				$pos2 = $n;
489
			$ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1);
490
491
			if ($smcFunc['substr']($data, $pos + 1, 1) == '#')
492
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
493
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
494
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
495
496
			$pos = $pos2 + 1;
497
		}
498
	}
499
500
	$cdata .= ']]>';
501
502
	return strtr($cdata, array('<![CDATA[]]>' => ''));
503
}
504
505
/**
506
 * Formats data retrieved in other functions into xml format.
507
 * Additionally formats data based on the specific format passed.
508
 * This function is recursively called to handle sub arrays of data.
509
 *
510
 * @param array $data The array to output as xml data
511
 * @param int $i The amount of indentation to use.
512
 * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML)
513
 * @param array $forceCdataKeys A list of keys on which to force cdata wrapping (used by mods, maybe)
514
 * @param array $nsKeys Key-value pairs of namespace prefixes to pass to cdata_parse() (used by mods, maybe)
515
 */
516
function dumpTags($data, $i, $tag = null, $xml_format = '', $forceCdataKeys = array(), $nsKeys = array())
517
{
518
	// Wrap the values of these keys into CDATA tags
519
	$keysToCdata = array(
520
		'title',
521
		'name',
522
		'description',
523
		'summary',
524
		'subject',
525
		'body',
526
		'username',
527
		'signature',
528
		'position',
529
		'language',
530
		'gender',
531
		'blurb',
532
	);
533
	if ($xml_format != 'atom')
534
		$keysToCdata[] = 'category';
535
536
	if (!empty($forceCdataKeys))
537
	{
538
		$keysToCdata = array_merge($keysToCdata, $forceCdataKeys);
539
		$keysToCdata = array_unique($keysToCdata);
540
	}
541
542
	// For every array in the data...
543
	foreach ($data as $element)
544
	{
545
		$key = isset($element['tag']) ? $element['tag'] : null;
546
		$val = isset($element['content']) ? $element['content'] : null;
547
		$attrs = isset($element['attributes']) ? $element['attributes'] : null;
548
549
		// Skip it, it's been set to null.
550
		if ($val === null && $attrs === null)
551
			continue;
552
553
		// If a tag was passed, use it instead of the key.
554
		$key = isset($tag) ? $tag : $key;
555
556
		$forceCdata = in_array($key, $forceCdataKeys);
557
		$ns = !empty($nsKeys[$key]) ? $nsKeys[$key] : '';
558
559
		// If the value should maybe be CDATA, do that now.
560
		if (!is_array($val) && in_array($key, $keysToCdata))
561
			$val = cdata_parse($val, $ns, $forceCdata);
562
563
		// First let's indent!
564
		echo "\n", str_repeat("\t", $i);
565
566
		// If it's empty/0/nothing simply output an empty element.
567
		if (empty($val))
568
		{
569
			echo '<', $key;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$key can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $key is assigned
    in Sources/News.php on line 545
  6. $key is assigned
    in Sources/News.php on line 554

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
570
571
			if (!empty($attrs))
572
			{
573
				foreach ($attrs as $attr_key => $attr_value)
574
					echo ' ', $attr_key, '="', $attr_value, '"';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$attr_key can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $attrs is assigned
    in Sources/News.php on line 547
  6. $attr_key is assigned
    in Sources/News.php on line 573

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$attr_value can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $attrs is assigned
    in Sources/News.php on line 547
  6. $attr_value is assigned
    in Sources/News.php on line 573

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
575
			}
576
577
			echo ' />';
578
		}
579
		else
580
		{
581
			// Beginning tag.
582
			echo '<', $key;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$key can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $key is assigned
    in Sources/News.php on line 545
  6. $key is assigned
    in Sources/News.php on line 554

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
583
584
			if (!empty($attrs))
585
			{
586
				foreach ($attrs as $attr_key => $attr_value)
587
					echo ' ', $attr_key, '="', $attr_value, '"';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$attr_key can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $attrs is assigned
    in Sources/News.php on line 547
  6. $attr_key is assigned
    in Sources/News.php on line 586

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$attr_value can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $attrs is assigned
    in Sources/News.php on line 547
  6. $attr_value is assigned
    in Sources/News.php on line 586

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
588
			}
589
590
			echo '>';
591
592
			// The element's value.
593
			if (is_array($val))
594
			{
595
				// An array.  Dump it, and then indent the tag.
596
				dumpTags($val, $i + 1, null, $xml_format, $forceCdataKeys, $nsKeys);
597
				echo "\n", str_repeat("\t", $i);
598
			}
599
			// A string with returns in it.... show this as a multiline element.
600
			elseif (strpos($val, "\n") !== false)
601
				echo "\n", fix_possible_url($val), "\n", str_repeat("\t", $i);
0 ignored issues
show
Security Cross-Site Scripting introduced by
fix_possible_url($val) can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $val is assigned
    in Sources/News.php on line 546

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
602
			// A simple string.
603
			else
604
				echo fix_possible_url($val);
0 ignored issues
show
Security Cross-Site Scripting introduced by
fix_possible_url($val) can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $val is assigned
    in Sources/News.php on line 546

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
605
606
			// Ending tag.
607
			echo '</', $key, '>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$key can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $xml_format is assigned
    in Sources/News.php on line 182
  2. $xml_format is passed through call_user_func(), and $xml is assigned
    in Sources/News.php on line 216
  3. $xml is passed to dumpTags()
    in Sources/News.php on line 313
  4. $element is assigned
    in Sources/News.php on line 543
  5. $key is assigned
    in Sources/News.php on line 545
  6. $key is assigned
    in Sources/News.php on line 554

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
608
		}
609
	}
610
}
611
612
/**
613
 * Retrieve the list of members from database.
614
 * The array will be generated to match the format.
615
 * @todo get the list of members from Subs-Members.
616
 *
617
 * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
618
 * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format.
619
 */
620
function getXmlMembers($xml_format)
621
{
622
	global $scripturl, $smcFunc;
623
624
	if (!allowedTo('view_mlist'))
625
		return array();
626
627
	// Find the most recent members.
628
	$request = $smcFunc['db_query']('', '
629
		SELECT id_member, member_name, real_name, date_registered, last_login
630
		FROM {db_prefix}members
631
		ORDER BY id_member DESC
632
		LIMIT {int:limit}',
633
		array(
634
			'limit' => $_GET['limit'],
635
		)
636
	);
637
	$data = array();
638
	while ($row = $smcFunc['db_fetch_assoc']($request))
639
	{
640
		// Make the data look rss-ish.
641
		if ($xml_format == 'rss' || $xml_format == 'rss2')
642
			$data[] = array(
643
				'tag' => 'item',
644
				'content' => array(
645
					array(
646
						'tag' => 'title',
647
						'content' => $row['real_name'],
648
					),
649
					array(
650
						'tag' => 'link',
651
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
652
					),
653
					array(
654
						'tag' => 'comments',
655
						'content' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'],
656
					),
657
					array(
658
						'tag' => 'pubDate',
659
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']),
660
					),
661
					array(
662
						'tag' => 'guid',
663
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
664
					),
665
				),
666
			);
667
		elseif ($xml_format == 'rdf')
668
			$data[] = array(
669
				'tag' => 'item',
670
				'attributes' => array('rdf:about' => fix_possible_url($scripturl . '?action=profile;u=' . $row['id_member'])),
671
				'content' => array(
672
					array(
673
						'tag' => 'dc:format',
674
						'content' => 'text/html',
675
					),
676
					array(
677
						'tag' => 'title',
678
						'content' => $row['real_name'],
679
					),
680
					array(
681
						'tag' => 'link',
682
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
683
					),
684
				),
685
			);
686
		elseif ($xml_format == 'atom')
687
			$data[] = array(
688
				'tag' => 'entry',
689
				'content' => array(
690
					array(
691
						'tag' => 'title',
692
						'content' => $row['real_name'],
693
					),
694
					array(
695
						'tag' => 'link',
696
						'attributes' => array(
697
							'rel' => 'alternate',
698
							'type' => 'text/html',
699
							'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
700
						),
701
					),
702
					array(
703
						'tag' => 'published',
704
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']),
705
					),
706
					array(
707
						'tag' => 'updated',
708
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']),
709
					),
710
					array(
711
						'tag' => 'id',
712
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
713
					),
714
				),
715
			);
716
		// More logical format for the data, but harder to apply.
717
		else
718
			$data[] = array(
719
				'tag' => 'member',
720
				'content' => array(
721
					array(
722
						'tag' => 'name',
723
						'content' => $row['real_name'],
724
					),
725
					array(
726
						'tag' => 'time',
727
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))),
728
					),
729
					array(
730
						'tag' => 'id',
731
						'content' => $row['id_member'],
732
					),
733
					array(
734
						'tag' => 'link',
735
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
736
					),
737
				),
738
			);
739
	}
740
	$smcFunc['db_free_result']($request);
741
742
	return $data;
743
}
744
745
/**
746
 * Get the latest topics information from a specific board,
747
 * to display later.
748
 * The returned array will be generated to match the xmf_format.
749
 * @todo does not belong here
750
 *
751
 * @param $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'.
752
 * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format.
753
 */
754
function getXmlNews($xml_format)
755
{
756
	global $scripturl, $modSettings, $board, $user_info;
757
	global $query_this_board, $smcFunc, $context, $txt;
758
759
	/* Find the latest posts that:
760
		- are the first post in their topic.
761
		- are on an any board OR in a specified board.
762
		- can be seen by this user.
763
		- are actually the latest posts. */
764
765
	$done = false;
766
	$loops = 0;
767
	while (!$done)
768
	{
769
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
770
		$request = $smcFunc['db_query']('', '
771
			SELECT
772
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
773
				m.icon, t.id_topic, t.id_board, t.num_replies,
774
				b.name AS bname,
775
				COALESCE(mem.id_member, 0) AS id_member,
776
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
777
				COALESCE(mem.real_name, m.poster_name) AS poster_name
778
			FROM {db_prefix}topics AS t
779
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
780
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
781
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
782
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
783
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
784
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
785
				AND t.approved = {int:is_approved}' : '') . '
786
			ORDER BY t.id_first_msg DESC
787
			LIMIT {int:limit}',
788
			array(
789
				'current_board' => $board,
790
				'is_approved' => 1,
791
				'limit' => $_GET['limit'],
792
				'optimize_msg' => $optimize_msg,
793
			)
794
		);
795
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
796
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
797
		{
798
			$smcFunc['db_free_result']($request);
799 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
800
				unset($context['optimize_msg']['lowest']);
801
			else
802
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
803
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
804
			$loops++;
805
		}
806
		else
807
			$done = true;
808
	}
809
	$data = array();
810
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Bug introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
811
	{
812
		// Limit the length of the message, if the option is set.
813 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
814
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
815
816
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
817
818
		censorText($row['body']);
819
		censorText($row['subject']);
820
821
		// Do we want to include any attachments?
822 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
823
		{
824
			$attach_request = $smcFunc['db_query']('', '
825
				SELECT
826
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
827
				FROM {db_prefix}attachments AS a
828
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
829
				WHERE a.attachment_type = {int:attachment_type}
830
					AND a.id_msg = {int:message_id}',
831
				array(
832
					'message_id' => $row['id_msg'],
833
					'attachment_type' => 0,
834
					'is_approved' => 1,
835
				)
836
			);
837
			$loaded_attachments = array();
838
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
839
			{
840
				// Include approved attachments only
841
				if ($attach['approved'])
842
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
843
			}
844
			$smcFunc['db_free_result']($attach_request);
845
846
			// Sort the attachments by size to make things easier below
847
			if (!empty($loaded_attachments))
848
			{
849
				uasort($loaded_attachments, function($a, $b) {
850
					if ($a['filesize'] == $b['filesize'])
851
					        return 0;
852
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
853
				});
854
			}
855
			else
856
				$loaded_attachments = null;
857
		}
858
		else
859
			$loaded_attachments = null;
860
861
		// Being news, this actually makes sense in rss format.
862
		if ($xml_format == 'rss' || $xml_format == 'rss2')
863
		{
864
			// Only one attachment allowed in RSS.
865 View Code Duplication
			if ($loaded_attachments !== null)
866
			{
867
				$attachment = array_pop($loaded_attachments);
868
				$enclosure = array(
869
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
870
					'length' => $attachment['filesize'],
871
					'type' => $attachment['mime_type'],
872
				);
873
			}
874
			else
875
				$enclosure = null;
876
877
			$data[] = array(
878
				'tag' => 'item',
879
				'content' => array(
880
					array(
881
						'tag' => 'title',
882
						'content' => $row['subject'],
883
					),
884
					array(
885
						'tag' => 'link',
886
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
887
					),
888
					array(
889
						'tag' => 'description',
890
						'content' => $row['body'],
891
					),
892
					array(
893
						'tag' => 'author',
894
						'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' (' . $row['poster_name'] . ')' : null,
895
					),
896
					array(
897
						'tag' => 'comments',
898
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
899
					),
900
					array(
901
						'tag' => 'category',
902
						'content' => $row['bname'],
903
					),
904
					array(
905
						'tag' => 'pubDate',
906
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
907
					),
908
					array(
909
						'tag' => 'guid',
910
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
911
					),
912
					array(
913
						'tag' => 'enclosure',
914
						'attributes' => $enclosure,
915
					),
916
				),
917
			);
918
		}
919
		elseif ($xml_format == 'rdf')
920
		{
921
			$data[] = array(
922
				'tag' => 'item',
923
				'attributes' => array('rdf:about' => fix_possible_url($scripturl . '?topic=' . $row['id_topic'] . '.0')),
924
				'content' => array(
925
					array(
926
						'tag' => 'dc:format',
927
						'content' => 'text/html',
928
					),
929
					array(
930
						'tag' => 'title',
931
						'content' => $row['subject'],
932
					),
933
					array(
934
						'tag' => 'link',
935
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
936
					),
937
					array(
938
						'tag' => 'description',
939
						'content' => $row['body'],
940
					),
941
				),
942
			);
943
		}
944
		elseif ($xml_format == 'atom')
945
		{
946
			// Only one attachment allowed
947 View Code Duplication
			if (!empty($loaded_attachments))
948
			{
949
				$attachment = array_pop($loaded_attachments);
950
				$enclosure = array(
951
					'rel' => 'enclosure',
952
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
953
					'length' => $attachment['filesize'],
954
					'type' => $attachment['mime_type'],
955
				);
956
			}
957
			else
958
				$enclosure = null;
959
960
			$data[] = array(
961
				'tag' => 'entry',
962
				'content' => array(
963
					array(
964
						'tag' => 'title',
965
						'content' => $row['subject'],
966
					),
967
					array(
968
						'tag' => 'link',
969
						'attributes' => array(
970
							'rel' => 'alternate',
971
							'type' => 'text/html',
972
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
973
						),
974
					),
975
					array(
976
						'tag' => 'summary',
977
						'attributes' => array('type' => 'html'),
978
						'content' => $row['body'],
979
					),
980
					array(
981
						'tag' => 'category',
982
						'attributes' => array('term' => $row['bname']),
983
					),
984
					array(
985
						'tag' => 'author',
986
						'content' => array(
987
							array(
988
								'tag' => 'name',
989
								'content' => $row['poster_name'],
990
							),
991
							array(
992
								'tag' => 'email',
993
								'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null,
994
							),
995
							array(
996
								'tag' => 'uri',
997
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
998
							),
999
						)
1000
					),
1001
					array(
1002
						'tag' => 'published',
1003
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1004
					),
1005
					array(
1006
						'tag' => 'updated',
1007
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1008
					),
1009
					array(
1010
						'tag' => 'id',
1011
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1012
					),
1013
					array(
1014
						'tag' => 'link',
1015
						'attributes' => $enclosure,
1016
					),
1017
				),
1018
			);
1019
		}
1020
		// The biggest difference here is more information.
1021
		else
1022
		{
1023
			$attachments = array();
1024 View Code Duplication
			if (!empty($loaded_attachments))
1025
			{
1026
				foreach ($loaded_attachments as $attachment)
1027
				{
1028
					$attachments[] = array(
1029
						'tag' => 'attachment',
1030
						'content' => array(
1031
							array(
1032
								'tag' => 'id',
1033
								'content' => $attachment['id_attach'],
1034
							),
1035
							array(
1036
								'tag' => 'name',
1037
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1038
							),
1039
							array(
1040
								'tag' => 'downloads',
1041
								'content' => $attachment['downloads'],
1042
							),
1043
							array(
1044
								'tag' => 'size',
1045
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1046
							),
1047
							array(
1048
								'tag' => 'byte_size',
1049
								'content' => $attachment['filesize'],
1050
							),
1051
							array(
1052
								'tag' => 'link',
1053
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1054
							),
1055
						)
1056
					);
1057
				}
1058
			}
1059
			else
1060
				$attachments = null;
1061
1062
			$data[] = array(
1063
				'tag' => 'article',
1064
				'content' => array(
1065
					array(
1066
						'tag' => 'time',
1067
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1068
					),
1069
					array(
1070
						'tag' => 'id',
1071
						'content' => $row['id_topic'],
1072
					),
1073
					array(
1074
						'tag' => 'subject',
1075
						'content' => $row['subject'],
1076
					),
1077
					array(
1078
						'tag' => 'body',
1079
						'content' => $row['body'],
1080
					),
1081
					array(
1082
						'tag' => 'poster',
1083
						'content' => array(
1084
							array(
1085
								'tag' => 'name',
1086
								'content' => $row['poster_name'],
1087
							),
1088
							array(
1089
								'tag' => 'id',
1090
								'content' => $row['id_member'],
1091
							),
1092
							array(
1093
								'tag' => 'link',
1094
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1095
							),
1096
						)
1097
					),
1098
					array(
1099
						'tag' => 'topic',
1100
						'content' => $row['id_topic'],
1101
					),
1102
					array(
1103
						'tag' => 'board',
1104
						'content' => array(
1105
							array(
1106
								'tag' => 'name',
1107
								'content' => $row['bname'],
1108
							),
1109
							array(
1110
								'tag' => 'id',
1111
								'content' => $row['id_board'],
1112
							),
1113
							array(
1114
								'tag' => 'link',
1115
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1116
							),
1117
						),
1118
					),
1119
					array(
1120
						'tag' => 'link',
1121
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1122
					),
1123
					array(
1124
						'tag' => 'attachments',
1125
						'content' => $attachments,
1126
					),
1127
				),
1128
			);
1129
		}
1130
	}
1131
	$smcFunc['db_free_result']($request);
1132
1133
	return $data;
1134
}
1135
1136
/**
1137
 * Get the recent topics to display.
1138
 * The returned array will be generated to match the xml_format.
1139
 * @todo does not belong here.
1140
 *
1141
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
1142
 * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format.
1143
 */
1144
function getXmlRecent($xml_format)
1145
{
1146
	global $scripturl, $modSettings, $board, $txt;
1147
	global $query_this_board, $smcFunc, $context, $user_info, $sourcedir;
1148
1149
	require_once($sourcedir . '/Subs-Attachments.php');
1150
1151
	$done = false;
1152
	$loops = 0;
1153
	while (!$done)
1154
	{
1155
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
1156
		$request = $smcFunc['db_query']('', '
1157
			SELECT m.id_msg
1158
			FROM {db_prefix}messages AS m
1159
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1160
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1161
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
1162
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
1163
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
1164
				AND m.approved = {int:is_approved}' : '') . '
1165
			ORDER BY m.id_msg DESC
1166
			LIMIT {int:limit}',
1167
			array(
1168
				'limit' => $_GET['limit'],
1169
				'current_board' => $board,
1170
				'is_approved' => 1,
1171
				'optimize_msg' => $optimize_msg,
1172
			)
1173
		);
1174
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
1175
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
1176
		{
1177
			$smcFunc['db_free_result']($request);
1178 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
1179
				unset($context['optimize_msg']['lowest']);
1180
			else
1181
				$context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
1182
			$loops++;
1183
		}
1184
		else
1185
			$done = true;
1186
	}
1187
	$messages = array();
1188
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Bug introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1189
		$messages[] = $row['id_msg'];
1190
	$smcFunc['db_free_result']($request);
1191
1192
	if (empty($messages))
1193
		return array();
1194
1195
	// Find the most recent posts this user can see.
1196
	$request = $smcFunc['db_query']('', '
1197
		SELECT
1198
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
1199
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
1200
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
1201
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
1202
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
1203
		FROM {db_prefix}messages AS m
1204
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1205
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1206
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1207
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1208
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1209
		WHERE m.id_msg IN ({array_int:message_list})
1210
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
1211
		ORDER BY m.id_msg DESC
1212
		LIMIT {int:limit}',
1213
		array(
1214
			'limit' => $_GET['limit'],
1215
			'current_board' => $board,
1216
			'message_list' => $messages,
1217
		)
1218
	);
1219
	$data = array();
1220
	while ($row = $smcFunc['db_fetch_assoc']($request))
1221
	{
1222
		// Limit the length of the message, if the option is set.
1223 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
1224
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
1225
1226
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1227
1228
		censorText($row['body']);
1229
		censorText($row['subject']);
1230
1231
		// Do we want to include any attachments?
1232 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
1233
		{
1234
			$attach_request = $smcFunc['db_query']('', '
1235
				SELECT
1236
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
1237
				FROM {db_prefix}attachments AS a
1238
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
1239
				WHERE a.attachment_type = {int:attachment_type}
1240
					AND a.id_msg = {int:message_id}',
1241
				array(
1242
					'message_id' => $row['id_msg'],
1243
					'attachment_type' => 0,
1244
					'is_approved' => 1,
1245
				)
1246
			);
1247
			$loaded_attachments = array();
1248
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
1249
			{
1250
				// Include approved attachments only
1251
				if ($attach['approved'])
1252
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
1253
			}
1254
			$smcFunc['db_free_result']($attach_request);
1255
1256
			// Sort the attachments by size to make things easier below
1257
			if (!empty($loaded_attachments))
1258
			{
1259
				uasort($loaded_attachments, function($a, $b) {
1260
					if ($a['filesize'] == $b['filesize'])
1261
					        return 0;
1262
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
1263
				});
1264
			}
1265
			else
1266
				$loaded_attachments = null;
1267
		}
1268
		else
1269
			$loaded_attachments = null;
1270
1271
		// Doesn't work as well as news, but it kinda does..
1272
		if ($xml_format == 'rss' || $xml_format == 'rss2')
1273
		{
1274
			// Only one attachment allowed in RSS.
1275 View Code Duplication
			if ($loaded_attachments !== null)
1276
			{
1277
				$attachment = array_pop($loaded_attachments);
1278
				$enclosure = array(
1279
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1280
					'length' => $attachment['filesize'],
1281
					'type' => $attachment['mime_type'],
1282
				);
1283
			}
1284
			else
1285
				$enclosure = null;
1286
1287
			$data[] = array(
1288
				'tag' => 'item',
1289
				'content' => array(
1290
					array(
1291
						'tag' => 'title',
1292
						'content' => $row['subject'],
1293
					),
1294
					array(
1295
						'tag' => 'link',
1296
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1297
					),
1298
					array(
1299
						'tag' => 'description',
1300
						'content' => $row['body'],
1301
					),
1302
					array(
1303
						'tag' => 'author',
1304
						'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1305
					),
1306
					array(
1307
						'tag' => 'category',
1308
						'content' => $row['bname'],
1309
					),
1310
					array(
1311
						'tag' => 'comments',
1312
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
1313
					),
1314
					array(
1315
						'tag' => 'pubDate',
1316
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
1317
					),
1318
					array(
1319
						'tag' => 'guid',
1320
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1321
					),
1322
					array(
1323
						'tag' => 'enclosure',
1324
						'attributes' => $enclosure,
1325
					),
1326
				),
1327
			);
1328
		}
1329
		elseif ($xml_format == 'rdf')
1330
		{
1331
			$data[] = array(
1332
				'tag' => 'item',
1333
				'attributes' => array('rdf:about' => fix_possible_url($scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'])),
1334
				'content' => array(
1335
					array(
1336
						'tag' => 'dc:format',
1337
						'content' => 'text/html',
1338
					),
1339
					array(
1340
						'tag' => 'title',
1341
						'content' => $row['subject'],
1342
					),
1343
					array(
1344
						'tag' => 'link',
1345
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1346
					),
1347
					array(
1348
						'tag' => 'description',
1349
						'content' => $row['body'],
1350
					),
1351
				),
1352
			);
1353
		}
1354
		elseif ($xml_format == 'atom')
1355
		{
1356
			// Only one attachment allowed
1357 View Code Duplication
			if (!empty($loaded_attachments))
1358
			{
1359
				$attachment = array_pop($loaded_attachments);
1360
				$enclosure = array(
1361
					'rel' => 'enclosure',
1362
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1363
					'length' => $attachment['filesize'],
1364
					'type' => $attachment['mime_type'],
1365
				);
1366
			}
1367
			else
1368
				$enclosure = null;
1369
1370
			$data[] = array(
1371
				'tag' => 'entry',
1372
				'content' => array(
1373
					array(
1374
						'tag' => 'title',
1375
						'content' => $row['subject'],
1376
					),
1377
					array(
1378
						'tag' => 'link',
1379
						'attributes' => array(
1380
							'rel' => 'alternate',
1381
							'type' => 'text/html',
1382
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1383
						),
1384
					),
1385
					array(
1386
						'tag' => 'summary',
1387
						'attributes' => array('type' => 'html'),
1388
						'content' => $row['body'],
1389
					),
1390
					array(
1391
						'tag' => 'category',
1392
						'attributes' => array('term' => $row['bname']),
1393
					),
1394
					array(
1395
						'tag' => 'author',
1396
						'content' => array(
1397
							array(
1398
								'tag' => 'name',
1399
								'content' => $row['poster_name'],
1400
							),
1401
							array(
1402
								'tag' => 'email',
1403
								'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1404
							),
1405
							array(
1406
								'tag' => 'uri',
1407
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1408
							),
1409
						),
1410
					),
1411
					array(
1412
						'tag' => 'published',
1413
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1414
					),
1415
					array(
1416
						'tag' => 'updated',
1417
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1418
					),
1419
					array(
1420
						'tag' => 'id',
1421
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1422
					),
1423
					array(
1424
						'tag' => 'link',
1425
						'attributes' => $enclosure,
1426
					),
1427
				),
1428
			);
1429
		}
1430
		// A lot of information here.  Should be enough to please the rss-ers.
1431
		else
1432
		{
1433
			$attachments = array();
1434 View Code Duplication
			if (!empty($loaded_attachments))
1435
			{
1436
				foreach ($loaded_attachments as $attachment)
1437
				{
1438
					$attachments[] = array(
1439
						'tag' => 'attachment',
1440
						'content' => array(
1441
							array(
1442
								'tag' => 'id',
1443
								'content' => $attachment['id_attach'],
1444
							),
1445
							array(
1446
								'tag' => 'name',
1447
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1448
							),
1449
							array(
1450
								'tag' => 'downloads',
1451
								'content' => $attachment['downloads'],
1452
							),
1453
							array(
1454
								'tag' => 'size',
1455
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1456
							),
1457
							array(
1458
								'tag' => 'byte_size',
1459
								'content' => $attachment['filesize'],
1460
							),
1461
							array(
1462
								'tag' => 'link',
1463
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1464
							),
1465
						)
1466
					);
1467
				}
1468
			}
1469
			else
1470
				$attachments = null;
1471
1472
			$data[] = array(
1473
				'tag' => 'recent-post',
1474
				'content' => array(
1475
					array(
1476
						'tag' => 'time',
1477
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1478
					),
1479
					array(
1480
						'tag' => 'id',
1481
						'content' => $row['id_msg'],
1482
					),
1483
					array(
1484
						'tag' => 'subject',
1485
						'content' => $row['subject'],
1486
					),
1487
					array(
1488
						'tag' => 'body',
1489
						'content' => $row['body'],
1490
					),
1491
					array(
1492
						'tag' => 'starter',
1493
						'content' => array(
1494
							array(
1495
								'tag' => 'name',
1496
								'content' => $row['first_poster_name'],
1497
							),
1498
							array(
1499
								'tag' => 'id',
1500
								'content' => $row['id_first_member'],
1501
							),
1502
							array(
1503
								'tag' => 'link',
1504
								'content' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '',
1505
							),
1506
						),
1507
					),
1508
					array(
1509
						'tag' => 'poster',
1510
						'content' => array(
1511
							array(
1512
								'tag' => 'name',
1513
								'content' => $row['poster_name'],
1514
							),
1515
							array(
1516
								'tag' => 'id',
1517
								'content' => $row['id_member'],
1518
							),
1519
							array(
1520
								'tag' => 'link',
1521
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1522
							),
1523
						),
1524
					),
1525
					array(
1526
						'tag' => 'topic',
1527
						'content' => array(
1528
							array(
1529
								'tag' => 'subject',
1530
								'content' => $row['first_subject'],
1531
							),
1532
							array(
1533
								'tag' => 'id',
1534
								'content' => $row['id_topic'],
1535
							),
1536
							array(
1537
								'tag' => 'link',
1538
								'content' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new',
1539
							),
1540
						),
1541
					),
1542
					array(
1543
						'tag' => 'board',
1544
						'content' => array(
1545
							array(
1546
								'tag' => 'name',
1547
								'content' => $row['bname'],
1548
							),
1549
							array(
1550
								'tag' => 'id',
1551
								'content' => $row['id_board'],
1552
							),
1553
							array(
1554
								'tag' => 'link',
1555
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1556
							),
1557
						),
1558
					),
1559
					array(
1560
						'tag' => 'link',
1561
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1562
					),
1563
					array(
1564
						'tag' => 'attachments',
1565
						'content' => $attachments,
1566
					),
1567
				),
1568
			);
1569
		}
1570
	}
1571
	$smcFunc['db_free_result']($request);
1572
1573
	return $data;
1574
}
1575
1576
/**
1577
 * Get the profile information for member into an array,
1578
 * which will be generated to match the xml_format.
1579
 * @todo refactor.
1580
 *
1581
 * @param $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
1582
 * @return array An array profile data
1583
 */
1584
function getXmlProfile($xml_format)
1585
{
1586
	global $scripturl, $memberContext, $user_profile, $user_info;
1587
1588
	// You must input a valid user....
1589
	if (empty($_GET['u']) || !loadMemberData((int) $_GET['u']))
1590
		return array();
1591
1592
	// Make sure the id is a number and not "I like trying to hack the database".
1593
	$_GET['u'] = (int) $_GET['u'];
1594
	// Load the member's contextual information!
1595
	if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view'))
1596
		return array();
1597
1598
	// Okay, I admit it, I'm lazy.  Stupid $_GET['u'] is long and hard to type.
1599
	$profile = &$memberContext[$_GET['u']];
1600
1601
	if ($xml_format == 'rss' || $xml_format == 'rss2')
1602
	{
1603
		$data[] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1604
			'tag' => 'item',
1605
			'content' => array(
1606
				array(
1607
					'tag' => 'title',
1608
					'content' => $profile['name'],
1609
				),
1610
				array(
1611
					'tag' => 'link',
1612
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1613
				),
1614
				array(
1615
					'tag' => 'description',
1616
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1617
				),
1618
				array(
1619
					'tag' => 'comments',
1620
					'content' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'],
1621
				),
1622
				array(
1623
					'tag' => 'pubDate',
1624
					'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1625
				),
1626
				array(
1627
					'tag' => 'guid',
1628
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1629
				),
1630
			)
1631
		);
1632
	}
1633
	elseif ($xml_format == 'rdf')
1634
	{
1635
		$data[] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1636
			'tag' => 'item',
1637
			'attributes' => array('rdf:about' => fix_possible_url($scripturl . '?action=profile;u=' . $profile['id'])),
1638
			'content' => array(
1639
				array(
1640
					'tag' => 'dc:format',
1641
					'content' => 'text/html',
1642
				),
1643
				array(
1644
					'tag' => 'title',
1645
					'content' => $profile['name'],
1646
				),
1647
				array(
1648
					'tag' => 'link',
1649
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1650
				),
1651
				array(
1652
					'tag' => 'description',
1653
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1654
				),
1655
			)
1656
		);
1657
	}
1658
	elseif ($xml_format == 'atom')
1659
	{
1660
		$data[] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1661
			'tag' => 'entry',
1662
			'content' => array(
1663
				array(
1664
					'tag' => 'title',
1665
					'content' => $profile['name'],
1666
				),
1667
				array(
1668
					'tag' => 'link',
1669
					'attributes' => array(
1670
						'rel' => 'alternate',
1671
						'type' => 'text/html',
1672
						'href' => $scripturl . '?action=profile;u=' . $profile['id'],
1673
					),
1674
				),
1675
				array(
1676
					'tag' => 'summary',
1677
					'attributes' => array('type' => 'html'),
1678
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1679
				),
1680
				array(
1681
					'tag' => 'author',
1682
					'content' => array(
1683
						array(
1684
							'tag' => 'name',
1685
							'content' => $profile['name'],
1686
						),
1687
						array(
1688
							'tag' => 'email',
1689
							'content' => $profile['show_email'] ? $profile['email'] : null,
1690
						),
1691
						array(
1692
							'tag' => 'uri',
1693
							'content' => !empty($profile['website']['url']) ? $profile['website']['url'] : null,
1694
						),
1695
					),
1696
				),
1697
				array(
1698
					'tag' => 'published',
1699
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']),
1700
				),
1701
				array(
1702
					'tag' => 'updated',
1703
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']),
1704
				),
1705
				array(
1706
					'tag' => 'id',
1707
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1708
				),
1709
			)
1710
		);
1711
	}
1712
	else
1713
	{
1714
		$data = array(
1715
			array(
1716
				'tag' => 'username',
1717
				'content' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : null,
1718
			),
1719
			array(
1720
				'tag' => 'name',
1721
				'content' => $profile['name'],
1722
			),
1723
			array(
1724
				'tag' => 'link',
1725
				'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1726
			),
1727
			array(
1728
				'tag' => 'posts',
1729
				'content' => $profile['posts'],
1730
			),
1731
			array(
1732
				'tag' => 'post-group',
1733
				'content' => $profile['post_group'],
1734
			),
1735
			array(
1736
				'tag' => 'language',
1737
				'content' => $profile['language'],
1738
			),
1739
			array(
1740
				'tag' => 'last-login',
1741
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']),
1742
			),
1743
			array(
1744
				'tag' => 'registered',
1745
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1746
			),
1747
			array(
1748
				'tag' => 'gender',
1749
				'content' => !empty($profile['gender']['name']) ? $profile['gender']['name'] : null,
1750
			),
1751
			array(
1752
				'tag' => 'avatar',
1753
				'content' => !empty($profile['avatar']['url']) ? $profile['avatar']['url'] : null,
1754
			),
1755
			array(
1756
				'tag' => 'online',
1757
				'content' => !empty($profile['online']['is_online']) ? '' : null,
1758
			),
1759
			array(
1760
				'tag' => 'signature',
1761
				'content' => !empty($profile['signature']) ? $profile['signature'] : null,
1762
			),
1763
			array(
1764
				'tag' => 'blurb',
1765
				'content' => !empty($profile['blurb']) ? $profile['blurb'] : null,
1766
			),
1767
			array(
1768
				'tag' => 'title',
1769
				'content' => !empty($profile['title']) ? $profile['title'] : null,
1770
			),
1771
			array(
1772
				'tag' => 'position',
1773
				'content' => !empty($profile['group']) ? $profile['group'] : null,
1774
			),
1775
			array(
1776
				'tag' => 'email',
1777
				'content' => !empty($profile['show_email']) ? $profile['show_email'] : null,
1778
			),
1779
			array(
1780
				'tag' => 'website',
1781
				'content' => empty($profile['website']['url']) ? null : array(
1782
					array(
1783
						'tag' => 'title',
1784
						'content' => !empty($profile['website']['title']) ? $profile['website']['title'] : null,
1785
					),
1786
					array(
1787
						'tag' => 'link',
1788
						'content' => $profile['website']['url'],
1789
					),
1790
				),
1791
			),
1792
		);
1793
1794
		if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000')
1795
		{
1796
			list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d');
1797
			$datearray = getdate(forum_time());
1798
			$age = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
1799
1800
			$data[] = array(
1801
				'tag' => 'age',
1802
				'content' => $age,
1803
			);
1804
1805
		}
1806
	}
1807
1808
	// Save some memory.
1809
	unset($profile, $memberContext[$_GET['u']]);
1810
1811
	return $data;
1812
}
1813
1814
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...