Отправка письма через smtp C++

141
20 декабря 2020, 10:30

Искал реализацию для отправки писем через smtp, документации не нашел, пришлось по форумам бродить но нигде ничего дельного не было. Нашел тему на форуме http://www.cyberforum.ru/cpp-beginners/thread1020524... от 2012 года, вроде делал по аналогии, но так ничего и не добился.

Если коротко, вот код:

#define _CRT_SECURE_NO_WARNINGS 
#pragma comment (lib,"Ws2_32.lib") 
#include <windows.h> 
#include <winsock.h> 
#include <stdio.h> 
WSADATA ws; 
SOCKET s; 
struct sockaddr_in addr; 
hostent* d_addr; 
char text[1024]; 
int main() 
{ 
// инициализируем сокеты 
if (FAILED(WSAStartup(MAKEWORD(1, 1), &ws))) 
{ 
printf("Error in WSAStartup(...)\n"); 
return 1; 
} 
// создаем сокет 
s = socket(AF_INET, SOCK_STREAM, 0); 
if (s == INVALID_SOCKET) 
{ 
printf("Error in socket(...)\n"); 
return 1; 
} 
// получаем адрес сервера 
d_addr = gethostbyname("smtp.gmail.com"); 
if (d_addr == NULL) 
{ 
printf("Error in gethostbyname(...)\n"); 
return 1; 
}; 
// заполняем параметры адреса 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = *((unsigned long*)d_addr->h_addr); 
addr.sin_port = htons(465); 
// устанавливаем соединение 
if (SOCKET_ERROR == (connect(s, (sockaddr*)& addr, 
sizeof(addr)))) 
{ 
printf("Error in connect(...)\n"); 
return 1; 
} 
// ждем ответ от сервера 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// приветствуем сервер 
strcpy(text, "EHLO SSL\r\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// ждем подтверждение от сервера 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// начинаем отправлять конверт состоящий из полей 
// MAIL FROM: и RCPT TO: После каждого поля ждем 
// подтверждение 
strcpy(text, "AUTH LOGIN\r\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// сообщаем отправителя 
strcpy(text, "MAIL FROM: [email]sender@mail.ru[/email] "); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// ждем подтверждение от сервера 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// сообщаем получателя 
strcpy(text, "RCPT TO: [email]receiver@mtu-net.ru[/email] "); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// ждем подтверждение от сервера 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// подаем команду, что готовы начать передачу письма 
strcpy(text, "DATA\r\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// ждем подтверждение от сервера 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// передаем заголовок 
// от кого письмо 
strcpy(text, "FROM: [email]sender@mail.ru[/email] "); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// кому письмо 
strcpy(text, "TO: [email]receiver@mtu-net.ru[/email] "); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// тема письма 
strcpy(text, "SUBJECT: test\r\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// текст письма 
strcpy(text, "Hi!\nIt is a message for you\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// говорим, что закончили 
strcpy(text, "\r\n.\r\n"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
recv(s, text, sizeof(text), 0); 
printf("recv - %s", text); 
// прощаемся с сервером 
strcpy(text, "QUIT"); 
send(s, text, strlen(text), 0); 
printf("send - %s", text); 
// закрываем сокет 
closesocket(s); 
return 0; 
}

Он вроде как рабочий, через 25 порт и 587 соединение проходит. Но на моменте запроса авторизации сервер выдает ошибку, что не используется шифрование. Как я понял, это из-за того, что я использую порт, который не поддерживает шифрование. По документации такой порт - 465, но когда я его ставлю, сервер долго соединяется и в итоге ничего не отвечает, т.е пустыми сообщениями, хотя вроде соединение есть.

Вот такая картина при 465 порте:

recv - send - EHLO SSL recv - EHLO SSL send - AUTH LOGIN recv - AUTH LOGIN send

  • MAIL FROM: sender@mail.ru recv - MAIL FROM: sender@mail.ru send - RCPT TO: receiver@mtu-net.ru recv - RCPT TO: receiver@mtu-net.ru send
  • DATA recv - DATA send - FROM: sender@mail.ru send - TO: receiver@mtu-net.ru send - SUBJECT: test send - Hi! It is a message for you send - . recv - . send - QUIT

Вот такая при других:

recv - 220 smtp2o.mail.yandex.net ESMTP (Want to use Yandex.Mail for your domain? Visit http://pdd.yandex.ru) send - EHLO SSL recv - 250-smtp2o.mail.yandex.net 250-8BITMIME 250-PIPELINING 250-SIZE 42991616 250-STARTTLS 250-AUTH LOGIN PLAIN XOAUTH2 250-DSN 250 ENHANCEDSTATUSCODES send - AUTH LOGIN recv - 530 5.7.7 Email sending without SSL/TLS encryption is not allowed. Please see: http://help.yandex.ru/mail/mail-clients/ssl.xml

250 ENHANCEDSTATUSCODES send - MAIL FROM: sender@mail.ru recv - MAIL FROM: sender@mail.ru send - RCPT TO: receiver@mtu-net.ru recv - RCPT TO: receiver@mtu-net.ru send - DATA recv - DATA send - FROM: sender@mail.ru send - TO: receiver@mtu-net.ru send - SUBJECT: test send - Hi! It is a message for you send - . recv - . send - QUIT

Не знаю, что делать, буду признателен, если поможете.

Answer 1

Советую воспользоваться библиотекой libcurl: https://curl.haxx.se/libcurl/c/smtp-mail.html

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
/*
* For an SMTP example using the multi interface please see smtp-multi.c.
*/
/* The libcurl options want plain addresses, the viewable headers in the mail
* can very well get a full name as well.
*/
#define FROM_ADDR    "<sender@example.org>"
#define TO_ADDR      "<addressee@example.net>"
#define CC_ADDR      "<info@example.org>"
#define FROM_MAIL "Sender Person " FROM_ADDR
#define TO_MAIL   "A Receiver " TO_ADDR
#define CC_MAIL   "John CC Smith " CC_ADDR
static const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO_MAIL "\r\n",
"From: " FROM_MAIL "\r\n",
"Cc: " CC_MAIL "\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@"
"rfcpedant.example.org>\r\n",
"Subject: SMTP example message\r\n",
"\r\n", /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\r\n",
"\r\n",
"It could be a lot of lines, could be MIME encoded, whatever.\r\n",
"Check RFC5322.\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
    return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
    size_t len = strlen(data);
    memcpy(ptr, data, len);
    upload_ctx->lines_read++;
    return len;
}
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
    /* This is the URL for your mailserver */
    curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com");
    /* Note that this option isn't strictly required, omitting it will result
    * in libcurl sending the MAIL FROM command with empty sender data. All
    * autoresponses should have an empty reverse-path, and should be directed
    * to the address in the reverse-path which triggered them. Otherwise,
    * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more
    * details.
    */
    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM_ADDR);
    /* Add two recipients, in this particular case they correspond to the
    * To: and Cc: addressees in the header, but they could be any kind of
    * recipient. */
    recipients = curl_slist_append(recipients, TO_ADDR);
    recipients = curl_slist_append(recipients, CC_ADDR);
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
    /* We're using a callback function to specify the payload (the headers and
    * body of the message). You could just use the CURLOPT_READDATA option to
    * specify a FILE pointer to read from. */
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
    curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    /* Send the message */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));
    /* Free the list of recipients */
    curl_slist_free_all(recipients);
    /* curl won't send the QUIT command until you call cleanup, so you should
    * be able to re-use this connection for additional messages (setting
    * CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and calling
    * curl_easy_perform() again. It may not be a good idea to keep the
    * connection open for a very long time though (more than a few minutes
    * may result in the server timing out the connection), and you do want to
    * clean up in the end.
    */
    curl_easy_cleanup(curl);
}
return (int)res;
}
READ ALSO
База данных в C++ [закрыт]

База данных в C++ [закрыт]

Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме

124
Можно ли test_case размещать в циклах?

Можно ли test_case размещать в циклах?

Собственно, есть ли ограничения на размещение TEST_CASE, WHEN, THEN и дрв циклах?

108
Задача с ориентированным графом [дубликат]

Задача с ориентированным графом [дубликат]

Нужно найти и вывести все минимальные циклы в ориентированном графе, который задан матрицей смежности, подскажите оптимальное решения данной...

132
Если есть dll а проект не находит lib файл (QT)

Если есть dll а проект не находит lib файл (QT)

Не понимаю почему проекту нужно именно lib (ну и в целом в этой теме особо ничего не понимаю)

146