Skip to content

Instantly share code, notes, and snippets.

@IzzySoft
Created October 27, 2017 07:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IzzySoft/4b1482b1a441393b447ce57874840bfc to your computer and use it in GitHub Desktop.
Save IzzySoft/4b1482b1a441393b447ce57874840bfc to your computer and use it in GitHub Desktop.
<?php
############################################################################
# smtp mailer #
# Written by Itzchak Rehberg <izzysoft@qumran.org> #
# ------------------------------------------------ #
# This module should replace php´s mail() function. It is fully syntax #
# compatible. In addition, when an error occures, a detailed error info #
# is stored in the array $smtp->err #
# NOTE: Being intended to speak to your "Intranet MailServer", it´s kept #
# simple: no TLS etc., just pure "insecure" SMTP. #
# ------------------------------------------------------------------------ #
# It is recommended to define YOUR local settings. Please, do so only in #
# the "defaults" section - unless you are really sure what you are doing! #
############################################################################
/** Extended SMTP module
* @package Api
* @class smtp
* @author Itzchak Rehberg (info@izzysoft.de)
* @copyright (c) 2001-2017 by Itzchak Rehberg
*/
class smtp {
var $err = array("code"=>"000","msg"=>"init","desc"=>"");
var $to_res = array();
// defaults
protected $default_domain = '.qumran.org';
protected $fromuser = 'izzy@qumran.org';
protected $smtpserver = 'jona.qumran.org';
/** Class initialization
* @constructor smtp
* @param optional string hostname hostname to identify with at the SMTP server
* @param optional string fromuser default sender (and "Return-Path")
* @param optional string server smtp server to use
* @param optional integer port port of this smtp server (default: 25)
* @param optional string charset charset for the message (default: iso-8859-15)
*/
function __construct($hostname="",$fromuser="",$server="",$port=25,$charset="utf-8") {
( empty($hostname) ) ? $this->hostname = gethostname() : $this->hostname = $hostname;
if ( !strpos($this->hostname,".") ) $this->hostname .= $this->default_domain;
if ( !empty($fromuser) ) $this->fromuser = $fromuser;
if ( !preg_match('![ <]!',$this->fromuser) ) $this->fromuser = '<'.$this->fromuser.'>';
if ( !empty($server) ) $this->smtpserver = $server;
$this->port = $port;
$this->mimeversion = "1.0";
$this->charset = $charset;
// don't change anything below here - unless you know what you're doing!
$this->err["code"] = "000";
$this->err["msg"] = "init";
$this->err["desc"] = "Session is just initializing.";
}
// ==================================================[ some sub-functions ]===
/** read response from socket */
protected function socket2msg($socket) {
$followme = "-"; //$this->err["msg"] = "";
do {
$rmsg = fgets($socket,255);
// echo "< $rmsg";
if (substr($rmsg,0,1) != 2 && substr($rmsg,0,1) != 3) { // session end
$rc = fclose($socket);
if (substr($rmsg,0,1)==4 || substr($rmsg,0,1)==5) { // error reported
$this->err["code"] = substr($rmsg,0,3);
$this->err["msg"] = trim(substr($rmsg,4));
$this->err["desc"] = "";
}
return false;
}
$this->err["code"] = substr($rmsg,0,3);
$followme = substr($rmsg,3,1);
$this->err["msg"] = trim(substr($rmsg,4));
$this->err["desc"] = "";
if ($followme == " ") { break; }
} while ($followme == "-");
return true;
}
/** Send a single line to the socket */
protected function msg2socket($socket,$message) {
// echo "> $message";
$rc = fputs($socket,"$message");
if (!$rc) {
$this->err["code"] = "420";
$this->err["msg"] = "lost connection";
$this->err["desc"] = "Lost connection to smtp server.";
$rc = fclose($socket);
return false;
}
return true;
}
/** send a multi-line text to the socket via self::msg2socket() */
protected function put2socket($socket,$message) {
$this->err["code"] = "000";
$this->err["msg"] = "init";
$this->err["desc"] = "The session is still to be initiated.";
$pos = strpos($message,"\n");
if (!is_int($pos)) { // no new line found
$message .= "\n";
$this->msg2socket($socket,$message);
} else { // multiple lines, we have to split it
do {
$msglen = $pos + 1;
$msg = substr($message,0,$msglen);
$message = substr($message,$msglen);
$pos = strpos($msg,"\n");
if (!is_int($pos)) { // line not terminated
$msg .= "\n";
}
$pos = strpos($msg,"."); // escape leading periods
if (is_int($pos) && !$pos):
$msg = "." . $msg;
endif;
if (!$this->msg2socket($socket,$msg)): return false; endif;
$pos = strpos($message,"\n");
} while (strlen($message)>0);
}
return true;
}
/** Check the header passed to self::smail() for essential elements
* (whether it contains subject and recipient and is correctly terminated)
*/
protected function check_header($to,$subject,$header) {
// first we check for the subject
if ( !(is_string($subject) && !$subject) ) { // subject specified?
$theader = strtolower($header);
$nl = strpos($theader,"\nsubject:"); // found after a new line
$beg = strpos($theader,"subject:"); // found at start
if ( !(is_int($nl)) || (is_int($beg) && !$beg) ) {
$subject = "Subject: " . stripslashes($subject);
$pos = substr($subject,"\n");
if (!is_int($pos)) $subject .= "\n";
$header .= $subject;
}
}
// now we check for the recipient
if ( !(is_string($to) && !$to) ) { // recipient specified?
$nl = strpos($theader,"\nto:"); // found after a new line
$beg = strpos($theader,"to:"); // found at start
if ( !(is_int($nl)) || (is_int($beg) && !$beg) ) {
$pos = substr($to,"\n");
if (!is_int($pos)) $subject .= "\n";
$to = "To: " .$to ."\n";
$header .= $to;
}
}
// so what about the mime version...
$nl = strpos($theader,"\nmime-version:"); // found after a new line
$beg = strpos($theader,"mime-version:"); // found at start
if ( !(is_int($nl)) || (is_int($beg) && !$beg) ) {
$header .= "MIME-Version: " . $this->mimeversion . "\n";
}
// and the content type?
$nl = strpos($theader,"\ncontent-type:"); // found after a new line
$beg = strpos($theader,"content-type:"); // found at start
if ( !(is_int($nl)) || (is_int($beg) && !$beg) ) {
$header .= "Content-type: text/plain; charset=" . $this->charset . "\n";
}
// now we complete the header (make sure it's correct terminated)
$header = chop($header);
$header .= "\n";
return $header;
}
/** Create a new mail header (called if none was passed to self::smail())
*/
protected function make_header($to,$subject) {
$now = getdate();
$header = "Date: " . gmdate("D, d M Y H:i:s") . " +0000\n";
$header .= "From: ".$this->fromuser."\n";
$header .= "To: $to\n";
$header .= "Subject: " . stripslashes($subject) . "\n";
$header .= "MIME-Version: " . $this->mimeversion . "\n";
$header .= "Content-type: text/plain; charset=" . $this->charset . "\n";
$header .= "Message-Id: <". microtime(true) ."@" .$this->hostname.">\n";
return $header;
}
// ==============================================[ main function: smail() ]===
/** Send a mail via SMTP
* @class smtp
* @method smail
* @param string to target address
* @param string subject mail subject
* @param string message the message body
* @param optional string header mail header (will be generated automatically)
* @return boolean success
*/
public function smail($to,$subject,$message,$header="") {
$errcode = ""; $errmsg = ""; // error code and message of failed connection
$timeout = 5; // timeout in secs
// now we try to open the socket and check, if any smtp server responds
$socket = fsockopen($this->smtpserver,$this->port,$errcode,$errmsg,$timeout);
if (!$socket) {
$this->err["code"] = "420";
$this->err["msg"] = "$errcode:$errmsg";
$this->err["desc"] = "Connection to ".$this->smtpserver.":".$this->port." failed - could not open socket.";
return false;
} else {
$rrc = $this->socket2msg($socket);
}
// now we can send our message. 1st we identify ourselves and the sender
$cmds = array (
"\$src = \$this->msg2socket(\$socket,\"HELO \$this->hostname\n\");",
"\$rrc = \$this->socket2msg(\$socket);",
"\$src = \$this->msg2socket(\$socket,\"MAIL FROM:<\$this->fromuser>\n\");",
"\$rrc = \$this->socket2msg(\$socket);"
);
for ($src=true,$rrc=true,$i=0; $i<count($cmds);$i++) {
eval ($cmds[$i]);
if (!$src || !$rrc) return false;
}
// now we've got to evaluate the $to's
$toaddr = explode(",",$to);
$numaddr = count($toaddr);
for ($i=0; $i<$numaddr; $i++) {
$src = $this->msg2socket($socket,"RCPT TO:<$toaddr[$i]>\n");
$rrc = $this->socket2msg($socket);
$this->to_res[$i]['addr'] = $toaddr[$i]; // for lateron validation
$this->to_res[$i]['code'] = $this->err["code"];
$this->to_res[$i]['msg'] = $this->err["msg"];
$this->to_res[$i]['desc'] = $this->err["desc"];
}
//now we have to make sure that at least one $to-address was accepted
$stop = 1;
for ($i=0;$i<count($this->to_res);$i++) {
$rc = substr($this->to_res[$i]['code'],0,1);
if ($rc == 2) { // at least to this address we can deliver
$stop = 0;
}
}
if ($stop) return false; // no address found we can deliver to
// now we can go to deliver the message!
if (!$this->msg2socket($socket,"DATA\n")) return false;
if (!$this->socket2msg($socket)) return false;
if ($header != "") {
$header = $this->check_header($to,$subject,$header);
} else {
$header = $this->make_header($to,$subject);
}
if (!$this->put2socket($socket,$header)) return false;
if (!$this->put2socket($socket,"\n")) return false;
$message = chop($message);
$message .= "\n";
if (!$this->put2socket($socket,$message)) return false;
if (!$this->msg2socket($socket,".\n")) return false;
if (!$this->socket2msg($socket)) return false;
if (!$this->msg2socket($socket,"QUIT\n")) return false;
Do {
$closing = $this->socket2msg($socket);
} while ($closing);
return true;
}
} // end of class
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment