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

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

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

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