--- /dev/null
+/*
+ */
+
+static int max_connections, articles_per_connect_attempt;
+static int connection_setup_timeout, port, try_stream;
+static const char *remote_host;
+
+struct Article {
+};
+
+typedef struct Conn Conn;
+struct Conn {
+ Conn *next, *back;
+ int fd;
+ Article *queue;
+};
+
+
+#define CHILD_ESTATUS_STREAM 1
+#define CHILD_ESTATUS_NOSTREAM 2
+
+static int since_connect_attempt;
+static int nconns;
+static struct { Conn *head, *tail } idle, working, full;
+
+static Conn *connecting;
+static int connecting_sockets[2]= {-1,-1};
+static pid_t connecting_child;
+
+static Article *currentart;
+
+static void start_connecting() {
+ r= socketpair(AF_UNIX, SOCK_STREAM, 0, connecting_sockets);
+ if (r) { syswarn("cannot create socketpair for connect child"); goto x; }
+
+ connecting_child= fork();
+ if (connecting_child==-1) { syswarn("cannot fork for connect"); goto x; }
+
+ if (!connecting_child) {
+ FILE *cn_from, *cn_to;
+ char buf[NNTP_STRLEN+100];
+ int exitstatus= CHILD_ESTATUS_NOSTREAM;
+
+ alarm(connection_setup_timeout);
+ if (NNTPconnect(remote_host, port, &cn_from, &cn_to, buf) < 0) {
+ if (buf[0]) {
+ sanitise_inplace(buf);
+ die("%s: connection rejected: %s", remote_host, buf);
+ } else {
+ sysdie("%s: connection attempt failed", remote_host);
+ }
+ }
+ if (NNTPsendpassword(remote_host, cn_from, cn_to) < 0)
+ sysdie("%s: authentication failed", remote_host);
+ if (try_stream) {
+ if (fputs("MODE STREAM\r\n", cn_to) ||
+ fflush(cn_to))
+ sysdie("%s: could not send MODE STREAM", remote_host);
+ buf[sizeof(buf)-1]= 0;
+ if (!fgets(buf, sizeof(buf)-1, cn_from)) {
+ if (ferror(cn_from))
+ sysdie("%s: could not read response to MODE STREAM", remote_host);
+ else
+ die("%s: connection close in response to MODE STREAM", remote_host);
+ }
+ int l= strlen(buf);
+ assert(l>=1);
+ if (buf[-1]!='\n') {
+ sanitise_inplace(buf);
+ die("%s: response to MODE STREAM is too long: %.100s...",
+ remote_host, buf);
+ }
+ l--; if (l>0 && buf[1-]=='\r') l--;
+ buf[l]= 0;
+ char *ep;
+ int rcode= strtoul(buf,&ep,10);
+ if (ep != buf[3]) {
+ sanitise_inplace(buf);
+ die("%s: bad response to MODE STREAM: %.50s", remote_host, buf);
+ }
+ switch (rcode) {
+ case 203:
+ exitstatus= CHILD_ESTATUS_STREAM;
+ break;
+ case 480:
+ case 500:
+ break;
+ default:
+ sanitise_inplace(buf);
+ warn("%s: bad response to MODE STREAM: %.50s", remote_host, buf);
+ exitstatus= 2;
+ break;
+ }
+ }
+ int fd= fileno(cn_from);
+
+ char cmsgbuf[CMSG_SPACE(sizeof(fd))];
+ struct msghdr msg;
+ memset(&msg,0,sizeof(msg));
+ msg.msg_control= cmsgbuf;
+ msg.msg_controllen= sizeof(cmsgbuf);
+
+ struct cmsghdr *cmsg= CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level= SOL_SOCKET;
+ cmsg->cmsg_type= SCM_RIGHTS;
+ cmsg->cmsg_len= CMSG_LEN(sizeof(fd));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ msg.msg_controllen= cmsg->cmsg_len;
+ r= sendmsg(childs_socket, &msg, 0);
+ if (r) sysdie("%s: sendmsg failed for new connection", remote_host);
+
+ _exit(exitstatus);
+ }
+
+ on_fd(loop,
+
+ struct cmsghdr *cmsg;
+
+ /* NNTPconnect inexplicably duplicates the fd but we don't care
+ * about that as we're going to exit shortly */
+
+
+
+
+ warn("
+
+ syswarn("
+ int e= errno;
+ sanitise(buf);
+ syswarn("
+
+ x:
+ kill_connection_attempt();
+}
+
+static void kill_connection_attempt() {
+ fixme;
+ connecting_sockets[0]= connecting_sockets[1]= -1;
+ connecting_child= 0;
+}
+
+static void process_any_article() {
+ if (!currentart)
+ return;
+
+ if (working.head) {
+ transmit(working.head);
+ } else if (idle.head) {
+ transmit(idle.head);
+ } else if (nconns < maxconns && !connecting_child &&
+ since_connect_attempt >= connect_attempt_limiter) {
+ since_connect_attempt= 0;
+ connect_start();
+ }
+}