imap/smtp polishing;

This commit is contained in:
Alexander Vdolainen 2026-10-15 02:26:58 +03:00
parent f5a2d2b38f
commit f42c6b1dbe
6 changed files with 110 additions and 65 deletions

View File

@ -0,0 +1,56 @@
/*
* this is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* this is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
* (c) Alexander Vdolainen 2007, 2026 <alex@vapaa.xyz>
* subrelay: a minimalistic maillist server based on external IMAPs/SMTPs
*
* curl_imapsmtp.h: IMAPS SMTPs functions using libcurl
*
*/
#ifndef __MAIL_ENGINE_H__
#define __MAIL_ENGINE_H__
#include <stddef.h>
#include <ilib/configuration.h>
#define MAX_IMAP_RESPONSE (25u * 1024u * 1024u)
typedef struct mail_message {
char *raw_data; /* Full raw RFC 822 byte payload */
size_t size; /* Size of raw payload */
unsigned long uid; /* IMAP Unique Identifier */
} mail_message_t;
typedef struct mail_list_results {
unsigned long *uids;
size_t count;
} mail_list_results_t;
/* Initialize and cleanup global libcurl environment */
int mail_engine_global_init(void);
void mail_engine_global_cleanup(void);
/* IMAP Client API */
int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *results);
int imap_fetch_message(const subrelay_config_t *cfg, unsigned long uid, mail_message_t *msg);
int imap_expunge_message(const subrelay_config_t *cfg, unsigned long uid);
void mail_message_free(mail_message_t *msg);
void mail_list_results_free(mail_list_results_t *res);
/* SMTP Client API */
int smtp_send_message(const subrelay_config_t *cfg, const char **recipients,
size_t recipient_count, const char *raw_msg, size_t msg_len);
#endif /* __MAIL_ENGINE_H__ */

View File

@ -1,38 +0,0 @@
/* mail_engine.h - IMAP fetching & SMTP fan-out engine using libcurl */
#ifndef __MAIL_ENGINE_H__
#define __MAIL_ENGINE_H__
#include <stddef.h>
#include <ilib/configuration.h>
typedef struct mail_message {
char *raw_data; /* Full raw RFC 822 byte payload */
size_t size; /* Size of raw payload */
unsigned long uid; /* IMAP Unique Identifier */
} mail_message_t;
typedef struct mail_list_results {
unsigned long *uids;
size_t count;
} mail_list_results_t;
/* Initialize and cleanup global libcurl environment */
int mail_engine_global_init(void);
void mail_engine_global_cleanup(void);
/* IMAP Client API */
int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *results);
int imap_fetch_message(const subrelay_config_t *cfg, unsigned long uid, mail_message_t *msg);
int imap_expunge_message(const subrelay_config_t *cfg, unsigned long uid);
void mail_message_free(mail_message_t *msg);
void mail_list_results_free(mail_list_results_t *res);
/* SMTP Client API */
int smtp_send_message(const subrelay_config_t *cfg,
const char **recipients,
size_t recipient_count,
const char *raw_msg,
size_t msg_len);
#endif /* __MAIL_ENGINE_H__ */

View File

@ -24,7 +24,7 @@
#include <ilib/configuration.h> #include <ilib/configuration.h>
#include <ilib/subscribers.h> #include <ilib/subscribers.h>
#include <ilib/pending.h> #include <ilib/pending.h>
#include <ilib/mail_engine.h> #include <ilib/curl_imapsmtp.h>
typedef struct mail_processor { typedef struct mail_processor {
subrelay_config_t *cfg; subrelay_config_t *cfg;

View File

@ -3,7 +3,7 @@ bin_PROGRAMS = subrelayd sbrlctrl
noinst_HEADERS = \ noinst_HEADERS = \
$(top_srcdir)/include/ilib/configuration.h \ $(top_srcdir)/include/ilib/configuration.h \
$(top_srcdir)/include/ilib/local.h \ $(top_srcdir)/include/ilib/local.h \
$(top_srcdir)/include/ilib/mail_engine.h \ $(top_srcdir)/include/ilib/curl_imapsmtp.h \
$(top_srcdir)/include/ilib/mail_processor.h \ $(top_srcdir)/include/ilib/mail_processor.h \
$(top_srcdir)/include/ilib/pending.h \ $(top_srcdir)/include/ilib/pending.h \
$(top_srcdir)/include/ilib/splay.h \ $(top_srcdir)/include/ilib/splay.h \
@ -20,7 +20,7 @@ subrelayd_SOURCES = \
pending.c \ pending.c \
configuration.c \ configuration.c \
template.c \ template.c \
mail_engine.c \ curl_imapsmtp.c \
mail_processor.c \ mail_processor.c \
local.c local.c

View File

@ -1,11 +1,33 @@
/* mail_engine.c - libcurl IMAP and SMTP implementation */ /*
* this is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* this is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
* (c) Alexander Vdolainen 2007, 2026 <alex@vapaa.xyz>
* subrelay: a minimalistic maillist server based on external IMAPs/SMTPs
*
* curl_imapsmtp.c: IMAPS SMTPs functions using libcurl
*
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <curl/curl.h> #include <curl/curl.h>
#include <ilib/mail_engine.h> #include <ilib/curl_imapsmtp.h>
typedef struct memory_buffer { typedef struct memory_buffer {
char *data; char *data;
@ -20,24 +42,28 @@ typedef struct upload_buffer {
/* Callback for libcurl to append fetched HTTP/IMAP bytes into memory */ /* Callback for libcurl to append fetched HTTP/IMAP bytes into memory */
static size_t write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp) static size_t write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp)
{ {
size_t real_size = size * nmemb; memory_buffer_t *mem = userp;
memory_buffer_t *mem = (memory_buffer_t *)userp;
char *ptr = realloc(mem->data, mem->size + real_size + 1); if (size != 0 && nmemb > SIZE_MAX / size) return 0;
if (!ptr) return 0; /* Out of memory */ size_t n = size * nmemb;
if (n > MAX_IMAP_RESPONSE - mem->size) return 0;
char *ptr = realloc(mem->data, mem->size + n + 1);
if (!ptr) return 0;
mem->data = ptr; mem->data = ptr;
memcpy(&(mem->data[mem->size]), contents, real_size); memcpy(mem->data + mem->size, contents, n);
mem->size += real_size; mem->size += n;
mem->data[mem->size] = 0; mem->data[mem->size] = '\0';
return real_size; return n;
} }
/* Callback for libcurl to read bytes when sending outbound mail via SMTP */ /* Callback for libcurl to read bytes when sending outbound mail via SMTP */
static size_t read_payload_callback(char *buffer, size_t size, size_t nitems, void *userp) static size_t read_payload_callback(char *buffer, size_t size, size_t nitems, void *userp)
{ {
upload_buffer_t *upload = (upload_buffer_t *)userp; upload_buffer_t *upload = (upload_buffer_t *)userp;
if (size != 0 && nitems > SIZE_MAX / size) return CURL_READFUNC_ABORT;
size_t max_bytes = size * nitems; size_t max_bytes = size * nitems;
if (upload->bytes_remaining == 0) return 0; /* EOF */ if (upload->bytes_remaining == 0) return 0; /* EOF */
@ -61,9 +87,7 @@ void mail_engine_global_cleanup(void)
curl_global_cleanup(); curl_global_cleanup();
} }
/* -------------------------------------------------------------------------- */ /* IMAPs */
/* IMAP Operations */
/* -------------------------------------------------------------------------- */
int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *results) int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *results)
{ {
@ -77,6 +101,7 @@ int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *re
snprintf(url, sizeof(url), "imaps://%s:%d/INBOX", cfg->imap_host, cfg->imap_port); snprintf(url, sizeof(url), "imaps://%s:%d/INBOX", cfg->imap_host, cfg->imap_port);
memory_buffer_t buf = { calloc(1, 1), 0 }; memory_buffer_t buf = { calloc(1, 1), 0 };
if(!buf.data) return -ENOMEM;
curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERNAME, cfg->maillist_addr); curl_easy_setopt(curl, CURLOPT_USERNAME, cfg->maillist_addr);
@ -97,12 +122,18 @@ int imap_fetch_unseen_uids(const subrelay_config_t *cfg, mail_list_results_t *re
/* Parse IMAP response string (e.g., "* SEARCH 101 102 103\r\n") */ /* Parse IMAP response string (e.g., "* SEARCH 101 102 103\r\n") */
char *search_str = strstr(buf.data, "* SEARCH"); char *search_str = strstr(buf.data, "* SEARCH");
if (search_str) { if (search_str) {
search_str += 8; /* Skip "* SEARCH" */ search_str += 8; /* skip "* SEARCH" */
char *token = strtok(search_str, " \r\n"); char *token = strtok(search_str, " \r\n");
while (token) { while (token) {
unsigned long uid = strtoul(token, NULL, 10); unsigned long uid = strtoul(token, NULL, 10);
if (uid > 0) { if (uid > 0) {
results->uids = realloc(results->uids, sizeof(unsigned long) * (results->count + 1)); unsigned long *tmp = realloc(results->uids, sizeof(unsigned long) * (results->count + 1));
if(!tmp) {
free(buf.data);
mail_list_results_free(results);
return -ENOMEM;
}
results->uids = tmp;
results->uids[results->count++] = uid; results->uids[results->count++] = uid;
} }
token = strtok(NULL, " \r\n"); token = strtok(NULL, " \r\n");
@ -125,6 +156,7 @@ int imap_fetch_message(const subrelay_config_t *cfg, unsigned long uid, mail_mes
snprintf(url, sizeof(url), "imaps://%s:%d/INBOX;UID=%lu", cfg->imap_host, cfg->imap_port, uid); snprintf(url, sizeof(url), "imaps://%s:%d/INBOX;UID=%lu", cfg->imap_host, cfg->imap_port, uid);
memory_buffer_t buf = { calloc(1, 1), 0 }; memory_buffer_t buf = { calloc(1, 1), 0 };
if(!buf.data) return -ENOMEM;
curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERNAME, cfg->maillist_addr); curl_easy_setopt(curl, CURLOPT_USERNAME, cfg->maillist_addr);
@ -179,15 +211,10 @@ int imap_expunge_message(const subrelay_config_t *cfg, unsigned long uid)
return (res == CURLE_OK) ? 0 : -EIO; return (res == CURLE_OK) ? 0 : -EIO;
} }
/* -------------------------------------------------------------------------- */ /* SMTPS */
/* SMTP Operations */
/* -------------------------------------------------------------------------- */
int smtp_send_message(const subrelay_config_t *cfg, int smtp_send_message(const subrelay_config_t *cfg, const char **recipients, size_t recipient_count,
const char **recipients, const char *raw_msg, size_t msg_len)
size_t recipient_count,
const char *raw_msg,
size_t msg_len)
{ {
if (!cfg || !recipients || recipient_count == 0 || !raw_msg) return -EINVAL; if (!cfg || !recipients || recipient_count == 0 || !raw_msg) return -EINVAL;

View File

@ -33,7 +33,7 @@
#include <ilib/configuration.h> #include <ilib/configuration.h>
#include <ilib/subscribers.h> #include <ilib/subscribers.h>
#include <ilib/pending.h> #include <ilib/pending.h>
#include <ilib/mail_engine.h> #include <ilib/curl_imapsmtp.h>
#include <ilib/mail_processor.h> #include <ilib/mail_processor.h>
#include <ilib/local.h> #include <ilib/local.h>