Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 5

.pdf
Скачиваний:
28
Добавлен:
23.12.2021
Размер:
2.2 Mб
Скачать

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