configuration file handling polished and fixed;

This commit is contained in:
Alexander Vdolainen 2026-10-13 01:56:01 +03:00
parent 2d900b0839
commit 3f0f915d99
8 changed files with 108 additions and 48 deletions

View File

@ -1,23 +0,0 @@
/* config.h - Subrelayd configuration loader */
#ifndef __CONFIG_H__
#define __CONFIG_H__
typedef struct subrelay_config {
char *maillist_addr; /* C001.1 */
char *imap_host; /* C001.2 */
int imap_port; /* C001.2 */
char *smtp_host; /* C001.3 */
int smtp_port; /* C001.3 */
char *account_pass; /* C001.4 */
char *maillist_name; /* C001.5 */
char *template_path; /* C001.6 */
char *run_user; /* C001.7 */
char *run_group; /* C001.7 */
char *socket_path; /* C001.8 */
char *subscribers_file;/* C001.9 */
} subrelay_config_t;
int config_load(subrelay_config_t *cfg, const char *filepath);
void config_free(subrelay_config_t *cfg);
#endif /* __CONFIG_H__ */

View File

@ -0,0 +1,41 @@
/*
* 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 2026 <alex@vapaa.xyz>
* subrelay: a minimalistic maillist server based on external IMAPs/SMTPs
*
* configuration.h: configuration file handling
*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
typedef struct subrelay_config {
char *maillist_addr; /* C001.1 */
char *imap_host; /* C001.2 */
int imap_port; /* C001.2 */
char *smtp_host; /* C001.3 */
int smtp_port; /* C001.3 */
char *account_pass; /* C001.4 */
char *maillist_name; /* C001.5 */
char *template_path; /* C001.6 */
char *run_user; /* C001.7 */
char *run_group; /* C001.7 */
char *socket_path; /* C001.8 */
char *subscribers_file;/* C001.9 */
} subrelay_config_t;
int configuration_load(subrelay_config_t *cfg, const char *filepath);
void configuration_free(subrelay_config_t *cfg);
#endif /* __CONFIG_H__ */

View File

@ -20,7 +20,7 @@
#ifndef __LOCAL_H__ #ifndef __LOCAL_H__
#define __LOCAL_H__ #define __LOCAL_H__
#include <ilib/config.h> #include <ilib/configuration.h>
#include <ilib/subscribers.h> #include <ilib/subscribers.h>
#include <ilib/pending.h> #include <ilib/pending.h>

View File

@ -4,7 +4,7 @@
#include <stddef.h> #include <stddef.h>
#include <ilib/config.h> #include <ilib/configuration.h>
typedef struct mail_message { typedef struct mail_message {
char *raw_data; /* Full raw RFC 822 byte payload */ char *raw_data; /* Full raw RFC 822 byte payload */

View File

@ -2,7 +2,7 @@
#ifndef __MAIL_PROCESSOR_H__ #ifndef __MAIL_PROCESSOR_H__
#define __MAIL_PROCESSOR_H__ #define __MAIL_PROCESSOR_H__
#include <ilib/config.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/mail_engine.h>

View File

@ -1,7 +1,7 @@
bin_PROGRAMS = subrelayd sbrlctrl bin_PROGRAMS = subrelayd sbrlctrl
noinst_HEADERS = \ noinst_HEADERS = \
$(top_srcdir)/include/ilib/config.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/mail_engine.h \
$(top_srcdir)/include/ilib/mail_processor.h \ $(top_srcdir)/include/ilib/mail_processor.h \
@ -16,7 +16,7 @@ subrelayd_SOURCES = \
splay.c \ splay.c \
subscribers.c \ subscribers.c \
pending.c \ pending.c \
config.c \ configuration.c \
template.c \ template.c \
mail_engine.c \ mail_engine.c \
mail_processor.c \ mail_processor.c \

View File

@ -1,11 +1,32 @@
/* config.c - Key-value configuration parser for subrelayd */ /*
* 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 2026 <alex@vapaa.xyz>
* subrelay: a minimalistic maillist server based on external IMAPs/SMTPs
*
* configuration.c: configuration file handling
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <ilib/config.h> #include <ilib/configuration.h>
#define LINE_MAXLEN 1024
static char *trim(char *str) static char *trim(char *str)
{ {
@ -33,19 +54,32 @@ static char *strip_quotes(char *val)
return val; return val;
} }
static int text2port(const char *value, int *port)
{
char *end;
long n = strtol(value, &end, 10);
if (value == end || errno == ERANGE || *trim(end) != '\0' ||
n < 1 || n > 65535)
return -EINVAL;
*port = (int)n;
return 0;
}
static void set_string_param(char **target, const char *val) static void set_string_param(char **target, const char *val)
{ {
if (*target) free(*target); if (*target) free(*target);
*target = strdup(val); *target = strdup(val);
} }
int config_load(subrelay_config_t *cfg, const char *filepath) int configuration_load(subrelay_config_t *cfg, const char *filepath)
{ {
if (!cfg || !filepath) return -EINVAL; if (!cfg || !filepath) return -EINVAL;
memset(cfg, 0, sizeof(subrelay_config_t)); memset(cfg, 0, sizeof(subrelay_config_t));
/* Apply sensible defaults */ /* default values */
cfg->imap_port = 993; cfg->imap_port = 993;
cfg->smtp_port = 587; cfg->smtp_port = 587;
cfg->socket_path = strdup("/var/run/subrelayd.sock"); cfg->socket_path = strdup("/var/run/subrelayd.sock");
@ -56,20 +90,20 @@ int config_load(subrelay_config_t *cfg, const char *filepath)
FILE *fp = fopen(filepath, "r"); FILE *fp = fopen(filepath, "r");
if (!fp) return -errno; if (!fp) return -errno;
char line[1024]; char line[LINE_MAXLEN];
int line_num = 0; int line_num = 0;
while (fgets(line, sizeof(line), fp)) { while (fgets(line, sizeof(line), fp)) {
line_num++; line_num++;
/* Rule C004.1: Everything after '#' is ignored */ /* requirement C004.1: everything after '#' is ignored -- comment */
char *comment = strchr(line, '#'); char *comment = strchr(line, '#');
if (comment) *comment = '\0'; if (comment) *comment = '\0';
char *trimmed = trim(line); char *trimmed = trim(line);
if (*trimmed == '\0') continue; /* Skip empty/comment lines */ if (*trimmed == '\0') continue; /* skip empty/comment lines */
/* Rule C004.2: <KEY>: '<VALUE>' */ /* requirement C004.2: <KEY>: '<VALUE>' */
char *colon = strchr(trimmed, ':'); char *colon = strchr(trimmed, ':');
if (!colon) { if (!colon) {
fprintf(stderr, "config: syntax error at line %d: missing ':'\n", line_num); fprintf(stderr, "config: syntax error at line %d: missing ':'\n", line_num);
@ -85,11 +119,17 @@ int config_load(subrelay_config_t *cfg, const char *filepath)
} else if (strcasecmp(key, "IMAP_HOST") == 0) { } else if (strcasecmp(key, "IMAP_HOST") == 0) {
set_string_param(&cfg->imap_host, value); set_string_param(&cfg->imap_host, value);
} else if (strcasecmp(key, "IMAP_PORT") == 0) { } else if (strcasecmp(key, "IMAP_PORT") == 0) {
cfg->imap_port = atoi(value); if (text2port(value, &(cfg->imap_port))) {
fprintf(stderr, "config: IMAP port value is invalid at %d\n", line_num);
goto syntax_error;
}
} else if (strcasecmp(key, "SMTP_HOST") == 0) { } else if (strcasecmp(key, "SMTP_HOST") == 0) {
set_string_param(&cfg->smtp_host, value); set_string_param(&cfg->smtp_host, value);
} else if (strcasecmp(key, "SMTP_PORT") == 0) { } else if (strcasecmp(key, "SMTP_PORT") == 0) {
cfg->smtp_port = atoi(value); if (text2port(value, &(cfg->smtp_port))) {
fprintf(stderr, "config: SMTP port value is invalid at %d\n", line_num);
goto syntax_error;
}
} else if (strcasecmp(key, "ACCOUNT_PASS") == 0) { } else if (strcasecmp(key, "ACCOUNT_PASS") == 0) {
set_string_param(&cfg->account_pass, value); set_string_param(&cfg->account_pass, value);
} else if (strcasecmp(key, "MAILLIST_NAME") == 0) { } else if (strcasecmp(key, "MAILLIST_NAME") == 0) {
@ -112,14 +152,15 @@ int config_load(subrelay_config_t *cfg, const char *filepath)
/* Validate essential parameters */ /* Validate essential parameters */
if (!cfg->maillist_addr || !cfg->imap_host || !cfg->smtp_host || !cfg->account_pass) { if (!cfg->maillist_addr || !cfg->imap_host || !cfg->smtp_host || !cfg->account_pass) {
fprintf(stderr, "config error: missing mandatory credentials (MAILLIST_ADDR, IMAP_HOST, SMTP_HOST, ACCOUNT_PASS)\n"); fprintf(stderr, "config error: missing mandatory credentials (MAILLIST_ADDR, IMAP_HOST, SMTP_HOST, ACCOUNT_PASS)\n");
config_free(cfg); syntax_error:
configuration_free(cfg);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
void config_free(subrelay_config_t *cfg) void configuration_free(subrelay_config_t *cfg)
{ {
if (!cfg) return; if (!cfg) return;
@ -135,4 +176,5 @@ void config_free(subrelay_config_t *cfg)
if (cfg->subscribers_file)free(cfg->subscribers_file); if (cfg->subscribers_file)free(cfg->subscribers_file);
memset(cfg, 0, sizeof(subrelay_config_t)); memset(cfg, 0, sizeof(subrelay_config_t));
return;
} }

View File

@ -30,7 +30,7 @@
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <ilib/config.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/mail_engine.h>
@ -127,7 +127,7 @@ int main(int argc, char **argv)
/* 1. Load Configuration */ /* 1. Load Configuration */
subrelay_config_t cfg; subrelay_config_t cfg;
if (config_load(&cfg, config_path) != 0) { if (configuration_load(&cfg, config_path) != 0) {
fprintf(stderr, "error: failed to load config file '%s'\n", config_path); fprintf(stderr, "error: failed to load config file '%s'\n", config_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -136,7 +136,7 @@ int main(int argc, char **argv)
subscriber_pool_t subscribers; subscriber_pool_t subscribers;
if (subscriber_pool_init(&subscribers, cfg.subscribers_file) != 0) { if (subscriber_pool_init(&subscribers, cfg.subscribers_file) != 0) {
fprintf(stderr, "error: failed to initialize subscriber pool\n"); fprintf(stderr, "error: failed to initialize subscriber pool\n");
config_free(&cfg); configuration_free(&cfg);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
subscriber_load_file(&subscribers); subscriber_load_file(&subscribers);
@ -148,7 +148,7 @@ int main(int argc, char **argv)
if (pending_pool_init(&pending, pending_path, 86400) != 0) { if (pending_pool_init(&pending, pending_path, 86400) != 0) {
fprintf(stderr, "error: failed to initialize pending pool\n"); fprintf(stderr, "error: failed to initialize pending pool\n");
subscriber_pool_destroy(&subscribers); subscriber_pool_destroy(&subscribers);
config_free(&cfg); configuration_free(&cfg);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
pending_load_file(&pending); pending_load_file(&pending);
@ -159,7 +159,7 @@ int main(int argc, char **argv)
fprintf(stderr, "error: failed to bind IPC socket '%s'\n", cfg.socket_path); fprintf(stderr, "error: failed to bind IPC socket '%s'\n", cfg.socket_path);
pending_pool_destroy(&pending); pending_pool_destroy(&pending);
subscriber_pool_destroy(&subscribers); subscriber_pool_destroy(&subscribers);
config_free(&cfg); configuration_free(&cfg);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -169,7 +169,7 @@ int main(int argc, char **argv)
local_ctrl_destroy(&ipc); local_ctrl_destroy(&ipc);
pending_pool_destroy(&pending); pending_pool_destroy(&pending);
subscriber_pool_destroy(&subscribers); subscriber_pool_destroy(&subscribers);
config_free(&cfg); configuration_free(&cfg);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -179,7 +179,7 @@ int main(int argc, char **argv)
local_ctrl_destroy(&ipc); local_ctrl_destroy(&ipc);
pending_pool_destroy(&pending); pending_pool_destroy(&pending);
subscriber_pool_destroy(&subscribers); subscriber_pool_destroy(&subscribers);
config_free(&cfg); configuration_free(&cfg);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -231,7 +231,7 @@ int main(int argc, char **argv)
local_ctrl_destroy(&ipc); local_ctrl_destroy(&ipc);
pending_pool_destroy(&pending); pending_pool_destroy(&pending);
subscriber_pool_destroy(&subscribers); subscriber_pool_destroy(&subscribers);
config_free(&cfg); configuration_free(&cfg);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }