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

Agreement::accept()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 16

Duplication

Lines 23
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 3
dl 23
loc 23
ccs 0
cts 0
cp 0
crap 2
rs 9.0856
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->storeBackup();
94
		}
95
96
		// Off it goes to the agreement file.
97
		$fp = fopen(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt', 'w');
98
		fwrite($fp, str_replace("\r", '', $text));
99
		fclose($fp);
100
101
		return $backup_id;
102
	}
103
104
	/**
105
	 * Creates a backup of the current version of the agreement/s.
106
	 *
107
	 * @return string|bool the backup_id if successful, false if creating the backup fails
108
	 */
109
	public function storeBackup()
110
	{
111
		$backup_id = $this->_backupId();
112
		if ($this->_createBackup($backup_id) === false)
113
		{
114
			$backup_id = false;
115
		}
116
117
		return $backup_id;
118
	}
119
120
	/**
121
	 * Retrieves the plain text version of the agreement directly from
122
	 * the file that contains it.
123
	 *
124
	 * It uses the language, but if the localized version doesn't exist
125
	 * then it may return the english version.
126
	 *
127
	 * @param boolean $fallback if fallback to the English version (default true).
128
	 */
129
	public function getPlainText($fallback = true)
130
	{
131
		// Have we got a localized one?
132
		if (file_exists(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt'))
133
		{
134
			$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 442
  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 228
  7. Agreement::normalizeLanguage() returns tainted data
    in sources/subs/Agreement.class.php on line 134

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