]> git.pond.sub.org Git - empserver/blobdiff - src/client/servcmd.c
Update copyright notice
[empserver] / src / client / servcmd.c
index 60cbb1664ed2e7c85c78e758257609d3c531427d..b818b0dedbb0fccdd00cf76c02e80b5ee1530ed2 100644 (file)
@@ -1,11 +1,11 @@
 /*
  *  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>
-#else
-#include <io.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);
 }