Completed
Pull Request — patch_1-1-4 (#3191)
by Emanuele
11:20
created

Agreement::checkAccepted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 2
dl 0
loc 15
ccs 0
cts 14
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This class takes care of the registration agreement
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * @version 1.1
11
 *
12
 */
13
14
/**
15
 * Class Agreement
16
 *
17
 * A simple class to take care of the registration agreement
18
 */
19
class Agreement
20
{
21
	/**
22
	 * Default language of the agreement.
23
	 *
24
	 * @var string
25
	 */
26
	protected $_language = '';
27
28
	/**
29
	 * The directory where backups are stored
30
	 *
31
	 * @var string
32
	 */
33
	protected $_backup_dir = '';
34
35
	/**
36
	 * The name of the file where the agreement is stored
37
	 *
38
	 * @var string
39
	 */
40
	protected $_file_name = 'agreement';
41
42
	/**
43
	 * The name of the directory where the backup will be saved
44
	 *
45
	 * @var string
46
	 */
47
	protected $_backupdir_name = 'agreements';
48
49
	/**
50
	 * The name of the log table
51
	 *
52
	 * @var string
53
	 */
54
	protected $_log_table_name = '{db_prefix}log_agreement_accept';
55
56
	/**
57
	 * The database object
58
	 *
59
	 * @var Object
60
	 */
61
	protected $_db = null;
62
63
	/**
64
	 * Everything starts here.
65
	 *
66
	 * @param string $language the wanted language of the agreement.
67
	 * @param string $backup_dir where to store the backup of the agreements.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $backup_dir not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
68
	 */
69
	public function __construct($language, $backup_dir = null)
70
	{
71
		$this->_language = strtr($language, array('.' => ''));
72
		if ($backup_dir === null || file_exists($backup_dir) === false)
73
		{
74
			$backup_dir = BOARDDIR . '/packages/backups/' . $this->_backupdir_name;
75
		}
76
		$this->_backup_dir = $backup_dir;
77
		$this->_db = database();
78
	}
79
80
	/**
81
	 * Stores a text into the agreement file.
82
	 * It stores strictly on the *language* agreement, no fallback.
83
	 * If the language passed to the class is empty, then it uses agreement.txt.
84
	 *
85
	 * @param string $text the language of the agreement we want.
86
	 * @param bool $update_backup if store a copy of the text of the agreements.
87
	 */
88
	public function save($text, $update_backup = false)
89
	{
90
		$backup_id = '';
91
		if ($update_backup === true)
92
		{
93
			$backup_id = $this->_backupId();
94
			if ($this->_createBackup($backup_id) === false)
95
			{
96
				$backup_id = false;
97
			}
98
		}
99
100
		// Off it goes to the agreement file.
101
		$fp = fopen(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt', 'w');
102
		fwrite($fp, str_replace("\r", '', $text));
103
		fclose($fp);
104
105
		return $backup_id;
106
	}
107
108
	/**
109
	 * Retrieves the plain text version of the agreement directly from
110
	 * the file that contains it.
111
	 *
112
	 * It uses the language, but if the localized version doesn't exist
113
	 * then it may return the english version.
114
	 *
115
	 * @param boolean $fallback if fallback to the English version (default true).
116
	 */
117
	public function getPlainText($fallback = true)
118
	{
119
		// Have we got a localized one?
120
		if (file_exists(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt'))
121
		{
122
			$agreement = file_get_contents(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt');
0 ignored issues
show
Security File Exposure introduced by
BOARDDIR . '/' . $this->...lizeLanguage() . '.txt' can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $_GET['language'] is passed through strtr(), and $user_info is assigned
    in sources/Load.php on line 411
  2. $user_info is assigned
    in sources/Load.php on line 419
  3. $user_info is assigned
    in sources/Load.php on line 428
  4. $user_info['language'] is passed to Agreement::__construct()
    in sources/Load.php on line 440
  5. $language is passed through strtr(), and Agreement::$_language is assigned
    in sources/subs/Agreement.class.php on line 71
  6. Tainted property Agreement::$_language is read
    in sources/subs/Agreement.class.php on line 216
  7. Agreement::normalizeLanguage() returns tainted data
    in sources/subs/Agreement.class.php on line 122

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...
123
		}
124
		elseif ($fallback === true && file_exists(BOARDDIR . '/' . $this->_file_name . '.txt'))
125
		{
126
			$agreement = file_get_contents(BOARDDIR . '/' . $this->_file_name . '.txt');
127
		}
128
		else
129
		{
130
			$agreement = '';
131
		}
132
133
		return $agreement;
134
	}
135
136
	/**
137
	 * Retrieves the BBC-parsed version of the agreement.
138
	 *
139
	 * It uses the language, but if the localized version doesn't exist
140
	 * then it may return the english version.
141
	 *
142
	 * @param boolean $fallback if fallback to the English version (default true).
143
	 */
144
	public function getParsedText($fallback = true)
145
	{
146
		$bbc_parser = \BBC\ParserWrapper::instance();
147
148
		return $bbc_parser->parseAgreement($this->getPlainText($fallback));
149
	}
150
151
	/**
152
	 * Retrieves the BBC-parsed version of the agreement.
153
	 *
154
	 * If the language passed to the class is empty, then it uses agreement.txt.
155
	 */
156
	public function isWritable()
157
	{
158
		$filename = BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt';
159
160
		return file_exists($filename) && is_writable($filename);
161
	}
162
163
	/**
164
	 * Test if the user accepted the current agreement or not.
165
	 *
166
	 * @param int $id_member The id of the member
167
	 * @param string $version The date of the agreement
168
	 */
169
	public function checkAccepted($id_member, $version)
170
	{
171
		$accepted = $this->_db->fetchQuery('
172
			SELECT 1
173
			FROM ' . $this->_log_table_name . '
174
			WHERE version = {string:version}
175
				AND id_member = {int:id_member}',
176
			array(
177
				'id_member' => $id_member,
178
				'version' => $version,
179
			)
180
		);
181
182
		return !empty($accepted);
183
	}
184
185 View Code Duplication
	public function accept($id_member, $ip, $version)
186
	{
187
		$db = database();
188
189
		$db->insert('',
190
			$this->_log_table_name,
191
			array(
192
				'version' => 'string-20',
193
				'id_member' => 'int',
194
				'accepted_date' => 'date',
195
				'accepted_ip' => 'string-255',
196
			),
197
			array(
198
				array(
199
					'version' => $version,
200
					'id_member' => $id_member,
201
					'accepted_date' => strftime('%Y-%m-%d', forum_time(false)),
202
					'accepted_ip' => $ip,
203
				)
204
			),
205
			array('version', 'id_member')
206
		);
207
	}
208
209
	/**
210
	 * Takes care of the edge-case of the default agreement that doesn't have
211
	 * the language in the name, and the fact that the admin panels loads it
212
	 * as an empty language.
213
	 */
214
	protected function normalizeLanguage()
215
	{
216
		return $this->_language === '' ? '' : '.' . $this->_language;
217
	}
218
219
	protected function _backupId()
220
	{
221
		$backup_id = strftime('%Y-%m-%d', forum_time(false));
222
		$counter = '';
223
		$merger = '';
224
225
		while (file_exists($this->_backup_dir . '/' . $backup_id . $merger . $counter . '/') === true)
226
		{
227
			$counter++;
228
			$merger = '_';
229
		}
230
231
		return $backup_id . $merger . $counter;
232
	}
233
234
	/**
235
	 * Creates a full backup of all the agreements.
236
	 *
237
	 * @param string $backup_id the name of the directory of the backup
238
	 * @return bool true if successful, false if failes to create the directory
239
	 */
240
	protected function _createBackup($backup_id)
241
	{
242
		$destination = $this->_backup_dir . '/' . $backup_id . '/';
243
		if (file_exists($this->_backup_dir) === false)
244
		{
245
			mkdir($this->_backup_dir);
246
		}
247
		if (mkdir($destination) === false)
248
		{
249
			return false;
250
		}
251
		$glob = new GlobIterator(BOARDDIR . '/' . $this->_file_name . '*.txt', FilesystemIterator::SKIP_DOTS);
252
		foreach ($glob as $file)
253
		{
254
			copy($file->getPathname(), $destination . $file->getBasename());
255
		}
256
		return true;
257
	}
258
}
259