1 | <?php |
||||
2 | /* Divine CMS - Open source CMS for widespread use. |
||||
3 | Copyright (c) 2019 Mykola Burakov ([email protected]) |
||||
4 | |||||
5 | See SOURCE.txt for other and additional information. |
||||
6 | |||||
7 | This file is part of Divine CMS. |
||||
8 | |||||
9 | This program is free software: you can redistribute it and/or modify |
||||
10 | it under the terms of the GNU General Public License as published by |
||||
11 | the Free Software Foundation, either version 3 of the License, or |
||||
12 | (at your option) any later version. |
||||
13 | |||||
14 | This program is distributed in the hope that it will be useful, |
||||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
17 | GNU General Public License for more details. |
||||
18 | |||||
19 | You should have received a copy of the GNU General Public License |
||||
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
||||
21 | |||||
22 | namespace Divine\Engine\Library; |
||||
23 | |||||
24 | class Mail |
||||
25 | { |
||||
26 | protected $to; |
||||
27 | protected $from; |
||||
28 | protected $sender; |
||||
29 | protected $reply_to; |
||||
30 | protected $subject; |
||||
31 | protected $text; |
||||
32 | protected $html; |
||||
33 | protected $attachments = array(); |
||||
34 | public $protocol = 'mail'; |
||||
35 | public $smtp_hostname; |
||||
36 | public $smtp_username; |
||||
37 | public $smtp_password; |
||||
38 | public $smtp_port = 25; |
||||
39 | public $smtp_timeout = 5; |
||||
40 | public $verp = false; |
||||
41 | public $parameter = ''; |
||||
42 | |||||
43 | public function __construct($config = array()) |
||||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||||
44 | { |
||||
45 | foreach ($config as $key => $value) { |
||||
46 | $this->$key = $value; |
||||
47 | } |
||||
48 | } |
||||
49 | |||||
50 | public function setTo($to) |
||||
51 | { |
||||
52 | $this->to = $to; |
||||
53 | } |
||||
54 | |||||
55 | public function setFrom($from) |
||||
0 ignored issues
–
show
The parameter
$from is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
56 | { |
||||
57 | // http://forum.opencart-russia.ru/threads/nastrojka-pochty-gmail-ocstore-3-0-2-0.8374/#post-61155 |
||||
58 | // $this->from = $from; |
||||
59 | $this->from = '[email protected]'; |
||||
60 | } |
||||
61 | |||||
62 | public function setSender($sender) |
||||
63 | { |
||||
64 | $this->sender = $sender; |
||||
65 | } |
||||
66 | |||||
67 | public function setReplyTo($reply_to) |
||||
68 | { |
||||
69 | $this->reply_to = $reply_to; |
||||
70 | } |
||||
71 | |||||
72 | public function setSubject($subject) |
||||
73 | { |
||||
74 | $this->subject = $subject; |
||||
75 | } |
||||
76 | |||||
77 | public function setText($text) |
||||
78 | { |
||||
79 | $this->text = $text; |
||||
80 | } |
||||
81 | |||||
82 | public function setHtml($html) |
||||
83 | { |
||||
84 | $this->html = $html; |
||||
85 | } |
||||
86 | |||||
87 | public function addAttachment($filename) |
||||
88 | { |
||||
89 | $this->attachments[] = $filename; |
||||
90 | } |
||||
91 | |||||
92 | public function send() |
||||
93 | { |
||||
94 | if (!$this->to) { |
||||
95 | throw new \Exception('Error: E-Mail to required!'); |
||||
96 | } |
||||
97 | |||||
98 | if (!$this->from) { |
||||
99 | throw new \Exception('Error: E-Mail from required!'); |
||||
100 | } |
||||
101 | |||||
102 | if (!$this->sender) { |
||||
103 | throw new \Exception('Error: E-Mail sender required!'); |
||||
104 | } |
||||
105 | |||||
106 | if (!$this->subject) { |
||||
107 | throw new \Exception('Error: E-Mail subject required!'); |
||||
108 | } |
||||
109 | |||||
110 | if ((!$this->text) && (!$this->html)) { |
||||
111 | throw new \Exception('Error: E-Mail message required!'); |
||||
112 | } |
||||
113 | |||||
114 | if (is_array($this->to)) { |
||||
115 | $to = implode(',', $this->to); |
||||
116 | } else { |
||||
117 | $to = $this->to; |
||||
118 | } |
||||
119 | |||||
120 | $boundary = '----=_NextPart_' . md5(time()); |
||||
121 | |||||
122 | $header = 'MIME-Version: 1.0' . PHP_EOL; |
||||
123 | |||||
124 | if ($this->protocol != 'mail') { |
||||
125 | $header .= 'To: <' . $to . '>' . PHP_EOL; |
||||
126 | $header .= 'Subject: =?UTF-8?B?' . base64_encode($this->subject) . '?=' . PHP_EOL; |
||||
127 | } |
||||
128 | |||||
129 | $header .= 'Date: ' . date('D, d M Y H:i:s O') . PHP_EOL; |
||||
130 | $header .= 'From: =?UTF-8?B?' . base64_encode($this->sender) . '?= <' . $this->from . '>' . PHP_EOL; |
||||
131 | |||||
132 | if (!$this->reply_to) { |
||||
133 | $header .= 'Reply-To: =?UTF-8?B?' . base64_encode($this->sender) . '?= <' . $this->from . '>' . PHP_EOL; |
||||
134 | } else { |
||||
135 | $header .= 'Reply-To: =?UTF-8?B?' . base64_encode($this->reply_to) . '?= <' . $this->reply_to . '>' . PHP_EOL; |
||||
136 | } |
||||
137 | |||||
138 | $header .= 'Return-Path: ' . $this->from . PHP_EOL; |
||||
139 | $header .= 'X-Mailer: PHP/' . phpversion() . PHP_EOL; |
||||
140 | $header .= 'Content-Type: multipart/mixed; boundary="' . $boundary . '"' . PHP_EOL . PHP_EOL; |
||||
141 | |||||
142 | if (!$this->html) { |
||||
143 | $message = '--' . $boundary . PHP_EOL; |
||||
144 | $message .= 'Content-Type: text/plain; charset="utf-8"' . PHP_EOL; |
||||
145 | $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; |
||||
146 | $message .= $this->text . PHP_EOL; |
||||
147 | } else { |
||||
148 | $message = '--' . $boundary . PHP_EOL; |
||||
149 | $message .= 'Content-Type: multipart/alternative; boundary="' . $boundary . '_alt"' . PHP_EOL . PHP_EOL; |
||||
150 | $message .= '--' . $boundary . '_alt' . PHP_EOL; |
||||
151 | $message .= 'Content-Type: text/plain; charset="utf-8"' . PHP_EOL; |
||||
152 | $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; |
||||
153 | |||||
154 | if ($this->text) { |
||||
155 | $message .= $this->text . PHP_EOL; |
||||
156 | } else { |
||||
157 | $message .= 'This is a HTML email and your email client software does not support HTML email!' . PHP_EOL; |
||||
158 | } |
||||
159 | |||||
160 | $message .= '--' . $boundary . '_alt' . PHP_EOL; |
||||
161 | $message .= 'Content-Type: text/html; charset="utf-8"' . PHP_EOL; |
||||
162 | $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; |
||||
163 | $message .= $this->html . PHP_EOL; |
||||
164 | $message .= '--' . $boundary . '_alt--' . PHP_EOL; |
||||
165 | } |
||||
166 | |||||
167 | foreach ($this->attachments as $attachment) { |
||||
168 | if (file_exists($attachment)) { |
||||
169 | $handle = fopen($attachment, 'r'); |
||||
170 | |||||
171 | $content = fread($handle, filesize($attachment)); |
||||
0 ignored issues
–
show
It seems like
$handle can also be of type false ; however, parameter $handle of fread() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
172 | |||||
173 | fclose($handle); |
||||
0 ignored issues
–
show
It seems like
$handle can also be of type false ; however, parameter $handle of fclose() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
174 | |||||
175 | $message .= '--' . $boundary . PHP_EOL; |
||||
176 | $message .= 'Content-Type: application/octet-stream; name="' . basename($attachment) . '"' . PHP_EOL; |
||||
177 | $message .= 'Content-Transfer-Encoding: base64' . PHP_EOL; |
||||
178 | $message .= 'Content-Disposition: attachment; filename="' . basename($attachment) . '"' . PHP_EOL; |
||||
179 | $message .= 'Content-ID: <' . urlencode(basename($attachment)) . '>' . PHP_EOL; |
||||
180 | $message .= 'X-Attachment-Id: ' . urlencode(basename($attachment)) . PHP_EOL . PHP_EOL; |
||||
181 | $message .= chunk_split(base64_encode($content)); |
||||
182 | } |
||||
183 | } |
||||
184 | |||||
185 | $message .= '--' . $boundary . '--' . PHP_EOL; |
||||
186 | |||||
187 | if ($this->protocol == 'mail') { |
||||
188 | ini_set('sendmail_from', $this->from); |
||||
189 | |||||
190 | if ($this->parameter) { |
||||
191 | mail($to, '=?UTF-8?B?' . base64_encode($this->subject) . '?=', $message, $header, $this->parameter); |
||||
192 | } else { |
||||
193 | mail($to, '=?UTF-8?B?' . base64_encode($this->subject) . '?=', $message, $header); |
||||
194 | } |
||||
195 | } elseif ($this->protocol == 'smtp') { |
||||
196 | if (substr($this->smtp_hostname, 0, 3) == 'tls') { |
||||
197 | $hostname = substr($this->smtp_hostname, 6); |
||||
198 | } else { |
||||
199 | $hostname = $this->smtp_hostname; |
||||
200 | } |
||||
201 | |||||
202 | $handle = fsockopen($hostname, $this->smtp_port, $errno, $errstr, $this->smtp_timeout); |
||||
203 | |||||
204 | if (!$handle) { |
||||
0 ignored issues
–
show
|
|||||
205 | throw new \Exception('Error: ' . $errstr . ' (' . $errno . ')'); |
||||
206 | } else { |
||||
207 | if (substr(PHP_OS, 0, 3) != 'WIN') { |
||||
208 | socket_set_timeout($handle, $this->smtp_timeout, 0); |
||||
209 | } |
||||
210 | |||||
211 | |||||
212 | while ($line = fgets($handle, 515)) { |
||||
213 | if (substr($line, 3, 1) == ' ') { |
||||
214 | break; |
||||
215 | } |
||||
216 | } |
||||
217 | |||||
218 | fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . "\r\n"); |
||||
219 | |||||
220 | $reply = ''; |
||||
221 | |||||
222 | while ($line = fgets($handle, 515)) { |
||||
223 | $reply .= $line; |
||||
224 | |||||
225 | //some SMTP servers respond with 220 code before responding with 250. hence, we need to ignore 220 response string |
||||
226 | if (substr($reply, 0, 3) == 220 && substr($line, 3, 1) == ' ') { |
||||
227 | $reply = ''; |
||||
228 | continue; |
||||
229 | } elseif (substr($line, 3, 1) == ' ') { |
||||
230 | break; |
||||
231 | } |
||||
232 | } |
||||
233 | |||||
234 | if (substr($reply, 0, 3) != 250) { |
||||
235 | throw new \Exception('Error: EHLO not accepted from server!'); |
||||
236 | } |
||||
237 | |||||
238 | if (substr($this->smtp_hostname, 0, 3) == 'tls') { |
||||
239 | fputs($handle, 'STARTTLS' . "\r\n"); |
||||
240 | |||||
241 | $reply = ''; |
||||
242 | |||||
243 | while ($line = fgets($handle, 515)) { |
||||
244 | $reply .= $line; |
||||
245 | |||||
246 | if (substr($line, 3, 1) == ' ') { |
||||
247 | break; |
||||
248 | } |
||||
249 | } |
||||
250 | |||||
251 | if (substr($reply, 0, 3) != 220) { |
||||
252 | throw new \Exception('Error: STARTTLS not accepted from server!'); |
||||
253 | } |
||||
254 | |||||
255 | stream_socket_enable_crypto($handle, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); |
||||
256 | } |
||||
257 | |||||
258 | if (!empty($this->smtp_username) && !empty($this->smtp_password)) { |
||||
259 | fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . "\r\n"); |
||||
260 | |||||
261 | $reply = ''; |
||||
262 | |||||
263 | while ($line = fgets($handle, 515)) { |
||||
264 | $reply .= $line; |
||||
265 | |||||
266 | if (substr($line, 3, 1) == ' ') { |
||||
267 | break; |
||||
268 | } |
||||
269 | } |
||||
270 | |||||
271 | if (substr($reply, 0, 3) != 250) { |
||||
272 | throw new \Exception('Error: EHLO not accepted from server!'); |
||||
273 | } |
||||
274 | |||||
275 | fputs($handle, 'AUTH LOGIN' . "\r\n"); |
||||
276 | |||||
277 | $reply = ''; |
||||
278 | |||||
279 | while ($line = fgets($handle, 515)) { |
||||
280 | $reply .= $line; |
||||
281 | |||||
282 | if (substr($line, 3, 1) == ' ') { |
||||
283 | break; |
||||
284 | } |
||||
285 | } |
||||
286 | |||||
287 | if (substr($reply, 0, 3) != 334) { |
||||
288 | throw new \Exception('Error: AUTH LOGIN not accepted from server!'); |
||||
289 | } |
||||
290 | |||||
291 | fputs($handle, base64_encode($this->smtp_username) . "\r\n"); |
||||
292 | |||||
293 | $reply = ''; |
||||
294 | |||||
295 | while ($line = fgets($handle, 515)) { |
||||
296 | $reply .= $line; |
||||
297 | |||||
298 | if (substr($line, 3, 1) == ' ') { |
||||
299 | break; |
||||
300 | } |
||||
301 | } |
||||
302 | |||||
303 | if (substr($reply, 0, 3) != 334) { |
||||
304 | throw new \Exception('Error: Username not accepted from server!'); |
||||
305 | } |
||||
306 | |||||
307 | fputs($handle, base64_encode($this->smtp_password) . "\r\n"); |
||||
308 | |||||
309 | $reply = ''; |
||||
310 | |||||
311 | while ($line = fgets($handle, 515)) { |
||||
312 | $reply .= $line; |
||||
313 | |||||
314 | if (substr($line, 3, 1) == ' ') { |
||||
315 | break; |
||||
316 | } |
||||
317 | } |
||||
318 | |||||
319 | if (substr($reply, 0, 3) != 235) { |
||||
320 | throw new \Exception('Error: Password not accepted from server!'); |
||||
321 | } |
||||
322 | } else { |
||||
323 | fputs($handle, 'HELO ' . getenv('SERVER_NAME') . "\r\n"); |
||||
324 | |||||
325 | $reply = ''; |
||||
326 | |||||
327 | while ($line = fgets($handle, 515)) { |
||||
328 | $reply .= $line; |
||||
329 | |||||
330 | if (substr($line, 3, 1) == ' ') { |
||||
331 | break; |
||||
332 | } |
||||
333 | } |
||||
334 | |||||
335 | if (substr($reply, 0, 3) != 250) { |
||||
336 | throw new \Exception('Error: HELO not accepted from server!'); |
||||
337 | } |
||||
338 | } |
||||
339 | |||||
340 | if ($this->verp) { |
||||
341 | fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . "\r\n"); |
||||
342 | } else { |
||||
343 | fputs($handle, 'MAIL FROM: <' . $this->from . '>' . "\r\n"); |
||||
344 | } |
||||
345 | |||||
346 | $reply = ''; |
||||
347 | |||||
348 | while ($line = fgets($handle, 515)) { |
||||
349 | $reply .= $line; |
||||
350 | |||||
351 | if (substr($line, 3, 1) == ' ') { |
||||
352 | break; |
||||
353 | } |
||||
354 | } |
||||
355 | |||||
356 | if (substr($reply, 0, 3) != 250) { |
||||
357 | throw new \Exception('Error: MAIL FROM not accepted from server!'); |
||||
358 | } |
||||
359 | |||||
360 | if (!is_array($this->to)) { |
||||
361 | fputs($handle, 'RCPT TO: <' . $this->to . '>' . "\r\n"); |
||||
362 | |||||
363 | $reply = ''; |
||||
364 | |||||
365 | while ($line = fgets($handle, 515)) { |
||||
366 | $reply .= $line; |
||||
367 | |||||
368 | if (substr($line, 3, 1) == ' ') { |
||||
369 | break; |
||||
370 | } |
||||
371 | } |
||||
372 | |||||
373 | if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { |
||||
374 | throw new \Exception('Error: RCPT TO not accepted from server!'); |
||||
375 | } |
||||
376 | } else { |
||||
377 | foreach ($this->to as $recipient) { |
||||
378 | fputs($handle, 'RCPT TO: <' . $recipient . '>' . "\r\n"); |
||||
379 | |||||
380 | $reply = ''; |
||||
381 | |||||
382 | while ($line = fgets($handle, 515)) { |
||||
383 | $reply .= $line; |
||||
384 | |||||
385 | if (substr($line, 3, 1) == ' ') { |
||||
386 | break; |
||||
387 | } |
||||
388 | } |
||||
389 | |||||
390 | if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { |
||||
391 | throw new \Exception('Error: RCPT TO not accepted from server!'); |
||||
392 | } |
||||
393 | } |
||||
394 | } |
||||
395 | |||||
396 | fputs($handle, 'DATA' . "\r\n"); |
||||
397 | |||||
398 | $reply = ''; |
||||
399 | |||||
400 | while ($line = fgets($handle, 515)) { |
||||
401 | $reply .= $line; |
||||
402 | |||||
403 | if (substr($line, 3, 1) == ' ') { |
||||
404 | break; |
||||
405 | } |
||||
406 | } |
||||
407 | |||||
408 | if (substr($reply, 0, 3) != 354) { |
||||
409 | throw new \Exception('Error: DATA not accepted from server!'); |
||||
410 | } |
||||
411 | |||||
412 | // According to rfc 821 we should not send more than 1000 including the CRLF |
||||
413 | $message = str_replace("\r\n", "\n", $header . $message); |
||||
414 | $message = str_replace("\r", "\n", $message); |
||||
415 | |||||
416 | $lines = explode("\n", $message); |
||||
417 | |||||
418 | foreach ($lines as $line) { |
||||
419 | $results = str_split($line, 998); |
||||
420 | |||||
421 | foreach ($results as $result) { |
||||
422 | if (substr(PHP_OS, 0, 3) != 'WIN') { |
||||
423 | fputs($handle, $result . "\r\n"); |
||||
424 | } else { |
||||
425 | fputs($handle, str_replace("\n", "\r\n", $result) . "\r\n"); |
||||
426 | } |
||||
427 | } |
||||
428 | } |
||||
429 | |||||
430 | fputs($handle, '.' . "\r\n"); |
||||
431 | |||||
432 | $reply = ''; |
||||
433 | |||||
434 | while ($line = fgets($handle, 515)) { |
||||
435 | $reply .= $line; |
||||
436 | |||||
437 | if (substr($line, 3, 1) == ' ') { |
||||
438 | break; |
||||
439 | } |
||||
440 | } |
||||
441 | |||||
442 | if (substr($reply, 0, 3) != 250) { |
||||
443 | throw new \Exception('Error: DATA not accepted from server!'); |
||||
444 | } |
||||
445 | |||||
446 | fputs($handle, 'QUIT' . "\r\n"); |
||||
447 | |||||
448 | $reply = ''; |
||||
449 | |||||
450 | while ($line = fgets($handle, 515)) { |
||||
451 | $reply .= $line; |
||||
452 | |||||
453 | if (substr($line, 3, 1) == ' ') { |
||||
454 | break; |
||||
455 | } |
||||
456 | } |
||||
457 | |||||
458 | if (substr($reply, 0, 3) != 221) { |
||||
459 | throw new \Exception('Error: QUIT not accepted from server!'); |
||||
460 | } |
||||
461 | |||||
462 | fclose($handle); |
||||
463 | } |
||||
464 | } |
||||
465 | } |
||||
466 | } |
||||
467 |