/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
- * Ken Stevens, Steve McClure
+ * Copyright (C) 1986-2012, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Ken Stevens, Steve McClure, Markus Armbruster
*
- * This program is free software; you can redistribute it and/or modify
+ * Empire 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ---
*
- * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
- * related information and legal notices. It is expected that any future
- * projects/authors will amend these files as needed.
+ * 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.
*
* ---
*
* servercmd.c: Change the state depending on the command from the server.
- *
+ *
* Known contributors to this file:
* Dave Pare, 1989
* Steve McClure, 1998
+ * Ron Koenderink, 2005
+ * Markus Armbruster, 2005-2010
*/
-#include "misc.h"
-#include "proto.h"
-#include "queue.h"
-#include "ioqueue.h"
+#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <assert.h>
#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
-#if !defined(_WIN32)
-#include <unistd.h>
-#endif
-
-extern s_char *gettag();
+#include <stdio.h>
+#include <string.h>
+#include "misc.h"
+#include "proto.h"
+#include "secure.h"
-s_char num_teles[64];
-static s_char the_prompt[1024];
-static int mode;
-static int nbtu;
-static int nmin;
-FILE *redir_fp;
-FILE *pipe_fp;
-int exec_fd;
+int eight_bit_clean;
+FILE *auxfp;
-void prompt();
-void doredir();
-void dopipe();
-void doexecute();
-void output();
-void screen();
-int sendeof();
-int termio();
-void _noecho();
+static FILE *redir_fp;
+static int redir_is_pipe;
+static int executing;
+static size_t input_to_forget;
-extern s_char *SO;
-extern s_char *SE;
+static void prompt(int, char *, char *);
+static void doredir(char *p);
+static void dopipe(char *p);
+static int doexecute(char *p);
void
-servercmd(ioq, auxfi)
-struct ioqueue *ioq;
-FILE *auxfi;
+servercmd(int code, char *arg, int len)
{
- s_char *ioq_gets(struct ioqueue *, s_char *, int);
- s_char buf[1024];
- s_char *p;
- int code;
+ static int nmin, nbtu, fd;
+ static char the_prompt[1024];
+ static char teles[64];
- while (ioq_gets(ioq, buf, sizeof(buf))) {
- p = buf;
- while (*p && !isspace(*p))
- p++;
- *p++ = 0;
- if (isalpha(*buf))
- code = 10 + (*buf - 'a');
- else
- code = *buf - '0';
- switch (code) {
- case C_PROMPT:
- if (sscanf(p, "%d %d", &nbtu, &nmin) != 2) {
- fprintf(stderr, "prompt: bad server prompt %s\n", p);
- }
- mode = code;
- sprintf(the_prompt, "[%d:%d] Command : ", nbtu, nmin);
- prompt(auxfi);
- break;
- case C_REDIR:
- doredir(p);
- break;
- case C_PIPE:
- dopipe(p);
- break;
- case C_FLUSH:
- mode = code;
- sprintf(the_prompt, "%s", p);
- prompt(auxfi);
- break;
- case C_EXECUTE:
- doexecute(p, auxfi);
- break;
- case C_INFORM:
- if (*p) {
- p[strlen(p) - 1] = '\0';
- sprintf(num_teles, "(%s) ", p + 1);
- if (!redir_fp && !pipe_fp && !exec_fd) {
- putchar('\07');
- prompt(0);
- }
- } else
- *num_teles = '\0';
- break;
- default:
- output(code, p, auxfi);
- break;
+ switch (code) {
+ case C_PROMPT:
+ if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) {
+ fprintf(stderr, "prompt: bad server prompt %s\n", arg);
}
+ snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ",
+ nmin, nbtu);
+ if (redir_fp) {
+ if (redir_is_pipe)
+ (void)pclose(redir_fp);
+ else
+ (void)fclose(redir_fp);
+ redir_fp = NULL;
+ }
+ if (input_to_forget) {
+ forget_input(input_to_forget);
+ input_to_forget = 0;
+ }
+ prompt(code, the_prompt, teles);
+ executing = 0;
+ break;
+ case C_FLUSH:
+ snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
+ prompt(code, the_prompt, teles);
+ break;
+ case C_EXECUTE:
+ fd = doexecute(arg);
+ if (fd < 0)
+ send_eof++;
+ else {
+ input_fd = fd;
+ executing = 1;
+ }
+ 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[0] != '\n') {
+ snprintf(teles, sizeof(teles), "(%.*s) ", len - 1, arg);
+ if (!redir_fp) {
+ putchar('\07');
+ prompt(code, the_prompt, teles);
+ }
+ } else
+ teles[0] = 0;
+ break;
+ case C_PIPE:
+ dopipe(arg);
+ break;
+ case C_REDIR:
+ doredir(arg);
+ break;
+ default:
+ assert(0);
+ break;
}
}
-void
-prompt(auxfi)
-FILE *auxfi;
+static void
+prompt(int code, char *prompt, char *teles)
{
- if (mode == C_PROMPT) {
- if (redir_fp) {
- (void)fclose(redir_fp);
- redir_fp = 0;
- } else if (pipe_fp) {
-#ifndef _WIN32
- (void)pclose(pipe_fp);
-#endif
- pipe_fp = 0;
- } else if (exec_fd > 0) {
- close(exec_fd);
- close(0);
- exec_fd = -1;
- open("/dev/tty", O_RDONLY, 0);
- }
+ char *nl;
+
+ 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);
+ }
+}
+
+static char *
+fname(char *s)
+{
+ char *beg, *end;
+
+ for (beg = s; isspace(*(unsigned char *)beg); beg++) ;
+ for (end = beg; !isspace(*(unsigned char *)end); end++) ;
+ *end = 0;
+ return beg;
+}
+
+static int
+redir_authorized(char *arg, char *attempt, int expected)
+{
+ size_t seen = seen_input(arg);
+
+ if (executing) {
+ fprintf(stderr, "Can't %s in a batch file\n", attempt);
+ return 0;
}
- 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);
+
+ if (!expected) {
+ fprintf(stderr, "WARNING! Server attempted to %s unexpectedly\n",
+ attempt);
+ return 0;
}
+
+ if (!seen || (input_to_forget && input_to_forget != seen)) {
+ fprintf(stderr, "WARNING! Server attempted to %s %s\n",
+ attempt, arg);
+ return 0;
+ }
+ input_to_forget = seen;
+ return 1;
}
-/*
- * opens redir_fp if successful
- */
-void
-doredir(p)
-s_char *p;
+static void
+doredir(char *p)
{
- s_char *how;
- s_char *name;
- s_char *tag;
int mode;
int fd;
- if (redir_fp) {
- (void)fclose(redir_fp);
- redir_fp = 0;
- }
- how = p++;
- if (*p && ((*p == '>') || (*p == '!')))
- p++;
- tag = gettag(p);
- while (*p && isspace(*p))
- p++;
- name = p;
- while (*p && !isspace(*p))
- p++;
- *p = 0;
- if (tag == NULL) {
- fprintf(stderr, "WARNING! Server redirected output to file %s\n",
- name);
+ if (!redir_authorized(p, "redirect to file", !redir_fp))
+ return;
+ if (*p++ != '>') {
+ fprintf(stderr, "WARNING! Weird redirection %s", p);
return;
}
+
mode = O_WRONLY | O_CREAT;
- if (how[1] == '>')
+ if (*p == '>') {
mode |= O_APPEND;
- else if (how[1] == '!')
+ p++;
+ } else if (*p == '!') {
mode |= O_TRUNC;
- else
+ p++;
+ } else
mode |= O_EXCL;
- if (*name == 0) {
- fprintf(stderr, "Null file name after redirect\n");
- free(tag);
+
+ p = fname(p);
+ if (*p == 0) {
+ fprintf(stderr, "Redirection lacks a file name\n");
return;
}
- if ((fd = open(name, mode, 0600)) < 0) {
- fprintf(stderr, "Redirect open failed\n");
- perror(name);
- } else {
- redir_fp = fdopen(fd, "w");
+
+ redir_is_pipe = 0;
+ fd = open(p, mode, 0666);
+ redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
+ if (!redir_fp) {
+ fprintf(stderr, "Can't redirect to %s: %s\n",
+ p, strerror(errno));
}
- free(tag);
}
-/*
- * opens "pipe_fp" if successful
- */
-void
-dopipe(p)
-s_char *p;
+static void
+dopipe(char *p)
{
- s_char *tag;
-
- if (*p == '|')
- p++;
- tag = gettag(p);
- while (*p && isspace(*p))
- p++;
- if (tag == NULL) {
- fprintf(stderr, "WARNING! Server attempted to run: %s\n", p);
+ if (!redir_authorized(p, "pipe to shell command", !redir_fp))
+ return;
+ if (*p++ != '|') {
+ fprintf(stderr, "WARNING! Weird pipe %s", p);
return;
}
+
+ for (; *p && isspace(*p); p++) ;
if (*p == 0) {
- fprintf(stderr, "Null program name after redirect\n");
- free(tag);
+ fprintf(stderr, "Redirection lacks a command\n");
return;
}
-#ifndef _WIN32
- if ((pipe_fp = popen(p, "w")) == 0) {
-#else
- if (1) {
-#endif
- fprintf(stderr, "Pipe open failed\n");
- perror(p);
+
+ redir_is_pipe = 1;
+ if ((redir_fp = popen(p, "w")) == NULL) {
+ fprintf(stderr, "Can't redirect to pipe %s: %s\n",
+ p, strerror(errno));
}
- free(tag);
}
-void
-doexecute(p, auxfi)
-s_char *p;
-FILE *auxfi;
+static int
+doexecute(char *p)
{
- extern int sock;
int fd;
- s_char *tag;
- tag = gettag(p);
- while (*p && isspace(*p))
- p++;
- if (tag == NULL) {
- fprintf(stderr,
- "WARNING! Server attempted unauthorized read of file %s\n",
- p);
- return;
- }
- if (p == 0) {
- fprintf(stderr, "Null file to execute\n");
- free(tag);
- return;
- }
-#if !defined(_WIN32)
- if ((fd = open(p, O_RDONLY, 0)) < 0) {
-#else
- if ((fd = open(p, O_RDONLY | O_BINARY, 0)) < 0) {
-#endif
- fprintf(stderr, "Can't open execute file\n");
- perror(p);
- free(tag);
- 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);
- free(tag);
-}
+ if (!redir_authorized(p, "execute batch file", 1))
+ return -1;
-void
-output(code, buf, auxfi)
-int code;
-s_char *buf;
-FILE *auxfi;
-{
- switch (code) {
- case C_NOECHO:
- _noecho(0);
- break;
- case C_FLUSH:
- (void)fflush(stdout);
- if (auxfi)
- (void)fflush(auxfi);
- 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 (code == C_FLUSH)
- (void)fflush(auxfi);
- else
- (void)putc('\n', auxfi);
+ p = fname(p);
+ if (*p == 0) {
+ fprintf(stderr, "Need a file to execute\n");
+ return -1;
}
- if (redir_fp)
- fprintf(redir_fp, "%s\n", buf);
- else if (pipe_fp)
- fprintf(pipe_fp, "%s\n", buf);
- else {
- if (SO && SE)
- screen(buf);
- else
- fputs(buf, stdout);
- if (code == C_FLUSH)
- (void)fflush(stdout);
- else
- (void)putc('\n', stdout);
+ if ((fd = open(p, O_RDONLY)) < 0) {
+ fprintf(stderr, "Can't open execute file %s: %s\n",
+ p, strerror(errno));
+ return -1;
}
+
+ return fd;
}
void
-screen(buf)
-register s_char *buf;
+outch(char c)
{
- register s_char *sop;
- register s_char c;
-
- while ((c = *buf++)) {
- if (c & 0x80) {
- for (sop = SO; putc(*sop, stdout); sop++) ;
- (void)putc(c & 0x7f, stdout);
- for (sop = SE; putc(*sop, stdout); sop++) ;
- } else
- (void)putc(c, stdout);
- }
+ if (auxfp)
+ putc(c, auxfp);
+ if (redir_fp)
+ putc(c, redir_fp);
+ else if (eight_bit_clean) {
+ if (c == 14)
+ putso();
+ else if (c == 15)
+ putse();
+ else
+ putchar(c);
+ } else if (c & 0x80) {
+ putso();
+ putchar(c & 0x7f);
+ putse();
+ } else
+ putchar(c);
}