	Retrieving documents with NNTP

	This agent conforms to W3A/A. It uses some utility routines
	that are not shown.

	The agents handles nntp: and news: URLs.

	[[news:*]] retrieves list of all newsgroups.

	[[news:group]] retrieves list of articles in the group.

	[[news:artID]] retrieves article with ID.

	[[news:group/artnr]] retrieves n'th article in group.

	[[nntp://host:port/group/artnr]]

	The agent gets the server from the NNTPSERVER environment
	variable or from a compiled-in default.

<<*>>=
static char copyright[] = "Copyright NBBI, Den Haag, 1995";
/* Author: Bert Bos <bert@let.rug.nl> */

#define USE_POLL 0

#include <config.h>
#if USE_POLL
#include <poll.h>
#endif
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pwd.h>				/* To find who we are */
#include <w3a.h>
#include <tcp.h>				/* connectTCP() */
#include <str.h>				/* String and heap functions */
#include <url.h>				/* URL parsing */
#include <mime.h>				/* Read/parse MIME headers */

#ifndef howmany
#define howmany(x,y)  (((x)+((y)-1))/(y))
#endif

#define NRBITS (sizeof(int) * 8)		/* 8 bits per byte */

#ifndef NNTPSERVER
#define NNTPSERVER "news"
#endif

typedef enum {AllGroups, ArtList, ArtID, ArtNr} Mode;

char *server;					/* NNTP server */
static Strip news, nntp, star;			/* = "news", "nntp", "*" */

static struct {
    FILE *f;
    Mode mode;
    char *group;
    char *id;
    int nr;
} conn_info[FD_SETSIZE];


EXPORT Bool initNNTP(char ***protocols, int *nrprotocols)
{
    static char *protos[] = {"news", "nntp"};

    *protocols = protos;
    *nrprotocols = XtNumber(protos);
    if (! (server = getenv("NNTPSERVER"))) server = NNTPSERVER;
    news = str2strip("news");
    nntp = str2strip("nntp");
    star = str2strip("*");
    return TRUE;
}


EXPORT int openNNTP(const char *url, int method, int flags,
		    const char *referer)
{
    URI uri;
    char *host, *port, *p;
    int s;

    if (! URL_parse(url, &uri)) {
	errno = EURL;				/* Bad URL syntax */
	return -1;
    }
    assert(uri.scheme == nntp || uri.scheme == news);
    if (uri.scheme == news) {
	host = server;
	port = "nntp";
    } else {
	host = strip2str(uri.host);
	port = uri.port : strip2str(uri.port) : "nntp";
    }
    if ((s = connectTCP(host, port, (flags & O_NONBLOCK) != 0)) == -1
	&& errno != EINPROGRESS) {
	return -1;				/* Could not connect */
    }
    if (!(conn_info[s].f = fdopen(s, "r+"))) return -1;	/* I/O error */

    if (uri.path == star) {
	conn_info[s].mode = AllGroups;
	conn_info[s].group = NULL;
	conn_info[s].id = NULL;
    } else if (strchr(strip2str(uri.path, '@'))) {
	conn_info[s].mode = ArtId;
	conn_info[s].group = NULL;
	conn_info[s].id = strdup(strip2str(uri.path));
    } else if (strchr(strip2str(uri.path), '/')) {
	conn_info[s].mode = ArtNr;
	conn_info[s].group = tokenize(strdup(strip2str(uri.path)). "/", &p);
	conn_info[s].id = NULL;
	conn_info[s].nr = atoi(tokenize(p, "", &p));
    } else {
	conn_info[s].mode = ArtList;
	conn_info[s].group = strdup(strip2str(uri.path));
	conn_info[s].id = NULL;
    }
    return s;
}



EXPORT Bool doneNNTP(int fd)
{
    
    ...
}



EXPORT int peekNNTP(int fd)
{
#if USE_POLL
    struct pollfd fds[1];

    fds[0].fd = fd;
    fds[0].events = POLLNORM;
    /* return: -1 = err; 0 = timed out; 1 = input available */
    return poll(fds, 1, 0);
#else /* USE_POLL */
    int mask[howmany(FD_SETSIZE, NRBITS)];
    struct timeval timeout;
    int n, i;

    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    for (i = 0; i < XtNumber(mask); i++) mask[i] = 0;
    mask[fd/NRBITS] |= 1 << (fd % NRBITS);
    if ((n = select(fd + 1, mask, NULL, NULL, &timeout)) == -1) return -1;
    return n;					/* 0 or 1 */
#endif /* USE_POLL */
}


EXPORT int readNNTP(int fd, char *buf, size_t nbytes)
{
    ...
}


EXPORT int writeNNTP(int fd, const char *buf, size_t nbytes)
{
    ...
}


EXPORT Bool infoNNTP(int fd, W3ADocumentInfo *buf)
{
    ...
}


EXPORT Bool closeNNTP(int fd)
{
    dispose(conn_info[fd].group);
    dispose(conn_info[fd].id);
    return fclose(conn_info[fd].f) != -1;
}


EXPORT Bool deleteNNTP(const char *url)
{
    errno = ENYI;				/* Not yet implemented */
    return FALSE;
}
