smtp_lib/smtp_lib.h
enum smtp_address_type
{
///Адрес отправителя.
SMTP_ADDRESS_FROM = 0,
///Адрес получателя.
SMTP_ADDRESS_TO = 1,
///Адрес получателя копии письма.
SMTP_ADDRESS_CC = 2,
/** Адрес получателя скрытой копии письма (BCC).
*Получатели не должны видеть ни один из адресов BCC в заголовке письма.
*Однако некоторые реализации SMTP-сервера могут копировать эту информацию
*в заголовок, поэтому она не всегда будет скрыта.
*Если адреса BCC строго не должны отображаться получателям, то в
*таком случае следует отправить по одному отдельному письму каждой
*стороне BCC. */
SMTP_ADDRESS_BCC = 3
};
/// Методы шифрования соединения (с шифрованием TLS / без шифрования). enum smtp_connection_security
{
/** STARTTLS. Сначала подключение без шифрования, затем установка
*зашифрованного соединения (команда STARTTLS). Обычно используется
*при подключении к почтовому серверу через порты 25 и 587. */
SMTP_SECURITY_STARTTLS = 0,
/** TLS. Подключение с шифрованием. Обычно используется при * подключении к почтовому серверу через порт 465. */
SMTP_SECURITY_TLS = 1,
/// Без шифрования. Не рекомендуется при нелокальном подключении.
SMTP_SECURITY_NONE = 2
};
/// Методы аутентификации учетной записи пользователя на сервере. enum smtp_authentication_method
{
/** Без аутентификации.
* Некоторые серверы поддерживают эту опцию при локальном подключении. */
SMTP_AUTH_NONE = 0,
///base64 аутентификация с использованием пользователя и пароля.
SMTP_AUTH_PLAIN = 1,
///base64 аутентификация, похожая на SMTP_AUTH_PLAIN.
SMTP_AUTH_LOGIN = 2
};
/// Специальные флаги контекста клиента SMTP. enum smtp_flag
{
/// Печать лога коммуникации клиента и сервера в поток stderr.
SMTP_DEBUG = 1,
/** Не проверять TLS сертификат.
*По умолчанию функция подтверждения TLS проверяет, истек ли
*срок действия сертификата или используется самозаверяющий сертификат.
*Любое из этих условий приведет к сбою соединения. Эта опция позволяет
*продолжить соединение, даже если эти проверки не пройдут. */
SMTP_NO_CERT_VERIFY = 2
};
/** Открывает соединение с SMTP-сервером и возвращает контекст.
* @param[in] |
server Имя сервера или IP адрес. |
|
* @param[in] |
port Порт сервера. |
|
* @param[in] |
connection_security См. @ref smtp_connection_security. |
|
* @param[in] |
flags См. @ref smtp_flag. |
|
* @param[in] |
cafile Путь к файлу сертификата или NULL (NULL: сертификат в пути по умолчанию). |
|
* @param[out] |
smtp Указатель на новый контекст |
SMTP. |
* |
По завершении вызывающая сторона должна освободить |
|
* |
этот контекст с помощью |
@ref smtp_close. |
* @return См. @ref smtp_status_code. */
enum smtp_status_code smtp_open(const char *const server, const char *const port, enum smtp_connection_security connection_security,
enum smtp_flag flags, const char *const cafile, struct smtp **smtp);
/**
*Аутентифицирует пользователя, используя один из методов, перечисленных в
*@ref smtp_authentication_method.
* @param[in] smtp |
Контекст клиента |
SMTP. |
||
* |
@param[in] |
auth_method См. @ref smtp_authentication_method. |
||
* |
@param[in] |
user |
Имя пользователя |
SMTP. |
*@param[in] pass Пароль пользователя SMTP.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_auth(struct smtp *const smtp,
enum smtp_authentication_method auth_method, const char *const user, const char *const pass);
/** Отправляет электронное письмо с адресами, вложениями и заголовками, * определенными в текущем контексте SMTP.
21
smtp_lib/smtp_lib.h
*Перед этим вызывающая сторона должна вызвать функцию @ref smtp_open.
*Заголовок "Date" будет автоматически создан в этой функции,
*если он еще не был установлен с помощью @ref smtp_header_add.
*Если приложение переопределяет заголовок "Content-Type" по умолчанию,
*то эта функция будет выводить @p body в виде необработанных данных сразу под заголовками
*электронной почты и не будет выводить вложения, добавленные с помощью функций
*smtp_attachment_add_*.
*Другими словами, приложение должно создавать свои собственные разделы
*MIME (если необходимо) при переопределении заголовка Content-Type.
*@param[in] smtp Контекст клиента SMTP.
*@param[in] body Тело письма (строка с '\0' в конце).
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_mail(struct smtp *const smtp, const char *const body);
/** Закрывает соединение SMTP и освобождает все ресурсы, удерживаемые контекстом SMTP.
*@param[in] smtp Контекст клиента SMTP.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_close(struct smtp *smtp);
/** Возвращает текущий код ошибки.
*@param[in] smtp Контекст клиента SMTP.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_status_code_get(const struct smtp *const smtp);
/** Очищает текущий код ошибки, |
установленный в контексте клиента SMTP. |
||
* |
@param[in,out] smtp Контекст |
клиента SMTP. |
|
* |
@return |
Код предыдущей ошибки перед очисткой. */ |
|
enum smtp_status_code smtp_status_code_clear(struct smtp *const smtp);
/** Устанавливает код ошибки в контексте клиента SMTP и возвращает его же.
*Это позволяет вызывающей стороне очистить код ошибки до @ref SMTP_STATUS_OK ,
*чтобы предыдущие ошибки перестали распространяться. Однако это будет работать
*правильно только для всех ошибок до границы @ref SMTP_STATUS__LAST .
*Не сбрасывает код ошибки >= @ref SMTP_STATUS__LAST .
*@param[in] smtp Контекст клиента SMTP.
*@param[in] new_status_code См. @ref smtp_status_code.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp,
enum smtp_status_code new_status_code);
/** Преобразует код состояния клиента SMTP в описательную строку.
*@param[in] status_code Код состояния.
*@return Строка, содержащая описание @p status_code .
*Вызывающая сторона не должна освобождать или изменять эту строку. */ const char *smtp_status_code_errstr(enum smtp_status_code status_code);
/** Добавляет заголовок "ключ-значение" в список заголовков в контексте SMTP.
*При добавлении заголовка с существующим ключом он будет вставлен вместо
*замены существующего заголовка.
*@param[in] smtp Контекст клиента SMTP.
*@param[in] key Имя ключа для нового заголовка.
* |
Оно должно |
состоять |
только из печатаемых |
|
* |
символов |
US-ASCII, кроме двоеточия. |
||
* @param[in] value Значение для |
нового |
заголовка. |
||
* |
Оно должно |
состоять |
только из печатаемых |
|
* |
символов |
US-ASCII, пробела или горизонтальной табуляции. |
||
* |
Может быть |
установлено в NULL. |
||
* @return См. @ref smtp_status_code. */
enum smtp_status_code smtp_header_add(struct smtp *const smtp, const char *const key, const char *const value);
/** Освобождает всю память, связанную с заголовками сообщений электронной почты. * @param[in] smtp Контекст клиента SMTP. */
void smtp_header_clear_all(struct smtp *const smtp);
/** Добавляет адрес назначения FROM, TO, CC или BCC в контекст SMTP.
*Некоторые SMTP-серверы могут отклонять более 100 получателей.
*@param[in] smtp Контекст клиента SMTP.
*@param[in] type См. @ref smtp_address_type.
*@param[in] email Электронный адрес стороны.
* |
Должен |
состоять только |
из печатаемых символов, |
* |
за |
исключением угловых скобок (<) и (>). |
|
* @param[in] name Название или описание стороны. |
|||
* |
Должно |
состоять только |
из печатаемых символов, |
* |
за |
исключением символов кавычек. |
|
* |
Может быть установлено |
в NULL. |
|
* @return См. @ref smtp_status_code. */
enum smtp_status_code smtp_address_add(struct smtp *const smtp, enum smtp_address_type type, const char *const email, const char *const name);
/** Освобождает всю память, относящуюся к списку адресов. * @param[in] smtp Контекст клиента SMTP. */
void smtp_address_clear_all(struct smtp *const smtp);
/** Добавляет вложение (файл) по пути.
22
smtp_lib/smtp_lib.h
*@param[in] smtp Контекст клиента SMTP.
*@param[in] name Имя файла вложения, отправляемого получателям.
*Должно состоять только из печатаемых символов ASCII,
* |
за исключением кавычек (') и ("). |
*@param[in] path Путь к файлу.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_attachment_add_path(struct smtp *const smtp, const char *const name, const char *const path);
/** Добавляет вложение с помощью файлового указателя.
*@param[in] smtp Контекст клиента SMTP.
*@param[in] name Имя файла вложения, отправляемого получателям.
*Должно состоять только из печатаемых символов ASCII,
* |
за исключением кавычек (') и ("). |
*@param[in] fp Уже открытый на чтение файловый указатель.
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_attachment_add_fp(struct smtp *const smtp, const char *const name, FILE *fp);
/** Добавляет в SMTP-контекст MIME-вложение с данными, полученными из памяти.
*@param[in] smtp Контекст клиента SMTP.
*@param[in] name Имя файла вложения, отправляемого получателям.
* |
Должно состоять только из |
печатаемых |
символов ASCII, |
* |
за исключением кавычек |
(') и ("). |
|
*@param[in] data Данные вложения, хранящиеся в памяти.
*@param[in] datasz Число байт в @p data , или -1 если есть '\0' в @p data .
*@return См. @ref smtp_status_code. */
enum smtp_status_code smtp_attachment_add_mem(struct smtp *const smtp, const char *const name, const void *const data, size_t datasz);
/** Удаляет все вложения из контекста SMTP-клиента. * @param[in] smtp Контекст клиента SMTP. */
void smtp_attachment_clear_all(struct smtp *const smtp);
#endif // SMTP_LIB_H
Таблица 3. Файл SMTP-библиотеки «smtp_lib/smtp_lib.c»
smtp_lib/smtp_lib.c
/** @file
*@brief SMTP client library
*@author Kovalenko Leonid && James Humphrey
*@version 1.00
*
*Клиентская библиотека SMTP.
*Позволяет пользователю отправлять электронные письма на сервер SMTP.
*/
#if defined(_WIN32) || defined(WIN32) /** Если ОС Windows. */
#define SMTP_IS_WINDOWS #endif // SMTP_IS_WINDOWS
#ifdef SMTP_IS_WINDOWS #include <winsock2.h> #include <ws2tcpip.h> #else // POSIX #include <netdb.h>
#include <netinet/in.h> #include <sys/select.h> #include <sys/socket.h> #include <unistd.h> #endif // SMTP_IS_WINDOWS
#include <errno.h> #include <limits.h> #include <signal.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <time.h>
#ifdef SMTP_OPENSSL #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/ssl.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #endif // SMTP_OPENSSL
#include "smtp_lib.h"
23
smtp_lib/smtp_lib.c
/// Коды SMTP, возвращаемые сервером и анализируемые клиентом. enum smtp_result_code
{
///Внутренняя ошибка (код ошибки клиента, сервером не устанавливается).
SMTP_INTERNAL_ERROR = -1,
///Возвращается, когда готово начать обработку следующего шага.
SMTP_READY = 220,
///Возвращается в ответ на QUIT.
SMTP_CLOSE = 221,
///Возвращается, если клиент успешно аутентифицируется.
SMTP_AUTH_SUCCESS = 235,
///Возвращается после успешного завершения некоторых команд.
SMTP_DONE = 250,
/** Возвращается для некоторых механизмов многострочной аутентификации, * где этот код указывает следующий этап на этапе аутентификации. */
SMTP_AUTH_CONTINUE = 334,
///Возвращается в ответ на команду DATA.
SMTP_BEGIN_MAIL = 354
};
/** Используется для анализа ответов от SMTP-сервера.
*Например, если сервер отправляет обратно "250-STARTTLS",
*тогда code будет установлен в 250, more в 1, text в STARTTLS. */ struct smtp_command
{
///Код результата (целое число).
enum smtp_result_code code;
/** Указывает, последуют ли другие серверные команды.
*Будет установлен в 1, если четвертый символ в строке ответа содержит '-',
*иначе будет установлен в 0. */
int more;
/// Текст, отображаемый после кода состояния. const char *text;
};
/** Коды возврата для интерфейса getdelim,
*который позволяет вызывающей стороне проверить,
*могут ли быть обработаны другие строки с разделителями. */ enum str_getdelim_retcode
{
///Ошибка при обработке getdelim.
STRING_GETDELIMFD_ERROR = -1,
///Обнаружил новую строку и может обработать больше строк при следующем вызове.
STRING_GETDELIMFD_NEXT = 0,
///Обнаружил новую строку и в настоящее время не может читать больше строк.
STRING_GETDELIMFD_DONE = 1
};
/** Структура данных для буфера чтения и синтаксического анализа строк. * Помогает получить и проанализировать строки ответа сервера. */
struct str_getdelimfd
{
///Буфер чтения, который может включать байты после разделителя. char *_buf;
///Количество выделенных байтов в буфере чтения.
size_t _bufsz;
///Количество фактически сохраненных байтов в буфере чтения. size_t _buf_len;
///Текущая строка, содержащая текст до разделителя.
char *line;
///Количество хранимых байтов в строке line. size_t line_len;
/** Указатель функции на пользовательскую функцию чтения для интерфейса smtp_str_getdelimfd. Этот прототип функции имеет семантику, аналогичную функции чтения.
Параметр @p gdfd позволяет настраиваемой функции извлекать информацию user_data
из структуры str_getdelimfd, которая может содержать указатель файла, соединение сокета...
*/
long (*getdelimfd_read)(struct str_getdelimfd *const gdfd, void *buf, size_t count);
///Пользовательские данные, которые отправляются в функцию-обработчик чтения.
void *user_data;
///Разделитель символов. int delim;
///Заполнение для выравнивания. char pad[4];
};
/// Данные адреса электронной почты. struct smtp_address
{
///Электронный адрес без специального форматирования. Например, mail@example.com. char *email;
///Имя пользователя электронного адреса.
char *name;
/// Определяет FROM, TO, CC или BCC. enum smtp_address_type type;
24
smtp_lib/smtp_lib.c
/// Заполнение для выравнивания. char pad[4];
};
/// Данные вложения, которые помещаются в раздел MIME. struct smtp_attachment
{
///Имя файла вложения. char *name;
///Данные файла в кодировке base64. char *b64_data;
};
/// Данные заголовка письма. struct smtp_header
{
///Имя заголовка (список заголовков сортируется по имени заголовка). char *key;
///Содержимое соответствующего ключа заголовка.
char *value;
};
/// Основная структура данных, которая содержит контекст клиента SMTP. struct smtp
{
///Побитовый список флагов, управляющих поведением SMTP-клиента. enum smtp_flag flags;
///Описатель сокета.
int sock;
///Буфер чтения и структура синтаксического анализа строк. struct str_getdelimfd gdfd;
///Список заголовков письма.
struct smtp_header *header_list;
///Количество заголовков в header_list. size_t num_headers;
///Список адресов электронной почты FROM, TO, CC и BCC. struct smtp_address *address_list;
///Количество адресов в address_list.
size_t num_address;
/// Список вложений для отправки.
struct smtp_attachment *attachment_list;
///Количество вложений в списке вложений. size_t num_attachment;
/** Тайм-аут в секундах для ожидания перед возвратом с ошибкой.
* Это относится как к записи, так и к чтению из сетевого сокета. */ long timeout_sec;
///Код состояния, указывающий на успех / неудачу.
enum smtp_status_code status_code;
/** Указывает, есть ли в этом контексте активное соединение TLS.
*0 означает, что TLS-соединение неактивно.
*1 означает, что TLS-соединение активно. */ int tls_on;
/** Путь к файлу сертификата при использовании самозаверяющего или ненадежного сертификата,
*не находящегося в хранилище сертификатов по умолчанию. */
const char *cafile; #ifdef SMTP_OPENSSL
///OpenSSL TLS объект. SSL *tls;
///OpenSSL TLS контекст. SSL_CTX *tls_ctx;
///OpenSSL TLS I/O абстракция. BIO *tls_bio;
#endif // SMTP_OPENSSL };
/** Проверяет, |
приведет ли |
сложение значений size_t к переносу. |
|||||
* @param[in] |
a |
Складывает это |
значение |
с |
@p |
b . |
|
* @param[in] |
b |
Складывает это |
значение |
с |
@p |
a . |
|
* @param[out] |
result |
Сохраняет результат сложения |
в этот буфер. |
||||
* |
|
Если |
установлено значение |
NULL, результат не записывается. |
|||
*@retval 1 Перенос совершен.
*@retval 0 Перенос не совершен. */
static int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
{
int wraps = (int)(SIZE_MAX - a < b);
if (result) |
|
|
*result = a + |
b; |
|
return wraps; |
|
|
} |
|
|
/** Проверяет, |
приведет ли вычитание значений size_t к заему. |
|
* @param[in] |
a |
Вычитает из этого значения @p b . |
* @param[in] |
b |
Вычитает это значение из @p a . |
* @param[out] |
result |
Сохраняет результат вычитания в этот буфер. |
* |
|
Если установлено значение NULL, результат не записывается. |
|
|
25 |