diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..217a4a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +MKDIR_P := mkdir -p +CP := cp +MV := mv +CC := gcc +CCC := g++ +RM_RF = rm -rf + +# directories +CWD := $(realpath .) +BINDIR := $(CWD)/bin +BUILDDIR := $(CWD)/build +SRCDIR := $(CWD)/src +INCLUDEDIR := $(CWD)/include + +# flas +CFLAGS := -O2 -I$(INCLUDEDIR) -Wall -Wextra +LDFLAGS := -pthread + +# target files +DIRS_TARGET := $(BINDIR) $(BUILDDIR) +TARGET := $(BINDIR)/mailattach +SRCFILES := $(wildcard $(SRCDIR)/*.c) +OBJFILES := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCFILES)) + +# fancy targets +all: directories $(TARGET) + +directories: $(DIRS_TARGET) + +# less fancy targets + +$(DIRS_TARGET): + $(MKDIR_P) $@ + +$(TARGET): $(OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ /usr/lib/libmilter.a + +$(BUILDDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + $(RM_RF) $(DIRS_TARGET) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..be48f35 --- /dev/null +++ b/src/main.c @@ -0,0 +1,250 @@ +/* + * Mailattach - a program to remove attachments and replace them with links. + * Licensed under the Apache 2.0 License. Parts taken from the sendmail + * libmilter sample. License restrictions from their license may apply. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +struct mlfiPriv +{ + char *mlfi_fname; + FILE *mlfi_fp; +}; + +#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) + +static unsigned long mta_caps = 0; + +sfsistat mlfi_cleanup(SMFICTX* ctx, bool ok) { + sfsistat rstat = SMFIS_CONTINUE; + struct mlfiPriv *priv = MLFIPRIV; + char *p; + char host[512]; + char hbuf[1024]; + + if (priv == NULL) + return rstat; + + /* close the archive file */ + if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) + { + /* failed; we have to wait until later */ + rstat = SMFIS_TEMPFAIL; + (void) unlink(priv->mlfi_fname); + } + else if (ok) + { + /* add a header to the message announcing our presence */ + if (gethostname(host, sizeof host) < 0){ + snprintf(host, sizeof host, "localhost"); + } + p = strrchr(priv->mlfi_fname, '/'); + if (p == NULL) + p = priv->mlfi_fname; + else + p++; + snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); + smfi_addheader(ctx, "X-Archived", hbuf); + } + else + { + /* message was aborted -- delete the archive file */ + (void) unlink(priv->mlfi_fname); + } + + /* release private memory */ + free(priv->mlfi_fname); + free(priv); + smfi_setpriv(ctx, NULL); + + /* return status */ + return rstat; +} + + +sfsistat +mlfi_envfrom(ctx, envfrom) + SMFICTX *ctx; + char **envfrom; +{ + struct mlfiPriv *priv; + int fd = -1; + + /* allocate some private memory */ + priv = malloc(sizeof *priv); + if (priv == NULL) + { + /* can't accept this message right now */ + return SMFIS_TEMPFAIL; + } + memset(priv, '\0', sizeof *priv); + + /* open a file to store this message */ + priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); + if (priv->mlfi_fname == NULL) + { + free(priv); + return SMFIS_TEMPFAIL; + } + if ((fd = mkstemp(priv->mlfi_fname)) < 0 || + (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) + { + if (fd >= 0) + (void) close(fd); + free(priv->mlfi_fname); + free(priv); + return SMFIS_TEMPFAIL; + } + + /* save the private data */ + smfi_setpriv(ctx, priv); + + /* continue processing */ + return SMFIS_CONTINUE; +} + +sfsistat +mlfi_header(ctx, headerf, headerv) + SMFICTX *ctx; + char *headerf; + char *headerv; +{ + /* write the header to the log file */ + fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); + + /* continue processing */ + return ((mta_caps & SMFIP_NR_HDR) != 0) + ? SMFIS_NOREPLY : SMFIS_CONTINUE; +} + +sfsistat +mlfi_eoh(ctx) + SMFICTX *ctx; +{ + /* output the blank line between the header and the body */ + fprintf(MLFIPRIV->mlfi_fp, "\r\n"); + + /* continue processing */ + return SMFIS_CONTINUE; +} + +sfsistat mlfi_body(SMFICTX* ctx, unsigned char * bodyp, size_t bodylen) { + + /* output body block to log file */ + if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) + { + /* write failed */ + (void) mlfi_cleanup(ctx, false); + return SMFIS_TEMPFAIL; + } + + /* continue processing */ + return SMFIS_CONTINUE; +} + +sfsistat mlfi_eom(ctx) + SMFICTX *ctx; +{ + return mlfi_cleanup(ctx, true); +} + +sfsistat mlfi_close(ctx) + SMFICTX *ctx; +{ + return SMFIS_ACCEPT; +} + +sfsistat mlfi_abort(ctx) + SMFICTX *ctx; +{ + return mlfi_cleanup(ctx, false); +} + +sfsistat mlfi_unknown(ctx, cmd) + SMFICTX *ctx; + char *cmd; +{ + return SMFIS_CONTINUE; +} + +sfsistat mlfi_data(ctx) + SMFICTX *ctx; +{ + return SMFIS_CONTINUE; +} + +sfsistat +mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) + SMFICTX *ctx; + unsigned long f0; + unsigned long f1; + unsigned long f2; + unsigned long f3; + unsigned long *pf0; + unsigned long *pf1; + unsigned long *pf2; + unsigned long *pf3; +{ + /* milter actions: add headers */ + *pf0 = SMFIF_ADDHDRS; + + /* milter protocol steps: all but connect, HELO, RCPT */ + *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT; + mta_caps = f1; + if ((mta_caps & SMFIP_NR_HDR) != 0) + *pf1 |= SMFIP_NR_HDR; + *pf2 = 0; + *pf3 = 0; + return SMFIS_CONTINUE; +} + +struct smfiDesc smfilter = +{ + "mailattach", /* filter name */ + SMFI_VERSION, /* version code -- do not change */ + SMFIF_ADDHDRS, /* flags */ + NULL, /* connection info filter */ + NULL, /* SMTP HELO command filter */ + mlfi_envfrom, /* envelope sender filter */ + NULL, /* envelope recipient filter */ + mlfi_header, /* header filter */ + mlfi_eoh, /* end of header */ + mlfi_body, /* body block filter */ + mlfi_eom, /* end of message */ + mlfi_abort, /* message aborted */ + mlfi_close, /* connection cleanup */ + mlfi_unknown, /* unknown/unimplemented SMTP commands */ + mlfi_data, /* DATA command filter */ + mlfi_negotiate /* option negotiation at connection startup */ +}; + +/* The actual entry point for the program. Performs initialisation routines and + * then hands over to the libmilter main function */ +int main(){ + + const char* socket_location = "/var/run/mailattach"; + + + smfi_setconn("local:/var/run/mailattach"); + + if (smfi_register(smfilter) == MI_FAILURE) + { + fprintf(stderr, "smfi_register failed\n"); + exit(EX_UNAVAILABLE); + } + chmod(socket_location,0x1FF); + return smfi_main(); +}