diff --git a/src/client/Makefile.in b/src/client/Makefile.in index ce45e255..6c8b0a05 100644 --- a/src/client/Makefile.in +++ b/src/client/Makefile.in @@ -53,8 +53,8 @@ srcdir = @srcdir@ VPATH = @srcdir@ prog = empire$E -obj = expect.$O host.$O ioqueue.$O ipglob.$O login.$O main.$O queue.$O \ -servcmd.$O serverio.$O tags.$O termio.$O termlib.$O version.$O +obj = expect.$O host.$O ipglob.$O linebuf.$O login.$O main.$O play.$O \ +ringbuf.$O secure.$O servcmd.$O termlib.$O version.$O all: $(prog) @@ -81,14 +81,13 @@ uninstall: # FIXME generate from .d expect.$O: misc.h host.$O: misc.h -ioqueue.$O: misc.h queue.h ioqueue.h +linebuf.$O: linebuf.h login.$O: misc.h proto.h -main.$O: misc.h proto.h queue.h ioqueue.h tags.h version.h -queue.$O: misc.h queue.h -servcmd.$O: misc.h proto.h queue.h ioqueue.h tags.h -serverio.$O: misc.h queue.h ioqueue.h -tags.$O: misc.h tags.h -termio.$O: misc.h tags.h +main.$O: misc.h version.h +play.$O: linebuf.h misc.h proto.h ringbuf.h secure.h +ringbuf.$O: ringbuf.h +secure.$O: ringbuf.h secure.h +servcmd.$O: misc.h proto.h secure.h termlib.$O: misc.h version.$O: version.h $(obj): config.h diff --git a/src/client/ioqueue.c b/src/client/ioqueue.c deleted file mode 100644 index 872e6ad5..00000000 --- a/src/client/ioqueue.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * 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 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 program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * ioqueue.c: Manage an i/o queue - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#include -#include "ioqueue.h" -#include "misc.h" -#include "queue.h" - -static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc); -static void enqueuecc(struct ioqueue *ioq, char *buf, int cc); -static int dequeuecc(struct ioqueue *ioq, int cc); - - -void -ioq_init(struct ioqueue *ioq, int bsize) -{ - initque(&ioq->queue); - ioq->cc = 0; - ioq->bsize = bsize; -} - -/* - * Copy the specified number of characters into the buffer - * provided, without actually dequeueing the data. Return - * number of bytes actually found. - */ -int -ioq_peek(struct ioqueue *ioq, char *buf, int cc) -{ - return ioqtobuf(ioq, buf, cc); -} - -int -ioq_dequeue(struct ioqueue *ioq, int cc) -{ - if (dequeuecc(ioq, cc) != cc) - return 0; - return cc; -} - -int -ioq_read(struct ioqueue *ioq, char *buf, int cc) -{ - int n; - - n = ioqtobuf(ioq, buf, cc); - if (n > 0) - dequeuecc(ioq, n); - return n; -} - -void -ioq_write(struct ioqueue *ioq, char *buf, int cc) -{ - enqueuecc(ioq, buf, cc); -} - -int -ioq_qsize(struct ioqueue *ioq) -{ - return ioq->cc; -} - -void -ioq_drain(struct ioqueue *ioq) -{ - struct io *io; - struct qelem *qp; - - while ((qp = ioq->queue.q_forw) != &ioq->queue) { - io = (struct io *)qp; - free(io->data); - (void)remque(&io->queue); - (void)free(io); - } - ioq->cc = 0; -} - -char * -ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol) -{ - char *p; - char *end; - int nbytes; - - *eol = 0; - - cc--; - - nbytes = ioqtobuf(ioq, buf, cc); - end = &buf[nbytes]; - for (p = buf; p < end && *p; p++) { - if (*p == '\n') { - *++p = '\0'; - *eol = 1; - dequeuecc(ioq, p - buf); - return buf; - } - } - if (cc && (p - buf) == cc) { - dequeuecc(ioq, cc); - buf[cc] = '\0'; - return buf; - } - return NULL; -} - -/* - * all the rest are local to this module - */ - - -/* - * copy cc bytes from ioq to buf. - * this routine doesn't free memory; this is - * left for a higher level. - */ -static int -ioqtobuf(struct ioqueue *ioq, char *buf, int cc) -{ - struct io *io; - struct qelem *qp; - char *offset; - int nbytes; - int nleft; - - nleft = cc; - offset = buf; - for (qp = ioq->queue.q_forw; qp != &ioq->queue; qp = qp->q_forw) { - io = (struct io *)qp; - if ((nbytes = io->nbytes - io->offset) < 0) { - fprintf(stderr, "ioqtobuf: offset %d nbytes %d\n", - io->offset, io->nbytes); - continue; - } - if (nbytes > 0) { - if (nleft < nbytes) - nbytes = nleft; - memcpy(offset, io->data + io->offset, nbytes); - offset += nbytes; - nleft -= nbytes; - } - } - return offset - buf; -} - -/* - * append a buffer to the end of the ioq. - */ -static void -enqueuecc(struct ioqueue *ioq, char *buf, int cc) -{ - struct io *io; - - io = malloc(sizeof(*io)); - io->nbytes = cc; - io->offset = 0; - io->data = buf; - insque(&io->queue, ioq->queue.q_back); - ioq->cc += cc; -} - -/* - * remove cc bytes from ioqueue ioq - * free memory, dequeue io elements - * which are no longer used. - */ -static int -dequeuecc(struct ioqueue *ioq, int cc) -{ - struct io *io; - struct qelem *qp; - int nbytes; - int there; - - nbytes = 0; - while ((qp = ioq->queue.q_forw) != &ioq->queue) { - io = (struct io *)qp; - there = io->nbytes - io->offset; - if (there < 0) { - fprintf(stderr, "dequeuecc: nbytes %d, offset %d\n", - io->nbytes, io->offset); - continue; - } - if (cc > there) { - cc -= there; - nbytes += there; - (void)remque(&io->queue); - free(io->data); - } else { - io->offset += cc; - nbytes += cc; - break; - } - } - ioq->cc -= nbytes; - return nbytes; -} diff --git a/src/client/linebuf.c b/src/client/linebuf.c new file mode 100644 index 00000000..4187bd50 --- /dev/null +++ b/src/client/linebuf.c @@ -0,0 +1,107 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * 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 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * linebuf.c: Simple line buffer + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include "linebuf.h" + +/* + * Initialize empty line buffer. + * Not necessary if *LBUF is already zeroed. + */ +void +lbuf_init(struct lbuf *lbuf) +{ + lbuf->len = lbuf->full = 0; +} + +/* + * Return length of currently buffered line. + * This includes the newline if present. + */ +int +lbuf_len(struct lbuf *lbuf) +{ + return lbuf->len; +} + +/* + * Is LBUF full (i.e. we got the newline)? + */ +int +lbuf_full(struct lbuf *lbuf) +{ + return lbuf->full; +} + +/* + * Return a pointer to the currently buffered line. + * If you mess with the line in a way that changes the line length, + * better call lbuf_init() next. + */ +char * +lbuf_line(struct lbuf *lbuf) +{ + assert(lbuf->len < sizeof(lbuf->line)); + lbuf->line[lbuf->len] = 0; + return lbuf->line; +} + +/* + * Append CH to the line buffered in LBUF. + * LBUF must not be full. + * If CH is a newline, the buffer is now full. Return the line + * length, including the newline. + * Else return 0 if there was space, and -1 if not. + */ +int +lbuf_putc(struct lbuf *lbuf, char ch) +{ + assert(!lbuf->full); + + if (ch == '\n') { + assert(lbuf->len + 1 < sizeof(lbuf->line)); + lbuf->line[lbuf->len++] = ch; + lbuf->line[lbuf->len] = 0; + lbuf->full = 1; + return lbuf->len; + } + + if (lbuf->len + 2 >= sizeof(lbuf->line)) + return -1; /* truncating long line */ + + lbuf->line[lbuf->len++] = ch; + return 0; +} diff --git a/src/client/queue.h b/src/client/linebuf.h similarity index 69% rename from src/client/queue.h rename to src/client/linebuf.h index 9db972b4..4149adf2 100644 --- a/src/client/queue.h +++ b/src/client/linebuf.h @@ -25,24 +25,28 @@ * * --- * - * queue.h: generic vax-like doubly linked list queues. + * linebuf.h: Simple line buffer * * Known contributors to this file: - * + * Markus Armbruster, 2007 */ -#ifndef QUEUE_H -#define QUEUE_H +#ifndef LINEBUF_H +#define LINEBUF_H -#define QEMPTY(p) ((p)->q_forw == (p)) +#define LBUF_LEN_MAX 4096 -struct qelem { - struct qelem *q_forw; - struct qelem *q_back; +struct lbuf { + /* All members are private! */ + unsigned len; /* strlen(line) */ + int full; /* got a complete line, with newline? */ + char line[LBUF_LEN_MAX]; /* buffered line, zero-terminated */ }; -void insque(struct qelem *p, struct qelem *q); -void remque(struct qelem *p); -void initque(struct qelem *p); +extern void lbuf_init(struct lbuf *); +extern int lbuf_len(struct lbuf *); +extern int lbuf_full(struct lbuf *); +extern char *lbuf_line(struct lbuf *); +extern int lbuf_putc(struct lbuf *, char); #endif diff --git a/src/client/main.c b/src/client/main.c index 754f6b0f..cd0f6fc2 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -36,39 +36,12 @@ #include -#include -#include -#include -#include -#include -#if defined(_WIN32) -#include -#else #include -#include -#include +#include #include -#endif - -#include "ioqueue.h" #include "misc.h" -#include "proto.h" -#include "tags.h" #include "version.h" -#ifdef _WIN32 -HANDLE hStdIn; -#endif - -#define RETRY 3 - -int eight_bit_clean; -int sock; - -static volatile sig_atomic_t interrupt; -static void intr(int sig); -static int handleintr(int); - static void print_usage(char *program_name) { @@ -94,25 +67,10 @@ main(int argc, char **argv) char *uname; char *host; char *port; + int sock; #ifdef _WIN32 - WORD wVersionRequested; - WSADATA WsaData; - int err; - fd_set readfds; - struct timeval tm; - DWORD stdinmode; - SECURITY_ATTRIBUTES security; - int bRedirected = 0; char unamebuf[128]; -#else - struct sigaction sa; - fd_set mask; - fd_set savemask; - int retry = 0; #endif - struct ioqueue server; - FILE *auxout_fp = NULL; - int n; #ifdef _WIN32 /* @@ -124,9 +82,6 @@ main(int argc, char **argv) * after each prompt is required. */ setvbuf(stdout, NULL, _IOLBF, 4096); -#else - FD_ZERO(&mask); - FD_ZERO(&savemask); #endif while ((opt = getopt(argc, argv, "2:kuhv")) != EOF) { @@ -192,14 +147,13 @@ main(int argc, char **argv) } getsose(); - if (auxfname && (auxout_fp = fopen(auxfname, "a")) == NULL) { + if (auxfname && (auxfp = fopen(auxfname, "a")) == NULL) { fprintf(stderr, "Unable to open %s for append\n", auxfname); exit(1); } #ifdef _WIN32 - wVersionRequested = MAKEWORD(2, 0); - err = WSAStartup(wVersionRequested, &WsaData); + err = WSAStartup(MAKEWORD(2, 0), &WsaData); if (err != 0) { printf("WSAStartup Failed, error code %d\n", err); exit(1); @@ -211,156 +165,8 @@ main(int argc, char **argv) if (!login(sock, uname, country, passwd, send_kill, utf8)) exit(1); - ioq_init(&server, 2048); - io_init(); -#ifndef _WIN32 - FD_ZERO(&mask); - FD_SET(0, &savemask); - FD_SET(sock, &savemask); - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = intr; - sigaction(SIGINT, &sa, NULL); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - while (FD_ISSET(sock, &savemask)) { - mask = savemask; - n = select(sock + 1, &mask, NULL, NULL, NULL); - if (interrupt) { - if (!handleintr(sock)) - break; - errno = 0; - } - if (n <= 0) { - if (errno == EINTR) { - perror("select"); - (void)close(sock); - FD_CLR(sock, &savemask); - } - } else { - if (FD_ISSET(0, &mask)) { - if (!termio(0, sock, auxout_fp)) { - if (retry++ >= RETRY) { - FD_CLR(0, &savemask); - } - } else { - retry = 0; - } - } - if (FD_ISSET(sock, &mask)) { - if (!serverio(sock, &server)) - FD_CLR(sock, &savemask); - else - servercmd(&server, auxout_fp); - } - } - } -#else /* _WIN32 */ - signal(SIGINT, intr); + if (play(sock) < 0) + exit(1); - bRedirected = 0; - tm.tv_sec = 0; - tm.tv_usec = 1000; - - if (!isatty(fileno(stdin))) - bRedirected = 1; - else { - security.nLength = sizeof(SECURITY_ATTRIBUTES); - security.lpSecurityDescriptor = NULL; - security.bInheritHandle = TRUE; - hStdIn = CreateFile("CONIN$", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &security, OPEN_EXISTING, (DWORD) NULL, NULL); - - if (hStdIn == INVALID_HANDLE_VALUE) { - printf("Error getting hStdIn.\n"); - fflush(stdout); - exit(-3); - } - - err = GetConsoleMode(hStdIn, &stdinmode); - if (!err) { - printf("Error getting console mode.\n"); - fflush(stdout); - exit(-4); - } else { - stdinmode |= ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT; - err = SetConsoleMode(hStdIn, stdinmode); - if (!err) { - printf("Error setting console mode.\n"); - fflush(stdout); - exit(-5); - } - } - } - while (1) { - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - n = select(sock + 1, &readfds, NULL, NULL, &tm); - if (interrupt) { - if (!handleintr(sock)) - break; - errno = 0; - } - if (n < 0) { - if (errno == EINTR) { - errno = WSAGetLastError(); - perror("select"); - (void)closesocket(sock); - break; - } - } else { - if (bRedirected == 1) { - if (!termio(0, sock, auxout_fp)) - bRedirected = -1; - } else if (bRedirected == 0) { - if (WaitForSingleObject(hStdIn, 10) != WAIT_TIMEOUT) { - termio(-1, sock, auxout_fp); - FlushConsoleInputBuffer(hStdIn); - } - } - if (FD_ISSET(sock, &readfds)) { - if (!serverio(sock, &server)) - break; - else - servercmd(&server, auxout_fp); - } - } - } - if (bRedirected == 0) - CloseHandle(hStdIn); -#endif /* _WIN32 */ - ioq_drain(&server); -#ifdef _WIN32 - (void)closesocket(sock); -#else - (void)close(sock); -#endif - return 0; /* Shut the compiler up */ -} - -static void -intr(int sig) -{ - interrupt = 1; -#ifdef _WIN32 - signal(SIGINT, intr); -#endif -} - -static int -handleintr(int s) -{ - if (interrupt) { - /* tacky, but it works */ -#if !defined(_WIN32) - if (write(s, "\naborted\n", 1 + 7 + 1) <= 0) -#else - if (send(s, "\naborted\n", 1 + 7 + 1, 0) <= 0) -#endif - return 0; - interrupt = 0; - } - return 1; + return 0; } diff --git a/src/client/misc.h b/src/client/misc.h index 2dc3bbd3..ee9c9e06 100644 --- a/src/client/misc.h +++ b/src/client/misc.h @@ -39,19 +39,16 @@ #include #endif -struct ioqueue; +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) extern char empirehost[]; extern char empireport[]; extern int eight_bit_clean; -extern int sock; +extern int input_fd; +extern FILE *auxfp; extern char *SO; extern char *SE; -#ifdef _WIN32 -extern HANDLE hStdIn; -#endif - #ifdef _WIN32 #define getsose() ((void)0) #define putso() ((void)0) @@ -67,9 +64,9 @@ int recvline(int s, char *buf); int expect(int s, int match, char *buf); int tcp_connect(char *, char *); int login(int s, char *uname, char *cname, char *cpass, int kill_proc, int); +int play(int); void sendcmd(int s, char *cmd, char *arg); -void servercmd(struct ioqueue *ioq, FILE *auxfi); -int serverio(int s, struct ioqueue *ioq); -int termio(int fd, int sock, FILE *auxfi); +void servercmd(int, char *, int); +void outch(char); #endif diff --git a/src/client/play.c b/src/client/play.c new file mode 100644 index 00000000..c3fe98e2 --- /dev/null +++ b/src/client/play.c @@ -0,0 +1,316 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * 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 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * play.c: Playing the game + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "linebuf.h" +#include "misc.h" +#include "proto.h" +#include "ringbuf.h" +#include "secure.h" + +#define EOF_COOKIE "ctld\n" +#define INTR_COOKIE "\naborted\n" + +int input_fd; +static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */ + +/* + * Receive and process server output from SOCK. + * Return number of characters received on success, -1 on error. + */ +static int +recv_output(int sock) +{ + /* + * Read a chunk of server output and feed its characters into a + * simple state machine. + * Initial state is SCANNING_ID. + * In state SCANNING_ID, buffer the character. If it's a space, + * decode the id that has been buffered, and enter state BUFFERING + * or COPYING depending on its value. + * In state BUFFERING, buffer the character. If it's newline, + * pass id and buffered text to servercmd(), then enter state + * SCANNING_ID. + * In state COPYING, pass the character to outch(). If it's + * newline, enter state SCANNING_ID. + */ + static enum { + SCANNING_ID, BUFFERING, COPYING + } state = SCANNING_ID; + static int id; + static struct lbuf lbuf; + char buf[4096]; + ssize_t n; + int i, ch, len; + char *line, *end; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) + return -1; + + for (i = 0; i < n; i++) { + ch = buf[i]; + switch (state) { + case SCANNING_ID: + if (ch == '\n') { + /* FIXME gripe unexpected! */ + lbuf_init(&lbuf); + break; + } + if (ch != ' ') { + lbuf_putc(&lbuf, ch); + break; + } + line = lbuf_line(&lbuf); + id = strtol(line, &end, 16); + if (end == line || *end) { + /* FIXME gripe bad id */ + id = -1; + } + lbuf_init(&lbuf); + + switch (id) { + case C_PROMPT: + case C_FLUSH: + case C_EXECUTE: + case C_EXIT: + case C_FLASH: + case C_INFORM: + case C_PIPE: + case C_REDIR: + state = BUFFERING; + break; + default: + /* unknown or unexpected id, treat like C_DATA */ + case C_DATA: + state = COPYING; + break; + } + break; + + case BUFFERING: + len = lbuf_putc(&lbuf, ch); + if (len) { + line = lbuf_line(&lbuf); + servercmd(id, line, len); + lbuf_init(&lbuf); + state = SCANNING_ID; + } + break; + + case COPYING: + outch(ch); + if (ch == '\n') + state = SCANNING_ID; + } + } + + return n; +} + +/* + * Receive command input from FD into INBUF. + * Return 1 on receipt of input, zero on EOF, -1 on error. + */ +static int +recv_input(int fd, struct ring *inbuf) +{ + static struct lbuf cmdbuf; + int n, i, ch; + char *line; + int res = 1; + + n = ring_from_file(inbuf, fd); + if (n < 0) + return -1; + if (n == 0) { + /* EOF on input */ + if (lbuf_len(&cmdbuf)) { + /* incomplete line */ + ring_putc(inbuf, '\n'); + n++; + } + /* + * Can't put EOF cookie into INBUF here, it may not fit. + * Leave it to caller. + */ + res = 0; + } + + /* copy input to AUXFP etc. */ + for (i = -n; i < 0; i++) { + ch = ring_peek(inbuf, i); + assert(ch != EOF); + if (lbuf_putc(&cmdbuf, ch)) { + line = lbuf_line(&cmdbuf); + if (auxfp) + fputs(line, auxfp); + save_input(line); + lbuf_init(&cmdbuf); + } + } + + return res; +} + +static void +intr(int sig) +{ + send_intr = 1; +#ifdef _WIN32 + signal(SIGINT, intr); +#endif +} + +/* + * Play on SOCK. + * The session must be in the playing phase. + * Return 0 when the session ended, -1 on error. + */ +int +play(int sock) +{ + /* + * Player input flows from INPUT_FD through recv_input() into ring + * buffer INBUF, which drains into SOCK. This must not block. + * Server output flows from SOCK into recv_output(). Reading SOCK + * must not block. + */ + struct sigaction sa; + struct ring inbuf; /* input buffer, draining to SOCK */ + int eof_fd0; /* read fd 0 hit EOF? */ + int send_eof; /* need to send EOF_COOKIE */ + fd_set rdfd, wrfd; + int n; + + sa.sa_flags = 0; + sa.sa_handler = intr; + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + ring_init(&inbuf); + eof_fd0 = send_eof = 0; + + for (;;) { + FD_ZERO(&rdfd); + FD_ZERO(&wrfd); + + /* + * Want to read player input only when we don't need to send + * cookies, and we haven't hit EOF on fd 0, and INBUF can + * accept some. + */ + if (!send_intr && !send_eof && !eof_fd0 && ring_space(&inbuf)) + FD_SET(input_fd, &rdfd); + /* Want to send player input only when we have something */ + if (send_intr || send_eof || ring_len(&inbuf)) + FD_SET(sock, &wrfd); + /* Always want to read server output */ + FD_SET(sock, &rdfd); + + n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL); + if (n < 0) { + if (errno != EINTR) { + perror("select"); + return -1; + } + } + + if (send_eof + && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0) + send_eof = 0; + if (send_intr + && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) + send_intr = 0; + + if (n < 0) + continue; + + /* read player input */ + if (FD_ISSET(input_fd, &rdfd)) { + n = recv_input(input_fd, &inbuf); + if (n < 0) { + perror("read stdin"); /* FIXME stdin misleading, could be execing */ + return -1; + } + if (n == 0) { + /* EOF on input */ + send_eof = 1; + if (input_fd) { + /* execute done, switch back to fd 0 */ + input_fd = 0; + } else { + /* stop reading input, drain socket ring buffers */ + eof_fd0 = 1; + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + } + } + } + + /* send it to the server */ + if (FD_ISSET(sock, &wrfd)) { + n = ring_to_file(&inbuf, sock); + if (n < 0) { + perror("write socket"); + return -1; + } + } + + /* read server output and print it */ + if (FD_ISSET(sock, &rdfd)) { + n = recv_output(sock); + if (n < 0) { + perror("read socket"); + return -1; + } + if (n == 0) + return 0; + if (input_fd < 0) { + /* execute failed */ + input_fd = 0; + send_eof = 1; + } + } + } +} diff --git a/src/client/ringbuf.c b/src/client/ringbuf.c new file mode 100644 index 00000000..b893bc10 --- /dev/null +++ b/src/client/ringbuf.c @@ -0,0 +1,245 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * 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 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * ringbuf.c: Simple ring buffer + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include +#include +#include +#include "ringbuf.h" + +/* + * Initialize empty ring buffer. + * Not necessary if *R is already zeroed. + */ +void +ring_init(struct ring *r) +{ + r->cons = r->prod = 0; +} + +/* + * Return number of bytes held in ring buffer. + */ +int +ring_len(struct ring *r) +{ + assert(r->prod - r->cons <= RING_SIZE); + return r->prod - r->cons; +} + +/* + * Return how much space is left in ring buffer. + */ +int +ring_space(struct ring *r) +{ + return RING_SIZE - (r->prod - r->cons); +} + +/* + * Peek at ring buffer contents. + * N must be between -RING_SIZE-1 and RING_SIZE. + * If N>=0, peek at the (N+1)-th byte to be gotten. + * If N<0, peek at the -N-th byte that has been put in. + * Return the byte as unsigned char coverted to int, or EOF if there + * aren't that many bytes in the ring buffer. + */ +int +ring_peek(struct ring *r, int n) +{ + unsigned idx; + + assert(-RING_SIZE - 1 <= n && n <= RING_SIZE); + + idx = n >= 0 ? r->cons + n : r->prod - -n; + if (idx < r->cons && idx >= r->prod) + return EOF; + return r->buf[idx % RING_SIZE]; +} + +/* + * Get and remove the oldest byte from the ring buffer. + * Return it as unsigned char coverted to int, or EOF if the buffer was + * empty. + */ +int +ring_getc(struct ring *r) +{ + if (r->cons == r->prod) + return EOF; + return r->buf[r->cons++ % RING_SIZE]; +} + +/* + * Attempt to put byte C into the ring buffer. + * Return EOF when the buffer is full, else C. + */ +int +ring_putc(struct ring *r, unsigned char c) +{ + if (r->prod - r->cons == RING_SIZE) + return EOF; + return r->buf[r->prod++ % RING_SIZE] = c; +} + +/* + * Attempt to put SZ bytes from BUF into the ring buffer. + * Return space left in ring buffer when it fits, else don't change + * the ring buffer and return how much space is missing. + */ +int +ring_putm(struct ring *r, void *buf, size_t sz) +{ + char *p = buf; + int left = ring_space(r) - sz; + int res; + size_t i; + + if (left >= 0) { + res = 0; + for (i = 0; i < sz; i++) + res = ring_putc(r, p[i]); + assert(res != EOF); + } + + return left; +} + +/* + * Discard the N oldest bytes from the ring buffer. + * It must hold at least that many. + */ +void +ring_discard(struct ring *r, int n) +{ + assert(n <= ring_len(r)); + r->cons += n; +} + +/* + * Search the ring buffer for zero-terminated string S. + * If found, return a non-negative value referring to the beginning of + * S in the buffer when passed to ring_peek(). Else return -1. + */ +int +ring_search(struct ring *r, char *s) +{ + size_t len = strlen(s); + size_t i, j; + + for (i = r->cons; i + len <= r->prod; i++) { + for (j = 0; j < len && s[j] == r->buf[(i + j) % RING_SIZE]; j++) ; + if (!s[j]) + return i - r->cons; + } + return -1; +} + +/* + * Fill ring buffer from file referred by file descriptor FD. + * If ring buffer is already full, do nothing and return 0. + * Else attempt to read as many bytes as space permits, with readv(), + * and return its value. + */ +int +ring_from_file(struct ring *r, int fd) +{ + unsigned cons = r->cons % RING_SIZE; + unsigned prod = r->prod % RING_SIZE; + struct iovec iov[2]; + int cnt; + ssize_t res; + + if (r->prod == r->cons + RING_SIZE) + return 0; + + iov[0].iov_base = r->buf + prod; + if (cons <= prod) { + /* r->buf[prod..] */ + iov[0].iov_len = RING_SIZE - prod; + /* r->buf[..cons-1] */ + iov[1].iov_base = r->buf; + iov[1].iov_len = cons; + cnt = 2; + } else { + /* r->buf[prod..cons-1] */ + iov[0].iov_len = cons - prod; + cnt = 1; + } + res = readv(fd, iov, cnt); + if (res < 0) + return res; + r->prod += res; + return res; +} + +/* + * Drain ring buffer to file referred by file descriptor FD. + * If ring buffer is already empty, do nothing and return 0. + * Else attempt to write complete contents with writev(), and return + * its value. + */ +int +ring_to_file(struct ring *r, int fd) +{ + unsigned cons = r->cons % RING_SIZE; + unsigned prod = r->prod % RING_SIZE; + struct iovec iov[2]; + int cnt; + ssize_t res; + + if (r->cons == r->prod) + return 0; + + iov[0].iov_base = r->buf + cons; + if (prod <= cons) { + /* r->buf[cons..] */ + iov[0].iov_len = RING_SIZE - cons; + /* r->buf[..prod-1] */ + iov[1].iov_base = r->buf; + iov[1].iov_len = prod; + cnt = 2; + } else { + /* r->buf[cons..prod-1] */ + iov[0].iov_len = prod - cons; + cnt = 1; + } + res = writev(fd, iov, cnt); + if (res < 0) + return res; + r->cons += res; + return res; +} diff --git a/src/client/ioqueue.h b/src/client/ringbuf.h similarity index 53% rename from src/client/ioqueue.h rename to src/client/ringbuf.h index 9a88c385..0dc08660 100644 --- a/src/client/ioqueue.h +++ b/src/client/ringbuf.h @@ -25,38 +25,43 @@ * * --- * - * ioqueue.h: stores and frees data associated with a file descriptor. - * uses writev to write, and read to read. + * ringbuf.h: Simple ring buffer * * Known contributors to this file: - * + * Markus Armbruster, 2007 */ -#ifndef IOQUEUE_H -#define IOQUEUE_H +#ifndef RINGBUF_H +#define RINGBUF_H -#include "queue.h" +#include -struct ioqueue { - struct qelem queue; /* queue fwd/back */ - int bsize; /* basic block size */ - int cc; /* character count */ +#define RING_SIZE 4096 + +/* Ring buffer, consumer reads, producer writes */ +struct ring { + /* All members are private! */ + /* + * Consumer reads from buf[cons % RING_SIZE], incrementing cons + * Produces writes to buf[prod % RING_SIZE], incrementing prod + * prod == cons: empty + * prod == cons + RING_SIZE: full + * invariant: prod - cons <= RING_SIZE + */ + unsigned cons, prod; + unsigned char buf[RING_SIZE]; }; -struct io { - struct qelem queue; /* list of ioqueue elements */ - int nbytes; /* number of data bytes present */ - int offset; /* offset into current entry */ - char *data; /* pointer to start */ -}; - -void ioq_init(struct ioqueue *ioq, int bsize); -int ioq_peek(struct ioqueue *ioq, char *buf, int cc); -int ioq_dequeue(struct ioqueue *ioq, int cc); -int ioq_read(struct ioqueue *ioq, char *buf, int cc); -void ioq_write(struct ioqueue *ioq, char *buf, int cc); -int ioq_qsize(struct ioqueue *ioq); -void ioq_drain(struct ioqueue *ioq); -char *ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol); +extern void ring_init(struct ring *); +extern int ring_len(struct ring *); +extern int ring_space(struct ring *); +extern int ring_peek(struct ring *, int); +extern int ring_getc(struct ring *); +extern int ring_putc(struct ring *, unsigned char); +extern int ring_putm(struct ring *, void *, size_t); +extern void ring_discard(struct ring *, int); +extern int ring_search(struct ring *, char *); +extern int ring_from_file(struct ring *, int fd); +extern int ring_to_file(struct ring *, int fd); #endif diff --git a/src/client/queue.c b/src/client/secure.c similarity index 67% rename from src/client/queue.c rename to src/client/secure.c index 557ff688..258ee313 100644 --- a/src/client/queue.c +++ b/src/client/secure.c @@ -25,35 +25,41 @@ * * --- * - * queue.c: implementation of various queuing routines + * secure.c: Check redir etc. to protect against tampering deity * * Known contributors to this file: - * Steve McClure, 1998 + * Markus Armbruster, 2007 */ #include -#include "queue.h" +#include +#include "ringbuf.h" +#include "secure.h" + +struct ring recent_input; void -insque(struct qelem *p, struct qelem *q) +save_input(char *inp) { - p->q_forw = q->q_forw; - p->q_back = q; - q->q_forw->q_back = p; - q->q_forw = p; + size_t len = strlen(inp); + int left; + + left = ring_putm(&recent_input, inp, len); + if (left < 0) { + ring_discard(&recent_input, ring_search(&recent_input, "\n")); + ring_putm(&recent_input, inp, len); + } } -void -remque(struct qelem *p) +int +seen_input(char *tail) { - p->q_back->q_forw = p->q_forw; - p->q_forw->q_back = p->q_back; -} + int dist = ring_search(&recent_input, tail); -void -initque(struct qelem *p) -{ - p->q_forw = p; - p->q_back = p; + if (dist < 0) + return 0; + + ring_discard(&recent_input, dist + strlen(tail)); + return 1; } diff --git a/src/client/tags.h b/src/client/secure.h similarity index 83% rename from src/client/tags.h rename to src/client/secure.h index 9da5e348..a92026eb 100644 --- a/src/client/tags.h +++ b/src/client/secure.h @@ -25,23 +25,17 @@ * * --- * - * tags.h: Tag structure definition + * secure.h: Check redir etc. to protect against tampering deity * * Known contributors to this file: - * John Yockey, 1998 + * Markus Armbruster, 2007 */ -#ifndef TAGS_H -#define TAGS_H +#ifndef SECURE_H +#define SECURE_H -struct tagstruct { - char *item; - struct tagstruct *next; -}; - -extern struct tagstruct *taglist; - -void io_init(void); -char *gettag(char *p); +extern void clear_recent_input(void); +extern void save_input(char *); +extern int seen_input(char *); #endif diff --git a/src/client/servcmd.c b/src/client/servcmd.c index 24bae608..f4129b13 100644 --- a/src/client/servcmd.c +++ b/src/client/servcmd.c @@ -35,119 +35,88 @@ #include +#include #include #include #include #include -#include #include -#ifdef _WIN32 -#include -#else -#include -#endif - -#include "ioqueue.h" #include "misc.h" #include "proto.h" -#include "tags.h" +#include "secure.h" + +int eight_bit_clean; +FILE *auxfp; -static char num_teles[64]; -static char the_prompt[1024]; -static int mode; -static int nbtu; -static int nmin; static FILE *redir_fp; static FILE *pipe_fp; -static void prompt(FILE *auxfi); +static void prompt(int, char *, char *); static void doredir(char *p); static void dopipe(char *p); -static void doexecute(char *p, FILE *auxfi); -static void output(int code, char *buf, FILE *auxfi); -static void screen(char *buf); +static void doexecute(char *p); void -servercmd(struct ioqueue *ioq, FILE *auxfi) +servercmd(int code, char *arg, int len) { - char buf[1024]; - char *p; - static int code = -1; - int eol; + static int nmin, nbtu; + static char the_prompt[1024]; + static char teles[64]; - while (ioq_gets(ioq, buf, sizeof(buf), &eol)) { - p = buf; - if (code == -1) { - if (isalpha(*buf)) - code = 10 + (*buf - 'a'); - else - code = *buf - '0'; - while (*p && !isspace(*p)) - p++; - *p++ = 0; + switch (code) { + case C_PROMPT: + if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) { + fprintf(stderr, "prompt: bad server prompt %s\n", arg); } - /* - * FIXME - * C_REDIR, C_PIPE, and C_EXECUTE will not - * work with filename longer than one buffer - */ - switch (code) { - case C_PROMPT: - if (sscanf(p, "%d %d", &nmin, &nbtu) != 2) { - fprintf(stderr, "prompt: bad server prompt %s\n", p); + snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ", + nmin, nbtu); + prompt(code, the_prompt, teles); + break; + case C_FLUSH: + snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg); + prompt(code, the_prompt, teles); + break; + case C_EXECUTE: + doexecute(arg); + break; + case C_EXIT: + printf("Exit: %s", arg); + if (auxfp) + fprintf(auxfp, "Exit: %s", arg); + break; + case C_FLASH: + printf("\n%s", arg); + if (auxfp) + fprintf(auxfp, "\n%s", arg); + break; + case C_INFORM: + if (*arg) { + snprintf(teles, sizeof(teles), "(%.*s )", len -1, arg); + if (!redir_fp && !pipe_fp) { + putchar('\07'); + prompt(code, the_prompt, teles); } - mode = code; - sprintf(the_prompt, "[%d:%d] Command : ", nmin, nbtu); - prompt(auxfi); - break; - case C_REDIR: - if (eol) - p[strlen(p) - 1] = '\0'; - doredir(p); - break; - case C_PIPE: - if (eol) - p[strlen(p) - 1] = '\0'; - dopipe(p); - break; - case C_FLUSH: - mode = code; - if (eol) - p[strlen(p) - 1] = '\0'; - sprintf(the_prompt, "%s", p); - prompt(auxfi); - break; - case C_EXECUTE: - if (eol) - p[strlen(p) - 1] = '\0'; - doexecute(p, auxfi); - break; - case C_INFORM: - if (eol) - p[strlen(p) - 1] = '\0'; - if (*p) { - p[strlen(p) - 1] = '\0'; - sprintf(num_teles, "(%s) ", p + 1); - if (!redir_fp && !pipe_fp) { - putchar('\07'); - prompt(NULL); - } - } else - *num_teles = '\0'; - break; - default: - output(code, p, auxfi); - break; - } - if (eol) - code = -1; + } else + teles[0] = 0; + break; + case C_PIPE: + dopipe(arg); + break; + case C_REDIR: + doredir(arg); + break; + default: + assert(0); + break; } } static void -prompt(FILE *auxfi) +prompt(int code, char *prompt, char *teles) { - if (mode == C_PROMPT) { + char *nl; + + if (code == C_PROMPT) { if (redir_fp) { (void)fclose(redir_fp); redir_fp = NULL; @@ -156,13 +125,12 @@ prompt(FILE *auxfi) pipe_fp = NULL; } } - if (mode == C_PROMPT) - printf("\n"); - printf("%s%s", num_teles, the_prompt); - (void)fflush(stdout); - if (auxfi) { - fprintf(auxfi, "\n%s%s", num_teles, the_prompt); - (void)fflush(auxfi); + nl = code == C_PROMPT || code == C_INFORM ? "\n" : ""; + printf("%s%s%s", nl, teles, prompt); + fflush(stdout); + if (auxfp) { + fprintf(auxfp, "%s%s%s", nl, teles, prompt); + fflush(auxfp); } } @@ -183,7 +151,6 @@ fname(char *s) static void doredir(char *p) { - char *tag; int mode; int fd; @@ -192,6 +159,12 @@ doredir(char *p) redir_fp = NULL; } + if (!seen_input(p)) { + fprintf(stderr, "WARNING! Server attempted to redirect %s\n", + p); + return; + } + if (*p++ != '>') { fprintf(stderr, "WARNING! Weird redirection %s", p); return; @@ -207,19 +180,12 @@ doredir(char *p) } else mode |= O_EXCL; - tag = gettag(p); p = fname(p); - if (tag == NULL) { - fprintf(stderr, "WARNING! Server redirected output to file %s\n", - p); - return; - } - free(tag); - if (*p == 0) { fprintf(stderr, "Redirection lacks a file name\n"); return; } + fd = open(p, mode, 0600); redir_fp = fd < 0 ? NULL : fdopen(fd, "w"); if (!redir_fp) { @@ -234,25 +200,22 @@ doredir(char *p) static void dopipe(char *p) { - char *tag; + if (!seen_input(p)) { + fprintf(stderr, "WARNING! Server attempted to pipe %s", p); + return; + } if (*p++ != '|') { fprintf(stderr, "WARNING! Weird pipe %s", p); return; } - tag = gettag(p); - if (tag == NULL) { - fprintf(stderr, "WARNING! Server attempted to run: %s\n", p); - return; - } - free(tag); - for (; *p && isspace(*p); p++) ; if (*p == 0) { fprintf(stderr, "Redirection lacks a command\n"); return; } + if ((pipe_fp = popen(p, "w")) == NULL) { fprintf(stderr, "Can't redirect to pipe %s: %s\n", p, strerror(errno)); @@ -260,103 +223,48 @@ dopipe(char *p) } static void -doexecute(char *p, FILE *auxfi) +doexecute(char *p) { - int fd; - char *tag; - - tag = gettag(p); - if (tag == NULL) { + if (!seen_input(p)) { fprintf(stderr, - "WARNING! Server attempted unauthorized read of file %s\n", + "WARNING! Server attempted to read file %s", p); return; } - free(tag); p = fname(p); if (*p == 0) { fprintf(stderr, "Need a file to execute\n"); return; } - if ((fd = open(p, O_RDONLY, 0)) < 0) { + + if ((input_fd = open(p, O_RDONLY)) < 0) { fprintf(stderr, "Can't open execute file %s: %s\n", p, strerror(errno)); return; } - /* copies 4k at a time to the socket */ - while (termio(fd, sock, auxfi)) /*do copy */ - ; - /* - * Some platforms don't send the eof (cntl-D) at the end of - * copying a file. If emp_client hangs at the end of an - * execute, include the following line and notify wolfpack - * of the platform you are using. - * sendeof(sock); - */ - close(fd); } -static void -output(int code, char *buf, FILE *auxfi) +void +outch(char c) { - switch (code) { - case C_NOECHO: - /* not implemented; server doesn't send it */ - break; - case C_ABORT: - printf("Aborted\n"); - if (auxfi) - fprintf(auxfi, "Aborted\n"); - break; - case C_CMDERR: - case C_BADCMD: - printf("Error; "); - if (auxfi) - fprintf(auxfi, "Error; "); - break; - case C_EXIT: - printf("Exit: "); - if (auxfi) - fprintf(auxfi, "Exit: "); - break; - case C_FLASH: - printf("\n"); - break; - default: - break; - } - if (auxfi) { - fprintf(auxfi, "%s", buf); - } - + if (auxfp) + putc(c, auxfp); if (redir_fp) - fprintf(redir_fp, "%s", buf); + putc(c, redir_fp); else if (pipe_fp) - fprintf(pipe_fp, "%s", buf); - else { - screen(buf); - } -} - -static void -screen(char *buf) -{ - char c; - - while ((c = *buf++)) { - if (eight_bit_clean) { - if (c == 14) - putso(); - else if (c == 15) - putse(); - else - putchar(c); - } else if (c & 0x80) { + putc(c, pipe_fp); + else if (eight_bit_clean) { + if (c == 14) putso(); - putchar(c & 0x7f); + else if (c == 15) putse(); - } else + else putchar(c); - } + } else if (c & 0x80) { + putso(); + putchar(c & 0x7f); + putse(); + } else + putchar(c); } diff --git a/src/client/serverio.c b/src/client/serverio.c deleted file mode 100644 index 7df0ae10..00000000 --- a/src/client/serverio.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * 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 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 program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * serverio.c: Handle input from server - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#if !defined(_WIN32) -#include -#endif -#include "misc.h" -#include "ioqueue.h" - -int -serverio(int s, struct ioqueue *ioq) -{ - char *buf; - int n; - - if ((buf = malloc(ioq->bsize)) == NULL) { - fprintf(stderr, "malloc server i/o failed\n"); - return 0; - } -#ifdef _WIN32 - n = recv(s, buf, ioq->bsize, 0); -#else - n = read(s, buf, ioq->bsize); -#endif - if (n < 0) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - perror("server i/o read"); - free(buf); - return 0; - } - if (n == 0) { - fprintf(stderr, "Server EOF\n"); -#ifdef WIN32 - (void)closesocket(s); -#else - (void)close(s); -#endif - return 0; - } - if (n != ioq->bsize) - buf = realloc(buf, n); - ioq_write(ioq, buf, n); - return 1; -} diff --git a/src/client/tags.c b/src/client/tags.c deleted file mode 100644 index 97139102..00000000 --- a/src/client/tags.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * 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 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 program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * tags.c: save redir, execute and pipe info to protect against tampering - * by the deity. - * - * Known contributors to this file: - * John Yockey, 1998 - */ - -#include - -#include -#include -#include "misc.h" -#include "tags.h" - -struct tagstruct *taglist; - -void -io_init(void) -{ - taglist = NULL; -} - -char * -gettag(char *p) -{ - struct tagstruct *tag1, *tag2; - - if (taglist == NULL) - return NULL; - tag1 = taglist; - if (!strncmp(tag1->item, p, strlen(tag1->item))) { - p = tag1->item; - taglist = taglist->next; - free(tag1); - return p; - } - while (tag1->next != NULL) { - tag2 = tag1->next; - if (!strncmp(tag2->item, p, strlen(tag2->item))) { - p = tag2->item; - tag1->next = tag2->next; - free(tag2); - return p; - } - tag1 = tag1->next; - } - return NULL; -} diff --git a/src/client/termio.c b/src/client/termio.c deleted file mode 100644 index 9819bde0..00000000 --- a/src/client/termio.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * 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 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 program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * termio.c: Various io functions - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#include "misc.h" -#include "tags.h" - -static int sendeof(int sock); - -int -termio(int fd, int sock, FILE *auxfi) -{ - static char exec[] = "execute"; - static char buf[4096]; - char out[4096]; - int i, n; - char *ptr; - char *p, *q, *r, *s, *t; - int nbytes; - int prespace, exec_com, inarg, quoted, tagging; - struct tagstruct *tag; -#ifdef _WIN32 - char c; - INPUT_RECORD InpBuffer[2]; - int ret; - DWORD records; -#endif - - i = strlen(buf); - p = buf + i; -#ifndef _WIN32 - n = read(fd, p, sizeof(buf) - i); -#else -/* The keyboard is sometimes generating both keydown and keyup - * events for the same key. Thus, we only want to grab keydown - * events. */ - if (fd == -1) { - ret = PeekConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error peeking the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - if (InpBuffer[0].EventType != KEY_EVENT) { - ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error reading the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - return 1; - } - if (!InpBuffer[0].Event.KeyEvent.bKeyDown) { - ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error reading the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - return 1; - } - c = InpBuffer[0].Event.KeyEvent.uChar.AsciiChar; - - if (c == 13) - c = 10; - records = 1; - p[0] = c; - p[1] = '\0'; - if (c != 10) { - ret = ReadConsole(hStdIn, p, sizeof(buf) - i, &records, NULL); - if (!ret) { - fprintf(stderr, "Error reading the console (%lu)\n", - GetLastError()); - return 0; - } - } else - putchar(c); -/* Strip off the CRLF to just LF */ - if (records > 1) { - if (p[records - 2] == 13 && p[records - 1] == 10) { - p[records - 2] = 10; - p[records - 1] = 0; - records--; - } - } - FlushConsoleInputBuffer(hStdIn); - if (records == 0) - return 0; - n = records; - } else if (fd == 0) { - if (feof(stdin)) { - sendeof(sock); - return 0; - } - fgets(p, sizeof(buf) - i, stdin); - n = strlen(p); - } else { - n = read(fd, p, sizeof(buf) - i); - } -#endif - if (n == 0) { - sendeof(sock); - return 0; - } - if (n < 0) { - perror("read standard input"); - return 0; - } - n += i; - ptr = buf; - p = buf; - q = out; - r = out; - tagging = 0; - inarg = 0; - prespace = 1; - quoted = 0; - exec_com = 0; - while (p < buf + n && q < out + 4000) { - if (*p == '\n') { - if (tagging) { - tag = malloc(sizeof(struct tagstruct)); - tag->item = malloc(1 + p - s); - tag->next = taglist; - taglist = tag; - t = tag->item; - while (s < p) - *t++ = *s++; - *t = 0; - } - *q++ = *p++; - tagging = 0; - inarg = 0; - prespace = 1; - quoted = 0; - exec_com = 0; - ptr = p; - r = q; - } else if (tagging) { - *q++ = *p++; - } else if (!quoted && isspace(*p)) { - *q++ = *p++; - prespace = 1; - if (exec_com && s > exec + 2) { - tagging = 1; - s = p; - } - } else if (prespace && *p == '|') { - tagging = 1; - *q++ = *p++; - s = p; - } else if (prespace && *p == '>') { - tagging = 1; - *q++ = *p++; - if (*p != '\n' && (*p == '!' || *p == '>')) - *q++ = *p++; - s = p; - } else { - prespace = 0; - if (*p == '"') { - quoted = !quoted; - } else { - if (!inarg && *p != '?') { - s = exec; - exec_com = 1; - inarg = 1; - } - if (exec_com && *s && *s++ != *p) - exec_com = 0; - } - *q++ = *p++; - } - } - p = buf; - while (ptr < buf + n) - *p++ = *ptr++; - *p = 0; - ptr = out; - n = r - out; - if (auxfi) { - fwrite(out, n, 1, auxfi); - } - while (n > 0) { -#ifndef _WIN32 - nbytes = write(sock, ptr, n); -#else - nbytes = send(sock, ptr, n, 0); -#endif - if (nbytes <= 0) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - perror("write server socket"); - return 0; - } - ptr += nbytes; - n -= nbytes; - } - return 1; -} - -static int -sendeof(int sock) -{ -#ifndef _WIN32 - if (write(sock, "ctld\n", 5) < 5) { -#else - if (send(sock, "ctld\n", 5, 0) < 5) { -#endif - fprintf(stderr, "sendeof: EOF send failed\n"); -#ifdef _WIN32 - closesocket(sock); -#else - close(sock); -#endif - return 0; - } - return 1; -}