OSSP CVS Repository

ossp - ossp-pkg/lmtp2nntp/lmtp.c 1.3
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

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;
}


CVSTrac 2.0.1