errors = array(); } public function hasErrors(): bool { return count($this->errors) > 0; } public function getErrors(): array { return $this->errors; } public function hasError($id): bool { return isset($this->errors[$id]); } public function getError($id) { if (!$this->hasError($id)) return null; return (strlen($this->errors[$id]) > 0) ? $this->errors[$id] : $id; } protected function getDefaultEncryption() { $methods = openssl_get_cipher_methods(); return reset($methods); } protected function getDefaultKey() { // This is key used to encrypt form data to prevent it being tampered. // This is NOT the Google reCAPTCHA v3 key! return 'hM7cA7iD5eT4mA9aV9oT5dJ6uQ3hJ5uO'; } protected function getDefaultIV($length = 16) { return substr(hash('sha256', 'mQ5dO7uZ7gG2qD8u'), 0, $length); } protected function encrypt($data) { $cipher = $this->getDefaultEncryption(); return openssl_encrypt($data, $cipher, $this->getDefaultKey(), 0, $this->getDefaultIV(openssl_cipher_iv_length($cipher))); } protected function decrypt($data) { $cipher = $this->getDefaultEncryption(); return openssl_decrypt($data, $cipher, $this->getDefaultKey(), 0, $this->getDefaultIV(openssl_cipher_iv_length($cipher))); } } class DirectoryContactFormProcessor extends DirectoryContactForm { // IMPORTANT: This is the Google reCAPTCHA v3 secret key is used with the directory contact form. // If you update the secret key, ensure you update the site key in the 'Directory Profile' content type too. // See 'Directory Profile' content type: // https://csumbedu-cms01-production.terminalfour.net/terminalfour/page/contenttype#edit/317/contentlayouts/15635 protected $googleSecretKey = '6LfqmGUbAAAAAMF_JqeHJnWyWTSuLM7b7Ao4rt5f'; public function run() { $this->processInput(); if ($this->hasErrors()) { $this->goBack(); } $this->processSpamFilter(); if ($this->hasErrors()) { $this->goBack(); } $this->sendEmail(); if ($this->hasErrors()) { $this->goBack(); } $this->sendCopy(); //echo sprintf("Success!!! Email sent to %s\n", $this->getContactEmail()); $this->goFinish(); } protected function getAbsoluteUrl($url) { $parsed_url = is_array($url) ? $url : parse_url($url); $scheme = !empty($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : 'http://'; $host = !empty($parsed_url['host']) ? $parsed_url['host'] : (!empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'); $port = !empty($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; $user = !empty($parsed_url['user']) ? $parsed_url['user'] : ''; $pass = !empty($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; $pass = (!empty($user) || !empty($pass)) ? "$pass@" : ''; $path = !empty($parsed_url['path']) ? $parsed_url['path'] : ''; $query = (!empty($parsed_url['query']) && strlen($parsed_url['query']) > 0) ? '?' . $parsed_url['query'] : ''; $fragment = !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; return "$scheme$user$pass$host$port$path$query$fragment"; } protected function getEmail(): string { return trim(strip_tags(strval($_POST['email']))); } protected function getSubject(): string { return trim(strip_tags(strval($_POST['subject']))); } protected function getMessage(): string { return trim(strip_tags(strval($_POST['message']))); } protected function getReference(): int { return intval(trim(strip_tags(strval($_POST['reference'])))); } protected function getSendCopy(): int { return intval(trim(strip_tags(strval($_POST['sendcopy'])))); } protected function hasEmail(): bool { return !!filter_var($this->getEmail(), FILTER_VALIDATE_EMAIL); } protected function hasSubject(): bool { return strlen($this->getSubject()) > 0; } protected function hasMessage(): bool { return strlen($this->getMessage()) > 0; } protected function hasReference(): bool { return $this->getReference() > 0; } protected function getInputs(): array { return array( 'email' => $this->getEmail(), 'subject' => $this->getSubject(), 'message' => $this->getMessage(), 'sendcopy' => $this->getSendCopy() ); } protected function processInput() { if (!$this->hasEmail()) { $this->errors['INVALID_EMAIL'] = 'Please enter a valid email address'; } if (!$this->hasSubject()) { $this->errors['SUBJECT_REQUIRED'] = 'Please enter a subject title'; } if (!$this->hasMessage()) { $this->errors['MESSAGE_REQUIRED'] = 'Please enter your message'; } } protected function processSpamFilter() { $captcha = trim(strip_tags(strval($_POST['g-recaptcha-response']))); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify', CURLOPT_POST => true, CURLOPT_POSTFIELDS => [ 'secret' => $this->googleSecretKey, 'response' => $captcha, 'remoteip' => $_SERVER['REMOTE_ADDR'] ], CURLOPT_RETURNTRANSFER => true ]); $output = curl_exec($ch); curl_close($ch); $result = json_decode($output); if (!isset($result->success) || $result->success !== true || !isset($result->score) || floatval($result->score) < 0.5) { $this->errors['SEND_FAILED'] = 'The details entered into this form have been flagged as spam. Please reword your message.'; return; } } protected function getContact() { $src = $this->getAbsoluteUrl('/staff-json/staff-email/index.json'); //$src = $this->getAbsoluteUrl('/media/csumb/site-assets/php-form/staff-json.json'); $json = file_get_contents($src); $data = json_decode($json, true); foreach ($data['staff'] as $contact) { if (!empty($contact['contentID']) && $contact['contentID'] === strval($this->getReference())) { return $contact; break; } } return null; } protected function getContactEmail(): string { $contact = $this->getContact(); if (!is_array($contact)) return ''; return trim(strval($contact['email'])); } protected function getContactName(): string { $contact = $this->getContact(); if (!is_array($contact)) return ''; return trim(strval($contact['name'])); } protected function sendEmail() { $to = $this->getContactEmail(); if (strlen($to) < 1) { $this->errors['SEND_FAILED'] = 'Sorry, this member is not contactable at present.'; return; } $message = sprintf('Message from %1$s:%2$s%2$s%3$s', $this->getEmail(), PHP_EOL, $this->getMessage()); $success = mail($to, 'DIRECTORY: ' . $this->getSubject(), $message, array( 'From' => 'noreply@csumb.edu', 'Reply-To' => $this->getEmail() )); /* echo sprintf('%s = mail(%s, %s, %s, array( \'From\' => %s ));', (($success) ? 'true' : 'false'), json_encode($to), json_encode($this->getSubject()), json_encode($message), json_encode('noreply@csumb.edu')); */ if (!$success) { $this->errors['SEND_FAILED'] = error_get_last(); } } protected function sendCopy() { if ($this->getSendCopy() !== 1) return; $name = $this->getContactName(); $name = (strlen($name) > 0) ? $name : 'unnamed member'; $message = sprintf('A copy of your message to %1$s is shown below:%2$s%2$s%3$s', $name, PHP_EOL, $this->getMessage()); $success = mail($this->getEmail(), $this->getSubject(), $message, array( 'From' => 'noreply@csumb.edu' )); } protected function goBack() { if (isset($_SERVER['HTTP_REFERER'])) { $state = array( 'inputs' => $this->getInputs(), 'errors' => $this->errors ); $url = $_SERVER['HTTP_REFERER']; parse_str(parse_url($url, PHP_URL_QUERY), $query); $query = (is_array($query)) ? $query : array(); $query['state'] = $this->encrypt(json_encode($state)); $parts = explode('?', $url); $url = reset($parts); $url = implode('?', array($url, implode('#', array(http_build_query($query), 'contact-form')))); //var_dump(json_decode($this->decrypt($query['state']), true)); } else { $url = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/'; } $this->redirect($url); } protected function redirect($url) { @header(sprintf('Location: %s', $url)); //var_dump($url); exit(); } protected function goFinish() { $path = '/directory/thank-you/'; $this->redirect((!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $path); } } $processor = new DirectoryContactFormProcessor(); $processor->run();