通过C套接字编程的邮件格式

时间:2018-02-19 08:35:50

标签: c email smtp mime winsock2

我正在学习C语言中的套接字编程,学习内容的最佳方法是实现它。所以我编写了一个简单的程序,通过C套接字编程发送附件和一些文本体的邮件。到目前为止,代码只有一个例外 - 格式化。 MIME标题显示在邮件正文中(附带屏幕截图),邮件主题全部搞砸了。

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

#define SMTP_PORT "25"
#define DEFAULT_BUFLEN 1024

int sendData(SOCKET* socket, const char* data)
{
    int iResult;
    printf("%s", data);
    iResult = send(*socket, data, (int)strlen(data), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed (msg: %s): %d\n", data, WSAGetLastError());
    }
    return iResult;
}

void recvData(SOCKET* socket)
{
    int recvbuflen = DEFAULT_BUFLEN;
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    iResult = recv(*socket, recvbuf, recvbuflen - 1, 0);
    printf("In recvData function: %d\n", iResult);
    if (iResult > 0) {
        recvbuf[iResult] = '\0';
        printf("%s", recvbuf);
    }
    else if (iResult == 0)
        printf("Connection closed\n");
    else
        printf("recv failed: %d\n", WSAGetLastError());
}

int main(int argc, char** argv)
{
    WSADATA wsaData;

    int iResult;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }

    struct addrinfo *result = NULL;
    struct addrinfo *ptr = NULL;
    struct addrinfo hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    iResult = getaddrinfo("mail.sharklasers.com", SMTP_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    SOCKET ConnectSocket = INVALID_SOCKET;
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    sendData(&ConnectSocket, "HELO mail.sharklasers.com\r\n");
    recvData(&ConnectSocket);
    sendData(&ConnectSocket, "MAIL FROM: psgibrxp@sharklasers.com\r\n");
    recvData(&ConnectSocket);
    sendData(&ConnectSocket, "RCPT TO: psgibrxp@sharklasers.com\r\n");
    recvData(&ConnectSocket);
    sendData(&ConnectSocket, "DATA\r\n");
    recvData(&ConnectSocket);
    sendData(&ConnectSocket, "Subject:This is subject of my mail\r\n");
    sendData(&ConnectSocket, "And this is text\r\n");
    sendData(&ConnectSocket, "MIME-Version: 1.0\r\n");
    sendData(&ConnectSocket, "Content-Type:multipart/mixed;boundary=\"KkK170891tpbkKk__FV_KKKkkkjjwq\"\r\n");
    sendData(&ConnectSocket, "--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n");
    sendData(&ConnectSocket, "Content-Transfer-Encoding: quoted-printable\r\n");
    sendData(&ConnectSocket, "Content-Type: text/plain;name=\"details.txt\"\r\n");
    sendData(&ConnectSocket, "Content-Disposition: attachment; filename=\"details.txt\"\r\n");
    sendData(&ConnectSocket, "\r\n\r\n");

    FILE* MailFilePtr = fopen("details.txt", "r");
    if (MailFilePtr == NULL)
        printf("Error opening attachment\n");

    char FileBuffer[1024];
    char buf[1024];

    memset(FileBuffer, 0, sizeof(FileBuffer));
    while (fgets(FileBuffer, sizeof(FileBuffer), MailFilePtr))
    {
        sprintf(buf, "%s", FileBuffer);
        buf[strlen(buf) - 1] = 0;
        sendData(&ConnectSocket, buf);
        memset(FileBuffer, 0, sizeof(FileBuffer));
        memset(buf, 0, sizeof(buf));
    }

    fclose(MailFilePtr);

    sendData(&ConnectSocket, "\r\n\r\n--KkK170891tpbkKk__FV_KKKkkkjjwq--\r\n\r\n");
    sendData(&ConnectSocket, ".\r\n");
    recvData(&ConnectSocket);

    sendData(&ConnectSocket, "QUIT\r\n");

    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}

问题: 如何以正确的格式获取邮件正文?我想我错过了一些SMTP命令或命令序列,但我不确定。

注意

  1. 我现在正在使用Guerrilla Mail,因为Gmail需要SSL \ TLS是沟通所必需的(如果我错了,请纠正我)。我将使用openssl库。
  2. 这不是最终的代码。我必须做一些代码清理。但是,也欢迎代码审查建议。
  3. 我在这里学习,所以欢迎任何有关C语言和网络编程的链接,书籍参考。
  4. 修改

    编辑的代码基于目前为止的评论和答案。不幸的是问题仍然存在。 我没有读取文件,而是直接传递base64编码的附件(这是有效的)。此编辑代码的输出显示在下面的屏幕截图

        sendData(&ConnectSocket, "MIME-Version: 1.0\r\n");
        sendData(&ConnectSocket, "Content-Type:multipart/mixed;boundary=\"977d81ff9d852ab2a0cad646f8058349\"\r\n");
        sendData(&ConnectSocket, "Subject:This is subject of my mail\r\n");
        sendData(&ConnectSocket, "--977d81ff9d852ab2a0cad646f8058349\r\n");
        sendData(&ConnectSocket, "Content-Type: text/plain; charset=\"utf-8\"\r\n");
        sendData(&ConnectSocket, "Content-Transfer-Encoding: quoted-printable\r\n\r\n");
        sendData(&ConnectSocket, "Hi Me,=0A=0AThis is an empty file.=0A=0ARegards,=0A<ME>=0A=0A---- =0ASent using Guerrillamail.com =0ABlock or report abuse : https://www.guerrillamail.com//abuse/?a=3DUVJzDA8SW6Q1mwa14nUTcwfCX9ne0dhd=0A \r\n\r\n");
        sendData(&ConnectSocket, "--977d81ff9d852ab2a0cad646f8058349\r\n");
        sendData(&ConnectSocket, "Content-Type: text/plain\r\n");
        sendData(&ConnectSocket, "Content-Transfer-Encoding: base64\r\n");
        sendData(&ConnectSocket, "Content-Disposition: attachment; filename=\"details.txt\"\r\n\r\n");
        sendData(&ConnectSocket, "U2FtcGxlIFRleHQu");
        sendData(&ConnectSocket, "\r\n\r\n--977d81ff9d852ab2a0cad646f8058349--\r\n\r\n");
        sendData(&ConnectSocket, ".\r\n");
        recvData(&ConnectSocket);
        sendData(&ConnectSocket, "QUIT\r\n");
    

    已编辑的输出邮件屏幕截图:

    Output Mail Screenshot

2 个答案:

答案 0 :(得分:1)

需要使用空行将标题与标题分开。

sendData(&ConnectSocket, "MIME-Version: 1.0\r\n");
sendData(&ConnectSocket, "Content-Type:multipart/mixed;boundary=\"977d81ff9d852ab2a0cad646f8058349\"\r\n");
sendData(&ConnectSocket, "Subject:This is subject of my mail\r\n");
sendData(%ConnectSocket, "\r\n"); /* added */
sendData(&ConnectSocket, "--977d81ff9d852ab2a0cad646f8058349\r\n");
sendData(&ConnectSocket, "Content-Type: text/plain; charset=\"utf-8\"\r\n");
sendData(&ConnectSocket, "Content-Transfer-Encoding: quoted-printable\r\n\r\n");
sendData(&ConnectSocket, "Hi Me,=0A=0AThis is an empty file.=0A=0ARegards,=0A<ME>=0A=0A---- =0ASent using Guerrillamail.com =0ABlock or report abuse : https://www.guerrillamail.com//abuse/?a=3DUVJzDA8SW6Q1mwa14nUTcwfCX9ne0dhd=0A \r\n\r\n");
sendData(&ConnectSocket, "--977d81ff9d852ab2a0cad646f8058349\r\n");
sendData(&ConnectSocket, "Content-Type: text/plain\r\n");
sendData(&ConnectSocket, "Content-Transfer-Encoding: base64\r\n");
sendData(&ConnectSocket, "Content-Disposition: attachment; filename=\"details.txt\"\r\n\r\n");
sendData(&ConnectSocket, "U2FtcGxlIFRleHQu");
sendData(&ConnectSocket, "\r\n\r\n--977d81ff9d852ab2a0cad646f8058349--\r\n\r\n");
sendData(&ConnectSocket, ".\r\n");

当您向SMTP发送内容时,应正确检查结果代码是否为预期结果(大多数命令为2xx,DATA为345)。

使用静态边界字符串不会随着时间的推移正常工作(尽管如果您发现它嵌入内容中,我认为您可以通过编码边界字符串来替换或混淆)。

当邮件长度超过大约1,000个字符时,将整个邮件作为单个长行发送将开始中断。引用可打印的原因之一是它允许您透明地在内容中嵌入换行符(尽管只发送包含换行符的文字换行符在这种情况下已经让你相当远)。

格式正确的邮件还应包含From:To:标题。

答案 1 :(得分:0)

在Content-Type中,

名称看起来不必要,将其更改为

sendData(&ConnectSocket, "MIME-Version: 1.0\r\n");
sendData(&ConnectSocket, "Content-Type:multipart/mixed;boundary=\"KkK170891tpbkKk__FV_KKKkkkjjwq\"\r\n");
sendData(&ConnectSocket, "--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n");
sendData(&ConnectSocket, "Content-Type: text/plain\r\n");
sendData(&ConnectSocket, "And this is text\r\n");
sendData(&ConnectSocket, "--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n");
sendData(&ConnectSocket, "Content-Type: text/plain;name=\"details.txt\"\r\n");
sendData(&ConnectSocket, "Content-Transfer-Encoding: base64\r\n");
sendData(&ConnectSocket, "Content-Disposition: attachment; filename=\"details.txt\"\r\n");

修改

请更改为此内容(将文本/ plain下面的邮件消息移动,然后执行其他更改)

<img id="s_2_1_193_0_icon" class="applet-form-combo" data-allowdblclick="true" src="images/janna/down.gif" alt="Combobox Field" style="display: inline;">
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible">
    20 results are available, use up and down arrow keys to navigate.
</span>