Skip to content

Instantly share code, notes, and snippets.

@xeoncross
Created March 8, 2012 17:31
Show Gist options
  • Save xeoncross/2002233 to your computer and use it in GitHub Desktop.
Save xeoncross/2002233 to your computer and use it in GitHub Desktop.
Tiny, SMTP client in PHP
<?php
/*
This is a very tiny proof-of-concept SMTP client. Currently it's over 320 characters (if the var names are compressed). Think you can build one smaller?
*/
ini_set('default_socket_timeout', 3);
$user = '[email protected]';
$pass = '';
$host = 'ssl://smtp.gmail.com';
//$host = 'ssl://email-smtp.us-east-1.amazonaws.com'; //Amazon SES
$port = 465;
$to = '[email protected]';
$from = '[email protected]';
$template = "Subject: =?UTF-8?B?VGVzdCBFbWFpbA==?=\r\n"
."To: <[email protected]>\r\n"
."From: [email protected]\r\n"
."MIME-Version: 1.0\r\n"
."Content-Type: text/html; charset=utf-8\r\n"
."Content-Transfer-Encoding: base64\r\n\r\n"
."PGgxPlRlc3QgRW1haWw8L2gxPjxwPkhlbGxvIFRoZXJlITwvcD4=\r\n.";
if(smtp_mail($to, $from, $template, $user, $pass, $host, $port))
{
echo "Mail sent\n\n";
}
else
{
echo "Some error occured\n\n";
}
<?php
/**
* Connecto to an SMTP and send the given message
*/
function smtp_mail($to, $from, $message, $user, $pass, $host, $port)
{
if ($h = fsockopen($host, $port))
{
$data = array(
0,
"EHLO $host",
'AUTH LOGIN',
base64_encode($user),
base64_encode($pass),
"MAIL FROM: <$from>",
"RCPT TO: <$to>",
'DATA',
$message
);
foreach($data as $c)
{
$c && fwrite($h, "$c\r\n");
while(substr(fgets($h, 256), 3, 1) != ' '){}
}
fwrite($h, "QUIT\r\n");
return fclose($h);
}
}
@divinity76
Copy link

divinity76 commented May 27, 2017

thanks! very easy to understand. (ahm, except the foreach loop)

ini_set('default_socket_timeout', 3); , the 3 should actually be quoted like '3' , else PHP7 will complain about incompatible types (string vs int) in php7's strict_mode - and it is actually supposed to be a string even in PHP5, its just that PHP5 silently convert it (as will php7 when strict_mode is disabled)

also.. this won't work by default on gmail anymore, see https://security.googleblog.com/2014/04/new-security-measures-will-affect-older.html for info.

and here's something i threw together to debug why it wasn't working on gmail...:

$user = '???';
$pass = '???';
$host = 'ssl://smtp.gmail.com';
// $host = 'ssl://email-smtp.us-east-1.amazonaws.com'; //Amazon SES
$port = 465;
$to = '???';
$from = '???';
$subject = "test";
$body = 'test<br/>test';

var_dump(smtp_mail2 ( $to, $from, $subject, $body, $user, $pass, $host, $port ));
function smtp_mail2($to, $from, $subject, $body, $user, $pass, $host, $port) {
	$message = implode ( "\r\n", array (
			'Subject: =?UTF-8?B?' . base64_encode ( $subject ) . '?=',
			'To: <' . $to . '>',
			'From: ' . $from,
			'MIME-Version: 1.0',
			'Content-Type: text/html; charset=utf-8',
			'Content-Transfer-Encoding: base64',
			"\r\n" . base64_encode ( $body )  // the (double) \r\n separate request headers from request body.
	) );
	$data = array (
			"EHLO " . parse_url ( $host, PHP_URL_HOST ),
			'AUTH LOGIN',
			base64_encode ( $user ),
			base64_encode ( $pass ),
			"MAIL FROM: <$from>",
			"RCPT TO: <$to>",
			'DATA',
			$message 
	);
	// $sock = fsockopen ( $host, $port, $errno, $errstr );
	$sslstuff = array (
			"ssl" => array (
					"verify_peer" => false,
					"verify_peer_name" => false 
				// "cafile" => "/path/to/bundle/cacert.pem",
				// "verify_peer"=> true,
				// "verify_peer_name"=> true,
			) 
	);
	$socket_write_all = function ($sock, $data) {
		$len = strlen ( $data );
		while ( $len > 0 ) {
			echo "writing...";
			$written = fwrite ( $sock, $data );
			if ($written === false) {
				throw new RuntimeException ( 'socket_write failed. errno: ' . socket_last_error ( $sock ) . '. error: ' . socket_strerror ( socket_last_error ( $sock ) ) );
			}
			$len -= $written;
			$data = substr ( $data, $written );
		}
		return; // all data written
	};
	$sock = stream_socket_client ( $host . ':' . $port, $errno, $errstr, 10, STREAM_CLIENT_CONNECT, stream_context_create ( $sslstuff ) );
	if (false === $sock) {
		throw new \RuntimeException ( 'failed to connect! errno: ' . $errno . '. errstr: ' . $errstr );
	}
	$debugMessageLog = array ();
	stream_set_blocking ( $sock, true );
	$welcomeMessage = fgets ( $sock );
	$debugMessageLog [] = array (
			'connect',
			$welcomeMessage 
	);
	// var_dump ( $welcomeMessage );
	foreach ( $data as $message ) {
		$socket_write_all ( $sock, $message . "\r\n" );
		$tmpFullResponse = '';
		while ( false !== ($tmpResponse = fgets ( $sock )) ) {
			var_dump ( $tmpResponse );
			$tmpFullResponse .= $tmpResponse;
		}
		$debugMessageLog [] = array (
				$message,
				$tmpFullResponse 
		);
		unset ( $tmpFullResponse, $tmpResponse, $message );
	}
	$socket_write_all ( $sock, "QUIT\r\n" );
	var_dump ( $debugMessageLog );
	return fclose ( $sock );

}

@jchook
Copy link

jchook commented Dec 28, 2019

Really neat code golf here, but do not use this in production.

  • Requires pre-quoted mailboxes
  • No dot stuffing on message content
  • Possible infinite loop on the while line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment