
7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 5
.pdf
pop3_lib/pop3_lib.c
{
size_t buf_len, buf_len_inc, buf_i, decode_len, decode_block_len; signed char decode_table[4];
unsigned char *b64_decode, outb[3]; *decode = NULL;
buf_len = strlen(buf); if (buf_len % 4 != 0) return SIZE_MAX;
if (pop3_si_add_size_t(buf_len, 1, &buf_len_inc) || (b64_decode = malloc(buf_len_inc)) == NULL) return SIZE_MAX;
decode_len = 0;
for (buf_i = 0; buf_i < buf_len; buf_i += 4)
{
decode_block_len = 0;
for (size_t i = 0; i < 4; i++)
{
if (buf[buf_i + i] == '=')
{
decode_table[i] = 0; continue;
}
decode_table[i] = g_base64_decode_table[(unsigned)buf[buf_i + i]]; if (decode_table[i] < 0)
{
free(b64_decode); return 0;
}
}
outb[0] = ((decode_table[0] << 2) & 0xFC) | ((decode_table[1] >> 4) & 0x03); outb[1] = ((decode_table[1] << 4) & 0xF0) | ((decode_table[2] >> 2) & 0x0F); outb[2] = ((decode_table[2] << 6) & 0xC0) | ((decode_table[3]) & 0x3F); b64_decode[decode_len + 0] = outb[0];
++decode_block_len;
if (buf[buf_i + 2] == '=') b64_decode[decode_len + 1] = '\0';
else
{
b64_decode[decode_len + 1] = outb[1]; ++decode_block_len;
}
if (buf[buf_i + 3] == '=') b64_decode[decode_len + 2] = '\0';
else
{
b64_decode[decode_len + 2] = outb[2]; ++decode_block_len;
}
decode_len += decode_block_len;
}
*decode = b64_decode; return decode_len;
}
/** Разбивает строку ответа сервера в структуру данных @ref pop3_command .
* @param[in] |
line |
Строка ответа сервера. |
* @param[out] |
cmd |
Структура, содержащая данные ответа сервера, разбитые на отдельные компоненты. |
* @return См. @ref pop3_status_code. */ |
||
static enum pop3_status_code pop3_parse_cmd_line(char *const line, struct pop3_command *cmd) |
||
{ |
|
|
int is_ok = strncmp(line, "+OK", 3) == 0;
int is_err = !is_ok && strncmp(line, "-ERR", 4) == 0; if (cmd->text)
free(cmd->text), cmd->text = NULL; if (!is_ok && !is_err)
{
cmd->code = POP3_STATUS_RECV; cmd->text = pop3_strdup(line, 0);
}
else if (is_err)
{
cmd->code = POP3_STATUS_PARAM; cmd->text = pop3_strdup(line, 0);
}
else
cmd->code = POP3_STATUS_OK; return cmd->code;
}
/** Печатает лог коммуникации клиента и сервера в stderr, только если установлен флаг отладки.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] prefix Печатает этот префикс перед текстом основной строки отладки.
*@param[in] str Отладочный текст для печати. */
static void pop3_puts_dbg(struct pop3 *const pop3, const char *const prefix, const char *const str)
{
char *sdup;
21

pop3_lib/pop3_lib.c
if (pop3->flags & POP3_DEBUG)
{
if ((sdup = pop3_strdup(str, 0)) == NULL) return;
// Заменяет возврат каретки и символ новой строки на пробел для печати в stderr. for (size_t i = 0; sdup[i]; ++i)
if (sdup[i] == '\r' || sdup[i] == '\n') sdup[i] = ' ';
fprintf(stderr, "[pop3 %s]: %s\n", prefix, sdup); free(sdup);
}
}
/** Читает строку ответа сервера.
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref str_getdelim_retcode . */
static enum str_getdelim_retcode pop3_getline(struct pop3 *const pop3)
{
enum str_getdelim_retcode rc; errno = 0;
rc = pop3_str_getdelimfd(&pop3->gdfd); if (errno == ENOMEM)
{
pop3_status_code_set(pop3, POP3_STATUS_NOMEM); return rc;
}
if (pop3->gdfd.line_len > 0)
{
pop3->gdfd.line[pop3->gdfd.line_len - 1] = '\0'; pop3_puts_dbg(pop3, "Server", pop3->gdfd.line);
}
return rc;
}
/** Читает строку ответа сервера и возвращает из нее код состояния.
*В случае ошибки обновляет строку pop3->error_description.
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref pop3_status_code. */
static enum pop3_status_code pop3_read_and_parse_code(struct pop3 *const pop3)
{
struct pop3_command cmd;
enum str_getdelim_retcode rc = pop3_getline(pop3); memset((void *)&cmd, 0, sizeof(struct pop3_command)); if (rc == STRING_GETDELIMFD_ERROR)
return pop3->status_code = POP3_STATUS_RECV; pop3_parse_cmd_line(pop3->gdfd.line, &cmd);
if (pop3->error_description)
{
free(pop3->error_description); pop3->error_description = NULL;
}
if (cmd.text)
pop3->error_description = cmd.text; return pop3->status_code = cmd.code;
}
/** Считывает все строки ответа сервера до "\r\n.\r\n".
* |
В случае ошибки обновляет строку pop3->error_description. |
||
* |
@param[in] |
pop3 |
Контекст клиента POP3. |
*@param[in,out] out_response Мультистрочный ответ сервера.
*@return См. @ref pop3_status_code. */
static enum pop3_status_code pop3_read_all(struct pop3 *const pop3, char **out_response)
{
size_t response_size = 0, response_size_prev = 0;
char *response = malloc(response_size), *new_response = NULL; if (response == NULL)
return pop3->status_code = POP3_STATUS_NOMEM;
while (pop3_getline(pop3) != (enum str_getdelim_retcode)(-1) && pop3->gdfd.line_len > 0)
{
// Каждый раз перераспределяем больше памяти для хранения данных
if (pop3_si_add_size_t(response_size, pop3->gdfd.line_len - 1, &response_size))
{
free(response);
return pop3->status_code = POP3_STATUS_NOMEM;
}
if (pop3_si_add_size_t(response_size, 1, NULL) ||
(new_response = realloc(response, response_size + 1)) == NULL)
{
free(response);
return pop3->status_code = POP3_STATUS_NOMEM;
}
response = new_response; // Добавляем новую строку
strncpy(response + response_size_prev, pop3->gdfd.line, pop3->gdfd.line_len - 1); response[response_size] = '\0';
22

pop3_lib/pop3_lib.c
free(pop3->gdfd.line); pop3->gdfd.line = NULL; pop3->gdfd.line_len = 0; if (response_size >= 5 &&
strstr(response + (response_size_prev - 2 * (response_size_prev >= 2)), "\r\n.\r\n")) break;
response_size_prev = response_size;
}
*out_response = response;
if (strncmp(response, "+OK", 3) == 0)
return pop3->status_code = POP3_STATUS_OK;
else
{
if (pop3->error_description)
free(pop3->error_description), pop3->error_description = NULL; pop3->error_description = response;
if (strncmp(response, "-ERR", 4) == 0)
return pop3->status_code = POP3_STATUS_PARAM;
}
return pop3->status_code = POP3_STATUS_SERVER_RESPONSE;
}
/** Отправляет данные на POP3-сервер.
*Записывает буфер длиной @p len либо в незашифрованный сокет TCP, либо в зашифрованный сокет TLS,
*в зависимости от текущего базового режима сокета.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] buf Данные для отправки на POP3-сервер.
*@param[in] len Количество байтов в @p buf .
*@return См. @ref pop3_status_code. */
static enum pop3_status_code pop3_write(struct pop3 *const pop3, const char *const buf, size_t len)
{
size_t bytes_to_send = len; long bytes_sent;
const char *buf_offset = buf; pop3_puts_dbg(pop3, "Client", buf); while (bytes_to_send)
{
if (bytes_to_send > INT_MAX)
return pop3_status_code_set(pop3, POP3_STATUS_SEND); if (pop3->tls_on)
{
#ifdef POP3_OPENSSL
// bytes_to_send <= INT_MAX.
int ssl_bytes_to_send = (int)bytes_to_send;
bytes_sent = SSL_write(pop3->tls, buf_offset, ssl_bytes_to_send); if (bytes_sent <= 0)
return pop3_status_code_set(pop3, POP3_STATUS_SEND); #else // !(POP3_OPENSSL)
bytes_sent = 0; #endif // POP3_OPENSSL
}
else
{
bytes_sent = send(pop3->sock, buf_offset, bytes_to_send, 0); if (bytes_sent < 0)
return pop3_status_code_set(pop3, POP3_STATUS_SEND);
}
bytes_to_send -= (size_t)bytes_sent; buf_offset += bytes_sent;
}
return pop3->status_code;
}
/** Отправляет на POP3-сервер строку с '\0'.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] s Строка с '\0' для отправки на POP3-сервер.
*@return См. @ref pop3_status_code и @ref pop3_write . */
static enum pop3_status_code pop3_puts(struct pop3 *const pop3, const char *const s)
{
return pop3_write(pop3, s, strlen(s));
}
#ifndef POP3_IS_WINDOWS
/// Структура, содержащая информацию об адресе поставщика услуг. struct addrinfo
{
///Флаги ввода. int ai_flags;
///Семейство протоколов для сокета. int ai_family;
///Тип сокета.
int ai_socktype;
///Протокол для сокета. int ai_protocol;
///Длина адреса сокета.
23

pop3_lib/pop3_lib.c
socklen_t ai_addrlen;
/// Адрес сокета.
struct sockaddr *ai_addr;
///Каноническое название места обслуживания. char *ai_canonname;
///Указатель на следующую структуру.
struct addrinfo *ai_next;
};
/** Переводит имя местоположения службы и / или имя службы в набор адресов сокетов. * Эта функция является точкой возможной отмены и поэтому не отмечена __THROW. */
extern int getaddrinfo(const char *__restrict __name, const char *__restrict __service,
const struct addrinfo *__restrict __req, struct addrinfo **__restrict __pai);
/// Освобождает __ai, которую функция getaddrinfo динамически выделяет extern void freeaddrinfo(struct addrinfo *__ai) __THROW;
#endif
/** Подключается к серверу через стандартный сокет TCP.
*Эта функция переводит имя сервера в его IP-адрес, а затем подключается к этому IP,
*используя обычное TCP-соединение.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] server Имя или IP-адрес почтового сервера.
*@param[in] port Номер порта почтового сервера.
* @retval |
0 |
Удалось подключиться к серверу. |
* @retval |
-1 |
Не удалось подключиться к серверу. */ |
static int |
pop3_connect(struct pop3 *const pop3, const char *const server, const char *const port) |
|
{ |
|
|
struct |
addrinfo hints, *res0, *res; |
//Windows требует инициализации библиотеки сокетов перед вызовом каких-либо функций сокетов.
#ifdef POP3_IS_WINDOWS
//Структура данных глобального сетевого сокета Windows.
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) return pop3->status_code = POP3_STATUS_PARAM;
#endif // POP3_IS_WINDOWS memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(server, port, &hints, &res0) != 0) return -1;
for (res = res0; res; res = res->ai_next)
{
pop3->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (pop3->sock < 0)
continue;
if (connect(pop3->sock, res->ai_addr, res->ai_addrlen) < 0)
{
#ifdef POP3_IS_WINDOWS closesocket(pop3->sock);
#else // POSIX close(pop3->sock);
#endif // POP3_IS_WINDOWS pop3->sock = -1;
}
else
break;
}
freeaddrinfo(res0);
return (pop3->sock < 0) ? -1 : POP3_STATUS_OK;
}
#ifdef POP3_OPENSSL
/** Инициализирует библиотеку TLS и устанавливает рукопожатие TLS с сервером
*через существующее соединение сокета.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] server Имя сервера или IP-адрес.
* @retval |
0 |
Удалось установить TLS-соединение с |
сервером. |
* @retval |
-1 |
Не удалось установить TLS-соединение с сервером. */ |
|
static int |
pop3_tls_init(struct pop3 *const pop3, |
const char *const server) |
|
{ |
|
|
|
X509 *X509_cert_peer;
//Не нужно проверять возвращаемое значение, поскольку оно всегда возвращает 1.
SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms();
if ((pop3->tls_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) return -1;
//Запрещаем использование SSLv2, SSLv3, и TLSv1.0.
SSL_CTX_set_options(pop3->tls_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); SSL_CTX_set_mode(pop3->tls_ctx, SSL_MODE_AUTO_RETRY);
if ((pop3->flags & POP3_NO_CERT_VERIFY) == 0)
24

pop3_lib/pop3_lib.c
SSL_CTX_set_verify(pop3->tls_ctx, SSL_VERIFY_PEER, NULL);
/* Задает путь к предоставленному пользователем файлу CA
или использует пути к сертификатам по умолчанию, если они не указаны. */ if (pop3->cafile)
{
if (SSL_CTX_load_verify_locations(pop3->tls_ctx, pop3->cafile, NULL) != 1)
{
SSL_CTX_free(pop3->tls_ctx); return -1;
}
}
else
{
X509_STORE_set_default_paths(SSL_CTX_get_cert_store(pop3->tls_ctx)); if (ERR_peek_error() != 0)
{
SSL_CTX_free(pop3->tls_ctx); return -1;
}
}
if ((pop3->tls = SSL_new(pop3->tls_ctx)) == NULL)
{
SSL_CTX_free(pop3->tls_ctx); return -1;
}
if ((pop3->tls_bio = BIO_new_socket(pop3->sock, 0)) == NULL)
{
SSL_CTX_free(pop3->tls_ctx); SSL_free(pop3->tls);
return -1;
}
SSL_set_bio(pop3->tls, pop3->tls_bio, pop3->tls_bio); SSL_set_connect_state(pop3->tls);
if (SSL_connect(pop3->tls) != 1)
{
SSL_CTX_free(pop3->tls_ctx); SSL_free(pop3->tls);
return -1;
}
if (SSL_do_handshake(pop3->tls) != 1)
{
SSL_CTX_free(pop3->tls_ctx); SSL_free(pop3->tls);
return -1;
}
if ((pop3->flags & POP3_NO_CERT_VERIFY) == 0)
{
if ((X509_cert_peer = SSL_get_peer_certificate(pop3->tls)) == NULL)
{
SSL_CTX_free(pop3->tls_ctx); SSL_free(pop3->tls);
return -1;
}
if (X509_check_host(X509_cert_peer, server, 0, 0, NULL) != 1)
{
SSL_CTX_free(pop3->tls_ctx); SSL_free(pop3->tls);
return -1;
}
X509_free(X509_cert_peer);
}
pop3->tls_on = 1; return 0;
}
#endif // POP3_OPENSSL
/** Устанавливает тайм-аут для следующей операции чтения сокета.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] seconds Тайм-аут в секундах. */
static void pop3_set_read_timeout(struct pop3 *const pop3, long seconds)
{
pop3->timeout_sec = seconds;
}
/** Выполняет рукопожатие с сервером POP3, которое включает в себя
*необязательную настройку TLS.
*На этом этапе клиент уже подключился к POP3-серверу через свое сокетное соединение.
*В этой функции клиент:
*1. При желании преобразует соединение в TLS ( @ref POP3_SECURITY_TLS ).
*2. Читает начальное приветствие сервера.
*3. При необходимости запускает STARTTLS ( @ref POP3_SECURITY_STARTTLS ).
* |
@param[in] |
pop3 |
Контекст клиента POP3. |
* |
@param[in] |
server |
Имя сервера или IP-адрес. |
*@param[in] connection_security См. @ref pop3_connection_security.
*@return См. @ref pop3_status_code. */
25

pop3_lib/pop3_lib.c
static enum pop3_status_code
pop3_initiate_handshake(struct pop3 *const pop3, const char *const server, enum pop3_connection_security connection_security)
{
// (1)
#ifdef POP3_OPENSSL
if (connection_security == POP3_SECURITY_TLS && pop3_tls_init(pop3, server) < 0) return pop3_status_code_set(pop3, POP3_STATUS_HANDSHAKE);
#endif // POP3_OPENSSL
//(2)
//Получает начальное сообщение - таймаут 10 секунд pop3_set_read_timeout(pop3, 10);
if (pop3_getline(pop3) == STRING_GETDELIMFD_ERROR)
return pop3->status_code; #ifdef POP3_OPENSSL
// (3)
if (connection_security == POP3_SECURITY_STARTTLS)
{
if (pop3_puts(pop3, "STLS\r\n") != POP3_STATUS_OK) return pop3->status_code;
if (pop3_read_and_parse_code(pop3) != POP3_STATUS_OK)
return pop3_status_code_set(pop3, POP3_STATUS_HANDSHAKE); if (pop3_tls_init(pop3, server) < 0)
return pop3_status_code_set(pop3, POP3_STATUS_HANDSHAKE);
}
#endif // POP3_OPENSSL return pop3->status_code;
}
/** Функция сравнения для qsort, которая сортирует заголовки в алфавитном порядке по ключу.
*@param[in] v1 Первый @ref pop3_header для сравнения.
*@param[in] v2 Второй @ref pop3_header для сравнения.
* @retval |
0 |
Если ключи совпадают. |
* @retval |
!0 |
Если ключи не совпадают. */ |
static int |
pop3_header_cmp(const void *v1, const void *v2) |
|
{ |
|
|
const struct pop3_header *header1, *header2; header1 = v1, header2 = v2;
return strcmp(header1->key, header2->key);
}
/** Функция декодирует и заменяет MIME-B UTF-8 последовательности в строке @p str .
*@param[in] str Исходная строка.
*@retval char* Декодирование успешно.
*@retval NULL Ошибка выделения памяти. */
static char *pop3_decode_if_needed(const char *str)
{
size_t new_str_size = 0, new_str_reserved = 1, decoded_len = 0; const char *pos = str;
unsigned char *decoded = NULL;
char *new_str = NULL, *new_str_n = NULL, *substr, *next_pos; if (!str)
return pop3_strdup("", 0);
for (size_t i = 0, n = strlen(str); i < n;)
{
if (strncasecmp(str + i, "=?UTF-8?B?", 10) == 0)
{
pos = str + i + 10;
next_pos = strstr(pos, "?=");
substr = pop3_strdup(pos, next_pos - pos); decoded_len = pop3_base64_decode(substr, &decoded); if (decoded_len == (size_t)-1)
{
free(decoded); return NULL;
}
if (decoded_len >= new_str_reserved - new_str_size - 1)
{
new_str_reserved = new_str_size + decoded_len + 1; new_str_n = realloc(new_str, new_str_reserved);
if (new_str_n == NULL)
{
free(decoded);
free(substr); free(new_str); return NULL;
}
new_str = new_str_n;
}
memcpy(new_str + new_str_size, (char *)decoded, decoded_len); new_str_size += decoded_len;
i += 10 + 2 + strlen(substr); free(decoded);
free(substr);
}
26

pop3_lib/pop3_lib.c
else
{
if (new_str_size + 1 >= new_str_reserved)
{
new_str_reserved *= 2;
new_str_n = realloc(new_str, new_str_reserved); if (new_str_n == NULL)
{
free(new_str); return NULL;
}
new_str = new_str_n;
}
new_str[new_str_size++] = str[i++]; new_str[new_str_size] = '\0';
}
}
return new_str;
}
/// Размер буфера для отправки номера сообщения
#define POP3_MESSAGE_NUM_BUF_SZ 100
/** Функция парсит тело сообщения и его вложения.
* @param[in] multipart_message Исходное письмо без заголовков.
* @param[out] |
body |
Тело письма. |
|
* |
@param[out] |
num_attachment |
Число вложений. |
* |
@param[out] |
attachment_list |
Список вложений. |
* @return См. @ref pop3_status_code. */
static enum pop3_status_code parse_body_and_attachments(char *multipart_message, char **body, size_t *num_attachment,
struct pop3_attachment **attachment_list)
{
int content_type = 0;
size_t decoded_len = 0, body_size = 0, part = 0, c = 0, k, n = 0, is_attachment, must_decoded; unsigned char *decoded = NULL;
char *encoded = NULL, *temp = NULL;
if (!multipart_message || !body || !attachment_list || !num_attachment) return POP3_STATUS_PARAM;
n = strlen(multipart_message);
for (size_t i = 0, start_pos = 0, finish_pos = 0; i < n; ++i)
{
if (strncmp(&multipart_message[i], "Content-Type:", 13) == 0)
{
content_type = 1;
while (i > 0 && strncmp(&multipart_message[i], "\n--", 3) != 0) --i;
is_attachment = 0; must_decoded = 0;
while (i < n && strncmp(&multipart_message[i], "\r\n\r\n", 4) != 0)
{
++i;
if (strncmp(&multipart_message[i], "Content-Disposition: attachment", 31) == 0) is_attachment = 1;
else if (strncmp(&multipart_message[i], "Content-Transfer-Encoding: base64", 33) == 0)
must_decoded = 1;
else if (strncmp(&multipart_message[i], "filename=\"", 10) == 0)
{
c = start_pos = i + 10; if (temp)
free(temp), temp = NULL;
while (c < n && multipart_message[c] != '\"' && multipart_message[c] != '\r') ++c;
if (c == n) break;
temp = pop3_strdup(&multipart_message[start_pos], c - start_pos);
}
}
if (c == n) break;
i += 4; start_pos = i;
while (i < n && strncmp(&multipart_message[i], "\n--", 3) != 0) ++i;
finish_pos = i; if (must_decoded)
{
encoded = malloc(finish_pos - start_pos + 2); if (encoded == NULL)
return POP3_STATUS_NOMEM; k = 0;
while (start_pos <= finish_pos)
{
char ch = multipart_message[start_pos];
27

pop3_lib/pop3_lib.c
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
('0' <= ch && ch <= '9') || (ch == '+' || ch == '/' || ch == '=')) encoded[k++] = ch;
++start_pos;
}
encoded[k] = '\0';
decoded_len = pop3_base64_decode(encoded, &decoded); free(encoded);
if (decoded_len == (size_t)-1) return POP3_STATUS_PARAM;
if (is_attachment)
{
struct pop3_attachment *temp_list; ++(*num_attachment);
temp_list =
realloc(*attachment_list, *num_attachment * sizeof(struct pop3_attachment)); if (temp_list == NULL)
{
--(*num_attachment); return POP3_STATUS_NOMEM;
}
*attachment_list = temp_list; (*attachment_list)[(*num_attachment) - 1].data = (char *)decoded; (*attachment_list)[(*num_attachment) - 1].name = temp; (*attachment_list)[(*num_attachment) - 1].data_len = decoded_len; temp = NULL;
}
else
{
char *temp_body;
temp_body = realloc(*body, body_size + decoded_len + 1); if (temp_body == NULL)
return POP3_STATUS_NOMEM; *body = temp_body;
strncpy((*body) + body_size, (char *)decoded, decoded_len); body_size += decoded_len;
(*body)[body_size] = '\0'; free(decoded);
decoded = NULL;
}
}
else
{
if (is_attachment)
{
struct pop3_attachment *temp_list;
temp = pop3_strdup(&multipart_message[start_pos], finish_pos - start_pos); if (temp == NULL)
return POP3_STATUS_NOMEM; ++(*num_attachment); temp_list =
realloc(*attachment_list, *num_attachment * sizeof(struct pop3_attachment)); if (temp_list == NULL)
{
free(temp); --(*num_attachment); return POP3_STATUS_NOMEM;
}
*attachment_list = temp_list; (*attachment_list)[(*num_attachment) - 1].data = temp; (*attachment_list)[(*num_attachment) - 1].name =
malloc(POP3_MESSAGE_NUM_BUF_SZ); (*attachment_list)[(*num_attachment) - 1].data_len = strlen(temp); sprintf((*attachment_list)[(*num_attachment) - 1].name, "%d-attachment",
part + 1);
++part;
}
else
{
char *temp_body;
k = finish_pos - start_pos + 1;
temp_body = realloc(*body, body_size + k + 1); if (temp_body == NULL)
return POP3_STATUS_NOMEM; *body = temp_body;
strncpy((*body) + body_size, &multipart_message[start_pos], k); body_size += k;
(*body)[body_size] = '\0';
}
}
}
}
if (content_type == 0 && *body == NULL)
*body = pop3_strdup(multipart_message, strlen(multipart_message)); return POP3_STATUS_OK;
28

pop3_lib/pop3_lib.c
}
/** Специальное значение флага для контекста POP3, используемое для определения того, * не удалось ли при первоначальном выделении памяти создать контекст. */
#define POP3_FLAG_INVALID_MEMORY (enum pop3_flag)(0xFFFFFFFF)
/** Эта структура ошибок используется для случая единственной ошибки, когда мы не можем изначально
*выделить память. Это упрощает распространение любых кодов ошибок при вызове других функций
*заголовка, поскольку вызывающая сторона всегда будет получать верную структуру POP3. */ static struct pop3 g_pop3_error = {
///flags
POP3_FLAG_INVALID_MEMORY,
///sock
0,
///gdfd {/// _buf
NULL,
///_bufsz
0,
///_buf_len
0,
///line NULL,
///line_len
0,
///getdelimfd_read NULL,
///user_data
NULL,
///delim
0,
///pad {0}},
///messages NULL,
///messages_count
0,
///timeout_sec
0,
///status_code POP3_STATUS_NOMEM,
///tls_on
0,
///cafile NULL,
///error_description
NULL
#ifdef POP3_OPENSSL
,
///tls NULL,
///tls_ctx NULL,
///tls_bio
NULL
#endif // POP3_OPENSSL };
enum pop3_status_code pop3_open(const char *const server, const char *const port,
const enum pop3_connection_security connection_security, const enum pop3_flag flags, const char *const cafile, struct pop3 **const pop3)
{
struct pop3 *snew;
if ((snew = malloc(sizeof(**pop3))) == NULL)
{
*pop3 = &g_pop3_error;
return pop3_status_code_get(*pop3);
}
memset(snew, 0, sizeof(struct pop3)); *pop3 = snew;
snew->flags = flags; snew->sock = -1; snew->gdfd.delim = '\n';
snew->gdfd.getdelimfd_read = pop3_str_getdelimfd_read; snew->gdfd.user_data = snew;
snew->cafile = cafile; #ifndef POP3_IS_WINDOWS
signal(SIGPIPE, SIG_IGN); #endif // !(POP3_IS_WINDOWS)
if (pop3_connect(snew, server, port) < 0)
return pop3_status_code_set(*pop3, POP3_STATUS_CONNECT);
if (pop3_initiate_handshake(snew, server, connection_security) != POP3_STATUS_OK) return pop3_status_code_set(*pop3, POP3_STATUS_HANDSHAKE);
return snew->status_code;
29

pop3_lib/pop3_lib.c
}
enum pop3_status_code pop3_auth(struct pop3 *const pop3, const char *const user, const char *const pass)
{
char user_prefix[6] = "USER ", pass_prefix[6] = "PASS ", end[3] = "\r\n", *user_q = NULL, *pass_q = NULL, *concat = NULL;
size_t user_q_len, pass_q_len;
if (pop3->status_code != POP3_STATUS_OK) return pop3->status_code;
if (pop3_si_add_size_t(strlen(user_prefix) + strlen(end) + 1, strlen(user), &user_q_len) || pop3_si_add_size_t(strlen(pass_prefix) + strlen(end) + 1, strlen(pass), &pass_q_len)) return pop3->status_code = POP3_STATUS_NOMEM;
if ((user_q = malloc(user_q_len)) == NULL)
return pop3->status_code = POP3_STATUS_NOMEM; if ((pass_q = malloc(pass_q_len)) == NULL)
{
free(user_q);
return pop3->status_code = POP3_STATUS_NOMEM;
}
memset(user_q, 0, user_q_len); memset(pass_q, 0, pass_q_len);
concat = pop3_stpcpy(user_q, user_prefix); concat = pop3_stpcpy(concat, user);
concat = pop3_stpcpy(concat, end); pop3_puts(pop3, user_q); free(user_q);
if (pop3->status_code != POP3_STATUS_OK)
{
free(pass_q);
return pop3->status_code;
}
if (pop3_read_and_parse_code(pop3) != POP3_STATUS_OK)
{
free(pass_q);
return pop3->status_code = POP3_STATUS_AUTH;
}
concat = pop3_stpcpy(pass_q, pass_prefix); concat = pop3_stpcpy(concat, pass);
concat = pop3_stpcpy(concat, end); pop3_puts(pop3, pass_q); free(pass_q);
if (pop3->status_code != POP3_STATUS_OK) return pop3->status_code;
if (pop3_read_and_parse_code(pop3) != POP3_STATUS_OK) return pop3->status_code = POP3_STATUS_AUTH;
return pop3->status_code;
}
enum pop3_status_code pop3_stat(struct pop3 *const pop3, size_t *const messages_count, size_t *const messages_size)
{
size_t fictive; pop3_puts(pop3, "STAT\r\n");
if (pop3->status_code != POP3_STATUS_OK) return pop3->status_code;
if (pop3_read_and_parse_code(pop3) != POP3_STATUS_OK) return pop3->status_code;
if (messages_size)
sscanf(pop3->gdfd.line + 3 /* length("+OK") */, " %zu %zu ", messages_count ? messages_count : &fictive, messages_size);
else if (messages_count)
sscanf(pop3->gdfd.line + 3 /* length("+OK") */, " %zu ", messages_count); return pop3->status_code;
}
enum pop3_status_code pop3_noop(struct pop3 *const pop3)
{
pop3_puts(pop3, "NOOP\r\n");
if (pop3->status_code != POP3_STATUS_OK) return pop3->status_code;
return pop3->status_code = pop3_read_and_parse_code(pop3);
}
enum pop3_status_code pop3_list(struct pop3 *const pop3, const size_t messages_count_arg, struct pop3_message **const messages)
{
size_t messages_count = 0; if (messages == NULL)
return pop3->status_code = POP3_STATUS_PARAM; if (messages_count_arg == 0)
{
int status_code = pop3_stat(pop3, &messages_count, NULL); if (status_code != POP3_STATUS_OK)
return pop3->status_code = status_code;
30