#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "libmilter/mfapi.h"

// Compile with:
// cc -std=c99 -pedantic -Wall -Werror all_stages.c -lmilter -o all_stages

static sfsistat _negotiate(
    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
) {
    printf("NEGOTIATE\n");
    return SMFIS_ALL_OPTS;
}

static sfsistat _connect(SMFICTX *ctx, char *hostname, struct sockaddr *hostaddr) {
    printf("CONNECT\n");
    return SMFIS_CONTINUE;
}

static sfsistat _helo(SMFICTX *ctx, char *helohost) {
    printf("HELO\n");
    return SMFIS_CONTINUE;
}

static sfsistat _envfrom(SMFICTX *ctx, char **argv) {
    printf("MAIL\n");
    return SMFIS_CONTINUE;
}

static sfsistat _envrcpt(SMFICTX *ctx, char **argv) {
    printf("RCPT\n");
    return SMFIS_CONTINUE;
}

static sfsistat _data(SMFICTX *ctx) {
    printf("DATA\n");
    return SMFIS_CONTINUE;
}

static sfsistat _header(SMFICTX *ctx, char *headerf, char *headerv) {
    printf("HEADER\n");
    return SMFIS_CONTINUE;
}

static sfsistat _eoh(SMFICTX *ctx) {
    printf("EOH\n");
    return SMFIS_CONTINUE;
}

static sfsistat _body(SMFICTX *ctx, unsigned char *bodyp, size_t len) {
    printf("BODY\n");
    return SMFIS_CONTINUE;
}

static sfsistat _eom(SMFICTX *ctx) {
    printf("EOM\n");
    return SMFIS_CONTINUE;
}

static sfsistat _abort(SMFICTX *ctx) {
    printf("ABORT\n");
    return SMFIS_CONTINUE;
}

static sfsistat _close(SMFICTX *ctx) {
    printf("CLOSE\n");
    return SMFIS_CONTINUE;
}

static sfsistat _unknown(SMFICTX *ctx, const char *arg) {
    printf("UNKNOWN\n");
    return SMFIS_CONTINUE;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        char *program = argv[0];
        fprintf(stderr, "usage: %s <socket>\n", program ? program : "all_stages");
        exit(EXIT_FAILURE);
    }

    char *socket = argv[1];

    // Note: smfi_setdbg messages are printed to stdout. 6 is the max level.
    int status = smfi_setdbg(6);
    assert(status == MI_SUCCESS);

    status = smfi_setconn(socket);
    assert(status == MI_SUCCESS);

    status = smfi_register((struct smfiDesc) {
        .xxfi_name = "All Stages Milter",
        .xxfi_version = SMFI_VERSION,
        .xxfi_flags = 0,
        .xxfi_negotiate = _negotiate,
        .xxfi_connect = _connect,
        .xxfi_helo = _helo,
        .xxfi_envfrom = _envfrom,
        .xxfi_envrcpt = _envrcpt,
        .xxfi_data = _data,
        .xxfi_header = _header,
        .xxfi_eoh = _eoh,
        .xxfi_body = _body,
        .xxfi_eom = _eom,
        .xxfi_abort = _abort,
        .xxfi_close = _close,
        .xxfi_unknown = _unknown,
    });
    assert(status == MI_SUCCESS);

    return smfi_main();
}
