/* standard headers */ #include #include #include #include #include /* third-party headers */ // #include "str.h" /* own headers */ #include "lmtp.h" #include "lmtp_p.h" int verbindex(lmtp_t *lmtp, char *verb); lmtp_rc_t lmtp_cb_default(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); 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, zero is empty line * return = -1 end of file, no chars inside * return = -2 buffer overrun, chars inside but no newline seen * return = -3 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 -3; /* error see errno */ if (rl->rl_cnt == 0) return -1; /* 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)) ? -2 : 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 ? io->select : select; lmtp->io.read = io->read ? io->read : read; lmtp->io.write = io->write ? io->write : 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; lmtp_register(lmtp, "", lmtp_cb_default, NULL, NULL, 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 == -3) return LMTP_ERR_SYSTEM; /* io error (errno), buffer content undefined */ if(rc == -2) return LMTP_ERR_OVERFLOW; /* buffer overrun, chars inside but no newline seen */ if(rc == -1) return LMTP_EOF; /* end of file, no chars inside */ return LMTP_OK; } lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char **cppBuf, size_t maxlen) { /* 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; char *cpBuf; /* buffer as a whole */ char *cpPtr; /* write cursor */ char *cpLine; /* start of the current line (see offsetline) */ size_t nBuf; /* size of buffer, doubled through realloc until maximum reached */ size_t offset; /* required when cpBuf changed through realloc */ size_t offsetline; /* memorizing start of line when reallocing in the middle of a line */ nBuf = 4096; if ((cpBuf = (char *)malloc(nBuf)) == NULL) return LMTP_ERR_MEM; *cppBuf = cpBuf; cpPtr = cpBuf; cpLine = cpBuf; while (1) { // printf("DEBUG: cpPtr-cpBuf=%d, nBuf=%d, nBuf-(cpPtr-cpBuf)=%d\n", cpPtr-cpBuf, nBuf, nBuf-(cpPtr-cpBuf)); rc = lmtp_readline(lmtp, cpPtr, nBuf-(cpPtr-cpBuf)); // printf("DEBUG: lmtp_readline()=***%s***, rc=%d\n", cpPtr, rc); if (rc == LMTP_ERR_OVERFLOW) { if (nBuf == maxlen) return LMTP_ERR_OVERFLOW; offset = nBuf-1; offsetline = cpLine - cpBuf; nBuf *= 2; if (nBuf > maxlen) nBuf = maxlen; if ((cpBuf = (char *)realloc(cpBuf, nBuf)) == NULL) { free(cpBuf); return LMTP_ERR_MEM; } *cppBuf = cpBuf; cpPtr = cpBuf + offset; cpLine = cpBuf + offsetline; } else if (rc == LMTP_OK) { if (strcmp(cpLine, ".") == 0) { // printf("DEBUG: \".\" found ***%s***\n", cpLine); *cpLine = '\0'; break; } if (*cpLine == '.') memmove(cpLine, cpLine+1, strlen(cpLine+1)+1); /* escaped dot */ cpPtr += strlen(cpPtr); *cpPtr++ = '\n'; *cpPtr = '\0'; cpLine = cpPtr; } else { /* rc == LMTP_ERR_SYSTEM, etc. */ break; } } 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) { if ((verblen = strlen(verb)) == 0) continue; if( (strlen(req->msg) >= verblen) //FIXME check for verb\s && (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 (strcasecmp(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 = LMTP_OK; char *verb; int i; res.statuscode = "220"; res.dsncode = NULL; res.statusmsg = "LMTP server ready (lmtp2nntp)"; if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK) return rc; while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK || (rc == LMTP_ERR_VERB)) { verb = req.verb; if (rc == LMTP_ERR_VERB) verb = ""; if ((i = verbindex(lmtp, verb)) == -1) return LMTP_ERR_VERB; rc = lmtp->dispatch[i]->cb(lmtp, &lmtp->io, &req, lmtp->dispatch[i]->ctx); if (rc != LMTP_OK) break; } return rc; } lmtp_rc_t lmtp_cb_default(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx) { lmtp_res_t res; lmtp_rc_t rc = LMTP_OK; res.statuscode = "500"; res.dsncode = "5.5.1"; res.statusmsg = "Command unrecognized."; lmtp_response(lmtp, &res); return rc; }