Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
45.97% |
371 / 807 |
|
6.25% |
2 / 32 |
CRAP | |
0.00% |
0 / 2 |
bbcode_firstpass | |
71.10% |
278 / 391 |
|
5.26% |
1 / 19 |
693.11 | |
0.00% |
0 / 1 |
parse_bbcode | |
66.67% |
12 / 18 |
|
0.00% |
0 / 1 |
13.70 | |||
prepare_bbcodes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
bbcode_init | |
79.55% |
70 / 88 |
|
0.00% |
0 / 1 |
7.42 | |||
check_bbcode | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
bbcode_specialchars | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
bbcode_size | |
55.56% |
5 / 9 |
|
0.00% |
0 / 1 |
7.19 | |||
bbcode_color | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
bbcode_underline | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
bbcode_strong | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
bbcode_italic | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
bbcode_img | |
76.92% |
10 / 13 |
|
0.00% |
0 / 1 |
7.60 | |||
bbcode_attachment | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
bbcode_parse_code | |
9.68% |
3 / 31 |
|
0.00% |
0 / 1 |
68.69 | |||
bbcode_code | |
55.26% |
21 / 38 |
|
0.00% |
0 / 1 |
28.13 | |||
bbcode_parse_list | |
81.63% |
40 / 49 |
|
0.00% |
0 / 1 |
26.28 | |||
bbcode_quote | |
91.18% |
62 / 68 |
|
0.00% |
0 / 1 |
29.58 | |||
validate_email | |
85.71% |
12 / 14 |
|
0.00% |
0 / 1 |
6.10 | |||
validate_url | |
65.22% |
15 / 23 |
|
0.00% |
0 / 1 |
24.47 | |||
path_in_domain | |
68.75% |
11 / 16 |
|
0.00% |
0 / 1 |
16.39 | |||
parse_message | |
22.36% |
93 / 416 |
|
7.69% |
1 / 13 |
9447.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
parse | |
83.33% |
70 / 84 |
|
0.00% |
0 / 1 |
36.74 | |||
format_display | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
30 | |||
decode_message | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
magic_url | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
smilies | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
132 | |||
check_attachment_form_token | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
42 | |||
parse_attachments | |
0.00% |
0 / 150 |
|
0.00% |
0 / 1 |
2352 | |||
get_submitted_attachment_data | |
0.00% |
0 / 43 |
|
0.00% |
0 / 1 |
132 | |||
parse_poll | |
76.92% |
20 / 26 |
|
0.00% |
0 / 1 |
15.08 | |||
remove_nested_quotes | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
56 | |||
set_plupload | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
validate_bbcode_by_extension | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | /** |
15 | * @ignore |
16 | */ |
17 | if (!defined('IN_PHPBB')) |
18 | { |
19 | exit; |
20 | } |
21 | |
22 | if (!class_exists('bbcode')) |
23 | { |
24 | // The following lines are for extensions which include message_parser.php |
25 | // while $phpbb_root_path and $phpEx are out of the script scope |
26 | // which may lead to the 'Undefined variable' and 'failed to open stream' errors |
27 | if (!isset($phpbb_root_path)) |
28 | { |
29 | global $phpbb_root_path; |
30 | } |
31 | |
32 | if (!isset($phpEx)) |
33 | { |
34 | global $phpEx; |
35 | } |
36 | |
37 | include($phpbb_root_path . 'includes/bbcode.' . $phpEx); |
38 | } |
39 | |
40 | /** |
41 | * BBCODE FIRSTPASS |
42 | * BBCODE first pass class (functions for parsing messages for db storage) |
43 | */ |
44 | class bbcode_firstpass extends bbcode |
45 | { |
46 | var $message = ''; |
47 | var $warn_msg = array(); |
48 | var $parsed_items = array(); |
49 | var $mode; |
50 | |
51 | /** |
52 | * Parse BBCode |
53 | */ |
54 | function parse_bbcode() |
55 | { |
56 | if (!$this->bbcodes) |
57 | { |
58 | $this->bbcode_init(); |
59 | } |
60 | |
61 | global $user; |
62 | |
63 | $this->bbcode_bitfield = ''; |
64 | $bitfield = new bitfield(); |
65 | |
66 | foreach ($this->bbcodes as $bbcode_name => $bbcode_data) |
67 | { |
68 | if (isset($bbcode_data['disabled']) && $bbcode_data['disabled']) |
69 | { |
70 | foreach ($bbcode_data['regexp'] as $regexp => $replacement) |
71 | { |
72 | if (preg_match($regexp, $this->message)) |
73 | { |
74 | $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']'); |
75 | continue; |
76 | } |
77 | } |
78 | } |
79 | else |
80 | { |
81 | foreach ($bbcode_data['regexp'] as $regexp => $replacement) |
82 | { |
83 | // The pattern gets compiled and cached by the PCRE extension, |
84 | // it should not demand recompilation |
85 | if (preg_match($regexp, $this->message)) |
86 | { |
87 | if (is_callable($replacement)) |
88 | { |
89 | $this->message = preg_replace_callback($regexp, $replacement, $this->message); |
90 | } |
91 | else |
92 | { |
93 | $this->message = preg_replace($regexp, $replacement, $this->message); |
94 | } |
95 | $bitfield->set($bbcode_data['bbcode_id']); |
96 | } |
97 | } |
98 | } |
99 | } |
100 | |
101 | $this->bbcode_bitfield = $bitfield->get_base64(); |
102 | } |
103 | |
104 | /** |
105 | * Prepare some bbcodes for better parsing |
106 | */ |
107 | function prepare_bbcodes() |
108 | { |
109 | // Ok, seems like users instead want the no-parsing of urls, smilies, etc. after and before and within quote tags being tagged as "not a bug". |
110 | // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you. |
111 | |
112 | /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.) |
113 | if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false) |
114 | { |
115 | $this->message = str_replace("\r\n", "\n", $this->message); |
116 | |
117 | // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline |
118 | $this->message = preg_replace('#\[quote(=".*?")?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message); |
119 | } |
120 | */ |
121 | |
122 | // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...) |
123 | } |
124 | |
125 | /** |
126 | * Init bbcode data for later parsing |
127 | */ |
128 | function bbcode_init($allow_custom_bbcode = true) |
129 | { |
130 | global $phpbb_dispatcher; |
131 | |
132 | static $rowset; |
133 | |
134 | $bbcode_class = $this; |
135 | |
136 | // This array holds all bbcode data. BBCodes will be processed in this |
137 | // order, so it is important to keep [code] in first position and |
138 | // [quote] in second position. |
139 | // To parse multiline URL we enable dotall option setting only for URL text |
140 | // but not for link itself, thus [url][/url] is not affected. |
141 | // |
142 | // To perform custom validation in extension, use $this->validate_bbcode_by_extension() |
143 | // method which accepts variable number of parameters |
144 | $this->bbcodes = array( |
145 | 'code' => array('bbcode_id' => BBCODE_ID_CODE, 'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uis' => function ($match) use($bbcode_class) |
146 | { |
147 | return $bbcode_class->bbcode_code($match[1], $match[2]); |
148 | } |
149 | )), |
150 | 'quote' => array('bbcode_id' => BBCODE_ID_QUOTE, 'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uis' => function ($match) use($bbcode_class) |
151 | { |
152 | return $bbcode_class->bbcode_quote($match[0]); |
153 | } |
154 | )), |
155 | 'attachment' => array('bbcode_id' => BBCODE_ID_ATTACH, 'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uis' => function ($match) use($bbcode_class) |
156 | { |
157 | return $bbcode_class->bbcode_attachment($match[1], $match[2]); |
158 | } |
159 | )), |
160 | 'b' => array('bbcode_id' => BBCODE_ID_B, 'regexp' => array('#\[b\](.*?)\[/b\]#uis' => function ($match) use($bbcode_class) |
161 | { |
162 | return $bbcode_class->bbcode_strong($match[1]); |
163 | } |
164 | )), |
165 | 'i' => array('bbcode_id' => BBCODE_ID_I, 'regexp' => array('#\[i\](.*?)\[/i\]#uis' => function ($match) use($bbcode_class) |
166 | { |
167 | return $bbcode_class->bbcode_italic($match[1]); |
168 | } |
169 | )), |
170 | 'url' => array('bbcode_id' => BBCODE_ID_URL, 'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiU' => function ($match) use($bbcode_class) |
171 | { |
172 | return $bbcode_class->validate_url($match[2], ($match[3]) ? $match[3] : $match[4]); |
173 | } |
174 | )), |
175 | 'img' => array('bbcode_id' => BBCODE_ID_IMG, 'regexp' => array('#\[img\](.*)\[/img\]#uiU' => function ($match) use($bbcode_class) |
176 | { |
177 | return $bbcode_class->bbcode_img($match[1]); |
178 | } |
179 | )), |
180 | 'size' => array('bbcode_id' => BBCODE_ID_SIZE, 'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uis' => function ($match) use($bbcode_class) |
181 | { |
182 | return $bbcode_class->bbcode_size($match[1], $match[2]); |
183 | } |
184 | )), |
185 | 'color' => array('bbcode_id' => BBCODE_ID_COLOR, 'regexp' => array('!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!uis' => function ($match) use($bbcode_class) |
186 | { |
187 | return $bbcode_class->bbcode_color($match[1], $match[2]); |
188 | } |
189 | )), |
190 | 'u' => array('bbcode_id' => BBCODE_ID_U, 'regexp' => array('#\[u\](.*?)\[/u\]#uis' => function ($match) use($bbcode_class) |
191 | { |
192 | return $bbcode_class->bbcode_underline($match[1]); |
193 | } |
194 | )), |
195 | 'list' => array('bbcode_id' => BBCODE_ID_LIST, 'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uis' => function ($match) use($bbcode_class) |
196 | { |
197 | return $bbcode_class->bbcode_parse_list($match[0]); |
198 | } |
199 | )), |
200 | 'email' => array('bbcode_id' => BBCODE_ID_EMAIL, 'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uis' => function ($match) use($bbcode_class) |
201 | { |
202 | return $bbcode_class->validate_email($match[1], $match[2]); |
203 | } |
204 | )), |
205 | ); |
206 | |
207 | // Zero the parsed items array |
208 | $this->parsed_items = array(); |
209 | |
210 | foreach ($this->bbcodes as $tag => $bbcode_data) |
211 | { |
212 | $this->parsed_items[$tag] = 0; |
213 | } |
214 | |
215 | if (!$allow_custom_bbcode) |
216 | { |
217 | return; |
218 | } |
219 | |
220 | if (!is_array($rowset)) |
221 | { |
222 | global $db; |
223 | $rowset = array(); |
224 | |
225 | $sql = 'SELECT * |
226 | FROM ' . BBCODES_TABLE; |
227 | $result = $db->sql_query($sql); |
228 | |
229 | while ($row = $db->sql_fetchrow($result)) |
230 | { |
231 | $rowset[] = $row; |
232 | } |
233 | $db->sql_freeresult($result); |
234 | } |
235 | |
236 | foreach ($rowset as $row) |
237 | { |
238 | $this->bbcodes[$row['bbcode_tag']] = array( |
239 | 'bbcode_id' => (int) $row['bbcode_id'], |
240 | 'regexp' => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace'])) |
241 | ); |
242 | } |
243 | |
244 | $bbcodes = $this->bbcodes; |
245 | |
246 | /** |
247 | * Event to modify the bbcode data for later parsing |
248 | * |
249 | * @event core.modify_bbcode_init |
250 | * @var array bbcodes Array of bbcode data for use in parsing |
251 | * @var array rowset Array of bbcode data from the database |
252 | * @since 3.1.0-a3 |
253 | */ |
254 | $vars = array('bbcodes', 'rowset'); |
255 | extract($phpbb_dispatcher->trigger_event('core.modify_bbcode_init', compact($vars))); |
256 | |
257 | $this->bbcodes = $bbcodes; |
258 | } |
259 | |
260 | /** |
261 | * Making some pre-checks for bbcodes as well as increasing the number of parsed items |
262 | */ |
263 | function check_bbcode($bbcode, &$in) |
264 | { |
265 | // when using the /e modifier, preg_replace slashes double-quotes but does not |
266 | // seem to slash anything else |
267 | $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in)); |
268 | |
269 | // Trimming here to make sure no empty bbcodes are parsed accidently |
270 | if (trim($in) == '') |
271 | { |
272 | return false; |
273 | } |
274 | |
275 | $this->parsed_items[$bbcode]++; |
276 | |
277 | return true; |
278 | } |
279 | |
280 | /** |
281 | * Transform some characters in valid bbcodes |
282 | */ |
283 | function bbcode_specialchars($text) |
284 | { |
285 | $str_from = array('<', '>', '[', ']', '.', ':'); |
286 | $str_to = array('<', '>', '[', ']', '.', ':'); |
287 | |
288 | return str_replace($str_from, $str_to, $text); |
289 | } |
290 | |
291 | /** |
292 | * Parse size tag |
293 | */ |
294 | function bbcode_size($stx, $in) |
295 | { |
296 | global $user, $config; |
297 | |
298 | if (!$this->check_bbcode('size', $in)) |
299 | { |
300 | return $in; |
301 | } |
302 | |
303 | if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx) |
304 | { |
305 | $this->warn_msg[] = $user->lang('MAX_FONT_SIZE_EXCEEDED', (int) $config['max_' . $this->mode . '_font_size']); |
306 | |
307 | return '[size=' . $stx . ']' . $in . '[/size]'; |
308 | } |
309 | |
310 | // Do not allow size=0 |
311 | if ($stx <= 0) |
312 | { |
313 | return '[size=' . $stx . ']' . $in . '[/size]'; |
314 | } |
315 | |
316 | return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']'; |
317 | } |
318 | |
319 | /** |
320 | * Parse color tag |
321 | */ |
322 | function bbcode_color($stx, $in) |
323 | { |
324 | if (!$this->check_bbcode('color', $in)) |
325 | { |
326 | return $in; |
327 | } |
328 | |
329 | return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']'; |
330 | } |
331 | |
332 | /** |
333 | * Parse u tag |
334 | */ |
335 | function bbcode_underline($in) |
336 | { |
337 | if (!$this->check_bbcode('u', $in)) |
338 | { |
339 | return $in; |
340 | } |
341 | |
342 | return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']'; |
343 | } |
344 | |
345 | /** |
346 | * Parse b tag |
347 | */ |
348 | function bbcode_strong($in) |
349 | { |
350 | if (!$this->check_bbcode('b', $in)) |
351 | { |
352 | return $in; |
353 | } |
354 | |
355 | return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']'; |
356 | } |
357 | |
358 | /** |
359 | * Parse i tag |
360 | */ |
361 | function bbcode_italic($in) |
362 | { |
363 | if (!$this->check_bbcode('i', $in)) |
364 | { |
365 | return $in; |
366 | } |
367 | |
368 | return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']'; |
369 | } |
370 | |
371 | /** |
372 | * Parse img tag |
373 | */ |
374 | function bbcode_img($in) |
375 | { |
376 | global $user, $config; |
377 | |
378 | if (!$this->check_bbcode('img', $in)) |
379 | { |
380 | return $in; |
381 | } |
382 | |
383 | $in = trim($in); |
384 | $error = false; |
385 | |
386 | $in = str_replace(' ', '%20', $in); |
387 | |
388 | // Checking urls |
389 | if (!preg_match('#^' . get_preg_expression('url_http') . '$#iu', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in)) |
390 | { |
391 | return '[img]' . $in . '[/img]'; |
392 | } |
393 | |
394 | // Try to cope with a common user error... not specifying a protocol but only a subdomain |
395 | if (!preg_match('#^[a-z0-9]+://#i', $in)) |
396 | { |
397 | $in = 'http://' . $in; |
398 | } |
399 | |
400 | if ($error || $this->path_in_domain($in)) |
401 | { |
402 | return '[img]' . $in . '[/img]'; |
403 | } |
404 | |
405 | return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']'; |
406 | } |
407 | |
408 | /** |
409 | * Parse inline attachments [ia] |
410 | */ |
411 | function bbcode_attachment($stx, $in) |
412 | { |
413 | if (!$this->check_bbcode('attachment', $in)) |
414 | { |
415 | return $in; |
416 | } |
417 | |
418 | return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']'; |
419 | } |
420 | |
421 | /** |
422 | * Parse code text from code tag |
423 | * @access private |
424 | */ |
425 | function bbcode_parse_code($stx, &$code) |
426 | { |
427 | switch (strtolower($stx)) |
428 | { |
429 | case 'php': |
430 | |
431 | $remove_tags = false; |
432 | |
433 | $str_from = array('<', '>', '[', ']', '.', ':', ':'); |
434 | $str_to = array('<', '>', '[', ']', '.', ':', ':'); |
435 | $code = str_replace($str_from, $str_to, $code); |
436 | |
437 | if (!preg_match('/\<\?.*?\?\>/is', $code)) |
438 | { |
439 | $remove_tags = true; |
440 | $code = "<?php $code ?>"; |
441 | } |
442 | |
443 | $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string'); |
444 | foreach ($conf as $ini_var) |
445 | { |
446 | @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var)); |
447 | } |
448 | |
449 | // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results |
450 | $code = html_entity_decode($code, ENT_COMPAT); |
451 | $code = highlight_string($code, true); |
452 | |
453 | $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':'); |
454 | $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '[', ']', '.', ':'); |
455 | |
456 | if ($remove_tags) |
457 | { |
458 | $str_from[] = '<span class="syntaxdefault"><?php </span>'; |
459 | $str_to[] = ''; |
460 | $str_from[] = '<span class="syntaxdefault"><?php '; |
461 | $str_to[] = '<span class="syntaxdefault">'; |
462 | } |
463 | |
464 | $code = str_replace($str_from, $str_to, $code); |
465 | $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code); |
466 | |
467 | if ($remove_tags) |
468 | { |
469 | $code = preg_replace('#(<span class="[a-z]+">)?\?>(</span>)#', '$1 $2', $code); |
470 | } |
471 | |
472 | $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code); |
473 | $code = preg_replace('#(?:\s++| )*+</span>$#u', '</span>', $code); |
474 | |
475 | // remove newline at the end |
476 | if (!empty($code) && substr($code, -1) == "\n") |
477 | { |
478 | $code = substr($code, 0, -1); |
479 | } |
480 | |
481 | return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']'; |
482 | break; |
483 | |
484 | default: |
485 | return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']'; |
486 | break; |
487 | } |
488 | } |
489 | |
490 | /** |
491 | * Parse code tag |
492 | * Expects the argument to start right after the opening [code] tag and to end with [/code] |
493 | */ |
494 | function bbcode_code($stx, $in) |
495 | { |
496 | if (!$this->check_bbcode('code', $in)) |
497 | { |
498 | return $in; |
499 | } |
500 | |
501 | // We remove the hardcoded elements from the code block here because it is not used in code blocks |
502 | // Having it here saves us one preg_replace per message containing [code] blocks |
503 | // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too... |
504 | $htm_match = get_preg_expression('bbcode_htm'); |
505 | unset($htm_match[4], $htm_match[5]); |
506 | $htm_replace = array('\1', '\1', '\2', '\1'); |
507 | |
508 | $out = $code_block = ''; |
509 | $open = 1; |
510 | |
511 | while ($in) |
512 | { |
513 | // Determine position and tag length of next code block |
514 | preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer); |
515 | $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false; |
516 | $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false; |
517 | |
518 | // Determine position of ending code tag |
519 | $pos2 = stripos($in, '[/code]'); |
520 | |
521 | // Which is the next block, ending code or code block |
522 | if ($pos !== false && $pos < $pos2) |
523 | { |
524 | // Open new block |
525 | if (!$open) |
526 | { |
527 | $out .= substr($in, 0, $pos); |
528 | $in = substr($in, $pos); |
529 | $stx = (isset($buffer[3])) ? $buffer[3] : ''; |
530 | $code_block = ''; |
531 | } |
532 | else |
533 | { |
534 | // Already opened block, just append to the current block |
535 | $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : ''); |
536 | $in = substr($in, $pos); |
537 | } |
538 | |
539 | $in = substr($in, $tag_length); |
540 | $open++; |
541 | } |
542 | else |
543 | { |
544 | // Close the block |
545 | if ($open == 1) |
546 | { |
547 | $code_block .= substr($in, 0, $pos2); |
548 | $code_block = preg_replace($htm_match, $htm_replace, $code_block); |
549 | |
550 | // Parse this code block |
551 | $out .= $this->bbcode_parse_code($stx, $code_block); |
552 | $code_block = ''; |
553 | $open--; |
554 | } |
555 | else if ($open) |
556 | { |
557 | // Close one open tag... add to the current code block |
558 | $code_block .= substr($in, 0, $pos2 + 7); |
559 | $open--; |
560 | } |
561 | else |
562 | { |
563 | // end code without opening code... will be always outside code block |
564 | $out .= substr($in, 0, $pos2 + 7); |
565 | } |
566 | |
567 | $in = substr($in, $pos2 + 7); |
568 | } |
569 | } |
570 | |
571 | // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up. |
572 | if ($code_block) |
573 | { |
574 | $code_block = substr($code_block, 0, -7); |
575 | $code_block = preg_replace($htm_match, $htm_replace, $code_block); |
576 | |
577 | $out .= $this->bbcode_parse_code($stx, $code_block); |
578 | } |
579 | |
580 | return $out; |
581 | } |
582 | |
583 | /** |
584 | * Parse list bbcode |
585 | * Expects the argument to start with a tag |
586 | */ |
587 | function bbcode_parse_list($in) |
588 | { |
589 | if (!$this->check_bbcode('list', $in)) |
590 | { |
591 | return $in; |
592 | } |
593 | |
594 | // $tok holds characters to stop at. Since the string starts with a '[' we'll get everything up to the first ']' which should be the opening [list] tag |
595 | $tok = ']'; |
596 | $out = '['; |
597 | |
598 | // First character is [ |
599 | $in = substr($in, 1); |
600 | $list_end_tags = $item_end_tags = array(); |
601 | |
602 | do |
603 | { |
604 | $pos = strlen($in); |
605 | |
606 | for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) |
607 | { |
608 | $tmp_pos = strpos($in, $tok[$i]); |
609 | |
610 | if ($tmp_pos !== false && $tmp_pos < $pos) |
611 | { |
612 | $pos = $tmp_pos; |
613 | } |
614 | } |
615 | |
616 | $buffer = substr($in, 0, $pos); |
617 | $tok = $in[$pos]; |
618 | |
619 | $in = substr($in, $pos + 1); |
620 | |
621 | if ($tok == ']') |
622 | { |
623 | // if $tok is ']' the buffer holds a tag |
624 | if (strtolower($buffer) == '/list' && count($list_end_tags)) |
625 | { |
626 | // valid [/list] tag, check nesting so that we don't hit false positives |
627 | if (count($item_end_tags) && count($item_end_tags) >= count($list_end_tags)) |
628 | { |
629 | // current li tag has not been closed |
630 | $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . ']['; |
631 | } |
632 | |
633 | $out .= array_pop($list_end_tags) . ']'; |
634 | $tok = '['; |
635 | } |
636 | else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m)) |
637 | { |
638 | // sub-list, add a closing tag |
639 | if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1])) |
640 | { |
641 | array_push($list_end_tags, '/list:u:' . $this->bbcode_uid); |
642 | } |
643 | else |
644 | { |
645 | array_push($list_end_tags, '/list:o:' . $this->bbcode_uid); |
646 | } |
647 | $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']'; |
648 | $tok = '['; |
649 | } |
650 | else |
651 | { |
652 | if (($buffer == '*' || substr($buffer, -2) == '[*') && count($list_end_tags)) |
653 | { |
654 | // the buffer holds a bullet tag and we have a [list] tag open |
655 | if (count($item_end_tags) >= count($list_end_tags)) |
656 | { |
657 | if (substr($buffer, -2) == '[*') |
658 | { |
659 | $out .= substr($buffer, 0, -2) . '['; |
660 | } |
661 | // current li tag has not been closed |
662 | if (preg_match('/\n\[$/', $out, $m)) |
663 | { |
664 | $out = preg_replace('/\n\[$/', '[', $out); |
665 | $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid; |
666 | } |
667 | else |
668 | { |
669 | $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid; |
670 | } |
671 | } |
672 | else |
673 | { |
674 | $buffer = '*:' . $this->bbcode_uid; |
675 | } |
676 | |
677 | $item_end_tags[] = '/*:m:' . $this->bbcode_uid; |
678 | } |
679 | else if ($buffer == '/*') |
680 | { |
681 | array_pop($item_end_tags); |
682 | $buffer = '/*:' . $this->bbcode_uid; |
683 | } |
684 | |
685 | $out .= $buffer . $tok; |
686 | $tok = '[]'; |
687 | } |
688 | } |
689 | else |
690 | { |
691 | // Not within a tag, just add buffer to the return string |
692 | $out .= $buffer . $tok; |
693 | $tok = ($tok == '[') ? ']' : '[]'; |
694 | } |
695 | } |
696 | while ($in); |
697 | |
698 | // do we have some tags open? close them now |
699 | if (count($item_end_tags)) |
700 | { |
701 | $out .= '[' . implode('][', $item_end_tags) . ']'; |
702 | } |
703 | if (count($list_end_tags)) |
704 | { |
705 | $out .= '[' . implode('][', $list_end_tags) . ']'; |
706 | } |
707 | |
708 | return $out; |
709 | } |
710 | |
711 | /** |
712 | * Parse quote bbcode |
713 | * Expects the argument to start with a tag |
714 | */ |
715 | function bbcode_quote($in) |
716 | { |
717 | $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in))); |
718 | |
719 | if (!$in) |
720 | { |
721 | return ''; |
722 | } |
723 | |
724 | // To let the parser not catch tokens within quote_username quotes we encode them before we start this... |
725 | $in = preg_replace_callback('#quote="(.*?)"\]#i', function ($match) { |
726 | return 'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), $match[1]) . '"]'; |
727 | }, $in); |
728 | |
729 | $tok = ']'; |
730 | $out = '['; |
731 | |
732 | $in = substr($in, 1); |
733 | $close_tags = $error_ary = array(); |
734 | $buffer = ''; |
735 | |
736 | do |
737 | { |
738 | $pos = strlen($in); |
739 | for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) |
740 | { |
741 | $tmp_pos = strpos($in, $tok[$i]); |
742 | if ($tmp_pos !== false && $tmp_pos < $pos) |
743 | { |
744 | $pos = $tmp_pos; |
745 | } |
746 | } |
747 | |
748 | $buffer .= substr($in, 0, $pos); |
749 | $tok = $in[$pos]; |
750 | $in = substr($in, $pos + 1); |
751 | |
752 | if ($tok == ']') |
753 | { |
754 | if (strtolower($buffer) == '/quote' && count($close_tags) && substr($out, -1, 1) == '[') |
755 | { |
756 | // we have found a closing tag |
757 | $out .= array_pop($close_tags) . ']'; |
758 | $tok = '['; |
759 | $buffer = ''; |
760 | |
761 | /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly |
762 | * Do not try to think for the user. :/ Do not parse urls/smilies if there is no space - is the same as with other bbcodes too. |
763 | * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982 |
764 | if (!$in || $in[0] !== ' ') |
765 | { |
766 | $out .= ' '; |
767 | }*/ |
768 | } |
769 | else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[') |
770 | { |
771 | $this->parsed_items['quote']++; |
772 | array_push($close_tags, '/quote:' . $this->bbcode_uid); |
773 | |
774 | if (isset($m[1]) && $m[1]) |
775 | { |
776 | $username = str_replace(array('[', ']'), array('[', ']'), $m[1]); |
777 | $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '[$1', $username); |
778 | |
779 | $end_tags = array(); |
780 | $error = false; |
781 | |
782 | preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags); |
783 | foreach ($tags[1] as $tag) |
784 | { |
785 | if ($tag[0] != '/') |
786 | { |
787 | $end_tags[] = '/' . $tag; |
788 | } |
789 | else |
790 | { |
791 | $end_tag = array_pop($end_tags); |
792 | $error = ($end_tag != $tag) ? true : false; |
793 | } |
794 | } |
795 | |
796 | if ($error) |
797 | { |
798 | $username = $m[1]; |
799 | } |
800 | |
801 | $out .= 'quote="' . $username . '":' . $this->bbcode_uid . ']'; |
802 | } |
803 | else |
804 | { |
805 | $out .= 'quote:' . $this->bbcode_uid . ']'; |
806 | } |
807 | |
808 | $tok = '['; |
809 | $buffer = ''; |
810 | } |
811 | else if (preg_match('#^quote="(.*?)#is', $buffer, $m)) |
812 | { |
813 | // the buffer holds an invalid opening tag |
814 | $buffer .= ']'; |
815 | } |
816 | else |
817 | { |
818 | $out .= $buffer . $tok; |
819 | $tok = '[]'; |
820 | $buffer = ''; |
821 | } |
822 | } |
823 | else |
824 | { |
825 | /** |
826 | * Old quote code working fine, but having errors listed in bug #3572 |
827 | * |
828 | * $out .= $buffer . $tok; |
829 | * $tok = ($tok == '[') ? ']' : '[]'; |
830 | * $buffer = ''; |
831 | */ |
832 | |
833 | $out .= $buffer . $tok; |
834 | |
835 | if ($tok == '[') |
836 | { |
837 | // Search the text for the next tok... if an ending quote comes first, then change tok to [] |
838 | $pos1 = stripos($in, '[/quote'); |
839 | // If the token ] comes first, we change it to ] |
840 | $pos2 = strpos($in, ']'); |
841 | // If the token [ comes first, we change it to [ |
842 | $pos3 = strpos($in, '['); |
843 | |
844 | if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3)) |
845 | { |
846 | $tok = '[]'; |
847 | } |
848 | else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2)) |
849 | { |
850 | $tok = '['; |
851 | } |
852 | else |
853 | { |
854 | $tok = ']'; |
855 | } |
856 | } |
857 | else |
858 | { |
859 | $tok = '[]'; |
860 | } |
861 | $buffer = ''; |
862 | } |
863 | } |
864 | while ($in); |
865 | |
866 | $out .= $buffer; |
867 | |
868 | if (count($close_tags)) |
869 | { |
870 | $out .= '[' . implode('][', $close_tags) . ']'; |
871 | } |
872 | |
873 | foreach ($error_ary as $error_msg) |
874 | { |
875 | $this->warn_msg[] = $error_msg; |
876 | } |
877 | |
878 | return $out; |
879 | } |
880 | |
881 | /** |
882 | * Validate email |
883 | */ |
884 | function validate_email($var1, $var2) |
885 | { |
886 | $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); |
887 | $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); |
888 | |
889 | $txt = $var2; |
890 | $email = ($var1) ? $var1 : $var2; |
891 | |
892 | $validated = true; |
893 | |
894 | if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email)) |
895 | { |
896 | $validated = false; |
897 | } |
898 | |
899 | if (!$validated) |
900 | { |
901 | return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]'; |
902 | } |
903 | |
904 | $this->parsed_items['email']++; |
905 | |
906 | if ($var1) |
907 | { |
908 | $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']'; |
909 | } |
910 | else |
911 | { |
912 | $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']'; |
913 | } |
914 | |
915 | return $retval; |
916 | } |
917 | |
918 | /** |
919 | * Validate url |
920 | * |
921 | * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url] |
922 | * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url] |
923 | */ |
924 | function validate_url($var1, $var2) |
925 | { |
926 | $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); |
927 | $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); |
928 | |
929 | $url = ($var1) ? $var1 : $var2; |
930 | |
931 | if ($var1 && !$var2) |
932 | { |
933 | $var2 = $var1; |
934 | } |
935 | |
936 | if (!$url) |
937 | { |
938 | return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; |
939 | } |
940 | |
941 | $valid = false; |
942 | |
943 | $url = str_replace(' ', '%20', $url); |
944 | |
945 | // Checking urls |
946 | if (preg_match('#^' . get_preg_expression('url') . '$#iu', $url) || |
947 | preg_match('#^' . get_preg_expression('www_url') . '$#iu', $url) || |
948 | preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#iu', $url)) |
949 | { |
950 | $valid = true; |
951 | } |
952 | |
953 | if ($valid) |
954 | { |
955 | $this->parsed_items['url']++; |
956 | |
957 | // if there is no scheme, then add http schema |
958 | if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url)) |
959 | { |
960 | $url = 'http://' . $url; |
961 | } |
962 | |
963 | // Is this a link to somewhere inside this board? If so then remove the session id from the url |
964 | if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false) |
965 | { |
966 | $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}&/', '\1', $url); |
967 | $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}$/', '', $url); |
968 | $url = append_sid($url); |
969 | } |
970 | |
971 | return ($var1) ? '[url=' . $this->bbcode_specialchars($url) . ':' . $this->bbcode_uid . ']' . $var2 . '[/url:' . $this->bbcode_uid . ']' : '[url:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($url) . '[/url:' . $this->bbcode_uid . ']'; |
972 | } |
973 | |
974 | return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; |
975 | } |
976 | |
977 | /** |
978 | * Check if url is pointing to this domain/script_path/php-file |
979 | * |
980 | * @param string $url the url to check |
981 | * @return true if the url is pointing to this domain/script_path/php-file, false if not |
982 | * |
983 | * @access private |
984 | */ |
985 | function path_in_domain($url) |
986 | { |
987 | global $config, $phpEx, $user; |
988 | |
989 | if ($config['force_server_vars']) |
990 | { |
991 | $check_path = !empty($config['script_path']) ? $config['script_path'] : '/'; |
992 | } |
993 | else |
994 | { |
995 | $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/'; |
996 | } |
997 | |
998 | // Is the user trying to link to a php file in this domain and script path? |
999 | if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false) |
1000 | { |
1001 | $server_name = $user->host; |
1002 | |
1003 | // Forcing server vars is the only way to specify/override the protocol |
1004 | if ($config['force_server_vars'] || !$server_name) |
1005 | { |
1006 | $server_name = $config['server_name']; |
1007 | } |
1008 | |
1009 | // Check again in correct order... |
1010 | $pos_ext = strpos($url, ".{$phpEx}"); |
1011 | $pos_path = strpos($url, $check_path); |
1012 | $pos_domain = strpos($url, $server_name); |
1013 | |
1014 | if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path) |
1015 | { |
1016 | // Ok, actually we allow linking to some files (this may be able to be extended in some way later...) |
1017 | // @deprecated |
1018 | if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0) |
1019 | { |
1020 | return false; |
1021 | } |
1022 | |
1023 | return true; |
1024 | } |
1025 | } |
1026 | |
1027 | return false; |
1028 | } |
1029 | } |
1030 | |
1031 | /** |
1032 | * Main message parser for posting, pm, etc. takes raw message |
1033 | * and parses it for attachments, bbcode and smilies |
1034 | */ |
1035 | class parse_message extends bbcode_firstpass |
1036 | { |
1037 | var $attachment_data = array(); |
1038 | var $filename_data = array(); |
1039 | |
1040 | // Helps ironing out user error |
1041 | var $message_status = ''; |
1042 | |
1043 | var $allow_img_bbcode = true; |
1044 | var $allow_quote_bbcode = true; |
1045 | var $allow_url_bbcode = true; |
1046 | |
1047 | /** |
1048 | * The plupload object used for dealing with attachments |
1049 | * @var \phpbb\plupload\plupload |
1050 | */ |
1051 | protected $plupload; |
1052 | |
1053 | /** |
1054 | * Init - give message here or manually |
1055 | */ |
1056 | function __construct($message = '') |
1057 | { |
1058 | // Init BBCode UID |
1059 | $unique_id = preg_replace('/[^0-9a-f]/', '', unique_id()); |
1060 | $this->bbcode_uid = substr(base_convert($unique_id, 16, 36), 0, BBCODE_UID_LEN); |
1061 | $this->message = $message; |
1062 | } |
1063 | |
1064 | /** |
1065 | * Parse Message |
1066 | */ |
1067 | function parse($allow_bbcode, $allow_magic_url, $allow_smilies, $allow_img_bbcode = true, $allow_quote_bbcode = true, $allow_url_bbcode = true, $update_this_message = true, $mode = 'post') |
1068 | { |
1069 | global $config, $user, $phpbb_dispatcher, $phpbb_container; |
1070 | |
1071 | $this->mode = $mode; |
1072 | |
1073 | foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key) |
1074 | { |
1075 | if (!isset($config['max_' . $mode . '_' . $key])) |
1076 | { |
1077 | $config['max_' . $mode . '_' . $key] = 0; |
1078 | } |
1079 | } |
1080 | |
1081 | $this->allow_img_bbcode = $allow_img_bbcode; |
1082 | $this->allow_quote_bbcode = $allow_quote_bbcode; |
1083 | $this->allow_url_bbcode = $allow_url_bbcode; |
1084 | |
1085 | // If false, then $this->message won't be altered, the text will be returned instead. |
1086 | if (!$update_this_message) |
1087 | { |
1088 | $tmp_message = $this->message; |
1089 | $return_message = &$this->message; |
1090 | } |
1091 | |
1092 | if ($this->message_status == 'display') |
1093 | { |
1094 | $this->decode_message(); |
1095 | } |
1096 | |
1097 | // Store message length... |
1098 | $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(?:=\S+?)?\]#ius', '', $this->message)); |
1099 | |
1100 | // Maximum message length check. 0 disables this check completely. |
1101 | if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars']) |
1102 | { |
1103 | $this->warn_msg[] = $user->lang('CHARS_' . strtoupper($mode) . '_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_MANY_CHARS_LIMIT', (int) $config['max_' . $mode . '_chars']); |
1104 | return (!$update_this_message) ? $return_message : $this->warn_msg; |
1105 | } |
1106 | |
1107 | // Minimum message length check for post only |
1108 | if ($mode === 'post') |
1109 | { |
1110 | if (!$message_length || $message_length < (int) $config['min_post_chars']) |
1111 | { |
1112 | $this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : ($user->lang('CHARS_POST_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_FEW_CHARS_LIMIT', (int) $config['min_post_chars'])); |
1113 | return (!$update_this_message) ? $return_message : $this->warn_msg; |
1114 | } |
1115 | } |
1116 | |
1117 | /** |
1118 | * This event can be used for additional message checks/cleanup before parsing |
1119 | * |
1120 | * @event core.message_parser_check_message |
1121 | * @var bool allow_bbcode Do we allow BBCodes |
1122 | * @var bool allow_magic_url Do we allow magic urls |
1123 | * @var bool allow_smilies Do we allow smilies |
1124 | * @var bool allow_img_bbcode Do we allow image BBCode |
1125 | * @var bool allow_quote_bbcode Do we allow quote BBCode |
1126 | * @var bool allow_url_bbcode Do we allow url BBCode |
1127 | * @var bool update_this_message Do we alter the parsed message |
1128 | * @var string mode Posting mode |
1129 | * @var string message The message text to parse |
1130 | * @var string bbcode_bitfield The bbcode_bitfield before parsing |
1131 | * @var string bbcode_uid The bbcode_uid before parsing |
1132 | * @var bool return Do we return after the event is triggered if $warn_msg is not empty |
1133 | * @var array warn_msg Array of the warning messages |
1134 | * @since 3.1.2-RC1 |
1135 | * @changed 3.1.3-RC1 Added vars $bbcode_bitfield and $bbcode_uid |
1136 | * @changed 4.0.0-a1 Removed $allow_flash_bbcode |
1137 | */ |
1138 | $message = $this->message; |
1139 | $warn_msg = $this->warn_msg; |
1140 | $return = false; |
1141 | $bbcode_bitfield = $this->bbcode_bitfield; |
1142 | $bbcode_uid = $this->bbcode_uid; |
1143 | $vars = array( |
1144 | 'allow_bbcode', |
1145 | 'allow_magic_url', |
1146 | 'allow_smilies', |
1147 | 'allow_img_bbcode', |
1148 | 'allow_quote_bbcode', |
1149 | 'allow_url_bbcode', |
1150 | 'update_this_message', |
1151 | 'mode', |
1152 | 'message', |
1153 | 'bbcode_bitfield', |
1154 | 'bbcode_uid', |
1155 | 'return', |
1156 | 'warn_msg', |
1157 | ); |
1158 | extract($phpbb_dispatcher->trigger_event('core.message_parser_check_message', compact($vars))); |
1159 | $this->message = $message; |
1160 | $this->warn_msg = $warn_msg; |
1161 | $this->bbcode_bitfield = $bbcode_bitfield; |
1162 | $this->bbcode_uid = $bbcode_uid; |
1163 | if ($return && !empty($this->warn_msg)) |
1164 | { |
1165 | return (!$update_this_message) ? $return_message : $this->warn_msg; |
1166 | } |
1167 | |
1168 | // Get the parser |
1169 | $parser = $phpbb_container->get('text_formatter.parser'); |
1170 | |
1171 | // Set the parser's options |
1172 | ($allow_bbcode) ? $parser->enable_bbcodes() : $parser->disable_bbcodes(); |
1173 | ($allow_magic_url) ? $parser->enable_magic_url() : $parser->disable_magic_url(); |
1174 | ($allow_smilies) ? $parser->enable_smilies() : $parser->disable_smilies(); |
1175 | ($allow_img_bbcode) ? $parser->enable_bbcode('img') : $parser->disable_bbcode('img'); |
1176 | ($allow_quote_bbcode) ? $parser->enable_bbcode('quote') : $parser->disable_bbcode('quote'); |
1177 | ($allow_url_bbcode) ? $parser->enable_bbcode('url') : $parser->disable_bbcode('url'); |
1178 | |
1179 | // Set some config values |
1180 | $parser->set_vars(array( |
1181 | 'max_font_size' => $config['max_' . $this->mode . '_font_size'], |
1182 | 'max_smilies' => $config['max_' . $this->mode . '_smilies'], |
1183 | 'max_urls' => $config['max_' . $this->mode . '_urls'] |
1184 | )); |
1185 | |
1186 | // Parse this message |
1187 | $this->message = $parser->parse(html_entity_decode($this->message, ENT_QUOTES)); |
1188 | |
1189 | // Remove quotes that are nested too deep |
1190 | if ($config['max_quote_depth'] > 0) |
1191 | { |
1192 | $this->remove_nested_quotes($config['max_quote_depth']); |
1193 | } |
1194 | |
1195 | // Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length. |
1196 | // The maximum length check happened before any parsings. |
1197 | if ($mode === 'post' && utf8_clean_string($this->message) === '') |
1198 | { |
1199 | $this->warn_msg[] = $user->lang['TOO_FEW_CHARS']; |
1200 | return (!$update_this_message) ? $return_message : $this->warn_msg; |
1201 | } |
1202 | |
1203 | // Remove quotes that are nested too deep |
1204 | if ($config['max_quote_depth'] > 0) |
1205 | { |
1206 | $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode( |
1207 | $this->message, |
1208 | 'quote', |
1209 | $config['max_quote_depth'] |
1210 | ); |
1211 | } |
1212 | |
1213 | // Check for errors |
1214 | $errors = $parser->get_errors(); |
1215 | if ($errors) |
1216 | { |
1217 | foreach ($errors as $i => $args) |
1218 | { |
1219 | // Translate each error with $user->lang() |
1220 | $errors[$i] = call_user_func_array(array($user, 'lang'), $args); |
1221 | } |
1222 | $this->warn_msg = array_merge($this->warn_msg, $errors); |
1223 | |
1224 | return (!$update_this_message) ? $return_message : $this->warn_msg; |
1225 | } |
1226 | |
1227 | if (!$update_this_message) |
1228 | { |
1229 | unset($this->message); |
1230 | $this->message = $tmp_message; |
1231 | return $return_message; |
1232 | } |
1233 | |
1234 | $this->message_status = 'parsed'; |
1235 | return false; |
1236 | } |
1237 | |
1238 | /** |
1239 | * Formatting text for display |
1240 | */ |
1241 | function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true) |
1242 | { |
1243 | global $phpbb_container, $phpbb_dispatcher; |
1244 | |
1245 | // If false, then the parsed message get returned but internal message not processed. |
1246 | if (!$update_this_message) |
1247 | { |
1248 | $tmp_message = $this->message; |
1249 | $return_message = &$this->message; |
1250 | } |
1251 | |
1252 | $text = $this->message; |
1253 | $uid = $this->bbcode_uid; |
1254 | |
1255 | /** |
1256 | * Event to modify the text before it is parsed |
1257 | * |
1258 | * @event core.modify_format_display_text_before |
1259 | * @var string text The message text to parse |
1260 | * @var string uid The bbcode uid |
1261 | * @var bool allow_bbcode Do we allow bbcodes |
1262 | * @var bool allow_magic_url Do we allow magic urls |
1263 | * @var bool allow_smilies Do we allow smilies |
1264 | * @var bool update_this_message Do we update the internal message |
1265 | * with the parsed result |
1266 | * @since 3.1.6-RC1 |
1267 | */ |
1268 | $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message'); |
1269 | extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_before', compact($vars))); |
1270 | |
1271 | $this->message = $text; |
1272 | $this->bbcode_uid = $uid; |
1273 | unset($text, $uid); |
1274 | |
1275 | // NOTE: message_status is unreliable for detecting unparsed text because some callers |
1276 | // change $this->message without resetting $this->message_status to 'plain' so we |
1277 | // inspect the message instead |
1278 | //if ($this->message_status == 'plain') |
1279 | if (!preg_match('/^<[rt][ >]/', $this->message)) |
1280 | { |
1281 | // Force updating message - of course. |
1282 | $this->parse($allow_bbcode, $allow_magic_url, $allow_smilies, $this->allow_img_bbcode, $this->allow_quote_bbcode, $this->allow_url_bbcode, true); |
1283 | } |
1284 | |
1285 | // There's a bug when previewing a topic with no poll, because the empty title of the poll |
1286 | // gets parsed but $this->message still ends up empty. This fixes it, until a proper fix is |
1287 | // devised |
1288 | if ($this->message === '') |
1289 | { |
1290 | $this->message = $phpbb_container->get('text_formatter.parser')->parse($this->message); |
1291 | } |
1292 | |
1293 | $this->message = $phpbb_container->get('text_formatter.renderer')->render($this->message); |
1294 | |
1295 | $text = $this->message; |
1296 | $uid = $this->bbcode_uid; |
1297 | |
1298 | /** |
1299 | * Event to modify the text after it is parsed |
1300 | * |
1301 | * @event core.modify_format_display_text_after |
1302 | * @var string text The message text to parse |
1303 | * @var string uid The bbcode uid |
1304 | * @var bool allow_bbcode Do we allow bbcodes |
1305 | * @var bool allow_magic_url Do we allow magic urls |
1306 | * @var bool allow_smilies Do we allow smilies |
1307 | * @var bool update_this_message Do we update the internal message |
1308 | * with the parsed result |
1309 | * @since 3.1.0-a3 |
1310 | */ |
1311 | $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message'); |
1312 | extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_after', compact($vars))); |
1313 | |
1314 | $this->message = $text; |
1315 | $this->bbcode_uid = $uid; |
1316 | |
1317 | if (!$update_this_message) |
1318 | { |
1319 | unset($this->message); |
1320 | $this->message = $tmp_message; |
1321 | return $return_message; |
1322 | } |
1323 | |
1324 | $this->message_status = 'display'; |
1325 | return false; |
1326 | } |
1327 | |
1328 | /** |
1329 | * Decode message to be placed back into form box |
1330 | */ |
1331 | function decode_message($custom_bbcode_uid = '', $update_this_message = true) |
1332 | { |
1333 | // If false, then the parsed message get returned but internal message not processed. |
1334 | if (!$update_this_message) |
1335 | { |
1336 | $tmp_message = $this->message; |
1337 | $return_message = &$this->message; |
1338 | } |
1339 | |
1340 | ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid); |
1341 | |
1342 | if (!$update_this_message) |
1343 | { |
1344 | unset($this->message); |
1345 | $this->message = $tmp_message; |
1346 | return $return_message; |
1347 | } |
1348 | |
1349 | $this->message_status = 'plain'; |
1350 | return false; |
1351 | } |
1352 | |
1353 | /** |
1354 | * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx. |
1355 | * Cuts down displayed size of link if over 50 chars, turns absolute links |
1356 | * into relative versions when the server/script path matches the link |
1357 | */ |
1358 | function magic_url($server_url) |
1359 | { |
1360 | // We use the global make_clickable function |
1361 | $this->message = make_clickable($this->message, $server_url); |
1362 | } |
1363 | |
1364 | /** |
1365 | * Parse Smilies |
1366 | */ |
1367 | function smilies($max_smilies = 0) |
1368 | { |
1369 | global $db, $user; |
1370 | static $match; |
1371 | static $replace; |
1372 | |
1373 | // See if the static arrays have already been filled on an earlier invocation |
1374 | if (!is_array($match)) |
1375 | { |
1376 | $match = $replace = array(); |
1377 | |
1378 | // NOTE: obtain_* function? chaching the table contents? |
1379 | |
1380 | // For now setting the ttl to 10 minutes |
1381 | switch ($db->get_sql_layer()) |
1382 | { |
1383 | case 'mssql_odbc': |
1384 | case 'mssqlnative': |
1385 | $sql = 'SELECT * |
1386 | FROM ' . SMILIES_TABLE . ' |
1387 | ORDER BY LEN(code) DESC'; |
1388 | break; |
1389 | |
1390 | // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure... |
1391 | default: |
1392 | $sql = 'SELECT * |
1393 | FROM ' . SMILIES_TABLE . ' |
1394 | ORDER BY LENGTH(code) DESC'; |
1395 | break; |
1396 | } |
1397 | $result = $db->sql_query($sql, 600); |
1398 | |
1399 | while ($row = $db->sql_fetchrow($result)) |
1400 | { |
1401 | if (empty($row['code'])) |
1402 | { |
1403 | continue; |
1404 | } |
1405 | |
1406 | // (assertion) |
1407 | $match[] = preg_quote($row['code'], '#'); |
1408 | $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->'; |
1409 | } |
1410 | $db->sql_freeresult($result); |
1411 | } |
1412 | |
1413 | if (count($match)) |
1414 | { |
1415 | if ($max_smilies) |
1416 | { |
1417 | // 'u' modifier has been added to correctly parse smilies within unicode strings |
1418 | // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 |
1419 | $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#u', $this->message, $matches); |
1420 | unset($matches); |
1421 | |
1422 | if ($num_matches !== false && $num_matches > $max_smilies) |
1423 | { |
1424 | $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies); |
1425 | return; |
1426 | } |
1427 | } |
1428 | |
1429 | // Make sure the delimiter # is added in front and at the end of every element within $match |
1430 | // 'u' modifier has been added to correctly parse smilies within unicode strings |
1431 | // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 |
1432 | |
1433 | $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#u' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#u'), $replace, $this->message)); |
1434 | } |
1435 | } |
1436 | |
1437 | /** |
1438 | * Check attachment form token depending on submit type |
1439 | * |
1440 | * @param \phpbb\language\language $language Language |
1441 | * @param \phpbb\request\request_interface $request Request |
1442 | * @param string $form_name Form name for checking form key |
1443 | * |
1444 | * @return bool True if form token is not needed or valid, false if needed and invalid |
1445 | */ |
1446 | function check_attachment_form_token(\phpbb\language\language $language, \phpbb\request\request_interface $request, $form_name) |
1447 | { |
1448 | $add_file = $request->is_set_post('add_file'); |
1449 | $delete_file = $request->is_set_post('delete_file'); |
1450 | |
1451 | if (($add_file || $delete_file) && !check_form_key($form_name)) |
1452 | { |
1453 | $this->warn_msg[] = $language->lang('FORM_INVALID'); |
1454 | |
1455 | if ($request->is_ajax() && $this->plupload) |
1456 | { |
1457 | $this->plupload->emit_error(-400, 'FORM_INVALID'); |
1458 | } |
1459 | |
1460 | return false; |
1461 | } |
1462 | |
1463 | return true; |
1464 | } |
1465 | |
1466 | /** |
1467 | * Parse Attachments |
1468 | */ |
1469 | function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false) |
1470 | { |
1471 | global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request; |
1472 | global $phpbb_container, $phpbb_dispatcher; |
1473 | |
1474 | $controller_helper = $phpbb_container->get('controller.helper'); |
1475 | |
1476 | $error = array(); |
1477 | |
1478 | $num_attachments = count($this->attachment_data); |
1479 | $this->filename_data['filecomment'] = $request->variable('filecomment', '', true); |
1480 | $upload = $request->file($form_name); |
1481 | $upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name'])); |
1482 | |
1483 | $add_file = (isset($_POST['add_file'])) ? true : false; |
1484 | $delete_file = (isset($_POST['delete_file'])) ? true : false; |
1485 | |
1486 | // First of all adjust comments if changed |
1487 | $actual_comment_list = $request->variable('comment_list', array(''), true); |
1488 | |
1489 | foreach ($actual_comment_list as $comment_key => $comment) |
1490 | { |
1491 | if (!isset($this->attachment_data[$comment_key])) |
1492 | { |
1493 | continue; |
1494 | } |
1495 | |
1496 | if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key]) |
1497 | { |
1498 | $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key]; |
1499 | } |
1500 | } |
1501 | |
1502 | $cfg = array(); |
1503 | $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments']; |
1504 | $forum_id = ($is_message) ? 0 : $forum_id; |
1505 | |
1506 | if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file) |
1507 | { |
1508 | if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id)) |
1509 | { |
1510 | /** @var \phpbb\attachment\manager $attachment_manager */ |
1511 | $attachment_manager = $phpbb_container->get('attachment.manager'); |
1512 | $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message); |
1513 | $error = $filedata['error']; |
1514 | |
1515 | if ($filedata['post_attach'] && !count($error)) |
1516 | { |
1517 | $sql_ary = array( |
1518 | 'physical_filename' => $filedata['physical_filename'], |
1519 | 'attach_comment' => $this->filename_data['filecomment'], |
1520 | 'real_filename' => $filedata['real_filename'], |
1521 | 'extension' => $filedata['extension'], |
1522 | 'mimetype' => $filedata['mimetype'], |
1523 | 'filesize' => $filedata['filesize'], |
1524 | 'filetime' => $filedata['filetime'], |
1525 | 'thumbnail' => $filedata['thumbnail'], |
1526 | 'is_orphan' => 1, |
1527 | 'in_message' => ($is_message) ? 1 : 0, |
1528 | 'poster_id' => $user->data['user_id'], |
1529 | ); |
1530 | |
1531 | /** |
1532 | * Modify attachment sql array on submit |
1533 | * |
1534 | * @event core.modify_attachment_sql_ary_on_submit |
1535 | * @var array sql_ary Array containing SQL data |
1536 | * @since 3.2.6-RC1 |
1537 | */ |
1538 | $vars = array('sql_ary'); |
1539 | extract($phpbb_dispatcher->trigger_event('core.modify_attachment_sql_ary_on_submit', compact($vars))); |
1540 | |
1541 | $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); |
1542 | |
1543 | $new_entry = array( |
1544 | 'attach_id' => $db->sql_nextid(), |
1545 | 'is_orphan' => 1, |
1546 | 'real_filename' => $filedata['real_filename'], |
1547 | 'attach_comment'=> $this->filename_data['filecomment'], |
1548 | 'filesize' => $filedata['filesize'], |
1549 | ); |
1550 | |
1551 | $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); |
1552 | |
1553 | /** |
1554 | * Modify attachment data on submit |
1555 | * |
1556 | * @event core.modify_attachment_data_on_submit |
1557 | * @var array attachment_data Array containing attachment data |
1558 | * @since 3.2.2-RC1 |
1559 | */ |
1560 | $attachment_data = $this->attachment_data; |
1561 | $vars = array('attachment_data'); |
1562 | extract($phpbb_dispatcher->trigger_event('core.modify_attachment_data_on_submit', compact($vars))); |
1563 | $this->attachment_data = $attachment_data; |
1564 | unset($attachment_data); |
1565 | |
1566 | $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) { |
1567 | return '[attachment='.($match[1] + 1).']' . $match[2] . '[/attachment]'; |
1568 | }, $this->message); |
1569 | |
1570 | $this->filename_data['filecomment'] = ''; |
1571 | |
1572 | // This Variable is set to false here, because Attachments are entered into the |
1573 | // Database in two modes, one if the id_list is 0 and the second one if post_attach is true |
1574 | // Since post_attach is automatically switched to true if an Attachment got added to the filesystem, |
1575 | // but we are assigning an id of 0 here, we have to reset the post_attach variable to false. |
1576 | // |
1577 | // This is very relevant, because it could happen that the post got not submitted, but we do not |
1578 | // know this circumstance here. We could be at the posting page or we could be redirected to the entered |
1579 | // post. :) |
1580 | $filedata['post_attach'] = false; |
1581 | } |
1582 | } |
1583 | else |
1584 | { |
1585 | $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); |
1586 | } |
1587 | } |
1588 | |
1589 | if ($preview || $refresh || count($error)) |
1590 | { |
1591 | if (isset($this->plupload) && $this->plupload->is_active()) |
1592 | { |
1593 | $json_response = new \phpbb\json_response(); |
1594 | } |
1595 | |
1596 | // Perform actions on temporary attachments |
1597 | if ($delete_file) |
1598 | { |
1599 | include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx); |
1600 | |
1601 | $index = array_keys($request->variable('delete_file', array(0 => 0))); |
1602 | $index = (!empty($index)) ? $index[0] : false; |
1603 | |
1604 | if ($index !== false && !empty($this->attachment_data[$index])) |
1605 | { |
1606 | /** @var \phpbb\attachment\manager $attachment_manager */ |
1607 | $attachment_manager = $phpbb_container->get('attachment.manager'); |
1608 | |
1609 | // delete selected attachment |
1610 | if ($this->attachment_data[$index]['is_orphan']) |
1611 | { |
1612 | $sql = 'SELECT attach_id, physical_filename, thumbnail |
1613 | FROM ' . ATTACHMENTS_TABLE . ' |
1614 | WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . ' |
1615 | AND is_orphan = 1 |
1616 | AND poster_id = ' . $user->data['user_id']; |
1617 | $result = $db->sql_query($sql); |
1618 | $row = $db->sql_fetchrow($result); |
1619 | $db->sql_freeresult($result); |
1620 | |
1621 | if ($row) |
1622 | { |
1623 | $attachment_manager->unlink($row['physical_filename'], 'file'); |
1624 | |
1625 | if ($row['thumbnail']) |
1626 | { |
1627 | $attachment_manager->unlink($row['physical_filename'], 'thumbnail'); |
1628 | } |
1629 | |
1630 | $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']); |
1631 | } |
1632 | } |
1633 | else |
1634 | { |
1635 | $attachment_manager->delete('attach', $this->attachment_data[$index]['attach_id']); |
1636 | } |
1637 | |
1638 | unset($this->attachment_data[$index]); |
1639 | $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) use($index) { |
1640 | return ($match[1] == $index) ? '' : (($match[1] > $index) ? '[attachment=' . ($match[1] - 1) . ']' . $match[2] . '[/attachment]' : $match[0]); |
1641 | }, $this->message); |
1642 | |
1643 | // Reindex Array |
1644 | $this->attachment_data = array_values($this->attachment_data); |
1645 | if (isset($this->plupload) && $this->plupload->is_active()) |
1646 | { |
1647 | $json_response->send($this->attachment_data); |
1648 | } |
1649 | } |
1650 | } |
1651 | else if (($add_file || $preview) && $upload_file) |
1652 | { |
1653 | if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id)) |
1654 | { |
1655 | /** @var \phpbb\attachment\manager $attachment_manager */ |
1656 | $attachment_manager = $phpbb_container->get('attachment.manager'); |
1657 | $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message); |
1658 | $error = array_merge($error, $filedata['error']); |
1659 | |
1660 | if (!count($error)) |
1661 | { |
1662 | $sql_ary = array( |
1663 | 'physical_filename' => $filedata['physical_filename'], |
1664 | 'attach_comment' => $this->filename_data['filecomment'], |
1665 | 'real_filename' => $filedata['real_filename'], |
1666 | 'extension' => $filedata['extension'], |
1667 | 'mimetype' => $filedata['mimetype'], |
1668 | 'filesize' => $filedata['filesize'], |
1669 | 'filetime' => $filedata['filetime'], |
1670 | 'thumbnail' => $filedata['thumbnail'], |
1671 | 'is_orphan' => 1, |
1672 | 'in_message' => ($is_message) ? 1 : 0, |
1673 | 'poster_id' => $user->data['user_id'], |
1674 | ); |
1675 | |
1676 | /** |
1677 | * Modify attachment sql array on upload |
1678 | * |
1679 | * @event core.modify_attachment_sql_ary_on_upload |
1680 | * @var array sql_ary Array containing SQL data |
1681 | * @since 3.2.6-RC1 |
1682 | */ |
1683 | $vars = array('sql_ary'); |
1684 | extract($phpbb_dispatcher->trigger_event('core.modify_attachment_sql_ary_on_upload', compact($vars))); |
1685 | |
1686 | $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); |
1687 | |
1688 | $new_entry = array( |
1689 | 'attach_id' => $db->sql_nextid(), |
1690 | 'is_orphan' => 1, |
1691 | 'real_filename' => $filedata['real_filename'], |
1692 | 'attach_comment'=> $this->filename_data['filecomment'], |
1693 | 'filesize' => $filedata['filesize'], |
1694 | ); |
1695 | |
1696 | $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); |
1697 | |
1698 | /** |
1699 | * Modify attachment data on upload |
1700 | * |
1701 | * @event core.modify_attachment_data_on_upload |
1702 | * @var array attachment_data Array containing attachment data |
1703 | * @since 3.2.2-RC1 |
1704 | */ |
1705 | $attachment_data = $this->attachment_data; |
1706 | $vars = array('attachment_data'); |
1707 | extract($phpbb_dispatcher->trigger_event('core.modify_attachment_data_on_upload', compact($vars))); |
1708 | $this->attachment_data = $attachment_data; |
1709 | unset($attachment_data); |
1710 | |
1711 | $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) { |
1712 | return '[attachment=' . ($match[1] + 1) . ']' . $match[2] . '[/attachment]'; |
1713 | }, $this->message); |
1714 | $this->filename_data['filecomment'] = ''; |
1715 | |
1716 | if (isset($this->plupload) && $this->plupload->is_active()) |
1717 | { |
1718 | $download_url = $controller_helper->route( |
1719 | 'phpbb_storage_attachment', |
1720 | [ |
1721 | 'id' => (int) $new_entry['attach_id'], |
1722 | 'filename' => $new_entry['real_filename'], |
1723 | ] |
1724 | ); |
1725 | |
1726 | // Send the client the attachment data to maintain state |
1727 | $json_response->send(array('data' => $this->attachment_data, 'download_url' => $download_url)); |
1728 | } |
1729 | } |
1730 | } |
1731 | else |
1732 | { |
1733 | $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); |
1734 | } |
1735 | |
1736 | if (!empty($error) && isset($this->plupload) && $this->plupload->is_active()) |
1737 | { |
1738 | // If this is a plupload (and thus ajax) request, give the |
1739 | // client the first error we have |
1740 | $json_response->send(array( |
1741 | 'jsonrpc' => '2.0', |
1742 | 'id' => 'id', |
1743 | 'error' => array( |
1744 | 'code' => 105, |
1745 | 'message' => current($error), |
1746 | ), |
1747 | )); |
1748 | } |
1749 | } |
1750 | } |
1751 | |
1752 | foreach ($error as $error_msg) |
1753 | { |
1754 | $this->warn_msg[] = $error_msg; |
1755 | } |
1756 | } |
1757 | |
1758 | /** |
1759 | * Get Attachment Data |
1760 | */ |
1761 | function get_submitted_attachment_data($check_user_id = false) |
1762 | { |
1763 | global $user, $db; |
1764 | global $request; |
1765 | |
1766 | $this->filename_data['filecomment'] = $request->variable('filecomment', '', true); |
1767 | $attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST); |
1768 | $this->attachment_data = array(); |
1769 | |
1770 | $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id; |
1771 | |
1772 | if (!count($attachment_data)) |
1773 | { |
1774 | return; |
1775 | } |
1776 | |
1777 | $not_orphan = $orphan = array(); |
1778 | |
1779 | foreach ($attachment_data as $pos => $var_ary) |
1780 | { |
1781 | if ($var_ary['is_orphan']) |
1782 | { |
1783 | $orphan[(int) $var_ary['attach_id']] = $pos; |
1784 | } |
1785 | else |
1786 | { |
1787 | $not_orphan[(int) $var_ary['attach_id']] = $pos; |
1788 | } |
1789 | } |
1790 | |
1791 | // Regenerate already posted attachments |
1792 | if (count($not_orphan)) |
1793 | { |
1794 | // Get the attachment data, based on the poster id... |
1795 | $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize |
1796 | FROM ' . ATTACHMENTS_TABLE . ' |
1797 | WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . ' |
1798 | AND poster_id = ' . $check_user_id; |
1799 | $result = $db->sql_query($sql); |
1800 | |
1801 | while ($row = $db->sql_fetchrow($result)) |
1802 | { |
1803 | $pos = $not_orphan[$row['attach_id']]; |
1804 | $this->attachment_data[$pos] = $row; |
1805 | $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; |
1806 | |
1807 | unset($not_orphan[$row['attach_id']]); |
1808 | } |
1809 | $db->sql_freeresult($result); |
1810 | } |
1811 | |
1812 | if (count($not_orphan)) |
1813 | { |
1814 | trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); |
1815 | } |
1816 | |
1817 | // Regenerate newly uploaded attachments |
1818 | if (count($orphan)) |
1819 | { |
1820 | $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize |
1821 | FROM ' . ATTACHMENTS_TABLE . ' |
1822 | WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . ' |
1823 | AND poster_id = ' . $user->data['user_id'] . ' |
1824 | AND is_orphan = 1'; |
1825 | $result = $db->sql_query($sql); |
1826 | |
1827 | while ($row = $db->sql_fetchrow($result)) |
1828 | { |
1829 | $pos = $orphan[$row['attach_id']]; |
1830 | $this->attachment_data[$pos] = $row; |
1831 | $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; |
1832 | |
1833 | unset($orphan[$row['attach_id']]); |
1834 | } |
1835 | $db->sql_freeresult($result); |
1836 | } |
1837 | |
1838 | if (count($orphan)) |
1839 | { |
1840 | trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); |
1841 | } |
1842 | |
1843 | ksort($this->attachment_data); |
1844 | } |
1845 | |
1846 | /** |
1847 | * Parse Poll |
1848 | */ |
1849 | function parse_poll(&$poll) |
1850 | { |
1851 | global $user, $config; |
1852 | |
1853 | $poll_max_options = $poll['poll_max_options']; |
1854 | |
1855 | // Parse Poll Option text |
1856 | $tmp_message = $this->message; |
1857 | |
1858 | $poll['poll_options'] = preg_split('/\s*?\n\s*/', trim($poll['poll_option_text'])); |
1859 | $poll['poll_options_size'] = count($poll['poll_options']); |
1860 | |
1861 | foreach ($poll['poll_options'] as &$poll_option) |
1862 | { |
1863 | $this->message = $poll_option; |
1864 | $poll_option = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, $config['allow_post_links'], false, 'poll'); |
1865 | } |
1866 | unset($poll_option); |
1867 | $poll['poll_option_text'] = implode("\n", $poll['poll_options']); |
1868 | |
1869 | // Parse Poll Title |
1870 | $this->message = $poll['poll_title']; |
1871 | if (!$poll['poll_title'] && $poll['poll_options_size']) |
1872 | { |
1873 | $this->warn_msg[] = $user->lang['NO_POLL_TITLE']; |
1874 | } |
1875 | else |
1876 | { |
1877 | if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100) |
1878 | { |
1879 | $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG']; |
1880 | } |
1881 | $poll['poll_title'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, $config['allow_post_links'], false, 'poll'); |
1882 | if (strlen($poll['poll_title']) > 255) |
1883 | { |
1884 | $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG']; |
1885 | } |
1886 | } |
1887 | |
1888 | if (count($poll['poll_options']) == 1) |
1889 | { |
1890 | $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS']; |
1891 | } |
1892 | else if ($poll['poll_options_size'] > (int) $config['max_poll_options']) |
1893 | { |
1894 | $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS']; |
1895 | } |
1896 | else if ($poll_max_options > $poll['poll_options_size']) |
1897 | { |
1898 | $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS']; |
1899 | } |
1900 | |
1901 | $poll['poll_max_options'] = ($poll['poll_max_options'] < 1) ? 1 : (($poll['poll_max_options'] > $config['max_poll_options']) ? $config['max_poll_options'] : $poll['poll_max_options']); |
1902 | |
1903 | $this->message = $tmp_message; |
1904 | } |
1905 | |
1906 | /** |
1907 | * Remove nested quotes at given depth in current parsed message |
1908 | * |
1909 | * @param integer $max_depth Depth limit |
1910 | * @return void |
1911 | */ |
1912 | public function remove_nested_quotes($max_depth) |
1913 | { |
1914 | global $phpbb_container; |
1915 | |
1916 | if (preg_match('#^<[rt][ >]#', $this->message)) |
1917 | { |
1918 | $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode( |
1919 | $this->message, |
1920 | 'quote', |
1921 | $max_depth |
1922 | ); |
1923 | |
1924 | return; |
1925 | } |
1926 | |
1927 | // Capture all [quote] and [/quote] tags |
1928 | preg_match_all('(\\[/?quote(?:="(.*?)")?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE); |
1929 | |
1930 | // Iterate over the quote tags to mark the ranges that must be removed |
1931 | $depth = 0; |
1932 | $ranges = array(); |
1933 | $start_pos = 0; |
1934 | foreach ($matches[0] as $match) |
1935 | { |
1936 | if ($match[0][1] === '/') |
1937 | { |
1938 | --$depth; |
1939 | if ($depth == $max_depth) |
1940 | { |
1941 | $end_pos = $match[1] + strlen($match[0]); |
1942 | $length = $end_pos - $start_pos; |
1943 | $ranges[] = array($start_pos, $length); |
1944 | } |
1945 | } |
1946 | else |
1947 | { |
1948 | ++$depth; |
1949 | if ($depth == $max_depth + 1) |
1950 | { |
1951 | $start_pos = $match[1]; |
1952 | } |
1953 | } |
1954 | } |
1955 | |
1956 | foreach (array_reverse($ranges) as $range) |
1957 | { |
1958 | list($start_pos, $length) = $range; |
1959 | $this->message = substr_replace($this->message, '', $start_pos, $length); |
1960 | } |
1961 | } |
1962 | |
1963 | /** |
1964 | * Setter function for passing the plupload object |
1965 | * |
1966 | * @param \phpbb\plupload\plupload $plupload The plupload object |
1967 | * |
1968 | * @return null |
1969 | */ |
1970 | public function set_plupload(\phpbb\plupload\plupload $plupload) |
1971 | { |
1972 | $this->plupload = $plupload; |
1973 | } |
1974 | |
1975 | /** |
1976 | * Function to perform custom bbcode validation by extensions |
1977 | * can be used in bbcode_init() to assign regexp replacement |
1978 | * Example: 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->validate_bbcode_by_extension('\$1')") |
1979 | * |
1980 | * Accepts variable number of parameters |
1981 | * |
1982 | * @return mixed Validation result |
1983 | */ |
1984 | public function validate_bbcode_by_extension() |
1985 | { |
1986 | global $phpbb_dispatcher; |
1987 | |
1988 | $return = false; |
1989 | $params_array = func_get_args(); |
1990 | |
1991 | /** |
1992 | * Event to validate bbcode with the custom validating methods |
1993 | * provided by extensions |
1994 | * |
1995 | * @event core.validate_bbcode_by_extension |
1996 | * @var array params_array Array with the function parameters |
1997 | * @var mixed return Validation result to return |
1998 | * |
1999 | * @since 3.1.5-RC1 |
2000 | */ |
2001 | $vars = array('params_array', 'return'); |
2002 | extract($phpbb_dispatcher->trigger_event('core.validate_bbcode_by_extension', compact($vars))); |
2003 | |
2004 | return $return; |
2005 | } |
2006 | } |