1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22/**
23 * Setup wizzard form.
24 */
25class CSetupWizard extends CForm {
26
27	protected $DISABLE_CANCEL_BUTTON = false;
28
29	protected $DISABLE_BACK_BUTTON = false;
30
31	protected $SHOW_RETRY_BUTTON = false;
32
33	protected $STEP_FAILED = false;
34
35	protected $frontend_setup;
36
37	public function __construct() {
38		$this->frontend_setup = new CFrontendSetup();
39
40		$this->stage = [
41			0 => [
42				'title' => _('Welcome'),
43				'fnc' => 'stage0'
44			],
45			1 => [
46				'title' => _('Check of pre-requisites'),
47				'fnc' => 'stage1'
48			],
49			2 => [
50				'title' => _('Configure DB connection'),
51				'fnc' => 'stage2'
52			],
53			3 => [
54				'title' => _('Zabbix server details'),
55				'fnc' => 'stage3'
56			],
57			4 => [
58				'title' => _('Pre-installation summary'),
59				'fnc' => 'stage4'
60			],
61			5 => [
62				'title' => _('Install'),
63				'fnc' => 'stage5'
64			]
65		];
66
67		$this->eventHandler();
68
69		parent::__construct('post');
70		parent::setId('setup-form');
71	}
72
73	private function getConfig(string $name, $default = null) {
74		return CSession::keyExists($name) ? CSession::getValue($name) : $default;
75	}
76
77	private function setConfig(string $name, $value) {
78		CSession::setValue($name, $value);
79	}
80
81	private function getStep(): int {
82		return $this->getConfig('step', 0);
83	}
84
85	private function doNext(): bool {
86		if (isset($this->stage[$this->getStep() + 1])) {
87			$this->setConfig('step', $this->getStep('step') + 1);
88
89			return true;
90		}
91
92		return false;
93	}
94
95	private function doBack(): bool {
96		if (isset($this->stage[$this->getStep() - 1])) {
97			$this->setConfig('step', $this->getStep('step') - 1);
98
99			return true;
100		}
101
102		return false;
103	}
104
105	protected function bodyToString(bool $destroy = true): string {
106		$step = $this->getStep();
107
108		$setup_left = (new CDiv())
109			->addClass(ZBX_STYLE_SETUP_LEFT)
110			->addItem((new CDiv(makeLogo(LOGO_TYPE_NORMAL)))->addClass('setup-logo'))
111			->addItem($this->getList());
112
113		$setup_right = (new CDiv($this->getStage()))->addClass(ZBX_STYLE_SETUP_RIGHT);
114
115		if (CWebUser::$data && CWebUser::getType() == USER_TYPE_SUPER_ADMIN) {
116			$cancel_button = (new CSubmit('cancel', _('Cancel')))
117				->addClass(ZBX_STYLE_BTN_ALT)
118				->addClass(ZBX_STYLE_FLOAT_LEFT);
119			if ($this->DISABLE_CANCEL_BUTTON) {
120				$cancel_button->setEnabled(false);
121			}
122		}
123		else {
124			$cancel_button = null;
125		}
126
127		if (array_key_exists($step + 1, $this->stage)) {
128			$next_button = new CSubmit('next['.$step.']', _('Next step'));
129		}
130		else {
131			$next_button = new CSubmit($this->SHOW_RETRY_BUTTON ? 'retry' : 'finish', _('Finish'));
132		}
133
134		$back_button = (new CSubmit('back['.$step.']', _('Back')))
135			->addClass(ZBX_STYLE_BTN_ALT)
136			->addClass(ZBX_STYLE_FLOAT_LEFT);
137
138		if ($step == 0 || $this->DISABLE_BACK_BUTTON) {
139			$back_button->setEnabled(false);
140		}
141
142		$setup_footer = (new CDiv([new CDiv([$next_button, $back_button]), $cancel_button]))
143			->addClass(ZBX_STYLE_SETUP_FOOTER);
144
145		$setup_container = (new CDiv([$setup_left, $setup_right, $setup_footer]))->addClass(ZBX_STYLE_SETUP_CONTAINER);
146
147		return parent::bodyToString($destroy).$setup_container->toString();
148	}
149
150	private function getList(): CList {
151		$list = new CList();
152
153		foreach ($this->stage as $id => $data) {
154			$list->addItem($data['title'], ($id <= $this->getStep()) ? ZBX_STYLE_SETUP_LEFT_CURRENT : null);
155		}
156
157		return $list;
158	}
159
160	private function getStage(): array {
161		$function = $this->stage[$this->getStep()]['fnc'];
162		return $this->$function();
163	}
164
165	private function stage0(): array {
166		preg_match('/^\d+\.\d+/', ZABBIX_VERSION, $version);
167		$setup_title = (new CDiv([new CSpan(_('Welcome to')), 'Zabbix '.$version[0]]))->addClass(ZBX_STYLE_SETUP_TITLE);
168
169		return [(new CDiv($setup_title))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)];
170	}
171
172	private function stage1(): array {
173		$table = (new CTable())
174			->addClass(ZBX_STYLE_LIST_TABLE)
175			->setHeader(['', _('Current value'), _('Required'), '']);
176
177		$messages = [];
178		$finalResult = CFrontendSetup::CHECK_OK;
179
180		foreach ($this->frontend_setup->checkRequirements() as $req) {
181			if ($req['result'] == CFrontendSetup::CHECK_OK) {
182				$class = ZBX_STYLE_GREEN;
183				$result = 'OK';
184			}
185			elseif ($req['result'] == CFrontendSetup::CHECK_WARNING) {
186				$class = ZBX_STYLE_ORANGE;
187				$result = new CSpan(_x('Warning', 'setup'));
188			}
189			else {
190				$class = ZBX_STYLE_RED;
191				$result = new CSpan(_('Fail'));
192				$messages[] = ['type' => 'error', 'message' => $req['error']];
193			}
194
195			$table->addRow(
196				[
197					$req['name'],
198					$req['current'],
199					($req['required'] !== null) ? $req['required'] : '',
200					(new CCol($result))->addClass($class)
201				]
202			);
203
204			if ($req['result'] > $finalResult) {
205				$finalResult = $req['result'];
206			}
207		}
208
209		if ($finalResult == CFrontendSetup::CHECK_FATAL) {
210			$message_box = makeMessageBox(ZBX_STYLE_MSG_BAD, $messages, null, false, true);
211		}
212		else {
213			$message_box = null;
214		}
215
216		return [
217			new CTag('h1', true, _('Check of pre-requisites')),
218			(new CDiv([$message_box, $table]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
219		];
220	}
221
222	private function stage2(): array {
223		$DB['TYPE'] = $this->getConfig('DB_TYPE', key(CFrontendSetup::getSupportedDatabases()));
224
225		$table = (new CFormList())
226			->addItem([
227				(new CVar('tls_encryption', 0))->removeId(),
228				(new CVar('verify_certificate', 0))->removeId(),
229				(new CVar('verify_host', 0))->removeId()
230			]);
231
232		$table->addRow(new CLabel(_('Database type'), 'label-type'),
233			(new CSelect('type'))
234				->setId('type')
235				->setFocusableElementId('label-type')
236				->setValue($DB['TYPE'])
237				->addOptions(CSelect::createOptionsFromArray(CFrontendSetup::getSupportedDatabases()))
238		);
239
240		$table->addRow(_('Database host'),
241			(new CTextBox('server', $this->getConfig('DB_SERVER', 'localhost')))
242				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
243		);
244
245		$table->addRow(_('Database port'), [
246			(new CNumericBox('port', $this->getConfig('DB_PORT', '0'), 5, false, false, false))
247				->removeAttribute('style')
248				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
249			(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
250			(new CSpan(_('0 - use default port')))->addClass(ZBX_STYLE_GREY)
251		]);
252
253		$table->addRow(_('Database name'),
254			(new CTextBox('database', $this->getConfig('DB_DATABASE', 'zabbix')))
255				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
256		);
257
258		$table->addRow(_('Database schema'),
259			(new CTextBox('schema', $this->getConfig('DB_SCHEMA', '')))
260				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
261			'db_schema_row',
262			ZBX_STYLE_DISPLAY_NONE
263		);
264
265		$table->addRow(_('User'),
266			(new CTextBox('user', $this->getConfig('DB_USER', 'zabbix')))->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
267		);
268		$table->addRow(_('Password'),
269			(new CPassBox('password', $this->getConfig('DB_PASSWORD')))->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
270		);
271
272		$table->addRow(_('Database TLS encryption'), [
273				(new CCheckBox('tls_encryption'))->setChecked($this->getConfig('DB_ENCRYPTION', true)),
274				(new CDiv(
275					_('Connection will not be encrypted because it uses a socket file (on Unix) or shared memory (Windows).')
276				))
277					->setId('tls_encryption_hint')
278					->addClass(ZBX_STYLE_DISPLAY_NONE)
279			],
280			'db_encryption_row',
281			ZBX_STYLE_DISPLAY_NONE
282		);
283
284		$table->addRow(_('Verify database certificate'),
285			(new CCheckBox('verify_certificate'))->setChecked($this->getConfig('DB_ENCRYPTION_ADVANCED')),
286			'db_verify_host',
287			ZBX_STYLE_DISPLAY_NONE
288		);
289
290		$table->addRow((new CLabel(_('Database TLS CA file')))->setAsteriskMark(),
291			(new CTextBox('ca_file', $this->getConfig('DB_CA_FILE')))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
292			'db_cafile_row',
293			ZBX_STYLE_DISPLAY_NONE
294		);
295
296		$table->addRow(_('Database TLS key file'),
297			(new CTextBox('key_file', $this->getConfig('DB_KEY_FILE')))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
298			'db_keyfile_row',
299			ZBX_STYLE_DISPLAY_NONE
300		);
301
302		$table->addRow(_('Database TLS certificate file'),
303			(new CTextBox('cert_file', $this->getConfig('DB_CERT_FILE')))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
304			'db_certfile_row',
305			ZBX_STYLE_DISPLAY_NONE
306		);
307
308		$table->addRow(_('Database host verification'),
309			(new CCheckBox('verify_host'))->setChecked($this->getConfig('DB_VERIFY_HOST')),
310			'db_verify_host_row',
311			ZBX_STYLE_DISPLAY_NONE
312		);
313
314		$table->addRow(_('Database TLS cipher list'),
315			(new CTextBox('cipher_list', $this->getConfig('DB_CIPHER_LIST')))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
316			'db_cipher_row',
317			ZBX_STYLE_DISPLAY_NONE
318		);
319
320		if ($this->STEP_FAILED) {
321			global $ZBX_MESSAGES;
322
323			$message_box = makeMessageBox(ZBX_STYLE_MSG_BAD, $ZBX_MESSAGES, _('Cannot connect to the database.'), false,
324				true
325			);
326		}
327		else {
328			$message_box = null;
329		}
330
331		return [
332			new CTag('h1', true, _('Configure DB connection')),
333			(new CDiv([
334				new CTag('p', true, _s('Please create database manually, and set the configuration parameters for connection to this database. Press "%1$s" button when done.', _('Next step'))),
335				$message_box,
336				$table
337			]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
338		];
339	}
340
341	private function stage3(): array {
342		$table = new CFormList();
343
344		$table->addRow(_('Host'),
345			(new CTextBox('zbx_server', $this->getConfig('ZBX_SERVER', 'localhost')))
346				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
347		);
348
349		$table->addRow(_('Port'),
350			(new CNumericBox('zbx_server_port', $this->getConfig('ZBX_SERVER_PORT', '10051'), 5, false, false, false))
351				->removeAttribute('style')
352				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
353		);
354
355		$table->addRow('Name',
356			(new CTextBox('zbx_server_name', $this->getConfig('ZBX_SERVER_NAME', '')))
357				->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
358		);
359
360		return [
361			new CTag('h1', true, _('Zabbix server details')),
362			(new CDiv([
363				new CTag('p', true, _('Please enter the host name or host IP address and port number of the Zabbix server, as well as the name of the installation (optional).')),
364				$table
365			]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
366		];
367	}
368
369	private function stage4(): array {
370		$db_type = $this->getConfig('DB_TYPE');
371		$databases = CFrontendSetup::getSupportedDatabases();
372
373		$table = new CFormList();
374		$table->addRow((new CSpan(_('Database type')))->addClass(ZBX_STYLE_GREY), $databases[$db_type]);
375
376		$db_port = ($this->getConfig('DB_PORT') == 0) ? _('default') : $this->getConfig('DB_PORT');
377		$db_password = preg_replace('/./', '*', $this->getConfig('DB_PASSWORD'));
378
379		$table->addRow((new CSpan(_('Database server')))->addClass(ZBX_STYLE_GREY), $this->getConfig('DB_SERVER'));
380		$table->addRow((new CSpan(_('Database port')))->addClass(ZBX_STYLE_GREY), $db_port);
381		$table->addRow((new CSpan(_('Database name')))->addClass(ZBX_STYLE_GREY), $this->getConfig('DB_DATABASE'));
382		$table->addRow((new CSpan(_('Database user')))->addClass(ZBX_STYLE_GREY), $this->getConfig('DB_USER'));
383		$table->addRow((new CSpan(_('Database password')))->addClass(ZBX_STYLE_GREY), $db_password);
384		if ($db_type === ZBX_DB_POSTGRESQL) {
385			$table->addRow((new CSpan(_('Database schema')))->addClass(ZBX_STYLE_GREY), $this->getConfig('DB_SCHEMA'));
386		}
387		$table->addRow((new CSpan(_('Database TLS encryption')))->addClass(ZBX_STYLE_GREY),
388			$this->getConfig('DB_ENCRYPTION') ? 'true' : 'false'
389		);
390		if ($this->getConfig('DB_ENCRYPTION_ADVANCED')) {
391			$table->addRow((new CSpan(_('Database TLS CA file')))->addClass(ZBX_STYLE_GREY),
392				$this->getConfig('DB_CA_FILE')
393			);
394			$table->addRow((new CSpan(_('Database TLS key file')))->addClass(ZBX_STYLE_GREY),
395				$this->getConfig('DB_KEY_FILE')
396			);
397			$table->addRow((new CSpan(_('Database TLS certificate file')))->addClass(ZBX_STYLE_GREY),
398				$this->getConfig('DB_CERT_FILE')
399			);
400			$table->addRow((new CSpan(_('Database host verification')))->addClass(ZBX_STYLE_GREY),
401				$this->getConfig('DB_VERIFY_HOST') ? 'true' : 'false'
402			);
403			if ($db_type === ZBX_DB_MYSQL) {
404				$table->addRow((new CSpan(_('Database TLS cipher list')))->addClass(ZBX_STYLE_GREY),
405					$this->getConfig('DB_CIPHER_LIST')
406				);
407			}
408		}
409
410		$table->addRow(null, null);
411
412		$table->addRow((new CSpan(_('Zabbix server')))->addClass(ZBX_STYLE_GREY), $this->getConfig('ZBX_SERVER'));
413		$table->addRow((new CSpan(_('Zabbix server port')))->addClass(ZBX_STYLE_GREY),
414			$this->getConfig('ZBX_SERVER_PORT')
415		);
416		$table->addRow((new CSpan(_('Zabbix server name')))->addClass(ZBX_STYLE_GREY),
417			$this->getConfig('ZBX_SERVER_NAME')
418		);
419
420		return [
421			new CTag('h1', true, _('Pre-installation summary')),
422			(new CDiv([
423				new CTag('p', true, _s('Please check configuration parameters. If all is correct, press "%1$s" button, or "%2$s" button to change configuration parameters.', _('Next step'), _('Back'))),
424				$table
425			]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
426		];
427	}
428
429	private function stage5(): array {
430		/*
431		 * Having non-super-admin authenticated at this step means:
432		 *   - Either the config file has been manually created by the user.
433		 *   - Or dealing with a spoofed session cookie.
434		 *
435		 * Since it is not possible to distinguish between the two, it's also impossible to validate the config file
436		 * and display any discrepancies with the configuration stored within the session.
437		 */
438		if (CWebUser::$data && CWebUser::getType() < USER_TYPE_SUPER_ADMIN) {
439			CSession::clear();
440
441			return $this->stageInstalled();
442		}
443
444		$this->setConfig('ZBX_CONFIG_FILE_CORRECT', true);
445
446		$config_file_name = APP::getInstance()->getRootDir().CConfigFile::CONFIG_FILE_PATH;
447		$config = new CConfigFile($config_file_name);
448		$config->config = [
449			'DB' => [
450				'TYPE' => $this->getConfig('DB_TYPE'),
451				'SERVER' => $this->getConfig('DB_SERVER'),
452				'PORT' => $this->getConfig('DB_PORT'),
453				'DATABASE' => $this->getConfig('DB_DATABASE'),
454				'USER' => $this->getConfig('DB_USER'),
455				'PASSWORD' => $this->getConfig('DB_PASSWORD'),
456				'SCHEMA' => $this->getConfig('DB_SCHEMA'),
457				'ENCRYPTION' => $this->getConfig('DB_ENCRYPTION'),
458				'KEY_FILE' => $this->getConfig('DB_KEY_FILE'),
459				'CERT_FILE' => $this->getConfig('DB_CERT_FILE'),
460				'CA_FILE' => $this->getConfig('DB_CA_FILE'),
461				'VERIFY_HOST' => $this->getConfig('DB_VERIFY_HOST'),
462				'CIPHER_LIST' => $this->getConfig('DB_CIPHER_LIST'),
463				'DOUBLE_IEEE754' => $this->getConfig('DB_DOUBLE_IEEE754')
464			],
465			'ZBX_SERVER' => $this->getConfig('ZBX_SERVER'),
466			'ZBX_SERVER_PORT' => $this->getConfig('ZBX_SERVER_PORT'),
467			'ZBX_SERVER_NAME' => $this->getConfig('ZBX_SERVER_NAME')
468		];
469
470		$error = false;
471
472		if (!$config->save()) {
473			$error = true;
474			$messages[] = [
475				'type' => 'error',
476				'message' => $config->error
477			];
478		}
479
480		if ($error) {
481			$this->SHOW_RETRY_BUTTON = true;
482
483			$this->setConfig('ZBX_CONFIG_FILE_CORRECT', false);
484
485			$message_box = makeMessageBox(ZBX_STYLE_MSG_BAD, $messages, _('Cannot create the configuration file.'),
486				false, true
487			);
488			$message = [
489				new CTag('p', true, _('Alternatively, you can install it manually:')),
490				new CTag('ol', true, [
491					new CTag('li', true, new CLink(_('Download the configuration file'), 'setup.php?save_config=1')),
492					new CTag('li', true, _s('Save it as "%1$s"', $config_file_name))
493				])
494			];
495
496			return [
497				new CTag('h1', true, _('Install')),
498				(new CDiv([$message_box, $message]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
499			];
500		}
501
502		// Clear session after success install.
503		CSession::clear();
504
505		return $this->stageInstalled();
506	}
507
508	private function stageInstalled() {
509		$this->DISABLE_CANCEL_BUTTON = true;
510		$this->DISABLE_BACK_BUTTON = true;
511
512		$message_box = null;
513		$message = [
514			(new CTag('h1', true, _('Congratulations! You have successfully installed Zabbix frontend.')))
515				->addClass(ZBX_STYLE_GREEN),
516			new CTag('p', true, _s('Configuration file "%1$s" created.', ltrim(CConfigFile::CONFIG_FILE_PATH, '/')))
517		];
518
519		return [
520			new CTag('h1', true, _('Install')),
521			(new CDiv([$message_box, $message]))->addClass(ZBX_STYLE_SETUP_RIGHT_BODY)
522		];
523	}
524
525	private function dbConnect() {
526		global $DB;
527
528		if (!$this->getConfig('check_fields_result')) {
529			return false;
530		}
531
532		$DB['TYPE'] = $this->getConfig('DB_TYPE');
533		if ($DB['TYPE'] === null) {
534			return false;
535		}
536
537		$DB['SERVER'] = $this->getConfig('DB_SERVER', 'localhost');
538		$DB['PORT'] = $this->getConfig('DB_PORT', '0');
539		$DB['DATABASE'] = $this->getConfig('DB_DATABASE', 'zabbix');
540		$DB['USER'] = $this->getConfig('DB_USER', 'root');
541		$DB['PASSWORD'] = $this->getConfig('DB_PASSWORD', '');
542		$DB['SCHEMA'] = $this->getConfig('DB_SCHEMA', '');
543		$DB['ENCRYPTION'] = (bool) $this->getConfig('DB_ENCRYPTION', true);
544		$DB['VERIFY_HOST'] = (bool) $this->getConfig('DB_VERIFY_HOST', true);
545		$DB['KEY_FILE'] = $this->getConfig('DB_KEY_FILE', '');
546		$DB['CERT_FILE'] = $this->getConfig('DB_CERT_FILE', '');
547		$DB['CA_FILE'] = $this->getConfig('DB_CA_FILE', '');
548		$DB['CIPHER_LIST'] = $this->getConfig('DB_CIPHER_LIST', '');
549
550		$error = '';
551
552		// Check certificate files exists.
553		if ($DB['ENCRYPTION'] && ($DB['TYPE'] === ZBX_DB_MYSQL || $DB['TYPE'] === ZBX_DB_POSTGRESQL)) {
554			if (($this->getConfig('DB_ENCRYPTION_ADVANCED') || $DB['CA_FILE'] !== '') && !file_exists($DB['CA_FILE'])) {
555				return _s('Incorrect file path for "%1$s": %2$s.', _('Database TLS CA file'), $DB['CA_FILE']);
556			}
557
558			if ($DB['KEY_FILE'] !== '' && !file_exists($DB['KEY_FILE'])) {
559				return _s('Incorrect file path for "%1$s": %2$s.', _('Database TLS key file'), $DB['KEY_FILE']);
560			}
561
562			if ($DB['CERT_FILE'] !== '' && !file_exists($DB['CERT_FILE'])) {
563				return _s('Incorrect file path for "%1$s": %2$s.', _('Database TLS certificate file'),
564					$DB['CERT_FILE']
565				);
566			}
567		}
568
569		// During setup set debug to false to avoid displaying unwanted PHP errors in messages.
570		if (DBconnect($error)) {
571			return true;
572		}
573		else {
574			return $error;
575		}
576	}
577
578	private function dbClose(): void {
579		global $DB;
580
581		DBclose();
582
583		$DB = null;
584	}
585
586	private function checkConnection() {
587		global $DB;
588
589		$result = true;
590
591		if (!zbx_empty($DB['SCHEMA']) && $DB['TYPE'] == ZBX_DB_POSTGRESQL) {
592			$db_schema = DBselect(
593				"SELECT schema_name".
594				" FROM information_schema.schemata".
595				" WHERE schema_name='".pg_escape_string($DB['SCHEMA'])."'"
596			);
597			$result = DBfetch($db_schema);
598		}
599
600		$db = DB::getDbBackend();
601
602		if (!$db->checkEncoding()) {
603			error($db->getWarning());
604
605			return false;
606		}
607
608		return $result;
609	}
610
611	private function eventHandler(): void {
612		/*
613		 * Having non-super-admin authenticated at this step means:
614		 *   - Either the config file has been manually created by the user.
615		 *   - Or dealing with a spoofed session cookie.
616		 *
617		 * Since it is not possible to distinguish between the two, skip data validation and prevent stage switching.
618		 * Any of either cases is only possible with 5th stage.
619		 */
620		if (CWebUser::$data && CWebUser::getType() < USER_TYPE_SUPER_ADMIN) {
621			return;
622		}
623
624		if (hasRequest('back') && array_key_exists($this->getStep(), getRequest('back'))) {
625			$this->doBack();
626		}
627
628		if ($this->getStep() == 1) {
629			if (hasRequest('next') && array_key_exists(1, getRequest('next'))) {
630				$finalResult = CFrontendSetup::CHECK_OK;
631				foreach ($this->frontend_setup->checkRequirements() as $req) {
632					if ($req['result'] > $finalResult) {
633						$finalResult = $req['result'];
634					}
635				}
636
637				if ($finalResult == CFrontendSetup::CHECK_FATAL) {
638					$this->STEP_FAILED = true;
639					unset($_REQUEST['next']);
640				}
641				else {
642					$this->doNext();
643				}
644			}
645		}
646		elseif ($this->getStep() == 2) {
647			$input = [
648				'DB_TYPE' => getRequest('type', $this->getConfig('DB_TYPE')),
649				'DB_SERVER' => getRequest('server', $this->getConfig('DB_SERVER', 'localhost')),
650				'DB_PORT' => getRequest('port', $this->getConfig('DB_PORT', '0')),
651				'DB_DATABASE' => getRequest('database', $this->getConfig('DB_DATABASE', 'zabbix')),
652				'DB_USER' => getRequest('user', $this->getConfig('DB_USER', 'root')),
653				'DB_PASSWORD' => getRequest('password', $this->getConfig('DB_PASSWORD', '')),
654				'DB_SCHEMA' => getRequest('schema', $this->getConfig('DB_SCHEMA', '')),
655				'DB_ENCRYPTION' => (bool) getRequest('tls_encryption', $this->getConfig('DB_ENCRYPTION', false)),
656				'DB_ENCRYPTION_ADVANCED' => (bool) getRequest('verify_certificate',
657					$this->getConfig('DB_ENCRYPTION_ADVANCED', false)
658				),
659				'DB_VERIFY_HOST' => (bool) getRequest('verify_host', $this->getConfig('DB_VERIFY_HOST', false)),
660				'DB_KEY_FILE' => getRequest('key_file', $this->getConfig('DB_KEY_FILE', '')),
661				'DB_CERT_FILE' => getRequest('cert_file', $this->getConfig('DB_CERT_FILE', '')),
662				'DB_CA_FILE' => getRequest('ca_file', $this->getConfig('DB_CA_FILE', '')),
663				'DB_CIPHER_LIST' => getRequest('cipher_list', $this->getConfig('DB_CIPHER_LIST', ''))
664			];
665
666			if (!$input['DB_ENCRYPTION_ADVANCED']) {
667				$input['DB_KEY_FILE'] = '';
668				$input['DB_CERT_FILE'] = '';
669				$input['DB_CA_FILE'] = '';
670				$input['DB_CIPHER_LIST'] = '';
671			}
672			else if ($input['DB_TYPE'] === ZBX_DB_MYSQL) {
673				$input['DB_VERIFY_HOST'] = true;
674			}
675
676			if ($input['DB_TYPE'] !== ZBX_DB_POSTGRESQL) {
677				$input['DB_SCHEMA'] = '';
678			}
679
680			array_map([$this, 'setConfig'], array_keys($input), $input);
681
682			if (hasRequest('next') && array_key_exists(2, getRequest('next'))) {
683				$db_connected = $this->dbConnect();
684				if ($db_connected === true) {
685					$db_connection_checked = $this->checkConnection();
686				}
687				else {
688					error($db_connected);
689					$db_connection_checked = false;
690				}
691
692				if ($db_connection_checked) {
693					$this->setConfig('DB_DOUBLE_IEEE754', DB::getDbBackend()->isDoubleIEEE754());
694				}
695
696				if ($db_connected === true) {
697					$this->dbClose();
698				}
699
700				if ($db_connection_checked) {
701					$this->doNext();
702				}
703				else {
704					$this->STEP_FAILED = true;
705					unset($_REQUEST['next']);
706				}
707			}
708		}
709		elseif ($this->getStep() == 3) {
710			$this->setConfig('ZBX_SERVER', getRequest('zbx_server', $this->getConfig('ZBX_SERVER', 'localhost')));
711			$this->setConfig('ZBX_SERVER_PORT', getRequest('zbx_server_port', $this->getConfig('ZBX_SERVER_PORT', '10051')));
712			$this->setConfig('ZBX_SERVER_NAME', getRequest('zbx_server_name', $this->getConfig('ZBX_SERVER_NAME', '')));
713
714			if (hasRequest('next') && array_key_exists(3, getRequest('next'))) {
715				$this->doNext();
716			}
717		}
718		elseif ($this->getStep() == 4) {
719			if (hasRequest('next') && array_key_exists(4, getRequest('next'))) {
720				$this->doNext();
721			}
722		}
723		elseif ($this->getStep() == 5) {
724			if (hasRequest('save_config')) {
725				// make zabbix.conf.php downloadable
726				header('Content-Type: application/x-httpd-php');
727				header('Content-Disposition: attachment; filename="'.basename(CConfigFile::CONFIG_FILE_PATH).'"');
728				$config = new CConfigFile(APP::getInstance()->getRootDir().CConfigFile::CONFIG_FILE_PATH);
729				$config->config = [
730					'DB' => [
731						'TYPE' => $this->getConfig('DB_TYPE'),
732						'SERVER' => $this->getConfig('DB_SERVER'),
733						'PORT' => $this->getConfig('DB_PORT'),
734						'DATABASE' => $this->getConfig('DB_DATABASE'),
735						'USER' => $this->getConfig('DB_USER'),
736						'PASSWORD' => $this->getConfig('DB_PASSWORD'),
737						'SCHEMA' => $this->getConfig('DB_SCHEMA'),
738						'ENCRYPTION' => (bool) $this->getConfig('DB_ENCRYPTION'),
739						'VERIFY_HOST' => (bool) $this->getConfig('DB_VERIFY_HOST'),
740						'KEY_FILE' => $this->getConfig('DB_KEY_FILE'),
741						'CERT_FILE' => $this->getConfig('DB_CERT_FILE'),
742						'CA_FILE' => $this->getConfig('DB_CA_FILE'),
743						'CIPHER_LIST' => $this->getConfig('DB_CIPHER_LIST'),
744						'DOUBLE_IEEE754' => $this->getConfig('DB_DOUBLE_IEEE754')
745					],
746					'ZBX_SERVER' => $this->getConfig('ZBX_SERVER'),
747					'ZBX_SERVER_PORT' => $this->getConfig('ZBX_SERVER_PORT'),
748					'ZBX_SERVER_NAME' => $this->getConfig('ZBX_SERVER_NAME')
749				];
750				die($config->getString());
751			}
752		}
753
754		if (hasRequest('next') && array_key_exists($this->getStep(), getRequest('next'))) {
755			$this->doNext();
756		}
757	}
758}
759