smtp.php (9341B)
1 <?php 2 namespace Mail; 3 class Smtp { 4 public $smtp_hostname; 5 public $smtp_username; 6 public $smtp_password; 7 public $smtp_port = 25; 8 public $smtp_timeout = 5; 9 public $verp = false; 10 11 public function send() { 12 if (is_array($this->to)) { 13 $to = implode(',', $this->to); 14 } else { 15 $to = $this->to; 16 } 17 18 $boundary = '----=_NextPart_' . md5(time()); 19 20 $header = 'MIME-Version: 1.0' . PHP_EOL; 21 $header .= 'To: <' . $to . '>' . PHP_EOL; 22 $header .= 'Subject: =?UTF-8?B?' . base64_encode($this->subject) . '?=' . PHP_EOL; 23 $header .= 'Date: ' . date('D, d M Y H:i:s O') . PHP_EOL; 24 $header .= 'From: =?UTF-8?B?' . base64_encode($this->sender) . '?= <' . $this->from . '>' . PHP_EOL; 25 26 if (!$this->reply_to) { 27 $header .= 'Reply-To: =?UTF-8?B?' . base64_encode($this->sender) . '?= <' . $this->from . '>' . PHP_EOL; 28 } else { 29 $header .= 'Reply-To: =?UTF-8?B?' . base64_encode($this->reply_to) . '?= <' . $this->reply_to . '>' . PHP_EOL; 30 } 31 32 $header .= 'Return-Path: ' . $this->from . PHP_EOL; 33 $header .= 'X-Mailer: PHP/' . phpversion() . PHP_EOL; 34 $header .= 'Content-Type: multipart/mixed; boundary="' . $boundary . '"' . PHP_EOL . PHP_EOL; 35 36 if (!$this->html) { 37 $message = '--' . $boundary . PHP_EOL; 38 $message .= 'Content-Type: text/plain; charset="utf-8"' . PHP_EOL; 39 $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; 40 $message .= $this->text . PHP_EOL; 41 } else { 42 $message = '--' . $boundary . PHP_EOL; 43 $message .= 'Content-Type: multipart/alternative; boundary="' . $boundary . '_alt"' . PHP_EOL . PHP_EOL; 44 $message .= '--' . $boundary . '_alt' . PHP_EOL; 45 $message .= 'Content-Type: text/plain; charset="utf-8"' . PHP_EOL; 46 $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; 47 48 if ($this->text) { 49 $message .= $this->text . PHP_EOL; 50 } else { 51 $message .= 'This is a HTML email and your email client software does not support HTML email!' . PHP_EOL; 52 } 53 54 $message .= '--' . $boundary . '_alt' . PHP_EOL; 55 $message .= 'Content-Type: text/html; charset="utf-8"' . PHP_EOL; 56 $message .= 'Content-Transfer-Encoding: 8bit' . PHP_EOL . PHP_EOL; 57 $message .= $this->html . PHP_EOL; 58 $message .= '--' . $boundary . '_alt--' . PHP_EOL; 59 } 60 61 foreach ($this->attachments as $attachment) { 62 if (file_exists($attachment)) { 63 $handle = fopen($attachment, 'r'); 64 65 $content = fread($handle, filesize($attachment)); 66 67 fclose($handle); 68 69 $message .= '--' . $boundary . PHP_EOL; 70 $message .= 'Content-Type: application/octet-stream; name="' . basename($attachment) . '"' . PHP_EOL; 71 $message .= 'Content-Transfer-Encoding: base64' . PHP_EOL; 72 $message .= 'Content-Disposition: attachment; filename="' . basename($attachment) . '"' . PHP_EOL; 73 $message .= 'Content-ID: <' . urlencode(basename($attachment)) . '>' . PHP_EOL; 74 $message .= 'X-Attachment-Id: ' . urlencode(basename($attachment)) . PHP_EOL . PHP_EOL; 75 $message .= chunk_split(base64_encode($content)); 76 } 77 } 78 79 $message .= '--' . $boundary . '--' . PHP_EOL; 80 81 if (substr($this->smtp_hostname, 0, 3) == 'tls') { 82 $hostname = substr($this->smtp_hostname, 6); 83 } else { 84 $hostname = $this->smtp_hostname; 85 } 86 87 $handle = fsockopen($hostname, $this->smtp_port, $errno, $errstr, $this->smtp_timeout); 88 89 if (!$handle) { 90 throw new \Exception('Error: ' . $errstr . ' (' . $errno . ')'); 91 } else { 92 if (substr(PHP_OS, 0, 3) != 'WIN') { 93 socket_set_timeout($handle, $this->smtp_timeout, 0); 94 } 95 96 while ($line = fgets($handle, 515)) { 97 if (substr($line, 3, 1) == ' ') { 98 break; 99 } 100 } 101 102 fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . "\r\n"); 103 104 $reply = ''; 105 106 while ($line = fgets($handle, 515)) { 107 $reply .= $line; 108 109 //some SMTP servers respond with 220 code before responding with 250. hence, we need to ignore 220 response string 110 if (substr($reply, 0, 3) == 220 && substr($line, 3, 1) == ' ') { 111 $reply = ''; 112 continue; 113 } 114 else if (substr($line, 3, 1) == ' ') { 115 break; 116 } 117 } 118 119 if (substr($reply, 0, 3) != 250) { 120 throw new \Exception('Error: EHLO not accepted from server!'); 121 } 122 123 if (substr($this->smtp_hostname, 0, 3) == 'tls') { 124 fputs($handle, 'STARTTLS' . "\r\n"); 125 126 $reply = ''; 127 128 while ($line = fgets($handle, 515)) { 129 $reply .= $line; 130 131 if (substr($line, 3, 1) == ' ') { 132 break; 133 } 134 } 135 136 if (substr($reply, 0, 3) != 220) { 137 throw new \Exception('Error: STARTTLS not accepted from server!'); 138 } 139 140 stream_socket_enable_crypto($handle, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); 141 } 142 143 if (!empty($this->smtp_username) && !empty($this->smtp_password)) { 144 fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . "\r\n"); 145 146 $reply = ''; 147 148 while ($line = fgets($handle, 515)) { 149 $reply .= $line; 150 151 if (substr($line, 3, 1) == ' ') { 152 break; 153 } 154 } 155 156 if (substr($reply, 0, 3) != 250) { 157 throw new \Exception('Error: EHLO not accepted from server!'); 158 } 159 160 fputs($handle, 'AUTH LOGIN' . "\r\n"); 161 162 $reply = ''; 163 164 while ($line = fgets($handle, 515)) { 165 $reply .= $line; 166 167 if (substr($line, 3, 1) == ' ') { 168 break; 169 } 170 } 171 172 if (substr($reply, 0, 3) != 334) { 173 throw new \Exception('Error: AUTH LOGIN not accepted from server!'); 174 } 175 176 fputs($handle, base64_encode($this->smtp_username) . "\r\n"); 177 178 $reply = ''; 179 180 while ($line = fgets($handle, 515)) { 181 $reply .= $line; 182 183 if (substr($line, 3, 1) == ' ') { 184 break; 185 } 186 } 187 188 if (substr($reply, 0, 3) != 334) { 189 throw new \Exception('Error: Username not accepted from server!'); 190 } 191 192 fputs($handle, base64_encode($this->smtp_password) . "\r\n"); 193 194 $reply = ''; 195 196 while ($line = fgets($handle, 515)) { 197 $reply .= $line; 198 199 if (substr($line, 3, 1) == ' ') { 200 break; 201 } 202 } 203 204 if (substr($reply, 0, 3) != 235) { 205 throw new \Exception('Error: Password not accepted from server!'); 206 } 207 } else { 208 fputs($handle, 'HELO ' . getenv('SERVER_NAME') . "\r\n"); 209 210 $reply = ''; 211 212 while ($line = fgets($handle, 515)) { 213 $reply .= $line; 214 215 if (substr($line, 3, 1) == ' ') { 216 break; 217 } 218 } 219 220 if (substr($reply, 0, 3) != 250) { 221 throw new \Exception('Error: HELO not accepted from server!'); 222 } 223 } 224 225 if ($this->verp) { 226 fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . "\r\n"); 227 } else { 228 fputs($handle, 'MAIL FROM: <' . $this->from . '>' . "\r\n"); 229 } 230 231 $reply = ''; 232 233 while ($line = fgets($handle, 515)) { 234 $reply .= $line; 235 236 if (substr($line, 3, 1) == ' ') { 237 break; 238 } 239 } 240 241 if (substr($reply, 0, 3) != 250) { 242 throw new \Exception('Error: MAIL FROM not accepted from server!'); 243 } 244 245 if (!is_array($this->to)) { 246 fputs($handle, 'RCPT TO: <' . $this->to . '>' . "\r\n"); 247 248 $reply = ''; 249 250 while ($line = fgets($handle, 515)) { 251 $reply .= $line; 252 253 if (substr($line, 3, 1) == ' ') { 254 break; 255 } 256 } 257 258 if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { 259 throw new \Exception('Error: RCPT TO not accepted from server!'); 260 } 261 } else { 262 foreach ($this->to as $recipient) { 263 fputs($handle, 'RCPT TO: <' . $recipient . '>' . "\r\n"); 264 265 $reply = ''; 266 267 while ($line = fgets($handle, 515)) { 268 $reply .= $line; 269 270 if (substr($line, 3, 1) == ' ') { 271 break; 272 } 273 } 274 275 if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) { 276 throw new \Exception('Error: RCPT TO not accepted from server!'); 277 } 278 } 279 } 280 281 fputs($handle, 'DATA' . "\r\n"); 282 283 $reply = ''; 284 285 while ($line = fgets($handle, 515)) { 286 $reply .= $line; 287 288 if (substr($line, 3, 1) == ' ') { 289 break; 290 } 291 } 292 293 if (substr($reply, 0, 3) != 354) { 294 throw new \Exception('Error: DATA not accepted from server!'); 295 } 296 297 // According to rfc 821 we should not send more than 1000 including the CRLF 298 $message = str_replace("\r\n", "\n", $header . $message); 299 $message = str_replace("\r", "\n", $message); 300 301 $lines = explode("\n", $message); 302 303 foreach ($lines as $line) { 304 $results = str_split($line, 998); 305 306 foreach ($results as $result) { 307 if (substr(PHP_OS, 0, 3) != 'WIN') { 308 fputs($handle, $result . "\r\n"); 309 } else { 310 fputs($handle, str_replace("\n", "\r\n", $result) . "\r\n"); 311 } 312 } 313 } 314 315 fputs($handle, '.' . "\r\n"); 316 317 $reply = ''; 318 319 while ($line = fgets($handle, 515)) { 320 $reply .= $line; 321 322 if (substr($line, 3, 1) == ' ') { 323 break; 324 } 325 } 326 327 if (substr($reply, 0, 3) != 250) { 328 throw new \Exception('Error: DATA not accepted from server!'); 329 } 330 331 fputs($handle, 'QUIT' . "\r\n"); 332 333 $reply = ''; 334 335 while ($line = fgets($handle, 515)) { 336 $reply .= $line; 337 338 if (substr($line, 3, 1) == ' ') { 339 break; 340 } 341 } 342 343 if (substr($reply, 0, 3) != 221) { 344 throw new \Exception('Error: QUIT not accepted from server!'); 345 } 346 347 fclose($handle); 348 } 349 } 350 }