ossp-pkg/lmtp2nntp/nntp.c
1.4
/*
* nntp.c: NNTP 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 NNTP 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include "nntp.h"
/* maximum NNTP protocol line length */
#define NNTP_LINE_MAXLEN 1024
typedef struct {
int rl_cnt;
char *rl_bufptr;
char rl_buf[NNTP_LINE_MAXLEN];
} nntp_readline_t;
struct nntp_st {
int rfd;
int wfd;
nntp_io_t io;
nntp_readline_t rl;
struct timeval tv;
};
nntp_t *nntp_create(int rfd, int wfd, nntp_io_t *io)
{
nntp_t *nntp;
if ((nntp = (nntp_t *)malloc(sizeof(nntp_t))) == NULL)
return NULL;
if (io == NULL) {
nntp->io.select = select;
nntp->io.read = read;
nntp->io.write = write;
} else {
nntp->io.select = io->select ? io->select : select;
nntp->io.read = io->read ? io->read : read;
nntp->io.write = io->write ? io->write : write;
}
nntp->rfd = rfd;
nntp->wfd = wfd;
nntp->rl.rl_cnt = 0;
nntp->rl.rl_bufptr = NULL;
nntp->rl.rl_buf[0] = '\0';
nntp_timeout(nntp, 3);
return nntp;
}
nntp_rc_t nntp_timeout(nntp_t *nntp, long timeout)
{
nntp->tv.tv_sec = timeout;
nntp->tv.tv_usec = 0;
return NNTP_OK;
}
nntp_rc_t nntp_init(nntp_t *nntp)
{
nntp_rc_t rc;
char line[NNTP_LINE_MAXLEN];
/* RFC0977 2.4.3. General Responses
* In general, 1xx codes may be ignored or displayed as desired; code 200
* or 201 is sent upon initial connection to the NNTP server depending
* upon posting permission; code 400 will be sent when the NNTP server
* discontinues service (by operator request, for example); and 5xx codes
* indicate that the command could not be performed for some unusual
* reason.
*
* 1xx text
* 200 server ready - posting allowed
* 201 server ready - no posting allowed
* 400 service discontinued
* 500 command not recognized
* 501 command syntax error
* 502 access restriction or permission denied
* 503 program fault - command not performed
*/
do {
if ((rc = nntp_readline(nntp, line, sizeof(line))) != NNTP_OK) {
fprintf(stderr, "DEBUG: nntp_readline returned ***%d***\n", rc);
return rc;
}
} while (line[0] == '1');
// fprintf(stderr, "DEBUG: nntp_readline got ***%s***\n", line);
if (strncmp(line, "200", 3) == 0)
return NNTP_OK;
return NNTP_ERR_INIT;
}
void nntp_destroy(nntp_t *nntp)
{
if (nntp != NULL)
free(nntp);
return;
}
nntp_rc_t nntp_readline(nntp_t *nntp, char *buf, size_t buflen)
{
/* read a line (characters until NL) from input stream */
size_t n;
char c;
nntp_readline_t *rl = &nntp->rl;
struct timeval tv;
fd_set fds;
int rc;
if (nntp == NULL)
return NNTP_ERR_ARG;
for (n = 0; n < buflen-1;) {
/* fetch one character (but read more) */
if (rl->rl_cnt <= 0) {
FD_ZERO(&fds);
FD_SET(nntp->rfd, &fds);
tv.tv_sec = nntp->tv.tv_sec;
tv.tv_usec = nntp->tv.tv_usec;
rc = nntp->io.select(nntp->rfd + 1, &fds, NULL, NULL, &tv);
if (rc == 0)
return NNTP_TIMEOUT;
else if (rc == -1)
return NNTP_ERR_SYSTEM;
do {
rl->rl_cnt = nntp->io.read(nntp->rfd, rl->rl_buf, NNTP_LINE_MAXLEN);
} while (rl->rl_cnt == -1 && errno == EINTR);
if (rl->rl_cnt == -1)
return NNTP_ERR_SYSTEM;
if (rl->rl_cnt == 0)
return NNTP_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 */
if (n == (buflen-1))
return NNTP_ERR_OVERFLOW;
fprintf(stderr, "DEBUG: nntp_readline >>>%s\n", buf);
return NNTP_OK;
}
nntp_rc_t nntp_writeline(nntp_t *nntp, char *buf)
{
char tmp[NNTP_LINE_MAXLEN];
if (nntp == NULL)
return NNTP_ERR_ARG;
strncpy(tmp, buf, NNTP_LINE_MAXLEN-3);
strcat(tmp, "\r\n");
fprintf(stderr, "DEBUG: nntp_writeline >>>%s", tmp);
if (nntp->io.write(nntp->wfd, tmp, strlen(tmp)) < 0)
return NNTP_ERR_SYSTEM;
return NNTP_OK;
}
nntp_rc_t nntp_post(nntp_t *nntp, msg_t *msg)
{
nntp_rc_t rc = NNTP_OK;
char line[NNTP_LINE_MAXLEN];
/* check if this server already knows that artice
* > STAT <message-id>
* < 223 yes, article already known
* < 430 no, i don't know the article, yet
*/
*line = '\0';
strcat(line, "STAT ");
strcat(line, msg->cpMsgid);
if ((rc = nntp_writeline(nntp, line)) != NNTP_OK)
return rc;
if ((rc = nntp_readline(nntp, line, sizeof(line))) != NNTP_OK)
return rc;
if (strncmp(line, "223", 3) == 0)
return NNTP_OK;
if (strncmp(line, "430", 3) != 0)
return NNTP_ERR_POSTPERM; //FIXME
/* post the article
* > POST
* < 340 gimme that thing
* > From: ...
* > Subject: ...
* > Newsgroups: ...
* > Message-ID: <...>
* > [additional headers]
* >
* > [body with dots escaped]
* > .
* < 240 ok, thank you
* < 441 duplicate (ok for us)
*/
*line = '\0';
strcat(line, "POST ");
strcat(line, msg->cpMsgid);
if ((rc = nntp_writeline(nntp, line)) != NNTP_OK)
return rc;
if ((rc = nntp_readline(nntp, line, sizeof(line))) != NNTP_OK)
return rc;
if (strncmp(line, "xxx", 3) == 0)
return NNTP_OK;
if (strncmp(line, "340", 3) != 0)
return NNTP_ERR_POSTPERM; //FIXME
fprintf(stderr, "DEBUG: before write ***%s***(%d)\n", msg->cpMsg, strlen(msg->cpMsg));
if ((rc = nntp->io.write(nntp->wfd, msg->cpMsg, strlen(msg->cpMsg))) < 0)
return NNTP_ERR_SYSTEM;
fprintf(stderr, "DEBUG: after write, written = %i\n", rc);
{//DEBUG paragraph
int i;
char buf[NNTP_LINE_MAXLEN];
fprintf(stderr, "DEBUG: before writeline\n");
if ((rc = nntp_writeline(nntp, "\r\n.\r\nHELP\r\n")) != NNTP_OK);
fprintf(stderr, "DEBUG: writeline returned %d\n", rc);
fprintf(stderr, "DEBUG: before io.read(%d, ..., %d)\n", nntp->rfd, NNTP_LINE_MAXLEN);
i = nntp->io.read(nntp->rfd, &buf, NNTP_LINE_MAXLEN);
fprintf(stderr, "DEBUG: io.read = ***%s***, rc = %d\n", buf, i);
for (i=0; i<10; i++) {
if ((rc = nntp_readline(nntp, line, sizeof(line))) != NNTP_OK)
; //return rc;
fprintf(stderr, "DEBUG: answer to post = ***%s***, rc = %s\n", line, nntp_error(nntp, rc));
};
}
return NNTP_OK;
#if 0
/* check if this server accepts at least one of the newsgroups
> GROUP
< 211 yes, group exists
< 411 no, group doesn't exist
*/
#endif
}
char *nntp_error(nntp_t *nntp, nntp_rc_t rc)
{
char *str;
str = "NNTP: errorcode has no description";
if (rc == NNTP_OK ) str = "NNTP: no error";
else if (rc == NNTP_EOF ) str = "NNTP: end of file";
else if (rc == NNTP_TIMEOUT ) str = "NNTP: timeout";
else if (rc == NNTP_ERR_SYSTEM ) str = "NNTP: see errno";
else if (rc == NNTP_ERR_ARG ) str = "NNTP: invalid argument";
else if (rc == NNTP_ERR_OVERFLOW) str = "NNTP: buffer overflow";
return str;
}