作者:Tuuzed(土仔) 发表于:2008年7月29日
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。
http://www.cppblog.com/tuuzed/archive/2008/07/29/57477.html
Indy10中的IdSMTP控件比BCB6中自带的NMSMTP支持更多的SMTP 命令(支持RFC 821,RFC 1869 ,RFC 2197 ,RFC 2554),像国内的网易、TOM、21CN等的SMTP都需要EHLO命令先进行认证才可使用。但是,目前很多大型的邮件服务商出于预防垃圾邮件的考 虑,已经开始取消或限制SMTP发信了,改为提供WebMail服务,因此可用的SMTP服务器是少之又少了。有人说现在做SMTP邮件发送器意义已经不 大,可我认为,动手去做这个SMTP发送器可以更好的了解一封电子邮件的结构和它的产生过程,管他别人爱说啥!
【邮件大致结构】
先用DreamMail发一封测试信:由TestID@163.com发信给TestID2@21cn.com,其中挂载附件1.txt。信件从21CN收回来之后是这样子的:
2 HMM_SOURCE_IP: 10.27 . 2.7 : 55493.2027099718
3 HMM_ATTACHE_NUM: 0001
4 HMM_SOURCE_TYPE:SMTP
5 Received: from aisp7 - mta ? dg (dgproxy7.inner - hermes.com [ 10.27 . 2.7 ])
6 by 21cn.com (HERMES) with SMTP id 359B63813A
7 for < TestID2@21cn.com > ; Tue, 29 Jul 2008 22 : 20 : 12 + 0800 (CST)
8
9 Received: from m12 - 11.163 .com([ 220.181 . 12.11 ])
10 by aisp7 - mta@dg(Knowledge - based Antispam Gateway 2 .126n5( 2008 - 07 - 01 ), 59.36 . 102.56 ) with ESMTP id
11
12 mx23749. 1217341212 for < TestID2@21cn.com > ;
13 Tue, 11 Jul 2008 22 : 20 : 13 + 0000
14
15 X - Original - MailFrom: TestID@ 163 .com
16 Received: from ChinaPC (unknown [ 58.145 . 147.196 ])
17 by smtp7 (Coremail) with SMTP id C8CowLCrpS8XJ49IDQBdEQ == .5964S2;
18 Tue, 11 Jul 2008 22 : 20 : 07 + 0800 (CST)
2 // 回复地址
3 Reply - To: TestID@ 163 .com
4
5 From: " TestID " < TestID@ 163 .com >
6 To: " TestID2 " < TestID2@21cn.com >
7 Subject: Test Attachment
8 Date: Tue, 29 Jul 2008 22 : 31 : 07 + 0800
9 // Dreammail的识别ID
10 Message - Id: < DreamMail__223107_76081071266@smtp. 163 .com >
11 MIME - Version: 1.0
12 // 内容类型及内容“指针ID”
13 Content - Type: multipart / mixed;
14 boundary = " ----=_NextPart_08072922310693970267282_000 "
15 // 邮件优先级
16 X - Priority: 3
17 // 客户端名称
18 X - Mailer: DreamMail 4.4 . 1.0
19 X - Coremail - Antispam: 1Uf129KBjDUn29KB7ZKAUJUUUUUYxn0WfASr - VFAUDa7 - sFnT
20 9fnUUIcSsGvfJTRUUUj_xYjsxI4VWUJwAYFVCjjxCrM7AC8VAFwI0_Jr0_Gr1l1I0E4x80
21 FVCIwcAKzIAtM7C26IkvcIIF6IxKo4kEV4yl1IIY67AEw4v_Jr0_Jr4le4C267I2x7xF54
22 xIwI1l52xGzVA2a4k0FcxF6cIjj282cryl52xGzVA2a4k0FcxF6xCjrcI26cIUMc02F40E
23 57IF67AEF4xIwI1lYx0E2Ix0cI8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r1j6r4UM4
24 xvF2IEb7IF0Fy264kE64k0F24lFcxC0VAYjxAxZF0Ex2IqxwAC62BYpTIE1TZKA3svLVAK
25 vSnIqfZI6r4lFVCF04k20xvEw2I207IF0wAKzVCY07xG64k0F24l7I0Y6sxI4wCY1Ik26c
26 xK620vw7xCY7Wlc7Ca8VAvwVCjb41lc7Ca8VAvwVCFzxkY4VA2I41lc2xSY4AK67AK6ry5
27 MxkI7II2jI8vz4v_Jr0_Jryl4x8a6c8ajcxJMI8E67AF67kF1VAFwI0_Jr0_JrylIxAIcV
28 C0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Jr0_Gr1lIxAIcVC2
29 z280aVAFwI0_Jr0_Gr1lIxAIcVC2z280aVCY1x0267AKxVWUJVW8JbIYCTnIWIevJa73Uj
30 IFyTuYvjxUgg4SUUUUU
31 // ESet病毒防火墙添加
32 X - EsetId: 2080B02B6D6871693F86B07B643E31
2 ------= _NextPart_08072922310693970267282_000
3 Content - Type: multipart / alternative;
4 boundary = " ----=_NextPart_08072922310693970267282_002 "
5
6 // 无控制符、无标签的文本内容(BASE64编码)
7 ------= _NextPart_08072922310693970267282_002
8 Content - Type: text / plain;
9 charset = " GB2312 "
10 Content - Transfer - Encoding: base64
11
12 SGVsbG8gV29ybGQhDQo =
13
14 // 有控制符、标签的HTML内容(BASE64编码)
15 ------= _NextPart_08072922310693970267282_002
16 Content - Type: text / html;
17 charset = " GB2312 "
18 Content - Transfer - Encoding: base64
19
20 PEhUTUw + PEhFQUQ + PFRJVExFPk1haWw8L1RJVExFPg0KPE1FVEEgY29udGVudD0iS3NESFRNTEVE
21 TGliLm9jeCwgRnJlZVdhcmUgSFRNTCBFZGl0b3IgMS4xNjQuMiwgP0t1cnQgU2VuZmVyIiANCm5h
22 bWU9R0VORVJBVE9SPg0KPE1FVEEgaHR0cC1lcXVpdj1Db250ZW50LVR5cGUgY29udGVudD0idGV4
23 dC9odG1sOyBjaGFyc2V0PUdCMjMxMiI + PC9IRUFEPg0KPEJPRFkgc3R5bGU9IkZPTlQtU0laRTog
24 OXB0OyBGT05ULUZBTUlMWTogy87M5SIgbGVmdE1hcmdpbj01IHRvcE1hcmdpbj01ICNmZmZmZmY +
25 DQo8RElWPkhlbGxvIFdvcmxkITwhLS1BSURfU0VORFRPX0JFR0lOLS0 + PC9ESVY + PCEtLURyZWFt
26 TWFpbF9BRF9CRUdJTi0tPjxESVY + PEZPTlQgZmFjZT0iQXJpYWwsc2Fucy1zZXJpZiIgc2l6ZT0y
27 Pl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
28 X19fX19fX19fX19fPEJSPjxFTT48U3Ryb25nPkRyZWFtTWFpbDwvU3Ryb25nPjwvRU0 + Jm5ic3A7
29 PEZPTlQgZmFjZT0iQXJpYWwsc2Fucy1zZXJpZiIgc2l6ZT0yPi0gtdrSu7j21qez1tPKvP7AtNS0
30 uPrX2bXEtefX09PKvP6 / zbuntssmbmJzcDs8L0ZPTlQ + PEZPTlQgZmFjZT0iQXJpYWwsc2Fucy1z
31 ZXJpZiIgY29sb3I9IzAwMDBmZiBzaXplPTI + PEEgaHJlZj0iaHR0cDovL3d3dy5kcmVhbW1haWwu
32 b3JnIj53d3cuZHJlYW1tYWlsLm9yZzwvQT48L0ZPTlQ + PC9GT05UPjwvRElWPjwhLS1EcmVhbU1h
33 aWxfQURfRU5ELS0 + PC9CT0RZPjwvSFRNTD4NCg ==
34
35 ------= _NextPart_08072922310693970267282_002 --
36 // 附件的内容
37 ------= _NextPart_08072922310693970267282_000
38 Content - Type: text / plain;
39 name = " 1.txt "
40 Content - Transfer - Encoding: base64
41 Content - Disposition: attachment;
42 filename = " 1.txt "
43
44 MTIz
45
46 ------= _NextPart_08072922310693970267282_000 --
看完之后应该对邮件结构有个大致的印象了,其实不难,牛人通常都是把各种值自己填充完,然后用winsocket发出去的。只是都喜欢偷懒,Indy已经帮忙包装好了,只管用就OK。
【准备工作】
准备啥呢?已经安装好最新版Indy10的BCB6、一个可以使用SMTP端口的邮件帐号和一条可以上网的线路。
【了解所需的控件】
TIdSMTP中的属性名称都很清楚(服务器地址、端口、认证类型、登录超时等),无需再多说。发信最关键是要用到控件的两个方法:Connect和Send。 Connect(AnsiString ServerAddress, AnsiString ServerPort)方法实现的是登录SMTP服务器和用户名认证;Send(TIdMessage *AMsg)方法实现的是认证后的发信过程,AMsg指的是信件的内容,也就是与上面所看到类似的信件源码,它是Indy10中的TIdMessage类 指针。
TIdMessage属性大致归两类,一是邮件头:就是寄信人、收信人、抄送、密送、主题、信件内容编码、附件编码等;二是邮件内容: 包括无控制符无标签的纯文本内容(Plain)、带控制符带标签的网页内容(Html)、编码后的附件(Attachment)。Indy10 中,Plain和Html用TIdText类实现,Attachment用TIdAttachmentFile实现。只要将两个类实例化并挂载在 TIdMessage下,就组成一个完整的邮件内容了。
【开始动手】
一个很简陋的界面:
堆上比较容易理解的代码:
2 // ---------------------------------------------------------------------------
3
4 #include < vcl.h >
5 #pragma hdrstop
6
7 #include " Main.h "
8 // ---------------------------------------------------------------------------
9 #pragma package(smart_init)
10 #pragma link " IdExplicitTLSClientServerBase "
11 #pragma link " IdSMTPBase "
12 #pragma resource " *.dfm "
13 TForm1 * Form1;
14 // ---------------------------------------------------------------------------
15 __fastcall TForm1::TForm1(TComponent * Owner)
16 : TForm(Owner)
17 {
18}
19 // ---------------------------------------------------------------------------
20 void __fastcall TForm1::btnSendClick(TObject * Sender)
21 {
22 //Mail
23 TIdText *idBody, *idHtml;
24 TIdAttachmentFile *idAtta;
25 try
26 {
27
28 idMsg=new TIdMessage(Application);
29 //Msg base header
30 idMsg->From->Name=edtName->Text.Trim();
31 idMsg->From->Address=edtMailaddr->Text.Trim();
32 idMsg->ReplyTo->EMailAddresses="no_reply@163.com";
33 idMsg->ContentType="multipart/alternative";
34 idMsg->ContentTransferEncoding="base64";
35 idMsg->AttachmentEncoding="MIME";
36 idMsg->Encoding=meDefault;
37 idMsg->CharSet="gb2312";
38 idMsg->Subject=edtSubject->Text.Trim();
39 idMsg->Recipients->EMailAddresses=edtTo->Text.Trim();
40 idMsg->Priority=mpNormal;
41
42 //msg body plain
43 idBody=new TIdText(idMsg->MessageParts, idMsg->Body);
44 idBody->CharSet="utf-8";
45 idBody->ContentType="text/plain";
46 idBody->ContentTransfer="base64";
47 idBody->Body->Add(mmoContent->Text);
48
49 //msg body html
50 idHtml=new TIdText(idMsg->MessageParts, idMsg->Body);
51 idHtml->CharSet="utf-8";
52 idHtml->ContentType="text/html";
53 idHtml->ContentTransfer="base64";
54 idHtml->Body->Add("<HTML><HEAD><TITLE>Mail</TITLE></HEAD>");
55 idHtml->Body->Add("<BODY>");
56 idHtml->Body->Add(mmoContent->Text);
57 idHtml->Body->Add("</BODY></HTML>");
58
59 //msg body attachment
60 if (edtAttach->Text.Trim()!="")
61 {
62 if (FileExists(edtAttach->Text.Trim()))
63 {
64 idAtta=new TIdAttachmentFile(idMsg->MessageParts, edtAttach->Text.Trim());
65
66 idAtta->ContentType="application/octet-stream";
67 idAtta->ContentDisposition="attachment";
68 idAtta->ContentTransfer="base64";
69 idAtta->FileName=ExtractFileName(edtAttach->Text.Trim());
70 }
71 else
72 {
73 edtAttach->Text="";
74 }
75 }
76 }
77 catch(Exception &exception)
78 {
79 //show Error!
80 idMsg->Clear();
81 delete idMsg;
82 return;
83 }
84
85 //SMTP Server
86 try
87 {
88 idSmtp->Username=edtUsername->Text;
89 idSmtp->Password=edtPasswd->Text;
90 idSmtp->HeloName="SMTP";
91 idSmtp->MailAgent="DreamMail";
92 idSmtp->UseEhlo=true;
93 idSmtp->ReadTimeout=5000;
94 idSmtp->Connect(edtServer->Text, StrToInt(edtPort->Text));
95 idSmtp->Send(idMsg);
96 }
97 catch()
98 {
99 //Show error!
100 idSmtp->Disconnect();
101 delete idMsg;
102 return;
103 }
104 idSmtp->Disconnect();
105 delete idMsg;
106 ShowMessage("Mail Sent!");
107}
108 // ---------------------------------------------------------------------------
109 void __fastcall TForm1::idSmtpConnected(TObject * Sender)
110 {
111 btnSend->Enabled=false;
112}
113 // ---------------------------------------------------------------------------
114 void __fastcall TForm1::idSmtpDisconnected(TObject * Sender)
115 {
116 btnSend->Enabled=true;
117}
118 // ---------------------------------------------------------------------------
119 void __fastcall TForm1::btnOpenfileClick(TObject * Sender)
120 {
121 try
122 {
123 TOpenDialog *opdGetfile=new TOpenDialog(Application);
124 opdGetfile->Options.Clear();
125 opdGetfile->Title = "Select attachment";
126 opdGetfile->Options << ofFileMustExist;
127 opdGetfile->InitialDir=ExtractFilePath(Application->ExeName);
128 opdGetfile->Filter = "All files (*.*)|*.*";
129 opdGetfile->FilterIndex = 2;
130 if(opdGetfile->Execute())
131 {
132 if (FileExists(opdGetfile->FileName))
133 edtAttach->Text=opdGetfile->FileName;
134
135 }
136 }
137 catch()
138 {
139 return;
140 }
141
142
143}
144 // ---------------------------------------------------------------------------
145
2 // ---------------------------------------------------------------------------
3
4 #ifndef MainH
5 #define MainH
6 // ---------------------------------------------------------------------------
7 #include < Classes.hpp >
8 #include < Controls.hpp >
9 #include < StdCtrls.hpp >
10 #include < Forms.hpp >
11 #include " IdExplicitTLSClientServerBase.hpp "
12 #include " IdSMTPBase.hpp "
13 #include < IdBaseComponent.hpp >
14 #include < IdComponent.hpp >
15 #include < IdMessageClient.hpp >
16 #include < IdSMTP.hpp >
17 #include < IdTCPClient.hpp >
18 #include < IdTCPConnection.hpp >
19 #include < IdText.hpp > // TIdText needed
20 #include < IdAttachmentFile.hpp > // TIdAttachment needed
21 // #include <IdMessageCoderMIME.hpp>
22 #include < IdCoderHeader.hpp >
23
24 // ---------------------------------------------------------------------------
25 class TForm1 : public TForm
26 {
27__published: // IDE-managed Components
28 TGroupBox *grpServer;
29 TEdit *edtServer;
30 TEdit *edtPort;
31 TEdit *edtUsername;
32 TEdit *edtPasswd;
33 TIdSMTP *idSmtp;
34 TGroupBox *grpMail;
35 TEdit *edtName;
36 TEdit *edtMailaddr;
37 TEdit *edtTo;
38 TEdit *edtSubject;
39 TMemo *mmoContent;
40 TButton *btnSend;
41 TEdit *edtAttach;
42 TButton *btnOpenfile;
43 TLabel *lbl1;
44 TLabel *lbl2;
45 TLabel *lbl3;
46 TLabel *lbl4;
47 TLabel *lbl5;
48 TLabel *lbl6;
49 TLabel *lbl7;
50 TLabel *lbl8;
51 TLabel *lbl9;
52 void __fastcall btnSendClick(TObject *Sender);
53 void __fastcall idSmtpConnected(TObject *Sender);
54 void __fastcall idSmtpDisconnected(TObject *Sender);
55 void __fastcall btnOpenfileClick(TObject *Sender);
56private: // User declarations
57 TIdMessage *idMsg;
58 //TIdMessageEncoderMIME *idMsg;
59public: // User declarations
60 __fastcall TForm1(TComponent* Owner);
61} ;
62 // ---------------------------------------------------------------------------
63 extern PACKAGE TForm1 * Form1;
64 // ---------------------------------------------------------------------------
65 #endif
66
完成后编译试试!