Материал: ММиВА. Лабораторная работа 4

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

smtp_lib/smtp_lib.c

signal(SIGPIPE, SIG_IGN); #endif // !(SMTP_IS_WINDOWS)

if (smtp_connect(snew, server, port) < 0)

return smtp_status_code_set(*smtp, SMTP_STATUS_CONNECT);

if (smtp_initiate_handshake(snew, server, connection_security) != SMTP_STATUS_OK) return smtp_status_code_set(*smtp, SMTP_STATUS_HANDSHAKE);

return snew->status_code;

}

enum smtp_status_code smtp_auth(struct smtp *const smtp,

enum smtp_authentication_method auth_method, const char *const user, const char *const pass)

{

int auth_rc;

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

if (auth_method == SMTP_AUTH_PLAIN)

auth_rc = smtp_auth_plain(smtp, user, pass); else if (auth_method == SMTP_AUTH_LOGIN)

auth_rc = smtp_auth_login(smtp, user, pass); else if (auth_method == SMTP_AUTH_NONE)

auth_rc = 0;

else

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); if (auth_rc < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_AUTH); return smtp->status_code;

}

enum smtp_status_code smtp_mail(struct smtp *const smtp, const char *const body)

{

int has_mail_from = 0;

struct smtp_address *address; char date[SMTP_DATE_MAX_SZ];

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

// MAIL timeout 5 минут. smtp_set_read_timeout(smtp, 60 * 5);

for (size_t i = 0; i < smtp->num_address; ++i)

{

address = &smtp->address_list[i];

if (address->type == SMTP_ADDRESS_FROM)

{

if (smtp_mail_envelope_header(smtp, "MAIL FROM", address) != SMTP_STATUS_OK) return smtp->status_code;

has_mail_from = 1; break;

}

}

if (!has_mail_from)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); // RCPT timeout 5 минут.

smtp_set_read_timeout(smtp, 60 * 5);

for (size_t i = 0; i < smtp->num_address; ++i)

{

address = &smtp->address_list[i];

if (address->type != SMTP_ADDRESS_FROM &&

smtp_mail_envelope_header(smtp, "RCPT TO", address) != SMTP_STATUS_OK) return smtp->status_code;

}

// DATA timeout 2 минут. smtp_set_read_timeout(smtp, 60 * 2);

if (smtp_puts(smtp, "DATA\r\n") != SMTP_STATUS_OK) return smtp->status_code;

//354 ответ на DATA должен быть возвращен, прежде чем мы сможем отправить сообщение. if (smtp_read_and_parse_code(smtp) != SMTP_BEGIN_MAIL)

return smtp_status_code_set(smtp, SMTP_STATUS_SERVER_RESPONSE); if (!smtp_header_exists(smtp, "Date"))

{

if (smtp_date_rfc_2822(date) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_DATE); if (smtp_header_add(smtp, "Date", date) != SMTP_STATUS_OK)

return smtp->status_code;

}

//DATA block timeout 3 минуты.

smtp_set_read_timeout(smtp, 60 * 3);

if (smtp_append_address_to_header(smtp, SMTP_ADDRESS_FROM, "From") != SMTP_STATUS_OK || smtp_append_address_to_header(smtp, SMTP_ADDRESS_TO, "To") != SMTP_STATUS_OK || smtp_append_address_to_header(smtp, SMTP_ADDRESS_CC, "Cc") != SMTP_STATUS_OK) return smtp->status_code;

for (size_t i = 0; i < smtp->num_headers; ++i)

if (smtp_print_header(smtp, &smtp->header_list[i]) != SMTP_STATUS_OK) return smtp->status_code;

if (smtp_print_email(smtp, body)) return smtp->status_code;

46

smtp_lib/smtp_lib.c

// Конец сегмента DATA.

if (smtp_puts(smtp, ".\r\n") != SMTP_STATUS_OK) return smtp->status_code;

// DATA termination timeout 250 return code - 10 минут. smtp_set_read_timeout(smtp, 60 * 10);

if (smtp_read_and_parse_code(smtp) != SMTP_DONE)

return smtp_status_code_set(smtp, SMTP_STATUS_SERVER_RESPONSE); return smtp->status_code;

}

enum smtp_status_code smtp_close(struct smtp *smtp)

{

enum smtp_status_code status_code; status_code = smtp->status_code;

if (smtp->flags == SMTP_FLAG_INVALID_MEMORY) return status_code;

if (smtp->sock != -1)

{

// Ошибка не игнорируется: нужно освободить ресурсы клиента SMTP. smtp->status_code = SMTP_STATUS_OK;

smtp_puts(smtp, "QUIT\r\n"); #ifdef SMTP_OPENSSL

if (smtp->tls_on)

{

SSL_free(smtp->tls); SSL_CTX_free(smtp->tls_ctx);

}

#endif // SMTP_OPENSSL #ifdef SMTP_IS_WINDOWS

closesocket(smtp->sock); WSACleanup();

#else // POSIX

if (close(smtp->sock) < 0 && smtp->status_code == SMTP_STATUS_OK) smtp_status_code_set(smtp, SMTP_STATUS_CLOSE);

#endif // SMTP_IS_WINDOWS

}

smtp_str_getdelimfd_free(&smtp->gdfd); smtp_header_clear_all(smtp); smtp_address_clear_all(smtp); smtp_attachment_clear_all(smtp);

if (status_code == SMTP_STATUS_OK) status_code = smtp->status_code;

free(smtp);

return status_code;

}

enum smtp_status_code smtp_status_code_get(const struct smtp *const smtp)

{

return smtp->status_code;

}

enum smtp_status_code smtp_status_code_clear(struct smtp *const smtp)

{

enum smtp_status_code old_status = smtp_status_code_get(smtp); smtp_status_code_set(smtp, SMTP_STATUS_OK);

return old_status;

}

enum smtp_status_code smtp_status_code_set(struct smtp *const smtp,

enum smtp_status_code status_code)

{

if ((unsigned)status_code >= SMTP_STATUS__LAST)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); smtp->status_code = status_code;

return status_code;

}

const char *smtp_status_code_errstr(enum smtp_status_code status_code)

{

const char *const status_code_err_str[] = { /* SMTP_STATUS_OK */

"Success",

/* SMTP_STATUS_NOMEM */ "Memory allocation failed", /* SMTP_STATUS_CONNECT */

"Failed to connect to the mail server", /* SMTP_STATUS_HANDSHAKE */

"Failed to handshake or negotiate a TLS connection with the server", /* SMTP_STATUS_AUTH */

"Failed to authenticate with the given credentials", /* SMTP_STATUS_SEND */

"Failed to send bytes to the server", /* SMTP_STATUS_RECV */

"Failed to receive bytes from the server", /* SMTP_STATUS_CLOSE */

47

smtp_lib/smtp_lib.c

"Failed to properly close a connection", /* SMTP_STATUS_SERVER_RESPONSE */

"SMTP server sent back an unexpected status code", /* SMTP_STATUS_PARAM */

"Invalid parameter", /* SMTP_STATUS_FILE */

"Failed to read or open a local file", /* SMTP_STATUS_DATE */

"Failed to get the local date and time", /* SMTP_STATUS__LAST */

"Unknown error"};

if ((unsigned)status_code > SMTP_STATUS__LAST) status_code = SMTP_STATUS__LAST;

return status_code_err_str[status_code];

}

enum smtp_status_code smtp_header_add(struct smtp *const smtp, const char *const key, const char *const value)

{

struct smtp_header *new_header_list, *new_header; size_t num_headers_inc;

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

if (smtp_header_key_validate(key) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); if (value && smtp_header_value_validate(value) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM);

if (smtp_si_add_size_t(smtp->num_headers, 1, &num_headers_inc) || (new_header_list = smtp_reallocarray(smtp->header_list, num_headers_inc,

sizeof(*smtp->header_list))) == NULL) return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

smtp->header_list = new_header_list;

new_header = &smtp->header_list[smtp->num_headers]; new_header->key = smtp_strdup(key); new_header->value = smtp_strdup(value);

if (new_header->key == NULL || (new_header->value == NULL && value))

{

free(new_header->key); free(new_header->value);

return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

}

smtp->num_headers = num_headers_inc;

qsort(smtp->header_list, smtp->num_headers, sizeof(*smtp->header_list), smtp_header_cmp); return smtp->status_code;

}

void smtp_header_clear_all(struct smtp *const smtp)

{

for (size_t i = 0; i < smtp->num_headers; ++i)

{

struct smtp_header *header = &smtp->header_list[i]; free(header->key);

free(header->value);

}

free(smtp->header_list); smtp->header_list = NULL; smtp->num_headers = 0;

}

enum smtp_status_code smtp_address_add(struct smtp *const smtp, enum smtp_address_type type, const char *const email, const char *const name)

{

struct smtp_address *new_address_list, *new_address; size_t num_address_inc;

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

if (smtp_address_validate_email(email) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); if (name && smtp_address_validate_name(name) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM);

if (smtp_si_add_size_t(smtp->num_address, 1, &num_address_inc)) return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

new_address_list =

smtp_reallocarray(smtp->address_list, num_address_inc, sizeof(*new_address_list)); if (new_address_list == NULL)

return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM); new_address = &new_address_list[smtp->num_address]; smtp->address_list = new_address_list;

new_address->type = type; new_address->email = smtp_strdup(email); new_address->name = smtp_strdup(name);

if (new_address->email == NULL || (new_address->name == NULL && name))

{

free(new_address->email); free(new_address->name);

48

smtp_lib/smtp_lib.c

return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

}

smtp->num_address = num_address_inc; return smtp->status_code;

}

void smtp_address_clear_all(struct smtp *const smtp)

{

for (size_t i = 0; i < smtp->num_address; ++i)

{

struct smtp_address *address = &smtp->address_list[i]; free(address->email);

free(address->name);

}

free(smtp->address_list); smtp->address_list = NULL; smtp->num_address = 0;

}

enum smtp_status_code smtp_attachment_add_path(struct smtp *const smtp, const char *const name, const char *const path)

{

char *data;

size_t bytes_read;

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

errno = 0;

if ((data = smtp_file_get_contents(path, &bytes_read)) == NULL)

return smtp_status_code_set(smtp, errno == ENOMEM ? SMTP_STATUS_NOMEM : SMTP_STATUS_FILE); smtp_attachment_add_mem(smtp, name, data, bytes_read);

free(data);

return smtp->status_code;

}

enum smtp_status_code smtp_attachment_add_fp(struct smtp *const smtp, const char *const name, FILE *fp)

{

char *data;

size_t bytes_read;

if (smtp->status_code != SMTP_STATUS_OK) return smtp->status_code;

errno = 0;

if ((data = smtp_ffile_get_contents(fp, &bytes_read)) == NULL)

return smtp_status_code_set(smtp, errno == ENOMEM ? SMTP_STATUS_NOMEM : SMTP_STATUS_FILE); smtp_attachment_add_mem(smtp, name, data, bytes_read);

free(data);

return smtp->status_code;

}

enum smtp_status_code smtp_attachment_add_mem(struct smtp *const smtp, const char *const name, const void *const data, size_t datasz)

{

size_t num_attachment_inc; char *b64_encode;

struct smtp_attachment *new_attachment_list, *new_attachment; if (smtp->status_code != SMTP_STATUS_OK)

return smtp->status_code;

if (smtp_attachment_validate_name(name) < 0)

return smtp_status_code_set(smtp, SMTP_STATUS_PARAM); if (datasz == SIZE_MAX)

datasz = strlen(data);

if (smtp_si_add_size_t(smtp->num_attachment, 1, &num_attachment_inc) || (new_attachment_list = smtp_reallocarray(smtp->attachment_list, num_attachment_inc,

sizeof(*new_attachment_list))) == NULL) return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

smtp->attachment_list = new_attachment_list;

new_attachment = &new_attachment_list[smtp->num_attachment]; new_attachment->name = smtp_strdup(name);

b64_encode = smtp_base64_encode(data, datasz);

if (new_attachment->name == NULL || b64_encode == NULL)

{

free(new_attachment->name); free(b64_encode);

return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

}

new_attachment->b64_data = smtp_chunk_split(b64_encode, SMTP_LINE_MAX, "\r\n"); free(b64_encode);

if (new_attachment->b64_data == NULL)

{

free(new_attachment->name);

return smtp_status_code_set(smtp, SMTP_STATUS_NOMEM);

}

smtp->num_attachment = num_attachment_inc; return smtp->status_code;

}

49

smtp_lib/smtp_lib.c

void smtp_attachment_clear_all(struct smtp *const smtp)

{

for (size_t i = 0; i < smtp->num_attachment; ++i)

{

struct smtp_attachment *attachment = &smtp->attachment_list[i]; free(attachment->name);

free(attachment->b64_data);

}

free(smtp->attachment_list); smtp->attachment_list = NULL; smtp->num_attachment = 0;

}

Таблица 4. Сборочный файл SMTP-библиотеки «smtp_lib/WinMakefile» /

«smtp_lib/LinuxMakefile»

smtp_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 -DSMTP_OPENSSL

all: smtp_lib.o

smtp_lib.o: smtp_lib.c smtp_lib.h

$(CC) $(CFLAGS) ./smtp_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 smtp_lib.o smtp_lib.d

smtp_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 -DSMTP_OPENSSL

all: smtp_lib.o

smtp_lib.o: smtp_lib.c smtp_lib.h

$(CC) $(CFLAGS) ./smtp_lib.c -c

clean:

rm -rf smtp_lib.o smtp_lib.d

50