
7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 5
.pdf
pop3_lib/pop3_lib.c
}
else
messages_count = messages_count_arg;
if ((*messages = malloc(messages_count * sizeof(struct pop3_message))) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
memset(*messages, 0, messages_count * sizeof(struct pop3_message)); for (size_t i = 1; i <= messages_count; ++i)
{
if (pop3_retr(pop3, i, &(*messages)[i - 1]) != POP3_STATUS_OK) return pop3->status_code;
}
if (pop3->messages)
{
for (size_t i = 0; i < pop3->messages_count; ++i)
{
for (size_t j = 0; j < pop3->messages[i].num_headers; ++j)
{
free(pop3->messages[i].header_list[j].key); free(pop3->messages[i].header_list[j].value);
}
free(pop3->messages[i].header_list);
for (size_t j = 0; j < pop3->messages[i].num_attachment; ++j)
{
free(pop3->messages[i].attachment_list[j].name); free(pop3->messages[i].attachment_list[j].data);
}
free(pop3->messages[i].attachment_list); free(pop3->messages[i].from); free(pop3->messages[i].envelope_from); free(pop3->messages[i].to); free(pop3->messages[i].cc); free(pop3->messages[i].date); free(pop3->messages[i].subject); free(pop3->messages[i].body);
}
free(pop3->messages); pop3->messages = NULL; pop3->messages_count = 0; free(pop3->error_description); pop3->error_description = NULL;
}
pop3->messages = *messages; pop3->messages_count = messages_count; return pop3->status_code;
}
enum pop3_status_code pop3_retr(struct pop3 *const pop3, const size_t message_number, struct pop3_message *const message)
{
char buffer[POP3_MESSAGE_NUM_BUF_SZ];
char *message_source, *next, *prev_message_source;
snprintf(buffer, POP3_MESSAGE_NUM_BUF_SZ, "LIST %zu\r\n", message_number); pop3_puts(pop3, buffer);
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 (strncmp(pop3->gdfd.line, "+OK", 3) != 0) return pop3->status_code = POP3_STATUS_PARAM;
sscanf(pop3->gdfd.line + 3 /* length("+OK") */, " %*ld %zu ", &message->size); snprintf(buffer, POP3_MESSAGE_NUM_BUF_SZ, "RETR %zu\r\n", message_number); pop3_puts(pop3, buffer);
if (pop3->status_code != POP3_STATUS_OK) return pop3->status_code;
if (pop3_read_all(pop3, &message_source) != POP3_STATUS_OK) return pop3->status_code;
if (strncmp(message_source, "+OK", 3) != 0) return pop3->status_code = POP3_STATUS_PARAM;
prev_message_source = message_source;
message_source = pop3_decode_if_needed(message_source); free(prev_message_source);
next = strchr(message_source, '\n') + 1; memset(message, 0, sizeof(struct pop3_message)); message->n = message_number; message->header_list = NULL;
while (next && *next != '\r')
{
struct pop3_header *temp_header_list;
char *pos = strchr(next, ':'), *next_pos = pos;
char *before = pop3_strdup(next, pos - next), *after = NULL; if (before == NULL)
return pop3->status_code = POP3_STATUS_NOMEM; while (1)
{
next_pos = strchr(next_pos + 1, '\n');
31

pop3_lib/pop3_lib.c
if (next_pos == NULL)
{
if ((after = pop3_strdup(pos + 1, 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
break;
}
else if (*(next_pos + 1) != '\t')
{
if ((after = pop3_strdup(pos + 2, next_pos - (pos + 1) - 2)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
break;
}
}
temp_header_list =
realloc(message->header_list, (message->num_headers + 1) * sizeof(struct pop3_header)); if (temp_header_list == NULL)
return pop3->status_code = POP3_STATUS_NOMEM; message->header_list = temp_header_list; message->header_list[message->num_headers].key = before; message->header_list[message->num_headers].value = after;
if (strncmp(before, "From", 4) == 0 && (message->from = pop3_strdup(after, 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
else if (strncmp(before, "Envelope-From", 13) == 0 && (message->envelope_from = pop3_strdup(after, 0)) == NULL)
return pop3->status_code = POP3_STATUS_NOMEM;
else if (strncmp(before, "To", 2) == 0 && (message->to = pop3_strdup(after, 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
else if (strncmp(before, "Cc", 2) == 0 && (message->cc = pop3_strdup(after, 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
else if (strncmp(before, "Date", 4) == 0 && (message->date = pop3_strdup(after, 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
else if (strncmp(before, "Subject", 7) == 0 && (message->subject = pop3_strdup(after, 0)) == NULL)
return pop3->status_code = POP3_STATUS_NOMEM; ++message->num_headers;
next = next_pos + 1;
}
if (message->from == NULL && (message->from = pop3_strdup("", 0)) == NULL) message->from = pop3_strdup("", 0);
if (message->envelope_from == NULL && (message->envelope_from = pop3_strdup("", 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
if (message->to == NULL && (message->to = pop3_strdup("", 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
if (message->cc == NULL && (message->cc = pop3_strdup("", 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
if (message->date == NULL && (message->date = pop3_strdup("", 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
if (message->subject == NULL && (message->subject = pop3_strdup("", 0)) == NULL) return pop3->status_code = POP3_STATUS_NOMEM;
if (message->header_list != NULL)
qsort(message->header_list, message->num_headers, sizeof(*message->header_list), pop3_header_cmp);
pop3->status_code = parse_body_and_attachments(next, &message->body, &message->num_attachment, &message->attachment_list);
free(message_source); return pop3->status_code;
}
enum pop3_status_code pop3_dele(struct pop3 *const pop3, const size_t message_number)
{
char buffer[POP3_MESSAGE_NUM_BUF_SZ];
snprintf(buffer, POP3_MESSAGE_NUM_BUF_SZ, "DELE %zu\r\n", message_number); pop3_puts(pop3, buffer);
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;
return pop3->status_code;
}
enum pop3_status_code pop3_rset(struct pop3 *const pop3)
{
pop3_puts(pop3, "RSET\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;
return pop3->status_code;
}
enum pop3_status_code pop3_close(struct pop3 *pop3)
{
enum pop3_status_code status_code = POP3_STATUS_OK; if (pop3->flags == POP3_FLAG_INVALID_MEMORY)
return pop3->status_code;
32

pop3_lib/pop3_lib.c
if (pop3->sock != -1)
{
pop3->status_code = POP3_STATUS_OK; pop3_puts(pop3, "QUIT\r\n");
#ifdef POP3_OPENSSL
if (pop3->tls_on)
{
SSL_free(pop3->tls); SSL_CTX_free(pop3->tls_ctx);
}
#endif // POP3_OPENSSL #ifdef POP3_IS_WINDOWS
closesocket(pop3->sock); WSACleanup();
#else // POSIX
if (close(pop3->sock) < 0 && pop3->status_code == POP3_STATUS_OK) pop3_status_code_set(pop3, POP3_STATUS_CLOSE);
#endif // POP3_IS_WINDOWS
}
pop3_str_getdelimfd_free(&pop3->gdfd); if (pop3->messages)
{
for (size_t i = 0; i < pop3->messages_count; ++i)
{
for (size_t j = 0; j < pop3->messages[i].num_headers; ++j)
{
free(pop3->messages[i].header_list[j].key); free(pop3->messages[i].header_list[j].value);
}
free(pop3->messages[i].header_list);
for (size_t j = 0; j < pop3->messages[i].num_attachment; ++j)
{
free(pop3->messages[i].attachment_list[j].name); free(pop3->messages[i].attachment_list[j].data);
}
free(pop3->messages[i].attachment_list); free(pop3->messages[i].from); free(pop3->messages[i].envelope_from); free(pop3->messages[i].to); free(pop3->messages[i].cc); free(pop3->messages[i].date); free(pop3->messages[i].subject); free(pop3->messages[i].body);
}
free(pop3->messages); pop3->messages = NULL; pop3->messages_count = 0; free(pop3->error_description); pop3->error_description = NULL;
}
status_code = pop3->status_code; free(pop3);
return status_code;
}
enum pop3_status_code pop3_status_code_get(const struct pop3 *const pop3)
{
return pop3->status_code;
}
enum pop3_status_code pop3_status_code_clear(struct pop3 *const pop3)
{
enum pop3_status_code old_status = pop3_status_code_get(pop3); pop3_status_code_set(pop3, POP3_STATUS_OK);
return old_status;
}
enum pop3_status_code pop3_status_code_set(struct pop3 *const pop3,
const enum pop3_status_code status_code)
{
if ((unsigned)status_code >= POP3_STATUS__LAST)
return pop3_status_code_set(pop3, POP3_STATUS_PARAM); pop3->status_code = status_code;
return status_code;
}
const char *pop3_status_code_errstr(enum pop3_status_code status_code)
{
const char *const status_code_err_str[] = { /* POP3_STATUS_OK */
"Success",
/* POP3_STATUS_NOMEM */ "Memory allocation failed", /* POP3_STATUS_CONNECT */
"Failed to connect to the mail server",
33

pop3_lib/pop3_lib.c
/* POP3_STATUS_HANDSHAKE */
"Failed to handshake or negotiate a TLS connection with the server", /* POP3_STATUS_AUTH */
"Failed to authenticate with the given credentials", /* POP3_STATUS_SEND */
"Failed to send bytes to the server", /* POP3_STATUS_RECV */
"Failed to receive bytes from the server", /* POP3_STATUS_CLOSE */
"Failed to properly close a connection", /* POP3_STATUS_SERVER_RESPONSE */
"POP3 server sent back an unexpected status code", /* POP3_STATUS_PARAM */
"Invalid parameter", /* POP3_STATUS_FILE */
"Failed to read or open a local file", /* POP3_STATUS_DATE */
"Failed to get the local date and time", /* POP3_STATUS__LAST */
"Unknown error"};
if ((unsigned)status_code > POP3_STATUS__LAST) status_code = POP3_STATUS__LAST;
return status_code_err_str[status_code];
}
const char *pop3_server_error_response(struct pop3 *const pop3) { return pop3->error_description; }
Таблица 4. Сборочный файл POP3-библиотеки «pop3_lib/WinMakefile» /
«pop3_lib/LinuxMakefile»
pop3_lib/WinMakefile
.PHONY : clean CC = gcc
CFLAGS = -O4 -std=c99 -Waggregate-return -Wall -Wbad-function-cast -Wcast-align -Wcast-qual \ -Wdeclaration-after-statement -Wdisabled-optimization -Wdouble-promotion -Werror \ -Wextra -Wfatal-errors -Wfloat-equal -Wframe-larger-than=5000 -Winit-self \ -Winline -Winvalid-pch -Wlarger-than=10000 -Wno-deprecated-declarations \ -Wmissing-include-dirs -Wnested-externs -Wno-aggressive-loop-optimizations \
-Wold-style-definition -Wpacked -Wpedantic -pedantic-errors -Wredundant-decls -Wshadow \ -Wstack-protector -Wstrict-aliasing -Wstrict-overflow=5 -Wstrict-prototypes \ -Wswitch-default -Wswitch-enum -Wundef -Wuninitialized -Wunknown-pragmas \ -Wunused-parameter -Wvla -Wwrite-strings -Wstack-usage=5000 -Wno-format \ -Wjump-misses-init -Wlogical-op -Wnormalized=nfkc -Wtrampolines \
-Wsync-nand -Wunsuffixed-float-constants -Wvector-operation-performance \ -fstack-protector-all -fstrict-overflow -MD -DPOP3_OPENSSL
all: pop3_lib.o
pop3_lib.o: pop3_lib.c pop3_lib.h
$(CC) $(CFLAGS) ./pop3_lib.c -c -I"C:\Program Files\OpenSSL-Win64\include" \
-I"C:\Program Files\OpenSSL-Win64\include\openssl" -lbio -lerr -lssl -lx509 -lx509v3
clean:
del /s /q pop3_lib.o pop3_lib.d
pop3_lib/LinuxMakefile
.PHONY : clean CC = gcc
CFLAGS = -O4 -std=c99 -Waggregate-return -Wall -Wbad-function-cast -Wcast-align -Wcast-qual \ -Wdeclaration-after-statement -Wdisabled-optimization -Wdouble-promotion -Werror \ -Wextra -Wfatal-errors -Wfloat-equal -Wframe-larger-than=5000 -Winit-self \ -Winline -Winvalid-pch -Wlarger-than=10000 -Wno-deprecated-declarations \ -Wmissing-include-dirs -Wnested-externs -Wno-aggressive-loop-optimizations \
-Wold-style-definition -Wpacked -Wpedantic -pedantic-errors -Wredundant-decls -Wshadow \ -Wstack-protector -Wstrict-aliasing -Wstrict-overflow=5 -Wstrict-prototypes \ -Wswitch-default -Wswitch-enum -Wundef -Wuninitialized -Wunknown-pragmas \ -Wunused-parameter -Wvla -Wwrite-strings -Wstack-usage=5000 -Wno-format \ -Wjump-misses-init -Wlogical-op -Wnormalized=nfkc -Wtrampolines \
-Wsync-nand -Wunsuffixed-float-constants -Wvector-operation-performance \ -fstack-protector-all -fstrict-overflow -MD -DPOP3_OPENSSL
all: pop3_lib.o
pop3_lib.o: pop3_lib.c pop3_lib.h
$(CC) $(CFLAGS) ./pop3_lib.c -c
clean:
rm -rf pop3_lib.o pop3_lib.d
34

Таблица 5. Файл POP3-клиента «pop3_client.c»
pop3_client.c
/** @file
*@brief POP3 client example
*@author Kovalenko Leonid
*@version 1.00
*
*Пример использования клиентской библиотеки POP3,
*которая позволяет пользователю получать
*электронные письма с сервера POP3.
*/
#include "./pop3_lib/pop3_lib.h" #include <stdio.h>
#include <stdlib.h> #include <string.h>
#include <unistd.h> // access()
///Максимальная длина имени файла
#define FILENAME_LENGTH 256
///Максимальная длина параметра/его значения
#define PARAM_LENGTH 500
///Максимальная длина заголовка письма
#define SUBJECT_LENGTH 500
///Максимальная длина тела письма
#define BODY_LENGTH 10000
///"double expansion" trick [1] #define STRINGIFY(x) #x
///"double expansion" trick [2] #define TOSTRING(x) STRINGIFY(x)
/** Возвращает строковое представление метода шифрования соединения.
*@param[in] conn_security См. @ref pop3_connection_security.
*@return Вызывающая сторона не должна освобождать или изменять эту строку. */
const char *str_connection_security(const enum pop3_connection_security conn_security)
{
switch (conn_security)
{
case POP3_SECURITY_NONE: return "NONE";
case POP3_SECURITY_STARTTLS: return "STARTTLS";
case POP3_SECURITY_TLS: return "TLS";
default:
return "UNKNOWN";
}
}
/** Возвращает строковое представление флагов контекста клиента POP3.
*@param[in] flag См. @ref pop3_flag.
*@return Вызывающая сторона не должна освобождать или изменять эту строку. */ const char *str_flag(const enum pop3_flag flag)
{
if ((flag & POP3_DEBUG) && (flag & POP3_NO_CERT_VERIFY)) return "DEBUG and NO_CERT_VERIFY";
else if (flag & POP3_DEBUG) return "DEBUG";
else if (flag & POP3_NO_CERT_VERIFY) return "NO_CERT_VERIFY";
return "NONE";
}
/** Проверяет статус операции @p status .
*Если @ref POP3_STATUS_OK , то возвращает 1.
*Если не @ref POP3_STATUS_OK , то пишет сообщение об ошибке в stderr и возвращает 0.
*@param[in] pop3 Контекст клиента POP3.
*@param[in] status Статус операции.
*@retval 1 OK.
*@retval 0 Ошибка. */
int check_status(struct pop3 *const pop3, const int status)
{
if (status != POP3_STATUS_OK)
{
fprintf(stderr, "pop3 failed: %s\n", pop3_status_code_errstr(status)); fprintf(stderr, "server message: %s\n", pop3_server_error_response(pop3)); return 0;
}
return 1;
}
/** Пропускает символы в потоке stdin после того,
*как на вызываемой стороне была считана строка @p str
*с максимальной длиной @p max_length .
35

pop3_client.c
*@param[in] str Строка.
*@param[in] max_length Максимальная длина строки @p str . */
void stdin_clear_if_needed(const char *const str, const size_t max_length)
{
if (str != NULL && strchr(str, (int)'\n') == NULL && (max_length == SIZE_MAX || strlen(str) < max_length)) while (fgetc(stdin) != '\n')
continue;
}
/** Заменяет символ @p from на символ @p to в строке @p str .
*@param[in,out] str Строка для изменения (с '\0').
*@param[in] from Символ, который нужно заменить.
*@param[in] to Символ, на который нужно заменить. */
void replace_char(char *const str, const char from, const char to)
{
char *current;
while ((current = strchr(str, (int)from)) != NULL) *current = to;
}
/** Считывает строку @p buffer с максимальной длиной @p max_length с консоли.
*@param[out] buffer Результирующая строка.
*@param[in] max_length Максимальная длина результирующей строки. */
void read_string_from_console(char *const buffer, const size_t max_length)
{
fgets(buffer, max_length, stdin); stdin_clear_if_needed(buffer, max_length); replace_char(buffer, '\n', '\0');
}
/** Ввод сервера с консоли.
*@param[out] argument Аргумент для записи.
*@param[in] max_length Максимальная длина. */
void change_server(char *const argument, const size_t max_length)
{
printf("Enter server: "); read_string_from_console(argument, max_length);
}
/** Ввод порта с консоли.
*@param[out] argument Аргумент для записи.
*@param[in] max_length Максимальная длина. */
void change_port(char *const argument, const size_t max_length)
{
do
{
printf("Enter port (10-999: 110 for STARTTLS | 995 for TLS): "); read_string_from_console(argument, max_length);
} while (
argument[0] < '0' || argument[0] > '9' || argument[1] < '0' || argument[1] > '9' || (argument[2] != '\0' && (argument[2] < '0' || argument[2] > '9' || argument[3] != '\0')));
}
/** Ввод имени пользователя с консоли.
*@param[out] argument Аргумент для записи.
*@param[in] max_length Максимальная длина. */
void change_name(char *const argument, const size_t max_length)
{
printf("Enter name: "); read_string_from_console(argument, max_length);
}
/** Ввод email пользователя с консоли.
*@param[out] argument Аргумент для записи.
*@param[in] max_length Максимальная длина. */
void change_user(char *const argument, const size_t max_length)
{
printf("Enter user email: "); read_string_from_console(argument, max_length);
}
/** Ввод пароля пользователя с консоли.
*@param[out] argument Аргумент для записи.
*@param[in] max_length Максимальная длина. */
void change_pass(char *const argument, const size_t max_length)
{
printf("Enter user password: "); read_string_from_console(argument, max_length);
}
/** Ввод метода шифрования соединения.
* @param[out] argument Аргумент для изменения. */
void change_connection_security(enum pop3_connection_security *const argument)
{
36

pop3_client.c
char buffer[PARAM_LENGTH]; while (1)
{
printf("Choose connection security:\n"); printf("-- STARTTLS (110 port)\n"); printf("-- TLS (995 port)\n"); printf("-- NONE\n");
printf("#> ");
read_string_from_console(buffer, sizeof(buffer) / sizeof(buffer[0])); if (strcmp(buffer, "STARTTLS") == 0 || strcmp(buffer, "starttls") == 0)
{
*argument = POP3_SECURITY_STARTTLS; return;
}
else if (strcmp(buffer, "TLS") == 0 || strcmp(buffer, "tls") == 0)
{
*argument = POP3_SECURITY_TLS; return;
}
else if (strcmp(buffer, "NONE") == 0 || strcmp(buffer, "none") == 0)
{
*argument = POP3_SECURITY_NONE; return;
}
}
}
/** Ввод флагов контекста клиента POP3.
* @param[out] argument Аргумент для изменения. */ void change_flag(enum pop3_flag *const argument)
{
char buffer[PARAM_LENGTH]; while (1)
{
printf("Debug flag (true|false): ");
read_string_from_console(buffer, sizeof(buffer) / sizeof(buffer[0])); if (strcmp(buffer, "TRUE") == 0 || strcmp(buffer, "true") == 0)
{
*argument |= POP3_DEBUG; break;
}
else if (strcmp(buffer, "FALSE") == 0 || strcmp(buffer, "false") == 0)
{
*argument &= ~POP3_DEBUG; break;
}
}
while (1)
{
printf("No cert. verify flag (true|false): "); read_string_from_console(buffer, sizeof(buffer) / sizeof(buffer[0])); if (strcmp(buffer, "TRUE") == 0 || strcmp(buffer, "true") == 0)
{
*argument |= POP3_NO_CERT_VERIFY; break;
}
else if (strcmp(buffer, "FALSE") == 0 || strcmp(buffer, "false") == 0)
{
*argument &= ~POP3_NO_CERT_VERIFY; break;
}
}
}
/** Точка входа программы-примера
* @return Код ошибки */ int main(void)
{
// Инициализация параметров
int r = POP3_STATUS_NOMEM, messages_removed = 0; struct pop3 *pop3 = NULL;
char cfg_mail_server[PARAM_LENGTH] = "pop.yandex.ru", cfg_mail_port[4] = "995", cfg_mail_name[PARAM_LENGTH] = "Robert Wayne ALX", cfg_mail_user[PARAM_LENGTH] = "robert.wayne.alx@yandex.ru",
cfg_mail_pass[PARAM_LENGTH] = "Robw12345", command[PARAM_LENGTH] = "", y_or_n[2] = "n", *filename = NULL;
const char *const cafile = NULL;
enum pop3_connection_security cfg_conn_security = POP3_SECURITY_TLS; enum pop3_flag cfg_flag = POP3_NO_CERT_VERIFY;
struct pop3_message *messages = NULL;
size_t messages_count = 0, messages_size = 0, message_number = 0; printf("=== Simple program for receiving mail messages ===\n"); while (/* always true */ 1)
{
// Вывод параметров
37

|
pop3_client.c |
printf("---------------------------------------------- |
\n"); |
printf("Server: %s |
| Port: %s\n", cfg_mail_server, cfg_mail_port); |
printf("Name: %s | |
Email: %s | Password: %s\n", cfg_mail_name, cfg_mail_user, |
cfg_mail_pass);
printf("Connection security: %s\n", str_connection_security(cfg_conn_security)); printf("Flag: %s\n", str_flag(cfg_flag));
printf("-------------\nCommands:\n"); printf("-- start\n");
printf("-- change server|port|name|email|pass|conn_security|auth_method|flag\n"); printf("-- exit\n");
printf("-------------\n#>");
read_string_from_console(command, sizeof(command) / sizeof(command[0]));
//Параметры можно менять, вводя соответствующие команды if (strcmp(command, "change server") == 0)
change_server(cfg_mail_server, sizeof(cfg_mail_server) / sizeof(cfg_mail_server[0])); else if (strcmp(command, "change port") == 0)
change_port(cfg_mail_port, sizeof(cfg_mail_port) / sizeof(cfg_mail_port[0])); else if (strcmp(command, "change name") == 0)
change_name(cfg_mail_name, sizeof(cfg_mail_name) / sizeof(cfg_mail_name[0])); else if (strcmp(command, "change email") == 0)
change_user(cfg_mail_user, sizeof(cfg_mail_user) / sizeof(cfg_mail_user[0])); else if (strcmp(command, "change pass") == 0)
change_pass(cfg_mail_pass, sizeof(cfg_mail_pass) / sizeof(cfg_mail_pass[0])); else if (strcmp(command, "change conn_security") == 0)
change_connection_security(&cfg_conn_security); else if (strcmp(command, "change flag") == 0)
change_flag(&cfg_flag);
//Выход из программы -- exit
else if (strcmp(command, "exit") == 0) return EXIT_SUCCESS;
// После настройки параметров можно принимать письма -- start else if (strcmp(command, "start") == 0)
{
messages_removed = 0;
// Открываем соединение с POP3 сервером
r = pop3_open(cfg_mail_server, cfg_mail_port, cfg_conn_security, cfg_flag, cafile, &pop3);
if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало // Проходим аутентификацию по email и паролю
r = pop3_auth(pop3, cfg_mail_user, cfg_mail_pass); if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало while (/* always true */ 1)
{
// Команда NOOP
r = pop3_noop(pop3);
if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало printf("-------------\n");
messages_count = 0;
// Получение количества сообщений и размера почтового ящика r = pop3_stat(pop3, &messages_count, &messages_size);
if (!check_status(pop3, r))
break; // Если не получилось, то выход в начало if (messages_count == 0)
{
printf("No messages.\n");
}
else // Если почтовый ящик не пуст
{
printf("%zu messages (%zu bytes)\n\n", messages_count, messages_size); // Получаем данные об электронных письмах
r = pop3_list(pop3, messages_count, &messages); if (!check_status(pop3, r))
break; // Если не получилось, то выход в начало for (size_t i = 0; i < messages_count; ++i)
{
printf("%d: %s\n", messages[i].n, messages[i].subject); printf("From: %s\n", messages[i].from);
if (messages[i].envelope_from && messages[i].envelope_from[0] != '\0') printf("Envelope-From: %s\n", messages[i].envelope_from);
printf("To: %s\n", messages[i].to);
if (messages[i].cc && messages[i].cc[0] != '\0') printf("Cc: %s\n", messages[i].cc);
if (messages[i].date && messages[i].date[0] != '\0') printf("Date: %s\n", messages[i].date);
printf("\n");
}
// Интерактивный просмотр писем while (1)
{
// Команда NOOP
r = pop3_noop(pop3);
if (!check_status(pop3, r))
38

pop3_client.c
continue; // Если не получилось, то выход в начало printf("Enter message number to see more info: "); message_number = (size_t)-1; read_string_from_console(command, PARAM_LENGTH); sscanf(command, " %zu ", &message_number);
if (message_number != (size_t)-1)
{
if (message_number <= 0 || message_number > messages_count)
{
printf("Number must be in [%d, %d]\n", 1, messages_count); continue;
}
printf("%d: %s\n", messages[message_number - 1].n, messages[message_number - 1].subject);
printf("From: %s\n", messages[message_number - 1].from); if (messages[message_number - 1].envelope_from &&
messages[message_number - 1].envelope_from[0] != '\0') printf("Envelope-From: %s\n",
messages[message_number - 1].envelope_from); printf("To: %s\n", messages[message_number - 1].to);
if (messages[message_number - 1].cc && messages[message_number - 1].cc[0] != '\0') printf("Cc: %s\n", messages[message_number - 1].cc);
if (messages[message_number - 1].date && messages[message_number - 1].date[0] != '\0') printf("Date: %s\n", messages[message_number - 1].date);
printf("Body:\n%s\n", messages[message_number - 1].body); printf("Attachments:\n");
for (size_t i = 0, n = messages[message_number - 1].num_attachment; i < n; ++i)
{
printf("%d. %s\n", i + 1,
messages[message_number - 1].attachment_list[i].name);
}
printf("\n\n"); printf("Commands:\n");
printf("attachment <number> -- save <number> attachment in current " "directory\n");
printf("remove -- remove message\n"); printf("close -- close message\n");
// Интерактивный ввод команд для выбранного письма while (1)
{
// Команда NOOP
r = pop3_noop(pop3);
if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало printf("%d #>", message_number); read_string_from_console(command, PARAM_LENGTH);
// Сохранение вложения в файл в текущей директории if (strncmp(command, "attachment", 10) == 0)
{
size_t attachment_number = -1; FILE *file = NULL;
sscanf(command + 10, " %zu ", &attachment_number); if (attachment_number != (size_t)-1)
{
if (attachment_number <= 0 || attachment_number >
messages[message_number - 1].num_attachment)
{
printf("Number must be in [%d, %d].\n", 1, messages[message_number - 1].num_attachment);
continue;
}
filename = messages[message_number - 1]
.attachment_list[attachment_number - 1]
.name; // Если файл существует
if (access(filename, F_OK) == 0)
{
printf(
"The file \"%s\" already exist.\nRewrite? [y/n]:", filename);
read_string_from_console(y_or_n, sizeof(y_or_n) / sizeof(y_or_n[0]));
if (!(y_or_n[0] == 'y' || y_or_n[0] == 'Y')) continue;
// Если файл нельзя открыть на запись if (access(filename, W_OK) != 0)
{
printf("Insufficient permission to write the file " "\"%s\"\n",
filename); continue;
39

pop3_client.c
}
}
//Открываем файл на запись file = fopen(filename, "wb"); if (file == NULL)
{
printf("Insufficient permission to write the file " "\"%s\"\n",
filename); continue;
}
//Записываем всю информацию
fwrite(messages[message_number - 1]
.attachment_list[attachment_number - 1]
.data, sizeof(char),
messages[message_number - 1]
.attachment_list[attachment_number - 1]
.data_len, file);
fflush(file);
// Закрываем файл fclose(file);
}
}
//Удаление сообщения на стороне pop3 сервера else if (strncmp(command, "remove", 6) == 0)
{
if ((r = pop3_dele(pop3, message_number)) == POP3_STATUS_OK)
{
++messages_removed; printf(
"The message has been removed on the pop3 server (on " "the client, the message list needs to be updated).\n");
printf("You can undo this by exiting the messages menu.\n"); break;
}
else
check_status(pop3, r);
}
//Закрытие сообщения
else if (strncmp(command, "close", 5) == 0) break;
else
printf("Unknown command.\n");
}
}
else
break;
}
// Команда NOOP
r = pop3_noop(pop3);
if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало
// Если было удалено хоть одно сообщение, то удаление можно отменить if (messages_removed)
{
printf("Undo removal of previously selected messages? [y/n]: "); read_string_from_console(&y_or_n[0], sizeof(y_or_n) / sizeof(y_or_n[0])); if (y_or_n[0] == 'y' || y_or_n[0] == 'Y')
{
r = pop3_rset(pop3);
if (!check_status(pop3, r))
continue; // Если не получилось, то выход в начало messages_removed = 0;
}
}
}
// Обновление списка писем (y -- обновить) printf("Update? [y/n]: ");
read_string_from_console(&y_or_n[0], sizeof(y_or_n) / sizeof(y_or_n[0])); if (y_or_n[0] != 'y' && y_or_n[0] != 'Y')
break;
}
pop3_close(pop3); pop3 = NULL;
printf("----------------------------------------------\n"); printf("----------------------------------------------\n");
}
else
printf("Unknown command\n");
}
return EXIT_SUCCESS;
}
40