diff --git a/Makefile b/Makefile index 4c619d5..e6c44c5 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # .PHONY: all -all: bin libremail endpoint +all: bin libremail tools endpoint .PHONY: bin bin: @@ -14,6 +14,10 @@ bin: libremail: cd libremail/; make +.PHONY: tools +tools: + cd tools/; make + .PHONY: endpoint endpoint: cd endpoint/; make @@ -22,3 +26,4 @@ endpoint: clean: cd endpoint/; make clean cd libremail/; make clean + cd tools/; make clean diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..b0d5139 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,12 @@ +# +# Copyright (c) 2026, Chloe Moffett +# Provided under the BSD-3 clause +# + +.PHONY: all +all: + cd mailutil/; make + +.PHONY: clean +clean: + cd mailutil/; make clean diff --git a/tools/mailutil/Makefile b/tools/mailutil/Makefile new file mode 100644 index 0000000..07cd3c1 --- /dev/null +++ b/tools/mailutil/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (c) 2026, Chloe Moffett +# Provided under the BSD-3 clause +# + +CFILES = $(shell find . -name "*.c") +OFILES = $(CFILES:.c=.o) +OFILES += $(shell find ../../libremail/ -name "*.o") +DFILES = $(CFILES:.c=.d) + +OUTPUT = ../../bin/mailutil +CC = gcc + +CFLAGS = \ + -Wall \ + -pedantic \ + -MMD \ + -Iinc \ + -I../../libremail/inc + +.PHONY: all +all: $(OUTPUT) $(OFILES) + +.PHONY: $(OUTPUT) +$(OUTPUT): $(OFILES) + $(CC) $^ -o $@ + +-include $(DFILES) +%.o: %.c + $(CC) -c $< $(CFLAGS) -o $@ + +.PHONY: clean +clean: + rm -f $(OFILES) $(DFILES) diff --git a/tools/mailutil/core/mailutil.c b/tools/mailutil/core/mailutil.c new file mode 100644 index 0000000..0c12269 --- /dev/null +++ b/tools/mailutil/core/mailutil.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026, Chloe Moffett + * Provided under the BSD-3 clause + */ + +#include +#include +#include +#include +#include "libremail/file.h" + +/* Maximum command components */ +#define CMD_MAX_CNP 6 + +/* Mailutil version */ +#define MAILUTIL_VERSION "0.0.1" + +#define DEFAULT_MAILBOX_MODE 0600 + +/* + * Macro used to assert that the command list count is not + * zero to avoid code duplication. + */ +#define ASSERT_COUNT(CNT) \ + if ((CNT) == 0) { \ + printf("error: cmdlist count is zero\n"); \ + return -1; \ + } + +/* + * Macro used to ensure that overflows don't happen + */ +#define ASSERT_COUNT_N(CNT, MAX) \ + if ((CNT) < (MAX)) { \ + printf("error: too few parameters for command\n"); \ + return -1; \ + } + +static void +help(void) +{ + printf("usage: ./mailutil [... flags] [verb] [...]\n"); + printf("... [-h] Display this help menu\n"); + printf("... [-v] Display the version\n"); +} + +static void +version(void) +{ + printf("Version v%s\n", MAILUTIL_VERSION); +} + +/* + * Parse a "mailbox create" command + * + * @cmdlist: Command list to parse + * @count: Number of entries inside command list + * + * Returns zero on success + */ +static int +cmd_mailbox_create(const char *cmdlist[CMD_MAX_CNP], size_t count) +{ + char pathbuf[64]; + + ASSERT_COUNT_N(count, 3); + + snprintf(pathbuf, sizeof(pathbuf), "/var/mail/%s", cmdlist[2]); + + /* Create the mailbox */ + if (try_mkdir(pathbuf, DEFAULT_MAILBOX_MODE) < 0) { + printf("fatal: failed to create mailbox\n"); + perror("try_mkdir"); + return -1; + } + + printf("created mailbox '%s' @ %s\n", + cmdlist[2], pathbuf); + + return 0; +} + +/* + * Parse a "mailbox" command + * + * @cmdlist: Command list to parse + * @count: Number of entries inside command list + * + * Returns zero on success + */ +static int +cmd_mailbox(const char *cmdlist[CMD_MAX_CNP], size_t count) +{ + ASSERT_COUNT_N(count, 2); + + if (strcmp(cmdlist[1], "create") == 0) { + return cmd_mailbox_create(cmdlist, count); + } + + printf("error: bad parameter\n"); + return -1; +} + +/* + * Parse a command given from the command line + * + * @cmdlist: Command list to parse + * @count: Number of entries inside command list + * + * Returns zero on success + */ +static int +parse_cmd(const char *cmdlist[CMD_MAX_CNP], size_t count) +{ + ASSERT_COUNT(count); + + if (strcmp(cmdlist[0], "mailbox") == 0) { + return cmd_mailbox(cmdlist, count); + } + + printf("error: bad parameter\n"); + return -1; +} + +int +main(int argc, char **argv) +{ + int opt; + const char *cmdlist[CMD_MAX_CNP]; + size_t cmdlist_i = 0; + + if (argc < 2) { + printf("fatal: too few arguments\n"); + help(); + return -1; + } + + while ((opt = getopt(argc, argv, "hv")) != -1) { + switch (opt) { + case 'h': + help(); + return -1; + case 'v': + version(); + return 0; + } + } + + /* Accumulate command line parameters */ + while (optind < argc) { + cmdlist[cmdlist_i++] = argv[optind++]; + } + + if (parse_cmd(cmdlist, cmdlist_i) < 0) { + printf("fatal: error parsing command list\n"); + return -1; + } + + return 0; +}