head 1.24;
access;
symbols
SOURCE_RESTRUCTURING_BEFORE:1.23
LMTP2NNTP_1_2a1:1.23
LMTP2NNTP_1_1_1:1.23
LMTP2NNTP_1_1_0:1.23
LMTP2NNTP_1_1b4:1.23
LMTP2NNTP_1_1b3:1.23
LMTP2NNTP_1_1b2:1.23
LMTP2NNTP_1_1b1:1.23
LMTP2NNTP_1_0_0:1.22
LMTP2NNTP_0_9_7:1.22
LMTP2NNTP_0_9_6:1.22
LMTP2NNTP_0_9_5:1.22
LMTP2NNTP_0_9_4:1.20
LMTP2NNTP_0_9_3:1.17
LMTP2NNTP_0_9_2:1.15
LMTP2NNTP_0_9_1:1.11
LMTP2NNTP_0_9_0:1.11;
locks; strict;
comment @ * @;
1.24
date 2001.12.31.11.09.52; author thl; state dead;
branches;
next 1.23;
1.23
date 2001.10.08.13.40.22; author rse; state Exp;
branches;
next 1.22;
1.22
date 2001.09.07.15.02.08; author thl; state Exp;
branches;
next 1.21;
1.21
date 2001.09.04.09.46.06; author rse; state Exp;
branches;
next 1.20;
1.20
date 2001.08.30.13.57.21; author thl; state Exp;
branches;
next 1.19;
1.19
date 2001.08.30.13.38.04; author thl; state Exp;
branches;
next 1.18;
1.18
date 2001.08.29.14.58.21; author thl; state Exp;
branches;
next 1.17;
1.17
date 2001.08.28.11.31.22; author thl; state Exp;
branches;
next 1.16;
1.16
date 2001.08.27.14.49.32; author thl; state Exp;
branches;
next 1.15;
1.15
date 2001.08.23.09.12.30; author rse; state Exp;
branches;
next 1.14;
1.14
date 2001.08.23.07.54.06; author thl; state Exp;
branches;
next 1.13;
1.13
date 2001.08.21.10.52.47; author thl; state Exp;
branches;
next 1.12;
1.12
date 2001.08.21.07.40.41; author thl; state Exp;
branches;
next 1.11;
1.11
date 2001.08.16.15.00.50; author thl; state Exp;
branches;
next 1.10;
1.10
date 2001.08.14.14.42.41; author thl; state Exp;
branches;
next 1.9;
1.9
date 2001.08.14.08.15.25; author thl; state Exp;
branches;
next 1.8;
1.8
date 2001.08.01.07.07.55; author thl; state Exp;
branches;
next 1.7;
1.7
date 2001.07.26.15.18.57; author thl; state Exp;
branches;
next 1.6;
1.6
date 2001.07.25.19.51.12; author rse; state Exp;
branches;
next 1.5;
1.5
date 2001.07.25.15.02.57; author thl; state Exp;
branches;
next 1.4;
1.4
date 2001.07.25.11.29.38; author thl; state Exp;
branches;
next 1.3;
1.3
date 2001.07.24.11.32.07; author thl; state Exp;
branches;
next 1.2;
1.2
date 2001.07.23.12.14.06; author thl; state Exp;
branches;
next 1.1;
1.1
date 2001.07.17.12.40.07; author thl; state Exp;
branches;
next ;
desc
@@
1.24
log
@Mega-Commit: Finally restructure the lmtp2nntp source tree in order to clean
it up. We especially use a consistent prefix for all inlined sources.
@
text
@/*
** Copyright (c) 2001 The OSSP Project
** Copyright (c) 2001 Cable & Wireless Deutschland
**
** 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.org/pkg/lmtp2nntp/.
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version
** 2.0 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this file; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA, or contact the OSSP project .
**
** lmtp.c: Local Mail Transfer Protocol (LMTP) server library
*/
#include
#include
#include
#include
#include
#include
#include "lmtp.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_DMALLOC_H) && defined(DMALLOC)
#include "dmalloc.h"
#endif
#ifndef NUL
#define NUL '\0'
#endif
/* maximum LMTP protocol line length */
#define LMTP_LINE_MAXLEN 1024
/* maximum number of verbs/callbacks to be registered */
#define LMTP_MAXVERBS 32
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 */
};
ssize_t lmtp_fd_read(void *_ctx, void *buf, size_t buflen)
{
lmtp_fd_t *ctx = (lmtp_fd_t *)_ctx;
return read(ctx->fd, buf, buflen);
}
ssize_t lmtp_fd_write(void *_ctx, const void *buf, size_t buflen)
{
lmtp_fd_t *ctx = (lmtp_fd_t *)_ctx;
return write(ctx->fd, buf, buflen);
}
static 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;
}
static 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;
}
lmtp_t *lmtp_create(lmtp_io_t *io)
{
/* create a lmtp structure allocating memory for it and initializing it.
* A lmtp_cb_default() callback is registered for the default "" verb.
* The _rfd_ and _wfd_ args are passed to the read(), write()
* functions and must have meaning for them. If _io_ is NULL,
* the system io functions are used. You can provide an _io_ structure
* and specify alternate functions. Ommiting one or more functions inside
* the _io_ structure by NULLing it causes use of the system default
* function.
*/
lmtp_t *lmtp;
if ((lmtp = (lmtp_t *)malloc(sizeof(lmtp_t))) == NULL)
return NULL;
if (io == NULL)
return NULL;
lmtp->io.ctx = io->ctx;
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_buf[0] = NUL;
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)
{
int i;
if (lmtp == NULL)
return;
for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) {
free(lmtp->dispatch[i]->verb); /* lmtp_register() */
free(lmtp->dispatch[i]); /* lmtp_register() */
}
free(lmtp->dispatch); /* lmtp_create() */
free(lmtp); /* lmtp_create() */
return;
}
lmtp_rc_t lmtp_readline(lmtp_t *lmtp, char *buf, size_t buflen)
{
/* read a line (characters until NL) from input stream */
size_t n;
char c;
lmtp_readline_t *rl = &lmtp->rl;
if (lmtp == NULL)
return LMTP_ERR_ARG;
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->io.ctx, 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 */
}
buf[n] = NUL; /* string termination */
if (n == (buflen-1))
return LMTP_ERR_OVERFLOW;
return LMTP_OK;
}
lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char **cppBuf, size_t maxlen)
{
/* 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 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.
*
* RFC0821 "Simple Mail Transfer Protocol" [excerpt]
* 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 *cpBufrealloc;/* buffer before realloc */
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 */
for (nBuf = 4096; nBuf > maxlen; nBuf = nBuf >> 1);
if ((cpBuf = (char *)malloc(nBuf)) == NULL)
return LMTP_ERR_MEM;
*cppBuf = cpBuf; /* tell caller about the buffer */
cpPtr = cpBuf; /* initialize write cursor */
cpLine = cpBuf; /* initialize start of line */
while (1) {
rc = lmtp_readline(lmtp, cpPtr, nBuf-(cpPtr-cpBuf));
if (rc == 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 ((cpBufrealloc = (char *)realloc(cpBuf, nBuf)) == NULL) {
free(cpBuf);
return LMTP_ERR_MEM;
}
cpBuf = cpBufrealloc;
*cppBuf = cpBuf; /* tell caller about the new buffer */
cpPtr = cpBuf + offset; /* recover write cursor */
cpLine = cpBuf + offsetline; /* recover start of line */
}
else if (rc == LMTP_OK) {
if (strcmp(cpLine, ".") == 0) { /* dot alone is end of message */
*cpLine = NUL; /* hide dot from caller */
break;
}
if (*cpLine == '.') /* escaped dot */
memmove(cpLine, cpLine+1, strlen(cpLine+1)+1);
cpPtr += strlen(cpPtr); /* write cursor to the end */
*cpPtr++ = '\n'; /* artifical NL */
*cpPtr = NUL; /* artifical end of string */
cpLine = cpPtr; /* start of line */
}
else break; /* rc == LMTP_ERR* */
}
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_OK req->msg set, req->verb "" means no verb seen
* 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
*
* RFC0821 "Simple Mail Transfer Protocol" [excerpts]
* 4.1.1. COMMAND SEMANTICS
* The command codes themselves are alphabetic characters terminated by
* if parameters follow and otherwise.
* 4.1.2. COMMAND SYNTAX
* ::= 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] == NUL)
|| (req->msg[verblen] == ' ') ) ) {
req->verb = verb;
return LMTP_OK;
}
}
}
req->verb = "";
return LMTP_OK;
}
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 rv;
int dash;
int len;
char *cpS;
char *cpE;
char formatbuf[LMTP_LINE_MAXLEN];
if ( strlen(res->statuscode) != 3
|| !isdigit((int)res->statuscode[0])
|| !isdigit((int)res->statuscode[1])
|| !isdigit((int)res->statuscode[2]))
return LMTP_ERR_ARG;
if (res->dsncode != NULL) {
if ( (strlen(res->dsncode) != 5)
|| !isdigit((int)res->dsncode[0])
|| (res->dsncode[1] != '.')
|| !isdigit((int)res->dsncode[2])
|| (res->dsncode[3] != '.')
|| !isdigit((int)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 ? '-' : ' ');
if ((len + cpE - cpS + 2) > sizeof(formatbuf)) { /* status + line + '\r\n' does not fit into formatbuf */
dash = 1;
if ((cpE = cpS + sizeof(formatbuf) - 2 - len) <= cpS) /* no space for line at all */
return LMTP_ERR_ARG;
}
strncpy(formatbuf+len, cpS, cpE-cpS);
len += (cpE-cpS);
formatbuf[len++] = '\r';
formatbuf[len++] = '\n';
do {
rv = lmtp->io.write(lmtp->io.ctx, formatbuf, len);
} while (rv == -1 && errno == EINTR);
if (rv == -1)
return LMTP_ERR_SYSTEM;
cpS = cpE;
if (*cpS == '\n')
cpS++;
}
return rc;
}
char *lmtp_error(lmtp_rc_t rc)
{
/* get an error message matching the given lmtp_rc_t code usually
* 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";
else if (rc == LMTP_ERR_SYSTEM ) str = "LMTP: see errno";
else if (rc == LMTP_ERR_MEM ) str = "LMTP: dynamic memory allocation failed";
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;
}
lmtp_rc_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_cb_t cb, void *ctx, lmtp_cb_t *oldcb, void **oldctx)
{
/* For _lmtp_ structure, register a _verb_ and associate a callback
* function _cb_ to it. A context can be specified which will be passed
* to the callback function for every call. Consider the context being
* user data. The library itself does not care about the context except
* passing it along. If the verb was registered previously, the
* registration is replaced and if _oldcb_ and/or _oldctx_ is given, the
* previous registration is returned. Calling the previously registered
* callbacks from within the newly registered callback effectively allows
* hooking or chaining to a previous registered callback. The _ctx_,
* _oldcb_ and _oldctx_ are optional and might be passed as NULL in case
* you don't care. Setting _cb_ to NULL means to check only for a
* 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 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;
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;
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;
return rc;
}
lmtp_rc_t lmtp_loop(lmtp_t *lmtp)
{
/* Print a welcome message then execute a request/ dispatch loop until
* request signals no more data. Each request is checked to contain a
* registered verb and if a verb is found the correspondig callback is
* executed. The lmtp_create() function usually cares to register a
* default callback in order to handle unregistered verbs. The psoudoverb
* for default is the empty string "" and the callback for this verb can
* be overridden.
*/
lmtp_rc_t rc = LMTP_OK;
lmtp_req_t req;
lmtp_res_t res;
char *verb;
int i;
req.verb = "";
req.msg = NULL;
res.statuscode = "220";
res.dsncode = NULL;
res.statusmsg = "LMTP Service ready.";
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 (req.msg != NULL)
free(req.msg);
req.verb = "";
req.msg = NULL;
if (rc != LMTP_OK)
break;
}
}
return rc;
}
@
1.23
log
@Change LMTP library from using a file-descriptor (int) to using a opaque
handle (void *) and use this new feature to support STDIN/STDOUT and SA.
@
text
@@
1.22
log
@Replace ad-hoc tracing with L2_LEVEL_TRACE based approach.
@
text
@a68 2
int rfd; /* file descriptor for reading */
int wfd; /* file descriptor for writing */
d71 1
a71 1
static int lmtp_select(void *ctx, int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv)
d73 2
a74 1
return select(nfds, rfds, wfds, efds, tv);
d77 1
a77 1
static ssize_t lmtp_read(void *ctx, int fd, void *buf, size_t buflen)
d79 2
a80 6
return read(fd, buf, buflen);
}
static ssize_t lmtp_write(void *ctx, int fd, const void *buf, size_t buflen)
{
return write(fd, buf, buflen);
d106 1
a106 1
lmtp_t *lmtp_create(int rfd, int wfd, lmtp_io_t *io)
d122 7
a128 11
if (io == NULL) {
lmtp->io.ctx = NULL;
lmtp->io.select = lmtp_select;
lmtp->io.read = lmtp_read;
lmtp->io.write = lmtp_write;
} else {
lmtp->io.ctx = io->ctx;
lmtp->io.select = io->select ? io->select : lmtp_select;
lmtp->io.read = io->read ? io->read : lmtp_read;
lmtp->io.write = io->write ? io->write : lmtp_write;
}
a132 2
lmtp->rfd = rfd;
lmtp->wfd = wfd;
d173 1
a173 1
rl->rl_cnt = lmtp->io.read(lmtp->io.ctx, lmtp->rfd, rl->rl_buf, LMTP_LINE_MAXLEN);
d362 1
a362 1
rv = lmtp->io.write(lmtp->io.ctx, lmtp->wfd, formatbuf, len);
@
1.21
log
@Finally apply GNU General Public License (GPL) to OSSP lmtp2nntp.
@
text
@d73 15
d128 4
a131 3
lmtp->io.select = select;
lmtp->io.read = read;
lmtp->io.write = write;
d133 4
a136 3
lmtp->io.select = io->select ? io->select : select;
lmtp->io.read = io->read ? io->read : read;
lmtp->io.write = io->write ? io->write : write;
d184 1
a184 1
rl->rl_cnt = lmtp->io.read(lmtp->rfd, rl->rl_buf, LMTP_LINE_MAXLEN);
d373 1
a373 1
rv = lmtp->io.write(lmtp->wfd, formatbuf, len);
@
1.20
log
@get rid of warnings by properly casting char to int for isdigit()
@
text
@d2 24
a25 27
* 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.
*/
@
1.19
log
@lmtp_response() no longer segfaults when a physical line ('\n' terminated) of
the logical line input exceeds LMTP_LINE_MAXLEN but wraps the line by cutting
it off; manpage header issue was no problem when using pod in Makefile; 00TODO
updated;
@
text
@d323 3
a325 3
|| !isdigit(res->statuscode[0])
|| !isdigit(res->statuscode[1])
|| !isdigit(res->statuscode[2]))
d330 1
a330 1
|| !isdigit(res->dsncode[0])
d332 1
a332 1
|| !isdigit(res->dsncode[2])
d334 1
a334 1
|| !isdigit(res->dsncode[4])
@
1.18
log
@added -m maxmessagesize option
@
text
@d349 5
d356 1
d363 3
a365 1
cpS = cpE+1;
@
1.17
log
@reduced dmalloc() complaints about non-free()d resources during one a "lmtp
post arg cw.de.sd.apps.dev.test" run to zero regarding own code; A memoryleak
in str library 0.9.5 was detected which was reported to and fixed by RSE;
@
text
@d221 1
a221 1
nBuf = 4096;
@
1.16
log
@now using NUL instead of '\0'
@
text
@d146 7
a152 4
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() */
d222 2
a223 1
if ((cpBuf = (char *)malloc(nBuf)) == NULL) return LMTP_ERR_MEM;
d444 4
a447 3
* executed. The create() function usually cares to register a default
* callback in order to handle unregistered verbs. The psoudoverb for
* default is the empty string "".
d455 3
d468 5
@
1.15
log
@Add DMalloc support
@
text
@d46 4
d127 1
a127 1
lmtp->rl.rl_buf[0] = '\0';
d186 1
a186 1
buf[n] = '\0'; /* string termination */
d244 1
a244 1
*cpLine = '\0'; /* hide dot from caller */
d251 1
a251 1
*cpPtr = '\0'; /* artifical end of string */
d293 1
a293 1
&& ( (req->msg[verblen] == '\0')
@
1.14
log
@cosmetic correction make all RFC references match RFC\d{4} allowing to grep for them when writing documentation
@
text
@d39 7
@
1.13
log
@removed FIXME. We are a LMTP server and we send multiline replies and don't
receive any so no need to handle such incoming messages. The NNTP client has
no need for handling multiline replies because the protocol does not support
multiline replies.
@
text
@d191 1
a191 1
* RFC821 "Simple Mail Transfer Protocol" [excerpt]
d259 1
a259 1
* RFC821 "Simple Mail Transfer Protocol" [excerpts]
@
1.12
log
@replaced c++ style // comments with c style /* */ comments
@
text
@a144 1
/*FIXME RFC0821 4.2. SMTP REPLIES "Only the EXPN and HELP commands are expected to result in multiline replies in normal circumstances, however multiline replies are allowed for any command." */
@
1.11
log
@INN kludge, IHAVE feeding, -d deliverymode command line option, DEFER handing
@
text
@d145 1
a145 1
//FIXME RFC0821 4.2. SMTP REPLIES "Only the EXPN and HELP commands are expected to result in multiline replies in normal circumstances, however multiline replies are allowed for any command."
@
1.10
log
@connected LMTP and NNTP side, now featuring all three -g groupmodes, added post1000 articles burn-in
@
text
@d145 1
@
1.9
log
@cleanup msg.[ch], moved tracing to trace.[ch], added target to Makefile, added -t switch in lmtp2nntp.[c|pod], incorporated tracing into lmtp.[ch] and nntp.[ch], run.sh now using new tracing option
@
text
@d347 1
a347 1
char *lmtp_error(lmtp_t *lmtp, lmtp_rc_t rc)
@
1.8
log
@improved robustness adding some parameter sanit checks
@
text
@d105 3
a107 2
lmtp->io.read = read;
lmtp->io.write = write;
d109 3
a111 2
lmtp->io.read = io->read ? io->read : read;
lmtp->io.write = io->write ? io->write : write;
@
1.7
log
@lmtp_cb_helo using strict RFC821 checking in a perl-generated regex
@
text
@d99 1
a99 1
lmtp_t *lmtp = NULL;
d104 1
a104 1
if(io == NULL) {
d131 2
d147 2
@
1.6
log
@Full-metal jacket cleanup ;)
Hopefully Thomas does not kill me...
@
text
@d435 1
a435 1
res.statusmsg = "LMTP server ready (lmtp2nntp)";
@
1.5
log
@added argz library and first inter-callback functionality
@
text
@d1 28
a29 1
/* standard headers */
d35 1
d37 1
a37 2
/* third-party headers */
// #include "str.h"
d39 2
a40 3
/* own headers */
#include "lmtp.h"
#include "lmtp_p.h"
d42 2
a43 2
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);
d45 19
d65 1
a65 1
void lmtp_debug_dumplmtp(lmtp_t *lmtp)
d67 1
d70 4
a73 16
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;
a74 1
/*************************************************************************/
d76 1
a76 1
static int readline(lmtp_t *lmtp, char *buf, size_t buflen)
d78 2
a79 10
/* 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
*/
d81 5
a85 38
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;
d105 2
a106 2
lmtp->io.read = read;
lmtp->io.write = write;
d108 2
a109 2
lmtp->io.read = io->read ? io->read : read;
lmtp->io.write = io->write ? io->write : write;
d111 1
a119 1
d131 1
a131 1
for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) {
a132 1
}
d140 27
a166 6
/* read a line
*
* NOTE: the underlying readline() already reduces any CR/LF combination
* to a string terminating zero.
*/
lmtp_rc_t rc;
d168 4
a171 4
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 */
d180 1
a180 1
* NOTE: the lmtp_readline()'s underlying readline() already reduces any
a191 1
d209 2
a210 1
if (nBuf == maxlen) return LMTP_ERR_OVERFLOW;
d214 2
a215 1
if (nBuf > maxlen) nBuf = maxlen; /* but don't exceed maximum */
a259 1
a265 1
a267 1
a269 1
d272 6
a277 7
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] == ' ')
)
) {
d294 1
d296 1
a296 1
int len;
d304 2
a305 2
|| !isdigit(res->statuscode[2])
) return LMTP_ERR_ARG;
d307 1
a307 1
if (res->dsncode != NULL)
d314 3
a316 2
|| (res->dsncode[0] != res->statuscode[0])
) return LMTP_ERR_ARG;
d331 5
a335 1
lmtp->io.write(lmtp->wfd, formatbuf, len);
a346 1
a354 1
d374 2
a375 3
int overload=0; /* overload (replacement) detected has to return old oldcb
and/or oldctx, no overload requires growth of dispatch
table */
d380 4
a383 2
if (oldcb != NULL) *oldcb = (i == -1) ? NULL : lmtp->dispatch[i]->cb;
if (oldctx != NULL) *oldctx = (i == -1) ? NULL : lmtp->dispatch[i]->ctx;
d389 5
a393 3
overload=1;
if (oldcb != NULL) *oldcb = lmtp->dispatch[i]->cb;
if (oldctx != NULL) *oldctx = lmtp->dispatch[i]->ctx;
d397 2
a398 1
if (i > LMTP_MAXVERBS-2) return LMTP_ERR_OVERFLOW;
d405 4
a408 2
if (oldcb != NULL) *oldcb = NULL;
if (oldctx != NULL) *oldctx = NULL;
d410 1
d436 2
a437 1
if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK) return rc;
d443 2
a444 1
if (rc != LMTP_OK) break;
a446 11
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);
@
1.4
log
@shortly before lmtp_message/lmtp_reset are vanished
@
text
@a24 1
printf("io.select = %ld \n", (long)lmtp->io.select);
d100 2
a101 2
* The _rfd_ and _wfd_ args are passed to the read(), write() and
* select() functions and must have meaning for them. If _io_ is NULL,
a112 1
lmtp->io.select = select;
a115 1
lmtp->io.select = io->select ? io->select : select;
a137 4
lmtp_msg_t *msg;
lmtp_msg_t *next;
//_readmsg : if ((cpBuf = (char *)malloc(nBuf)) == NULL)
a139 6
msg = lmtp->dispatch[i]->msg;
do {
next = lmtp->dispatch[i]->msg->next;
free(msg); /* linked messages */
msg = next;
} while(next != NULL);
d165 2
a166 1
/* read lines until end of message, unescape dots
d183 1
d203 2
a204 2
if ((cpBuf = (char *)realloc(cpBuf, nBuf)) == NULL) {
free(cpBuf); //FIXME double check isn't this a destroy() task? */
d207 1
a326 17
lmtp_msg_t *lmtp_message(lmtp_t *lmtp, char *verb)
{
/* get the first message attached to a verb's dispatch structure. The
* messages are fifo linked lists.
*/
int i;
lmtp_msg_t *cpp = NULL;
if ((i = verbindex(lmtp, verb)) >= 0) cpp = lmtp->dispatch[i]->msg;
return cpp;
}
void lmtp_reset(lmtp_t *lmtp)
{
return;
}
d334 8
a341 8
str = "LMTP: errorcode has no description";
if (rc == LMTP_OK ) str = "LMTP: no error";
if (rc == LMTP_EOF ) str = "LMTP: eof";
if (rc == LMTP_ERR_SYSTEM ) str = "LMTP: see errno";
if (rc == LMTP_ERR_MEM ) str = "LMTP: dynamic memory allocation failed";
if (rc == LMTP_ERR_OVERFLOW) str = "LMTP: static allocated memory exhausted";
if (rc == LMTP_ERR_ARG ) str = "LMTP: invalid arg was passed to function";
if (rc == LMTP_ERR_UNKNOWN ) str = "LMTP: guru meditation";
a394 1
lmtp->dispatch[i]->msg = NULL;
@
1.3
log
@first time working with sendmail
@
text
@a43 6
/*
* 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
*/
d46 11
d99 9
d140 17
a156 1
free(lmtp);
d162 5
d178 1
a178 1
/* read lines until end of message, unescape dots
d180 11
a190 5
* 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. [...]
d202 4
a205 5
if ((cpBuf = (char *)malloc(nBuf)) == NULL)
return LMTP_ERR_MEM;
*cppBuf = cpBuf;
cpPtr = cpBuf;
cpLine = cpBuf;
a206 1
// printf("DEBUG: cpPtr-cpBuf=%d, nBuf=%d, nBuf-(cpPtr-cpBuf)=%d\n", cpPtr-cpBuf, nBuf, nBuf-(cpPtr-cpBuf));
a207 1
// printf("DEBUG: lmtp_readline()=***%s***, rc=%d\n", cpPtr, rc);
d209 5
a213 7
if (nBuf == maxlen)
return LMTP_ERR_OVERFLOW;
offset = nBuf-1;
offsetline = cpLine - cpBuf;
nBuf *= 2;
if (nBuf > maxlen)
nBuf = maxlen;
d215 1
a215 1
free(cpBuf);
d218 3
a220 3
*cppBuf = cpBuf;
cpPtr = cpBuf + offset;
cpLine = cpBuf + offsetline;
d223 2
a224 3
if (strcmp(cpLine, ".") == 0) {
// printf("DEBUG: \".\" found ***%s***\n", cpLine);
*cpLine = '\0';
d227 6
a232 10
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;
d234 1
d245 1
d249 7
a255 1
* LMTP_ERR_VERB req->msg set, req->verb NULL means no verb seen
d272 3
a274 4
if ((verb = lmtp->dispatch[i]->verb) != NULL) {
if ((verblen = strlen(verb)) == 0)
continue;
if( (strlen(req->msg) >= verblen) //FIXME check for verb\s
d276 3
d285 2
a286 1
return LMTP_ERR_VERB;
d337 1
a337 1
char **lmtp_message(lmtp_t *lmtp, char *verb)
d339 3
d344 2
a345 3
char **cpp = NULL;
if ((i = verbindex(lmtp, verb)) >= 0)
cpp = lmtp->dispatch[i]->msg;
d356 14
a369 1
char *str = NULL;
d375 13
d389 3
a391 1
int overload=0; /* overload returns old, no overload increases table */
d394 7
d404 2
a405 2
if(oldcb != NULL) *oldcb = lmtp->dispatch[i]->cb;
if(oldctx != NULL) *oldctx = lmtp->dispatch[i]->ctx;
d416 2
a417 2
if(oldcb != NULL) *oldcb = NULL;
if(oldctx != NULL) *oldctx = NULL;
a418 1
d429 8
a438 1
lmtp_rc_t rc = LMTP_OK;
d447 1
a447 1
while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK || (rc == LMTP_ERR_VERB)) {
d449 4
a452 7
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;
@
1.2
log
@first successful loop()
@
text
@d2 1
d9 4
a15 4
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);
d17 1
a18 48
/*
* 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;
}
d45 4
a48 4
* 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
d61 1
a61 1
return -2; /* error see errno */
d63 1
a63 1
return 0; /* EOF */
d78 1
a78 1
return (n == (buflen-1)) ? -1 : n;
d101 2
a102 2
lmtp->io.read = read;
lmtp->io.write = write;
d104 3
a106 3
lmtp->io.select = io->select;
lmtp->io.read = io->read;
lmtp->io.write = io->write;
d119 2
d135 3
a137 3
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 */
d141 1
a141 1
lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char *buf, size_t buflen)
d153 51
a203 6
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*/
d234 3
a236 2
verblen = strlen(verb);
if( (strlen(req->msg) >= verblen)
d323 1
a323 1
if (strcmp(verb, lmtp->dispatch[i]->verb) == 0) {
d353 2
a354 1
lmtp_rc_t rc;
d356 6
d363 8
a370 9
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;
a371 9
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.";
d375 1
a375 1
lmtp_rc_t lmtp_cb_lhlo(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx)
d377 1
d379 4
a382 12
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.";
@
1.1
log
@ralf s. engelschall first api draft
@
text
@d2 1
d4 3
d11 86
a96 1
static int readline(lmtp_readline_t *rl, int fd, char *buf, size_t buflen)
d99 2
a100 2
ssize_t rc;
char c = '\0', *cp;
d102 1
a102 2
cp = (char *)buf;
for (n = 1; n < buflen; n++) {
a104 1
rc = 1;
d106 5
a110 10
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++;
d114 20
a133 17
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;
d135 1
a135 2
*cp = '\0';
return n;
d144 10
a153 3
lmtp->io.select = select;
lmtp->io.read = read;
lmtp->io.write = write;
d156 9
a164 2
lmtp->rl.rl_read = lmtp->io.read;
/* lmtp->dispatch = ... */
d170 1
d174 12
a185 1
lmtp_rc_t lmtp_request(lmtp_t *lmtp, lmtp_req_t *req)
d187 9
d197 6
d206 40
a245 1
lmtp_rc_t lmtp_result(lmtp_t *lmtp, lmtp_res_t *res)
d247 4
d252 38
d295 2
d298 2
d314 1
a314 1
lmtp_cb_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_type_t type, lmtp_cb_t *cb, void *ctx)
d316 29
a344 2
lmtp_cb_t old = NULL;
return old;
d353 7
a359 8
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;
}
d361 2
d364 27
@