=$title?>
14 |-
18 |
- > 19 |
- > 20 | 21 | 22 |
- 23 |
-
52 |
- 模板文件 53 | next()): ?> 54 |
- current): ?> class="current"> 55 | file(); ?> 56 | 57 |
├── log └── .gitkeep ├── cache └── .gitkeep ├── owner.html ├── guest.html ├── README.md ├── page └── console.php ├── Console.php ├── Plugin.php ├── lib ├── class.pop3.php ├── class.smtp.php └── class.phpmailer.php └── Action.php /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cache/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /owner.html: -------------------------------------------------------------------------------- 1 |
{siteTitle}:{title} 有新的评论
3 |{author} 评论说:{text}
4 |时间:{time}
IP:{ip}
邮箱:{mail}
状态:{status} [管理评论]
{author_p}:{title} 有新的回复
3 |{author} 回复说:{text}
4 |你的评论:{text_p}
5 |时间:{time}
6 |本邮件为{siteTitle}自动发送,请勿直接回复,如有疑问,联系我 。
7 |';
395 | foreach ($this->error as $single_error) {
396 | print_r($single_error);
397 | }
398 | echo '';
399 | }
400 |
401 | /**
402 | * POP3 connection error handler.
403 | * @param integer $errno
404 | * @param string $errstr
405 | * @param string $errfile
406 | * @param integer $errline
407 | * @access private
408 | */
409 | private function catchWarning($errno, $errstr, $errfile, $errline)
410 | {
411 | $this->error[] = array(
412 | 'error' => "Connecting to the POP3 server raised a PHP warning: ",
413 | 'errno' => $errno,
414 | 'errstr' => $errstr,
415 | 'errfile' => $errfile,
416 | 'errline' => $errline
417 | );
418 | }
419 | }
420 |
--------------------------------------------------------------------------------
/Action.php:
--------------------------------------------------------------------------------
1 | init();
39 | //获取评论内容
40 | $file = $this->_dir . '/cache/' . $fileName;
41 | if (file_exists($file)) {
42 | $this->_email = unserialize(file_get_contents($file));
43 | @unlink($file);
44 |
45 | if (!$this->_user->simpleLogin($this->_email->ownerId)) {
46 | $this->widget('Widget_Archive@404', 'type=404')->render();
47 | exit;
48 | }
49 | } else {
50 | $this->widget('Widget_Archive@404', 'type=404')->render();
51 | exit;
52 | }
53 |
54 | //如果本次评论设置了拒收邮件,把coid加入拒收列表
55 | if ($this->_email->banMail) {
56 | $this->ban($this->_email->coid, true);
57 | }
58 |
59 | //发件人邮箱
60 | $this->_email->from = $this->_cfg->user;
61 |
62 | //发件人名称
63 | $this->_email->fromName = $this->_cfg->fromName ? $this->_cfg->fromName : $this->_email->siteTitle;
64 |
65 | //向博主发邮件的标题格式
66 | $this->_email->titleForOwner = $this->_cfg->titleForOwner;
67 |
68 | //向访客发邮件的标题格式
69 | $this->_email->titleForGuest = $this->_cfg->titleForGuest;
70 |
71 | //验证博主是否接收自己的邮件
72 | $toMe = (in_array('to_me', $this->_cfg->other) && $this->_email->ownerId == $this->_email->authorId) ? true : false;
73 |
74 | //向博主发信
75 | if (in_array($this->_email->status, $this->_cfg->status) && in_array('to_owner', $this->_cfg->other)
76 | && ( $toMe || $this->_email->ownerId != $this->_email->authorId) && 0 == $this->_email->parent ) {
77 | if (empty($this->_cfg->mail)) {
78 | Typecho_Widget::widget('Widget_Users_Author@temp' . $this->_email->cid, array('uid' => $this->_email->ownerId))->to($user);
79 | $this->_email->to = $user->mail;
80 | } else {
81 | $this->_email->to = $this->_cfg->mail;
82 | }
83 |
84 | $this->authorMail()->sendMail();
85 | }
86 |
87 | //向访客发信
88 | if (0 != $this->_email->parent
89 | && 'approved' == $this->_email->status
90 | && in_array('to_guest', $this->_cfg->other)
91 | && !$this->ban($this->_email->parent)) {
92 | //如果联系我的邮件地址为空,则使用文章作者的邮件地址
93 | if (empty($this->_email->contactme)) {
94 | if (!isset($user) || !$user) {
95 | Typecho_Widget::widget('Widget_Users_Author@temp' . $this->_email->cid, array('uid' => $this->_email->ownerId))->to($user);
96 | }
97 | $this->_email->contactme = $user->mail;
98 | } else {
99 | $this->_email->contactme = $this->_cfg->contactme;
100 | }
101 |
102 | $original = $this->_db->fetchRow($this->_db->select('author', 'mail', 'text')
103 | ->from('table.comments')
104 | ->where('coid = ?', $this->_email->parent));
105 |
106 | if (in_array('to_me', $this->_cfg->other)
107 | || $this->_email->mail != $original['mail']) {
108 | $this->_email->to = $original['mail'];
109 | $this->_email->originalText = $original['text'];
110 | $this->_email->originalAuthor = $original['author'];
111 | $this->guestMail()->sendMail();
112 | }
113 | }
114 |
115 | $date = new Typecho_Date(Typecho_Date::gmtTime());
116 | $time = $date->format('Y-m-d H:i:s');
117 | $this->mailLog(false, $time . " 邮件发送完毕!\r\n");
118 | }
119 |
120 | /**
121 | * 作者邮件信息
122 | * @return $this
123 | */
124 | public function authorMail()
125 | {
126 | $this->_email->toName = $this->_email->siteTitle;
127 | $date = new Typecho_Date($this->_email->created);
128 | $time = $date->format('Y-m-d H:i:s');
129 | $status = array(
130 | "approved" => '通过',
131 | "waiting" => '待审',
132 | "spam" => '垃圾'
133 | );
134 | $search = array(
135 | '{siteTitle}',
136 | '{title}',
137 | '{author}',
138 | '{ip}',
139 | '{mail}',
140 | '{permalink}',
141 | '{manage}',
142 | '{text}',
143 | '{time}',
144 | '{status}'
145 | );
146 | $replace = array(
147 | $this->_email->siteTitle,
148 | $this->_email->title,
149 | $this->_email->author,
150 | $this->_email->ip,
151 | $this->_email->mail,
152 | $this->_email->permalink,
153 | $this->_email->manage,
154 | $this->_email->text,
155 | $time,
156 | $status[$this->_email->status]
157 | );
158 |
159 | $this->_email->msgHtml = str_replace($search, $replace, $this->getTemplate('owner'));
160 | $this->_email->subject = str_replace($search, $replace, $this->_email->titleForOwner);
161 | $this->_email->altBody = "作者:".$this->_email->author."\r\n链接:".$this->_email->permalink."\r\n评论:\r\n".$this->_email->text;
162 |
163 | return $this;
164 | }
165 |
166 | /**
167 | * 访问邮件信息
168 | * @return $this
169 | */
170 | public function guestMail()
171 | {
172 | $this->_email->toName = $this->_email->originalAuthor ? $this->_email->originalAuthor : $this->_email->siteTitle;
173 | $date = new Typecho_Date($this->_email->created);
174 | $time = $date->format('Y-m-d H:i:s');
175 | $search = array(
176 | '{siteTitle}',
177 | '{title}',
178 | '{author_p}',
179 | '{author}',
180 | '{permalink}',
181 | '{text}',
182 | '{contactme}',
183 | '{text_p}',
184 | '{time}'
185 | );
186 | $replace = array(
187 | $this->_email->siteTitle,
188 | $this->_email->title,
189 | $this->_email->originalAuthor,
190 | $this->_email->author,
191 | $this->_email->permalink,
192 | $this->_email->text,
193 | $this->_email->contactme,
194 | $this->_email->originalText,
195 | $time
196 | );
197 |
198 | $this->_email->msgHtml = str_replace($search, $replace, $this->getTemplate('guest'));
199 | $this->_email->subject = str_replace($search, $replace, $this->_email->titleForGuest);
200 | $this->_email->altBody = "作者:".$this->_email->author."\r\n链接:".$this->_email->permalink."\r\n评论:\r\n".$this->_email->text;
201 |
202 | return $this;
203 | }
204 |
205 | /*
206 | * 发送邮件
207 | */
208 | public function sendMail()
209 | {
210 | /** 载入邮件组件 */
211 | require_once $this->_dir . '/lib/class.phpmailer.php';
212 | $mailer = new PHPMailer();
213 | $mailer->CharSet = 'UTF-8';
214 | $mailer->Encoding = 'base64';
215 |
216 | //选择发信模式
217 | switch ($this->_cfg->mode)
218 | {
219 | case 'mail':
220 | break;
221 | case 'sendmail':
222 | $mailer->IsSendmail();
223 | break;
224 | case 'smtp':
225 | $mailer->IsSMTP();
226 |
227 | if (in_array('validate', $this->_cfg->validate)) {
228 | $mailer->SMTPAuth = true;
229 | }
230 |
231 | if (in_array('ssl', $this->_cfg->validate)) {
232 | $mailer->SMTPSecure = "ssl";
233 | }
234 |
235 | $mailer->Host = $this->_cfg->host;
236 | $mailer->Port = $this->_cfg->port;
237 | $mailer->Username = $this->_cfg->user;
238 | $mailer->Password = $this->_cfg->pass;
239 |
240 | break;
241 | }
242 |
243 | $mailer->SetFrom($this->_email->from, $this->_email->fromName);
244 | $mailer->AddReplyTo($this->_email->to, $this->_email->toName);
245 | $mailer->Subject = $this->_email->subject;
246 | $mailer->AltBody = $this->_email->altBody;
247 | $mailer->MsgHTML($this->_email->msgHtml);
248 | $mailer->AddAddress($this->_email->to, $this->_email->toName);
249 |
250 | if ($result = $mailer->Send()) {
251 | $this->mailLog();
252 | } else {
253 | $this->mailLog(false, $mailer->ErrorInfo . "\r\n");
254 | $result = $mailer->ErrorInfo;
255 | }
256 |
257 | $mailer->ClearAddresses();
258 | $mailer->ClearReplyTos();
259 |
260 | return $result;
261 | }
262 |
263 | /*
264 | * 记录邮件发送日志和错误信息
265 | */
266 | public function mailLog($type = true, $content = null)
267 | {
268 | if (!$this->_isMailLog) {
269 | return false;
270 | }
271 |
272 | $fileName = $this->_dir . '/log/mailer_log.txt';
273 | if ($type) {
274 | $guest = explode('@', $this->_email->to);
275 | $guest = substr($this->_email->to, 0, 1) . '***' . $guest[1];
276 | $content = $content ? $content : "向 " . $guest . " 发送邮件成功!\r\n";
277 | }
278 |
279 | file_put_contents($fileName, $content, FILE_APPEND);
280 | }
281 |
282 | /*
283 | * 获取邮件正文模板
284 | * $author owner为博主 guest为访客
285 | */
286 | public function getTemplate($template = 'owner')
287 | {
288 | $template .= '.html';
289 | $filename = $this->_dir . '/' . $template;
290 |
291 | if (!file_exists($filename)) {
292 | throw new Typecho_Widget_Exception('模板文件' . $template . '不存在', 404);
293 | }
294 |
295 | return file_get_contents($this->_dir . '/' . $template);
296 | }
297 |
298 | /*
299 | * 验证原评论者是否接收评论
300 | */
301 | public function ban($parent, $isWrite = false)
302 | {
303 | if ($parent) {
304 | $index = ceil($parent / 500);
305 | $filename = $this->_dir . '/log/ban_' . $index . '.list';
306 |
307 | if (!file_exists($filename)) {
308 | $list = array();
309 | file_put_contents($filename, serialize($list));
310 | } else {
311 | $list = unserialize(file_get_contents($filename));
312 | }
313 |
314 | //写入记录
315 | if ($isWrite) {
316 | $list[$parent] = 1;
317 | file_put_contents($filename, serialize($list));
318 |
319 | return true;
320 | } else if (isset($list[$parent]) && $list[$parent]) {
321 | return true;
322 | }
323 | }
324 |
325 | return false;
326 | }
327 |
328 | /**
329 | * 邮件发送测试
330 | */
331 | public function testMail()
332 | {
333 | if (Typecho_Widget::widget('CommentToMail_Console')->testMailForm()->validate()) {
334 | $this->response->goBack();
335 | }
336 |
337 | $this->init();
338 | $this->_isMailLog = true;
339 | $email = $this->request->from('toName', 'to', 'title', 'content');
340 |
341 | $this->_email->from = $this->_cfg->user;
342 | $this->_email->fromName = $this->_cfg->fromName ? $this->_cfg->fromName : $this->_options->title;
343 | $this->_email->to = $email['to'] ? $email['to'] : $this->_user->mail;
344 | $this->_email->toName = $email['toName'] ? $email['toName'] : $this->_user->screenName;
345 | $this->_email->subject = $email['title'];
346 | $this->_email->altBody = $email['content'];
347 | $this->_email->msgHtml = $email['content'];
348 |
349 | $result = $this->sendMail();
350 |
351 | /** 提示信息 */
352 | $this->widget('Widget_Notice')->set(true === $result ? _t('邮件发送成功') : _t('邮件发送失败:' . $result),
353 | true === $result ? 'success' : 'notice');
354 |
355 | /** 转向原页 */
356 | $this->response->goBack();
357 | }
358 |
359 | /**
360 | * 编辑模板文件
361 | * @param $file
362 | * @throws Typecho_Widget_Exception
363 | */
364 | public function editTheme($file)
365 | {
366 | $this->init();
367 | $path = $this->_dir . '/' . $file;
368 |
369 | if (file_exists($path) && is_writeable($path)) {
370 | $handle = fopen($path, 'wb');
371 | if ($handle && fwrite($handle, $this->request->content)) {
372 | fclose($handle);
373 | $this->widget('Widget_Notice')->set(_t("文件 %s 的更改已经保存", $file), 'success');
374 | } else {
375 | $this->widget('Widget_Notice')->set(_t("文件 %s 无法被写入", $file), 'error');
376 | }
377 | $this->response->goBack();
378 | } else {
379 | throw new Typecho_Widget_Exception(_t('您编辑的模板文件不存在'));
380 | }
381 | }
382 |
383 | /**
384 | * 初始化
385 | * @return $this
386 | */
387 | public function init()
388 | {
389 | $this->_dir = dirname(__FILE__);
390 | $this->_db = Typecho_Db::get();
391 | $this->_user = $this->widget('Widget_User');
392 | $this->_options = $this->widget('Widget_Options');
393 | $this->_cfg = Helper::options()->plugin('CommentToMail');
394 | $this->mailLog(false, "开始发送邮件Action:" . $this->request->send . "\r\n");
395 | }
396 |
397 | /**
398 | * action 入口
399 | *
400 | * @access public
401 | * @return void
402 | */
403 | public function action()
404 | {
405 | $this->on($this->request->is('do=testMail'))->testMail();
406 | $this->on($this->request->is('do=editTheme'))->editTheme($this->request->edit);
407 | $this->on($this->request->is('send'))->process($this->request->send);
408 | }
409 | }
--------------------------------------------------------------------------------
/lib/class.smtp.php:
--------------------------------------------------------------------------------
1 |
10 | * @author Jim Jagielski (jimjag) ' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "
\n"; 2697 | } 2698 | } 2699 | $this->ErrorInfo = $msg; 2700 | } 2701 | 2702 | /** 2703 | * Return an RFC 822 formatted date. 2704 | * @access public 2705 | * @return string 2706 | * @static 2707 | */ 2708 | public static function rfcDate() 2709 | { 2710 | //Set the time zone to whatever the default is to avoid 500 errors 2711 | //Will default to UTC if it's not set properly in php.ini 2712 | date_default_timezone_set(@date_default_timezone_get()); 2713 | return date('D, j M Y H:i:s O'); 2714 | } 2715 | 2716 | /** 2717 | * Get the server hostname. 2718 | * Returns 'localhost.localdomain' if unknown. 2719 | * @access protected 2720 | * @return string 2721 | */ 2722 | protected function serverHostname() 2723 | { 2724 | $result = 'localhost.localdomain'; 2725 | if (!empty($this->Hostname)) { 2726 | $result = $this->Hostname; 2727 | } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { 2728 | $result = $_SERVER['SERVER_NAME']; 2729 | } elseif (function_exists('gethostname') && gethostname() !== false) { 2730 | $result = gethostname(); 2731 | } elseif (php_uname('n') !== false) { 2732 | $result = php_uname('n'); 2733 | } 2734 | return $result; 2735 | } 2736 | 2737 | /** 2738 | * Get an error message in the current language. 2739 | * @access protected 2740 | * @param string $key 2741 | * @return string 2742 | */ 2743 | protected function lang($key) 2744 | { 2745 | if (count($this->language) < 1) { 2746 | $this->setLanguage('en'); // set the default language 2747 | } 2748 | 2749 | if (isset($this->language[$key])) { 2750 | return $this->language[$key]; 2751 | } else { 2752 | return 'Language string failed to load: ' . $key; 2753 | } 2754 | } 2755 | 2756 | /** 2757 | * Check if an error occurred. 2758 | * @access public 2759 | * @return bool True if an error did occur. 2760 | */ 2761 | public function isError() 2762 | { 2763 | return ($this->error_count > 0); 2764 | } 2765 | 2766 | /** 2767 | * Ensure consistent line endings in a string. 2768 | * Changes every end of line from CRLF, CR or LF to $this->LE. 2769 | * @access public 2770 | * @param string $str String to fixEOL 2771 | * @return string 2772 | */ 2773 | public function fixEOL($str) 2774 | { 2775 | // Normalise to \n 2776 | $nstr = str_replace(array("\r\n", "\r"), "\n", $str); 2777 | // Now convert LE as needed 2778 | if ($this->LE !== "\n") { 2779 | $nstr = str_replace("\n", $this->LE, $nstr); 2780 | } 2781 | return $nstr; 2782 | } 2783 | 2784 | /** 2785 | * Add a custom header. 2786 | * $name value can be overloaded to contain 2787 | * both header name and value (name:value) 2788 | * @access public 2789 | * @param string $name Custom header name 2790 | * @param string $value Header value 2791 | * @return void 2792 | */ 2793 | public function addCustomHeader($name, $value = null) 2794 | { 2795 | if ($value === null) { 2796 | // Value passed in as name:value 2797 | $this->CustomHeader[] = explode(':', $name, 2); 2798 | } else { 2799 | $this->CustomHeader[] = array($name, $value); 2800 | } 2801 | } 2802 | 2803 | /** 2804 | * Create a message from an HTML string. 2805 | * Automatically makes modifications for inline images and backgrounds 2806 | * and creates a plain-text version by converting the HTML. 2807 | * Overwrites any existing values in $this->Body and $this->AltBody 2808 | * @access public 2809 | * @param string $message HTML message string 2810 | * @param string $basedir baseline directory for path 2811 | * @param bool $advanced Whether to use the advanced HTML to text converter 2812 | * @return string $message 2813 | */ 2814 | public function msgHTML($message, $basedir = '', $advanced = false) 2815 | { 2816 | preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); 2817 | if (isset($images[2])) { 2818 | foreach ($images[2] as $i => $url) { 2819 | // do not change urls for absolute images (thanks to corvuscorax) 2820 | if (!preg_match('#^[A-z]+://#', $url)) { 2821 | $filename = basename($url); 2822 | $directory = dirname($url); 2823 | if ($directory == '.') { 2824 | $directory = ''; 2825 | } 2826 | $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2 2827 | if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { 2828 | $basedir .= '/'; 2829 | } 2830 | if (strlen($directory) > 1 && substr($directory, -1) != '/') { 2831 | $directory .= '/'; 2832 | } 2833 | if ($this->addEmbeddedImage( 2834 | $basedir . $directory . $filename, 2835 | $cid, 2836 | $filename, 2837 | 'base64', 2838 | self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)) 2839 | ) 2840 | ) { 2841 | $message = preg_replace( 2842 | "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui", 2843 | $images[1][$i] . "=\"cid:" . $cid . "\"", 2844 | $message 2845 | ); 2846 | } 2847 | } 2848 | } 2849 | } 2850 | $this->isHTML(true); 2851 | //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 2852 | $this->Body = $this->normalizeBreaks($message); 2853 | $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 2854 | if (empty($this->AltBody)) { 2855 | $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 2856 | self::CRLF . self::CRLF; 2857 | } 2858 | return $this->Body; 2859 | } 2860 | 2861 | /** 2862 | * Convert an HTML string into plain text. 2863 | * @param string $html The HTML text to convert 2864 | * @param bool $advanced Should this use the more complex html2text converter or just a simple one? 2865 | * @return string 2866 | */ 2867 | public function html2text($html, $advanced = false) 2868 | { 2869 | if ($advanced) { 2870 | require_once 'extras/class.html2text.php'; 2871 | $h = new html2text($html); 2872 | return $h->get_text(); 2873 | } 2874 | return html_entity_decode( 2875 | trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), 2876 | ENT_QUOTES, 2877 | $this->CharSet 2878 | ); 2879 | } 2880 | 2881 | /** 2882 | * Get the MIME type for a file extension. 2883 | * @param string $ext File extension 2884 | * @access public 2885 | * @return string MIME type of file. 2886 | * @static 2887 | */ 2888 | public static function _mime_types($ext = '') 2889 | { 2890 | $mimes = array( 2891 | 'xl' => 'application/excel', 2892 | 'hqx' => 'application/mac-binhex40', 2893 | 'cpt' => 'application/mac-compactpro', 2894 | 'bin' => 'application/macbinary', 2895 | 'doc' => 'application/msword', 2896 | 'word' => 'application/msword', 2897 | 'class' => 'application/octet-stream', 2898 | 'dll' => 'application/octet-stream', 2899 | 'dms' => 'application/octet-stream', 2900 | 'exe' => 'application/octet-stream', 2901 | 'lha' => 'application/octet-stream', 2902 | 'lzh' => 'application/octet-stream', 2903 | 'psd' => 'application/octet-stream', 2904 | 'sea' => 'application/octet-stream', 2905 | 'so' => 'application/octet-stream', 2906 | 'oda' => 'application/oda', 2907 | 'pdf' => 'application/pdf', 2908 | 'ai' => 'application/postscript', 2909 | 'eps' => 'application/postscript', 2910 | 'ps' => 'application/postscript', 2911 | 'smi' => 'application/smil', 2912 | 'smil' => 'application/smil', 2913 | 'mif' => 'application/vnd.mif', 2914 | 'xls' => 'application/vnd.ms-excel', 2915 | 'ppt' => 'application/vnd.ms-powerpoint', 2916 | 'wbxml' => 'application/vnd.wap.wbxml', 2917 | 'wmlc' => 'application/vnd.wap.wmlc', 2918 | 'dcr' => 'application/x-director', 2919 | 'dir' => 'application/x-director', 2920 | 'dxr' => 'application/x-director', 2921 | 'dvi' => 'application/x-dvi', 2922 | 'gtar' => 'application/x-gtar', 2923 | 'php3' => 'application/x-httpd-php', 2924 | 'php4' => 'application/x-httpd-php', 2925 | 'php' => 'application/x-httpd-php', 2926 | 'phtml' => 'application/x-httpd-php', 2927 | 'phps' => 'application/x-httpd-php-source', 2928 | 'js' => 'application/x-javascript', 2929 | 'swf' => 'application/x-shockwave-flash', 2930 | 'sit' => 'application/x-stuffit', 2931 | 'tar' => 'application/x-tar', 2932 | 'tgz' => 'application/x-tar', 2933 | 'xht' => 'application/xhtml+xml', 2934 | 'xhtml' => 'application/xhtml+xml', 2935 | 'zip' => 'application/zip', 2936 | 'mid' => 'audio/midi', 2937 | 'midi' => 'audio/midi', 2938 | 'mp2' => 'audio/mpeg', 2939 | 'mp3' => 'audio/mpeg', 2940 | 'mpga' => 'audio/mpeg', 2941 | 'aif' => 'audio/x-aiff', 2942 | 'aifc' => 'audio/x-aiff', 2943 | 'aiff' => 'audio/x-aiff', 2944 | 'ram' => 'audio/x-pn-realaudio', 2945 | 'rm' => 'audio/x-pn-realaudio', 2946 | 'rpm' => 'audio/x-pn-realaudio-plugin', 2947 | 'ra' => 'audio/x-realaudio', 2948 | 'wav' => 'audio/x-wav', 2949 | 'bmp' => 'image/bmp', 2950 | 'gif' => 'image/gif', 2951 | 'jpeg' => 'image/jpeg', 2952 | 'jpe' => 'image/jpeg', 2953 | 'jpg' => 'image/jpeg', 2954 | 'png' => 'image/png', 2955 | 'tiff' => 'image/tiff', 2956 | 'tif' => 'image/tiff', 2957 | 'eml' => 'message/rfc822', 2958 | 'css' => 'text/css', 2959 | 'html' => 'text/html', 2960 | 'htm' => 'text/html', 2961 | 'shtml' => 'text/html', 2962 | 'log' => 'text/plain', 2963 | 'text' => 'text/plain', 2964 | 'txt' => 'text/plain', 2965 | 'rtx' => 'text/richtext', 2966 | 'rtf' => 'text/rtf', 2967 | 'xml' => 'text/xml', 2968 | 'xsl' => 'text/xml', 2969 | 'mpeg' => 'video/mpeg', 2970 | 'mpe' => 'video/mpeg', 2971 | 'mpg' => 'video/mpeg', 2972 | 'mov' => 'video/quicktime', 2973 | 'qt' => 'video/quicktime', 2974 | 'rv' => 'video/vnd.rn-realvideo', 2975 | 'avi' => 'video/x-msvideo', 2976 | 'movie' => 'video/x-sgi-movie' 2977 | ); 2978 | return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream'); 2979 | } 2980 | 2981 | /** 2982 | * Map a file name to a MIME type. 2983 | * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. 2984 | * @param string $filename A file name or full path, does not need to exist as a file 2985 | * @return string 2986 | * @static 2987 | */ 2988 | public static function filenameToType($filename) 2989 | { 2990 | //In case the path is a URL, strip any query string before getting extension 2991 | $qpos = strpos($filename, '?'); 2992 | if ($qpos !== false) { 2993 | $filename = substr($filename, 0, $qpos); 2994 | } 2995 | $pathinfo = self::mb_pathinfo($filename); 2996 | return self::_mime_types($pathinfo['extension']); 2997 | } 2998 | 2999 | /** 3000 | * Multi-byte-safe pathinfo replacement. 3001 | * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. 3002 | * Works similarly to the one in PHP >= 5.2.0 3003 | * @link http://www.php.net/manual/en/function.pathinfo.php#107461 3004 | * @param string $path A filename or path, does not need to exist as a file 3005 | * @param integer|string $options Either a PATHINFO_* constant, 3006 | * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 3007 | * @return string|array 3008 | * @static 3009 | */ 3010 | public static function mb_pathinfo($path, $options = null) 3011 | { 3012 | $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); 3013 | $m = array(); 3014 | preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); 3015 | if (array_key_exists(1, $m)) { 3016 | $ret['dirname'] = $m[1]; 3017 | } 3018 | if (array_key_exists(2, $m)) { 3019 | $ret['basename'] = $m[2]; 3020 | } 3021 | if (array_key_exists(5, $m)) { 3022 | $ret['extension'] = $m[5]; 3023 | } 3024 | if (array_key_exists(3, $m)) { 3025 | $ret['filename'] = $m[3]; 3026 | } 3027 | switch ($options) { 3028 | case PATHINFO_DIRNAME: 3029 | case 'dirname': 3030 | return $ret['dirname']; 3031 | break; 3032 | case PATHINFO_BASENAME: 3033 | case 'basename': 3034 | return $ret['basename']; 3035 | break; 3036 | case PATHINFO_EXTENSION: 3037 | case 'extension': 3038 | return $ret['extension']; 3039 | break; 3040 | case PATHINFO_FILENAME: 3041 | case 'filename': 3042 | return $ret['filename']; 3043 | break; 3044 | default: 3045 | return $ret; 3046 | } 3047 | } 3048 | 3049 | /** 3050 | * Set or reset instance properties. 3051 | * 3052 | * Usage Example: 3053 | * $page->set('X-Priority', '3'); 3054 | * 3055 | * @access public 3056 | * @param string $name 3057 | * @param mixed $value 3058 | * NOTE: will not work with arrays, there are no arrays to set/reset 3059 | * @throws phpmailerException 3060 | * @return bool 3061 | * @todo Should this not be using __set() magic function? 3062 | */ 3063 | public function set($name, $value = '') 3064 | { 3065 | try { 3066 | if (isset($this->$name)) { 3067 | $this->$name = $value; 3068 | } else { 3069 | throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL); 3070 | } 3071 | } catch (Exception $e) { 3072 | $this->setError($e->getMessage()); 3073 | if ($e->getCode() == self::STOP_CRITICAL) { 3074 | return false; 3075 | } 3076 | } 3077 | return true; 3078 | } 3079 | 3080 | /** 3081 | * Strip newlines to prevent header injection. 3082 | * @access public 3083 | * @param string $str 3084 | * @return string 3085 | */ 3086 | public function secureHeader($str) 3087 | { 3088 | return trim(str_replace(array("\r", "\n"), '', $str)); 3089 | } 3090 | 3091 | /** 3092 | * Normalize line breaks in a string. 3093 | * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. 3094 | * Defaults to CRLF (for message bodies) and preserves consecutive breaks. 3095 | * @param string $text 3096 | * @param string $breaktype What kind of line break to use, defaults to CRLF 3097 | * @return string 3098 | * @access public 3099 | * @static 3100 | */ 3101 | public static function normalizeBreaks($text, $breaktype = "\r\n") 3102 | { 3103 | return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); 3104 | } 3105 | 3106 | 3107 | /** 3108 | * Set the public and private key files and password for S/MIME signing. 3109 | * @access public 3110 | * @param string $cert_filename 3111 | * @param string $key_filename 3112 | * @param string $key_pass Password for private key 3113 | */ 3114 | public function sign($cert_filename, $key_filename, $key_pass) 3115 | { 3116 | $this->sign_cert_file = $cert_filename; 3117 | $this->sign_key_file = $key_filename; 3118 | $this->sign_key_pass = $key_pass; 3119 | } 3120 | 3121 | /** 3122 | * Quoted-Printable-encode a DKIM header. 3123 | * @access public 3124 | * @param string $txt 3125 | * @return string 3126 | */ 3127 | public function DKIM_QP($txt) 3128 | { 3129 | $line = ''; 3130 | for ($i = 0; $i < strlen($txt); $i++) { 3131 | $ord = ord($txt[$i]); 3132 | if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { 3133 | $line .= $txt[$i]; 3134 | } else { 3135 | $line .= "=" . sprintf("%02X", $ord); 3136 | } 3137 | } 3138 | return $line; 3139 | } 3140 | 3141 | /** 3142 | * Generate a DKIM signature. 3143 | * @access public 3144 | * @param string $s Header 3145 | * @throws phpmailerException 3146 | * @return string 3147 | */ 3148 | public function DKIM_Sign($s) 3149 | { 3150 | if (!defined('PKCS7_TEXT')) { 3151 | if ($this->exceptions) { 3152 | throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.'); 3153 | } 3154 | return ''; 3155 | } 3156 | $privKeyStr = file_get_contents($this->DKIM_private); 3157 | if ($this->DKIM_passphrase != '') { 3158 | $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3159 | } else { 3160 | $privKey = $privKeyStr; 3161 | } 3162 | if (openssl_sign($s, $signature, $privKey)) { 3163 | return base64_encode($signature); 3164 | } 3165 | return ''; 3166 | } 3167 | 3168 | /** 3169 | * Generate a DKIM canonicalization header. 3170 | * @access public 3171 | * @param string $s Header 3172 | * @return string 3173 | */ 3174 | public function DKIM_HeaderC($s) 3175 | { 3176 | $s = preg_replace("/\r\n\s+/", " ", $s); 3177 | $lines = explode("\r\n", $s); 3178 | foreach ($lines as $key => $line) { 3179 | list($heading, $value) = explode(":", $line, 2); 3180 | $heading = strtolower($heading); 3181 | $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces 3182 | $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value 3183 | } 3184 | $s = implode("\r\n", $lines); 3185 | return $s; 3186 | } 3187 | 3188 | /** 3189 | * Generate a DKIM canonicalization body. 3190 | * @access public 3191 | * @param string $body Message Body 3192 | * @return string 3193 | */ 3194 | public function DKIM_BodyC($body) 3195 | { 3196 | if ($body == '') { 3197 | return "\r\n"; 3198 | } 3199 | // stabilize line endings 3200 | $body = str_replace("\r\n", "\n", $body); 3201 | $body = str_replace("\n", "\r\n", $body); 3202 | // END stabilize line endings 3203 | while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { 3204 | $body = substr($body, 0, strlen($body) - 2); 3205 | } 3206 | return $body; 3207 | } 3208 | 3209 | /** 3210 | * Create the DKIM header and body in a new message header. 3211 | * @access public 3212 | * @param string $headers_line Header lines 3213 | * @param string $subject Subject 3214 | * @param string $body Body 3215 | * @return string 3216 | */ 3217 | public function DKIM_Add($headers_line, $subject, $body) 3218 | { 3219 | $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 3220 | $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 3221 | $DKIMquery = 'dns/txt'; // Query method 3222 | $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 3223 | $subject_header = "Subject: $subject"; 3224 | $headers = explode($this->LE, $headers_line); 3225 | $from_header = ''; 3226 | $to_header = ''; 3227 | $current = ''; 3228 | foreach ($headers as $header) { 3229 | if (strpos($header, 'From:') === 0) { 3230 | $from_header = $header; 3231 | $current = 'from_header'; 3232 | } elseif (strpos($header, 'To:') === 0) { 3233 | $to_header = $header; 3234 | $current = 'to_header'; 3235 | } else { 3236 | if ($current && strpos($header, ' =?') === 0) { 3237 | $current .= $header; 3238 | } else { 3239 | $current = ''; 3240 | } 3241 | } 3242 | } 3243 | $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3244 | $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3245 | $subject = str_replace( 3246 | '|', 3247 | '=7C', 3248 | $this->DKIM_QP($subject_header) 3249 | ); // Copied header fields (dkim-quoted-printable) 3250 | $body = $this->DKIM_BodyC($body); 3251 | $DKIMlen = strlen($body); // Length of body 3252 | $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body 3253 | $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";"; 3254 | $dkimhdrs = "DKIM-Signature: v=1; a=" . 3255 | $DKIMsignatureType . "; q=" . 3256 | $DKIMquery . "; l=" . 3257 | $DKIMlen . "; s=" . 3258 | $this->DKIM_selector . 3259 | ";\r\n" . 3260 | "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" . 3261 | "\th=From:To:Subject;\r\n" . 3262 | "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" . 3263 | "\tz=$from\r\n" . 3264 | "\t|$to\r\n" . 3265 | "\t|$subject;\r\n" . 3266 | "\tbh=" . $DKIMb64 . ";\r\n" . 3267 | "\tb="; 3268 | $toSign = $this->DKIM_HeaderC( 3269 | $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs 3270 | ); 3271 | $signed = $this->DKIM_Sign($toSign); 3272 | return $dkimhdrs . $signed . "\r\n"; 3273 | } 3274 | 3275 | /** 3276 | * Allows for public read access to 'to' property. 3277 | * @access public 3278 | * @return array 3279 | */ 3280 | public function getToAddresses() 3281 | { 3282 | return $this->to; 3283 | } 3284 | 3285 | /** 3286 | * Allows for public read access to 'cc' property. 3287 | * @access public 3288 | * @return array 3289 | */ 3290 | public function getCcAddresses() 3291 | { 3292 | return $this->cc; 3293 | } 3294 | 3295 | /** 3296 | * Allows for public read access to 'bcc' property. 3297 | * @access public 3298 | * @return array 3299 | */ 3300 | public function getBccAddresses() 3301 | { 3302 | return $this->bcc; 3303 | } 3304 | 3305 | /** 3306 | * Allows for public read access to 'ReplyTo' property. 3307 | * @access public 3308 | * @return array 3309 | */ 3310 | public function getReplyToAddresses() 3311 | { 3312 | return $this->ReplyTo; 3313 | } 3314 | 3315 | /** 3316 | * Allows for public read access to 'all_recipients' property. 3317 | * @access public 3318 | * @return array 3319 | */ 3320 | public function getAllRecipientAddresses() 3321 | { 3322 | return $this->all_recipients; 3323 | } 3324 | 3325 | /** 3326 | * Perform a callback. 3327 | * @param bool $isSent 3328 | * @param string $to 3329 | * @param string $cc 3330 | * @param string $bcc 3331 | * @param string $subject 3332 | * @param string $body 3333 | * @param string $from 3334 | */ 3335 | protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) 3336 | { 3337 | if (!empty($this->action_function) && is_callable($this->action_function)) { 3338 | $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); 3339 | call_user_func_array($this->action_function, $params); 3340 | } 3341 | } 3342 | } 3343 | 3344 | /** 3345 | * PHPMailer exception handler 3346 | * @package PHPMailer 3347 | */ 3348 | class phpmailerException extends Exception 3349 | { 3350 | /** 3351 | * Prettify error message output 3352 | * @return string 3353 | */ 3354 | public function errorMessage() 3355 | { 3356 | $errorMsg = '' . $this->getMessage() . "