멀더님, 임문환님... 두분모두 진심으로 감사드립니다.
telnet을 사용해서, 메일보내는부분과 mx host query하는부분 모두 공부하고는 있지만,,,
아직 코딩실력이 많이 모자라서 헤매고 있었는데...
두분의 도움으로 이해가 막 될려고(?) 하고 있습니다.
좋은 하루 되세요.
멀더 님이 쓰신 글 :
: 멀더입니다. 수정했습니다.
:
: 필요하신건 수신자메일도메인의 MX 호스트 값을 구하시면 됩니다.
: MX 호스트를 구한다음 그호스트를 SMTP OutBound서버로 이용해
: 임문환님의 방법과 같이 SMTP 발송하시면 OK.
: 그러면 Relay용 SMTP outbound 서버가 필요없어 지지요...;)
:
: 참고 :
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=58
:
:
: 초보자 님이 쓰신 글 :
: : 임문환.실업자 님이 쓰신 글 :
: : : 초보자 님이 쓰신 글 :
: : : : SMTP 서버 없이 이메일 전송할수 있는 예제코드나, 컴포넌트 없을까요?
: : : : 델마당이나 여기저기 기웃거리고, 나름대로 SMTP 쪽 공부한다고 하는데....
: : : : 머리가 나쁜지 영 이해를 하지 못하겠네요.
: : : :
: : : : 없다면, 구현을 하기위한 대략적인 OUTLINE이라도 그려주실분 없나요?
: : : : 그럼 즐거운 하루 되시길 바랍니다.
: : :
: : : 이 것을 어디에 사용하려 하시는지 궁금해지네요.
: : : 컴포넌트로 되어 있는 것은 모르겠구요 간단히 흐름만을 말씀드리겠습니다.
: : :
: : : SMTP(Simple Mail Transfer Protocol) Sender 구현에 관해
: : :
: : : 관련 RFC: rfc821(
http://www.ietf.org/rfc/rfc0821.txt?number=821)
: : :
: : : 0. 아래 작업은 보통 별도의 스레드 내에서 하는 것이 좋음
: : : 애플리케이션 내의 공용 자원들에 대해서는 동기화 메커니즘을 사용하는 게 안전함.
: : :
: : : 1. SMTP Receiver에 접속
: : : - SMTP 기본 포트는 25이지만 이를 무시하는 경우도 있으므로 각 서버마다 실제 포트를 알아야 함
: : : - 호스트와 포트를 알았다고 해도 등록된 IP들에만 개방하는 경우가 있어 접속 불가능할 수도 있음
: : : - TCP blocking 모드를 사용
: : :
: : : 2. 일단 접속에 성공하면 Receiver 쪽에서 응답코드를 반환하게 됨.
: : : 즉, 아래와 같은 형식의 문자열을 반환함.
: : : 220 USC-ISIF.ARPA Simple Mail Transfer Service Ready\r\n
: : : Sender는 이를 받아서 코드가 220인지 확인해야 하고 220이 아니면 메일을 보낼 수 없음.
: : :
: : : 3. Sender는 "HELO"를 아래와 같은 형식으로 Receiver에게 보내야 함.
: : : HELO HostNameOfSender\r\n
: : : 예: HELO LBL-UNIX.ARPA\r\n
: : : Receiver는 이를 받고 확인차원에서 아래 메시지를 보냄
: : : 250 HostNameOfReceiver\r\n
: : : 예: 250 USC-ISIF.ARPA\r\n
: : : 응답코드가 250이 아니면 메일을 보낼 수 없음.
: : :
: : : 4. 아래 메시지를 Receiver에 보냄
: : : MAIL FROM:<mo@LBL-UNIX.ARPA>\r\n
: : : 위에서 <> 안에 보내는이의 메일주소를 넣으면 됨.
: : : Receiver가 메일을 처리할 의사가 있으면 아래와 같은 형식으로 250 응답코드를 보내게 됨.
: : : 250 OK\r\n
: : : 응답코드가 250이 아니면 메일을 처리할 의사가 없는 것임.
: : : 응답코드는 여러가지 중 하나인데 각 응답코드에 대해서는 rfc821 문서를 참고.
: : :
: : : 5. 아래와 같은 형식으로 받는이의 메일주소를 Receiver에게 보냄
: : : RCPT TO:<fred@USC-ISIF.ARPA>\r\n
: : : Receiver가 250 또는 251이 아닌 응답코드를 보내면 메일을 보낼 수 없음.
: : : 251인 경우 포워드를 뜻하고 아래와 같은 형식으로 포워드 메일주소를 Receiver가 보내줌
: : : 251 User not local; will forward to <Jones@USC-ISI.ARPA>\r\n
: : : 각 응답코드에 대해서는 rfc821 문서를 참고.
: : :
: : : 6. 아래 메시지를 Receiver에게 보냅니다.
: : : DATA\r\n
: : : 이에 대해 Receiver가 354가 아닌 응답코드를 보내면 메일을 보낼 수 없음.
: : : Receiver가 354 응답코드를 보낼 때는 아래와 같이 메일의 끝을 지정하는 방법을 같이 보냅니다.
: : : 아래의 경우 메일 내용을 Receiver가 수신받는 중에 C언어 문자열로 말하자면 "\r\n.\r\n"을 만나면 그게 메일의 끝표시로 간주하겠다는 뜻입니다.
: : : 따라서 보내는 쪽에서는 메일끝에 "\r\n.\r\n"를 추가로 보내면 되는 것이지요. 보통 이것을 사용합니다.
: : : 354 Start mail input; end with <CRLF>.<CRLF>\r\n
: : :
: : : 7. 메일 내용(메일헤더 등 메일의 모든 내용)을 보냅니다.
: : :
: : : 8. 메일의 내용을 다 보냈으면 끝표시를 보냅니다.
: : : 위 예의 경우 "\r\n.\r\n"를 보냅니다.
: : : Receiver는 아래 응답을 보낼 것입니다.
: : : 250 OK\r\n
: : :
: : : 9. 마지막으로 종료 메시지를 보냅니다.
: : : QUIT\r\n
: : : Receiver는 이에 대해 아래와 같은 형식의 응답 메시지를 보냅니다.
: : : 221 USC-ISIF.ARPA Service closing transmission channel\r\n
: : :
: : : 10. TCP 커넥션을 끊습니다.
: : :
: : :
: : : 참고로, 메일 내용을 어떻게 구성해야 하는지는,
: : : 어떤 메일이든 하나 골라서 파일로 저장한 다음
: : : 텍스트 편집기로 열어보면 그 구조가 모두 나와 있으므로 이를 참고하면 됩니다.
: : : 한편, 첨부 파일 등 ASCII 코드 범위를 벗어난 Byte가 있는 경우는 인코딩을 해야하는데 보통 Base64 인코딩을 사용합니다. 제목에 한글이 있는 경우와 첨부파일은 인코딩해야 한다고 보면 됩니다.
: : :
: : :
: : : /////////////////////////////////////////////////////////////////////////
: : : void __fastcall TSendMailThread::Execute()
: : : {
: : : CSock1 = new TClientSocket(NULL);
: : : CSock1->Active=false;
: : : CSock1->ClientType=ctBlocking;
: : : CSock1->Port=25;
: : : CSock1->OnConnect = ClientSocket1Connect;
: : : CSock1->Host=Mail->ReceiverSMTP;
: : : CSock1->Open();
: : : .
: : : .
: : : .
: : :
: : : }
: : :
: : : /////////////////////////////////////////////////////////////////////////
: : : void __fastcall TSendMailThread::ClientSocket1Connect(TObject *Sender,
: : : TCustomWinSocket *Socket)
: : : {
: : : SendMail();
: : : }
: : :
: : : /////////////////////////////////////////////////////////////////////////
: : : int __fastcall TSendMailThread::SendMail()
: : : {
: : : String toAddress;
: : : String rep="";
: : : int repCode;
: : : String body;
: : : TReceiver *receiver;
: : :
: : : if(!CSock1) return -1;
: : :
: : : toAddress = receiver->Id + "@" + Mail->ReceiverDomain;
: : :
: : : Completed=false;
: : :
: : : pStream = new TWinSocketStream(CSock1->Socket, 30000);
: : : try
: : : {
: : : while (!Terminated && CSock1->Active)
: : : {
: : : try
: : : {
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=220)
: : : {
: : : receiver->Success=false;
: : : receiver->TryLater=true;
: : : receiver->ResultCode=repCode;
: : : return repCode;
: : : }
: : :
: : : SendMessage("HELO " + Mail->SenderSMTP + "\r\n");
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=250)
: : : {
: : : receiver->Success=false;
: : : receiver->TryLater=true;
: : : receiver->ResultCode=repCode;
: : : return repCode;
: : : }
: : :
: : : SendMessage(String("MAIL FROM: ") + "<" + Mail->From + ">\r\n");
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=250)
: : : {
: : : receiver->Success=false;
: : : receiver->ResultCode=repCode;
: : :
: : : if(repCode==500 || repCode==501 || repCode==421)
: : : {
: : : receiver->TryLater=false;
: : : }
: : : else
: : : {
: : : receiver->TryLater=true;
: : : }
: : : return repCode;
: : : }
: : :
: : : SendMessage(String("RCPT TO: ") + "<" + toAddress + ">\r\n");
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=250 && repCode!=251)
: : : {
: : : receiver->Success=false;
: : : receiver->TryLater=false;
: : : receiver->ResultCode=repCode;
: : : if(repCode==550)
: : : {
: : : }
: : : else if(repCode==551)
: : : {
: : : Mail->NewReceiverAddress=GetAddress(rep);
: : : }
: : : else if((repCode>=500 && repCode<=504) || repCode==553)
: : : {
: : : }
: : : else if(repCode==552 || repCode==450 ||repCode==451 ||repCode==452)
: : : {
: : : receiver->TryLater=true;
: : : }
: : : else
: : : {
: : : receiver->TryLater=true;
: : : }
: : : return repCode;
: : : }
: : :
: : : SendMessage(String("DATA\r\n"));
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=354)
: : : {
: : : receiver->Success=false;
: : : receiver->TryLater=true;
: : : receiver->ResultCode=repCode;
: : : return repCode;
: : : }
: : :
: : : body= "To: " + toAddress + " <" + toAddress + ">\r\n";
: : : body=body+Mail->Headers;
: : : body = body + Mail->Body1;
: : : body = body + Mail->Body3 + Mail->Attachment + "\r\n.\r\n";
: : :
: : : SendMessageBody(body);
: : :
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : if(repCode!=250)
: : : {
: : : receiver->Success=false;
: : : receiver->TryLater=true;
: : : receiver->ResultCode=repCode;
: : : return repCode;
: : : }
: : :
: : : SendMessage("QUIT\r\n");
: : : rep = GetReply();
: : : if(rep.IsEmpty()) ;
: : : if(!extractReplyCode(rep,repCode)) ;
: : :
: : : CSock1->Close();
: : :
: : : receiver->Success=true;
: : : receiver->TryLater=false;
: : : receiver->ResultCode=repCode;
: : : return 0;
: : : }
: : : catch (Exception &e)
: : : {
: : : //ShowMessage(e.Message);
: : : CSock1->Close();
: : : return -1;
: : : }
: : : }
: : : }
: : : __finally
: : : {
: : : delete pStream;
: : : }
: : :
: : : return 0;
: : : }
: : :
: : :
: : : /////////////////////////////////////////////////////////////////////////
: : : void __fastcall TSendMailThread::SendMessage(const String& msg)
: : : {
: : : pStream->Write(msg.c_str(), msg.Length());
: : : }
: : :
: : : /////////////////////////////////////////////////////////////////////////
: : : String __fastcall TSendMailThread::GetReply(void)
: : : {
: : : char buffer[1024];
: : : memset(buffer, 0, 1024);
: : : if(pStream->WaitForData(10000))
: : : {
: : : if(pStream->Read(buffer, 1024) == 0)
: : : {
: : : CSock1->Close();
: : : }
: : : return String(buffer);
: : : }
: : : else
: : : {
: : : CSock1->Close();
: : : return "";
: : : }
: : : return String(buffer);
: : : }
: :
: : 감사합니다.
: : 어디에 쓰려고 하는게 아니구요...
: : 그냥 순수히 학문적인 취지에서 질문한 겁니다.
: : 궁금해 하시는 것처럼... 어디(?)에 쓰지는 않을겁니다.
: : 빌더에 있는 smtp 컴퍼넌트를 사용해서는 성공을 했는데, smtp 서버없이는 어떻게 될까?하고 궁금해서 질문을 올린겁니다.
: : 답변 감사드리구요.. 좋은 하루되세요.