--- lmtp.c 2001/07/17 12:40:07 1.1
+++ lmtp.c 2001/07/23 12:14:06 1.2
@@ -1,54 +1,138 @@
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
#include "lmtp.h"
#include "lmtp_p.h"
-static int readline(lmtp_readline_t *rl, int fd, char *buf, size_t buflen)
+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;
- ssize_t rc;
- char c = '\0', *cp;
+ char c;
+ lmtp_readline_t *rl = &lmtp->rl;
- cp = (char *)buf;
- for (n = 1; n < buflen; n++) {
+ for (n = 0; n < buflen-1;) {
/* fetch one character (but read more) */
- rc = 1;
if (rl->rl_cnt <= 0) {
- if ((rl->rl_cnt = rl->rl_read(fd, rl->rl_buf, LMTP_READLINE_MAXLEN)) < 0)
- rc = -1;
- else if (rl->rl_cnt == 0)
- rc = 0;
- else
- rl->rl_bufptr = rl->rl_buf;
- }
- if (rc == 1) {
- rl->rl_cnt--;
- c = *rl->rl_bufptr++;
+ 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 */
- if (rc == 1) {
- if (c == '\r') {
- n--;
- continue;
- }
- *cp++ = c;
- if (c == '\n')
- break;
- }
- else if (rc == 0) {
- if (n == 1)
- return 0;
- else
- break;
- }
- else
- return -1;
+ 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 */
+
}
- *cp = '\0';
- return n;
+ 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)
@@ -57,36 +141,162 @@
if ((lmtp = (lmtp_t *)malloc(sizeof(lmtp_t))) == NULL)
return NULL;
- lmtp->io.select = select;
- lmtp->io.read = read;
- lmtp->io.write = write;
+
+ 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_read = lmtp->io.read;
- /* lmtp->dispatch = ... */
+ 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_request(lmtp_t *lmtp, lmtp_req_t *req)
+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_result(lmtp_t *lmtp, lmtp_res_t *res)
+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;
}
@@ -101,10 +311,37 @@
return str;
}
-lmtp_cb_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_type_t type, lmtp_cb_t *cb, void *ctx)
+lmtp_rc_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_cb_t cb, void *ctx, lmtp_cb_t *oldcb, void **oldctx)
{
- lmtp_cb_t old = NULL;
- return old;
+ 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)
@@ -113,16 +350,44 @@
lmtp_res_t res;
lmtp_rc_t rc;
int i;
-
- while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK) {
- for (i = 0; lmtp->dispatch[i]->verb != NULL; i++) {
- if (strcmp(req.verb, lmtp->dispatch[i]->verb) == 0) {
- lmtp->dispatch[i]->cb(&lmtp->io, &req, &res, lmtp->dispatch[i]->cbctx);
- if ((rc = lmtp_result(lmtp, &res)) != LMTP_OK)
- return rc;
- }
+ 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;
}
|