/****************************************************************************** * * Copyright (c) 2017-2019 by Löwenware Ltd * Please, refer LICENSE file for legal information * ******************************************************************************/ /** * @file mail.c * @author Ilja Kartašov * @brief Mail sending component source file * * @see https://lowenware.com/aisl/ */ #include #include #include #include #include #include "mail.h" struct payload { char now[40]; char *to; char *from; char *subject; char *msg_id; char *smtp_user; char *smtp_pass; char *smtp_url; char *data; size_t offset; size_t total; }; static size_t payload_feed(void *ptr, size_t size, size_t nmemb, void *userp) { struct payload *payload = (struct payload *)userp; size_t left; left = payload->total - payload->offset; if (left && (size = size*nmemb)) { if (size > left) size = left; memcpy(ptr, &payload->data[payload->offset], size); payload->offset += size; return size; } return 0; } static void payload__get_now(char *now, size_t size) { time_t time_now = time(NULL); struct tm tm; gmtime_r(&time_now, &tm); strftime(now, size, "%a, %d %b %Y %H:%M:%S GMT", &tm); } static char * payload__get_id(uuid_t uuid, const char *server) { size_t sz = strlen(server); char *result = malloc( sz + 3 + 36 + 1 ); if (result) { char * p =result; *(p++) = '<'; uuid_unparse(uuid, p); p += 36; *(p++) = '@'; strncpy(p, server, sz); p += sz; *(p++) = '>'; *p = 0; } return result; } static void payload_free(struct payload *payload) { if (payload->data) free(payload->data); if (payload->to) free(payload->to); if (payload->from) free(payload->from); if (payload->subject) free(payload->subject); if (payload->msg_id) free(payload->msg_id); if (payload->smtp_user) free(payload->smtp_user); if (payload->smtp_pass) free(payload->smtp_pass); if (payload->smtp_url) free(payload->smtp_url); free(payload); } static struct payload * payload_new(AxMail mail, struct ax_mail_smtp *smtp) { size_t total; struct payload *payload; if (!(payload = calloc(1, sizeof(struct payload)))) goto except; payload__get_now(payload->now, sizeof(payload->now)); if (cstuff_strcpy(&payload->to, mail->to) == -1) goto e_cleanup; if (cstuff_strcpy(&payload->from, mail->from) == -1) goto e_cleanup; if (cstuff_strcpy(&payload->smtp_user, smtp->user) == -1) goto e_cleanup; if (cstuff_strcpy(&payload->smtp_pass, smtp->pass) == -1) goto e_cleanup; if ( !(payload->msg_id = payload__get_id(mail->msg_id, smtp->host)) ) goto e_cleanup; if (cstuff_sprintf(&payload->smtp_url, "smtps://%s:%d/", smtp->host, smtp->port & 0xFFFF) == -1) { goto e_cleanup; } total = cstuff_sprintf( &payload->data, AX_MAIL_FORMAT , payload->now , payload->to , payload->from , mail->reply_to , payload->msg_id , mail->subject , mail->msg ); if (!(total > 0)) goto e_cleanup; payload->total = total; return payload; e_cleanup: payload_free(payload); except: return payload; } static void * ax_mail_execute(void *p_ctx) { struct payload * payload = (struct payload *)p_ctx; AislStatus status = AISL_MALLOC_ERROR; CURL *curl; struct curl_slist *rcpts; if ((curl = curl_easy_init()) != NULL) { curl_easy_setopt(curl, CURLOPT_USERNAME, payload->smtp_user); curl_easy_setopt(curl, CURLOPT_PASSWORD, payload->smtp_pass); curl_easy_setopt(curl, CURLOPT_URL, payload->smtp_url); curl_easy_setopt(curl, CURLOPT_USE_SSL, (long) CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_feed); curl_easy_setopt(curl, CURLOPT_READDATA, payload); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_MAIL_FROM, payload->from); if ((rcpts = curl_slist_append(NULL, payload->to)) != NULL) { CURLcode res; curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpts); if ((res = curl_easy_perform(curl)) != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n" , curl_easy_strerror(res)); status = AISL_INPUT_ERROR; } else { status = AISL_SUCCESS; } curl_slist_free_all(rcpts); } curl_easy_cleanup(curl); } return (void*)status; } AislStatus ax_mail_send(AxMail mail, struct ax_mail_smtp *smtp, int flags) { int rc; struct payload *payload; if (!(payload = payload_new(mail, smtp))) return AISL_MALLOC_ERROR; rc = pthread_create(&mail->thread, NULL, ax_mail_execute, (void*)payload); if (rc) { payload_free(payload); return AISL_SYSCALL_ERROR; } return AISL_SUCCESS; } AislStatus ax_mail_get_status(AxMail mail) { int rc; void * retval; rc = pthread_tryjoin_np(mail->thread, &retval); if (rc == 0) { rc = (AislStatus)retval; return rc; } else { return AISL_IDLE; } }