--- lmtp.c 2001/07/25 15:02:57 1.5
+++ lmtp.c 2001/07/25 19:51:12 1.6
@@ -1,96 +1,88 @@
+/*
+ * lmtp.c: LMTP library (implementation)
+ *
+ * Copyright (c) 2001 The OSSP Project (http://www.ossp.org/)
+ * Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/)
+ *
+ * This file is part of OSSP lmtp2nntp, an LMTP speaking local
+ * mailer which forwards mails as Usenet news articles via NNTP.
+ * It can be found at http://www.ossp.com/pkg/lmtp2nntp/.
+ *
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose with or without fee is hereby granted, provided that
+ * the above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
-/* standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
+#include <errno.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);
+/* maximum LMTP protocol line length */
+#define LMTP_LINE_MAXLEN 1024
+/* maximum number of verbs/callbacks to be registered */
+#define LMTP_MAXVERBS 32
-void lmtp_debug_dumplmtp(lmtp_t *lmtp)
-{
- int i;
+typedef struct {
+ int rl_cnt;
+ char *rl_bufptr;
+ char rl_buf[LMTP_LINE_MAXLEN];
+} lmtp_readline_t;
+
+typedef struct {
+ char *verb;
+ lmtp_cb_t cb;
+ void *ctx;
+} lmtp_dispatch_t;
+
+struct lmtp_st {
+ lmtp_io_t io; /* select, read, write functions */
+ lmtp_readline_t rl; /* a function to read in a single line */
+ lmtp_dispatch_t **dispatch; /* LMTP commands to be dispatched */
+ int rfd; /* file descriptor for reading */
+ int wfd; /* file descriptor for writing */
+};
- printf("lmtp = %ld \n", (long)lmtp);
- 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;
-}
-/*************************************************************************/
-
-static int readline(lmtp_t *lmtp, char *buf, size_t buflen)
+static int verbindex(lmtp_t *lmtp, char *verb)
{
- /* read a line
- *
- * NOTE: the underlying readline() already reduces any CR/LF combination
- * to a string terminating zero.
- *
- * 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
- */
-
- 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 */
+ /* returns the index of the verb or -1 if verb not registered */
+ int i;
- }
- buf[n] = '\0'; /* string termination */
- return (n == (buflen-1)) ? -2 : n;
+ for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++)
+ if (strcasecmp(lmtp->dispatch[i]->verb, verb) == 0)
+ return i;
+ return -1;
}
-int verbindex(lmtp_t *lmtp, char *verb)
+static lmtp_rc_t lmtp_cb_default(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx)
{
- /* returns the index of the verb or -1 if verb not registered */
+ lmtp_res_t res;
+ lmtp_rc_t rc = LMTP_OK;
- 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;
+ res.statuscode = "500";
+ res.dsncode = "5.5.1";
+ res.statusmsg = "Command unrecognized.";
+ lmtp_response(lmtp, &res);
+ return rc;
}
lmtp_t *lmtp_create(int rfd, int wfd, lmtp_io_t *io)
@@ -110,12 +102,13 @@
return NULL;
if(io == NULL) {
- lmtp->io.read = read;
- lmtp->io.write = write;
+ lmtp->io.read = read;
+ lmtp->io.write = write;
} else {
- lmtp->io.read = io->read ? io->read : read;
- lmtp->io.write = io->write ? io->write : write;
+ 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';
@@ -124,7 +117,6 @@
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);
@@ -136,9 +128,8 @@
{
int i;
- for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) {
+ for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++)
free(lmtp->dispatch[i]); /* lmtp_register() */
- }
free(lmtp->dispatch); /* lmtp_create() */
free(lmtp); /* lmtp_create() */
return;
@@ -146,17 +137,38 @@
lmtp_rc_t lmtp_readline(lmtp_t *lmtp, char *buf, size_t buflen)
{
- /* read a line
- *
- * NOTE: the underlying readline() already reduces any CR/LF combination
- * to a string terminating zero.
- */
- lmtp_rc_t rc;
+ /* read a line (characters until NL) from input stream */
+ 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) {
+ do {
+ rl->rl_cnt = lmtp->io.read(lmtp->rfd, rl->rl_buf, LMTP_LINE_MAXLEN);
+ } while (rl->rl_cnt == -1 && errno == EINTR);
+ if (rl->rl_cnt == -1)
+ return LMTP_ERR_SYSTEM;
+ if (rl->rl_cnt == 0)
+ return LMTP_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 */
- 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 */
+ }
+ buf[n] = '\0'; /* string termination */
+ if (n == (buflen-1))
+ return LMTP_ERR_OVERFLOW;
return LMTP_OK;
}
@@ -165,7 +177,7 @@
/* read lines until end of message, unescape dots.
* on success, returns a buffer which has to be free(3)d by the caller
*
- * NOTE: the lmtp_readline()'s underlying readline() already reduces any
+ * NOTE: the underlying lmtp_readline() already reduces any
* CR/LF combination to a string terminating zero. Callers of this
* function must assume multiline messages have lines terminated
* with NL only.
@@ -177,7 +189,6 @@
* 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 *cpBufrealloc;/* buffer before realloc */
@@ -195,11 +206,13 @@
while (1) {
rc = lmtp_readline(lmtp, cpPtr, nBuf-(cpPtr-cpBuf));
if (rc == LMTP_ERR_OVERFLOW) {
- if (nBuf == maxlen) return LMTP_ERR_OVERFLOW;
+ if (nBuf == maxlen)
+ return LMTP_ERR_OVERFLOW;
offset = nBuf-1; /* write cursor offset is end of buffer */
offsetline = cpLine - cpBuf; /* remember start of line offset */
nBuf *= 2; /* increase buffer */
- if (nBuf > maxlen) nBuf = maxlen; /* but don't exceed maximum */
+ if (nBuf > maxlen)
+ nBuf = maxlen; /* but don't exceed maximum */
if ((cpBufrealloc = (char *)realloc(cpBuf, nBuf)) == NULL) {
free(cpBuf);
return LMTP_ERR_MEM;
@@ -244,29 +257,24 @@
* 4.1.2. COMMAND SYNTAX
* <SP> ::= the space character (ASCII code 32)
*/
-
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) { /* skip NULL verb */
- if ((verblen = strlen(verb)) == 0) continue; /* skip "" verb */
- if( (strlen(req->msg) >= verblen)
- && (strncasecmp(req->msg, verb, verblen) == 0)
- && ( (req->msg[verblen] == '\0')
- || (req->msg[verblen] == ' ')
- )
- ) {
+ if ((verblen = strlen(verb)) == 0)
+ continue; /* skip "" verb */
+ if ( (strlen(req->msg) >= verblen)
+ && (strncasecmp(req->msg, verb, verblen) == 0)
+ && ( (req->msg[verblen] == '\0')
+ || (req->msg[verblen] == ' ') ) ) {
req->verb = verb;
return LMTP_OK;
}
@@ -283,8 +291,9 @@
* dash after the status for every line but the last one
*/
lmtp_rc_t rc = LMTP_OK;
+ int rv;
int dash;
- int len;
+ int len;
char *cpS;
char *cpE;
char formatbuf[LMTP_LINE_MAXLEN];
@@ -292,18 +301,19 @@
if ( strlen(res->statuscode) != 3
|| !isdigit(res->statuscode[0])
|| !isdigit(res->statuscode[1])
- || !isdigit(res->statuscode[2])
- ) return LMTP_ERR_ARG;
+ || !isdigit(res->statuscode[2]))
+ return LMTP_ERR_ARG;
- if (res->dsncode != NULL)
+ 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;
+ || (res->dsncode[0] != res->statuscode[0]))
+ return LMTP_ERR_ARG;
+ }
cpS = res->statusmsg;
for (dash = 1; dash == 1; ) {
@@ -318,7 +328,11 @@
strncpy(formatbuf+len, cpS, cpE-cpS);
len += (cpE-cpS);
formatbuf[len++] = '\n';
- lmtp->io.write(lmtp->wfd, formatbuf, len);
+ do {
+ rv = lmtp->io.write(lmtp->wfd, formatbuf, len);
+ } while (rv == -1 && errno == EINTR);
+ if (rv == -1)
+ return LMTP_ERR_SYSTEM;
cpS = cpE+1;
}
return rc;
@@ -330,7 +344,6 @@
* returned by a previously called function
*/
char *str;
-
str = "LMTP: errorcode has no description";
if (rc == LMTP_OK ) str = "LMTP: no error";
else if (rc == LMTP_EOF ) str = "LMTP: eof";
@@ -339,7 +352,6 @@
else if (rc == LMTP_ERR_OVERFLOW) str = "LMTP: static allocated memory exhausted";
else if (rc == LMTP_ERR_ARG ) str = "LMTP: invalid arg was passed to function";
else if (rc == LMTP_ERR_UNKNOWN ) str = "LMTP: guru meditation";
-
return str;
}
@@ -359,36 +371,43 @@
* previous registration;
*/
lmtp_rc_t rc = LMTP_OK;
- int overload=0; /* overload (replacement) detected has to return old oldcb
- and/or oldctx, no overload requires growth of dispatch
- table */
+ int overload = 0; /* overload (replacement) detected has to return old oldcb
+ and/or oldctx, no overload requires growth of dispatch table */
int i;
if (cb == NULL) { /* checking for existing callback only */
i = verbindex(lmtp, verb);
- if (oldcb != NULL) *oldcb = (i == -1) ? NULL : lmtp->dispatch[i]->cb;
- if (oldctx != NULL) *oldctx = (i == -1) ? NULL : lmtp->dispatch[i]->ctx;
+ if (oldcb != NULL)
+ *oldcb = (i == -1) ? NULL : lmtp->dispatch[i]->cb;
+ if (oldctx != NULL)
+ *oldctx = (i == -1) ? NULL : lmtp->dispatch[i]->ctx;
return LMTP_OK;
}
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;
+ 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 (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;
+ 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;
@@ -414,26 +433,17 @@
res.statuscode = "220";
res.dsncode = NULL;
res.statusmsg = "LMTP server ready (lmtp2nntp)";
- if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK) return rc;
+ if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK)
+ return rc;
while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK) {
verb = req.verb;
if ((i = verbindex(lmtp, verb)) != -1) {
rc = lmtp->dispatch[i]->cb(lmtp, &lmtp->io, &req, lmtp->dispatch[i]->ctx);
- if (rc != LMTP_OK) break;
+ 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;
-}
-
|