golden hour
/home/doctorbruno/public_html/doctorbruno.info/taeionline/support/include
⬆️ Go Up
Upload
File/Folder
Size
Actions
.MANIFEST
194.97 KB
Del
OK
.htaccess
14 B
Del
OK
JSON.php
33.13 KB
Del
OK
PasswordHash.php
6.92 KB
Del
OK
Spyc.php
31.74 KB
Del
OK
UniversalClassLoader.php
8.61 KB
Del
OK
ajax.admin.php
7.29 KB
Del
OK
ajax.config.php
4.61 KB
Del
OK
ajax.content.php
9.76 KB
Del
OK
ajax.draft.php
13.03 KB
Del
OK
ajax.email.php
2.33 KB
Del
OK
ajax.export.php
1006 B
Del
OK
ajax.filter.php
874 B
Del
OK
ajax.forms.php
13.84 KB
Del
OK
ajax.i18n.php
5.1 KB
Del
OK
ajax.kbase.php
2.93 KB
Del
OK
ajax.note.php
2.05 KB
Del
OK
ajax.orgs.php
11.73 KB
Del
OK
ajax.plugins.php
2.15 KB
Del
OK
ajax.schedule.php
4.29 KB
Del
OK
ajax.search.php
12.72 KB
Del
OK
ajax.sequence.php
3.2 KB
Del
OK
ajax.staff.php
11.62 KB
Del
OK
ajax.tasks.php
33.87 KB
Del
OK
ajax.thread.php
8.81 KB
Del
OK
ajax.tickets.php
79.7 KB
Del
OK
ajax.tips.php
1.66 KB
Del
OK
ajax.upgrader.php
2.24 KB
Del
OK
ajax.users.php
18.33 KB
Del
OK
api.cron.php
1.06 KB
Del
OK
api.tickets.php
10.36 KB
Del
OK
class.2fa.php
7.76 KB
Del
OK
class.ajax.php
1.45 KB
Del
OK
class.api.php
15.31 KB
Del
OK
class.app.php
1.49 KB
Del
OK
class.attachment.php
6.95 KB
Del
OK
class.auth.php
50.73 KB
Del
OK
class.avatar.php
6.44 KB
Del
OK
class.banlist.php
2.58 KB
Del
OK
class.base32.php
4.07 KB
Del
OK
class.businesshours.php
7.08 KB
Del
OK
class.canned.php
9.05 KB
Del
OK
class.captcha.php
1.73 KB
Del
OK
class.category.php
11.14 KB
Del
OK
class.charset.php
3.41 KB
Del
OK
class.cli.php
9.48 KB
Del
OK
class.client.php
15.39 KB
Del
OK
class.collaborator.php
5.65 KB
Del
OK
class.company.php
2.65 KB
Del
OK
class.config.php
60.36 KB
Del
OK
class.controller.php
1.15 KB
Del
OK
class.cron.php
3.6 KB
Del
OK
class.crypto.php
18.92 KB
Del
OK
class.csrf.php
2.38 KB
Del
OK
class.dept.php
33.21 KB
Del
OK
class.dispatcher.php
7.12 KB
Del
OK
class.draft.php
6.23 KB
Del
OK
class.dynamic_forms.php
64.98 KB
Del
OK
class.email.php
49 KB
Del
OK
class.error.php
1.69 KB
Del
OK
class.export.php
32.92 KB
Del
OK
class.faq.php
15.05 KB
Del
OK
class.file.php
34.59 KB
Del
OK
class.filter.php
31.9 KB
Del
OK
class.filter_action.php
23.67 KB
Del
OK
class.format.php
44.3 KB
Del
OK
class.forms.php
195.58 KB
Del
OK
class.http.php
7.16 KB
Del
OK
class.i18n.php
24.28 KB
Del
OK
class.import.php
6.9 KB
Del
OK
class.json.php
2.71 KB
Del
OK
class.knowledgebase.php
5.8 KB
Del
OK
class.list.php
42 KB
Del
OK
class.lock.php
4.05 KB
Del
OK
class.log.php
1.55 KB
Del
OK
class.mail.php
35.76 KB
Del
OK
class.mailer.php
25.75 KB
Del
OK
class.mailfetch.php
9.72 KB
Del
OK
class.mailparse.php
30.99 KB
Del
OK
class.message.php
6.42 KB
Del
OK
class.migrater.php
5.2 KB
Del
OK
class.misc.php
7.5 KB
Del
OK
class.model.php
2.3 KB
Del
OK
class.nav.php
14.15 KB
Del
OK
class.note.php
2.39 KB
Del
OK
class.oauth2.php
4.04 KB
Del
OK
class.organization.php
22.45 KB
Del
OK
class.orm.php
120.9 KB
Del
OK
class.osticket.php
20.64 KB
Del
OK
class.ostsession.php
21.86 KB
Del
OK
class.page.php
10.59 KB
Del
OK
class.pagenate.php
5.53 KB
Del
OK
class.passwd.php
1.21 KB
Del
OK
class.pdf.php
3.83 KB
Del
OK
class.plugin.php
36.13 KB
Del
OK
class.priority.php
1.81 KB
Del
OK
class.queue.php
103.51 KB
Del
OK
class.report.php
11.76 KB
Del
OK
class.role.php
11.27 KB
Del
OK
class.schedule.php
46.28 KB
Del
OK
class.search.php
61.66 KB
Del
OK
class.sequence.php
7.27 KB
Del
OK
class.session.php
19.25 KB
Del
OK
class.setup.php
3.55 KB
Del
OK
class.signal.php
4.16 KB
Del
OK
class.sla.php
9.11 KB
Del
OK
class.staff.php
60.21 KB
Del
OK
class.task.php
56.21 KB
Del
OK
class.team.php
12.31 KB
Del
OK
class.template.php
23.45 KB
Del
OK
class.thread.php
108.96 KB
Del
OK
class.thread_actions.php
17.08 KB
Del
OK
class.ticket.php
167.39 KB
Del
OK
class.timezone.php
21.94 KB
Del
OK
class.topic.php
20.06 KB
Del
OK
class.translation.php
34.79 KB
Del
OK
class.upgrader.php
13.76 KB
Del
OK
class.user.php
43.4 KB
Del
OK
class.usersession.php
7.55 KB
Del
OK
class.util.php
10.07 KB
Del
OK
class.validator.php
12.94 KB
Del
OK
class.variable.php
11.93 KB
Del
OK
class.xml.php
3.23 KB
Del
OK
class.yaml.php
1.15 KB
Del
OK
cli
-
Del
OK
client
-
Del
OK
config
-
Del
OK
fpdf
-
Del
OK
htmLawed.php
53.53 KB
Del
OK
html2text.php
33.71 KB
Del
OK
i18n
-
Del
OK
index.php
37 B
Del
OK
laminas-mail
-
Del
OK
mpdf
-
Del
OK
mysqli.php
9.55 KB
Del
OK
ost-config.php
5.63 KB
Del
OK
ost-sampleconfig.php
6.24 KB
Del
OK
pear
-
Del
OK
plugins
-
Del
OK
staff
-
Del
OK
tnef_decoder.php
19.82 KB
Del
OK
upgrader
-
Del
OK
Edit: class.mail.php
<?php /** * class.mail.php * * osTicket Laminas/Mail Wrapper and Mail/Auth Utils & Helpers * * @author Peter Rotich <peter@osticket.com> * @copyright Copyright (c) osTicket <gpl@osticket.com> * */ // osTicket/Mail namespace namespace osTicket\Mail { use osTicket\OAuth2\AccessToken; // Exception as Mail\RuntimeException use Laminas\Mail; class Exception extends Mail\Exception\RuntimeException { } // Message use Laminas\Mail\Message as MailMessage; use Laminas\Mime\Message as MimeMessage; use Laminas\Mime\Mime; use Laminas\Mime\Part as MimePart; use Laminas\Mail\Header; use osTicket\Mail\Header\ReturnPath; class Message extends MailMessage { // Message Id (mid) private $mid; // MimeMessage Parts private $mimeParts = null; // MimeMessage Content private $mimeContent = null; // Default Charset protected $charset = 'utf-8'; // Default Encoding (upstream is ASCII) protected $encoding = 'utf-8'; // Internal flags used to set Content-Type private $hasHtml = false; private $hasAttachments = false; private $hasInlineImages = false; public function hasAttachments() { return $this->hasAttachments; } public function hasInlineImages() { return $this->hasInlineImages; } public function hasHtml() { return $this->hasHtml; } // Files either attached or inline public function hasFiles() { return ($this->hasAttachments() || $this->hasInlineImages()); } public function getId() { if (!isset($this->mid) && ($header=$this->getHeader('message-id'))) $this->mid = $header->getId(); return $this->mid; } public function getMimeMessageParts() { if (!isset($this->mimeParts)) $this->mimeParts = new MimeMessage(); return $this->mimeParts; } public function getMimeMessageContent() { if (!isset($this->mimeContent)) $this->mimeContent = new ContentMimeMessage(); return $this->mimeContent; } public function getHeader(string $name) { return $this->getHeaders()->getHeader($name); } public function addHeader($header, $value=null) { if (isset($value)) $this->getHeaders()->addHeaderLine($header, $value); else $this->getHeaders()->addHeader($header); } public function addHeaders(array $headers) { foreach ($headers as $k => $v) $this->addHeader($k, $v); } private function addMimePart(MimePart $part) { $this->getMimeMessageParts()->addPart($part); } private function addMimeContent(MimePart $part) { $this->getMimeMessageContent()->addPart($part); } public function setTextBody($text, $encoding=false) { $part = new MimePart($text); $part->type = Mime::TYPE_TEXT; $part->charset = $this->charset; $part->encoding = $encoding; $this->addMimeContent($part); } public function setHtmlBody($html, $encoding=false) { $part = new MimePart($html); $part->type = Mime::TYPE_HTML; $part->charset = $this->charset; $part->encoding = $encoding ?: Mime::ENCODING_BASE64; $this->addMimeContent($part); $this->hasHtml = true; } public function addInlineImage($id, $file) { $f = new MimePart($file->getData()); $f->id = $id; $f->type = sprintf('%s; name="%s"', $file->getMimeType(), $file->getName()); $f->filename = $file->getName(); $f->disposition = Mime::DISPOSITION_INLINE; $f->encoding = Mime::ENCODING_BASE64; $this->addMimePart($f); $this->hasInlineImages = true; } public function addAttachment($file, $name=null) { $f = new MimePart($file->getData()); $f->type = $file->getMimeType(); $f->filename = $name ?: $file->getName(); $f->disposition = Mime::DISPOSITION_ATTACHMENT; $f->encoding = Mime::ENCODING_BASE64; $this->addMimePart($f); $this->hasAttachments = true; } // Expects a valid date e.g date('r') public function setDate(string $date) { $d = new Header\Date($date); // Laminas auto adds Date upstream when any header is added // We're clearing it here to we back that-date up like it's // 99 & 2000 ~ Juvenile $this->getHeaders()->removeHeader('date'); $this->addHeader($d); } // Please use this method to set Message-Id otherwise it will be // utf-8 endcoded and results is an invalid email & bounces public function setMessageId(string $id) { try { $header = new Header\MessageId(); $header->setId($id); $this->addHeader($header); $this->mid = $header->getId(); } catch (\Throwable $t) { // Ignore invalid mid. Upstream will generate one } } public function getMessageId() { return $this->getId(); } // Valid email address required or no return "<>" tag public function setReturnPath($email) { try { // Exception is thrown on invalid email address $header = new ReturnPath(); $header->addAddress($email); $this->getHeaders()->removeHeader($header->getType()); $this->addHeader($header); } catch (\Throwable $t) { // It's not email - perhaps it's a tag? if (!strcmp($email, '<>')) $this->addHeader($header->getFieldName(), $email); // Silently dropping the invalid path } } public function addInReplyTo($inReplyTo) { if (!is_array($inReplyTo)) $inReplyTo = explode(' ', $inReplyTo); try { $header = new Header\InReplyTo(); $header->setIds($inReplyTo); #nolint $this->addHeader($header); } catch (\Throwable $t) { // Mshenzi } } public function addReferences($references) { if (!is_array($references)) $references = explode(' ', $references); try { $header = new Header\References(); $header->setIds($references); #nolint $this->addHeader($header); } catch (\Throwable $t) { // Mshenzi } } // Set Message Sender is useful for SendMail transport, its basically -f // parameter in the mail() interface public function setSender($email, $name=null) { try { // Exception is thrown on invalid email address $header = new Header\Sender(); $header->setAddress($email, $name); #nolint $this->addHeader($header); } catch (\Throwable $t) { // Silently ignore invalid email sender defaults to FROM // addresses } } public function setFrom($email, $name=null) { // We're resetting the body here when FROM address changes - e.g // after failed send attempt while trying multiple SMTP accounts unset($this->body); return parent::setFrom($email, $name); } // This is used to set FROM & and clear Sender to a new Email Address public function setOriginator($email, $name=null) { // Set the FROM Header $this->setFrom($email, $name); // Remove Sender Header $this->getHeaders()->removeHeader('sender'); } public function setContentType($contentType) { // We can only set content type for multipart message if (isset($this->body) && $this->body->isMultiPart() && $contentType) { if (($header=$this->getHeaders()->get('Content-Type'))) $header->setType($contentType); #nolint else $this->addHeader('Content-Type', $contentType); } } public function setBody($body=null) { // We're ignoring $body param on purpose - only added for // upstream compatibility - local interfaces should use // prepare() to set the body $body = $this->getMimeMessageContent(); $contentType = $this->hasHtml() ? Mime::MULTIPART_ALTERNATIVE : Mime::TYPE_TEXT; // if we have files (inline images or attachments) if ($this->hasFiles()) { // Content MimePart $content = $body->getContentMimePart(); // Get attachments parts (inline and files) $parts = $this->getMimeMessageParts()->getParts(); // prepend content part to files parts array_unshift($parts, $content); // Create a new Mime Message and set parts $body = new MimeMessage(); $body->setParts($parts); #nolint // We we only have inline images then content type is related // otherwise it's mixed. $contentType = $this->hasAttachments() ? Mime::MULTIPART_MIXED : Mime::MULTIPART_RELATED; } // Set body beaches parent::setBody($body); // Set the content type $this->setContentType($contentType); } public function prepare() { if (!isset($this->body)) $this->setBody(); } } // This is a wrapper class for Mime/Message that generates multipart // alternative content when email is multipart class ContentMimeMessage extends MimeMessage { public function getContent() { // unpack content parts to a content mime part return $this->generateMessage(); #nolint } public function getContentMimePart($type=null) { $part = new MimePart($this->getContent()); #nolint $part->type = $type ?: Mime::MULTIPART_ALTERNATIVE; // Set the alternate content boundary $part->setBoundary($this->getMime()->boundary()); #nolint // Clear the encoding $part->encoding = ""; return $part; } } // MailBoxProtocolTrait use Laminas\Mail\Protocol\Imap as ImapProtocol; use Laminas\Mail\Protocol\Pop3 as Pop3Protocol; trait MailBoxProtocolTrait { final public function init(AccountSetting $setting) { // Attempt to connect to the mail server $connect = $setting->getConnectionConfig(); // Let's go Brandon parent::__construct($connect['host'], $connect['port'], $connect['ssl'], true); // Attempt authentication based on MailBoxAccount settings $auth = $setting->getAuthCredentials(); switch (true) { case $auth instanceof BasicAuthCredentials: if (!$this->basicAuth($auth->getUsername(), $auth->getPassword())) throw new Exception('cannot login, user or password wrong'); break; case $auth instanceof OAuth2AuthCredentials: // Get OAuth2 Authentication Request $authen = $auth->getAuthRequest($setting->getUser()); if (!$this->oauth2Auth($authen)) throw new Exception('OAuth2 Authentication Error'); break; default: throw new Exception('Unknown Credentials Type'); } return true; } /* * Basic Authentication (Legacy) for the OG */ private function basicAuth($username, $password) { return $this->login($username, $password); } abstract public function __construct($accountSetting); abstract protected function oauth2Auth($authen); } class ImapMailboxProtocol extends ImapProtocol { use MailBoxProtocolTrait; public function __construct($accountSetting) { $this->init($accountSetting); } /* * [connection begins] * C: C01 CAPABILITY * S: * CAPABILITY … AUTH=XOAUTH2 * S: C01 OK Completed * C: A01 AUTHENTICATE XOAUTH2 {XOAUTH2} * S: A01 (OK|NO|BAD) * [connection continues...] */ private function oauth2Auth($authen) { $this->sendRequest('AUTHENTICATE', ['XOAUTH2', $authen]); while (true) { $matches = []; $response = ''; if ($this->readLine($response, '+', true)) { $this->sendRequest(''); } elseif (preg_match("/^CAPABILITY /i", $response)) { continue; } elseif (preg_match("/^OK /i", $response)) { return true; } elseif (preg_match('/^(NO|BAD) (.*+)$/i', $response, $matches)) { throw new Exception($matches[2]); } else { throw new Exception('Unknown Oauth2 Error: '.$response); } } return false; } } class Pop3MailboxProtocol extends Pop3Protocol { use MailBoxProtocolTrait; public function __construct($accountSetting) { $this->init($accountSetting); } /* * [connection begins] * C: AUTH XOAUTH2 * S: + * C: {XOAUTH2} * S: (+OK|-ERR|+ {msg}) * [connection continues...] */ public function oauth2Auth($authen) { $this->sendRequest('AUTH XOAUTH2'); while (true) { $response = $this->readLine(); $matches = []; if ($response == '+') { // Send xOAuthRequest $this->sendRequest($authen); } elseif (preg_match("/^\+OK /i", $response)) { return true; } elseif (preg_match('/^-ERR (.*+)$/i', $response, $matches)) { throw new Exception($matches[2]); } else { break; } } return false; } /* * readLine * * Pop3 Protocol doesn't have readLine function and readRresponse * has hardcoded status of "+OK" whereas Oauth2 response returns "+" * on AUTH XOAUTH2 command. */ public function readLine() { $result = fgets($this->socket); if (!is_string($result)) throw new Exception('read failed - connection closed'); return trim($result); } public function login($user, $password, $tryApop = true) { try { parent::login($user, $password, $tryApop); return true; } catch (\Throwable $e) { throw new Exception(__('login failed').': '.$e->getMessage()); } } } // MailBoxStorageTrait use Laminas\Mail\Storage\Imap as ImapStorage; use Laminas\Mail\Storage\Pop3 as Pop3Storage; use RecursiveIteratorIterator; trait MailBoxStorageTrait { private $folder; private $hostInfo; private function init(AccountSetting $setting) { $this->folder = $setting->getAccount()->getFolder(); $this->hostInfo = $setting->getHostInfo(); } public function getHostInfo() { return $this->hostInfo; } private function getFolder() { return $this->folder; } public function createFolder($name, $parentFolder = null) { try { parent::createFolder($name, $parentFolder); $this->folders = null; return true; } catch (\Exception $ex) { // noop } return false; } public function hasFolder($folder, $rootFolder = null) { $folders = $this->getFolders($rootFolder); if (is_array($folders) && in_array(strtolower($folder), $folders)) return true; // Try selecting the folder. try { $this->selectFolder($folder); return true; } catch (\Exception $ex) { //noop } return false; } public function getFolders($rootFolder = null) { if (!isset($this->folders)) { $folders = new RecursiveIteratorIterator( parent::getFolders(), RecursiveIteratorIterator::SELF_FIRST ); $this->folders = []; foreach ($folders as $name => $folder) { if (!$folder->isSelectable()) #nolint continue; $this->folders[] = strtolower($folder->getGlobalName()); #nolint } } return $this->folders; } /* * getRawEmail * * Given message number - get full raw email (headers + content) * */ public function getRawEmail(int $i) { return trim($this->getRawHeader($i)) . "\r\n\r\n" . $this->getRawContent($i); } /* * move an existing message to a folder * * Caller should catch possible exception */ public function moveMessage($i, $folder) { parent::moveMessage($i, $folder); return true; } /* * Remove a message from server. * * Caller should catch possible exception */ public function removeMessage($i) { parent::removeMessage($i); return true; } /* * markAsSeen */ public function markAsSeen($i) { // noop - storage that implement it should define it } public function expunge() { // noop - only IMAP } } // Imap use Laminas\Mail\Storage; class Imap extends ImapStorage { use MailBoxStorageTrait; private $folders; public function __construct($accountSetting) { $protocol = new ImapMailBoxProtocol($accountSetting); parent::__construct($protocol); $this->init($accountSetting); } // Mark message as seen public function markAsSeen($i) { try { return $this->setFlags($i, [Storage::FLAG_SEEN]); } catch (\Throwable $t) { return false; } } /** * Remove a message from server without expunging the mailbox * * Laminas Mail (upstream) auto expunges the mailbox on message * removal or move (copy + remove) - which can cause major issues * for us since we fetcher uses message sequence numbers to fetch * messages / emails. * * We expunge the mailbox at the end if fetch session. * * TODO: Make PR upstream to support calling removeMessage with * a boolean flag i.e removeMessage(int $id, bool $expunge = true) * */ public function removeMessage($i) { if (! $this->protocol->store([Storage::FLAG_DELETED], $i, null, '+')) { throw new Exception('cannot set deleted flag'); } return true; } // Expunge mailbox public function expunge() { return $this->protocol->expunge(); } } // Pop3 class Pop3 extends Pop3Storage { use MailBoxStorageTrait; public function __construct($accountSetting) { $protocol = new Pop3MailboxProtocol($accountSetting); parent::__construct($protocol); $this->init($accountSetting); } } // Smtp use Laminas\Mail\Transport\Smtp as SmtpTransport; class Smtp extends SmtpTransport { private $connected = false; public function __construct(SmtpOptions $options) { parent::__construct($options); } private function isConnected() { return $this->connected; } public function connect() { try { if (!$this->isConnected() && parent::connect()) $this->connected = true; return $this->isConnected(); } catch (\Throwable $ex) { // Smtp protocol throws an Exception via error handler // resulting in unrestored handler on socket error restore_error_handler(); throw $ex; } } public function sendMessage(Message $message) { try { // Make sure the body is set $message->prepare(); parent::send($message); } catch (\Throwable $ex) { $this->connected = false; throw $ex; } return true; } } // SmtpOptions use Laminas\Mail\Transport\SmtpOptions as SmtpSettings; class SmtpOptions extends SmtpSettings { public function __construct(AccountSetting $setting) { parent::__construct($this->buildOptions($setting)); } // Build out SmtpOptions options based on SmtpAccount Settings private function buildOptions(AccountSetting $setting) { // Dont send 'QUIT' on __destruct() $config = [ 'use_complete_quit' => false, 'novalidatecert' => true ]; $connect = $setting->getConnectionConfig(); $auth = $setting->getAuthCredentials(); switch (true) { case $auth instanceof NoAuthCredentials: // No Authentication - simply return host and port return [ 'host' => $connect['host'], 'port' => $connect['port'], 'name' => $connect['name'], ]; break; case $auth instanceof BasicAuthCredentials: $config += [ 'username' => $auth->getUsername(), 'password' => $auth->getPasswd(), 'ssl' => $connect['ssl'], ]; break; case $auth instanceof OAuth2AuthCredentials: $token = $auth->getAccessToken(); if ($token->hasExpired()) throw new Exception('Access Token is Expired'); $config += [ 'xoauth2' => $token->getAuthRequest(), 'ssl' => $connect['ssl'], ]; break; default: throw new Exception('Unknown Authentication Type'); } return [ 'host' => $connect['host'], 'port' => $connect['port'], 'name' => $connect['name'], 'connection_time_limit' => 300, # 5 minutes limit 'connection_class' => $auth->getConnectionClass(), 'connection_config' => $config ]; } } // Sendmail use Laminas\Mail\Transport\Sendmail as SendmailTransport; class Sendmail extends SendmailTransport { public function __construct($options) { parent::__construct($options); } public function sendMessage(Message $message) { try { // Make sure the body is set $message->prepare(); parent::send($message); return true; } catch (\Throwable $ex) { throw $ex; } return true; } } // Credentials abstract class AuthCredentials { static $class = 'plain'; public function getConnectionClass() { return static::$class; } public function serialize() { return json_encode($this->__serialize()); } public function __serialize() { return $this->toArray(); } public static function init(array $options) { return new static($options); } abstract function __construct(array $options); abstract function toArray(); } class NoAuthCredentials extends AuthCredentials { private $username; public function __construct(array $options) { if (empty($options['username'])) { throw new Exception(sprintf( __('Required option not passed: "%s"'), 'username')); } $this->username = $options['username']; } public function getUsername() { return $this->username; } public function toArray() { return [ 'username' => $this->getUsername() ]; } } class BasicAuthCredentials extends AuthCredentials { static $class = 'login'; private $username; private $password; public function __construct(array $options) { if (empty($options['username'])) { throw new Exception(sprintf( __('Required option not passed: "%s"'), 'username')); } if (empty($options['password'])) { throw new Exception(sprintf( __('Required option not passed: "%s"'), 'password')); } $this->username = $options['username']; $this->password = $options['password']; } public function getUsername() { return $this->username; } public function getPasswd() { return $this->getPassword(); } public function getPassword() { return $this->password; } public function toArray() { return [ 'username' => $this->getUsername(), 'password' => $this->getPassword() ]; } } class OAuth2AuthCredentials extends AuthCredentials { static $class = 'osTicket\Mail\Protocol\Smtp\Auth\OAuth2'; private $token; public function __construct(array $options) { if (empty($options['access_token'])) { throw new Exception(sprintf( __('Required option not passed: "%s"'), 'access_token')); } if (empty($options['resource_owner_email'])) { throw new Exception(sprintf( __('Required option not passed: "%s"'), 'resource_owner_email')); } $this->token = new AccessToken($options); } public function getToken() { return $this->token; } public function getAuthRequest($user=null) { return $this->getToken() ? $this->getToken()->getAuthRequest($user) : null; } public function getAccessToken($signature=false) { $token = $this->getToken(); // check signature if requested return (!$signature || !strcmp($signature, $token->getConfigSignature())) ? $token : null; } public function toArray() { return $this->token->toArray(); } } // osTicket/Mail/AccountSetting class AccountSetting { private $account; private $creds; private $connection = []; private $errors = []; public function __construct(\EmailAccount $account) { // Set the account $this->account = &$account; // Parse Connection Options // We allow scheme to hint for encryption for people using ssl or tls // on nonstandard ports. $host = $account->getHost(); $port = (int) $account->getPort(); $ssl = null; $matches = []; if (preg_match('~^(ssl|tls|plain)://(.*+)$~iu', strtolower($host), $matches)) { list(, $ssl, $host) = $matches; // Clear ssl when "plain" connection is being forced without // using port number as the indicator! $ssl = strcmp($ssl, 'plain') ? $ssl : null; // Why would someone use a standard encryption based port // for unencrypted connection is beyond me - but apparently // it's a thing!! } elseif (!$ssl && $port) { // Set ssl or tls on based on standard ports if (in_array($port, [465, 993, 995])) $ssl = 'ssl'; elseif (in_array($port, [587])) $ssl = 'tls'; } // Set the connection settings $this->connection = [ 'host' => $host, 'port' => $port, 'ssl' => $ssl, 'protocol' => strtoupper($account->getProtocol()), 'name' => self::get_hostname(), ]; // Set errors to null to clear validation $this->errors = null; } public function getUser() { return $this->account->getEmail()->getEmail(); } public function getName() { return $this->connection['name']; } public function getHost() { return $this->connection['host']; } public function getPort() { return $this->connection['port']; } public function getSsl() { return $this->connection['ssl']; } public function getProtocol() { return $this->connection['protocol']; } public function setCredentials(AuthCredentials $creds) { $this->creds = $creds; } public function getCredentials() { if (!isset($this->creds)) $this->creds = $this->account->getCredentials(); return $this->creds; } public function getAuthCredentials() { return $this->getCredentials(); } public function getAccount() { return $this->account; } public function getConnectionConfig() { return $this->connection; } public function getHostInfo() { return $this->describe(); } public function asArray() { return $this->getConnectionConfig(); } public function describe() { return sprintf('%s://%s:%s/%s', $this->getSsl() ?: 'plain', $this->getHost(), $this->getPort(), $this->getProtocol()); } private function validate() { if (!isset($this->errors)) { // Set errors to an array to to make sure don't // unneccesarily validate valid info again. $this->errors = []; // We're simply making sure required info are set. True // validation will happen at the protocol connection level $info = $this->getConnectionConfig(); foreach (['host', 'port', 'protocol'] as $p ) { if (!isset($info[$p]) || !$info[$p]) $this->errors[$p] = sprintf('%s %s', strtoupper($p), __('Required')); } } return !count($this->errors); } public function isValid() { return $this->validate(); } public function getErrors() { return $this->errors; } /* * get_hostname * * Please note that this is different from getHost above * * Here we're getting the hostname to use on HELO/EHLO when * initiating an SMTP connection. It should be a valid hostname with * valid reverse-lookup for better deliverability. * * Perhaps this can be a setting in the future but allowing users * to set it to **anything** will results in more mail issues than just * defaulting to what the OS tells us or localhost for that matter. * * For now, we're simply asking core osTicket to give us the OS hostname * otherwise it will default to localhost which some mail servers frawns * upon since it won't have a valid reverse-lookup. * */ private static function get_hostname() { // We're simply returning what the OS is telling us! return php_uname('n'); } } } namespace osTicket\Mail\Protocol\Smtp\Auth { // Exception as Mail\RuntimeException use Laminas\Mail; class Exception extends Mail\Exception\RuntimeException { } use Laminas\Mail\Protocol\Smtp; class OAuth2 extends Smtp { private $xoauth2; public function __construct($host = null, $port = null, $config = null) { $this->setParams($host, $config); parent::__construct($host, $port, $config); } private function setParams($host, $config) { $_config = []; if (is_array($host)) $_config = is_array($config) ? array_replace_recursive($host, $config) : $host; if (is_array($_config) && isset($_config['xoauth2'])) $this->xoauth2 = $_config['xoauth2']; } private function getAuthRequest() { return $this->xoauth2; } /* * [connection begins] * C: auth xoauth2 * S: 334 * C: {XOAUTH2} * S: (235|XXX) * [connection continues...] */ public function auth() { // Check Parent parent::auth(); // Make sure we have XOAUTH2 if (!($xoauth2=$this->getAuthRequest())) throw new Exception('XOAUTH2 Required'); $this->_send('AUTH XOAUTH2'); $this->_expect(334); $this->_send($xoauth2); $this->_expect(235); $this->auth = true; } } } namespace osTicket\Mail\Header { use Laminas\Mail\Header\AbstractAddressList; use Laminas\Mail\Header\HeaderInterface; use Laminas\Mail\Address; class ReturnPath extends AbstractAddressList { protected $fieldName = 'Return-Path'; protected static $type = 'return-path'; public function addAddress($email) { $this->getAddressList()->add(new Address($email)); #nolint } public function getFieldValue($format = HeaderInterface::FORMAT_RAW) { // We're simply intercepting Value here to add <> to the email return sprintf('<%s>', parent::getFieldValue($format)); } public function getType() { return self::$type; } } }
Save