Core Module | > Module written by Matt Mecham | > Date started: Tuesday 1st March 2005 (11:52) | | > Module Version Number: 2.1.0 +-------------------------------------------------------------------------- */ /** * IPS Kernel Pages: Email * * This class contains all generic PHP mail and SMTP functions * as well as specific functions for IPB emails * * Example usage: * * $email = new class_email(); * $email->email_init(); * $email->to = 'user@invisionboard.com'; * $email->from = 'from@user.com'; * $email->subject = 'Test Email'; * $email->message = 'Hello there!'; * $email->send_mail(); * * * @package IPS_KERNEL * @author Matt Mecham * @copyright Invision Power Services, Inc. * @version 2.1 */ /** * */ /** * Email Class * * Methods and functions for handling emails * * @package IPS_KERNEL * @author Matt Mecham * @version 2.1 * @todo Separate out IPB content and leave a pure * email class. */ class class_email { /** * From email address * * @var string */ var $from = ""; /** * From email address * * @var string */ var $from_display = ""; /** * To email address * * @var string */ var $to = ""; /** * Email subject * * @var string */ var $subject = ""; /** * Email message contents * * @var string */ var $message = ""; /** * Plain text message contents * * @var string */ var $pt_message = ""; /** * Attachments: Parts * * @var array */ var $parts = array(); /** * BCC Email addresses * * @var array */ var $bcc = array(); /** * Email headers * * @var string */ var $mail_headers = array(); /** * Header EOL * RFC specs state \r\n * However most servers support \n * @var string */ var $header_eol = "\n"; /** * Attachments: Multi-part * * @var string */ var $multipart = ""; /** * Attachments: Boundry * * @var string */ var $boundry = "----=_NextPart_000_0022_01C1BD6C.D0C0F9F0"; /** * HTML email flag * * @var integer */ var $html_email = 0; /** * Email character set * * @var string */ var $char_set = 'utf-8'; /** * SMTP: * * @var boolean */ var $smtp_fp = null; /** * SMTP: Message * * @var string */ var $smtp_msg = ""; /** * SMTP: Port * * @var integer */ var $smtp_port = 25; /** * SMTP: Host * * @var string */ var $smtp_host = "localhost"; /** * SMTP: Username * * @var string */ var $smtp_user = ""; /** * SMTP: Password * * @var string */ var $smtp_pass = ""; /** * SMTP: Return code * * @var string */ var $smtp_code = ""; /** * SMTP: Wrap email addresses in brackets flag * * @var integer boolean */ var $wrap_brackets = 0; /** * Default email method (mail or smtp) * * @var string */ var $mail_method = 'mail'; /** * Dump email to flat file for testing * * @var integer */ var $temp_dump = 0; /** * Error message * * @var string */ var $error_msg; /** * Error description * * @var string */ var $error_help; /** * Error flag * * @var string */ var $error; /*-------------------------------------------------------------------------*/ // CONSTRUCTOR /*-------------------------------------------------------------------------*/ function class_email() { } /*-------------------------------------------------------------------------*/ // ADD ATTACHMENT /*-------------------------------------------------------------------------*/ /** * Add an attachment to the current email * * @param string File data * @param string File name * @param string File type (MIME) * @return void */ function add_attachment($data = "", $name = "", $ctype='application/octet-stream') { $this->parts[] = array( 'ctype' => $ctype, 'data' => $data, 'encode' => 'base64', 'name' => $name ); } /*-------------------------------------------------------------------------*/ // BUILD HEADERS /*-------------------------------------------------------------------------*/ /** * Build the email headers (MIME, Charset, From, BCC, To, Subject, etc) * * @return void */ function build_headers() { $extra_headers = array(); $extra_header_rfc = ""; //----------------------------------------- // HTML (hitmuhl) // If we're sending HTML messages, then // we'll add the plain text message along with // it for non HTML browsers //----------------------------------------- $this->pt_message = $this->message; $this->pt_message = str_replace( "
", "\n", $this->pt_message ); $this->pt_message = str_replace( "
" , "\n", $this->pt_message ); $this->pt_message = strip_tags( $this->pt_message ); $this->pt_message = html_entity_decode( $this->pt_message, ENT_QUOTES ); $this->pt_message = str_replace( '\', '\\', $this->pt_message ); $this->pt_message = str_replace( '$', '$', $this->pt_message ); // Now put & back... //----------------------------------------- // Start mail headers //----------------------------------------- $this->mail_headers['MIME-Version'] = "1.0"; //----------------------------------------- // From and to... //----------------------------------------- if( $this->from_display ) { $this->mail_headers['From'] = '"' . $this->from_display . '" <' . $this->from . '>'; } else { $this->mail_headers['From'] = '<' . $this->from . '>'; } if ( $this->mail_method != 'smtp' ) { if ( count( $this->bcc ) > 1 ) { $this->mail_headers['Bcc'] = implode( "," , $this->bcc ); } } else { if ( $this->to ) { $this->mail_headers['To'] = $this->to; } $this->mail_headers['Subject'] = $this->subject; } $this->mail_headers['Return-Path'] = $this->from; $this->mail_headers['X-Priority'] = "3"; $this->mail_headers['X-Mailer'] = "IPS PHP Mailer"; //----------------------------------------- // Attachments? //----------------------------------------- if ( count ($this->parts) > 0 ) { if ( ! $this->html_email ) { $extra_headers[0]['Content-Type'] = "multipart/mixed;\n\tboundary=\"".$this->boundry."\""; $extra_headers[0]['notencode'] = "\n\nThis is a MIME encoded message.\n\n--".$this->boundry."\n"; $extra_headers[1]['Content-Type'] = "text/plain;\n\tcharset=\"".$this->char_set."\""; #$extra_headers[1]['Content-Transfer-Encoding'] = "quoted-printable"; $extra_headers[1]['notencode'] = "\n\n".$this->message."\n\n--".$this->boundry; } else { $extra_headers[0]['Content-Type'] = "multipart/mixed;\n\tboundary=\"".$this->boundry."\""; $extra_headers[0]['notencode'] = "\n\nThis is a MIME encoded message.\n\n--".$this->boundry."\n"; $extra_headers[1]['Content-Type'] = "text/html;\n\tcharset=\"".$this->char_set."\""; #$extra_headers[1]['Content-Transfer-Encoding'] = "quoted-printable"; $extra_headers[1]['notencode'] = "\n\n".$this->message."\n\n--".$this->boundry; } $extra_headers[2]['notencode'] = $this->build_multipart(); reset($extra_headers); foreach( $extra_headers as $subset => $the_header ) { foreach( $the_header as $k => $v ) { if( $k == 'notencode' ) { $extra_headers_rfc .= $v; } else { $v = $this->encode_headers( array( 'v' => $v ) ); $extra_headers_rfc .= $k.': '.$v['v'].$this->header_eol; } } } $this->message = ""; } else { //----------------------------------------- // HTML (hitmuhl) ? //----------------------------------------- if ( $this->html_email ) { $extra_headers[0]['Content-Type'] = "multipart/alternative;\n\tboundary=\"".$this->boundry."\""; $extra_headers[0]['notencode'] = "\n\nThis is a MIME encoded message.\n\n--".$this->boundry."\n"; $extra_headers[1]['Content-Type'] = "text/html;\n\tcharset=\"".$this->char_set."\""; #$extra_headers[1]['Content-Transfer-Encoding'] = "quoted-printable"; $extra_headers[1]['notencode'] = "\n\n".$this->message."\n\n--".$this->boundry."\n"; $extra_headers[2]['Content-Type'] = "text/plain;\n\tcharset=\"".$this->char_set."\""; #$extra_headers[2]['Content-Transfer-Encoding'] = "quoted-printable"; $extra_headers[2]['notencode'] = "\n\n".$this->pt_message."\n\n"; reset($extra_headers); foreach( $extra_headers as $subset => $the_header ) { foreach( $the_header as $k => $v ) { if( $k == 'notencode' ) { $extra_headers_rfc .= $v; } else { $v = $this->encode_headers( array( 'v' => $v ) ); $extra_headers_rfc .= $k.': '.$v['v'].$this->header_eol; } } } $this->message = ""; } else { $this->mail_headers['Content-type'] = 'text/plain; charset="'.$this->char_set.'"'; } } $this->encode_headers(); foreach( $this->mail_headers as $k => $v ) { $this->rfc_headers .= $k.": ".$v.$this->header_eol; } //----------------------------------------- // Attachments extra? //----------------------------------------- if( $extra_headers_rfc ) { $this->rfc_headers .= $extra_headers_rfc; } } /*-------------------------------------------------------------------------*/ // BUILD MULTIPART /*-------------------------------------------------------------------------*/ /** * Build the multipart headers for the email * * @return void */ function build_multipart() { $multipart = ""; for ($i = sizeof($this->parts) - 1 ; $i >= 0 ; $i--) { $multipart .= $this->header_eol . $this->encode_attachment($this->parts[$i]) . "--" . $this->boundry; } return $multipart . "--\n"; } /*-------------------------------------------------------------------------*/ // Send_mail: // Physically sends the email /*-------------------------------------------------------------------------*/ /** * Send the mail (mail must be built by this point) * * @return boolean On error */ function send_mail() { //----------------------------------------- // Wipe ya face //----------------------------------------- $this->to = preg_replace( "/[ \t]+/" , "" , $this->to ); $this->from = preg_replace( "/[ \t]+/" , "" , $this->from ); $this->to = preg_replace( "/,,/" , "," , $this->to ); $this->from = preg_replace( "/,,/" , "," , $this->from ); $this->to = preg_replace( "#\#\[\]'\"\(\):;/\$!£%\^&\*\{\}#" , "", $this->to ); $this->from = preg_replace( "#\#\[\]'\"\(\):;/\$!£%\^&\*\{\}#" , "", $this->from); //----------------------------------------- // Build headers //----------------------------------------- $this->build_headers(); if( $this->mail_method != 'smtp' ) { $subject = $this->encode_headers( array( 'Subject' => $this->subject ) ); $this->subject = $subject['Subject']; } //----------------------------------------- // Lets go.. //----------------------------------------- if ( ($this->from) and ($this->subject) ) { //----------------------------------------- // Tmp dump? (Testing) //----------------------------------------- if ($this->temp_dump == 1) { $blah = $this->subject."\n------------\n".$this->rfc_headers."\n\n".$this->message; if( !is_dir( $this->root_path . '/_mail' ) ) { @mkdir( $this->root_path . '/_mail' ); @chmod( $this->root_path . '/_mail', 0777 ); } $pathy = str_replace( '//', '/', $this->root_path . '/_mail/' . date("M-j-Y,hi-A") . str_replace( '@', '+', $this->to ) . '.php' ); $fh = @fopen( $pathy, 'w' ); @fputs( $fh, $blah, strlen($blah) ); @fclose( $fh ); } else { //----------------------------------------- // PHP MAIL() //----------------------------------------- if ($this->mail_method != 'smtp') { if ( ! @mail( $this->to, $this->subject, $this->message, $this->rfc_headers, $this->extra_opts ) ) { # Try without args for safe mode peeps if ( ! @mail( $this->to, $this->subject, $this->message, $this->rfc_headers ) ) { $this->fatal_error("Could not send the email", "Failed at 'mail' command"); } } } //----------------------------------------- // SMTP //----------------------------------------- else { $this->smtp_send_mail(); } } } else { $this->fatal_error("From or subject empty"); return FALSE; } $this->to = ""; $this->from = ""; $this->from_display = ""; $this->message = ""; $this->pt_message = ""; $this->subject = ""; $this->mail_headers = array(); } /*-------------------------------------------------------------------------*/ // SMTP: Get line /*-------------------------------------------------------------------------*/ /** * SMTP: Get next line * * @return void */ function smtp_get_line() { $this->smtp_msg = ""; while ( $line = fgets( $this->smtp_fp, 515 ) ) { $this->smtp_msg .= $line; if ( substr($line, 3, 1) == " " ) { break; } } } /*-------------------------------------------------------------------------*/ // SMTP Send CMD /*-------------------------------------------------------------------------*/ /** * SMTP: Send command * * @param string SMTP command * @return boolean */ function smtp_send_cmd($cmd) { $this->smtp_msg = ""; $this->smtp_code = ""; fputs( $this->smtp_fp, $cmd."\r\n" ); $this->smtp_get_line(); $this->smtp_code = substr( $this->smtp_msg, 0, 3 ); return $this->smtp_code == "" ? FALSE : TRUE; } /*-------------------------------------------------------------------------*/ // SMTP Error /*-------------------------------------------------------------------------*/ /** * SMTP: Error handler * * @param string SMTP error * @return boolean */ function smtp_error($err = "") { $this->smtp_msg = $err; $this->fatal_error( $err ); return FALSE; } /*-------------------------------------------------------------------------*/ // SMTP: Encode newlines /*-------------------------------------------------------------------------*/ /** * Encode data and make it safe for SMTP transport * * @param string Raw Data * @return string CRLF Encoded Data */ function smtp_crlf_encode($data) { $data .= "\n"; $data = str_replace( "\n", "\r\n", str_replace( "\r", "", $data ) ); $data = str_replace( "\n.\r\n" , "\n. \r\n", $data ); return $data; } /*-------------------------------------------------------------------------*/ // SMTP send mail /*-------------------------------------------------------------------------*/ /** * SMTP: Sends the SMTP email * * @return void */ function smtp_send_mail() { $this->smtp_fp = @fsockopen( $this->smtp_host, intval($this->smtp_port), $errno, $errstr, 30 ); if ( ! $this->smtp_fp ) { $this->smtp_error("Could not open a socket to the SMTP server"); return; } $this->smtp_get_line(); $this->smtp_code = substr( $this->smtp_msg, 0, 3 ); if ( $this->smtp_code == 220 ) { $data = $this->smtp_crlf_encode( $this->mail_headers."\n" . $this->message); //----------------------------------------- // HELO!, er... HELLO! //----------------------------------------- $this->smtp_send_cmd("HELO ".$this->smtp_host); if ( $this->smtp_code != 250 ) { $this->smtp_send_cmd("EHLO ".$this->smtp_host); if ( $this->smtp_code != 250 ) { $this->smtp_error("HELO,EHLO"); return; } } //----------------------------------------- // Do you like my user! //----------------------------------------- if ($this->smtp_user and $this->smtp_pass) { $this->smtp_send_cmd("AUTH LOGIN"); if ( $this->smtp_code == 334 ) { $this->smtp_send_cmd( base64_encode($this->smtp_user) ); if ( $this->smtp_code != 334 ) { $this->smtp_error("Username not accepted from the server"); return; } $this->smtp_send_cmd( base64_encode($this->smtp_pass) ); if ( $this->smtp_code != 235 ) { $this->smtp_error("Password not accepted from the server"); return; } } else { $this->smtp_error("This server does not support authorisation"); return; } } //----------------------------------------- // We're from MARS! //----------------------------------------- if ( $this->wrap_brackets ) { if ( ! preg_match( "/^from ) ) { $this->from = "<".$this->from.">"; } } $this->smtp_send_cmd("MAIL FROM:".$this->from); if ( $this->smtp_code != 250 ) { $this->smtp_error(); return; } $to_arry = array( $this->to ); if ( count( $this->bcc ) > 0 ) { foreach ($this->bcc as $bcc) { if ( preg_match( "/^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,4})(\]?)$/", str_replace( " ", "", $bcc ) ) ) { $to_arry[] = $bcc; } } } //----------------------------------------- // You are from VENUS! //----------------------------------------- foreach( $to_arry as $to_email ) { if ( $this->wrap_brackets ) { $this->smtp_send_cmd("RCPT TO:<".$to_email.">"); } else { $this->smtp_send_cmd("RCPT TO:".$to_email); } if ( $this->smtp_code != 250 ) { $this->smtp_error("Incorrect email address: $to_email"); return; break; } } //----------------------------------------- // SEND MAIL! //----------------------------------------- $this->smtp_send_cmd("DATA"); if ( $this->smtp_code == 354 ) { fputs( $this->smtp_fp, $data."\r\n" ); } else { $this->smtp_error("Error on write to SMTP server"); return; } //----------------------------------------- // GO ON, NAFF OFF! //----------------------------------------- $this->smtp_send_cmd("."); if ( $this->smtp_code != 250 ) { $this->smtp_error(); return; } $this->smtp_send_cmd("quit"); if ( $this->smtp_code != 221 ) { $this->smtp_error(); return; } //----------------------------------------- // Tubby-bye-bye! //----------------------------------------- @fclose( $this->smtp_fp ); } else { $this->smtp_error(); return; } } /*-------------------------------------------------------------------------*/ // FATAL ERROR : RETURN /*-------------------------------------------------------------------------*/ /** * Fatal error handler * * @param string Error Message * @param string Error Help / Description * @return boolean */ function fatal_error($msg, $help="") { $this->error_msg = $msg; $this->error_help = $help; return FALSE; } /*-------------------------------------------------------------------------*/ // ENCODE ATTACHMENT /*-------------------------------------------------------------------------*/ /** * Encode an attachment * * @param string Raw data * @return string Processed data */ function encode_attachment($part) { $msg = chunk_split(base64_encode($part['data'])); $headers = array(); $header_str = ""; $headers['Content-Type'] = $part['ctype']. ($part['name'] ? "; name =\"".$part['name']."\"" : ""); $headers['Content-Transfer-Encoding'] = $part['encode']; $headers['Content-Disposition'] = "attachment; filename=\"".$part['name']."\""; $headers = $this->encode_headers( $headers ); foreach( $headers as $k => $v ) { $header_str .= $k.': '.$v.$this->header_eol; } $header_str .= "\n\n".$msg."\n"; return $header_str; } /*-------------------------------------------------------------------------*/ // ENCODE HEADERS - RFC2047 /*-------------------------------------------------------------------------*/ function encode_headers( $headers = array() ) { $enc_headers = count($headers) ? $headers : $this->mail_headers; foreach( $enc_headers as $header => $value) { preg_match_all( '/(\w*[\x80-\xFF]+\w*)/', $value, $matches ); foreach ($matches[1] as $value) { if( $header == 'From' ) { // Either sendmail or the email servers don't like 'From' encoded...let's remove the board name // and just move along, as email address cannot contain nasty characters themselves $this->mail_headers['From'] = $this->from; continue 2; } $replacement = preg_replace_callback( '/([\x80-\xFF])/', create_function( '$match', 'return "=" . strtoupper( dechex( ord( "$match[1]" ) ) );' ), $value ); $value = str_replace( $value, '=?' . $this->char_set . '?Q?' . $replacement . '?=', $value ); } if( !count($headers) ) { $this->mail_headers[ $header ] = $value; } else { $enc_headers[ $header ] = $value; } } return $enc_headers; } } ?>