2017-02-09 8 views
1

Мне было интересно, можете ли вы помочь мне понять, что здесь происходит не так. Я пытаюсь написать маленького клиента, который подключается к SMTP-диалогу на порту 25.Использование C++ для отправки 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) + "\r\n", buf); 
    doit(sock, "MAIL FROM: <" + org + "> \r\n", buf); 
    doit(sock, "RCPT TO: <" + dest + "> \r\n", buf); 
    doit(sock, "DATA \r\n", buf); 
    readstuff(sock, buf); //should say "go ahead" 

    //User writes email here  
    while (true) {    
     string line = ""; 
     getline(cin, line); 
     doit(sock, line + "\r\n", buf); 
     if (line == ".") { 
      readstuff(sock, buf); //should say "email cleared to send" 
      return 0; 
     } 
    } 
} 
+0

Что сервер сказать вместо идти вперед? Off Topic: ваши чтения могут не получить весь ответ. Вы должны зациклиться до тех пор, пока не получите сообщение терминатора – user4581301

ответ

0

Пожалуйста, прочитайте спецификацию SMTP, RFC 5321, в частности, разделы 4.1.1.4 DATA и 4.5.2 Transparency:

 
4.1.1.4. DATA (DATA) 

    The receiver normally sends a 354 response to DATA, and then treats 
    the lines (strings ending in <CRLF> sequences, as described in 
    Section 2.3.7) following the command as mail data from the sender. 
    This command causes the mail data to be appended to the mail data 
    buffer. The mail data may contain any of the 128 ASCII character 
    codes, although experience has indicated that use of control 
    characters other than SP, HT, CR, and LF may cause problems and 
    SHOULD be avoided when possible. 

    The mail data are terminated by a line containing only a period, that 
    is, the character sequence "<CRLF>.<CRLF>", where the first <CRLF> is 
    actually the terminator of the previous line (see Section 4.5.2). 
    This is the end of mail data indication. The first <CRLF> of this 
    terminating sequence is also the <CRLF> that ends the final line of 
    the data (message text) or, if there was no mail data, ends the DATA 
    command itself (the "no mail data" case does not conform to this 
    specification since it would require that neither the trace header 
    fields required by this specification nor the message header section 
    required by RFC 5322 [4] be transmitted). An extra <CRLF> MUST NOT 
    be added, as that would cause an empty line to be added to the 
    message. The only exception to this rule would arise if the message 
    body were passed to the originating SMTP-sender with a final "line" 
    that did not end in <CRLF>; in that case, the originating SMTP system 
    MUST either reject the message as invalid or add <CRLF> in order to 
    have the receiving SMTP server recognize the "end of data" condition. 

    The custom of accepting lines ending only in <LF>, as a concession to 
    non-conforming behavior on the part of some UNIX systems, has proven 
    to cause more interoperability problems than it solves, and SMTP 
    server systems MUST NOT do this, even in the name of improved 
    robustness. In particular, the sequence "<LF>.<LF>" (bare line 
    feeds, without carriage returns) MUST NOT be treated as equivalent to 
    <CRLF>.<CRLF> as the end of mail data indication. 

    Receipt of the end of mail data indication requires the server to 
    process the stored mail transaction information. This processing 
    consumes the information in the reverse-path buffer, the forward-path 
    buffer, and the mail data buffer, and on the completion of this 
    command these buffers are cleared. If the processing is successful, 
    the receiver MUST send an OK reply. If the processing fails, the 
    receiver MUST send a failure reply. The SMTP model does not allow 
    for partial failures at this point: either the message is accepted by 
    the server for delivery and a positive response is returned or it is 
    not accepted and a failure reply is returned. In sending a positive 
    "250 OK" completion reply to the end of data indication, the receiver 
    takes full responsibility for the message (see Section 6.1). Errors 
    that are diagnosed subsequently MUST be reported in a mail message, 
    as discussed in Section 4.4. 

    When the SMTP server accepts a message either for relaying or for 
    final delivery, it inserts a trace record (also referred to 
    interchangeably as a "time stamp line" or "Received" line) at the top 
    of the mail data. This trace record indicates the identity of the 
    host that sent the message, the identity of the host that received 
    the message (and is inserting this time stamp), and the date and time 
    the message was received. Relayed messages will have multiple time 
    stamp lines. Details for formation of these lines, including their 
    syntax, is specified in Section 4.4. 

    Additional discussion about the operation of the DATA command appears 
    in Section 3.3. 

    Syntax: 

     data = "DATA" CRLF 
 
4.5.2. Transparency 

    Without some provision for data transparency, the character sequence 
    "<CRLF>.<CRLF>" ends the mail text and cannot be sent by the user. 
    In general, users are not aware of such "forbidden" sequences. To 
    allow all user composed text to be transmitted transparently, the 
    following procedures are used: 

    o Before sending a line of mail text, the SMTP client checks the 
     first character of the line. If it is a period, one additional 
     period is inserted at the beginning of the line. 

    o When a line of mail text is received by the SMTP server, it checks 
     the line. If the line is composed of a single period, it is 
     treated as the end of mail indicator. If the first character is a 
     period and there are other characters on the line, the first 
     character is deleted. 

    ... 

Ваша команда DATA необходимо учитывать, что:

  • Ваш 01 Функцияожидает ответа после отправки строки. Это неправильная логика для использования при отправке данных электронной почты. Вы не можете прочитать ответ до тех пор, пока не будет отправлено окончательное завершение <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 + "\r\n"); 
    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, "\r\n") < 0) { 
      // failed, do something... 
     } 
    } 

    // NOW call readResponse() here! 
    if (sendCmd(sock, ".") != 250) { 
     // failed, do something... 
    } 

    sendCmd(sock, "QUIT"); 
    close(sock); 

    return 0; 
} 
+0

Ооо очень интересная документация .. и я вижу, в чем проблема. спасибо – nicho

Смежные вопросы