7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 5
.pdf
Рисунок 16. Одна из функций в документации
Рисунок 17. Одно из перечислений в документации
11
Рисунок 18. Макросы в документации
ЗАКЛЮЧЕНИЕ
В результате выполнения лабораторной работы мы разработали приложение, позволяющее принимать электронные письма с вложенными файлами, используя протокол POP3.
12
ПРИЛОЖЕНИЯ
Далее перечислены приложения в следующем порядке:
1.Файл POP3-библиотеки «pop3_lib/pop3_lib.h» (табл. 2).
2.Файл POP3-библиотеки «pop3_lib/pop3_lib.c» (табл. 3).
3.Сборочный файл POP3-библиотеки «pop3_lib/WinMakefile» /
«pop3_lib/LinuxMakefile» (табл. 4).
4.Файл POP3-клиента «pop3_client.c» (табл. 5).
5.Общий сборочный файл «WinMakefile» / «LinuxMakefile» (табл. 6).
Таблица 2. Файл POP3-библиотеки «pop3_lib/pop3_lib.h»
pop3_lib/pop3_lib.h
/** @file
*@brief POP3 client library
*@author Kovalenko Leonid
*@version 1.00
*
*Клиентская библиотека POP3.
*Позволяет пользователю получать электронные письма с сервера POP3.
*/
#ifndef POP3_LIB_H #define POP3_LIB_H
#include <stddef.h> #include <stdio.h> #include <sys/types.h>
#ifndef SIZE_MAX
///Максимальное значение числа типа size_t.
#define SIZE_MAX ((size_t)(-1)) #endif // SIZE_MAX
///Основная структура данных, которая содержит контекст клиента POP3.
///Определение находится в C-файле.
struct pop3;
/// Структура данных, содержащая письмо. struct pop3_message
{
///Номер письма. size_t n;
///Размер письма. size_t size;
///Заголовки письма.
struct pop3_header *header_list;
///Количество заголовков письма. size_t num_headers;
///Отправитель письма.
char *from;
///Реальный отправитель письма. char *envelope_from;
///Получатели письма.
char *to;
///Получатели копий письма. char *cc;
///Дата письма.
char *date;
///Заголовок письма. char *subject;
///Тело письма.
char *body;
/// Вложения письма.
struct pop3_attachment *attachment_list;
/// Количество вложений в списке вложений. size_t num_attachment;
};
/// Данные вложения, которые помещаются в раздел MIME. struct pop3_attachment
{
13
pop3_lib/pop3_lib.h
///Имя файла вложения. char *name;
///Данные файла.
char *data;
/// Длина данных файла size_t data_len;
};
/// Коды состояния вызова любой из функций библиотеки POP3. enum pop3_status_code
{
///Операция успешно завершена.
POP3_STATUS_OK = 0,
///Не удалось выделить память.
POP3_STATUS_NOMEM = 1,
///Не удалось подключиться к почтовому серверу.
POP3_STATUS_CONNECT = 2,
///Не удалось установить соединение или согласовать TLS-соединение с сервером.
POP3_STATUS_HANDSHAKE = 3,
///Не удалось пройти аутентификацию с указанными учетными данными.
POP3_STATUS_AUTH = 4,
///Не удалось отправить байты на сервер.
POP3_STATUS_SEND = 5,
///Не удалось получить байты от сервера.
POP3_STATUS_RECV = 6,
///Не удалось правильно закрыть соединение.
POP3_STATUS_CLOSE = 7,
///POP3-сервер отправил неожиданный код состояния.
POP3_STATUS_SERVER_RESPONSE = 8,
///Неверный параметр.
POP3_STATUS_PARAM = 9,
/** Указывает последний код состояния в перечислении.
*Используется для проверки границ: status_code >= POP3_STATUS__LAST.
*Не является кодом статуса. */
POP3_STATUS__LAST
};
/// Методы шифрования соединения (с шифрованием TLS / без шифрования). enum pop3_connection_security
{
/** STARTTLS. Сначала подключение без шифрования, затем установка
*зашифрованного соединения (команда STARTTLS). Обычно используется
*при подключении к почтовому серверу через порт 110. */
POP3_SECURITY_STARTTLS = 0,
/** TLS. Подключение с шифрованием. Обычно используется при * подключении к почтовому серверу через порт 995. */
POP3_SECURITY_TLS = 1,
/// Без шифрования. Не рекомендуется при нелокальном подключении.
POP3_SECURITY_NONE = 2
};
/// Специальные флаги контекста клиента POP3. enum pop3_flag
{
/// Печать лога коммуникации клиента и сервера в поток stderr.
POP3_DEBUG = 1,
/** Не проверять TLS сертификат.
*По умолчанию функция подтверждения TLS проверяет, истек ли
*срок действия сертификата или используется самозаверяющий сертификат.
*Любое из этих условий приведет к сбою соединения. Эта опция позволяет
*продолжить соединение, даже если эти проверки не пройдут. */
POP3_NO_CERT_VERIFY = 2
};
/** Открывает соединение с POP3-сервером и возвращает контекст.
* @param[in] |
server Имя сервера или |
IP адрес. |
* @param[in] |
port Порт сервера. |
|
* @param[in] |
connection_security См. @ref pop3_connection_security. |
|
* @param[in] |
flags См. @ref pop3_flag. |
|
* @param[in] |
cafile Путь к файлу сертификата или NULL (NULL: сертификат в пути по умолчанию). |
|
* @param[out] |
pop3 Указатель на новый контекст POP3. |
|
* |
По завершении вызывающая сторона должна освободить |
|
* |
этот контекст |
с помощью @ref pop3_close. |
* @return См. @ref pop3_status_code. */ |
||
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);
/** Аутентифицирует пользователя.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] user Имя пользователя POP3.
*@param[in] pass Пароль пользователя POP3.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_auth(struct pop3 *const pop3, const char *const user,
14
pop3_lib/pop3_lib.h
const char *const pass);
/** Получает количество сообщений в почтовом ящике и размер
*почтового ящика согласно текущему контексту POP3.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open .
*@param[in] pop3 Контекст клиента POP3.
*@param[out] messages_count Число писем.
*@param[out] messages_size Размер почтового ящика.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_stat(struct pop3 *const pop3, size_t *const messages_count, size_t *const messages_size);
/** Получает ответ сервера на команду NOOP.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open .
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_noop(struct pop3 *const pop3);
/** Получает данные об электронных письмах согласно текущему контексту POP3.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open .
*@param[in] pop3 Контекст клиента POP3.
*@param[in] messages_count_arg Число писем.
*@param[out] messages См. @ref pop3_message .
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_list(struct pop3 *const pop3, const size_t messages_count_arg, struct pop3_message **const messages);
/** Получает электронное письмо с адресами, вложениями и заголовками,
*определенными в текущем контексте POP3.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open .
*@param[in] pop3 Контекст клиента POP3.
*@param[in] message_number Номер письма.
*@param[out] message Письмо.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_retr(struct pop3 *const pop3, const size_t message_number, struct pop3_message *const message);
/** Удаляет электронное письмо по ID в текущем контексте POP3.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open .
*@param[in] pop3 Контекст клиента POP3.
*@param[in] message_number Номер письма.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_dele(struct pop3 *const pop3, const size_t message_number);
/** Отменяет удаление электронных писем в текущем контексте POP3.
*Перед этим вызывающая сторона должна вызвать функцию @ref pop3_open , а затем @ref pop3_dele .
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_rset(struct pop3 *const pop3);
/** Закрывает соединение POP3 и освобождает все ресурсы, удерживаемые контекстом POP3.
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_close(struct pop3 *pop3);
/** Возвращает текущий код ошибки.
*@param[in] pop3 Контекст клиента POP3.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_status_code_get(const struct pop3 *const pop3);
/** Очищает текущий код ошибки, |
установленный в контексте клиента POP3. |
||
* |
@param[in,out] pop3 Контекст |
клиента POP3. |
|
* |
@return |
Код предыдущей ошибки перед очисткой. */ |
|
enum pop3_status_code pop3_status_code_clear(struct pop3 *const pop3);
/** Устанавливает код ошибки в контексте клиента POP3 и возвращает его же.
*Это позволяет вызывающей стороне очистить код ошибки до @ref POP3_STATUS_OK ,
*чтобы предыдущие ошибки перестали распространяться. Однако это будет работать
*правильно только для всех ошибок до границы @ref POP3_STATUS__LAST .
*Не сбрасывает код ошибки >= @ref POP3_STATUS__LAST .
*@param[in] pop3 Контекст клиента POP3.
*@param[in] status_code См. @ref pop3_status_code.
*@return См. @ref pop3_status_code. */
enum pop3_status_code pop3_status_code_set(struct pop3 *const pop3,
const enum pop3_status_code status_code);
/** Преобразует код состояния клиента POP3 в описательную строку.
*@param[in] status_code Код состояния.
*@return Строка, содержащая описание @p status_code .
*Вызывающая сторона не должна освобождать или изменять эту строку. */ const char *pop3_status_code_errstr(enum pop3_status_code status_code);
/** Получает сообщение об ошибке от сервера.
*@param[in] pop3 Контекст клиента POP3.
*@return Строка, содержащая сообщение об ошибке от сервера.
15
pop3_lib/pop3_lib.h
* Вызывающая сторона не должна освобождать или изменять эту строку. */ const char *pop3_server_error_response(struct pop3 *const pop3);
#endif // POP3_LIB_H
Таблица 3. Файл POP3-библиотеки «pop3_lib/pop3_lib.c»
pop3_lib/pop3_lib.c
/** @file
*@brief POP3 client library
*@author Kovalenko Leonid
*@version 1.00
*
*Клиентская библиотека POP3.
*Позволяет пользователю получать электронные письма с сервера POP3.
*/
#if defined(_WIN32) || defined(WIN32) /** Если ОС Windows. */
#define POP3_IS_WINDOWS #endif // POP3_IS_WINDOWS
#ifdef POP3_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 // POP3_IS_WINDOWS
#include <errno.h> #include <limits.h> #include <signal.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <time.h>
#ifdef POP3_OPENSSL #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/ssl.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #endif // POP3_OPENSSL
#include "pop3_lib.h"
/** Используется для анализа ответов от POP3-сервера.
*Например, если сервер отправляет обратно "+OK <message>",
*тогда code будет установлен в POP3_STATUS_OK, text в "+OK <message>". */ struct pop3_command
{
///Код результата (целое число). enum pop3_status_code code;
///Текст ответа.
char *text;
};
/** Коды возврата для интерфейса getdelim,
*который позволяет вызывающей стороне проверить,
*могут ли быть обработаны другие строки с разделителями. */ enum str_getdelim_retcode
{
///Ошибка при обработке getdelim.
STRING_GETDELIMFD_ERROR = 0,
///Обнаружил новую строку и в настоящее время не может читать больше строк.
STRING_GETDELIMFD_DONE = 1
};
/** Структура данных для буфера чтения и синтаксического анализа строк. * Помогает получить и проанализировать строки ответа сервера. */
struct str_getdelimfd
{
///Буфер чтения, который может включать байты после разделителя. char *_buf;
///Количество выделенных байтов в буфере чтения.
16
pop3_lib/pop3_lib.c
size_t _bufsz;
///Количество фактически сохраненных байтов в буфере чтения. size_t _buf_len;
///Текущая строка, содержащая текст до разделителя.
char *line;
///Количество хранимых байтов в строке line. size_t line_len;
/** Указатель функции на пользовательскую функцию чтения для интерфейса pop3_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 pop3_header
{
///Имя заголовка (список заголовков сортируется по имени заголовка). char *key;
///Содержимое соответствующего ключа заголовка.
char *value;
};
/// Основная структура данных, которая содержит контекст клиента POP3. struct pop3
{
///Побитовый список флагов, управляющих поведением POP3-клиента. enum pop3_flag flags;
///Описатель сокета.
int sock;
///Буфер чтения и структура синтаксического анализа строк. struct str_getdelimfd gdfd;
///Список писем.
struct pop3_message *messages;
///Количество писем в списке писем. size_t messages_count;
/** Тайм-аут в секундах для ожидания перед возвратом с ошибкой.
* Это относится как к записи, так и к чтению из сетевого сокета. */ long timeout_sec;
///Код состояния, указывающий на успех / неудачу.
enum pop3_status_code status_code;
/** Указывает, есть ли в этом контексте активное соединение TLS.
*0 означает, что TLS-соединение неактивно.
*1 означает, что TLS-соединение активно. */ int tls_on;
/** Путь к файлу сертификата при использовании самозаверяющего или ненадежного сертификата,
*не находящегося в хранилище сертификатов по умолчанию. */
const char *cafile;
/// Описание последней ошибки char *error_description;
#ifdef POP3_OPENSSL
///OpenSSL TLS объект. SSL *tls;
///OpenSSL TLS контекст. SSL_CTX *tls_ctx;
///OpenSSL TLS I/O абстракция. BIO *tls_bio;
#endif // POP3_OPENSSL };
/** Проверяет, |
приведет ли |
сложение значений size_t к переносу. |
|||||
* @param[in] |
a |
Складывает это |
значение |
с |
@p |
b . |
|
* @param[in] |
b |
Складывает это |
значение |
с |
@p |
a . |
|
* @param[out] |
result |
Сохраняет результат сложения |
в этот буфер. |
||||
* |
|
Если |
установлено значение |
NULL, результат не записывается. |
|||
*@retval 1 Перенос совершен.
*@retval 0 Перенос не совершен. */
static int pop3_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 .
17
pop3_lib/pop3_lib.c
* |
@param[out] result Сохраняет результат вычитания в |
этот буфер. |
* |
Если установлено значение NULL, |
результат не записывается. |
*@retval 1 Заем совершен.
*@retval 0 Заем не совершен. */
static int pop3_si_sub_size_t(const size_t a, const size_t b, size_t *const result)
{
int wraps = (int)(a < b); if (result)
*result = a - b; return wraps;
}
/** Ожидает, пока на стороне чтения сокета не станет доступно больше данных.
*@param[in] pop3 Контекст клиента POP3.
*@retval POP3_STATUS_OK Если данные доступны для чтения на сокете.
*@retval POP3_STATUS_RECV Если соединение прерывается до того,
* как в сокете появятся какие-либо данные. */
static enum pop3_status_code pop3_str_getdelimfd_read_timeout(struct pop3 *const pop3)
{
fd_set readfds;
struct timeval timeout; FD_ZERO(&readfds); FD_SET(pop3->sock, &readfds); timeout.tv_sec = pop3->timeout_sec; timeout.tv_usec = 0;
if (select(pop3->sock + 1, &readfds, NULL, NULL, &timeout) < 1) return pop3_status_code_set(pop3, POP3_STATUS_RECV);
return pop3->status_code = POP3_STATUS_OK;
}
/** Эта функция вызывается интерфейсом pop3_str_getdelimfd, когда ему нужно прочитать больше данных.
*Читает, используя либо простое соединение сокета, если шифрование не включено,
*либо читает, используя OpenSSL, если у него есть активное соединение TLS.
* @param[in] |
gdfd |
См. @ref str_getdelimfd. |
||
* |
@param[out] |
buf |
Указатель на |
буфер для хранения прочитанных байтов. |
* |
@param[in] |
count |
Максимальное |
количество байтов для чтения. |
*@retval >=0 Количество прочитанных байтов.
*@retval -1 Не удалось прочитать из сокета. */
static long pop3_str_getdelimfd_read(struct str_getdelimfd *const gdfd, void *buf, size_t count)
{
struct pop3 *pop3; long bytes_read = 0; pop3 = gdfd->user_data;
if (pop3_str_getdelimfd_read_timeout(pop3) != POP3_STATUS_OK) return -1;
if (pop3->tls_on)
{
#ifdef POP3_OPENSSL do
{
/* Count никогда не будет иметь значение больше POP3_GETDELIM_READ_SZ, поэтому мы можем безопасно преобразовать его в int. */
bytes_read = SSL_read(pop3->tls, buf, (int)count);
} while (bytes_read <= 0 && BIO_should_retry(pop3->tls_bio)); #endif // POP3_OPENSSL
}
else
bytes_read = recv(pop3->sock, buf, count, 0); return bytes_read;
}
/** Возвращает |
расположение символа-разделителя в буфере поиска. |
||
* @param[in] |
buf |
Буфер поиска, используемый для |
поиска разделителя. |
* @param[in] |
buf_len |
Количество байтов для поиска в |
buf. |
* @param[in] |
delim |
Разделитель для поиска в buf. |
|
* @param[out] |
delim_pos |
Если разделитель найден в buf, |
возвращает позицию разделителя |
* |
|
в этом параметре, в противном случае 0. |
|
* |
|
Этот аргумент не должен быть равен NULL. |
|
*@retval 1 Если символ-разделитель найден.
*@retval 0 Если символ-разделитель не найден. */
static int pop3_str_getdelimfd_search_delim(const char *const buf, size_t buf_len, int delim, size_t *const delim_pos)
{
*delim_pos = 0;
for (size_t i = 0; i < buf_len; ++i) if (buf[i] == delim)
{
*delim_pos = i; return 1;
}
return 0;
}
/** Инициализирует внутренний строчный буфер.
* @param[in] gdfd См. @ref str_getdelimfd.
18
pop3_lib/pop3_lib.c
* @param[in] copy_len Количество байтов для копирования во внутренний строчный буфер. * @retval 0 Данные успешно размещены и скопированы в буфер новой строки.
* @retval -1 Не удалось выделить память для буфера новой строки. */
static int pop3_str_getdelimfd_set_line_and_buf(struct str_getdelimfd *const gdfd, size_t copy_len)
{
size_t copy_len_inc, nbytes_to_shift, new_buf_len; if (gdfd->line)
{
free(gdfd->line); gdfd->line = NULL;
}
if (pop3_si_add_size_t(copy_len, 2, ©_len_inc) || pop3_si_add_size_t((size_t)gdfd->_buf, copy_len_inc - 1, NULL) || pop3_si_sub_size_t(gdfd->_buf_len, copy_len, &nbytes_to_shift) || (gdfd->line = malloc(copy_len_inc)) == NULL)
return -1;
memcpy(gdfd->line, gdfd->_buf, copy_len_inc); gdfd->line_len = copy_len_inc;
memmove(gdfd->_buf, gdfd->_buf + (copy_len_inc - 1), nbytes_to_shift); if (pop3_si_sub_size_t(nbytes_to_shift, 1, &new_buf_len) == 0)
gdfd->_buf_len = new_buf_len; return 0;
}
/** Освобождает память @p gdfd .
* @param[in] gdfd См. @ref str_getdelimfd. */
static void pop3_str_getdelimfd_free(struct str_getdelimfd *const gdfd)
{
free(gdfd->_buf); free(gdfd->line); gdfd->_buf = NULL; gdfd->_bufsz = 0; gdfd->_buf_len = 0; gdfd->line = NULL; gdfd->line_len = 0;
}
/** Освобождает память @p gdfd и возвращает код ошибки @ref STRING_GETDELIMFD_ERROR .
*@param[in] gdfd См. @ref str_getdelimfd.
*@return См. @ref str_getdelim_retcode. */
static enum str_getdelim_retcode pop3_str_getdelimfd_throw_error(struct str_getdelimfd *const gdfd)
{
pop3_str_getdelimfd_free(gdfd); return STRING_GETDELIMFD_ERROR;
}
/** Величина, на которую увеличивается буфер чтения, если разделитель не найден. */
#define POP3_GETDELIM_READ_SZ 1000
/** Читает и анализирует строку с разделителями, используя настраиваемую функцию чтения.
*Этот интерфейс обрабатывает всю логику для расширения буфера, анализа разделителя в буфере и
*возврата каждой "строки" вызывающей стороне для обработки.
*@param[in] gdfd См. @ref str_getdelimfd.
*@return См. @ref str_getdelim_retcode. */
static enum str_getdelim_retcode pop3_str_getdelimfd(struct str_getdelimfd *const gdfd)
{
size_t delim_pos, buf_sz_remaining, buf_sz_new; long bytes_read = -1;
void *read_buf_ptr; char *buf_new;
if (gdfd->getdelimfd_read == NULL) return STRING_GETDELIMFD_ERROR;
while (1)
{
if (pop3_str_getdelimfd_search_delim(gdfd->_buf, gdfd->_buf_len, gdfd->delim, &delim_pos))
{
if (pop3_str_getdelimfd_set_line_and_buf(gdfd, delim_pos) < 0) return pop3_str_getdelimfd_throw_error(gdfd);
return STRING_GETDELIMFD_DONE;
}
else if (bytes_read == 0)
{
if (pop3_str_getdelimfd_set_line_and_buf(gdfd, gdfd->_buf_len) < 0) return pop3_str_getdelimfd_throw_error(gdfd);
return STRING_GETDELIMFD_DONE;
}
if (pop3_si_sub_size_t(gdfd->_bufsz, gdfd->_buf_len, &buf_sz_remaining)) return pop3_str_getdelimfd_throw_error(gdfd);
if (buf_sz_remaining < POP3_GETDELIM_READ_SZ)
{
if (pop3_si_add_size_t(buf_sz_remaining, POP3_GETDELIM_READ_SZ, &buf_sz_new)) return pop3_str_getdelimfd_throw_error(gdfd);
buf_new = realloc(gdfd->_buf, buf_sz_new); if (buf_new == NULL)
return pop3_str_getdelimfd_throw_error(gdfd);
19
pop3_lib/pop3_lib.c
gdfd->_buf = buf_new; gdfd->_bufsz = buf_sz_new;
}
if (pop3_si_add_size_t((size_t)gdfd->_buf, gdfd->_buf_len, NULL)) return pop3_str_getdelimfd_throw_error(gdfd);
read_buf_ptr = gdfd->_buf + gdfd->_buf_len;
bytes_read = (*gdfd->getdelimfd_read)(gdfd, read_buf_ptr, POP3_GETDELIM_READ_SZ); if (bytes_read < 0 ||
pop3_si_add_size_t(gdfd->_buf_len, (size_t)bytes_read, &gdfd->_buf_len)) return pop3_str_getdelimfd_throw_error(gdfd);
}
}
/** Копирует строку @p s2 в @p s1 .
*Эта функция ведет себя почти аналогично функции strcpy(), но
*возвращает указатель на конец скопированного буфера в целевой строке.
*Эта функция всегда добавляет '\0' в конец строки.
*@param[in] s1 Целевая строка.
*@param[in] s2 Строка с завершающим нулем для копирования в @p s1 .
*@return Указатель на место в @p s1 после последнего скопированного байта. */ static char *pop3_stpcpy(char *s1, const char *s2)
{
size_t i = 0; do
{
s1[i] = s2[i];
} while (s2[i++] != '\0'); return &s1[i - 1];
}
/** Копирует строку в новый динамически выделяемый буфер.
*Возвращает динамически выделяемую строку с тем же содержимым, что и входная строка.
*По завершении вызывающая сторона должна освободить возвращенную строку.
*@param[in] s Строка для дублирования.
* @param[in] max_chars Максимальное число символов.
*@retval char* Указатель на новую динамически выделяемую строку, дублированную из @p s .
*@retval NULL Не удалось выделить память для новой строки. */
static char *pop3_strdup(const char *s, size_t max_chars)
{
if (s != NULL)
{
char *dup;
size_t dup_len, slen = max_chars ? max_chars : strlen(s); if (pop3_si_add_size_t(slen, 1, &dup_len))
dup = NULL, errno = ENOMEM;
else if ((dup = malloc(dup_len)) != NULL)
{
memcpy(dup, s, slen); dup[slen] = '\0';
}
return dup;
}
return NULL;
}
/** Таблица поиска, используемая для декодирования данных base64.
*Для кодирования base64 каждые шесть битов были закодированы с использованием только символов
*ASCII из g_base64_encode_table. В этой таблице записи позволяют отменить
*этот процесс. Таблица имеет 128 записей, которые соответствуют значению индекса из таблицы
*кодирования.
*Если результат индексации заканчивается на -1 во время процесса декодирования, это
*указывает на недопустимый символ base64 в закодированных данных. */
static signed char g_base64_decode_table[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, // +
-1, -1, -1, 63, |
|
|
// / |
52, 53, 54, 55, 56, 57, 58, 59, |
60, |
61, |
// 0 - 9 |
-1, -1, -1, -1, -1, -1, -1, 0, |
1, |
2, 3, 4, 5, 6, 7, 8, 9, |
// A - J |
10, 11, 12, 13, 14, 15, 16, 17, |
18, |
19, |
// K - T |
20, 21, 22, 23, 24, 25, |
|
|
// U - Z |
-1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, |
// a - j |
||
36, 37, 38, 39, 40, 41, 42, 43, |
44, |
45, |
// k - t |
46, 47, 48, 49, 50, 51, |
|
|
// u - z |
-1, -1, -1, -1, -1}; |
|
|
|
/** Декодирует строку base64 в двоичные данные.
*Параметр декодирования будет динамически выделен этой функцией, если она успешно завершится.
*Следовательно, вызывающая сторона должна освободить параметр декодирования после использования.
* @param[in] |
buf |
Строка base64 с '\0'. |
|
* @param[out] |
decode Указатель на буфер, |
который будет выделяться динамически и будет содержать |
|
* |
|
декодированные двоичные данные. |
|
* |
|
Этот параметр будет |
установлен в NULL, если не удалось выделить память. |
*@retval >=0 Длина данных, хранящихся в параметре декодирования.
*@retval -1 Ошибка выделения памяти или недопустимые байтовые последовательности base64. */ static size_t pop3_base64_decode(const char *const buf, unsigned char **decode)
20
