Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 156
0.00% covered (danger)
0.00%
0 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
ajax_iohandler
0.00% covered (danger)
0.00%
0 / 156
0.00% covered (danger)
0.00%
0 / 21
3540
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 get_input
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_raw_input
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_server_variable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_header_variable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_secure
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add_user_form_group
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 generate_form_render_data
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
272
 send_response
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 prepare_json_array
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
240
 set_progress
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 request_refresh
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 set_active_stage_menu
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 set_finished_stage_menu
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 set_cookie
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 add_download_link
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 render_update_file_status
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 redirect
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 acquire_lock
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 release_lock
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 lang_replace_callback
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
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
14namespace phpbb\install\helper\iohandler;
15
16use phpbb\path_helper;
17use phpbb\routing\router;
18
19/**
20 * Input-Output handler for the AJAX frontend
21 */
22class ajax_iohandler extends iohandler_base
23{
24    /**
25     * @var path_helper
26     */
27    protected $path_helper;
28
29    /**
30     * @var \phpbb\request\request_interface
31     */
32    protected $request;
33
34    /**
35     * @var \phpbb\template\template
36     */
37    protected $template;
38
39    /**
40     * @var router
41     */
42    protected $router;
43
44    /**
45     * @var string
46     */
47    protected $phpbb_root_path;
48
49    /**
50     * @var string
51     */
52    protected $file_status;
53
54    /**
55     * @var string
56     */
57    protected $form;
58
59    /**
60     * @var bool
61     */
62    protected $request_client_refresh;
63
64    /**
65     * @var array
66     */
67    protected $nav_data;
68
69    /**
70     * @var array
71     */
72    protected $cookies;
73
74    /**
75     * @var array
76     */
77    protected $download;
78
79    /**
80     * @var array
81     */
82    protected $redirect_url;
83
84    /**
85     * @var resource
86     * @psalm-var resource|closed-resource
87     */
88    protected $file_lock_pointer;
89
90    /**
91     * Constructor
92     *
93     * @param path_helper                        $path_helper
94     * @param \phpbb\request\request_interface    $request    HTTP request interface
95     * @param \phpbb\template\template            $template    Template engine
96     * @param router                             $router        Router
97     * @param string $root_path    Path to phpBB's root
98     */
99    public function __construct(path_helper $path_helper, \phpbb\request\request_interface $request, \phpbb\template\template $template, router $router, string $root_path)
100    {
101        $this->path_helper = $path_helper;
102        $this->request    = $request;
103        $this->router    = $router;
104        $this->template    = $template;
105        $this->form        = '';
106        $this->nav_data    = array();
107        $this->cookies    = array();
108        $this->download    = array();
109        $this->redirect_url = array();
110        $this->file_status = '';
111        $this->phpbb_root_path = $root_path;
112
113        parent::__construct();
114    }
115
116    /**
117     * {@inheritdoc}
118     */
119    public function get_input($name, $default, $multibyte = false)
120    {
121        return $this->request->variable($name, $default, $multibyte);
122    }
123
124    /**
125     * {@inheritdoc}
126     */
127    public function get_raw_input($name, $default)
128    {
129        return $this->request->raw_variable($name, $default);
130    }
131
132    /**
133     * {@inheritdoc}
134     */
135    public function get_server_variable($name, $default = '')
136    {
137        return $this->request->server($name, $default);
138    }
139
140    /**
141     * {@inheritdoc}
142     */
143    public function get_header_variable($name, $default = '')
144    {
145        return $this->request->header($name, $default);
146    }
147
148    /**
149     * {@inheritdoc}
150     */
151    public function is_secure()
152    {
153        return $this->request->is_secure();
154    }
155
156    /**
157     * {@inheritdoc}
158     */
159    public function add_user_form_group($title, $form)
160    {
161        $this->form = $this->generate_form_render_data($title, $form);
162    }
163
164    /**
165     * {@inheritdoc}
166     */
167    public function generate_form_render_data($title, $form)
168    {
169        $this->template->assign_block_vars('options', array(
170            'LEGEND'    => $this->language->lang($title),
171            'S_LEGEND'    => true,
172        ));
173
174        $not_button_form = false;
175
176        foreach ($form as $input_name => $input_options)
177        {
178            if (!isset($input_options['type']))
179            {
180                continue;
181            }
182
183            $tpl_ary = array();
184            $not_button_form = ($input_options['type'] !== 'submit' || $not_button_form);
185
186            $tpl_ary['TYPE'] = $input_options['type'];
187            $tpl_ary['TITLE'] = $this->language->lang($input_options['label']);
188            $tpl_ary['KEY'] = $input_name;
189            $tpl_ary['S_EXPLAIN'] = false;
190            $tpl_ary['DISABLED'] = isset($input_options['disabled']) ? $input_options['disabled'] : false;
191            $tpl_ary['IS_SECONDARY'] = isset($input_options['is_secondary']) ? $input_options['is_secondary'] : false;
192
193            if (isset($input_options['default']))
194            {
195                $default = $input_options['default'];
196                $default = preg_replace_callback('#\{L_([A-Z0-9\-_]*)\}#s', array($this, 'lang_replace_callback'), $default);
197                $tpl_ary['DEFAULT'] = $default;
198            }
199
200            if (isset($input_options['description']))
201            {
202                $tpl_ary['TITLE_EXPLAIN'] = $this->language->lang($input_options['description']);
203                $tpl_ary['S_EXPLAIN'] = true;
204            }
205
206            if (in_array($input_options['type'], array('select', 'radio'), true))
207            {
208                for ($i = 0, $total = count($input_options['options']); $i < $total; $i++)
209                {
210                    if (isset($input_options['options'][$i]['label']))
211                    {
212                        $input_options['options'][$i]['label'] = $this->language->lang($input_options['options'][$i]['label']);
213                    }
214                }
215
216                $tpl_ary['OPTIONS'] = $input_options['options'];
217            }
218
219            $block_name = ($input_options['type'] === 'submit') ? 'submit_buttons' : 'options';
220            $this->template->assign_block_vars($block_name, $tpl_ary);
221        }
222
223        if (isset($form['database_update_submit']) && !$form['database_update_submit']['disabled'])
224        {
225            $this->template->assign_var('FORM_TITLE', $this->language->lang('UPDATE_CONTINUE_UPDATE_PROCESS'));
226        }
227
228        $this->template->assign_var('S_NOT_ONLY_BUTTON_FORM', $not_button_form);
229
230        if (!$not_button_form)
231        {
232            $this->template->destroy_block_vars('options');
233        }
234
235        $this->template->set_filenames(array(
236            'form_install' => 'installer_form.html',
237        ));
238
239        $compiled_template = $this->template->assign_display('form_install');
240
241        return is_string($compiled_template) ? $compiled_template : '';
242    }
243
244    /**
245     * {@inheritdoc}
246     */
247    public function send_response($no_more_output = false)
248    {
249        $json_data_array = $this->prepare_json_array($no_more_output);
250
251        if (empty($json_data_array))
252        {
253            return;
254        }
255
256        $json_data = json_encode($json_data_array);
257
258        // Try to push content to the browser
259        print(str_pad(' ', 4096) . "\n");
260        print($json_data . "\n\n");
261        flush();
262    }
263
264    /**
265     * Prepares iohandler's data to be sent out to the client.
266     *
267     * @param bool    $no_more_output    Whether or not there will be more output in this response
268     *
269     * @return array
270     */
271    protected function prepare_json_array($no_more_output = false)
272    {
273        $json_array = array();
274
275        if (!empty($this->errors))
276        {
277            $json_array['errors'] = $this->errors;
278            $this->errors = array();
279        }
280
281        if (!empty($this->warnings))
282        {
283            $json_array['warnings'] = $this->warnings;
284            $this->warnings = array();
285        }
286
287        if (!empty($this->logs))
288        {
289            $json_array['logs'] = $this->logs;
290            $this->logs = array();
291        }
292
293        if (!empty($this->success))
294        {
295            $json_array['success'] = $this->success;
296            $this->success = array();
297        }
298
299        if (!empty($this->download))
300        {
301            $json_array['download'] = $this->download;
302            $this->download = array();
303        }
304
305        if (!empty($this->form))
306        {
307            $json_array['form'] = $this->form;
308            $this->form = '';
309        }
310
311        if (!empty($this->file_status))
312        {
313            $json_array['file_status'] = $this->file_status;
314            $this->file_status = '';
315        }
316
317        // If current task name is set, we push progress message to the client side
318        if (!empty($this->current_task_name))
319        {
320            $json_array['progress'] = array(
321                'task_name'        => $this->current_task_name,
322                'task_num'        => $this->current_task_progress,
323                'task_count'    => $this->task_progress_count,
324            );
325
326            if ($this->restart_progress_bar)
327            {
328                $json_array['progress']['restart'] = 1;
329                $this->restart_progress_bar = false;
330            }
331        }
332
333        if (!empty($this->nav_data))
334        {
335            $json_array['nav'] = $this->nav_data;
336            $this->nav_data = array();
337        }
338
339        if ($this->request_client_refresh)
340        {
341            $json_array['refresh'] = true;
342            $this->request_client_refresh = false;
343        }
344
345        if (!empty($this->cookies))
346        {
347            $json_array['cookies'] = $this->cookies;
348            $this->cookies = array();
349        }
350
351        if (!empty($this->redirect_url))
352        {
353            $json_array['redirect'] = $this->redirect_url;
354            $this->redirect_url = array();
355        }
356
357        if ($no_more_output)
358        {
359            $json_array['over'] = true;
360        }
361
362        return $json_array;
363    }
364
365    /**
366     * {@inheritdoc}
367     */
368    public function set_progress($task_lang_key, $task_number)
369    {
370        parent::set_progress($task_lang_key, $task_number);
371        $this->send_response();
372    }
373
374    /**
375     * {@inheritdoc}
376     */
377    public function request_refresh()
378    {
379        $this->request_client_refresh = true;
380    }
381
382    /**
383     * {@inheritdoc}
384     */
385    public function set_active_stage_menu($menu_path)
386    {
387        $this->nav_data['active'] = $menu_path[count($menu_path) - 1];
388        $this->send_response();
389    }
390
391    /**
392     * {@inheritdoc}
393     */
394    public function set_finished_stage_menu($menu_path)
395    {
396        $this->nav_data['finished'][] = $menu_path[count($menu_path) - 1];
397        $this->send_response();
398    }
399
400    /**
401     * {@inheritdoc}
402     */
403    public function set_cookie($cookie_name, $cookie_value)
404    {
405        $this->cookies[] = array(
406            'name' => $cookie_name,
407            'value' => $cookie_value
408        );
409    }
410
411    /**
412     * {@inheritdoc}
413     */
414    public function add_download_link($route, $title, $msg = null)
415    {
416        $link_properties = array(
417            'href'    => $this->router->generate($route),
418            'title'    => $this->language->lang($title),
419            'download' => $this->language->lang('DOWNLOAD'),
420        );
421
422        if ($msg !== null)
423        {
424            $link_properties['msg'] = html_entity_decode($this->language->lang($msg), ENT_COMPAT);
425        }
426
427        $this->download[] = $link_properties;
428    }
429
430    /**
431     * {@inheritdoc}
432     */
433    public function render_update_file_status($status_array)
434    {
435        foreach ($status_array as $block => $list)
436        {
437            foreach ($list as $filename)
438            {
439                $dirname = dirname($filename);
440
441                $this->template->assign_block_vars($block, array(
442                    'STATUS'            => $block,
443                    'FILENAME'            => $filename,
444                    'DIR_PART'            => (!empty($dirname) && $dirname !== '.') ? dirname($filename) . '/' : false,
445                    'FILE_PART'            => basename($filename),
446                ));
447            }
448        }
449
450        $this->template->set_filenames(array(
451            'file_status' => 'installer_update_file_status.html',
452        ));
453
454        $this->file_status = $this->template->assign_display('file_status');
455    }
456
457    /**
458     * {@inheritdoc}
459     */
460    public function redirect($url, $use_ajax = false)
461    {
462        $this->redirect_url = array('url' => $url, 'use_ajax' => $use_ajax);
463        $this->send_response(true);
464    }
465
466    /**
467     * Acquires a file lock
468     */
469    public function acquire_lock()
470    {
471        $lock_file = $this->phpbb_root_path . 'store/io_lock.lock';
472        $this->file_lock_pointer = @fopen($lock_file, 'w+');
473
474        if ($this->file_lock_pointer)
475        {
476            flock($this->file_lock_pointer, LOCK_EX);
477        }
478    }
479
480    /**
481     * Release file lock
482     */
483    public function release_lock()
484    {
485        if ($this->file_lock_pointer)
486        {
487            fwrite($this->file_lock_pointer, 'ok');
488            flock($this->file_lock_pointer, LOCK_UN);
489            fclose($this->file_lock_pointer);
490        }
491    }
492
493    /**
494     * Callback function for language replacing
495     *
496     * @param array    $matches
497     * @return string
498     */
499    public function lang_replace_callback($matches)
500    {
501        if (!empty($matches[1]))
502        {
503            return $this->language->lang($matches[1]);
504        }
505
506        return '';
507    }
508}