使用c++发送smtp邮件

Using c++ to send smtp mail

本文关键字:邮件 smtp 发送 c++ 使用      更新时间:2023-10-16

我想知道你能不能帮我了解一下这里出了什么问题。我正在尝试编写一个小客户端,在端口25上进行SMTP对话。

如果你回忆起SMTP,你需要发送一些东西,然后在DATA消息之后写电子邮件,在它自己的行上用句点结束,以发送电子邮件。

程序处理此问题的方式存在问题。它可以很好地处理对话,直到DATA消息之后。只有我先键入它,它才会识别句点。在任何后续行之后,任何代码执行似乎都将丢失。if语句无法识别是否已输入期间。再次感谢。附件为相关代码。。

void readstuff(int sock, char* buf) {
int r = read (sock, buf, BUFSIZE -1);
buf[r] = NULL;
cout << buf << endl;
}
void doit(int sock, string arg, char* buf) {
int r = write(sock, arg.c_str(), arg.length());
readstuff(sock, buf);
}
int main(int argc, char *argv[]) {
char buf[BUFSIZE];
// Make a socket
int sock = MakeSocket(argv[1], argv[2]);
cout << "socket is " << sock << endl;
assert(sock != -1);
// Begin dialogue
doit(sock, "HELO " + org.substr(org.find("@") + 1) + "rn", buf);
doit(sock, "MAIL FROM: <" + org + "> rn", buf);
doit(sock, "RCPT TO: <" + dest + "> rn", buf);
doit(sock, "DATA rn", buf);
readstuff(sock, buf);  //should say "go ahead"
//User writes email here      
while (true) {              
string line = "";
getline(cin, line);
doit(sock, line + "rn", buf);
if (line == ".") {
readstuff(sock, buf); //should say "email cleared to send"
return 0;
}
}
}

请阅读SMTP规范,RFC 5321,特别是第4.1.1.4节"数据"和第4.5.2节"透明度:">

4.1.1.4.数据(DATA)接收器通常向DATA发送354响应,然后处理行(以<CRLF>序列结尾的字符串,如中所述第2.3.7节)作为来自发件人的邮件数据。此命令会将邮件数据附加到邮件数据中缓冲器邮件数据可以包含128个ASCII字符中的任何一个代码,尽管经验表明使用控制SP、HT、CR和LF以外的字符可能会导致问题应尽可能避免。邮件数据由一行终止,该行仅包含一个句点是,字符序列"<CRLF>.[lt;CRLF>",其中第一个<CRLF>是实际上是前一行的终止符(见第4.5.2节)。这是邮件数据的结束指示。第一<CRLF>终止序列也是<CRLF>结束数据(消息文本),或者,如果没有邮件数据,则结束data命令本身("无邮件数据"的情况不符合规范,因为它将不要求跟踪标头本规范或消息头部分所需的字段RFC 5322[4]所要求的)。额外的<CRLF>不得添加,因为这将导致在消息如果消息正文已通过最后一行传递给原始SMTP发件人并没有以<CRLF>;在这种情况下,原始SMTP系统必须以无效为由拒绝该消息,或者添加<CRLF>以便让接收SMTP服务器识别"数据结束"条件。接受仅以<LF>,作为对一些UNIX系统的不一致行为已被证明造成的互操作性问题多于解决的问题,以及SMTP服务器系统决不能这样做,即使是以改进的名义坚固性。特别是序列"<LF>.<LF>"(裸线进料,无回车)不得视为等同于<CRLF><CRLF>作为邮件数据的结束指示。接收邮件结束数据指示需要服务器处理所存储的邮件交易信息。此处理消耗反向路径缓冲区、正向路径中的信息缓冲区和邮件数据缓冲区,并在完成此操作时命令清除这些缓冲区。如果处理成功,接收器必须发送OK回复。如果处理失败接收方必须发送失败回复。SMTP模型不允许对于此时的部分故障:要传递的服务器,并且返回肯定响应,或者不接受并且返回失败回复。发送肯定"250 OK"完成应答数据结束指示,接收机对消息承担全部责任(见第6.1节)。错误随后被诊断的必须在邮件消息中报告,如第4.4节所述。当SMTP服务器接受用于中继或最后交付时,它插入一个跟踪记录(也称为可替换为"时间戳行"或"接收"行)的邮件数据。此跟踪记录指示发送消息的主机,接收消息的主机的标识消息(并且正在插入此时间戳),以及日期和时间消息已收到。中继邮件将有多次邮票线条。这些线路的形成细节,包括语法,如第4.4节所述。出现有关DATA命令操作的其他讨论第3.3节。语法:data="data"CRLF
4.5.2。透明度在没有数据透明度的情况下,字符序列"<CRLF>.[lt;CRLF>"结束邮件文本,用户无法发送。一般来说,用户不知道这种"被禁止"的序列。到允许透明地传输所有用户编写的文本使用以下程序:o在发送一行邮件文本之前,SMTP客户端会检查行的第一个字符。如果是一个周期,则增加一个句点插入行的开头。o当SMTP服务器接收到一行邮件文本时,它会进行检查线路。如果该行由单个周期组成,则为被视为邮件结束指示符。如果第一个字符是句点,行上还有其他字符,第一个字符被删除。…

您的DATA命令需要考虑到这一点:

  • 您的doit()函数在发送字符串后期望得到响应。这是发送电子邮件数据时使用的错误逻辑。在发送最终终止<CRLF>.<CRLF>之后,才能读取响应。

  • 您必须将透明度应用于电子邮件中以.字符开头的任何一行。

话虽如此,还是试试类似的东西:

int readLine(int sock, string &line)
{
// read a line from sock until CRLF is reached.
// I leave this as an exercise for you to implement...
line = ...;
return -1 on error, else 0;
}
int readResponse(int sock)
{
// Please read RFC 5321 section 4.2 for the PROPER format
// of an SMTP response.  You should be reading from the
// socket until you receive the terminating
// "Reply-code [ SP textstring ] CRLF" line...
string line;
int r = readLine(sock, line);
if (r < 0) return r;
string code = line.substr(0, 3);
string text = line.substr(4);
if ((line.length() >= 4) && (line[3] = '-'))
{
do
{
r = readLine(sock, line);
if (r < 0) return r;
text += (" " + line.substr(4));
}
while (line.compare(0, 4, code+"-") == 0);
}
cout << code << ": " << text << endl;
return stoi(code);
}
int sendText(int sock, const string &arg)
{
const char *p = arg.c_str();
int len = arg.length();
while (len > 0)
{
int r = write(sock, p, len);
if (r <= 0) return -1;
p += r;
len -= r;
}
return 0;
}
int sendCmd(int sock, const string &arg)
{
int r = sendText(sock, arg + "rn");
if (r < 0) return r;
return readResponse(sock);
}
int main(int argc, char *argv[])
{
// Make a socket
int sock = MakeSocket(argv[1], argv[2]);
cout << "socket is " << sock << endl;
assert(sock != -1);
// Begin dialogue
// read the server greeting first...
if (readResponse(sock) != 220) {
// failed, do something...
}
if (sendCmd(sock, "HELO " + org.substr(org.find("@") + 1)) != 250) {
// failed, do something...
}
if (sendCmd(sock, "MAIL FROM: <" + org + ">") != 250) {
// failed, do something...
}
int r = sendCmd(sock, "RCPT TO: <" + dest + ">");
if ((r != 250) && (r != 251)) {
// failed, do something...
}
if (sendCmd(sock, "DATA") != 354) {
// failed, do something...
}
//User writes email here      
while (true) {              
string line;
getline(cin, line);
// A line consisting of only "." is a valid line in an email
// message, so you should not use that as a terminator in your
// input, use something else, like an EOF marker, or CTRL-C,
// or something...
if (some termination condition)
break;
// DO NOT call readResponse() here!
if (!line.empty() && (line[0] == '.')) {
if (sendText(sock, ".") < 0) {
// failed, do something...
}
}
if (sendText(sock, line) < 0) {
// failed, do something...
}
if (sendText(sock, "rn") < 0) {
// failed, do something...
}
}
// NOW call readResponse() here!
if (sendCmd(sock, ".") != 250) {
// failed, do something...
}
sendCmd(sock, "QUIT");
close(sock);
return 0;
}