#include #include #include #include #include #include "lmtp.h" #include "lmtp_p.h" lmtp_rc_t lmtp_cb_default(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); lmtp_rc_t lmtp_cb_lhlo(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); lmtp_rc_t lmtp_cb_quit(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); int verbindex(lmtp_t *lmtp, char *verb); /* * test a function * */ void lmtp_debug_dumplmtp(lmtp_t *lmtp); void test(void) { lmtp_t *lmtp; lmtp_rc_t rc; lmtp_req_t req; lmtp_res_t res; #define BUFLEN 100 char buf[BUFLEN]; printf("DEBUG: 01 lmtp_create\n"); lmtp = lmtp_create(STDIN_FILENO, STDOUT_FILENO, NULL); // printf("DEBUG: 02\n"); lmtp_debug_dumplmtp(lmtp); printf("DEBUG: 03 lmtp_register\n"); lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, NULL, NULL, NULL); printf("DEBUG: 03 lmtp_register\n"); lmtp_register(lmtp, "QUIT", lmtp_cb_quit, NULL, NULL, NULL); // printf("DEBUG: 04\n"); lmtp_debug_dumplmtp(lmtp); /* printf("DEBUG: 05 lmtp_response\n"); res.statuscode="123"; res.dsncode="1.2.3"; res.statusmsg="Hello,\nthis is a\nmultiline\nmessage"; lmtp_response(lmtp, &res); */ /* do { rc = lmtp_readline(lmtp, buf, BUFLEN); printf("DEBUG: 06 lmtp_readline=%d ***%s***\n", rc, buf); } while(rc == LMTP_OK); */ /* do { rc = lmtp_request(lmtp, &req); printf("DEBUG: 07 lmtp_request=%d ***%s***%s***\n", rc, req.verb, req.msg); } while(rc == LMTP_OK || rc == LMTP_ERR_VERB); */ printf("DEBUG: 08 lmtp_loop=%d\n", lmtp_loop(lmtp)); printf("DEBUG: 99.\n"); return; } void lmtp_debug_dumplmtp(lmtp_t *lmtp) { int i; printf("lmtp = %ld \n", (long)lmtp); printf("io.select = %ld \n", (long)lmtp->io.select); printf("io.read = %ld \n", (long)lmtp->io.read); printf("io.write = %ld \n", (long)lmtp->io.write); printf("rl.cnt = %d \n", lmtp->rl.rl_cnt); printf("rl.bufptr =*%39s*\n", lmtp->rl.rl_bufptr); printf("rl.buf =*%39s*\n", lmtp->rl.rl_buf); for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { printf("dispatch[i].verb =*%39s*\n", lmtp->dispatch[i]->verb); printf("dispatch[i].cb = %ld \n", (long)lmtp->dispatch[i]->cb); printf("dispatch[i].ctx = %ld \n", (long)lmtp->dispatch[i]->ctx); // printf("dispatch[i].msg =*%39s*\n", lmtp->dispatch[i]->msg); } printf("rfd = %d \n", lmtp->rfd); printf("wfd = %d \n", lmtp->wfd); return; } /*************************************************************************/ /* * return > 0 number of chars read * return = 0 end of file, no chars inside * return = -1 buffer overrun, chars inside but no newline seen * return = -2 io error (errno), buffer content undefined */ static int readline(lmtp_t *lmtp, char *buf, size_t buflen) { size_t n; char c; lmtp_readline_t *rl = &lmtp->rl; for (n = 0; n < buflen-1;) { /* fetch one character (but read more) */ if (rl->rl_cnt <= 0) { if ((rl->rl_cnt = lmtp->io.read(lmtp->rfd, rl->rl_buf, LMTP_LINE_MAXLEN)) < 0) return -2; /* error see errno */ if (rl->rl_cnt == 0) return 0; /* EOF */ rl->rl_bufptr = rl->rl_buf; } /* act on fetched character */ rl->rl_cnt--; c = *rl->rl_bufptr++; if (c == '\r') continue; /* skip copying CR */ if (c == '\n') break; /* end of line */ buf[n++] = c; /* output char into given buffer */ } buf[n] = '\0'; /* string termination */ return (n == (buflen-1)) ? -1 : n; } int verbindex(lmtp_t *lmtp, char *verb) { /* returns the index of the verb or -1 if verb not registered */ int i; for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { if (strcasecmp(lmtp->dispatch[i]->verb, verb) == 0) return i; } return -1; } lmtp_t *lmtp_create(int rfd, int wfd, lmtp_io_t *io) { lmtp_t *lmtp = NULL; if ((lmtp = (lmtp_t *)malloc(sizeof(lmtp_t))) == NULL) return NULL; if(io == NULL) { lmtp->io.select = select; lmtp->io.read = read; lmtp->io.write = write; } else { lmtp->io.select = io->select; lmtp->io.read = io->read; lmtp->io.write = io->write; } lmtp->rl.rl_cnt = 0; lmtp->rl.rl_bufptr = NULL; lmtp->rl.rl_buf[0] = '\0'; lmtp->rfd = rfd; lmtp->wfd = wfd; if ((lmtp->dispatch = (lmtp_dispatch_t **)malloc(sizeof(void *)*LMTP_MAXVERBS)) == NULL) return NULL; lmtp->dispatch[0] = NULL; return lmtp; } void lmtp_destroy(lmtp_t *lmtp) { free(lmtp); return; } lmtp_rc_t lmtp_readline(lmtp_t *lmtp, char *buf, size_t buflen) { lmtp_rc_t rc; rc = readline(lmtp, buf, buflen); if(rc == -2) return LMTP_ERR_SYSTEM; /* io error (errno), buffer content undefined */ if(rc == -1) return LMTP_ERR_OVERFLOW; /* buffer overrun, chars inside but no newline seen */ if(rc == 0) return LMTP_EOF; /* end of file, no chars inside */ return LMTP_OK; } lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char *buf, size_t buflen) { /* read lines until end of message, unescape dots * * RFC821 "Simple Mail Transfer Protocol" [...] 4.5.2. TRANSPARENCY [...] * When a line of mail text is received by the receiver-SMTP it checks * the line. If the line is composed of a single period it is the end of * mail. If the first character is a period and there are other * characters on the line, the first character is deleted. [...] */ lmtp_rc_t rc = LMTP_OK; do { rc = lmtp_readline(lmtp, buf, buflen); printf("DEBUG: 06 lmtp_readline=%d ***%s***\n", rc, buf); if (strcmp(buf, ".") == 0) break; } while(rc == LMTP_OK); /*FIXME escaping and more missing*/ return rc; } lmtp_rc_t lmtp_request(lmtp_t *lmtp, lmtp_req_t *req) { /* reads a line and attaches the buffer to req->msg; * pulls the verb out and attaches the verb to req->verb; * * LMTP_OK req->msg set, req->verb set means normal operation * LMTP_EOF req->msg set, req->verb NULL means eof * LMTP_ERR_OVERFLOW req->msg set, req->verb NULL means buf overflow * LMTP_ERR_SYSTEM req->msg set, req->verb NULL means system error * LMTP_ERR_VERB req->msg set, req->verb NULL means no verb seen */ lmtp_rc_t rc; char *verb; int verblen; int i; req->verb = NULL; if ((req->msg = (char *)malloc(LMTP_LINE_MAXLEN)) == (char *)NULL) return LMTP_ERR_MEM; if ((rc = lmtp_readline(lmtp, req->msg, LMTP_LINE_MAXLEN)) != LMTP_OK) return rc; for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { if ((verb = lmtp->dispatch[i]->verb) != NULL) { verblen = strlen(verb); if( (strlen(req->msg) >= verblen) && (strncasecmp(req->msg, verb, verblen) == 0) ) { req->verb = verb; return LMTP_OK; } } } return LMTP_ERR_VERB; } lmtp_rc_t lmtp_response(lmtp_t *lmtp, lmtp_res_t *res) { /* write the status message. For multiline status messages it is * neccessary to repeat the status and dsn codes for every line with a * dash after the status for every line but the last one */ lmtp_rc_t rc = LMTP_OK; int dash; int len; char *cpS; char *cpE; char formatbuf[LMTP_LINE_MAXLEN]; if ( strlen(res->statuscode) != 3 || !isdigit(res->statuscode[0]) || !isdigit(res->statuscode[1]) || !isdigit(res->statuscode[2]) ) return LMTP_ERR_ARG; if (res->dsncode != NULL) if ( (strlen(res->dsncode) != 5) || !isdigit(res->dsncode[0]) || (res->dsncode[1] != '.') || !isdigit(res->dsncode[2]) || (res->dsncode[3] != '.') || !isdigit(res->dsncode[4]) || (res->dsncode[0] != res->statuscode[0]) ) return LMTP_ERR_ARG; cpS = res->statusmsg; for (dash = 1; dash == 1; ) { if ((cpE = strchr(cpS, '\n')) == NULL) { cpE = cpS+strlen(cpS); dash = 0; } if (res->dsncode != NULL) len = sprintf(formatbuf, "%3.3s%c%5.5s ", res->statuscode, dash ? '-' : ' ', res->dsncode); else len = sprintf(formatbuf, "%3.3s%c", res->statuscode, dash ? '-' : ' '); strncpy(formatbuf+len, cpS, cpE-cpS); len += (cpE-cpS); formatbuf[len++] = '\n'; lmtp->io.write(lmtp->wfd, formatbuf, len); cpS = cpE+1; } return rc; } char **lmtp_message(lmtp_t *lmtp, char *verb) { int i; char **cpp = NULL; if ((i = verbindex(lmtp, verb)) >= 0) cpp = lmtp->dispatch[i]->msg; return cpp; } void lmtp_reset(lmtp_t *lmtp) { return; } char *lmtp_error(lmtp_t *lmtp, lmtp_rc_t rc) { char *str = NULL; return str; } lmtp_rc_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_cb_t cb, void *ctx, lmtp_cb_t *oldcb, void **oldctx) { lmtp_rc_t rc = LMTP_OK; int overload=0; /* overload returns old, no overload increases table */ int i; for (i = 0; lmtp->dispatch[i] != NULL; i++) { if (strcmp(verb, lmtp->dispatch[i]->verb) == 0) { overload=1; if(oldcb != NULL) *oldcb = lmtp->dispatch[i]->cb; if(oldctx != NULL) *oldctx = lmtp->dispatch[i]->ctx; break; } } if (i > LMTP_MAXVERBS-2) return LMTP_ERR_OVERFLOW; if (!overload) { if ((lmtp->dispatch[i] = (lmtp_dispatch_t *)malloc(sizeof(lmtp_dispatch_t))) == NULL) return LMTP_ERR_MEM; lmtp->dispatch[i+1] = NULL; if(oldcb != NULL) *oldcb = NULL; if(oldctx != NULL) *oldctx = NULL; } lmtp->dispatch[i]->verb = strdup(verb); lmtp->dispatch[i]->cb = cb; lmtp->dispatch[i]->ctx = ctx; lmtp->dispatch[i]->msg = NULL; return rc; } lmtp_rc_t lmtp_loop(lmtp_t *lmtp) { lmtp_req_t req; lmtp_res_t res; lmtp_rc_t rc; int i; while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK || (rc == LMTP_ERR_VERB)) { if (rc != LMTP_ERR_VERB) { if ((i = verbindex(lmtp, req.verb)) == -1) return LMTP_ERR_VERB; rc = lmtp->dispatch[i]->cb(&lmtp->io, &req, &res, lmtp->dispatch[i]->ctx); } else { rc = lmtp_cb_default(&lmtp->io, &req, &res, NULL); } if (lmtp_response(lmtp, &res) != LMTP_OK) break; if (rc != LMTP_OK) break; } return LMTP_OK; } lmtp_rc_t lmtp_cb_default(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) { lmtp_rc_t rc = LMTP_OK; res->statuscode = "500"; res->dsncode = "5.5.1"; res->statusmsg = "Command unrecognized."; return rc; } lmtp_rc_t lmtp_cb_lhlo(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) { lmtp_rc_t rc = LMTP_OK; res->statuscode = "250"; res->dsncode = NULL; /* DSN not used for greeting */ res->statusmsg = "ENHANCEDSTATUSCODES\nDSN"; /* RFC2034, RFC1894 */ return rc; } lmtp_rc_t lmtp_cb_quit(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) { lmtp_rc_t rc = LMTP_EOF; res->statuscode = "221"; res->dsncode = "2.0.0"; res->statusmsg = "Closing connection."; return rc; }