]> git.pond.sub.org Git - empserver/blob - src/client/servcmd.c
client: Redistribute work among prompt() and its callers
[empserver] / src / client / servcmd.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  servercmd.c: Change the state depending on the command from the server.
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1989
31  *     Steve McClure, 1998
32  *     Ron Koenderink, 2005
33  *     Markus Armbruster, 2005-2017
34  */
35
36 #include <config.h>
37
38 #include <assert.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "misc.h"
46 #include "proto.h"
47 #include "secure.h"
48
49 #ifdef HAVE_LIBREADLINE
50 #  if defined(HAVE_READLINE_READLINE_H)
51 #    include <readline/readline.h>
52 #  elif defined(HAVE_READLINE_H)
53 #    include <readline.h>
54 #  endif /* defined(HAVE_READLINE_H) */
55 #endif /* HAVE_LIBREADLINE */
56
57 int eight_bit_clean;
58 FILE *auxfp;
59 int restricted;
60
61 static FILE *redir_fp;
62 static int redir_is_pipe;
63 static int executing;
64
65 static void prompt(int, char *, char *);
66 static void doredir(char *p);
67 static void dopipe(char *p);
68 static int doexecute(char *p);
69
70 int
71 servercmd(int code, char *arg, int len)
72 {
73     static int nmin, nbtu, fd;
74     static char the_prompt[1024];
75     static char teles[64];
76
77     switch (code) {
78     case C_PROMPT:
79         if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) {
80             fprintf(stderr, "Warning: server sent malformed prompt %s",
81                     arg);
82         }
83         snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ",
84                  nmin, nbtu);
85         if (redir_fp) {
86             if (redir_is_pipe)
87                 (void)pclose(redir_fp);
88             else
89                 (void)fclose(redir_fp);
90             redir_fp = NULL;
91         }
92         outch('\n');
93         prompt(code, the_prompt, teles);
94         executing = 0;
95         break;
96     case C_FLUSH:
97         snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
98         prompt(code, the_prompt, teles);
99         break;
100     case C_EXECUTE:
101         fd = doexecute(arg);
102         if (fd >= 0)
103             executing = 1;
104         return fd;
105     case C_EXIT:
106         printf("Exit: %s", arg);
107         if (auxfp)
108             fprintf(auxfp, "Exit: %s", arg);
109         break;
110     case C_FLASH:
111         printf("\n%s", arg);
112         if (auxfp)
113             fprintf(auxfp, "\n%s", arg);
114         break;
115     case C_INFORM:
116         if (arg[0] != '\n') {
117             snprintf(teles, sizeof(teles), "(%.*s) ", len - 1, arg);
118             if (!redir_fp) {
119                 outch('\n');
120                 putchar('\07');
121                 prompt(code, the_prompt, teles);
122             }
123         } else
124             teles[0] = 0;
125         break;
126     case C_PIPE:
127         dopipe(arg);
128         break;
129     case C_REDIR:
130         doredir(arg);
131         break;
132     default:
133         assert(0);
134         break;
135     }
136
137     return 0;
138 }
139
140 static void
141 prompt(int code, char *prompt, char *teles)
142 {
143     char pr[1024];
144
145     snprintf(pr, sizeof(pr), "%s%s", teles, prompt);
146 #ifdef HAVE_LIBREADLINE
147     rl_set_prompt(pr);
148     rl_forced_update_display();
149 #else  /* !HAVE_LIBREADLINE */
150     printf("%s", pr);
151     fflush(stdout);
152 #endif /* !HAVE_LIBREADLINE */
153     if (auxfp) {
154         fprintf(auxfp, "%s%s", teles, prompt);
155         fflush(auxfp);
156     }
157 }
158
159 static char *
160 fname(char *s)
161 {
162     char *beg, *end;
163
164     for (beg = s; isspace(*(unsigned char *)beg); beg++) ;
165     for (end = beg; !isspace(*(unsigned char *)end); end++) ;
166     *end = 0;
167     return beg;
168 }
169
170 static int
171 common_authorized(char *arg, char *attempt)
172 {
173     if (restricted) {
174         fprintf(stderr, "Can't %s in restricted mode\n", attempt);
175         return 0;
176     }
177
178     if (executing) {
179         fprintf(stderr, "Can't %s in a batch file\n", attempt);
180         return 0;
181     }
182     return 1;
183 }
184
185 static int
186 redir_authorized(char *arg, char *attempt)
187 {
188     if (redir_fp) {
189         fprintf(stderr, "Warning: dropped conflicting %s %s",
190                 attempt, arg);
191         return 0;
192     }
193
194     if (!seen_input(arg)) {
195         fprintf(stderr, "Warning: server attempted to %s %s",
196                 attempt, arg);
197         return 0;
198     }
199
200     return common_authorized(arg, attempt);
201 }
202
203 static int
204 exec_authorized(char *arg)
205 {
206     if (!seen_exec_input(arg)) {
207         fprintf(stderr,
208                 "Warning: server attempted to execute batch file %s", arg);
209         return 0;
210     }
211
212     return common_authorized(arg, "execute batch file");
213 }
214
215 static void
216 doredir(char *p)
217 {
218     int mode;
219     int fd;
220
221     if (!redir_authorized(p, "redirect to file"))
222         return;
223     if (*p++ != '>') {
224         fprintf(stderr, "Warning: dropped weird redirection %s", p);
225         return;
226     }
227
228     mode = O_WRONLY | O_CREAT;
229     if (*p == '>') {
230         mode |= O_APPEND;
231         p++;
232     } else if (*p == '!') {
233         mode |= O_TRUNC;
234         p++;
235     } else
236         mode |= O_EXCL;
237
238     p = fname(p);
239     if (*p == 0) {
240         fprintf(stderr, "Redirection lacks a file name\n");
241         return;
242     }
243
244     redir_is_pipe = 0;
245     fd = open(p, mode, 0666);
246     redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
247     if (!redir_fp) {
248         fprintf(stderr, "Can't redirect to %s: %s\n",
249                 p, strerror(errno));
250     }
251 }
252
253 static void
254 dopipe(char *p)
255 {
256     if (!redir_authorized(p, "pipe to shell command"))
257         return;
258     if (*p++ != '|') {
259         fprintf(stderr, "Warning: dropped weird pipe %s", p);
260         return;
261     }
262
263     for (; *p && isspace(*p); p++) ;
264     if (*p == 0) {
265         fprintf(stderr, "Redirection lacks a command\n");
266         return;
267     }
268
269     /* strip newline */
270     p[strlen(p) - 1] = 0;
271
272     redir_is_pipe = 1;
273     errno = 0;
274     if ((redir_fp = popen(p, "w")) == NULL) {
275         fprintf(stderr, "Can't redirect to pipe %s%s%s\n",
276                 p, errno ? ": " : "", errno ? strerror(errno) : "");
277     }
278 }
279
280 static int
281 doexecute(char *p)
282 {
283     int fd;
284
285     if (!exec_authorized(p))
286         return -1;
287
288     p = fname(p);
289     if (*p == 0) {
290         fprintf(stderr, "Need a file to execute\n");
291         return -1;
292     }
293
294     if ((fd = open(p, O_RDONLY)) < 0) {
295         fprintf(stderr, "Can't open batch file %s: %s\n",
296                 p, strerror(errno));
297         return -1;
298     }
299
300     return fd;
301 }
302
303 void
304 outch(char c)
305 {
306     if (auxfp)
307         putc(c, auxfp);
308     if (redir_fp)
309         putc(c, redir_fp);
310     else if (eight_bit_clean) {
311         if (c == 14)
312             putso();
313         else if (c == 15)
314             putse();
315         else
316             putchar(c);
317     } else if (c & 0x80) {
318         putso();
319         putchar(c & 0x7f);
320         putse();
321     } else
322         putchar(c);
323 }