Browse code

smtp fix + tester

Jimako authored on 2026/07/10 14:03:10
Showing 11 changed files
... ...
@@ -77,7 +77,117 @@ $sects = array("main", "submissions", "sitesettings", "display", "reviews", "use
77 77
 //if(!isset($_GET['sect'])) $sect = "main";
78 78
 //else $sect = $_GET['sect'];
79 79
 $sect = isset($_GET['sect']) ? $_GET['sect'] : "main";
80
-if(isset($_POST['submit'])) {
80
+if(isset($_POST['testsmtp']) && $sect == "email") {
81
+	// SMTP connection test. Runs only on POST (the Test button), never via a GET
82
+	// parameter. It verifies the SAVED settings loaded from fanfiction_settings
83
+	// (via header.php) -- NOT the unsaved form values -- by opening a real SMTP
84
+	// connection and reporting each step. It never sends an email and never writes
85
+	// to the database, so it must not touch $result or advance $sects.
86
+	$output .= "<h2>"._TESTSMTP_HEADER."</h2>";
87
+	if(empty($smtp_host)) {
88
+		$output .= write_error(_TESTSMTP_NOHOST);
89
+	}
90
+	else {
91
+		// Effective values MUST mirror the real sending behaviour of sendemail():
92
+		// PHPMailer's default port is 25, and 'ssl' uses an ssl:// transport prefix.
93
+		$port   = !empty($smtp_port) ? (int) $smtp_port : 25;
94
+		$secure = !empty($smtp_secure) ? $smtp_secure : '';
95
+		$host   = ($secure === 'ssl') ? 'ssl://'.$smtp_host : $smtp_host;
96
+
97
+		$output .= write_message(sprintf(_TESTSMTP_PORTNOTE, htmlspecialchars((string) $port), htmlspecialchars($secure !== '' ? $secure : '(none)')));
98
+		// If encryption is requested but no port is saved, warn about the usual port
99
+		// -- but still run the test on the effective values, since that is what real
100
+		// sending will do.
101
+		if(empty($smtp_port) && $secure === 'ssl') $output .= write_message(_TESTSMTP_SSLPORTWARN);
102
+		if(empty($smtp_port) && $secure === 'tls') $output .= write_message(_TESTSMTP_TLSPORTWARN);
103
+
104
+		// Load the SMTP class the same way emailer.php loads PHPMailer, via the
105
+		// autoloader -- NOT by requiring class.smtp.php directly. After the planned
106
+		// PHPMailer 6.x upgrade the autoload file becomes a shim and this must keep
107
+		// working (that upgrade must alias the SMTP class name; see issue #2).
108
+		require_once(_BASEDIR."includes/PHPMailerAutoload.php");
109
+
110
+		$steps = "";
111
+		ob_start();
112
+		$smtp = new SMTP;
113
+		$smtp->do_debug = 2;
114
+		try {
115
+			if(!$smtp->connect($host, $port, 10)) {
116
+				$steps .= write_error(sprintf(_TESTSMTP_CONNECTFAIL, htmlspecialchars($host), htmlspecialchars((string) $port)));
117
+			}
118
+			else {
119
+				$steps .= write_message(sprintf(_TESTSMTP_CONNECTOK, htmlspecialchars($host), htmlspecialchars((string) $port)));
120
+				if(!$smtp->hello(gethostname())) {
121
+					$err = $smtp->getError();
122
+					$steps .= write_error(sprintf(_TESTSMTP_HELOFAIL, htmlspecialchars(!empty($err['error']) ? $err['error'] : '')));
123
+				}
124
+				else {
125
+					$steps .= write_message(_TESTSMTP_HELOOK);
126
+					$ext = $smtp->getServerExtList();
127
+					$hasStartTls = is_array($ext) && array_key_exists('STARTTLS', $ext);
128
+					if($hasStartTls) {
129
+						$steps .= write_message(_TESTSMTP_STARTTLSOK);
130
+						if($secure === 'tls') {
131
+							if(!$smtp->startTLS()) {
132
+								$err = $smtp->getError();
133
+								$steps .= write_error(sprintf(_TESTSMTP_STARTTLSFAIL, htmlspecialchars(!empty($err['error']) ? $err['error'] : '')));
134
+							}
135
+							else {
136
+								// Re-issue EHLO over the now-encrypted channel and refresh caps.
137
+								$smtp->hello(gethostname());
138
+								$ext = $smtp->getServerExtList();
139
+							}
140
+						}
141
+					}
142
+					else if($secure === 'tls') {
143
+						$steps .= write_error(_TESTSMTP_STARTTLSMISSING);
144
+					}
145
+					$hasAuth = is_array($ext) && array_key_exists('AUTH', $ext);
146
+					if($hasAuth) {
147
+						if(!empty($smtp_username)) {
148
+							if(!$smtp->authenticate($smtp_username, $smtp_password)) {
149
+								// NOTE: never include the password in this message.
150
+								$err = $smtp->getError();
151
+								$steps .= write_error(sprintf(_TESTSMTP_AUTHFAIL, htmlspecialchars(!empty($err['error']) ? $err['error'] : '')));
152
+							}
153
+							else {
154
+								$steps .= write_message(_TESTSMTP_AUTHOK);
155
+							}
156
+						}
157
+						else {
158
+							$steps .= write_message(_TESTSMTP_AUTHSKIP);
159
+						}
160
+					}
161
+					else {
162
+						$steps .= write_message(_TESTSMTP_NOAUTH);
163
+					}
164
+				}
165
+			}
166
+		} catch (Exception $e) {
167
+			$steps .= write_error(htmlspecialchars($e->getMessage()));
168
+		}
169
+		// Always close the connection, even if we stopped early above.
170
+		$smtp->quit(true);
171
+		$debug = ob_get_clean();
172
+
173
+		// SECURITY: the debug transcript at level 2 echoes client commands, which
174
+		// during AUTH include the base64-encoded credentials. Redact every form of
175
+		// the password before it is ever shown so it is never printed anywhere.
176
+		if(!empty($smtp_password)) {
177
+			$secrets = array(
178
+				$smtp_password,
179
+				base64_encode($smtp_password),
180
+				base64_encode("\0".$smtp_username."\0".$smtp_password), // AUTH PLAIN
181
+			);
182
+			$debug = str_replace($secrets, "***REDACTED***", $debug);
183
+		}
184
+
185
+		$output .= $steps;
186
+		// Server banners are untrusted input -> escape the whole transcript (XSS).
187
+		$output .= "<h3>"._TESTSMTP_TRANSCRIPT."</h3><pre style='text-align:left; overflow:auto; background-color: rgba(0, 0, 0, .6); margin-top: 10px; color: #fff; padding: 8px;'>".htmlspecialchars($debug)."</pre>";
188
+	}
189
+}
190
+else if(isset($_POST['submit'])) {
81 191
 	if($sect == "main") {
82 192
 		if(!preg_match("!^[a-z0-9_]{3,30}$!i", $_POST['newsitekey'])) $output .= write_error(_BADSITEKEY);
83 193
 		else {
... ...
@@ -164,21 +274,29 @@ if(isset($_POST['submit'])) {
164 274
 		$result = dbquery("UPDATE ".$settingsprefix."fanfiction_settings SET alertson = '$alertson', disablepopups = '$disablepopups', agestatement = '$agestatement', pwdsetting = '$pwdsetting' WHERE sitekey ='".SITEKEY."'");
165 275
 	}
166 276
 	else if($sect == "email") {
167
-		$smtp_host = $_POST['newsmtp_host'];
168
-		$smtp_username = $_POST['newsmtp_username'];
169
-		$smtp_password = $_POST['newsmtp_password'];
170
-		$result = dbquery("UPDATE ".$settingsprefix."fanfiction_settings SET smtp_host = '$smtp_host', smtp_username = '$smtp_username', smtp_password = '$smtp_password' WHERE sitekey ='".SITEKEY."'");
277
+		$smtp_host = escapestring(descript(strip_tags($_POST['newsmtp_host'])));
278
+		$smtp_username = escapestring(descript(strip_tags($_POST['newsmtp_username'])));
279
+		$smtp_password = escapestring(descript(strip_tags($_POST['newsmtp_password'])));
280
+		$smtp_port = !empty($_POST['newsmtp_port']) ? (int) $_POST['newsmtp_port'] : '';
281
+		$smtp_secure = in_array($_POST['newsmtp_secure'], array('', 'tls', 'ssl'), true) ? $_POST['newsmtp_secure'] : '';
282
+		$result = dbquery("UPDATE ".$settingsprefix."fanfiction_settings SET smtp_host = '$smtp_host', smtp_username = '$smtp_username', smtp_password = '$smtp_password', smtp_port = '$smtp_port', smtp_secure = '$smtp_secure' WHERE sitekey ='".SITEKEY."'");
171 283
 	}
172 284
 	if ($result) {
173 285
 		$output .= write_message(_ACTIONSUCCESSFUL);
174
-		
175
-		$idx = array_search($sect, $sects);
176
-		if ($idx !== false) {
177
-			$next_idx = $idx + 1;
178
-			if ($next_idx >= count($sects)) {
179
-				$next_idx = 0;
286
+		// The section-advance wizard is for the INSTALLER only (install/install.php
287
+		// includes this file and steps through sections). In admin, "email" is the
288
+		// last entry in $sects, so advancing would wrap to 0 and jump back to the
289
+		// first tab while the URL still says sect=email. $action is undefined in the
290
+		// installer include context, so guard with isset() to avoid a PHP 8 warning.
291
+		if (!isset($action) || $action != "settings") {
292
+			$idx = array_search($sect, $sects);
293
+			if ($idx !== false) {
294
+				$next_idx = $idx + 1;
295
+				if ($next_idx >= count($sects)) {
296
+					$next_idx = 0;
297
+				}
298
+				$sect = $sects[$next_idx];
180 299
 			}
181
-			$sect = $sects[$next_idx];
182 300
 		}
183 301
 	} else {
184 302
 		$output .= write_error(_ERROR);
... ...
@@ -190,8 +308,9 @@ if(isset($_POST['submit'])) {
190 308
 		if(is_NULL($val)) $val = '';
191 309
 		$$var = stripslashes($val );
192 310
 	}
311
+	}
193 312
 
194
-	$output .= "<form method='POST' class='tblborder' style='' enctype='multipart/form-data' action='".($action == "settings" ? "admin.php?action=settings" : $_SERVER['PHP_SELF']."?step=".$_GET['step'])."&amp;sect=$sect'>";
313
+	$output .= "<form method='POST' class='tblborder' style='' enctype='multipart/form-data' action='".((isset($action) && $action == "settings") ? "admin.php?action=settings" : $_SERVER['PHP_SELF']."?step=".$_GET['step'])."&amp;sect=$sect'>";
195 314
 	if($sect == "main") {
196 315
 		$output .= "<h2>"._SITEINFO."</h2>
197 316
 		<table class='acp'>
... ...
@@ -488,8 +607,32 @@ if(isset($_POST['submit'])) {
488 607
 				<td><label for='newsmtp_username'>"._SMTPUSER.":</label></td><td><input name='newsmtp_username' type='text' value='$smtp_username'> <a href='#' class='pophelp'>[?]<span>"._HELP_SMTPUSER."</span></a></td>
489 608
 		</tr>
490 609
 		<tr>
491
-				<td><label for='newsmtp_password'>"._SMTPPASS.":</label></td><td><input name='newsmtp_password' type='password' value='$smtp_password'> <a href='#' class='pophelp'>[?]<span>"._HELP_SMTPPWD."</span></a></td></tr>";		
610
+				<td><label for='newsmtp_password'>"._SMTPPASS.":</label></td><td><input name='newsmtp_password' type='password' value='$smtp_password'> <a href='#' class='pophelp'>[?]<span>"._HELP_SMTPPWD."</span></a></td></tr>
611
+		<tr>
612
+				<td><label for='newsmtp_port'>"._SMTPPORT.":</label></td><td><input name='newsmtp_port' type='text' value='".((isset($smtp_port) && $smtp_port !== '') ? $smtp_port : '')."'> <a href='#' class='pophelp'>[?]<span>"._HELP_SMTPPORT."</span></a></td>
613
+		</tr>
614
+		<tr>
615
+				<td><label for='newsmtp_secure'>"._SMTPSECURE.":</label></td><td><select name='newsmtp_secure'>
616
+					<option value=''".((!isset($smtp_secure) || $smtp_secure === '') ? " selected" : "").">(none)</option>
617
+					<option value='tls'".((isset($smtp_secure) && $smtp_secure == 'tls') ? " selected" : "").">STARTTLS / 587</option>
618
+					<option value='ssl'".((isset($smtp_secure) && $smtp_secure == 'ssl') ? " selected" : "").">SSL / 465</option>
619
+				</select> <a href='#' class='pophelp'>[?]<span>"._HELP_SMTPSECURE."</span></a></td></tr>";
492 620
 		$output .= 	write_message(_SMTPOFF);
493 621
 	}
494 622
 	$output .= "<tr><td colspan='2'><div align='center'><input type='submit' id='submit' class='button' name='submit' value='"._SUBMIT."'></div></form></td></tr></table>";
623
+	// The SMTP test button lives OUTSIDE the main settings form (nested forms are
624
+	// invalid HTML). It is its own POST mini-form -- a submit button, not a GET
625
+	// link -- so the outbound SMTP connection can't be triggered by a plain link
626
+	// or a browser prefetch. The existing testsmtp handler posts to the same URL.
627
+	if($sect == "email") {
628
+		$output .= "<h2>"._TESTSMTP_HEADER."</h2>
629
+		<ul>
630
+			<li>
631
+				<form method='POST' action='admin.php?action=settings&amp;sect=email' style='display:inline'>
632
+					<input type='submit' class='button' name='testsmtp' value='"._TESTSMTP."'>
633
+				</form>
634
+				<a href='#' class='pophelp'>[?]<span>"._HELP_TESTSMTP."</span></a>
635
+			</li>
636
+		</ul>";
637
+	}
495 638
 ?>
... ...
@@ -29,7 +29,7 @@ if(!defined("_CHARSET")) exit( );
29 29
 
30 30
 function sendemail($to_name,$to_email,$from_name,$from_email,$subject,$message,$type="plain",$cc="",$bcc="") {
31 31
                  
32
-	global $language, $smtp_host, $smtp_username, $smtp_password, $siteemail;     
32
+	global $language, $smtp_host, $smtp_username, $smtp_password, $smtp_port, $smtp_secure, $smtp_debug, $siteemail;
33 33
    
34 34
 	// Check for hackers and spammers and bad input
35 35
 	if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
... ...
@@ -64,12 +64,22 @@ function sendemail($to_name,$to_email,$from_name,$from_email,$subject,$message,$
64 64
 	if(!$smtp_host) {
65 65
 		$mail->IsMail( );
66 66
 	}
67
-	else { 
67
+	else {
68 68
 		$mail->IsSMTP( );
69 69
 		$mail->Host = $smtp_host;
70 70
 		$mail->SMTPAuth = true;
71 71
 		$mail->Username = $smtp_username;
72 72
 		$mail->Password = $smtp_password;
73
+		// Apply transport settings only when configured; otherwise leave PHPMailer
74
+		// defaults (and SMTPAutoTLS) in place. !empty() avoids 8.x warnings on
75
+		// not-yet-migrated installs where these globals are undefined.
76
+		if (!empty($smtp_port))   $mail->Port = (int) $smtp_port;
77
+		if (!empty($smtp_secure)) $mail->SMTPSecure = $smtp_secure;   // '', 'tls', 'ssl'
78
+		// Optional handshake debugging, off unless $smtp_debug is set. Routed to the PHP error log.
79
+		if(!empty($smtp_debug)) {
80
+			$mail->SMTPDebug = 2;
81
+			$mail->Debugoutput = function($str, $level) { error_log("eFiction SMTP debug [$level]: ".trim($str)); };
82
+		}
73 83
 	}
74 84
 	$mail->CharSet = _CHARSET;
75 85
 	$mail->From = $siteemail;
... ...
@@ -97,10 +107,11 @@ function sendemail($to_name,$to_email,$from_name,$from_email,$subject,$message,$
97 107
 	$mail->Subject = $subject;
98 108
 	$mail->Body = $message;
99 109
 	if(!$mail->Send()) {
100
-		$mail->ErrorInfo;
110
+		$errorinfo = $mail->ErrorInfo;
111
+		error_log("eFiction sendemail failed for ".$to_email.": ".$errorinfo);
101 112
 		$mail->ClearAllRecipients();
102 113
 		$mail->ClearReplyTos();
103
-		return $mail->ErrorInfo;
114
+		return false;
104 115
 	} else {
105 116
 		$mail->ClearAllRecipients(); 
106 117
 		$mail->ClearReplyTos();
107 118
new file mode 100644
108 119
new file mode 100644
... ...
@@ -0,0 +1,23 @@
1
+<?php
2
+/**
3
+ * PHPMailer language file.  
4
+ * English Version
5
+ */
6
+
7
+$PHPMAILER_LANG = array();
8
+
9
+$PHPMAILER_LANG["provide_address"] = 'You must provide at least one ' .
10
+                                     'recipient email address.';
11
+$PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.';
12
+$PHPMAILER_LANG["execute"] = 'Could not execute: ';
13
+$PHPMAILER_LANG["instantiate"] = 'Could not instantiate mail function.';
14
+$PHPMAILER_LANG["authenticate"] = 'SMTP Error: Could not authenticate.';
15
+$PHPMAILER_LANG["from_failed"] = 'The following From address failed: ';
16
+$PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: The following ' .
17
+                                       'recipients failed: ';
18
+$PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data not accepted.';
19
+$PHPMAILER_LANG["connect_host"] = 'SMTP Error: Could not connect to SMTP host.';
20
+$PHPMAILER_LANG["file_access"] = 'Could not access file: ';
21
+$PHPMAILER_LANG["file_open"] = 'File Error: Could not open file: ';
22
+$PHPMAILER_LANG["encoding"] = 'Unknown encoding: ';
23
+?>
0 24
new file mode 100644
... ...
@@ -0,0 +1,70 @@
1
+<?php
2
+
3
+// Some DB functions 
4
+
5
+if(!function_exists("dbconnect")) { // just in case.  Some people seemed to be having an issue and this was the easiest fix.
6
+
7
+function dbconnect($dbhost, $dbuser, $dbpass, $dbname ) {
8
+	$mysql_access = mysql_connect($dbhost, $dbuser, $dbpass);
9
+	if(!$mysql_access) {
10
+		include(_BASEDIR."languages/en.php");
11
+		die(_FATALERROR." "._NOTCONNECTED);
12
+	}
13
+	mysql_select_db($dbname, $mysql_access);
14
+	mysql_query("SET SESSION sql_mode = 'MYSQL40'");
15
+	return $mysql_access;
16
+}
17
+
18
+
19
+function dbquery($query) {
20
+	global $debug, $headerSent, $dbconnect;
21
+	if($debug  && $headerSent) echo "<!-- $query -->\n";
22
+	$result = mysql_query($query, $dbconnect) or accessDenied( _FATALERROR.(isADMIN ? "Query: ".$query."<br />Error: (".mysql_errno( ).") ".mysql_error( ) : ""));
23
+	return $result;
24
+}
25
+
26
+function dbnumrows($query) {
27
+	global $debug, $dbconnect;
28
+	if ($query === false && mysql_errno( ) > 0 && $debug) {
29
+		echo "<!-- dbnumrows ".mysql_error( )." -->\n";
30
+	}
31
+	$query = mysql_num_rows($query);
32
+	return $query;
33
+}
34
+
35
+function dbassoc($query) {
36
+	global $debug, $dbconnect;
37
+	if ($query === false && mysql_errno( ) > 0 && $debug) {
38
+		echo "<!-- dbassoc ".mysql_error( )." -->\n";
39
+	}
40
+	$query = mysql_fetch_assoc($query);
41
+	return $query;
42
+}
43
+
44
+function dbinsertid($tablename = 0) {
45
+	return mysql_insert_id( );
46
+}
47
+
48
+function dbrow($query) {
49
+	global $debug, $dbconnect;
50
+	if ($query === false && mysql_errno( ) > 0 && $debug) {
51
+		if($error) echo "<!-- dbrow ".mysql_error( )." -->\n";
52
+	}
53
+	$query = mysql_fetch_row($query);
54
+	return $query;
55
+}
56
+
57
+// Used to escape text being put into the database.
58
+function escapestring($str) {
59
+   if (!is_array($str)) return mysql_real_escape_string($str);
60
+   else return array_map('escapestring', $str);
61
+}
62
+
63
+function dbclose( ) {
64
+	global $dbconnect;
65
+	mysql_close($dbconnect);
66
+
67
+}
68
+// End DB functions
69
+}
70
+?>
0 71
\ No newline at end of file
1 72
new file mode 100644
... ...
@@ -0,0 +1,1501 @@
1
+<?php
2
+if(!defined("_CHARSET")) exit( );
3
+////////////////////////////////////////////////////
4
+// PHPMailer - PHP email class
5
+//
6
+// Class for sending email using either
7
+// sendmail, PHP mail(), or SMTP.  Methods are
8
+// based upon the standard AspEmail(tm) classes.
9
+//
10
+// Copyright (C) 2001 - 2003  Brent R. Matzelle
11
+//
12
+// License: LGPL, see LICENSE
13
+////////////////////////////////////////////////////
14
+
15
+/**
16
+ * PHPMailer - PHP email transport class
17
+ * @package PHPMailer
18
+ * @author Brent R. Matzelle
19
+ * @copyright 2001 - 2003 Brent R. Matzelle
20
+ */
21
+class PHPMailer
22
+{
23
+
24
+    /////////////////////////////////////////////////
25
+    // PUBLIC VARIABLES
26
+    /////////////////////////////////////////////////
27
+
28
+    /**
29
+     * Email priority (1 = High, 3 = Normal, 5 = low).
30
+     * @var int
31
+     */
32
+    var $Priority          = 3;
33
+
34
+    /**
35
+     * Sets the CharSet of the message.
36
+     * @var string
37
+     */
38
+    var $CharSet           = "iso-8859-1";
39
+
40
+    /**
41
+     * Sets the Content-type of the message.
42
+     * @var string
43
+     */
44
+    var $ContentType        = "text/plain";
45
+
46
+    /**
47
+     * Sets the Encoding of the message. Options for this are "8bit",
48
+     * "7bit", "binary", "base64", and "quoted-printable".
49
+     * @var string
50
+     */
51
+    var $Encoding          = "8bit";
52
+
53
+    /**
54
+     * Holds the most recent mailer error message.
55
+     * @var string
56
+     */
57
+    var $ErrorInfo         = "";
58
+
59
+    /**
60
+     * Sets the From email address for the message.
61
+     * @var string
62
+     */
63
+    var $From               = "root@localhost";
64
+
65
+    /**
66
+     * Sets the From name of the message.
67
+     * @var string
68
+     */
69
+    var $FromName           = "Root User";
70
+
71
+    /**
72
+     * Sets the Sender email (Return-Path) of the message.  If not empty,
73
+     * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
74
+     * @var string
75
+     */
76
+    var $Sender            = "";
77
+
78
+    /**
79
+     * Sets the Subject of the message.
80
+     * @var string
81
+     */
82
+    var $Subject           = "";
83
+
84
+    /**
85
+     * Sets the Body of the message.  This can be either an HTML or text body.
86
+     * If HTML then run IsHTML(true).
87
+     * @var string
88
+     */
89
+    var $Body               = "";
90
+
91
+    /**
92
+     * Sets the text-only body of the message.  This automatically sets the
93
+     * email to multipart/alternative.  This body can be read by mail
94
+     * clients that do not have HTML email capability such as mutt. Clients
95
+     * that can read HTML will view the normal Body.
96
+     * @var string
97
+     */
98
+    var $AltBody           = "";
99
+
100
+    /**
101
+     * Sets word wrapping on the body of the message to a given number of 
102
+     * characters.
103
+     * @var int
104
+     */
105
+    var $WordWrap          = 0;
106
+
107
+    /**
108
+     * Method to send mail: ("mail", "sendmail", or "smtp").
109
+     * @var string
110
+     */
111
+    var $Mailer            = "mail";
112
+
113
+    /**
114
+     * Sets the path of the sendmail program.
115
+     * @var string
116
+     */
117
+    var $Sendmail          = "/usr/sbin/sendmail";
118
+    
119
+    /**
120
+     * Path to PHPMailer plugins.  This is now only useful if the SMTP class 
121
+     * is in a different directory than the PHP include path.  
122
+     * @var string
123
+     */
124
+    var $PluginDir         = "";
125
+
126
+    /**
127
+     *  Holds PHPMailer version.
128
+     *  @var string
129
+     */
130
+    var $Version           = "1.73";
131
+
132
+    /**
133
+     * Sets the email address that a reading confirmation will be sent.
134
+     * @var string
135
+     */
136
+    var $ConfirmReadingTo  = "";
137
+
138
+    /**
139
+     *  Sets the hostname to use in Message-Id and Received headers
140
+     *  and as default HELO string. If empty, the value returned
141
+     *  by SERVER_NAME is used or 'localhost.localdomain'.
142
+     *  @var string
143
+     */
144
+    var $Hostname          = "";
145
+
146
+    /////////////////////////////////////////////////
147
+    // SMTP VARIABLES
148
+    /////////////////////////////////////////////////
149
+
150
+    /**
151
+     *  Sets the SMTP hosts.  All hosts must be separated by a
152
+     *  semicolon.  You can also specify a different port
153
+     *  for each host by using this format: [hostname:port]
154
+     *  (e.g. "smtp1.example.com:25;smtp2.example.com").
155
+     *  Hosts will be tried in order.
156
+     *  @var string
157
+     */
158
+    var $Host        = "localhost";
159
+
160
+    /**
161
+     *  Sets the default SMTP server port.
162
+     *  @var int
163
+     */
164
+    var $Port        = 25;
165
+
166
+    /**
167
+     *  Sets the SMTP HELO of the message (Default is $Hostname).
168
+     *  @var string
169
+     */
170
+    var $Helo        = "";
171
+
172
+    /**
173
+     *  Sets SMTP authentication. Utilizes the Username and Password variables.
174
+     *  @var bool
175
+     */
176
+    var $SMTPAuth     = false;
177
+
178
+    /**
179
+     *  Sets SMTP username.
180
+     *  @var string
181
+     */
182
+    var $Username     = "";
183
+
184
+    /**
185
+     *  Sets SMTP password.
186
+     *  @var string
187
+     */
188
+    var $Password     = "";
189
+
190
+    /**
191
+     *  Sets the SMTP server timeout in seconds. This function will not 
192
+     *  work with the win32 version.
193
+     *  @var int
194
+     */
195
+    var $Timeout      = 10;
196
+
197
+    /**
198
+     *  Sets SMTP class debugging on or off.
199
+     *  @var bool
200
+     */
201
+    var $SMTPDebug    = false;
202
+
203
+    /**
204
+     * Prevents the SMTP connection from being closed after each mail 
205
+     * sending.  If this is set to true then to close the connection 
206
+     * requires an explicit call to SmtpClose(). 
207
+     * @var bool
208
+     */
209
+    var $SMTPKeepAlive = false;
210
+
211
+    /**#@+
212
+     * @access private
213
+     */
214
+    var $smtp            = NULL;
215
+    var $to              = array();
216
+    var $cc              = array();
217
+    var $bcc             = array();
218
+    var $ReplyTo         = array();
219
+    var $attachment      = array();
220
+    var $CustomHeader    = array();
221
+    var $message_type    = "";
222
+    var $boundary        = array();
223
+    var $language        = array();
224
+    var $error_count     = 0;
225
+    var $LE              = "\n";
226
+    /**#@-*/
227
+    
228
+    /////////////////////////////////////////////////
229
+    // VARIABLE METHODS
230
+    /////////////////////////////////////////////////
231
+
232
+    /**
233
+     * Sets message type to HTML.  
234
+     * @param bool $bool
235
+     * @return void
236
+     */
237
+    function IsHTML($bool) {
238
+        if($bool == true)
239
+            $this->ContentType = "text/html";
240
+        else
241
+            $this->ContentType = "text/plain";
242
+    }
243
+
244
+    /**
245
+     * Sets Mailer to send message using SMTP.
246
+     * @return void
247
+     */
248
+    function IsSMTP() {
249
+        $this->Mailer = "smtp";
250
+    }
251
+
252
+    /**
253
+     * Sets Mailer to send message using PHP mail() function.
254
+     * @return void
255
+     */
256
+    function IsMail() {
257
+        $this->Mailer = "mail";
258
+    }
259
+
260
+    /**
261
+     * Sets Mailer to send message using the $Sendmail program.
262
+     * @return void
263
+     */
264
+    function IsSendmail() {
265
+        $this->Mailer = "sendmail";
266
+    }
267
+
268
+    /**
269
+     * Sets Mailer to send message using the qmail MTA. 
270
+     * @return void
271
+     */
272
+    function IsQmail() {
273
+        $this->Sendmail = "/var/qmail/bin/sendmail";
274
+        $this->Mailer = "sendmail";
275
+    }
276
+
277
+
278
+    /////////////////////////////////////////////////
279
+    // RECIPIENT METHODS
280
+    /////////////////////////////////////////////////
281
+
282
+    /**
283
+     * Adds a "To" address.  
284
+     * @param string $address
285
+     * @param string $name
286
+     * @return void
287
+     */
288
+    function AddAddress($address, $name = "") {
289
+        $cur = count($this->to);
290
+        $this->to[$cur][0] = trim($address);
291
+        $this->to[$cur][1] = $name;
292
+    }
293
+
294
+    /**
295
+     * Adds a "Cc" address. Note: this function works
296
+     * with the SMTP mailer on win32, not with the "mail"
297
+     * mailer.  
298
+     * @param string $address
299
+     * @param string $name
300
+     * @return void
301
+    */
302
+    function AddCC($address, $name = "") {
303
+        $cur = count($this->cc);
304
+        $this->cc[$cur][0] = trim($address);
305
+        $this->cc[$cur][1] = $name;
306
+    }
307
+
308
+    /**
309
+     * Adds a "Bcc" address. Note: this function works
310
+     * with the SMTP mailer on win32, not with the "mail"
311
+     * mailer.  
312
+     * @param string $address
313
+     * @param string $name
314
+     * @return void
315
+     */
316
+    function AddBCC($address, $name = "") {
317
+        $cur = count($this->bcc);
318
+        $this->bcc[$cur][0] = trim($address);
319
+        $this->bcc[$cur][1] = $name;
320
+    }
321
+
322
+    /**
323
+     * Adds a "Reply-to" address.  
324
+     * @param string $address
325
+     * @param string $name
326
+     * @return void
327
+     */
328
+    function AddReplyTo($address, $name = "") {
329
+        $cur = count($this->ReplyTo);
330
+        $this->ReplyTo[$cur][0] = trim($address);
331
+        $this->ReplyTo[$cur][1] = $name;
332
+    }
333
+
334
+
335
+    /////////////////////////////////////////////////
336
+    // MAIL SENDING METHODS
337
+    /////////////////////////////////////////////////
338
+
339
+    /**
340
+     * Creates message and assigns Mailer. If the message is
341
+     * not sent successfully then it returns false.  Use the ErrorInfo
342
+     * variable to view description of the error.  
343
+     * @return bool
344
+     */
345
+    function Send() {
346
+        $header = "";
347
+        $body = "";
348
+        $result = true;
349
+
350
+        if((count($this->to) + count($this->cc) + count($this->bcc)) < 1)
351
+        {
352
+            $this->SetError($this->Lang("provide_address"));
353
+            return false;
354
+        }
355
+
356
+        // Set whether the message is multipart/alternative
357
+        if(!empty($this->AltBody))
358
+            $this->ContentType = "multipart/alternative";
359
+
360
+        $this->error_count = 0; // reset errors
361
+        $this->SetMessageType();
362
+        $header .= $this->CreateHeader();
363
+        $body = $this->CreateBody();
364
+
365
+        if($body == "") { return false; }
366
+
367
+        // Choose the mailer
368
+        switch($this->Mailer)
369
+        {
370
+            case "sendmail":
371
+                $result = $this->SendmailSend($header, $body);
372
+                break;
373
+            case "mail":
374
+                $result = $this->MailSend($header, $body);
375
+                break;
376
+            case "smtp":
377
+                $result = $this->SmtpSend($header, $body);
378
+                break;
379
+            default:
380
+            $this->SetError($this->Mailer . $this->Lang("mailer_not_supported"));
381
+                $result = false;
382
+                break;
383
+        }
384
+
385
+        return $result;
386
+    }
387
+    
388
+    /**
389
+     * Sends mail using the $Sendmail program.  
390
+     * @access private
391
+     * @return bool
392
+     */
393
+    function SendmailSend($header, $body) {
394
+        if ($this->Sender != "")
395
+            $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender);
396
+        else
397
+            $sendmail = sprintf("%s -oi -t", $this->Sendmail);
398
+
399
+        if(!@$mail = popen($sendmail, "w"))
400
+        {
401
+            $this->SetError($this->Lang("execute") . $this->Sendmail);
402
+            return false;
403
+        }
404
+
405
+        fputs($mail, $header);
406
+        fputs($mail, $body);
407
+        
408
+        $result = pclose($mail) >> 8 & 0xFF;
409
+        if($result != 0)
410
+        {
411
+            $this->SetError($this->Lang("execute") . $this->Sendmail);
412
+            return false;
413
+        }
414
+
415
+        return true;
416
+    }
417
+
418
+    /**
419
+     * Sends mail using the PHP mail() function.  
420
+     * @access private
421
+     * @return bool
422
+     */
423
+    function MailSend($header, $body) {
424
+        $to = "";
425
+        for($i = 0; $i < count($this->to); $i++)
426
+        {
427
+            if($i != 0) { $to .= ", "; }
428
+            $to .= $this->to[$i][0];
429
+        }
430
+
431
+        if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1)
432
+        {
433
+            $old_from = ini_get("sendmail_from");
434
+            ini_set("sendmail_from", $this->Sender);
435
+            $params = sprintf("-oi -f %s", $this->Sender);
436
+            $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, 
437
+                        $header, $params);
438
+        }
439
+        else
440
+            $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header);
441
+
442
+        if (isset($old_from))
443
+            ini_set("sendmail_from", $old_from);
444
+
445
+        if(!$rt)
446
+        {
447
+            $this->SetError($this->Lang("instantiate"));
448
+            return false;
449
+        }
450
+
451
+        return true;
452
+    }
453
+
454
+    /**
455
+     * Sends mail via SMTP using PhpSMTP (Author:
456
+     * Chris Ryan).  Returns bool.  Returns false if there is a
457
+     * bad MAIL FROM, RCPT, or DATA input.
458
+     * @access private
459
+     * @return bool
460
+     */
461
+    function SmtpSend($header, $body) {
462
+        include_once(_BASEDIR."includes/smtp_include.php");
463
+        $error = "";
464
+        $bad_rcpt = array();
465
+
466
+        if(!$this->SmtpConnect())
467
+            return false;
468
+
469
+        $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender;
470
+        if(!$this->smtp->Mail($smtp_from))
471
+        {
472
+            $error = $this->Lang("from_failed") . $smtp_from;
473
+            $this->SetError($error);
474
+            $this->smtp->Reset();
475
+            return false;
476
+        }
477
+
478
+        // Attempt to send attach all recipients
479
+        for($i = 0; $i < count($this->to); $i++)
480
+        {
481
+            if(!$this->smtp->Recipient($this->to[$i][0]))
482
+                $bad_rcpt[] = $this->to[$i][0];
483
+        }
484
+        for($i = 0; $i < count($this->cc); $i++)
485
+        {
486
+            if(!$this->smtp->Recipient($this->cc[$i][0]))
487
+                $bad_rcpt[] = $this->cc[$i][0];
488
+        }
489
+        for($i = 0; $i < count($this->bcc); $i++)
490
+        {
491
+            if(!$this->smtp->Recipient($this->bcc[$i][0]))
492
+                $bad_rcpt[] = $this->bcc[$i][0];
493
+        }
494
+
495
+        if(count($bad_rcpt) > 0) // Create error message
496
+        {
497
+            for($i = 0; $i < count($bad_rcpt); $i++)
498
+            {
499
+                if($i != 0) { $error .= ", "; }
500
+                $error .= $bad_rcpt[$i];
501
+            }
502
+            $error = $this->Lang("recipients_failed") . $error;
503
+            $this->SetError($error);
504
+            $this->smtp->Reset();
505
+            return false;
506
+        }
507
+
508
+        if(!$this->smtp->Data($header . $body))
509
+        {
510
+            $this->SetError($this->Lang("data_not_accepted"));
511
+            $this->smtp->Reset();
512
+            return false;
513
+        }
514
+        if($this->SMTPKeepAlive == true)
515
+            $this->smtp->Reset();
516
+        else
517
+            $this->SmtpClose();
518
+
519
+        return true;
520
+    }
521
+
522
+    /**
523
+     * Initiates a connection to an SMTP server.  Returns false if the 
524
+     * operation failed.
525
+     * @access private
526
+     * @return bool
527
+     */
528
+    function SmtpConnect() {
529
+        if($this->smtp == NULL) { $this->smtp = new SMTP(); }
530
+
531
+        $this->smtp->do_debug = $this->SMTPDebug;
532
+        $hosts = explode(";", $this->Host);
533
+        $index = 0;
534
+        $connection = ($this->smtp->Connected()); 
535
+
536
+        // Retry while there is no connection
537
+        while($index < count($hosts) && $connection == false)
538
+        {
539
+            if(strstr($hosts[$index], ":"))
540
+                list($host, $port) = explode(":", $hosts[$index]);
541
+            else
542
+            {
543
+                $host = $hosts[$index];
544
+                $port = $this->Port;
545
+            }
546
+
547
+            if($this->smtp->Connect($host, $port, $this->Timeout))
548
+            {
549
+                if ($this->Helo != '')
550
+                    $this->smtp->Hello($this->Helo);
551
+                else
552
+                    $this->smtp->Hello($this->ServerHostname());
553
+        
554
+                if($this->SMTPAuth)
555
+                {
556
+                    if(!$this->smtp->Authenticate($this->Username, 
557
+                                                  $this->Password))
558
+                    {
559
+                        $this->SetError($this->Lang("authenticate"));
560
+                        $this->smtp->Reset();
561
+                        $connection = false;
562
+                    }
563
+                }
564
+                $connection = true;
565
+            }
566
+            $index++;
567
+        }
568
+        if(!$connection)
569
+            $this->SetError($this->Lang("connect_host"));
570
+
571
+        return $connection;
572
+    }
573
+
574
+    /**
575
+     * Closes the active SMTP session if one exists.
576
+     * @return void
577
+     */
578
+    function SmtpClose() {
579
+        if($this->smtp != NULL)
580
+        {
581
+            if($this->smtp->Connected())
582
+            {
583
+                $this->smtp->Quit();
584
+                $this->smtp->Close();
585
+            }
586
+        }
587
+    }
588
+
589
+    /**
590
+     * Sets the language for all class error messages.  Returns false 
591
+     * if it cannot load the language file.  The default language type
592
+     * is English.
593
+     * @param string $lang_type Type of language (e.g. Portuguese: "br")
594
+     * @param string $lang_path Path to the language file directory
595
+     * @access public
596
+     * @return bool
597
+     */
598
+    function SetLanguage($lang_type, $lang_path = "language/") {
599
+        if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php'))
600
+            include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
601
+        else if(file_exists($lang_path.'phpmailer.lang-en.php'))
602
+            include($lang_path.'phpmailer.lang-en.php');
603
+        else
604
+        {
605
+            $this->SetError("Could not load language file");
606
+            return false;
607
+        }
608
+        $this->language = $PHPMAILER_LANG;
609
+    
610
+        return true;
611
+    }
612
+
613
+    /////////////////////////////////////////////////
614
+    // MESSAGE CREATION METHODS
615
+    /////////////////////////////////////////////////
616
+
617
+    /**
618
+     * Creates recipient headers.  
619
+     * @access private
620
+     * @return string
621
+     */
622
+    function AddrAppend($type, $addr) {
623
+        $addr_str = $type . ": ";
624
+        $addr_str .= $this->AddrFormat($addr[0]);
625
+        if(count($addr) > 1)
626
+        {
627
+            for($i = 1; $i < count($addr); $i++)
628
+                $addr_str .= ", " . $this->AddrFormat($addr[$i]);
629
+        }
630
+        $addr_str .= $this->LE;
631
+
632
+        return $addr_str;
633
+    }
634
+    
635
+    /**
636
+     * Formats an address correctly. 
637
+     * @access private
638
+     * @return string
639
+     */
640
+    function AddrFormat($addr) {
641
+        if(empty($addr[1]))
642
+            $formatted = $addr[0];
643
+        else
644
+        {
645
+            $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" . 
646
+                         $addr[0] . ">";
647
+        }
648
+
649
+        return $formatted;
650
+    }
651
+
652
+    /**
653
+     * Wraps message for use with mailers that do not
654
+     * automatically perform wrapping and for quoted-printable.
655
+     * Original written by philippe.  
656
+     * @access private
657
+     * @return string
658
+     */
659
+    function WrapText($message, $length, $qp_mode = false) {
660
+        $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
661
+
662
+        $message = $this->FixEOL($message);
663
+        if (substr($message, -1) == $this->LE)
664
+            $message = substr($message, 0, -1);
665
+
666
+        $line = explode($this->LE, $message);
667
+        $message = "";
668
+        for ($i=0 ;$i < count($line); $i++)
669
+        {
670
+          $line_part = explode(" ", $line[$i]);
671
+          $buf = "";
672
+          for ($e = 0; $e<count($line_part); $e++)
673
+          {
674
+              $word = $line_part[$e];
675
+              if ($qp_mode and (strlen($word) > $length))
676
+              {
677
+                $space_left = $length - strlen($buf) - 1;
678
+                if ($e != 0)
679
+                {
680
+                    if ($space_left > 20)
681
+                    {
682
+                        $len = $space_left;
683
+                        if (substr($word, $len - 1, 1) == "=")
684
+                          $len--;
685
+                        elseif (substr($word, $len - 2, 1) == "=")
686
+                          $len -= 2;
687
+                        $part = substr($word, 0, $len);
688
+                        $word = substr($word, $len);
689
+                        $buf .= " " . $part;
690
+                        $message .= $buf . sprintf("=%s", $this->LE);
691
+                    }
692
+                    else
693
+                    {
694
+                        $message .= $buf . $soft_break;
695
+                    }
696
+                    $buf = "";
697
+                }
698
+                while (strlen($word) > 0)
699
+                {
700
+                    $len = $length;
701
+                    if (substr($word, $len - 1, 1) == "=")
702
+                        $len--;
703
+                    elseif (substr($word, $len - 2, 1) == "=")
704
+                        $len -= 2;
705
+                    $part = substr($word, 0, $len);
706
+                    $word = substr($word, $len);
707
+
708
+                    if (strlen($word) > 0)
709
+                        $message .= $part . sprintf("=%s", $this->LE);
710
+                    else
711
+                        $buf = $part;
712
+                }
713
+              }
714
+              else
715
+              {
716
+                $buf_o = $buf;
717
+                $buf .= ($e == 0) ? $word : (" " . $word); 
718
+
719
+                if (strlen($buf) > $length and $buf_o != "")
720
+                {
721
+                    $message .= $buf_o . $soft_break;
722
+                    $buf = $word;
723
+                }
724
+              }
725
+          }
726
+          $message .= $buf . $this->LE;
727
+        }
728
+
729
+        return $message;
730
+    }
731
+    
732
+    /**
733
+     * Set the body wrapping.
734
+     * @access private
735
+     * @return void
736
+     */
737
+    function SetWordWrap() {
738
+        if($this->WordWrap < 1)
739
+            return;
740
+            
741
+        switch($this->message_type)
742
+        {
743
+           case "alt":
744
+              // fall through
745
+           case "alt_attachments":
746
+              $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
747
+              break;
748
+           default:
749
+              $this->Body = $this->WrapText($this->Body, $this->WordWrap);
750
+              break;
751
+        }
752
+    }
753
+
754
+    /**
755
+     * Assembles message header.  
756
+     * @access private
757
+     * @return string
758
+     */
759
+    function CreateHeader() {
760
+        $result = "";
761
+        
762
+        // Set the boundaries
763
+        $uniq_id = md5(uniqid(time()));
764
+        $this->boundary[1] = "b1_" . $uniq_id;
765
+        $this->boundary[2] = "b2_" . $uniq_id;
766
+
767
+        $result .= $this->HeaderLine("Date", $this->RFCDate());
768
+        if($this->Sender == "")
769
+            $result .= $this->HeaderLine("Return-Path", trim($this->From));
770
+        else
771
+            $result .= $this->HeaderLine("Return-Path", trim($this->Sender));
772
+        
773
+        // To be created automatically by mail()
774
+        if($this->Mailer != "mail")
775
+        {
776
+            if(count($this->to) > 0)
777
+                $result .= $this->AddrAppend("To", $this->to);
778
+            else if (count($this->cc) == 0)
779
+                $result .= $this->HeaderLine("To", "undisclosed-recipients:;");
780
+            if(count($this->cc) > 0)
781
+                $result .= $this->AddrAppend("Cc", $this->cc);
782
+        }
783
+
784
+        $from = array();
785
+        $from[0][0] = trim($this->From);
786
+        $from[0][1] = $this->FromName;
787
+        $result .= $this->AddrAppend("From", $from); 
788
+
789
+        // sendmail and mail() extract Bcc from the header before sending
790
+        if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0))
791
+            $result .= $this->AddrAppend("Bcc", $this->bcc);
792
+
793
+        if(count($this->ReplyTo) > 0)
794
+            $result .= $this->AddrAppend("Reply-to", $this->ReplyTo);
795
+
796
+        // mail() sets the subject itself
797
+        if($this->Mailer != "mail")
798
+            $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject)));
799
+
800
+        $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
801
+        $result .= $this->HeaderLine("X-Priority", $this->Priority);
802
+        $result .= $this->HeaderLine("X-Mailer", "PHPMailer [version " . $this->Version . "]");
803
+        
804
+        if($this->ConfirmReadingTo != "")
805
+        {
806
+            $result .= $this->HeaderLine("Disposition-Notification-To", 
807
+                       "<" . trim($this->ConfirmReadingTo) . ">");
808
+        }
809
+
810
+        // Add custom headers
811
+        for($index = 0; $index < count($this->CustomHeader); $index++)
812
+        {
813
+            $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), 
814
+                       $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
815
+        }
816
+        $result .= $this->HeaderLine("MIME-Version", "1.0");
817
+
818
+        switch($this->message_type)
819
+        {
820
+            case "plain":
821
+                $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding);
822
+                $result .= sprintf("Content-Type: %s; charset=\"%s\"",
823
+                                    $this->ContentType, $this->CharSet);
824
+                break;
825
+            case "attachments":
826
+                // fall through
827
+            case "alt_attachments":
828
+                if($this->InlineImageExists())
829
+                {
830
+                    $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 
831
+                                    "multipart/related", $this->LE, $this->LE, 
832
+                                    $this->boundary[1], $this->LE);
833
+                }
834
+                else
835
+                {
836
+                    $result .= $this->HeaderLine("Content-Type", "multipart/mixed;");
837
+                    $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
838
+                }
839
+                break;
840
+            case "alt":
841
+                $result .= $this->HeaderLine("Content-Type", "multipart/alternative;");
842
+                $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
843
+                break;
844
+        }
845
+
846
+        if($this->Mailer != "mail")
847
+            $result .= $this->LE.$this->LE;
848
+
849
+        return $result;
850
+    }
851
+
852
+    /**
853
+     * Assembles the message body.  Returns an empty string on failure.
854
+     * @access private
855
+     * @return string
856
+     */
857
+    function CreateBody() {
858
+        $result = "";
859
+
860
+        $this->SetWordWrap();
861
+
862
+        switch($this->message_type)
863
+        {
864
+            case "alt":
865
+                $result .= $this->GetBoundary($this->boundary[1], "", 
866
+                                              "text/plain", "");
867
+                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
868
+                $result .= $this->LE.$this->LE;
869
+                $result .= $this->GetBoundary($this->boundary[1], "", 
870
+                                              "text/html", "");
871
+                
872
+                $result .= $this->EncodeString($this->Body, $this->Encoding);
873
+                $result .= $this->LE.$this->LE;
874
+    
875
+                $result .= $this->EndBoundary($this->boundary[1]);
876
+                break;
877
+            case "plain":
878
+                $result .= $this->EncodeString($this->Body, $this->Encoding);
879
+                break;
880
+            case "attachments":
881
+                $result .= $this->GetBoundary($this->boundary[1], "", "", "");
882
+                $result .= $this->EncodeString($this->Body, $this->Encoding);
883
+                $result .= $this->LE;
884
+     
885
+                $result .= $this->AttachAll();
886
+                break;
887
+            case "alt_attachments":
888
+                $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
889
+                $result .= sprintf("Content-Type: %s;%s" .
890
+                                   "\tboundary=\"%s\"%s",
891
+                                   "multipart/alternative", $this->LE, 
892
+                                   $this->boundary[2], $this->LE.$this->LE);
893
+    
894
+                // Create text body
895
+                $result .= $this->GetBoundary($this->boundary[2], "", 
896
+                                              "text/plain", "") . $this->LE;
897
+
898
+                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
899
+                $result .= $this->LE.$this->LE;
900
+    
901
+                // Create the HTML body
902
+                $result .= $this->GetBoundary($this->boundary[2], "", 
903
+                                              "text/html", "") . $this->LE;
904
+    
905
+                $result .= $this->EncodeString($this->Body, $this->Encoding);
906
+                $result .= $this->LE.$this->LE;
907
+
908
+                $result .= $this->EndBoundary($this->boundary[2]);
909
+                
910
+                $result .= $this->AttachAll();
911
+                break;
912
+        }
913
+        if($this->IsError())
914
+            $result = "";
915
+
916
+        return $result;
917
+    }
918
+
919
+    /**
920
+     * Returns the start of a message boundary.
921
+     * @access private
922
+     */
923
+    function GetBoundary($boundary, $charSet, $contentType, $encoding) {
924
+        $result = "";
925
+        if($charSet == "") { $charSet = $this->CharSet; }
926
+        if($contentType == "") { $contentType = $this->ContentType; }
927
+        if($encoding == "") { $encoding = $this->Encoding; }
928
+
929
+        $result .= $this->TextLine("--" . $boundary);
930
+        $result .= sprintf("Content-Type: %s; charset = \"%s\"", 
931
+                            $contentType, $charSet);
932
+        $result .= $this->LE;
933
+        $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding);
934
+        $result .= $this->LE;
935
+       
936
+        return $result;
937
+    }
938
+    
939
+    /**
940
+     * Returns the end of a message boundary.
941
+     * @access private
942
+     */
943
+    function EndBoundary($boundary) {
944
+        return $this->LE . "--" . $boundary . "--" . $this->LE; 
945
+    }
946
+    
947
+    /**
948
+     * Sets the message type.
949
+     * @access private
950
+     * @return void
951
+     */
952
+    function SetMessageType() {
953
+        if(count($this->attachment) < 1 && strlen($this->AltBody) < 1)
954
+            $this->message_type = "plain";
955
+        else
956
+        {
957
+            if(count($this->attachment) > 0)
958
+                $this->message_type = "attachments";
959
+            if(strlen($this->AltBody) > 0 && count($this->attachment) < 1)
960
+                $this->message_type = "alt";
961
+            if(strlen($this->AltBody) > 0 && count($this->attachment) > 0)
962
+                $this->message_type = "alt_attachments";
963
+        }
964
+    }
965
+
966
+    /**
967
+     * Returns a formatted header line.
968
+     * @access private
969
+     * @return string
970
+     */
971
+    function HeaderLine($name, $value) {
972
+        return $name . ": " . $value . $this->LE;
973
+    }
974
+
975
+    /**
976
+     * Returns a formatted mail line.
977
+     * @access private
978
+     * @return string
979
+     */
980
+    function TextLine($value) {
981
+        return $value . $this->LE;
982
+    }
983
+
984
+    /////////////////////////////////////////////////
985
+    // ATTACHMENT METHODS
986
+    /////////////////////////////////////////////////
987
+
988
+    /**
989
+     * Adds an attachment from a path on the filesystem.
990
+     * Returns false if the file could not be found
991
+     * or accessed.
992
+     * @param string $path Path to the attachment.
993
+     * @param string $name Overrides the attachment name.
994
+     * @param string $encoding File encoding (see $Encoding).
995
+     * @param string $type File extension (MIME) type.
996
+     * @return bool
997
+     */
998
+    function AddAttachment($path, $name = "", $encoding = "base64", 
999
+                           $type = "application/octet-stream") {
1000
+        if(!@is_file($path))
1001
+        {
1002
+            $this->SetError($this->Lang("file_access") . $path);
1003
+            return false;
1004
+        }
1005
+
1006
+        $filename = basename($path);
1007
+        if($name == "")
1008
+            $name = $filename;
1009
+
1010
+        $cur = count($this->attachment);
1011
+        $this->attachment[$cur][0] = $path;
1012
+        $this->attachment[$cur][1] = $filename;
1013
+        $this->attachment[$cur][2] = $name;
1014
+        $this->attachment[$cur][3] = $encoding;
1015
+        $this->attachment[$cur][4] = $type;
1016
+        $this->attachment[$cur][5] = false; // isStringAttachment
1017
+        $this->attachment[$cur][6] = "attachment";
1018
+        $this->attachment[$cur][7] = 0;
1019
+
1020
+        return true;
1021
+    }
1022
+
1023
+    /**
1024
+     * Attaches all fs, string, and binary attachments to the message.
1025
+     * Returns an empty string on failure.
1026
+     * @access private
1027
+     * @return string
1028
+     */
1029
+    function AttachAll() {
1030
+        // Return text of body
1031
+        $mime = array();
1032
+
1033
+        // Add all attachments
1034
+        for($i = 0; $i < count($this->attachment); $i++)
1035
+        {
1036
+            // Check for string attachment
1037
+            $bString = $this->attachment[$i][5];
1038
+            if ($bString)
1039
+                $string = $this->attachment[$i][0];
1040
+            else
1041
+                $path = $this->attachment[$i][0];
1042
+
1043
+            $filename    = $this->attachment[$i][1];
1044
+            $name        = $this->attachment[$i][2];
1045
+            $encoding    = $this->attachment[$i][3];
1046
+            $type        = $this->attachment[$i][4];
1047
+            $disposition = $this->attachment[$i][6];
1048
+            $cid         = $this->attachment[$i][7];
1049
+            
1050
+            $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1051
+            $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
1052
+            $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1053
+
1054
+            if($disposition == "inline")
1055
+                $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1056
+
1057
+            $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", 
1058
+                              $disposition, $name, $this->LE.$this->LE);
1059
+
1060
+            // Encode as string attachment
1061
+            if($bString)
1062
+            {
1063
+                $mime[] = $this->EncodeString($string, $encoding);
1064
+                if($this->IsError()) { return ""; }
1065
+                $mime[] = $this->LE.$this->LE;
1066
+            }
1067
+            else
1068
+            {
1069
+                $mime[] = $this->EncodeFile($path, $encoding);                
1070
+                if($this->IsError()) { return ""; }
1071
+                $mime[] = $this->LE.$this->LE;
1072
+            }
1073
+        }
1074
+
1075
+        $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1076
+
1077
+        return join("", $mime);
1078
+    }
1079
+    
1080
+    /**
1081
+     * Encodes attachment in requested format.  Returns an
1082
+     * empty string on failure.
1083
+     * @access private
1084
+     * @return string
1085
+     */
1086
+    function EncodeFile ($path, $encoding = "base64") {
1087
+        if(!@$fd = fopen($path, "rb"))
1088
+        {
1089
+            $this->SetError($this->Lang("file_open") . $path);
1090
+            return "";
1091
+        }
1092
+        $magic_quotes = get_magic_quotes_runtime();
1093
+        set_magic_quotes_runtime(0);
1094
+        $file_buffer = fread($fd, filesize($path));
1095
+        $file_buffer = $this->EncodeString($file_buffer, $encoding);
1096
+        fclose($fd);
1097
+        set_magic_quotes_runtime($magic_quotes);
1098
+
1099
+        return $file_buffer;
1100
+    }
1101
+
1102
+    /**
1103
+     * Encodes string to requested format. Returns an
1104
+     * empty string on failure.
1105
+     * @access private
1106
+     * @return string
1107
+     */
1108
+    function EncodeString ($str, $encoding = "base64") {
1109
+        $encoded = "";
1110
+        switch(strtolower($encoding)) {
1111
+          case "base64":
1112
+              // chunk_split is found in PHP >= 3.0.6
1113
+              $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1114
+              break;
1115
+          case "7bit":
1116
+          case "8bit":
1117
+              $encoded = $this->FixEOL($str);
1118
+              if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1119
+                $encoded .= $this->LE;
1120
+              break;
1121
+          case "binary":
1122
+              $encoded = $str;
1123
+              break;
1124
+          case "quoted-printable":
1125
+              $encoded = $this->EncodeQP($str);
1126
+              break;
1127
+          default:
1128
+              $this->SetError($this->Lang("encoding") . $encoding);
1129
+              break;
1130
+        }
1131
+        return $encoded;
1132
+    }
1133
+
1134
+    /**
1135
+     * Encode a header string to best of Q, B, quoted or none.  
1136
+     * @access private
1137
+     * @return string
1138
+     */
1139
+    function EncodeHeader ($str, $position = 'text') {
1140
+      $x = 0;
1141
+      
1142
+      switch (strtolower($position)) {
1143
+        case 'phrase':
1144
+          if (!preg_match('/[\200-\377]/', $str)) {
1145
+            // Can't use addslashes as we don't know what value has magic_quotes_sybase.
1146
+            $encoded = addcslashes($str, "\0..\37\177\\\"");
1147
+
1148
+            if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str))
1149
+              return ($encoded);
1150
+            else
1151
+              return ("\"$encoded\"");
1152
+          }
1153
+          $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1154
+          break;
1155
+        case 'comment':
1156
+          $x = preg_match_all('/[()"]/', $str, $matches);
1157
+          // Fall-through
1158
+        case 'text':
1159
+        default:
1160
+          $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1161
+          break;
1162
+      }
1163
+
1164
+      if ($x == 0)
1165
+        return ($str);
1166
+
1167
+      $maxlen = 75 - 7 - strlen($this->CharSet);
1168
+      // Try to select the encoding which should produce the shortest output
1169
+      if (strlen($str)/3 < $x) {
1170
+        $encoding = 'B';
1171
+        $encoded = base64_encode($str);
1172
+        $maxlen -= $maxlen % 4;
1173
+        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1174
+      } else {
1175
+        $encoding = 'Q';
1176
+        $encoded = $this->EncodeQ($str, $position);
1177
+        $encoded = $this->WrapText($encoded, $maxlen, true);
1178
+        $encoded = str_replace("=".$this->LE, "\n", trim($encoded));
1179
+      }
1180
+
1181
+      $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1182
+      $encoded = trim(str_replace("\n", $this->LE, $encoded));
1183
+      
1184
+      return $encoded;
1185
+    }
1186
+    
1187
+    /**
1188
+     * Encode string to quoted-printable.  
1189
+     * @access private
1190
+     * @return string
1191
+     */
1192
+    function EncodeQP ($str) {
1193
+        $encoded = $this->FixEOL($str);
1194
+        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1195
+            $encoded .= $this->LE;
1196
+
1197
+        // Replace every high ascii, control and = characters
1198
+        $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e',
1199
+                  "'='.sprintf('%02X', ord('\\1'))", $encoded);
1200
+        // Replace every spaces and tabs when it's the last character on a line
1201
+        $encoded = preg_replace("/([\011\040])".$this->LE."/e",
1202
+                  "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded);
1203
+
1204
+        // Maximum line length of 76 characters before CRLF (74 + space + '=')
1205
+        $encoded = $this->WrapText($encoded, 74, true);
1206
+
1207
+        return $encoded;
1208
+    }
1209
+
1210
+    /**
1211
+     * Encode string to q encoding.  
1212
+     * @access private
1213
+     * @return string
1214
+     */
1215
+    function EncodeQ ($str, $position = "text") {
1216
+        // There should not be any EOL in the string
1217
+        $encoded = preg_replace("[\r\n]", "", $str);
1218
+
1219
+        switch (strtolower($position)) {
1220
+          case "phrase":
1221
+            $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1222
+            break;
1223
+          case "comment":
1224
+            $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1225
+          case "text":
1226
+          default:
1227
+            // Replace every high ascii, control =, ? and _ characters
1228
+            $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1229
+                  "'='.sprintf('%02X', ord('\\1'))", $encoded);
1230
+            break;
1231
+        }
1232
+        
1233
+        // Replace every spaces to _ (more readable than =20)
1234
+        $encoded = str_replace(" ", "_", $encoded);
1235
+
1236
+        return $encoded;
1237
+    }
1238
+
1239
+    /**
1240
+     * Adds a string or binary attachment (non-filesystem) to the list.
1241
+     * This method can be used to attach ascii or binary data,
1242
+     * such as a BLOB record from a database.
1243
+     * @param string $string String attachment data.
1244
+     * @param string $filename Name of the attachment.
1245
+     * @param string $encoding File encoding (see $Encoding).
1246
+     * @param string $type File extension (MIME) type.
1247
+     * @return void
1248
+     */
1249
+    function AddStringAttachment($string, $filename, $encoding = "base64", 
1250
+                                 $type = "application/octet-stream") {
1251
+        // Append to $attachment array
1252
+        $cur = count($this->attachment);
1253
+        $this->attachment[$cur][0] = $string;
1254
+        $this->attachment[$cur][1] = $filename;
1255
+        $this->attachment[$cur][2] = $filename;
1256
+        $this->attachment[$cur][3] = $encoding;
1257
+        $this->attachment[$cur][4] = $type;
1258
+        $this->attachment[$cur][5] = true; // isString
1259
+        $this->attachment[$cur][6] = "attachment";
1260
+        $this->attachment[$cur][7] = 0;
1261
+    }
1262
+    
1263
+    /**
1264
+     * Adds an embedded attachment.  This can include images, sounds, and 
1265
+     * just about any other document.  Make sure to set the $type to an 
1266
+     * image type.  For JPEG images use "image/jpeg" and for GIF images 
1267
+     * use "image/gif".
1268
+     * @param string $path Path to the attachment.
1269
+     * @param string $cid Content ID of the attachment.  Use this to identify 
1270
+     *        the Id for accessing the image in an HTML form.
1271
+     * @param string $name Overrides the attachment name.
1272
+     * @param string $encoding File encoding (see $Encoding).
1273
+     * @param string $type File extension (MIME) type.  
1274
+     * @return bool
1275
+     */
1276
+    function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64", 
1277
+                              $type = "application/octet-stream") {
1278
+    
1279
+        if(!@is_file($path))
1280
+        {
1281
+            $this->SetError($this->Lang("file_access") . $path);
1282
+            return false;
1283
+        }
1284
+
1285
+        $filename = basename($path);
1286
+        if($name == "")
1287
+            $name = $filename;
1288
+
1289
+        // Append to $attachment array
1290
+        $cur = count($this->attachment);
1291
+        $this->attachment[$cur][0] = $path;
1292
+        $this->attachment[$cur][1] = $filename;
1293
+        $this->attachment[$cur][2] = $name;
1294
+        $this->attachment[$cur][3] = $encoding;
1295
+        $this->attachment[$cur][4] = $type;
1296
+        $this->attachment[$cur][5] = false; // isStringAttachment
1297
+        $this->attachment[$cur][6] = "inline";
1298
+        $this->attachment[$cur][7] = $cid;
1299
+    
1300
+        return true;
1301
+    }
1302
+    
1303
+    /**
1304
+     * Returns true if an inline attachment is present.
1305
+     * @access private
1306
+     * @return bool
1307
+     */
1308
+    function InlineImageExists() {
1309
+        $result = false;
1310
+        for($i = 0; $i < count($this->attachment); $i++)
1311
+        {
1312
+            if($this->attachment[$i][6] == "inline")
1313
+            {
1314
+                $result = true;
1315
+                break;
1316
+            }
1317
+        }
1318
+        
1319
+        return $result;
1320
+    }
1321
+
1322
+    /////////////////////////////////////////////////
1323
+    // MESSAGE RESET METHODS
1324
+    /////////////////////////////////////////////////
1325
+
1326
+    /**
1327
+     * Clears all recipients assigned in the TO array.  Returns void.
1328
+     * @return void
1329
+     */
1330
+    function ClearAddresses() {
1331
+        $this->to = array();
1332
+    }
1333
+
1334
+    /**
1335
+     * Clears all recipients assigned in the CC array.  Returns void.
1336
+     * @return void
1337
+     */
1338
+    function ClearCCs() {
1339
+        $this->cc = array();
1340
+    }
1341
+
1342
+    /**
1343
+     * Clears all recipients assigned in the BCC array.  Returns void.
1344
+     * @return void
1345
+     */
1346
+    function ClearBCCs() {
1347
+        $this->bcc = array();
1348
+    }
1349
+
1350
+    /**
1351
+     * Clears all recipients assigned in the ReplyTo array.  Returns void.
1352
+     * @return void
1353
+     */
1354
+    function ClearReplyTos() {
1355
+        $this->ReplyTo = array();
1356
+    }
1357
+
1358
+    /**
1359
+     * Clears all recipients assigned in the TO, CC and BCC
1360
+     * array.  Returns void.
1361
+     * @return void
1362
+     */
1363
+    function ClearAllRecipients() {
1364
+        $this->to = array();
1365
+        $this->cc = array();
1366
+        $this->bcc = array();
1367
+    }
1368
+
1369
+    /**
1370
+     * Clears all previously set filesystem, string, and binary
1371
+     * attachments.  Returns void.
1372
+     * @return void
1373
+     */
1374
+    function ClearAttachments() {
1375
+        $this->attachment = array();
1376
+    }
1377
+
1378
+    /**
1379
+     * Clears all custom headers.  Returns void.
1380
+     * @return void
1381
+     */
1382
+    function ClearCustomHeaders() {
1383
+        $this->CustomHeader = array();
1384
+    }
1385
+
1386
+
1387
+    /////////////////////////////////////////////////
1388
+    // MISCELLANEOUS METHODS
1389
+    /////////////////////////////////////////////////
1390
+
1391
+    /**
1392
+     * Adds the error message to the error container.
1393
+     * Returns void.
1394
+     * @access private
1395
+     * @return void
1396
+     */
1397
+    function SetError($msg) {
1398
+        $this->error_count++;
1399
+        $this->ErrorInfo = $msg;
1400
+    }
1401
+
1402
+    /**
1403
+     * Returns the proper RFC 822 formatted date. 
1404
+     * @access private
1405
+     * @return string
1406
+     */
1407
+    function RFCDate() {
1408
+        $tz = date("Z");
1409
+        $tzs = ($tz < 0) ? "-" : "+";
1410
+        $tz = abs($tz);
1411
+        $tz = ($tz/3600)*100 + ($tz%3600)/60;
1412
+        $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz);
1413
+
1414
+        return $result;
1415
+    }
1416
+    
1417
+    /**
1418
+     * Returns the appropriate server variable.  Should work with both 
1419
+     * PHP 4.1.0+ as well as older versions.  Returns an empty string 
1420
+     * if nothing is found.
1421
+     * @access private
1422
+     * @return mixed
1423
+     */
1424
+    function ServerVar($varName) {
1425
+        global $HTTP_SERVER_VARS;
1426
+        global $HTTP_ENV_VARS;
1427
+
1428
+        if(!isset($_SERVER))
1429
+        {
1430
+            $_SERVER = $HTTP_SERVER_VARS;
1431
+            if(!isset($_SERVER["REMOTE_ADDR"]))
1432
+                $_SERVER = $HTTP_ENV_VARS; // must be Apache
1433
+        }
1434
+        
1435
+        if(isset($_SERVER[$varName]))
1436
+            return $_SERVER[$varName];
1437
+        else
1438
+            return "";
1439
+    }
1440
+
1441
+    /**
1442
+     * Returns the server hostname or 'localhost.localdomain' if unknown.
1443
+     * @access private
1444
+     * @return string
1445
+     */
1446
+    function ServerHostname() {
1447
+        if ($this->Hostname != "")
1448
+            $result = $this->Hostname;
1449
+        elseif ($this->ServerVar('SERVER_NAME') != "")
1450
+            $result = $this->ServerVar('SERVER_NAME');
1451
+        else
1452
+            $result = "localhost.localdomain";
1453
+
1454
+        return $result;
1455
+    }
1456
+
1457
+    /**
1458
+     * Returns a message in the appropriate language.
1459
+     * @access private
1460
+     * @return string
1461
+     */
1462
+    function Lang($key) {
1463
+        if(count($this->language) < 1)
1464
+            $this->SetLanguage("en"); // set the default language
1465
+    
1466
+        if(isset($this->language[$key]))
1467
+            return $this->language[$key];
1468
+        else
1469
+            return "Language string failed to load: " . $key;
1470
+    }
1471
+    
1472
+    /**
1473
+     * Returns true if an error occurred.
1474
+     * @return bool
1475
+     */
1476
+    function IsError() {
1477
+        return ($this->error_count > 0);
1478
+    }
1479
+
1480
+    /**
1481
+     * Changes every end of line from CR or LF to CRLF.  
1482
+     * @access private
1483
+     * @return string
1484
+     */
1485
+    function FixEOL($str) {
1486
+        $str = str_replace("\r\n", "\n", $str);
1487
+        $str = str_replace("\r", "\n", $str);
1488
+        $str = str_replace("\n", $this->LE, $str);
1489
+        return $str;
1490
+    }
1491
+
1492
+    /**
1493
+     * Adds a custom header. 
1494
+     * @return void
1495
+     */
1496
+    function AddCustomHeader($custom_header) {
1497
+        $this->CustomHeader[] = explode(":", $custom_header, 2);
1498
+    }
1499
+}
1500
+
1501
+?>
0 1502
\ No newline at end of file
1 1503
new file mode 100644
... ...
@@ -0,0 +1,1046 @@
1
+<?php
2
+if(!defined("_CHARSET")) exit( );
3
+////////////////////////////////////////////////////
4
+// SMTP - PHP SMTP class
5
+//
6
+// Version 1.02
7
+//
8
+// Define an SMTP class that can be used to connect
9
+// and communicate with any SMTP server. It implements
10
+// all the SMTP functions defined in RFC821 except TURN.
11
+//
12
+// Author: Chris Ryan
13
+//
14
+// License: LGPL, see LICENSE
15
+////////////////////////////////////////////////////
16
+
17
+/**
18
+ * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
19
+ * commands except TURN which will always return a not implemented
20
+ * error. SMTP also provides some utility methods for sending mail
21
+ * to an SMTP server.
22
+ * @package PHPMailer
23
+ * @author Chris Ryan
24
+ */
25
+class SMTP
26
+{
27
+    /**
28
+     *  SMTP server port
29
+     *  @var int
30
+     */
31
+    var $SMTP_PORT = 25;
32
+    
33
+    /**
34
+     *  SMTP reply line ending
35
+     *  @var string
36
+     */
37
+    var $CRLF = "\r\n";
38
+    
39
+    /**
40
+     *  Sets whether debugging is turned on
41
+     *  @var bool
42
+     */
43
+    var $do_debug;       # the level of debug to perform
44
+
45
+    /**#@+
46
+     * @access private
47
+     */
48
+    var $smtp_conn;      # the socket to the server
49
+    var $error;          # error if any on the last call
50
+    var $helo_rply;      # the reply the server sent to us for HELO
51
+    /**#@-*/
52
+
53
+    /**
54
+     * Initialize the class so that the data is in a known state.
55
+     * @access public
56
+     * @return void
57
+     */
58
+    function SMTP() {
59
+        $this->smtp_conn = 0;
60
+        $this->error = null;
61
+        $this->helo_rply = null;
62
+
63
+        $this->do_debug = 0;
64
+    }
65
+
66
+    /*************************************************************
67
+     *                    CONNECTION FUNCTIONS                  *
68
+     ***********************************************************/
69
+
70
+    /**
71
+     * Connect to the server specified on the port specified.
72
+     * If the port is not specified use the default SMTP_PORT.
73
+     * If tval is specified then a connection will try and be
74
+     * established with the server for that number of seconds.
75
+     * If tval is not specified the default is 30 seconds to
76
+     * try on the connection.
77
+     *
78
+     * SMTP CODE SUCCESS: 220
79
+     * SMTP CODE FAILURE: 421
80
+     * @access public
81
+     * @return bool
82
+     */
83
+    function Connect($host,$port=0,$tval=30) {
84
+        # set the error val to null so there is no confusion
85
+        $this->error = null;
86
+
87
+        # make sure we are __not__ connected
88
+        if($this->connected()) {
89
+            # ok we are connected! what should we do?
90
+            # for now we will just give an error saying we
91
+            # are already connected
92
+            $this->error =
93
+                array("error" => "Already connected to a server");
94
+            return false;
95
+        }
96
+
97
+        if(empty($port)) {
98
+            $port = $this->SMTP_PORT;
99
+        }
100
+
101
+        #connect to the smtp server
102
+        $this->smtp_conn = fsockopen($host,    # the host of the server
103
+                                     $port,    # the port to use
104
+                                     $errno,   # error number if any
105
+                                     $errstr,  # error message if any
106
+                                     $tval);   # give up after ? secs
107
+        # verify we connected properly
108
+        if(empty($this->smtp_conn)) {
109
+            $this->error = array("error" => "Failed to connect to server",
110
+                                 "errno" => $errno,
111
+                                 "errstr" => $errstr);
112
+            if($this->do_debug >= 1) {
113
+                echo "SMTP -> ERROR: " . $this->error["error"] .
114
+                         ": $errstr ($errno)" . $this->CRLF;
115
+            }
116
+            return false;
117
+        }
118
+
119
+        # sometimes the SMTP server takes a little longer to respond
120
+        # so we will give it a longer timeout for the first read
121
+        // Windows still does not have support for this timeout function
122
+        if(substr(PHP_OS, 0, 3) != "WIN")
123
+           socket_set_timeout($this->smtp_conn, $tval, 0);
124
+
125
+        # get any announcement stuff
126
+        $announce = $this->get_lines();
127
+
128
+        # set the timeout  of any socket functions at 1/10 of a second
129
+        //if(function_exists("socket_set_timeout"))
130
+        //   socket_set_timeout($this->smtp_conn, 0, 100000);
131
+
132
+        if($this->do_debug >= 2) {
133
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
134
+        }
135
+
136
+        return true;
137
+    }
138
+
139
+    /**
140
+     * Performs SMTP authentication.  Must be run after running the
141
+     * Hello() method.  Returns true if successfully authenticated.
142
+     * @access public
143
+     * @return bool
144
+     */
145
+    function Authenticate($username, $password) {
146
+        // Start authentication
147
+        fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
148
+
149
+        $rply = $this->get_lines();
150
+        $code = substr($rply,0,3);
151
+
152
+        if($code != 334) {
153
+            $this->error =
154
+                array("error" => "AUTH not accepted from server",
155
+                      "smtp_code" => $code,
156
+                      "smtp_msg" => substr($rply,4));
157
+            if($this->do_debug >= 1) {
158
+                echo "SMTP -> ERROR: " . $this->error["error"] .
159
+                         ": " . $rply . $this->CRLF;
160
+            }
161
+            return false;
162
+        }
163
+
164
+        // Send encoded username
165
+        fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
166
+
167
+        $rply = $this->get_lines();
168
+        $code = substr($rply,0,3);
169
+
170
+        if($code != 334) {
171
+            $this->error =
172
+                array("error" => "Username not accepted from server",
173
+                      "smtp_code" => $code,
174
+                      "smtp_msg" => substr($rply,4));
175
+            if($this->do_debug >= 1) {
176
+                echo "SMTP -> ERROR: " . $this->error["error"] .
177
+                         ": " . $rply . $this->CRLF;
178
+            }
179
+            return false;
180
+        }
181
+
182
+        // Send encoded password
183
+        fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
184
+
185
+        $rply = $this->get_lines();
186
+        $code = substr($rply,0,3);
187
+
188
+        if($code != 235) {
189
+            $this->error =
190
+                array("error" => "Password not accepted from server",
191
+                      "smtp_code" => $code,
192
+                      "smtp_msg" => substr($rply,4));
193
+            if($this->do_debug >= 1) {
194
+                echo "SMTP -> ERROR: " . $this->error["error"] .
195
+                         ": " . $rply . $this->CRLF;
196
+            }
197
+            return false;
198
+        }
199
+
200
+        return true;
201
+    }
202
+
203
+    /**
204
+     * Returns true if connected to a server otherwise false
205
+     * @access private
206
+     * @return bool
207
+     */
208
+    function Connected() {
209
+        if(!empty($this->smtp_conn)) {
210
+            $sock_status = socket_get_status($this->smtp_conn);
211
+            if($sock_status["eof"]) {
212
+                # hmm this is an odd situation... the socket is
213
+                # valid but we aren't connected anymore
214
+                if($this->do_debug >= 1) {
215
+                    echo "SMTP -> NOTICE:" . $this->CRLF .
216
+                         "EOF caught while checking if connected";
217
+                }
218
+                $this->Close();
219
+                return false;
220
+            }
221
+            return true; # everything looks good
222
+        }
223
+        return false;
224
+    }
225
+
226
+    /**
227
+     * Closes the socket and cleans up the state of the class.
228
+     * It is not considered good to use this function without
229
+     * first trying to use QUIT.
230
+     * @access public
231
+     * @return void
232
+     */
233
+    function Close() {
234
+        $this->error = null; # so there is no confusion
235
+        $this->helo_rply = null;
236
+        if(!empty($this->smtp_conn)) {
237
+            # close the connection and cleanup
238
+            fclose($this->smtp_conn);
239
+            $this->smtp_conn = 0;
240
+        }
241
+    }
242
+
243
+
244
+    /***************************************************************
245
+     *                        SMTP COMMANDS                       *
246
+     *************************************************************/
247
+
248
+    /**
249
+     * Issues a data command and sends the msg_data to the server
250
+     * finializing the mail transaction. $msg_data is the message
251
+     * that is to be send with the headers. Each header needs to be
252
+     * on a single line followed by a <CRLF> with the message headers
253
+     * and the message body being seperated by and additional <CRLF>.
254
+     *
255
+     * Implements rfc 821: DATA <CRLF>
256
+     *
257
+     * SMTP CODE INTERMEDIATE: 354
258
+     *     [data]
259
+     *     <CRLF>.<CRLF>
260
+     *     SMTP CODE SUCCESS: 250
261
+     *     SMTP CODE FAILURE: 552,554,451,452
262
+     * SMTP CODE FAILURE: 451,554
263
+     * SMTP CODE ERROR  : 500,501,503,421
264
+     * @access public
265
+     * @return bool
266
+     */
267
+    function Data($msg_data) {
268
+        $this->error = null; # so no confusion is caused
269
+
270
+        if(!$this->connected()) {
271
+            $this->error = array(
272
+                    "error" => "Called Data() without being connected");
273
+            return false;
274
+        }
275
+
276
+        fputs($this->smtp_conn,"DATA" . $this->CRLF);
277
+
278
+        $rply = $this->get_lines();
279
+        $code = substr($rply,0,3);
280
+
281
+        if($this->do_debug >= 2) {
282
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
283
+        }
284
+
285
+        if($code != 354) {
286
+            $this->error =
287
+                array("error" => "DATA command not accepted from server",
288
+                      "smtp_code" => $code,
289
+                      "smtp_msg" => substr($rply,4));
290
+            if($this->do_debug >= 1) {
291
+                echo "SMTP -> ERROR: " . $this->error["error"] .
292
+                         ": " . $rply . $this->CRLF;
293
+            }
294
+            return false;
295
+        }
296
+
297
+        # the server is ready to accept data!
298
+        # according to rfc 821 we should not send more than 1000
299
+        # including the CRLF
300
+        # characters on a single line so we will break the data up
301
+        # into lines by \r and/or \n then if needed we will break
302
+        # each of those into smaller lines to fit within the limit.
303
+        # in addition we will be looking for lines that start with
304
+        # a period '.' and append and additional period '.' to that
305
+        # line. NOTE: this does not count towards are limit.
306
+
307
+        # normalize the line breaks so we know the explode works
308
+        $msg_data = str_replace("\r\n","\n",$msg_data);
309
+        $msg_data = str_replace("\r","\n",$msg_data);
310
+        $lines = explode("\n",$msg_data);
311
+
312
+        # we need to find a good way to determine is headers are
313
+        # in the msg_data or if it is a straight msg body
314
+        # currently I'm assuming rfc 822 definitions of msg headers
315
+        # and if the first field of the first line (':' sperated)
316
+        # does not contain a space then it _should_ be a header
317
+        # and we can process all lines before a blank "" line as
318
+        # headers.
319
+        $field = substr($lines[0],0,strpos($lines[0],":"));
320
+        $in_headers = false;
321
+        if(!empty($field) && !strstr($field," ")) {
322
+            $in_headers = true;
323
+        }
324
+
325
+        $max_line_length = 998; # used below; set here for ease in change
326
+
327
+        while(list(,$line) = @each($lines)) {
328
+            $lines_out = null;
329
+            if($line == "" && $in_headers) {
330
+                $in_headers = false;
331
+            }
332
+            # ok we need to break this line up into several
333
+            # smaller lines
334
+            while(strlen($line) > $max_line_length) {
335
+                $pos = strrpos(substr($line,0,$max_line_length)," ");
336
+
337
+                # Patch to fix DOS attack
338
+                if(!$pos) {
339
+                    $pos = $max_line_length - 1;
340
+                }
341
+
342
+                $lines_out[] = substr($line,0,$pos);
343
+                $line = substr($line,$pos + 1);
344
+                # if we are processing headers we need to
345
+                # add a LWSP-char to the front of the new line
346
+                # rfc 822 on long msg headers
347
+                if($in_headers) {
348
+                    $line = "\t" . $line;
349
+                }
350
+            }
351
+            $lines_out[] = $line;
352
+
353
+            # now send the lines to the server
354
+            while(list(,$line_out) = @each($lines_out)) {
355
+                if(strlen($line_out) > 0)
356
+                {
357
+                    if(substr($line_out, 0, 1) == ".") {
358
+                        $line_out = "." . $line_out;
359
+                    }
360
+                }
361
+                fputs($this->smtp_conn,$line_out . $this->CRLF);
362
+            }
363
+        }
364
+
365
+        # ok all the message data has been sent so lets get this
366
+        # over with aleady
367
+        fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
368
+
369
+        $rply = $this->get_lines();
370
+        $code = substr($rply,0,3);
371
+
372
+        if($this->do_debug >= 2) {
373
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
374
+        }
375
+
376
+        if($code != 250) {
377
+            $this->error =
378
+                array("error" => "DATA not accepted from server",
379
+                      "smtp_code" => $code,
380
+                      "smtp_msg" => substr($rply,4));
381
+            if($this->do_debug >= 1) {
382
+                echo "SMTP -> ERROR: " . $this->error["error"] .
383
+                         ": " . $rply . $this->CRLF;
384
+            }
385
+            return false;
386
+        }
387
+        return true;
388
+    }
389
+
390
+    /**
391
+     * Expand takes the name and asks the server to list all the
392
+     * people who are members of the _list_. Expand will return
393
+     * back and array of the result or false if an error occurs.
394
+     * Each value in the array returned has the format of:
395
+     *     [ <full-name> <sp> ] <path>
396
+     * The definition of <path> is defined in rfc 821
397
+     *
398
+     * Implements rfc 821: EXPN <SP> <string> <CRLF>
399
+     *
400
+     * SMTP CODE SUCCESS: 250
401
+     * SMTP CODE FAILURE: 550
402
+     * SMTP CODE ERROR  : 500,501,502,504,421
403
+     * @access public
404
+     * @return string array
405
+     */
406
+    function Expand($name) {
407
+        $this->error = null; # so no confusion is caused
408
+
409
+        if(!$this->connected()) {
410
+            $this->error = array(
411
+                    "error" => "Called Expand() without being connected");
412
+            return false;
413
+        }
414
+
415
+        fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
416
+
417
+        $rply = $this->get_lines();
418
+        $code = substr($rply,0,3);
419
+
420
+        if($this->do_debug >= 2) {
421
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
422
+        }
423
+
424
+        if($code != 250) {
425
+            $this->error =
426
+                array("error" => "EXPN not accepted from server",
427
+                      "smtp_code" => $code,
428
+                      "smtp_msg" => substr($rply,4));
429
+            if($this->do_debug >= 1) {
430
+                echo "SMTP -> ERROR: " . $this->error["error"] .
431
+                         ": " . $rply . $this->CRLF;
432
+            }
433
+            return false;
434
+        }
435
+
436
+        # parse the reply and place in our array to return to user
437
+        $entries = explode($this->CRLF,$rply);
438
+        while(list(,$l) = @each($entries)) {
439
+            $list[] = substr($l,4);
440
+        }
441
+
442
+        return $list;
443
+    }
444
+
445
+    /**
446
+     * Sends the HELO command to the smtp server.
447
+     * This makes sure that we and the server are in
448
+     * the same known state.
449
+     *
450
+     * Implements from rfc 821: HELO <SP> <domain> <CRLF>
451
+     *
452
+     * SMTP CODE SUCCESS: 250
453
+     * SMTP CODE ERROR  : 500, 501, 504, 421
454
+     * @access public
455
+     * @return bool
456
+     */
457
+    function Hello($host="") {
458
+        $this->error = null; # so no confusion is caused
459
+
460
+        if(!$this->connected()) {
461
+            $this->error = array(
462
+                    "error" => "Called Hello() without being connected");
463
+            return false;
464
+        }
465
+
466
+        # if a hostname for the HELO wasn't specified determine
467
+        # a suitable one to send
468
+        if(empty($host)) {
469
+            # we need to determine some sort of appopiate default
470
+            # to send to the server
471
+            $host = "localhost";
472
+        }
473
+
474
+        // Send extended hello first (RFC 2821)
475
+        if(!$this->SendHello("EHLO", $host))
476
+        {
477
+            if(!$this->SendHello("HELO", $host))
478
+                return false;
479
+        }
480
+
481
+        return true;
482
+    }
483
+
484
+    /**
485
+     * Sends a HELO/EHLO command.
486
+     * @access private
487
+     * @return bool
488
+     */
489
+    function SendHello($hello, $host) {
490
+        fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
491
+
492
+        $rply = $this->get_lines();
493
+        $code = substr($rply,0,3);
494
+
495
+        if($this->do_debug >= 2) {
496
+            echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
497
+        }
498
+
499
+        if($code != 250) {
500
+            $this->error =
501
+                array("error" => $hello . " not accepted from server",
502
+                      "smtp_code" => $code,
503
+                      "smtp_msg" => substr($rply,4));
504
+            if($this->do_debug >= 1) {
505
+                echo "SMTP -> ERROR: " . $this->error["error"] .
506
+                         ": " . $rply . $this->CRLF;
507
+            }
508
+            return false;
509
+        }
510
+
511
+        $this->helo_rply = $rply;
512
+        
513
+        return true;
514
+    }
515
+
516
+    /**
517
+     * Gets help information on the keyword specified. If the keyword
518
+     * is not specified then returns generic help, ussually contianing
519
+     * A list of keywords that help is available on. This function
520
+     * returns the results back to the user. It is up to the user to
521
+     * handle the returned data. If an error occurs then false is
522
+     * returned with $this->error set appropiately.
523
+     *
524
+     * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
525
+     *
526
+     * SMTP CODE SUCCESS: 211,214
527
+     * SMTP CODE ERROR  : 500,501,502,504,421
528
+     * @access public
529
+     * @return string
530
+     */
531
+    function Help($keyword="") {
532
+        $this->error = null; # to avoid confusion
533
+
534
+        if(!$this->connected()) {
535
+            $this->error = array(
536
+                    "error" => "Called Help() without being connected");
537
+            return false;
538
+        }
539
+
540
+        $extra = "";
541
+        if(!empty($keyword)) {
542
+            $extra = " " . $keyword;
543
+        }
544
+
545
+        fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
546
+
547
+        $rply = $this->get_lines();
548
+        $code = substr($rply,0,3);
549
+
550
+        if($this->do_debug >= 2) {
551
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
552
+        }
553
+
554
+        if($code != 211 && $code != 214) {
555
+            $this->error =
556
+                array("error" => "HELP not accepted from server",
557
+                      "smtp_code" => $code,
558
+                      "smtp_msg" => substr($rply,4));
559
+            if($this->do_debug >= 1) {
560
+                echo "SMTP -> ERROR: " . $this->error["error"] .
561
+                         ": " . $rply . $this->CRLF;
562
+            }
563
+            return false;
564
+        }
565
+
566
+        return $rply;
567
+    }
568
+
569
+    /**
570
+     * Starts a mail transaction from the email address specified in
571
+     * $from. Returns true if successful or false otherwise. If True
572
+     * the mail transaction is started and then one or more Recipient
573
+     * commands may be called followed by a Data command.
574
+     *
575
+     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
576
+     *
577
+     * SMTP CODE SUCCESS: 250
578
+     * SMTP CODE SUCCESS: 552,451,452
579
+     * SMTP CODE SUCCESS: 500,501,421
580
+     * @access public
581
+     * @return bool
582
+     */
583
+    function Mail($from) {
584
+        $this->error = null; # so no confusion is caused
585
+
586
+        if(!$this->connected()) {
587
+            $this->error = array(
588
+                    "error" => "Called Mail() without being connected");
589
+            return false;
590
+        }
591
+
592
+        fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $this->CRLF);
593
+
594
+        $rply = $this->get_lines();
595
+        $code = substr($rply,0,3);
596
+
597
+        if($this->do_debug >= 2) {
598
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
599
+        }
600
+
601
+        if($code != 250) {
602
+            $this->error =
603
+                array("error" => "MAIL not accepted from server",
604
+                      "smtp_code" => $code,
605
+                      "smtp_msg" => substr($rply,4));
606
+            if($this->do_debug >= 1) {
607
+                echo "SMTP -> ERROR: " . $this->error["error"] .
608
+                         ": " . $rply . $this->CRLF;
609
+            }
610
+            return false;
611
+        }
612
+        return true;
613
+    }
614
+
615
+    /**
616
+     * Sends the command NOOP to the SMTP server.
617
+     *
618
+     * Implements from rfc 821: NOOP <CRLF>
619
+     *
620
+     * SMTP CODE SUCCESS: 250
621
+     * SMTP CODE ERROR  : 500, 421
622
+     * @access public
623
+     * @return bool
624
+     */
625
+    function Noop() {
626
+        $this->error = null; # so no confusion is caused
627
+
628
+        if(!$this->connected()) {
629
+            $this->error = array(
630
+                    "error" => "Called Noop() without being connected");
631
+            return false;
632
+        }
633
+
634
+        fputs($this->smtp_conn,"NOOP" . $this->CRLF);
635
+
636
+        $rply = $this->get_lines();
637
+        $code = substr($rply,0,3);
638
+
639
+        if($this->do_debug >= 2) {
640
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
641
+        }
642
+
643
+        if($code != 250) {
644
+            $this->error =
645
+                array("error" => "NOOP not accepted from server",
646
+                      "smtp_code" => $code,
647
+                      "smtp_msg" => substr($rply,4));
648
+            if($this->do_debug >= 1) {
649
+                echo "SMTP -> ERROR: " . $this->error["error"] .
650
+                         ": " . $rply . $this->CRLF;
651
+            }
652
+            return false;
653
+        }
654
+        return true;
655
+    }
656
+
657
+    /**
658
+     * Sends the quit command to the server and then closes the socket
659
+     * if there is no error or the $close_on_error argument is true.
660
+     *
661
+     * Implements from rfc 821: QUIT <CRLF>
662
+     *
663
+     * SMTP CODE SUCCESS: 221
664
+     * SMTP CODE ERROR  : 500
665
+     * @access public
666
+     * @return bool
667
+     */
668
+    function Quit($close_on_error=true) {
669
+        $this->error = null; # so there is no confusion
670
+
671
+        if(!$this->connected()) {
672
+            $this->error = array(
673
+                    "error" => "Called Quit() without being connected");
674
+            return false;
675
+        }
676
+
677
+        # send the quit command to the server
678
+        fputs($this->smtp_conn,"quit" . $this->CRLF);
679
+
680
+        # get any good-bye messages
681
+        $byemsg = $this->get_lines();
682
+
683
+        if($this->do_debug >= 2) {
684
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
685
+        }
686
+
687
+        $rval = true;
688
+        $e = null;
689
+
690
+        $code = substr($byemsg,0,3);
691
+        if($code != 221) {
692
+            # use e as a tmp var cause Close will overwrite $this->error
693
+            $e = array("error" => "SMTP server rejected quit command",
694
+                       "smtp_code" => $code,
695
+                       "smtp_rply" => substr($byemsg,4));
696
+            $rval = false;
697
+            if($this->do_debug >= 1) {
698
+                echo "SMTP -> ERROR: " . $e["error"] . ": " .
699
+                         $byemsg . $this->CRLF;
700
+            }
701
+        }
702
+
703
+        if(empty($e) || $close_on_error) {
704
+            $this->Close();
705
+        }
706
+
707
+        return $rval;
708
+    }
709
+
710
+    /**
711
+     * Sends the command RCPT to the SMTP server with the TO: argument of $to.
712
+     * Returns true if the recipient was accepted false if it was rejected.
713
+     *
714
+     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
715
+     *
716
+     * SMTP CODE SUCCESS: 250,251
717
+     * SMTP CODE FAILURE: 550,551,552,553,450,451,452
718
+     * SMTP CODE ERROR  : 500,501,503,421
719
+     * @access public
720
+     * @return bool
721
+     */
722
+    function Recipient($to) {
723
+        $this->error = null; # so no confusion is caused
724
+
725
+        if(!$this->connected()) {
726
+            $this->error = array(
727
+                    "error" => "Called Recipient() without being connected");
728
+            return false;
729
+        }
730
+
731
+        fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
732
+
733
+        $rply = $this->get_lines();
734
+        $code = substr($rply,0,3);
735
+
736
+        if($this->do_debug >= 2) {
737
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
738
+        }
739
+
740
+        if($code != 250 && $code != 251) {
741
+            $this->error =
742
+                array("error" => "RCPT not accepted from server",
743
+                      "smtp_code" => $code,
744
+                      "smtp_msg" => substr($rply,4));
745
+            if($this->do_debug >= 1) {
746
+                echo "SMTP -> ERROR: " . $this->error["error"] .
747
+                         ": " . $rply . $this->CRLF;
748
+            }
749
+            return false;
750
+        }
751
+        return true;
752
+    }
753
+
754
+    /**
755
+     * Sends the RSET command to abort and transaction that is
756
+     * currently in progress. Returns true if successful false
757
+     * otherwise.
758
+     *
759
+     * Implements rfc 821: RSET <CRLF>
760
+     *
761
+     * SMTP CODE SUCCESS: 250
762
+     * SMTP CODE ERROR  : 500,501,504,421
763
+     * @access public
764
+     * @return bool
765
+     */
766
+    function Reset() {
767
+        $this->error = null; # so no confusion is caused
768
+
769
+        if(!$this->connected()) {
770
+            $this->error = array(
771
+                    "error" => "Called Reset() without being connected");
772
+            return false;
773
+        }
774
+
775
+        fputs($this->smtp_conn,"RSET" . $this->CRLF);
776
+
777
+        $rply = $this->get_lines();
778
+        $code = substr($rply,0,3);
779
+
780
+        if($this->do_debug >= 2) {
781
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
782
+        }
783
+
784
+        if($code != 250) {
785
+            $this->error =
786
+                array("error" => "RSET failed",
787
+                      "smtp_code" => $code,
788
+                      "smtp_msg" => substr($rply,4));
789
+            if($this->do_debug >= 1) {
790
+                echo "SMTP -> ERROR: " . $this->error["error"] .
791
+                         ": " . $rply . $this->CRLF;
792
+            }
793
+            return false;
794
+        }
795
+
796
+        return true;
797
+    }
798
+
799
+    /**
800
+     * Starts a mail transaction from the email address specified in
801
+     * $from. Returns true if successful or false otherwise. If True
802
+     * the mail transaction is started and then one or more Recipient
803
+     * commands may be called followed by a Data command. This command
804
+     * will send the message to the users terminal if they are logged
805
+     * in.
806
+     *
807
+     * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
808
+     *
809
+     * SMTP CODE SUCCESS: 250
810
+     * SMTP CODE SUCCESS: 552,451,452
811
+     * SMTP CODE SUCCESS: 500,501,502,421
812
+     * @access public
813
+     * @return bool
814
+     */
815
+    function Send($from) {
816
+        $this->error = null; # so no confusion is caused
817
+
818
+        if(!$this->connected()) {
819
+            $this->error = array(
820
+                    "error" => "Called Send() without being connected");
821
+            return false;
822
+        }
823
+
824
+        fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
825
+
826
+        $rply = $this->get_lines();
827
+        $code = substr($rply,0,3);
828
+
829
+        if($this->do_debug >= 2) {
830
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
831
+        }
832
+
833
+        if($code != 250) {
834
+            $this->error =
835
+                array("error" => "SEND not accepted from server",
836
+                      "smtp_code" => $code,
837
+                      "smtp_msg" => substr($rply,4));
838
+            if($this->do_debug >= 1) {
839
+                echo "SMTP -> ERROR: " . $this->error["error"] .
840
+                         ": " . $rply . $this->CRLF;
841
+            }
842
+            return false;
843
+        }
844
+        return true;
845
+    }
846
+
847
+    /**
848
+     * Starts a mail transaction from the email address specified in
849
+     * $from. Returns true if successful or false otherwise. If True
850
+     * the mail transaction is started and then one or more Recipient
851
+     * commands may be called followed by a Data command. This command
852
+     * will send the message to the users terminal if they are logged
853
+     * in and send them an email.
854
+     *
855
+     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
856
+     *
857
+     * SMTP CODE SUCCESS: 250
858
+     * SMTP CODE SUCCESS: 552,451,452
859
+     * SMTP CODE SUCCESS: 500,501,502,421
860
+     * @access public
861
+     * @return bool
862
+     */
863
+    function SendAndMail($from) {
864
+        $this->error = null; # so no confusion is caused
865
+
866
+        if(!$this->connected()) {
867
+            $this->error = array(
868
+                "error" => "Called SendAndMail() without being connected");
869
+            return false;
870
+        }
871
+
872
+        fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
873
+
874
+        $rply = $this->get_lines();
875
+        $code = substr($rply,0,3);
876
+
877
+        if($this->do_debug >= 2) {
878
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
879
+        }
880
+
881
+        if($code != 250) {
882
+            $this->error =
883
+                array("error" => "SAML not accepted from server",
884
+                      "smtp_code" => $code,
885
+                      "smtp_msg" => substr($rply,4));
886
+            if($this->do_debug >= 1) {
887
+                echo "SMTP -> ERROR: " . $this->error["error"] .
888
+                         ": " . $rply . $this->CRLF;
889
+            }
890
+            return false;
891
+        }
892
+        return true;
893
+    }
894
+
895
+    /**
896
+     * Starts a mail transaction from the email address specified in
897
+     * $from. Returns true if successful or false otherwise. If True
898
+     * the mail transaction is started and then one or more Recipient
899
+     * commands may be called followed by a Data command. This command
900
+     * will send the message to the users terminal if they are logged
901
+     * in or mail it to them if they are not.
902
+     *
903
+     * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
904
+     *
905
+     * SMTP CODE SUCCESS: 250
906
+     * SMTP CODE SUCCESS: 552,451,452
907
+     * SMTP CODE SUCCESS: 500,501,502,421
908
+     * @access public
909
+     * @return bool
910
+     */
911
+    function SendOrMail($from) {
912
+        $this->error = null; # so no confusion is caused
913
+
914
+        if(!$this->connected()) {
915
+            $this->error = array(
916
+                "error" => "Called SendOrMail() without being connected");
917
+            return false;
918
+        }
919
+
920
+        fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
921
+
922
+        $rply = $this->get_lines();
923
+        $code = substr($rply,0,3);
924
+
925
+        if($this->do_debug >= 2) {
926
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
927
+        }
928
+
929
+        if($code != 250) {
930
+            $this->error =
931
+                array("error" => "SOML not accepted from server",
932
+                      "smtp_code" => $code,
933
+                      "smtp_msg" => substr($rply,4));
934
+            if($this->do_debug >= 1) {
935
+                echo "SMTP -> ERROR: " . $this->error["error"] .
936
+                         ": " . $rply . $this->CRLF;
937
+            }
938
+            return false;
939
+        }
940
+        return true;
941
+    }
942
+
943
+    /**
944
+     * This is an optional command for SMTP that this class does not
945
+     * support. This method is here to make the RFC821 Definition
946
+     * complete for this class and __may__ be implimented in the future
947
+     *
948
+     * Implements from rfc 821: TURN <CRLF>
949
+     *
950
+     * SMTP CODE SUCCESS: 250
951
+     * SMTP CODE FAILURE: 502
952
+     * SMTP CODE ERROR  : 500, 503
953
+     * @access public
954
+     * @return bool
955
+     */
956
+    function Turn() {
957
+        $this->error = array("error" => "This method, TURN, of the SMTP ".
958
+                                        "is not implemented");
959
+        if($this->do_debug >= 1) {
960
+            echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
961
+        }
962
+        return false;
963
+    }
964
+
965
+    /**
966
+     * Verifies that the name is recognized by the server.
967
+     * Returns false if the name could not be verified otherwise
968
+     * the response from the server is returned.
969
+     *
970
+     * Implements rfc 821: VRFY <SP> <string> <CRLF>
971
+     *
972
+     * SMTP CODE SUCCESS: 250,251
973
+     * SMTP CODE FAILURE: 550,551,553
974
+     * SMTP CODE ERROR  : 500,501,502,421
975
+     * @access public
976
+     * @return int
977
+     */
978
+    function Verify($name) {
979
+        $this->error = null; # so no confusion is caused
980
+
981
+        if(!$this->connected()) {
982
+            $this->error = array(
983
+                    "error" => "Called Verify() without being connected");
984
+            return false;
985
+        }
986
+
987
+        fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
988
+
989
+        $rply = $this->get_lines();
990
+        $code = substr($rply,0,3);
991
+
992
+        if($this->do_debug >= 2) {
993
+            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
994
+        }
995
+
996
+        if($code != 250 && $code != 251) {
997
+            $this->error =
998
+                array("error" => "VRFY failed on name '$name'",
999
+                      "smtp_code" => $code,
1000
+                      "smtp_msg" => substr($rply,4));
1001
+            if($this->do_debug >= 1) {
1002
+                echo "SMTP -> ERROR: " . $this->error["error"] .
1003
+                         ": " . $rply . $this->CRLF;
1004
+            }
1005
+            return false;
1006
+        }
1007
+        return $rply;
1008
+    }
1009
+
1010
+    /*******************************************************************
1011
+     *                       INTERNAL FUNCTIONS                       *
1012
+     ******************************************************************/
1013
+
1014
+    /**
1015
+     * Read in as many lines as possible
1016
+     * either before eof or socket timeout occurs on the operation.
1017
+     * With SMTP we can tell if we have more lines to read if the
1018
+     * 4th character is '-' symbol. If it is a space then we don't
1019
+     * need to read anything else.
1020
+     * @access private
1021
+     * @return string
1022
+     */
1023
+    function get_lines() {
1024
+        $data = "";
1025
+        while($str = fgets($this->smtp_conn,515)) {
1026
+            if($this->do_debug >= 4) {
1027
+                echo "SMTP -> get_lines(): \$data was \"$data\"" .
1028
+                         $this->CRLF;
1029
+                echo "SMTP -> get_lines(): \$str is \"$str\"" .
1030
+                         $this->CRLF;
1031
+            }
1032
+            $data .= $str;
1033
+            if($this->do_debug >= 4) {
1034
+                echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
1035
+            }
1036
+            # if the 4th character is a space then we are done reading
1037
+            # so just break the loop
1038
+            if(substr($str,3,1) == " ") { break; }
1039
+        }
1040
+        return $data;
1041
+    }
1042
+
1043
+}
1044
+
1045
+
1046
+ ?>
0 1047
\ No newline at end of file
... ...
@@ -1,6 +1,6 @@
1 1
 <?php
2
- 
3
-if(!isset($action) OR is_null($action)) $action = "";
2
+
3
+
4 4
 
5 5
 
6 6
 // Defines the doc type.  Most people will not need to change this.
... ...
@@ -12,7 +12,6 @@ $alphabet = array( 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', '
12 12
 
13 13
 // General...used in many pages.
14 14
 
15
-
16 15
 define ("_ACTIONSUCCESSFUL", "The action was successful.");
17 16
 define ("_ACTIONCANCELLED", "The requested action was cancelled.");
18 17
 define ("_ACTIVE", "Active");
... ...
@@ -62,10 +61,8 @@ define ("_FATALERROR", "<b>A fatal MySQL error was encountered.</b><br />");
62 61
 define ("_FSTORY", "Feature");
63 62
 define ("_GO", "Go");
64 63
 define ("_HALFSTAR", "half-star");
65
-define ("_HIDE", "Hide");
66 64
 define ("_JAVASCRIPTOFF", "You must have javascript enabled for this form to work properly."); // Modified for version 3.0
67 65
 define ("_LIKE", "like");
68
-if(!defined("_LIKES_NUMBER")) define("_LIKES_NUMBER", "Number of Likes");  
69 66
 define ("_LOGIN", "Log In");
70 67
 define ("_PLEASELOGIN", "Please login to access this feature.");
71 68
 define ("_MEMBER", "Member");
... ...
@@ -126,7 +123,6 @@ define ("_TINYMCETOGGLE", "Use tinyMCE");
126 123
 define ("_TITLE", "Title");
127 124
 define ("_TOC", "Table of Contents");
128 125
 define ("_TOPLEVEL", "Top Level Category"); // Really only used in the admin, but needs to load with the header.
129
-define ("_UNHIDE", "Unhide");
130 126
 define ("_UP", "up arrow");
131 127
 define ("_WIP", "Work in Progress Only"); // Added 01/12/07
132 128
 define ("_YES", "Yes");
... ...
@@ -447,4 +443,4 @@ define ("_RULESVIOLATION", "Violation of Rules");
447 443
 define ("_BUGREPORT", "Bug Report");
448 444
 define ("_REPORTTHIS", "Report This");
449 445
 
450
-?>
446
+?>
451 447
\ No newline at end of file
... ...
@@ -225,6 +225,26 @@ define ("_SMTPHOST", "SMTP Host");
225 225
 define ("_SMTPOFF", "Leave empty if sendmail is enabled.");
226 226
 define ("_SMTPPASS", "SMTP Password");
227 227
 define ("_SMTPUSER", "SMTP Username");
228
+define ("_SMTPPORT", "SMTP Port");
229
+define ("_SMTPSECURE", "SMTP Encryption");
230
+define ("_TESTSMTP", "Test SMTP connection (saved settings)");
231
+define ("_TESTSMTP_HEADER", "SMTP connection test");
232
+define ("_TESTSMTP_NOHOST", "SMTP host is not configured/saved. Save an SMTP host first, then run the test.");
233
+define ("_TESTSMTP_PORTNOTE", "Using effective port %s (encryption: %s).");
234
+define ("_TESTSMTP_SSLPORTWARN", "Warning: smtp_secure is 'ssl' but no port is saved; ssl usually needs port 465. Running the test on the effective values anyway.");
235
+define ("_TESTSMTP_TLSPORTWARN", "Warning: smtp_secure is 'tls' but no port is saved; tls (STARTTLS) usually needs port 587. Running the test on the effective values anyway.");
236
+define ("_TESTSMTP_CONNECTOK", "Connected to %s on port %s.");
237
+define ("_TESTSMTP_CONNECTFAIL", "Connect failed using %s on port %s.");
238
+define ("_TESTSMTP_HELOOK", "HELO/EHLO handshake succeeded.");
239
+define ("_TESTSMTP_HELOFAIL", "HELO/EHLO handshake failed: %s");
240
+define ("_TESTSMTP_STARTTLSOK", "STARTTLS supported.");
241
+define ("_TESTSMTP_STARTTLSFAIL", "STARTTLS negotiation failed: %s");
242
+define ("_TESTSMTP_STARTTLSMISSING", "The server does not offer STARTTLS but smtp_secure is set to 'tls'.");
243
+define ("_TESTSMTP_AUTHOK", "Authentication OK.");
244
+define ("_TESTSMTP_AUTHFAIL", "Authentication failed: %s");
245
+define ("_TESTSMTP_AUTHSKIP", "No SMTP username saved; authentication step skipped.");
246
+define ("_TESTSMTP_NOAUTH", "Server does not advertise AUTH; authentication step skipped.");
247
+define ("_TESTSMTP_TRANSCRIPT", "SMTP debug transcript");
228 248
 define ("_STARS", "Stars");
229 249
 define ("_STATS", "Re-calculate Site Statistics");
230 250
 define ("_STATUS", "Status");
... ...
@@ -334,6 +354,9 @@ define ("_HELP_STATS", "Click here to re-calculate the site statistics.");
334 354
 define ("_HELP_SMTPHOST", "The URL of your SMTP server.  Leave this setting blank to use sendmail instead.");
335 355
 define ("_HELP_SMTPUSER", "The user name to use with the SMTP server.");
336 356
 define ("_HELP_SMTPPWD", "The password for the SMTP user name set above.");
357
+define ("_HELP_SMTPPORT", "The port your SMTP server listens on, e.g. 587 for STARTTLS or 465 for SSL. Leave empty to use the PHPMailer default.");
358
+define ("_HELP_SMTPSECURE", "Encryption used to connect to the SMTP server: (none), STARTTLS (587) or SSL (465). Leave as (none) to keep the previous behaviour.");
359
+define ("_HELP_TESTSMTP", "Opens a real connection to the SMTP server and reports each step, without sending any email. It uses the SAVED settings from the database, not the values currently in this form, so save your changes first and then run the test.");
337 360
 define ("_HELP_PANELS", "Several pages within eFiction use panels to dynamically add to their options and features including this Admin area.  Select the panel type to re-arrange the order of the panels for that type.");
338 361
 define ("_HELP_CATLEVEL", "Choose where within the category tree you want this category to fall.  Choose '"._TOPLEVEL."' if you wish this category to be placed at the top of the tree.  Otherwise, choose the category you wish this category placed under.");
339 362
 define ("_HELP_ORDERAFTER", "Choose the category you want this category placed <strong>after</strong> in the list of categories.");
... ...
@@ -73,6 +73,17 @@ else {
73 73
 		dbquery("UPDATE " . $settingsprefix . "fanfiction_settings SET version = '3.5.6' WHERE sitekey = '" . SITEKEY . "'");
74 74
 		$settings['version'] = '3.5.6';
75 75
 	}
76
+ 
77
+	else {
78
+		$set_359 = do_version_check_359();
79
+	
80
+		if ($set_359)
81
+		{
82
+			$output .= write_message("Table <b>" . $set_359 . "</b> needs a update.");
83
+			dbquery("UPDATE " . $settingsprefix . "fanfiction_settings SET version = '3.5.8' WHERE sitekey = '" . SITEKEY . "'");
84
+			$settings['version'] = '3.5.8';
85
+		}
86
+	}
76 87
 }
77 88
 
78 89
 $oldVersion = explode(".", $settings['version']);
... ...
@@ -271,6 +282,30 @@ elseif ($oldVersion[0] == 3 && ($oldVersion[1] < 5 || $oldVersion[2] < 8))  //3.
271 282
 
272 283
 }
273 284
 
285
+elseif ($oldVersion[0] == 3 && $oldVersion[1] == 5 && $oldVersion[2] < 9)  // 3.5.8 -> 3.5.9
286
+{
287
+	if ($confirm == "yes")
288
+	{
289
+		if (!dbassoc(dbquery("SHOW COLUMNS FROM ".TABLEPREFIX."fanfiction_settings LIKE 'smtp_port'")))
290
+			dbquery("ALTER TABLE `".TABLEPREFIX."fanfiction_settings` ADD `smtp_port` varchar(5) NOT NULL DEFAULT ''");
291
+		if (!dbassoc(dbquery("SHOW COLUMNS FROM ".TABLEPREFIX."fanfiction_settings LIKE 'smtp_secure'")))
292
+			dbquery("ALTER TABLE `".TABLEPREFIX."fanfiction_settings` ADD `smtp_secure` varchar(3) NOT NULL DEFAULT ''");
293
+
294
+		$set_359 = do_version_check_359();
295
+		if ($set_359)
296
+		{
297
+			$output .= write_error(_ERROR);   // migration not confirmed -> DO NOT bump version
298
+		}
299
+		else
300
+		{
301
+			$update = dbquery("UPDATE ".$settingsprefix."fanfiction_settings SET version = '".$version."' WHERE sitekey = '".SITEKEY."'");
302
+			if ($update) $output .= write_message(_ACTIONSUCCESSFUL);
303
+		}
304
+	}
305
+	else if ($confirm == "no") $output .= write_message(_ACTIONCANCELLED);
306
+	else $output .= write_message("Are you ready to update? <a href='update.php?confirm=yes'>"._YES."</a> "._OR." <a href='update.php?confirm=no'>"._NO."</a>");
307
+}
308
+
274 309
 else $output .= write_message(_ALREADYUPDATED);
275 310
 
276 311
 /* until database is fully fixed, not update efiction version */
... ...
@@ -381,6 +416,23 @@ function do_version_check_356()
381 416
 	return $check_356;
382 417
 }
383 418
 
419
+function do_version_check_359()
420
+{
421
+	$check_359 = false;
422
+
423
+	$required = array('smtp_port', 'smtp_secure');
424
+	foreach ($required as $f)
425
+	{
426
+		if (!dbassoc(dbquery("SHOW COLUMNS FROM " . TABLEPREFIX . "fanfiction_settings LIKE '{$f}'")))
427
+		{
428
+			$check_359 = "fanfiction_settings - field: " . $f;
429
+			return $check_359;
430
+		}
431
+	}
432
+
433
+	return $check_359;
434
+}
435
+
384 436
 $tpl->assign("output", $output);
385 437
 $tpl->printToScreen();
386 438
 dbclose();
... ...
@@ -1,3 +1,3 @@
1 1
 <?php
2 2
 if(!defined("_CHARSET")) exit( );
3
-$version = "3.5.8";
3
+$version = "3.5.9";