| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- <?php
- /**
- * The model file of mail module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
- * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Chunsheng Wang <chunsheng@cnezsoft.com>
- * @package mail
- * @version $Id: model.php 4750 2013-05-05 00:22:53Z chencongzhi520@gmail.com $
- * @link https://www.zentao.net
- */
- ?>
- <?php
- class mailModel extends model
- {
- public static $instance;
- public $mta;
- public $mtaType;
- public $errors = array();
- /**
- * 初始化
- * Construct
- *
- * @access public
- * @return void
- */
- public function __construct()
- {
- parent::__construct();
- $mta = $this->config->mail->mta;
- $this->app->loadClass('phpmailer', $static = true);
- $this->setMTA();
- }
- /**
- * Auto detect email config.
- *
- * @param string $email
- * @access public
- * @return object
- */
- public function autoDetect($email)
- {
- $username = $domain = '';
- /* Split the email to username and domain. */
- if(strpos($email, '@') !== false) list($username, $domain) = explode('@', $email);
- $domain = strtolower($domain);
- /*
- * 1. try to find config from the providers.
- * 2. try to find the mx record to get the domain and then search it in providers.
- * 3. try smtp.$domain's 25 and 465 port, if can connect, use smtp.$domain.
- */
- $config = $this->getConfigFromProvider($domain, $username);
- if(!$config) $config = $this->getConfigByMXRR($domain, $username);
- if(!$config) $config = $this->getConfigByDetectingSMTP($domain, $username, 25);
- if(!$config) $config = $this->getConfigByDetectingSMTP($domain, $username, 465);
- if(!$config) $config = new stdclass();
- /* Set default values. */
- $config->mta = 'smtp';
- $config->fromName = '';
- $config->password = '';
- $config->debug = 1;
- $config->charset = 'utf-8';
- if(!isset($config->secure)) $config->secure = 0;
- if(!isset($config->username)) $config->username = $username;
- if(!isset($config->host)) $config->host = '';
- if(!isset($config->auth)) $config->auth = 1;
- if(!isset($config->port)) $config->port = '25';
- return $config;
- }
- /**
- * Try get config from providers.
- *
- * @param string $domain
- * @param string $username
- * @access public
- * @return object|false
- */
- public function getConfigFromProvider($domain, $username)
- {
- if(!isset($this->config->mail->provider[$domain])) return false;
- $config = (object)$this->config->mail->provider[$domain];
- $config->mta = 'smtp';
- $config->username = $username;
- $config->auth = 1;
- if(!isset($config->port)) $config->port = 25;
- if(!isset($config->secure)) $config->secure = 0;
- return $config;
- }
- /**
- * Get config by MXRR.
- *
- * @param string $domain
- * @param string $username
- * @access public
- * @return object|false
- */
- public function getConfigByMXRR($domain, $username)
- {
- /* Try to get mx record, under linux, use getmxrr() directly, windows use nslookup. */
- $smtpHosts = array();
- if(function_exists('getmxrr'))
- {
- getmxrr($domain, $smtpHosts);
- }
- elseif(strpos(PHP_OS, 'WIN') !== false)
- {
- $result = `nslookup -q=mx {$domain} 2>nul`;
- $lines = explode("\n", $result);
- foreach($lines as $line)
- {
- if(stripos($line, 'exchanger')) $smtpHosts[] = trim(substr($line, strrpos($line, '=') + 1));
- }
- }
- /* Cycle the smtpHosts and try to find it's config from the provider config. */
- foreach($smtpHosts as $smtpHost)
- {
- /* Get the domain name from the hosts, for example: imxbiz1.qq.com get qq.com. */
- $smtpDomain = explode('.', $smtpHost);
- array_shift($smtpDomain);
- $smtpDomain = strtolower(implode('.', $smtpDomain));
- $config = $this->getConfigFromProvider($smtpDomain, $username);
- if($config) $config->username = "$username@$domain";
- return $config;
- }
- return false;
- }
- /**
- * Try connect to smtp.$domain's 25 or 465 port and compute the config according to the connection result.
- *
- * @param string $domain
- * @param string $username
- * @param int $port
- * @access public
- * @return object|false
- */
- public function getConfigByDetectingSMTP($domain, $username, $port)
- {
- ini_set('default_socket_timeout', 3);
- $host = 'smtp.' . $domain;
- if(gethostbynamel($host) == false) return false;
- $connection = @fsockopen($host, $port);
- if(!$connection) return false;
- fclose($connection);
- $config = new stdclass();
- $config->username = $username;
- $config->host = $host;
- $config->auth = 1;
- $config->port = $port;
- $config->secure = $port == 465 ? 'ssl' : 0;
- return $config;
- }
- /**
- * Set MTA.
- *
- * @access public
- * @return void
- */
- public function setMTA()
- {
- if(static::$instance == null) static::$instance = new phpmailer(true);
- $this->mta = static::$instance;
- $this->mta->CharSet = $this->config->charset;
- $funcName = "set{$this->config->mail->mta}";
- if(!method_exists($this, $funcName)) $this->app->triggerError("The MTA {$this->config->mail->mta} not supported now.", __FILE__, __LINE__, $exit = true);
- $this->$funcName();
- return $this->mta;
- }
- /**
- * Set smtp.
- *
- * @access public
- * @return void
- */
- public function setSMTP()
- {
- $this->mta->isSMTP();
- $this->mta->SMTPDebug = $this->config->mail->smtp->debug;
- $this->mta->Host = $this->config->mail->smtp->host;
- $this->mta->SMTPAuth = $this->config->mail->smtp->auth;
- $this->mta->Username = $this->config->mail->smtp->username;
- $this->mta->Password = $this->config->mail->smtp->password;
- if(isset($this->config->mail->smtp->charset)) $this->mta->CharSet = $this->config->mail->smtp->charset;
- if(isset($this->config->mail->smtp->port)) $this->mta->Port = $this->config->mail->smtp->port;
- if(isset($this->config->mail->smtp->secure) and !empty($this->config->mail->smtp->secure))$this->mta->SMTPSecure = strtolower($this->config->mail->smtp->secure);
- }
- /**
- * Gmail.
- *
- * @access public
- * @return void
- */
- public function setGMail()
- {
- $this->mta->isSMTP();
- $this->mta->SMTPDebug = $this->config->mail->gmail->debug;
- $this->mta->Host = 'smtp.gmail.com';
- $this->mta->Port = 465;
- $this->mta->SMTPSecure = "ssl";
- $this->mta->SMTPAuth = true;
- $this->mta->Username = $this->config->mail->gmail->username;
- $this->mta->Password = $this->config->mail->gmail->password;
- }
- /**
- * Send email
- *
- * @param string $toList
- * @param string $subject
- * @param string $body
- * @param string $ccList
- * @param bool $includeMe
- * @param array $emails
- * @param bool $forceSync
- * @param bool $processUser
- * @access public
- * @return int|string|bool
- */
- public function send($toList, $subject, $body = '', $ccList = '', $includeMe = false, $emails = array(), $forceSync = false, $processUser = true)
- {
- if(!$this->config->mail->turnon) return false;
- if(!empty($this->config->mail->async) and !$forceSync) return $this->addQueue($toList, $subject, $body, $ccList, $includeMe, $emails = array());
- ob_start();
- $images = $this->mailTao->getImages($body);
- if($images) $body = $this->mailTao->replaceImageURL($body, $images);
- if($processUser) list($toList, $ccList) = $this->mailTao->processToAndCC($toList, $ccList, $includeMe);
- /* Get realname and email of users. */
- if(empty($emails)) $emails = $this->loadModel('user')->getRealNameAndEmails($toList . ',' . $ccList);
- $this->clear();
- try
- {
- /* Add for task #5301. */
- if(function_exists('putenv')) putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
- $this->mta->setFrom($this->config->mail->fromAddress, $this->convertCharset($this->config->mail->fromName));
- $this->setSubject($this->convertCharset($subject));
- $this->setTO(explode(',', $toList), $emails);
- $this->setCC(explode(',', $ccList), $emails);
- $this->setBody($this->convertCharset($body));
- if($images) $this->setImages($images);
- $this->setErrorLang();
- $this->mta->send();
- }
- catch (phpmailerException $e)
- {
- $mailError = ob_get_contents();
- $encoding = mb_detect_encoding($mailError, array('ASCII','UTF-8','GB2312','GBK','BIG5'));
- if($encoding != 'UTF-8') $mailError = mb_convert_encoding($mailError, 'utf8', $encoding);
- $this->errors[] = nl2br(trim(strip_tags($e->errorMessage())));
- $this->errors[] = $mailError;
- }
- catch (Exception $e)
- {
- $this->errors[] = trim(strip_tags($e->getMessage()));
- }
- if($this->config->mail->mta == 'smtp') $this->mta->smtpClose();
- /* save errors. */
- if($this->isError()) $this->app->saveError(2, implode(' ', $this->errors), __FILE__, __LINE__, true);
- $message = ob_get_contents();
- ob_end_clean();
- return $message;
- }
- /**
- * Set to address
- *
- * @param array $toList
- * @param array $emails
- * @access public
- * @return void
- */
- public function setTO($toList, $emails)
- {
- if(empty($toList)) return;
- foreach($toList as $account)
- {
- if(!isset($emails[$account]) or isset($emails[$account]->sended) or strpos($emails[$account]->email, '@') == false) continue;
- $this->mta->addAddress($emails[$account]->email, $this->convertCharset($emails[$account]->realname));
- $emails[$account]->sended = true;
- }
- }
- /**
- * Set cc.
- *
- * @param array $ccList
- * @param array $emails
- * @access public
- * @return void
- */
- public function setCC($ccList, $emails)
- {
- if(empty($ccList)) return;
- foreach($ccList as $account)
- {
- if(!isset($emails[$account]) or isset($emails[$account]->sended) or strpos($emails[$account]->email, '@') == false) continue;
- $this->mta->addCC($emails[$account]->email, $this->convertCharset($emails[$account]->realname));
- $emails[$account]->sended = true;
- }
- }
- /**
- * Set subject
- *
- * @param string $subject
- * @access public
- * @return void
- */
- public function setSubject($subject)
- {
- $this->mta->Subject = stripslashes($subject);
- }
- /**
- * Set body.
- *
- * @param string $body
- * @access public
- * @return void
- */
- public function setBody($body)
- {
- $this->mta->msgHtml($body);
- }
- /**
- * 设置嵌入邮件的图片。
- * Set embedded images in the email.
- *
- * @param array $images
- * @access public
- * @return void
- */
- public function setImages($images)
- {
- $wwwRoot = $this->app->getWwwRoot();
- $images = array_filter(array_unique($images));
- foreach($images as $image) $this->mta->AddEmbeddedImage($image, basename($image));
- }
- /**
- * Convert charset.
- *
- * @param string $string
- * @access public
- * @return string
- */
- public function convertCharset($string)
- {
- if(!empty($this->config->mail->smtp->charset) and $this->config->mail->smtp->charset != strtolower($this->config->charset)) return helper::convertEncoding($string, $this->config->charset, $this->config->mail->smtp->charset);
- return $string;
- }
- /**
- * Set error lang.
- *
- * @access public
- * @return void
- */
- public function setErrorLang()
- {
- $this->mta->SetLanguage($this->app->getClientLang());
- }
- /**
- * Clear.
- *
- * @access public
- * @return void
- */
- public function clear()
- {
- $this->mta->clearAllRecipients();
- $this->mta->clearAttachments();
- }
- /**
- * Check system if there is a mail at least.
- *
- * @access public
- * @return object|false
- */
- public function mailExist()
- {
- return $this->dao->select('email')->from(TABLE_USER)->where('email')->ne('')->fetch();
- }
- /**
- * Is error?
- *
- * @access public
- * @return bool
- */
- public function isError()
- {
- return !empty($this->errors);
- }
- /**
- * Get errors.
- *
- * @access public
- * @return array
- */
- public function getError()
- {
- $errors = $this->errors;
- $this->errors = array();
- return $errors;
- }
- /**
- * Add queue.
- *
- * @param string $toList
- * @param string $subject
- * @param string $body
- * @param string $ccList
- * @param bool $includeMe
- * @access public
- * @return int|bool
- */
- public function addQueue($toList, $subject, $body = '', $ccList = '', $includeMe = false)
- {
- list($toList, $ccList) = $this->mailTao->processToAndCC($toList, $ccList, $includeMe);
- if(empty($toList) and empty($ccList)) return false;
- if(empty($toList) or empty($subject)) return false;
- $data = new stdclass();
- $data->objectType = 'mail';
- $data->toList = $toList;
- $data->ccList = $ccList;
- $data->subject = $subject;
- $data->data = $body;
- $data->createdBy = $this->app->user->account;
- $data->createdDate = helper::now();
- $this->dao->insert(TABLE_NOTIFY)->data($data)->autocheck()->exec();
- return $this->dao->lastInsertID();
- }
- /**
- * Get queue.
- *
- * @param string $status all|wait|fail
- * @param string $orderBy
- * @param object $pager
- * @param bool $mergeByUser
- * @access public
- * @return array
- */
- public function getQueue($status = 'all', $orderBy = 'id_desc', $pager = null, $mergeByUser = true)
- {
- $mails = $this->dao->select('*')->from(TABLE_NOTIFY)
- ->where('objectType')->eq('mail')
- ->beginIF(!empty($status) && $status != 'all')->andWhere('status')->eq($status)->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id', false);
- if(!$mergeByUser) return $mails;
- /* Group mails by toList and ccList. */
- $groupMails = array();
- foreach($mails as $mail)
- {
- $users = $mail->toList . ',' . $mail->ccList;
- $groupMails[$users][] = $mail;
- }
- /* Merge the mails if a group has more than one mail. */
- $queue = array();
- foreach($groupMails as $groupMail)
- {
- if(count($groupMail) == 1) $queue[] = reset($groupMail);
- if(count($groupMail) > 1) $queue[] = $this->mergeMails($groupMail);
- }
- return $queue;
- }
- /**
- * Get queue by id.
- *
- * @param int $queueID
- * @access public
- * @return object|false
- */
- public function getQueueById($queueID)
- {
- return $this->dao->select('*')->from(TABLE_NOTIFY)->where('id')->eq($queueID)->fetch();
- }
- /**
- * Merge mails.
- *
- * @param array $mails
- * @access public
- * @return object
- */
- public function mergeMails($mails = array())
- {
- if(count($mails) <= 1) return array_shift($mails);
- /* Get first and last mail. */
- $firstMail = array_shift($mails);
- $lastMail = array_pop($mails);
- $secondMail = empty($mails) ? '' : reset($mails);
- /* Set mail info.*/
- $mail = new stdClass();
- $mail->status = 'wait';
- $mail->merge = true;
- $mail->id = $firstMail->id;
- $mail->toList = $firstMail->toList;
- $mail->ccList = $firstMail->ccList;
- $mail->subject = $firstMail->subject;
- $mail->data = $firstMail->data;
- /* Remove html tail for first mail. */
- if(($endPos = strripos($firstMail->data, '</td>')) !== false) $mail->data = substr($firstMail->data, 0, $endPos);
- if(empty($mails)) $mail->subject .= '|' . $lastMail->subject;
- if(!empty($mails)) $mail->subject .= '|' . $secondMail->subject . '|' . $this->lang->mail->more;
- foreach($mails as $middleMail)
- {
- $mail->id .= ',' . $middleMail->id;
- /* Remove html head and tail for middle mails. */
- $mailBody = $middleMail->data;
- if(($beginPos = strpos($mailBody, '</table>')) !== false) $mailBody = substr($mailBody, $beginPos + 8);
- if(($endPos = strripos($mailBody, '</td>')) !== false) $mailBody = substr($mailBody, 0, $endPos);
- $mail->data .= $mailBody;
- }
- /* Remove html head for last mail. */
- $mailBody = $lastMail->data;
- if(($beginPos = strpos($mailBody, '</table>')) !== false) $mailBody = substr($mailBody, $beginPos + 8);
- $mail->id .= ',' . $lastMail->id;
- $mail->data .= $mailBody;
- return $mail;
- }
- /**
- * Send mail by object.
- *
- * @param int $objectID
- * @param int $actionID
- * @access public
- * @return void
- */
- public function sendmail($objectID, $actionID)
- {
- if(empty($objectID) or empty($actionID)) return;
- /* Load module and get vars. */
- $action = $this->mailTao->getActionForMail($actionID);
- $objectType = $action->objectType;
- $object = $this->mailTao->getObjectForMail($objectType, $objectID);
- $title = $this->mailTao->getObjectTitle($object, $objectType);
- $subject = $this->mailTao->getSubject($objectType, $object, $title, $action->action);
- $domain = zget($this->config->mail, 'domain', common::getSysURL());
- $domain = rtrim($domain, '/');
- if(empty($title)) $action->appendLink = '';
- if($title and $action->appendLink) $action->appendLink = html::a($domain . helper::createLink($action->objectType, 'view', "id={$action->appendLink}"), "#{$action->appendLink} {$title}");
- if($objectType == 'kanbancard') $objectType = 'kanban';
- $addressees = $this->mailTao->getAddressees($objectType, $object, $action);
- if(!$addressees) return;
- list($toList, $ccList) = $addressees;
- /* Send it. */
- if($objectType == 'mr')
- {
- $mailContent = $this->mailTao->getMRMailContent($object, $action->action);
- if($action->action == 'compilefail') $this->send($toList, $subject, $mailContent, $ccList);
- if($action->action == 'compilepass')
- {
- $this->send($toList, $subject, $mailContent);
- $this->send($ccList, $subject, $this->mailTao->getMRMailContent($object, $action->action, 'cc'));
- /* Create a todo item for this MR. */
- $this->loadModel('mr')->apiCreateMRTodo($object->hostID, $object->targetProject, $object->mriid);
- }
- }
- else
- {
- if($objectType == 'review') $this->app->loadLang('baseline');
- $emails = array();
- $mailContent = $this->mailTao->getMailContent($objectType, $object, $action);
- if($objectType == 'ticket') $emails = $this->loadModel('ticket')->getContactEmails($object->id, $toList, $ccList, $action->action == 'closed');
- $this->send($toList, $subject, $mailContent, $ccList, false, $emails);
- }
- if($this->isError()) error_log(implode("\n", $this->getError()));
- }
- /**
- * Get subject.
- *
- * @param string $objectType
- * @param object $object
- * @param string $title
- * @param string $actionType
- * @access public
- * @return string
- */
- public function getSubject($objectType, $object, $title, $actionType)
- {
- $suffix = '';
- $subject = '';
- $titleType = 'edit';
- if($objectType == 'testtask')
- {
- $this->app->loadLang('testtask');
- if($actionType == 'opened') $titleType = 'create';
- if($actionType == 'closed') $titleType = 'close';
- return sprintf($this->lang->testtask->mail->{$titleType}->title, $this->app->user->realname, $object->id, $object->name);
- }
- if($objectType == 'doc')
- {
- $this->app->loadLang('doc');
- if($actionType == 'releaseddoc') $titleType = 'releasedDoc';
- return sprintf($this->lang->doc->mail->{$titleType}->title, $this->app->user->realname, $object->id, $object->title);
- }
- if($objectType == 'story' or $objectType == 'bug') $suffix = empty($object->product) ? '' : ' - ' . $this->loadModel('product')->getById($object->product)->name;
- if($objectType == 'task') $suffix = empty($object->execution) ? '' : ' - ' . $this->loadModel('execution')->getById($object->execution)->name;
- if($objectType == 'story') $objectType = $object->type; // 业务需求和用户需求的邮件标题不同。
- return strtoupper($objectType) . ' #' . $object->id . ' ' . $title . $suffix;
- }
- /**
- * 校验传入项的操作列对应操作按钮是否可点击。
- * Check if the item is clickable.
- *
- * @param object $queue
- * @param string $method
- * @access public
- * @return bool
- * @param object $item
- */
- public function isClickable($item, $method)
- {
- $hasPriv = common::hasPriv('mail', $method);
- if($method == 'resend' && $item->status == 'fail') return $hasPriv;
- if($method == 'delete') return $hasPriv;
- return false;
- }
- }
|