<?php
/*========================================================================*\
|| ###################################################################### ||
|| # vBulletin 6.2.3 - Licence Number L3138846
|| # ------------------------------------------------------------------ # ||
|| # Copyright 2000-2026 MH Sub I, LLC dba vBulletin. All Rights Reserved.  # ||
|| # This file may not be redistributed in whole or significant part.   # ||
|| # ----------------- VBULLETIN IS NOT FREE SOFTWARE ----------------- # ||
|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html   # ||
|| ###################################################################### ||
\*========================================================================*/

use League\OAuth2\Client\Provider\AbstractProvider;

// #################### PRE-CACHE TEMPLATES AND DATA ######################
global $phrasegroups, $specialtemplates, $vbphrase, $vbulletin;
$phrasegroups = ['cphome', 'cpoption', 'diagnostic'];
$specialtemplates = [];

// ########################## REQUIRE BACK-END ############################
require_once(dirname(__FILE__) . '/global.php');
require_once(DIR . '/includes/adminfunctions_options.php');

// ######################## CHECK ADMIN PERMISSIONS #######################
if (!can_administer('canadminsettings'))
{
	print_cp_no_permission();
}

// ############################# LOG ACTION ###############################
log_admin_action();

// ########################################################################
// ######################### START MAIN SCRIPT ############################
// ########################################################################

// gettoken redirects so we want to make sure there is no output
if ($_REQUEST['do'] != 'gettoken')
{
	print_cp_header($vbphrase['email_manager'], [
	]);
}

if (empty($_REQUEST['do']))
{
	$_REQUEST['do'] = 'intro';
}

// ###################### Intro Screen #######################
if ($_REQUEST['do'] == 'intro')
{
	// we should probably split these so we only fetch what we need to display the current type.
	$globalsettings = [
		'email_type',
		'webmasteremail',
		'bounceemail',
	];

	$typemap = [
		'microsoft' => [
			'typesettings' => [
				'email_microsoft_tenantid',
				'email_microsoft_clientid',
				'email_microsoft_clientsecret',
			],
			'tokensetting' => 'email_microsoft_refreshtoken',
		],
		'gmail' => [
			'typesettings' => [
				'email_gmail_clientid',
				'email_gmail_clientsecret',
			],
			'tokensetting' => 'email_gmail_refreshtoken',
		],
		'smtp' => [
			'typesettings' => [
				'smtp_host',
				'smtp_port',
				'smtp_user',
				'smtp_pass',
				'smtp_tls',
			],
			'tokensetting' => '',
		],
		'legacysmtp' => [
			'typesettings' => [
				'smtp_host',
				'smtp_sender_host',
				'smtp_port',
				'smtp_user',
				'smtp_pass',
				'smtp_tls',
				'utf8encode',
				'extra_smtp_headers',
				'usebulkheader',
			],
			'tokensetting' => '',
		],
		'phpmail' => [],
		'legacyphpmail' => [
			'typesettings' => [
				'mail_delay',
				'utf8encode',
				'needfromemail',
				'usebulkheader',
			],
			'tokensetting' => '',
		],
	];

	$vboptions = vB::getDatastore()->getValue('options');
	$type = $vboptions['email_type'];
	$typedata = $typemap[$type] ?? [];

	$settingnames = $globalsettings;
	if ($typedata)
	{
		$settingnames = array_merge($settingnames, $typedata['typesettings']);
		if ($typedata['tokensetting'])
		{
			$settingnames[] = $typedata['tokensetting'];
		}
	}

	$settingphrase = get_setting_phrases($settingnames);
	$settingrecords = get_settings_list($settingnames);
	// There has to be a better way to do this.
	$columnStyles = ['width:60%', 'width:40%; white-space:nowrap'];

	// Global config, especially choosing what type we're using.
	$globalrecords = get_allowed_settings($globalsettings, $settingrecords);
	if ($globalrecords)
	{
		print_form_header2('admincp/optionsemail', 'updateoptions');
		print_table_start2();
		print_column_style_code($columnStyles);

		print_table_header($vbphrase['email_options_settings']);
		foreach ($globalrecords AS $record)
		{
			print_setting_row($record, $settingphrase);
		}
		print_table_default_footer($vbphrase['save']);
	}

	// Type specific config
	// If they don't have access to the email type option, don't show the associated options.
	// It's possible in the future we might want to make some of these available but it's not sufficiently
	// likely that we want to do it now.
	if ($typedata)
	{
		// if we filter all of the type settings out then don't show the form.  For now we'll assume that if
		// we don't have any other setting then showing the tokensetting info is not going to be useful.
		$typerecords = get_allowed_settings($typedata['typesettings'], $settingrecords);
		if ($typerecords)
		{
			print_form_header2('admincp/optionsemail', 'updateoptions');
			print_table_start2();
			print_column_style_code($columnStyles);
			print_table_header($vbphrase['email_' . $type . '_options']);
			if ($typedata['tokensetting'])
			{
				// This isn't really an option but we want it to blend in so use the option class
				$redirectUrl = get_admincp_url('optionsemail', ['do' => 'savetoken']);
				print_description_row($vbphrase['email_redirecturl'], class: 'optiontitle');
				print_label_row($vbphrase['email_redirecturl_description'], $redirectUrl);
			}

			foreach ($typerecords AS $record)
			{
				print_setting_row($record, $settingphrase);
			}

			if ($typedata['tokensetting'])
			{
				if (isset($settingrecords[$typedata['tokensetting']]))
				{
					print_token_row($settingrecords[$typedata['tokensetting']], $settingphrase);
				}
			}

			print_table_default_footer($vbphrase['save']);
		}
	}

	print_form_header2('admincp/optionsemail', 'testemail');
	print_table_start2();
	print_table_header($vbphrase['diagnostics']);
	print_description_row($vbphrase['email_test_explained']);
	if (empty($vboptions['webmasteremail']))
	{
		print_description_row($vbphrase['warn_webmasteremail_empty']);
	}
	print_input_row($vbphrase['email'], 'emailaddress');
	print_input_row($vbphrase['replyto_email'], 'replytoaddress');
 	print_checkbox_row($vbphrase['html'], 'html', true);
	print_submit_row($vbphrase['send']);
}

if ($_POST['do'] == 'updateoptions')
{
	$vbulletin->input->clean_array_gpc('p', [
		'setting' => vB_Cleaner::TYPE_ARRAY,

		//These are submit buttons however the value is always the label so we really only want to
		//know if it was present or not.   This easier than changing the "do" field before submitting
		//the form via JS.  Only one should have a non empty value.
		'cleartoken' => vB_Cleaner::TYPE_NOHTML,
		'gettoken' => vB_Cleaner::TYPE_NOHTML,
	]);

	$type = vB::getDatastore()->getOption('email_type');

	$settings = $vbulletin->GPC['setting'];

	if ($vbulletin->GPC['cleartoken'])
	{
		$settings['email_' . $type . '_refreshtoken'] = '';
	}

	save_settings($settings);

	if ($vbulletin->GPC['gettoken'])
	{
		$provider = vB_Mail_Transport::create($type);
		$redirectUrl = get_admincp_url('optionsemail', ['do' => 'savetoken']);
		[$authurl, $state] = $provider->getAuthorizationUrl($redirectUrl);

		build_adminutil_text('email_oauth_state', $state);

		header('Location: ' . $authurl);
		exit;
	}

	print_stop_message('saved_settings_successfully', get_admincp_url('optionsemail', []));
}

if ($_REQUEST['do'] == 'savetoken')
{
	$vbulletin->input->clean_array_gpc('r', [
		'code'  => vB_Cleaner::TYPE_STR,
		'state'  => vB_Cleaner::TYPE_STR,
	]);

	$currentstate = fetch_adminutil_text('email_oauth_state');
	build_adminutil_text('email_oauth_state', '');

	// not sure that this will happen to legitimate users often enough to be worth phrasing.
	if ($currentstate != $vbulletin->GPC['state'])
	{
		print_stop_message(['error_x', 'Invalid Oauth State'], '', '');
	}

	$type = vB::getDatastore()->getOption('email_type');
	$provider = vB_Mail_Transport::create($type);

	$redirectUrl = get_admincp_url('optionsemail', ['do' => 'savetoken']);
	$setting = [
		'email_' . $type . '_refreshtoken' => $provider->getRefreshToken($vbulletin->GPC['code'], $redirectUrl),
	];

	save_settings($setting);

	$args = [
		'locfile' => 'optionsemail',
		'locparams' => [],
	];

	print_stop_message('saved_settings_successfully', get_admincp_url('index', $args));
}

if ($_POST['do'] == 'testemail')
{
	$vbulletin->input->clean_array_gpc('p', [
		'emailaddress' => vB_Cleaner::TYPE_STR,
		'replytoaddress' => vB_Cleaner::TYPE_STR,
		'html' => vB_Cleaner::TYPE_BOOL,
	]);

	$vboptions = vB::getDatastore()->getValue('options');
	$type = $vboptions['email_type'];
	$provider = vB_Mail_Transport::create($type);
	$provider->setDebug(true);

	$emailaddress = $vbulletin->GPC['emailaddress'];
	$replyto = '';
	if ($vbulletin->GPC['replytoaddress'])
	{
		$replyto = [
			$vbulletin->GPC['replytoaddress'],
			'Reply Name',
		];
	}

	if (empty($emailaddress))
	{
		print_stop_message('please_complete_required_fields');
	}

	if (!vB_String::isValidEmail($emailaddress))
	{
		print_stop_message('invalid_email_specified');
	}

	$phraseApi = vB_Api::instanceInternal('phrase');
	$string = vB::getString();
	$bbtitle_escaped = $string->htmlspecialchars($vboptions['bbtitle']);

	$subject = $vbphrase['vbulletin_email_test'];
	['phrases' => $phrases] = $phraseApi->renderPhrases([
		'subject' => 'vbulletin_email_test',
		'message' => ['vbulletin_email_test_msg', $bbtitle_escaped],
	]);

	$exception = null;
	$mailreturn = true;

	$olderrordisplay = ini_set('display_errors', true);
	try
	{
		ob_start();
		$mailreturn = $provider->send($emailaddress, $phrases['subject'], $phrases['message'], $vbulletin->GPC['html'], [], $replyto);
	}
	catch (Throwable $e)
	{
		$exception = $e;
	}
	finally
	{
		$errors = ob_get_contents();
		ini_set('display_errors', $olderrordisplay);
		ob_end_clean();
	}
	// end error handling

	$results = [];
	if (!$mailreturn OR $errors OR $exception)
	{
		if (!$mailreturn)
		{
			$results[] = $vbphrase['mail_function_returned_error'];
		}

		if ($errors)
		{
			$results[] = $vbphrase['mail_function_errors_returned_were'] . '<br /><br />' . $errors;
		}

		if ($exception)
		{
			['phrases' => $phrases] = $phraseApi->renderPhrases([
				['error_x', $exception->getMessage()],
				['exception_trace',  vB_Exception::getFullTrace($exception)]
			]);

			$results = array_merge($results, $phrases);
		}

		$debug = $provider->getDebugInfo();
		if ($debug)
		{
			$results[] = $vbphrase['debug_information'] . '<br /><br />' . implode('<br />', $debug);
		}
	}
	else
	{
		$results[] = construct_phrase($vbphrase['email_sent_check_shortly'], $emailaddress);
	}

	print_form_header2('admincp/optionsemail', '');
	print_table_start2();
	print_table_header($vbphrase['results']);
	foreach ($results AS $result)
	{
		print_description_row($result);
	}
	print_table_default_footer($vbphrase['continue']);
}

print_cp_footer();


function print_token_row($setting, $settingphrase)
{
	global $vbphrase;

	$settingid = $setting['varname'];
	$datastore = vB::getDatastore();

	$varname = $setting['varname'];
	$optiontitleattr = '';
	$controls = '';
	if (vB::isDebug())
	{
		$userContext = vB::getUserContext();
		$optiontitleattr = ' title="Option name: ' . $varname . '"';
		if ($userContext->hasAdminPermission('canadminsettingsall'))
		{
			$controls = '<div class="smallfont h-right">' .
				construct_link_code($vbphrase['edit'], "options.php?do=editsetting&amp;varname=$setting[varname]") .
				construct_link_code($vbphrase['delete'], "options.php?do=removesetting&amp;varname=$setting[varname]") .
			'</div>';
		}
	}

	echo '<tbody>';
	print_description_row($controls . '<div id="' . $varname . '" ' . $optiontitleattr . ' >' . $settingphrase['setting_' . $varname . '_title'] . '</div>', 0, 2, 'optiontitle');
	echo '</tbody>';

	// make sure all rows use the alt1 class
	global $bgcounter;
	$bgcounter--;

	$description = '<div class="smallfont"' . $optiontitleattr . '>' . $settingphrase['setting_' . $varname . '_desc'] . '</div>';

	$message = $vbphrase[($setting['value'] ? 'email_token_set' : 'email_token_not_set')];
	$getbutton = construct_submit_button($vbphrase['email_update_token'], ['js-alternate-submit',], ['name' => 'gettoken', 'data-target' => '_top']);
	$clearbutton = construct_submit_button($vbphrase['email_clear_token'], [], ['name' => 'cleartoken']);

	$contents = $message . $getbutton . $clearbutton;

	echo "<tbody id=\"tbody_$settingid\">\r\n";
	print_label_row($description, $contents);
	echo "</tbody>\r\n";
}



/*=========================================================================*\
|| #######################################################################
|| # Downloaded: 13:27, Sun Aug 30th 2026
|| # CVS: $RCSfile$ - $Revision: 120298 $
|| #######################################################################
\*=========================================================================*/
