ossp-pkg/lmtp2nntp/lmtp.c
1.3
/* standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
/* 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;
}