<?php namespace HashOver;

// Copyright (C) 2018-2019 Jacob Barkdull
// This file is part of HashOver.
//
// HashOver is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// HashOver is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with HashOver. If not, see <http://www.gnu.org/licenses/>.


class Sendmail
{
// Email data to send
protected $to = array ();
protected $from = array ();
protected $reply = array ();
protected $subject;
protected $text;

// Type of content being sent
protected $type = 'text';

// Sets who we're sending email to
public function to ($email, $name = null)
{
$this->to['email'] = $email;
$this->to['name'] = $name;
}

// Sets where recipient can reply to
public function replyTo ($email, $name = null)
{
$this->reply['email'] = $email;
$this->reply['name'] = $name;
}

// Sets who email is coming from
public function from ($email, $name = null)
{
// Set "from" email address and name
$this->from['email'] = $email;
$this->from['name'] = $name;

// Set "reply-to" the same way
if (empty ($this->reply['email'])) {
$this->replyTo ($email, $name);
}
}

// Sets subject line
public function subject ($text)
{
$this->subject = strip_tags ($text);
}

// Converts text to CRLF line ending format
protected function toCRLF ($text)
{
return preg_replace ('/\r\n|\r|\n/', "\r\n", $text);
}

// Converts message to plain text
protected function plainText ($text)
{
// Strip HTML tags
$text = strip_tags ($text);

// Convert to CRLF line ending format
$text = $this->toCRLF ($text);

// Convert HTML entities to normal characters
$text = html_entity_decode ($text, ENT_COMPAT, 'UTF-8');

// Encode text in quoted-printable format
$text = quoted_printable_encode ($text);

// And return text
return $text;
}

// Sets message body
public function text ($text)
{
// Set text property
$this->text = $this->plainText ($text);

// And set type to text
$this->type = 'text';
}

// Sets message body to HTML
public function html ($html)
{
// Convert to CRLF line ending format
$this->html = $this->toCRLF ($html);

// Encode HTML in quoted-printable format
$this->html = quoted_printable_encode ($this->html);

// Set automatic text version of message
if (empty ($this->text)) {
$this->text = $this->plainText ($html);
}

// And set type to HTML
$this->type = 'html';
}

// Sets message body
public function body ($text, $html = false)
{
// Set body as HTML if told to
if ($html === true) {
return $this->html ($text);
}

// Otherwise, set body as plain text
return $this->text ($text);
}

// Encodes given text as MIME "encoded word"
protected function encode ($text)
{
return mb_encode_mimeheader ($text);
}

// Converts to/from/reply-to to a formatted string
public function format (array $recipient)
{
// Check if a name was given
if (!empty ($recipient['name'])) {
// If so, encode name
$name = $this->encode ($recipient['name']);

// And construct email address in "name <email>" format
$address = $name . ' <' . $recipient['email'] . '>';
} else {
// If not, use email address as-is
$address = $recipient['email'];
}

// And return email address
return $address;
}

// Creates mail headers
protected function getHeaders ($boundary)
{
// Initial headers data
$data = array ();

// Add recipient headers
$data[] = 'MIME-Version: 1.0';

// Set From address if one is present
if (!empty ($this->from['email'])) {
$data[] = 'From: ' . $this->format ($this->from);
}

// Set Reply-To address if one is present
if (!empty ($this->reply['email'])) {
$data[] = 'Reply-To: ' . $this->format ($this->reply);
}

// Check if message type is text
if ($this->type === 'text') {
// If so, add plain text header
$data[] = 'Content-Type: text/plain; charset="UTF-8"';
$data[] = 'Content-Transfer-Encoding: quoted-printable';
} else {
// If not, add multipart header
$data[] = 'Content-Type: multipart/alternative; boundary="' . $boundary . '"';
}

// Convert headers data to string
$headers = implode ("\r\n", $data);

// And return headers as string
return $headers;
}

// Creates mail message
protected function getMessage ($boundary)
{
// Initial message data
$data = array ();

// Check if message type is text
if ($this->type === 'text') {
// If so, only add text version
$data[] = $this->text;
} else {
// If not, start multipart boundary
$data[] = '--' . $boundary;

// Add text version
$data[] = 'Content-Type: text/plain; charset="UTF-8"';
$data[] = 'Content-Transfer-Encoding: quoted-printable';
$data[] = '';
$data[] = $this->text;

// Add another multipart boundary
$data[] = '--' . $boundary;

// Add HTML version
$data[] = 'Content-Type: text/html; charset="UTF-8"';
$data[] = 'Content-Transfer-Encoding: quoted-printable';
$data[] = '';
$data[] = $this->html;

// And end multipart boundary
$data[] = '--' . $boundary . '--';
}

// Convert message data to string
$headers = implode ("\r\n", $data);

// And return message as string
return $headers;
}

// Sends an email
public function send ()
{
// Get email address we're sending email to
$to = $this->to['email'];

// Get quoted-printable encoded subject
$subject = $this->encode ($this->subject);

// Create unique boundary
$boundary = md5 (uniqid (time ()));

// Get message body
$message = $this->getMessage ($boundary);

// Get email headers
$headers = $this->getHeaders ($boundary);

// Check if a From email address is set
if (!empty ($this->from['email'])) {
// If so, set envelope sender address with -f option
$params = '-f ' . $this->from['email'];

// And actually send the email
mail ($to, $subject, $message, $headers, $params);
} else {
// If not, send email normally
mail ($to, $subject, $message, $headers);
}
}
}