diff --git a/docs/TODO.md b/docs/TODO.md index fc6c9d3..cc75ff2 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -45,9 +45,9 @@ [ ] → Rate Limiting - [ ] → PHP Mailer / Access Token + [?] → PHP Mailer / Access Token - [ ] → Twilio Support + [x] → Twilio Support [x] → Logger diff --git a/src/bootstrap/main.php b/src/bootstrap/main.php index e06a4c9..531459c 100644 --- a/src/bootstrap/main.php +++ b/src/bootstrap/main.php @@ -17,10 +17,6 @@ if (!function_exists('str_contains')) { } } -unset($_REQUEST); // Request is dangerious as order of Vars is not Known -unset($_GET); // Super Globals are not Filtered, so unset them -unset($_POST); - $mem_baseline = memory_get_usage(); require_once CodeHydrater_FRAMEWORK . 'bootstrap/errors.php'; diff --git a/src/classes/app.php b/src/classes/app.php index 5371170..c38d27f 100644 --- a/src/classes/app.php +++ b/src/classes/app.php @@ -25,6 +25,19 @@ class app { private $class; private $method; private $params; + + private function get_without_file_extension(string $route): string { + $dot_position = strpos($route, '.'); + if ($dot_position !== false) { + $offset = strlen($route) - $dot_position; + $ext = \CodeHydrater\bootstrap\common::get_string_right($route, $offset); + if ($ext === ".php") { + $this->local404(); + } + return substr($route, 0, $dot_position); + } + return $route; // No dot found, return full string + } private function get_first_chunks(string $input): string { $parts = explode('/', $input); @@ -43,13 +56,13 @@ class app { */ public function __construct() { $full_route = bootstrap\site_helper::get_route(); - $no_hmtl = str_replace(".html", "", $full_route); + $no_file_ext = $this->get_without_file_extension($full_route); // Find the Route - $route = $this->get_first_chunks($no_hmtl); + $route = $this->get_first_chunks($no_file_ext); // Find the Method - $the_method = $this->get_last_part($no_hmtl); + $the_method = $this->get_last_part($no_file_ext); $params = bootstrap\site_helper::get_params(); @@ -168,7 +181,7 @@ class app { if ($method === "error" && str_contains($class, "app") && method_exists($controller, $method) === false ) { - CodeHydrater_broken_error(); + \CodeHydrater\bootstrap\broken_error(); return false; } diff --git a/src/classes/misc.php b/src/classes/misc.php index 8c45134..7b13fc9 100644 --- a/src/classes/misc.php +++ b/src/classes/misc.php @@ -280,10 +280,19 @@ final class misc { } elseif (is_string($vars) && !empty($vars)) { $vars = self::safe_url($vars); } - + + $dot_position = strpos($method, '.'); + if ($dot_position !== false) { + $offset = strlen($method) - $dot_position; + $ext = \CodeHydrater\bootstrap\common::get_string_right($method, $offset); + $method = \CodeHydrater\bootstrap\common::get_string_left($method, $dot_position); + } else { + $ext = ".html"; + } + if (bootstrap\configure::get('CodeHydrater', 'short_url') === true) { $vars = (!empty($vars)) ? "?{$vars}" : ''; - return rtrim(PROJECT_BASE_REF, "/") . "/{$route}/{$method}.html{$vars}"; + return rtrim(PROJECT_BASE_REF, "/") . "/{$route}/{$method}{$ext}{$vars}"; } else { $vars = (!empty($vars)) ? "&{$vars}" : ''; return rtrim(PROJECT_BASE_REF, "/") . "?route={$route}&m={$method}{$vars}"; diff --git a/src/classes/services/emailer.php b/src/classes/services/emailer.php new file mode 100644 index 0000000..4200edf --- /dev/null +++ b/src/classes/services/emailer.php @@ -0,0 +1,239 @@ +array(addresses, address, name) + * @param array $options (to, cc, bcc, from, subject, email, type['html', or 'text']) + * @return bool + */ + function send_email(array $options): bool { + + if (!is_array($options)) { + return false; + } + + if (! Reg::get('loader')->is_loaded('PHPMailer\PHPMailer')) { + Reg::get('loader')->add_namespace('PHPMailer\PHPMailer', CodeHydrater_PROJECT . 'vendor/phpmailer/phpmailer/src'); + } + + $mail = new \PHPMailer\PHPMailer\PHPMailer(); + + $message = ''; + $subject = ''; + $to = ''; + $from = ''; + $from_name = ''; + $reply = ''; + $reply_name = ''; + $content_type = ''; + $cc = ''; + $cc_name = ''; + $bcc = ''; + $bcc_name = ''; + + foreach ($options as $key => $value) { + + $default = (isset($value[0])) ? $value[0] : null; + + switch (\CodeHydrater\bootstrap\common::string_to_lowercase($key)) { + case 'to': + if (is_array($value)) { + $mto = (isset($value['addresses']) && is_array($value['addresses'])) ? $value['addresses'] : false; + $to = (isset($value['address'])) ? $value['address'] : $default; + $to_name = (isset($value['name'])) ? $value['name'] : false; + if ($mto !== false) { + foreach ($mto as $name => $address) { + $mail->addAddress($address, $name); + $to .= $address . "({$name});"; + } + } elseif ($to_name === false) { + $mail->addAddress($to); + } else { + $mail->addAddress($to, $to_name); + } + } else { + $to = $value; + $mail->addAddress($to); // Add a recipient + } + break; + case 'cc': + if (is_array($value)) { + $mcc = (isset($value['addresses']) && is_array($value['addresses'])) ? $value['addresses'] : false; + $cc = (isset($value['address'])) ? $value['address'] : $default; + $cc_name = (isset($value['name'])) ? $value['name'] : false; + if ($mcc !== false) { + foreach ($mcc as $name => $address) { + $mail->addCC($address, $name); + } + } elseif ($cc_name === false) { + $mail->addCC($cc); + } else { + $mail->addCC($cc, $cc_name); + } + } else { + $mail->addCC($value); + } + break; + case 'bcc': + if (is_array($value)) { + $mbcc = (isset($value['addresses']) && is_array($value['addresses'])) ? $value['addresses'] : false; + $bcc = (isset($value['address'])) ? $value['address'] : $default; + $bcc_name = (isset($value['name'])) ? $value['name'] : false; + if ($mbcc !== false) { + foreach ($mbcc as $name => $address) { + $mail->addBBC($address, $name); + } + } elseif ($bbc_name === false) { + $mail->addBCC($bcc); + } else { + $mail->addBCC($bcc, $bcc_name); + } + } else { + $mail->addBCC($value); + } + break; + case 'from': + if (is_array($value)) { + $from = (isset($value['address'])) ? $value['address'] : $default; + $from_name = (isset($value['name'])) ? $value['name'] : false; + if ($from_name === false) { + $mail->setFrom($from); + } else { + $mail->setFrom($from, $from_name); + } + } else { + $from = $value; + $mail->setFrom($from); + } + break; + case 'subject': + $subject = $value; + $mail->Subject = $subject; + break; + case 'email': + case 'message': + $message = $value; + $mail->Body = $message; + $mail->AltBody = strip_tags($message); + break; + case 'reply to': + case 'reply': + if (is_array($value)) { + $mreply = (isset($value['addresses']) && is_array($value['addresses'])) ? $value['addresses'] : false; + $reply = (isset($value['address'])) ? $value['address'] : $default; + $reply_name = (isset($value['name'])) ? $value['name'] : false; + if ($mreply !== false) { + foreach ($mreply as $name => $address) { + $mail->addReplyTo($address, $name); + } + } elseif ($reply_name === false) { + $mail->addReplyTo($reply); + } else { + $mail->addReplyTo($reply, $reply_name); + } + } else { + $mail->addReplyTo($value); + } + break; + case 'bounce': + case 'bounce address': + case 'bounce backs': + $bounce = $value; + break; + case 'attachment': + case 'attach': + if (is_array($value)) { + $attachment = (isset($value['file'])) ? $value['file'] : $default; + $file_name = (isset($value['name'])) ? $value['name'] : false; + if ($file_name === false) { + $mail->addAttachment($attachment); + } else { + $mail->addAttachment($attachment, $file_name); + } + } else { + $mail->addAttachment($value); + } + break; + case 'type': + case 'content type': + $content_type = $value; + break; + } + } + + if (empty($to) || empty($from) || empty($subject) || empty($message)) { + return false; + } + + switch ($content_type) { + case 'html': + $mail->isHTML(true); // Set email format to HTML + break; + case 'text': + default: + $mail->isHTML(false); // Set email format to Plain Text + break; + } + + $settings = Reg::get('email'); + + if (isset($settings['send_emails']) && $settings['send_emails'] === false) { + try { + $log = Regy::get('di')->get_service('log', ['emails']); + $log->write("To: {$to} Subject: {$subject} Message: {$message}"); + } catch (Exception $e) { + + } + return false; + } + + if (isset($settings['host']) && !empty($settings['host'])) { + if (isset($settings['smtp_debug'])) { + $mail->SMTPDebug = $settings['smtp_debug']; // Enable verbose debug output + } + + $mail->isSMTP(); // Set mailer to use SMTP + $mail->Host = $settings['host']; // Specify main and backup SMTP servers + $auth = ( isset($settings['username']) && !empty($settings['username']) && + isset($settings['password']) && !empty($settings['password']) && + (!isset($settings['auth']) || $settings['auth'] === true) + ) ? true : false; // Enable SMTP authentication + if ($auth === true) { + $mail->SMTPAuth = true; + $mail->Username = $settings['username']; // SMTP username + $mail->Password = $settings['password']; // SMTP password + $mail->SMTPSecure = (isset($settings['secure'])) ? $settings['secure'] : 'tls'; // Enable TLS encryption, `ssl` also accepted + } + $mail->Port = (isset($settings['port'])) ? $settings['port'] : 587; // TCP port to connect to + } else { + $mail->isMail(); // Use SendMail + } + + if ($mail->send()) { + return true; // Yes, it worked! + } else { + try { + $log = Reg::get('di')->get_service('log', ['failed_emails']); + $log->write('Mailer Error: ' . $mail->ErrorInfo); + } catch (Exception $e) { + + } + error_log('Mailer Error: ' . $mail->ErrorInfo); + return false; + } + } + +}