<?php
/**
 * @copyright (c) Kufer Software GmbH, Oderstraße 7, D-84453 Mühldorf
 */


class smtpMail
{
  public $mailserver;
  public $mailaccount;
  public $mailpassword;
  public $from;
  public $to;
  public $toname;
  public $reply;
  public $replyname;
  public $subject;
  public $body;
  public $auth;
  public $debug;
  public $contenttype;
  public $content_encoding;
  public $useragent;
  public $mime_version;
  public $sendername;
  public $usephpmail;
  public $useDmz;
  public $usephpmailer;
  public $phpmailerVerschluesselung;
  public $phpmailerNoAutoTLS;
  
  public $bcc;
  
  public $boundary;
  public $outerBoundary;
  public $_multipart = true;
  public $encodeEHLO; 
  public $doNotSplitHTMLBody;
  public $attachments = array();

  
  private $_attachType = '';

  public function __construct(bool $pUseDefaultValues = true)
  {
    $this->clear();
    if ($pUseDefaultValues) {
      $allgsettings = AllgSettingsAPI::getAllgSettings();
      $this->sendername = $allgsettings->smtpabsendername !== '' ? $allgsettings->smtpabsendername : $allgsettings->vhsname;
      $this->from = $allgsettings->smtpadresse;
      $this->mailserver = $allgsettings->smtpserver;
      $this->mailaccount = $allgsettings->smtpuser;
      $this->mailpassword = $allgsettings->smtppass;
      $this->usephpmail = $allgsettings->usephpmail;
      $this->content_encoding = $allgsettings->content_encoding;
      $this->useDmz = $allgsettings->emailViaDmz;
      $this->usephpmailer = $allgsettings->usephpmailer;
      $this->phpmailerVerschluesselung = $allgsettings->phpmailerVerschluesselung;
      $this->phpmailerNoAutoTLS = $allgsettings->phpmailerNoAutoTLS;
      $this->replyname = $allgsettings->replyname;
      $this->reply = $allgsettings->reply;
    }
  }

  public function clear(): void
  {
    $this->mailserver = '';
    $this->mailaccount = '';
    $this->mailpassword = '';
    $this->from = '';
    $this->to = '';
    $this->reply = '';
    $this->replyname = '';
    $this->subject = '';
    $this->body = '';
    $this->auth = true;
    $this->debug = LOG_EMAIL;
    $this->contenttype = SMTP_CONTENTTYPE_TEXT;
    $allgsettings = AllgSettingsAPI::getAllgSettings();
    $this->content_encoding = ($allgsettings->content_encoding != '' ? $allgsettings->content_encoding : '7bit');
    $this->encodeEHLO = !$allgsettings->dont64Encode;
    $this->useragent = KUFERTOOLS . '/' . KUFERWEB . ' SMTP Engine 1.10';
    $this->mime_version = '1.0';
    $this->usephpmail = false;
    $this->bcc = '';
    $random_hash = md5(date('r', time()));
    $this->boundary = '----=Inner_Part_' . $random_hash;
    $this->outerBoundary = '----=_NextPart_' . $random_hash;
    $this->doNotSplitHTMLBody = false;
    $this->attachments = array();
    $this->_attachType = '';
  }

  
  public function setAttachType(string $pAttachType): void
  {
    $this->_attachType = $pAttachType;
  }

  public function hasAttachments(): bool
  {
    if (count($this->attachments) > 0) {
      return true;
    } else {
      return false;
    }
  }

  
  public function addAttachment($pFilename): void
  {
    if ($pFilename == '') {
      return;
    }
    if (!is_array($this->attachments)) {
      $this->attachments = array();
    }
    $this->attachments[] = $pFilename;
  }

  public function SetVar2Value($name, $value): void
  {
    $this->{$name} = $value;
  }

  public function generateMessageID(): string
  {
    $tempstr = microtime();
    $temparray = explode(' ', $tempstr);

    $idright = $this->mailserver;
    if ($idright == '') {
      $idright = $_SERVER['SERVER_ADDR'];
    }

    return '<' . date('YmdHis') . '.' . StringOperations::substr($temparray[0], 2) . '@' . $idright . '>';
  }

  
  public function getMainContentType(): string
  {
    if ($this->_multipart) {
      return 'Content-Type: multipart/alternative; boundary="' . $this->boundary . '"';
    } else {
      return 'Content-Type: ' . $this->contenttype;
    }
  }

  
  public function _prepareHeader(): array
  {
    $sender = MailFactory::getSender($this);

    $retour = array();
    $retour[] = 'Date: ' . date('r');
    if (!$this->usephpmail) {
      $retour[] = 'User-Agent: ' . $this->useragent;
    }

    $retour[] = 'Message-ID: ' . $this->generateMessageID();
    if (!$this->usephpmail) {
      if ($this->toname == '') {
        $retour[] = 'To: ' . $this->to;
      } else {
        $retour[] = 'To: "' . $sender->encodeForMailHeader(StringOperations::html_entity_decode($this->toname)) . '" <' . $this->to . '>';
      }
    }
    if ($this->sendername == '') {
      $retour[] = 'From: ' . $this->from;
    } else {
      $retour[] = 'From: "' . $sender->encodeForMailHeader(StringOperations::html_entity_decode($this->sendername)) . '" <' . $this->from . '>';
    }

    if ($this->reply !== '') {
      if ($this->replyname == '') {
        $retour[] = 'Reply-To: ' . $this->reply;
      } else {
        $retour[] = 'Reply-To: "' . $sender->encodeForMailHeader(\StringOperations::html_entity_decode($this->replyname)) . '" <' . $this->reply . '>';
      }
    }

    if ($this->hasAttachments()) {
      $retour[] = 'Content-Type: multipart/mixed; boundary="' . $this->outerBoundary . '"';
    } else {
      $retour[] = $this->getMainContentType();
    }
    $retour[] = 'MIME-Version: ' . $this->mime_version;

    return $retour;
  }

  public function _prepareBody(string $contentPlain, string $contentHtml): string
  {
    if (trim($contentPlain) !== '' && trim($contentHtml) !== '') {
      return $this->_prepareBodyMultiPart($contentPlain, $contentHtml);
    } elseif (trim($contentPlain) !== '') {
      return $this->_prepareBodySinglePart($contentPlain);
    } else {
      return $this->_prepareBodySinglePart($contentHtml);
    }
  }

  private function _prepareBodySinglePart(string $content): string
  {
    $eol = RFC_SMTP_LINEBREAK;
    $content = preg_replace('/^\\./', '..', $content);

    $body = '';

    if ($this->hasAttachments()) {
      $body .= $eol . '--' . $this->outerBoundary . $eol;
      $body .= 'Content-Type: ' . $this->contenttype . $eol;
    }

    $body .= $eol;
    $body .= $content . $eol . $eol;

    if ($this->hasAttachments()) {
      if ($this->_attachType == '' || $this->_attachType === 'file') {
        $body = $this->setAttachmentFilesToBody($body, $eol);
      } else {
        $body = $this->setAttachmentFileToBody($body, $eol);
      }
    }

    return $body;
  }

  private function _prepareBodyMultiPart(string $contentPlain, string $contentHtml): string
  {
    $eol = RFC_SMTP_LINEBREAK;
    $contentPlain = preg_replace('/^\\./', '..', $contentPlain);

    $body = '';

    if ($this->hasAttachments()) {
      $body .= $eol . '--' . $this->outerBoundary . $eol;
      $body .= 'Content-Type: multipart/alternative; boundary="' . $this->boundary . '"' . $eol;
    }

    $body .= $eol;
    $body .= '--' . $this->boundary . $eol;
    $body .= 'Content-Type: ' . SMTP_CONTENTTYPE_TEXT . $eol;
    $body .= 'Content-Transfer-Encoding: ' . $this->content_encoding . $eol . $eol;
    $body .= $contentPlain . $eol . $eol;

    $contentHtml = preg_replace('/^\\./', '..', $contentHtml);
    $body .= '--' . $this->boundary . $eol;
    $body .= 'Content-Type: ' . SMTP_CONTENTTYPE_HTML . $eol;
    $body .= 'Content-Transfer-Encoding: ' . $this->content_encoding . $eol . $eol;
    $body .= $contentHtml . $eol . $eol;
    $body .= '--' . $this->boundary . '--' . $eol;

    if ($this->hasAttachments()) {
      if ($this->_attachType == '' || $this->_attachType === 'file') {
        $body = $this->setAttachmentFilesToBody($body, $eol);
      } else {
        $body = $this->setAttachmentFileToBody($body, $eol);
      }
    }

    return $body;
  }

  protected function setAttachmentFilesToBody(string $pBody, string $pEol): string
  {
    foreach ($this->attachments as $file) {
      if (is_a($file, 'EMailAttachment')) {
        $name = $file->filename;
        $content = $file->content;
        $contentType = 'application/octetstream';
        if (isset($file->contentType)) {
          $contentType = $file->contentType;
        }

        $pBody .= '--' . $this->outerBoundary . $pEol;
        $pBody .= 'Content-Type: ' . $contentType . '; name="' . $name . '"' . $pEol;
        $pBody .= 'Content-Transfer-Encoding: base64' . $pEol;
        $pBody .= 'Content-Disposition: attachment; filename="' . $name . '"' . $pEol . $pEol;
        $pBody .= chunk_split(base64_encode($content));
      } else {
        if (is_array($file)) {
          
          
          $name = $file[1];
          $file = $file[0];
        } else {
          $name = basename($file);
        }

        if (file_exists($file) && is_readable($file)) {
          $pBody .= '--' . $this->outerBoundary . $pEol;
        }

        $filehandle = fopen($file, 'r');
        $dateiinhalt = fread($filehandle, filesize($file));

        $pBody .= 'Content-Type: application/octetstream; name="' . basename($file) . '"' . $pEol;
        $pBody .= 'Content-Transfer-Encoding: base64' . $pEol;
        $pBody .= 'Content-Disposition: attachment; filename="' . $name . '"' . $pEol . $pEol;
        $pBody .= chunk_split(base64_encode($dateiinhalt));
        fclose($filehandle);
      }
    }

    $pBody .= '--' . $this->outerBoundary . '--' . $pEol;

    return $pBody;
  }

  protected function setAttachmentFileToBody(string $pBody, string $pEol): string
  {
    switch ($this->_attachType) {
      default:
      case 'text':
        $genFilename = 'Anhang.txt';
        $contentType = 'text/plain';
        break;
      case 'pdf':
        $genFilename = 'Anhang.pdf';
        $contentType = 'application/pdf';
        break;
      case 'xml':
        $genFilename = 'Anhang' . date('Ymd_Hi', time()) . '.xml';
        $contentType = 'text/xml';
        break;
        
    }
    $pBody .= '--' . $this->outerBoundary . $pEol;
    $pBody .= 'Content-Type: ' . $contentType . '; name=' . $genFilename . $pEol;
    $pBody .= 'Content-Transfer-Encoding: base64' . $pEol;
    $pBody .= 'Content-Disposition: attachment; ' . $pEol . $pEol;
    $dateiinhalt = $this->attachments[0];
    $pBody .= chunk_split(base64_encode($dateiinhalt));
    $pBody .= '--' . $this->outerBoundary . '--' . $pEol;

    return $pBody;
  }

  
  public function sendMail(string $pPlain = '', string $pHtml = ''): bool
  {
    $result = false;
    if ($this->debug) {
      $this->_debug();
    }
    if ($pPlain != '' && $pHtml != '') {
      $this->contenttype = SMTP_CONTENTTYPE_HTML;
      $result = $this->sendMultipartMessage($pPlain . $this->_getFooter(), $pHtml . $this->_getFooter(true));
    } elseif ($this->body == '' && $pPlain == '' && $pHtml == '') {
      Logger::logg('Mail-Fehler: keinerlei Body angegeben', 100);
    } elseif ($this->body != '' && StringOperations::strpos($this->contenttype, 'text/html') !== false) {
      
      
      if (!$this->doNotSplitHTMLBody) {
        $txtContent = StringOperations::html2Txt($this->body);
        $result = $this->sendMultipartMessage($txtContent . $this->_getFooter(), $this->body . $this->_getFooter(true));
      } else {
        $result = $this->sendSingleContentTypeMessage($this->body . $this->_getFooter(true));
      }
    } else {
      if ($this->body != '') {
        $result = $this->sendSingleContentTypeMessage($this->body . $this->_getFooter());
      } elseif ($pPlain != '') {
        $this->contenttype = SMTP_CONTENTTYPE_TEXT;
        $result = $this->sendSingleContentTypeMessage($pPlain . $this->_getFooter());
      } elseif ($pHtml != '') {
        $this->contenttype = SMTP_CONTENTTYPE_HTML;
        $result = $this->sendSingleContentTypeMessage($pHtml . $this->_getFooter(true));
      }
    }
    if ($result) {
      if (!empty($this->toname)) {
        Logger::logg('Eine E-Mail wurde erfolgreich an ' . $this->toname . ' <' . $this->to . '> versendet', 11);
      } else {
        Logger::logg('Eine E-Mail wurde erfolgreich an ' . $this->to . ' versendet', 11);
      }
    }

    return $result;
  }

  
  protected function _getFooter(bool $pAsHtml = false): string
  {
    return '';
  }

  
  public function sendMultipartMessage(string $contentPlain, string $contentHtml): bool
  {
    $this->_multipart = true;
    if ($this->to == '') {
      Logger::logg('Mail-Fehler: kein to angegeben', 20);

      return false;
    }
    if ($contentPlain == '' || $contentHtml == '') {
      Logger::logg('Mail-Fehler: Multipart-Message: nicht beide Bodies angegeben', 20);

      return false;
    }
    $headers = $this->_prepareHeader();

    $contentHtml = static::trimHtmlContent($contentHtml);

    $contentPlain = static::replaceLineBreaksWithSMTPLineBreaks($contentPlain);
    $contentHtml = static::replaceLineBreaksWithSMTPLineBreaks($contentHtml);

    $sender = MailFactory::getSender($this);

    return $sender->send($headers, $contentPlain, $contentHtml);
  }

  
  public function sendSingleContentTypeMessage(string $content): bool
  {
    $this->_multipart = false;
    if ($this->to == '') {
      Logger::logg('Mail-Fehler: kein to angegeben', 100);

      return false;
    }
    if ($content == '') {
      Logger::logg('Mail-Fehler: Body nicht angegeben', 100);

      return false;
    }
    $headers = $this->_prepareHeader();
    
    $headers[] = 'Content-Transfer-Encoding: ' . $this->content_encoding;

    $sender = MailFactory::getSender($this);

    if (StringOperations::strpos($this->contenttype, 'text/html') !== false) {
      $content = static::trimHtmlContent($content);
    }

    $content = static::replaceLineBreaksWithSMTPLineBreaks($content);

    if (StringOperations::strpos($this->contenttype, 'text/html') !== false) {
      return $sender->send($headers, '', $content);
    } else {
      return $sender->send($headers, $content, '');
    }
  }

  
  private static function trimHtmlContent(string $content): string
  {
    return (new \Kufer\Web\Classes\Stream\ArrayStream(explode("\n", $content)))
      ->map(static function(string $line): string {
        if (strlen($line) > 300) { 
          
          
          

          $count = 0;

          $line = preg_replace('/(.)>(.)/', "$1>\n$2", $line, -1, $count);
          if ($count > 0) {
            return static::trimHtmlContent($line);
          }

          $line = preg_replace('&(.)</&', "$1\n</", $line, -1, $count);
          if ($count > 0) {
            return static::trimHtmlContent($line);
          }

          $line = preg_replace('/(.)<(.)/', "$1\n<$2", $line, -1, $count);
          if ($count > 0) {
            return static::trimHtmlContent($line);
          }

          return trim($line); 
        }

        return $line;
      })
      ->implode("\n");
  }

  public function _debug(string $sonstiges = ''): void
  {
    $string = '|$this->from = ' . $this->from;
    $string .= '|$this->sendername = ' . StringOperations::html_entity_decode($this->sendername);
    $string .= '|$this->to = ' . $this->to;
    $string .= '|$this->reply = ' . $this->reply;
    $string .= '|$this->replyname = ' . $this->replyname;
    $string .= '|$this->bcc = ' . $this->bcc;
    $string .= '|$this->subject = ' . $this->subject;
    $string .= '|$this->mailserver = ' . $this->mailserver;
    $string .= '|$this->mailaccount = ' . $this->mailaccount;
    $string .= '|$this->mailpassword = ' . $this->mailpassword;
    $string .= '|$this->contenttype = ' . $this->contenttype;
    $string .= '|$this->content_encoding = ' . $this->content_encoding;
    $string .= '|$this->useragent = ' . $this->useragent;
    $string .= '|$this->mime_version = ' . $this->mime_version;
    $string .= '|$this->boundary = ' . $this->boundary;
    $string .= '|$this->encodeEHLO = ' . $this->encodeEHLO;
    $string .= '|$this->usephpmail = ' . $this->usephpmail;
    $string .= '|$this->doNotSplitHTMLBody = ' . $this->doNotSplitHTMLBody;
    $string .= '|sonstiges: ' . $sonstiges;
    $string .= '|attachments' . print_r($this->attachments, true);
    $string .= '|attachtype' . print_r($this->_attachType, true);
    Logger::logg($string, 500);
  }

  
  public static function sendStandardTextMail(
    string $pTo,
    string $pSubject,
    string $pBody,
    ?string $pFrom = null,
    ?string $pSendername = null,
    ?string $pReply = null,
    ?string $pReplyName = null
  ): bool {
    $email = new smtpMail();
    if (!is_null($pFrom)) {
      $email->SetVar2Value('from', $pFrom);
    }
    if (!is_null($pSendername)) {
      $email->SetVar2Value('sendername', $pSendername);
    }
    if (!is_null($pReply)) {
      $email->SetVar2Value('reply', $pReply);
    }
    if (!is_null($pReplyName)) {
      $email->SetVar2Value('replyname', $pReplyName);
    }
    $email->SetVar2Value('to', $pTo);
    $email->SetVar2Value('contenttype', SMTP_CONTENTTYPE_TEXT);
    $email->SetVar2Value('doNotSplitHTMLBody', true);
    $email->subject = str_replace("\n", ' ', $pSubject); 
    $email->body = $pBody;

    return $email->sendMail();
  }

  public static function sendStandardHtmlMail(string $pTo, string $pSubject, string $pBody): bool
  {
    $email = new \smtpMail();
    $email->SetVar2Value('to', $pTo);
    $email->SetVar2Value('subject', $pSubject);
    $email->SetVar2Value('contenttype', SMTP_CONTENTTYPE_HTML);
    $email->SetVar2Value('body', $pBody);

    return $email->sendMail();
  }

  public static function replaceLineBreaksWithSMTPLineBreaks(string $content): string
  {
    return preg_replace("/(?>\n\r|\n|\r)/", RFC_SMTP_LINEBREAK, $content);
  }
}


class smtpMailTools extends smtpMail
{
  
  private $_footer = '';

  public function __construct(bool $pUseDefaultValues = true)
  {
    parent::__construct($pUseDefaultValues);
  }

  
  protected function _getFooter(bool $pAsHtml = false): string
  {
    if ($pAsHtml) {
      return str_replace("\n", '<br>', $this->_footer);
    } else {
      return $this->_footer;
    }
  }

  
  public function clearFooter(): void
  {
    $this->_footer = '';
  }

  
  public function setFooter(string $pGrund = '', ?\Benutzer $pUser = null, $pRelatedObject = null): void
  {
    $footer = "\n\n" . '---------------' . "\n\n";
    $footer .= 'Diese Nachricht wurde aus den ' . KUFERTOOLS . ' versandt.';

    
    if (!is_null($pUser) && !$pUser->isEmpty() && StringOperations::strtolower(get_class($pUser)) == 'benutzer') {
      $footer .= "\n" . 'Absender: ';

      $benutzerGruppe = '';
      $nr = '';

      if ($pUser->dnr > 0) {
        $dbwDozenten = new DBWorkDozenten(1, true);
        $dozent = $dbwDozenten->getDozent($pUser->dnr);
        $typenVeranstalter = \Kufer\Web\KuferTools\Veranstalter\Utils\VeranstalterUtils::getTypenVeranstalter();
        if (in_array($dozent->typ, $typenVeranstalter)) {
          $benutzerGruppe = 'Veranstalter';
        } else {
          $benutzerGruppe = 'Dozent';
          if (\DBWorkBASysSettings::isRheinsieg()) {
            $benutzerGruppe = 'Kursleiter';
          }
        }

        $nr = ' (Nr. ' . $pUser->dnr . ')';
      } elseif ($pUser->ben >= 0) {
        $benutzerGruppe = 'Mitarbeiter';
      
      } elseif ($pUser->tnr >= 0) {
        $benutzerGruppe = 'Teilnehmer';
        $nr = ' (Nr. ' . $pUser->tnr . ')';
      } elseif ($pUser->anr >= 0) {
        $benutzerGruppe = 'Hausmeister';
        $nr = ' (Nr. ' . $pUser->anr . ')';
      }

      $anredetitel = $pUser->name_titel;
      if ($anredetitel != '') {
        $anredetitel .= ' ';
      }

      if (\DBWorkBASysSettings::isRheinsieg()) {
        $footer .= $benutzerGruppe . ' ' . $anredetitel . $pUser->vorname . ' ' . $pUser->name . $nr;
      } else {
        $footer .= $benutzerGruppe . ' ' . $anredetitel . $pUser->name . ' ' . $pUser->vorname . $nr;
      }
    }

    
    if ($pGrund != '') {
      $footer .= "\n" . 'Funktion: ' . $pGrund;
    }
    
    
    if (!is_null($pRelatedObject)) {
      switch (StringOperations::strtolower(get_class($pRelatedObject))) {
        case 'kurstag':
          $footer .= "\n" . 'betrifft: ';

          $footer .= 'Kurstag am ' . $pRelatedObject->datum;
          if ($pRelatedObject->zeit_beg != '') {
            $footer .= ' um ' . $pRelatedObject->zeit_beg;
          }
          if ($pRelatedObject->zeit_ende != '') {
            $footer .= ' bis ' . $pRelatedObject->zeit_ende;
          }
          if ($pRelatedObject->zeit_beg != '' || $pRelatedObject->zeit_ende != '') {
            $footer .= ' Uhr';
          }

          $footer .= ' des Kurses ' . $pRelatedObject->kurs->kurzbez . ' (' . $pRelatedObject->knr . ')';
          break;
        case 'kurs':
          $footer .= "\n" . 'betrifft: ';
          $footer .= 'Kurs ' . $pRelatedObject->kurzbez
          . ' (' . $pRelatedObject->id . ')';
          break;
      }
    }
    $this->_footer = $footer;
  }

  
  public static function sendStandardTextMailWithFooter(
    $pTo,
    $pSubject,
    $pBody,
    $pFooterGrund = '',
    $pFooterUser = null,
    $pFooterRelatedObject = null,
    $pFrom = null,
    $pSendername = null,
    $pReply = null,
    $pReplyName = null
  ) {
    $email = new smtpMailTools();
    if ($pFrom !== null) {
      $email->SetVar2Value('from', $pFrom);
    }
    if ($pSendername !== null) {
      $email->SetVar2Value('sendername', $pSendername);
    }
    if ($pReply !== null) {
      $email->SetVar2Value('reply', $pReply);
    }
    if ($pReplyName !== null) {
      $email->SetVar2Value('replyname', $pReplyName);
    }
    $email->SetVar2Value('to', $pTo);
    $email->SetVar2Value('contenttype', SMTP_CONTENTTYPE_TEXT);
    $email->SetVar2Value('doNotSplitHTMLBody', true);
    $email->subject = $pSubject;
    $email->body = $pBody;
    $email->setFooter($pFooterGrund, $pFooterUser, $pFooterRelatedObject);

    return $email->sendMail();
  }
}

class smtpLine
{
  public $command;
  public $replyCode;
  public $errorMessage;
  public $logMessage;

  public function __construct($com, $reply, $errormsg)
  {
    $this->command = $com;
    $this->replyCode = $reply;
    $this->errorMessage = $errormsg;
  }
}

class EMailAttachment
{
  public function __construct($filename = null, $content = null, $contentType = null)
  {
    $this->filename = $filename;
    $this->content = $content;
    $this->contentType = $contentType;
  }

  public $filename;
  public $content;
  public $contentType;
}