明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

用Socket發(fā)送電子郵件--續(xù)篇(不錯的文章)(轉自動力在線)

[摘要]用Socket發(fā)送電子郵件--續(xù)篇作者:limodou  在前面我曾經(jīng)寫過一篇文章,介紹了如何利用socket編程來發(fā)送郵件,以解決web服務器不支持mail()函數(shù)的問題。經(jīng)過我的測試也是可以使用...
用Socket發(fā)送電子郵件--續(xù)篇
作者:limodou

  在前面我曾經(jīng)寫過一篇文章,介紹了如何利用socket編程來發(fā)送郵件,以解決web服務器不支持mail()函數(shù)的問題。經(jīng)過我的測試也是可以使用的。但目前眾多的免費郵件提供商(從263開始,163,新浪網(wǎng)也快開始了)均在smtp功能上增加了認證功能,使得原郵件發(fā)送類無法使用。在經(jīng)過對相應smtp后續(xù)rfc的學習之后,經(jīng)過了多次的試驗,我終于試驗成功了。于是懷著急迫的心情向大家介紹。

SMTP 認證功能介紹
  此處不想向你詳細介紹SMTP認證功能,因為我也說不清楚,詳細的請參考[RFC 2554]規(guī)范。SMTP的認證功能主要是增加了AUTH命令。AUTH命令有多種用法,而且有多種認證機制。AUTH支持的認證機制主要有LOGIN,CRAM-MD5[注1]等。LOGIN應該是大多數(shù)免費郵件服務器都支持的,263與新浪都支持。而新浪還支持CRAM-MD5機制。認證機制一般只在真正發(fā)送郵件之前進行,而且只需要執(zhí)行一次。當認證成功后,即可按原來正常的處理發(fā)送郵件。原理是口令-應答(Challenge-Response),即由服務器發(fā)送命令要求客戶端回答,客戶端根據(jù)服務器發(fā)送信息進行回答,如果應答通過了,則認證成功,即可繼續(xù)處理。下面對這兩種制作一個簡單介紹。S:表示服務器返回,C:表示客戶端發(fā)送。


LOGIN
它應該比較簡單?诹-應答過程如下:

1 C: AUTH LOGIN
2 S: 334 dXNlcm5hbWU6
3 C: dXNlcm5hbWU6
4 S: 334 cGFzc3dvcmQ6
5 C: cGFzc3dvcmQ6
6 S: 235 Authentication successful.
1 為客戶端向服務器發(fā)送認證指令。
2 服務端返回base64編碼串,成功碼為334。編碼字符串解碼后為“username:”,說明要求客戶端發(fā)送用戶名。
3 客戶端發(fā)送用base64編碼的用戶名,此處為“username:”。
4 服務端返回base64編碼串,成功碼為334。編碼字符串解碼后為“password:”,說明要求客戶端發(fā)送用戶口令。
5 客戶端發(fā)送用base64編碼的口令,此處為“password:”。
6 成功后,服務端返回碼為235,表示認證成功可以發(fā)送郵件了。

對于LOGIN方式認證,其實就是將用戶名與口令用base64進行編碼,根據(jù)服務器的要求,分別發(fā)出即可。(就我看來,由于base64是一種公共的編碼標準,也起不到太大的保護作用。) 
CRAM-MD5機制
關于CRAM-MD5的機制可以參考[RFC 2195]規(guī)范,這里不詳細說明了。主要就是通過口令-回答機制,由服務端發(fā)出一個信息串,這個由隨機數(shù),時間戳,服務器地址構成,并且用base64編碼。客戶端收到后,發(fā)送一個由用戶名,加一個空格,再加一個摘要構成的串,并用base64編碼。摘要是通過MD5算法求出。這種機制要求服務端與客戶端有相同的加密串。當客戶端發(fā)送摘要后,服務器對其合法性進行驗證,成功后,返回235。 
如何得知郵件服務器支持什么認證?
  在smtp的[RFC 821]中,在與郵件服務器連接成功后,第一個命令一般是“HELO”。但是在支持認證的郵件服務器中,第一個命令應改為“EHLO”[注2]。在命令成功后,263的返回可能為: 

EHLO hello
250-smtp.263.net [注3]
250-PIPELINING 
250-SIZE 10240000 
250-ETRN 
250-AUTH LOGIN 
250 8BITMIME 
  從而可以看到263支持LOGIN方式認證。當然,如果你已經(jīng)知道郵件服務器是什么方式,也沒有必要自動進行判斷,但是如果不知道,就需要分析這個返回結果了。不過大部分的郵件服務器都支持最簡單的LOGIN方式。

  好了,下面開始對以前所寫的sendmail.class.php3進行修改。你沒有不要緊,本文在最后提供了sendmail.class.php3的打包文件,可以下載。至于例子則自已根據(jù)本文進行編寫。

修改sendmail.class.php3
  此處只說出修改的重點,而不是全面的分析。

  首先回顧一下sendmail.class.php3的思路,讓大家先心中有數(shù)。

  sendmail.class.php3一共有四個函數(shù),分別為: 

send_mail 類的構造函數(shù),用于信息的初始化
send郵件發(fā)送函數(shù),執(zhí)行socket命令,發(fā)送郵件
do_command命令執(zhí)行函數(shù),執(zhí)行一條smtp命令,并將處理返回結果
show_debug顯示調(diào)示信息函數(shù)
  首先用戶應先調(diào)用類的構造函數(shù),對必要的參數(shù)進行初始化。如smtp服務器地址($smtp),歡迎信息($welcome),及是否顯示調(diào)示信息($debug)。同時還要初始化一些內(nèi)部變量,如最后執(zhí)行命令($lastact),最后響應信息($lastmessage),及端口號($port=25)。

  然后,用戶生成郵件信息,并調(diào)用send()函數(shù)發(fā)送郵件。在send()函數(shù)中,根據(jù)smtp規(guī)范,一條命令接一條命令執(zhí)行(詳情參見前面的文章)。在執(zhí)行命令時,是通過調(diào)用do_command()來實現(xiàn)的。如果do_command()執(zhí)行出錯,則程序立即返回,否則繼續(xù)向下執(zhí)行。如果設置了顯示調(diào)示信息標志,則do_command()在命令發(fā)送和信息響應時會返回調(diào)示信息。

  好了,大家已經(jīng)對它的運行有了一個了解,下面就是如何修改了。

  修改構造函數(shù)(send_mail)
  由于以前的send_mail類不支持認證功能,所以先要增加認證信息。增加了三個參數(shù),$auth, $authuser,和$authpasswd。$auth是一個標志,表示是否要使用認證功能。$authuser和$authpasswd是smtp認證的用戶名和口令,根據(jù)相應的郵件服務商的要求,例如263是同pop3相一致。大部分應該也是如此。這樣,同時需要在類的內(nèi)部變量表后面增加三個內(nèi)部變量:$auth,$user,$passwd。

  修改發(fā)送函數(shù)(send)
  將發(fā)送命令HELO改為發(fā)送EHLO。同時要加入判斷是否要進行認證處理: 

//改為支持ESMTP EHLO命令
if($this->auth)
{
$this->lastact="EHLO ";
}
else
$this->lastact="HELO ";
  即,如果需要認證處理,則發(fā)送EHLO命令,否則還發(fā)送HELO命令。

  然后,增加認證處理: 

//2000.02.28 增加認證處理
if($this->auth)
{
$this->lastact="AUTH LOGIN" . "\n";
if(!$this->do_command($this->lastact, "334"))
{
fclose($this->fp);
return false;
}

//回傳用戶名,用base64編碼
$this->lastact=base64_encode($this->user) . "\n";
if(!$this->do_command($this->lastact, "334"))
{
fclose($this->fp);
return false;
}

//回傳口令,用base64編碼
$this->lastact=base64_encode($this->passwd) . "\n";
if(!$this->do_command($this->lastact, "235"))
{
fclose($this->fp);
return false;
}
}
  注意,這里只實現(xiàn)了AUTH LOGIN機制,CRAM-MD5沒有實現(xiàn)。而且對服務器傳回的信息沒有判斷,默認為第一次要求用戶名,第二次要求口令。

  修改命令執(zhí)行函數(shù)(do_command)
  原函數(shù)不能顯示當響應串為多行的情況。修改為: 

/*2000.02.28 修改,將返回信息顯示完全
$this->lastmessage = fgets ( $this->fp, 512 );
$this->show_debug($this->lastmessage, "in");
*/
while(true)
{
$this->lastmessage = fgets ( $this->fp, 512 );
$this->show_debug($this->lastmessage, "in");
if(($this->lastmessage[3]==' ') or (empty($this->lastmessage)))
break;
}
  這樣類就改好了。

測試send_mail類
  下面是我編寫的一個測試小程序,用于發(fā)送一封信,但是為了安全起見,我將用戶名及口令沒有用真實信息,如果大家想要測試請改成你自已的信息。程序如下(send.php): 

<?
include("sendmail.class.php3");

$sendmail=new send_mail("smtp.263.net", true, "username", "password", "hello", true);
$sendmail->send("toemail, "fromemail", "test", "This is a test!");
?>
結論
  對于263的測試很順利,也比較快。但是新浪網(wǎng)則不容易成功,主要是超時,而且發(fā)成功也收不著,不知為何?

  注意:由于發(fā)送smtp需要用戶名及口令,且大部分的smtp認證使用與pop3相同的用戶名和口令。所以如果大家使用這個方法,可能會把用戶名和口令寫入程序,上傳到服務器。但是這樣做是不安全的。加密也不一定好用,因為信息放在服務器上,相應的解密信息也會放到服務器上。我的建議是,再申請一個專門用來發(fā)信用的信箱,這樣別人知道了也不怕。

  希望這個程序對你有用。sendmail.class.php3下載。 

附:相關的RFC 

RFC 1869SMTP Service Extensions
RFC 2195IMAP/POP AUTHorize Extension(里面有關于CRAM-MD5的說明)
RFC 2222Simple Authentication and Security Layer
RFC 2554SMTP Service Extension for Authentication

--------------------------------------------------------------------------------
[注1]
CRAM=Challenge-Response Authentication Mechanism 口令-應答認證機制
MD5是一種摘要算法,主要用于RSA,PGP中。
[注2]
關于EHLO的說明參見[RFC 1869]。
[注3]
在郵件服務器應答串中,如果應響碼后面跟空格(' ')表示,應答串只有一行;如果為減號('-')表示有多行,且最后一行響應碼后面為空格(' ')。 
本文所有權屬于limodou。如要轉載請保留此信息。


注意:sendmail.class.php3下載地址:
http://www.zphp.com/files/sendmail.class