]> git.pond.sub.org Git - empserver/blob - src/client/servcmd.c
client: Add readline support to empire client
[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         prompt(code, the_prompt, teles);
93         executing = 0;
94         break;
95     case C_FLUSH:
96         snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
97         prompt(code, the_prompt, teles);
98         break;
99     case C_EXECUTE:
100         fd = doexecute(arg);
101         if (fd >= 0)
102             executing = 1;
103         return fd;
104     case C_EXIT:
105         printf("Exit: %s", arg);
106         if (auxfp)
107             fprintf(auxfp, "Exit: %s", arg);
108         break;
109     case C_FLASH:
110         printf("\n%s", arg);
111         if (auxfp)
112             fprintf(auxfp, "\n%s", arg);
113         break;
114     case C_INFORM:
115         if (arg[0] != '\n') {
116             snprintf(teles, sizeof(teles), "(%.*s) ", len - 1, arg);
117             if (!redir_fp) {
118                 putchar('\07');
119                 prompt(code, the_prompt, teles);
120             }
121         } else
122             teles[0] = 0;
123         break;
124     case C_PIPE:
125         dopipe(arg);
126         break;
127     case C_REDIR:
128         doredir(arg);
129         break;
130     default:
131         assert(0);
132         break;
133     }
134
135     return 0;
136 }
137
138 static void
139 prompt(int code, char *prompt, char *teles)
140 {
141     char *nl;
142     char pr[1024];
143
144     nl = code == C_PROMPT || code == C_INFORM ? "\n" : "";
145     snprintf(pr, sizeof(pr), "%s%s", teles, prompt);
146 #ifdef HAVE_LIBREADLINE
147     rl_set_prompt(pr);
148     printf("%s", nl);
149     rl_forced_update_display();
150 #else  /* !HAVE_LIBREADLINE */
151     printf("%s%s", nl, pr);
152     fflush(stdout);
153 #endif /* !HAVE_LIBREADLINE */
154     if (auxfp) {
155         fprintf(auxfp, "%s%s%s", nl, teles, prompt);
156         fflush(auxfp);
157     }
158 }
159
160 static char *
161 fname(char *s)
162 {
163     char *beg, *end;
164
165     for (beg = s; isspace(*(unsigned char *)beg); beg++) ;
166     for (end = beg; !isspace(*(unsigned char *)end); end++) ;
167     *end = 0;
168     return beg;
169 }
170
171 static int
172 common_authorized(char *arg, char *attempt)
173 {
174     if (restricted) {
175         fprintf(stderr, "Can't %s in restricted mode\n", attempt);
176         return 0;
177     }
178
179     if (executing) {
180         fprintf(stderr, "Can't %s in a batch file\n", attempt);
181         return 0;
182     }
183     return 1;
184 }
185
186 static int
187 redir_authorized(char *arg, char *attempt)
188 {
189     if (redir_fp) {
190         fprintf(stderr, "Warning: dropped conflicting %s %s",
191                 attempt, arg);
192         return 0;
193     }
194
195     if (!seen_input(arg)) {
196         fprintf(stderr, "Warning: server attempted to %s %s",
197                 attempt, arg);
198         return 0;
199     }
200
201     return common_authorized(arg, attempt);
202 }
203
204 static int
205 exec_authorized(char *arg)
206 {
207     if (!seen_exec_input(arg)) {
208         fprintf(stderr,
209                 "Warning: server attempted to execute batch file %s", arg);
210         return 0;
211     }
212
213     return common_authorized(arg, "execute batch file");
214 }
215
216 static void
217 doredir(char *p)
218 {
219     int mode;
220     int fd;
221
222     if (!redir_authorized(p, "redirect to file"))
223         return;
224     if (*p++ != '>') {
225         fprintf(stderr, "Warning: dropped weird redirection %s", p);
226         return;
227     }
228
229     mode = O_WRONLY | O_CREAT;
230     if (*p == '>') {
231         mode |= O_APPEND;
232         p++;
233     } else if (*p == '!') {
234         mode |= O_TRUNC;
235         p++;
236     } else
237         mode |= O_EXCL;
238
239     p = fname(p);
240     if (*p == 0) {
241         fprintf(stderr, "Redirection lacks a file name\n");
242         return;
243     }
244
245     redir_is_pipe = 0;
246     fd = open(p, mode, 0666);
247     redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
248     if (!redir_fp) {
249         fprintf(stderr, "Can't redirect to %s: %s\n",
250                 p, strerror(errno));
251     }
252 }
253
254 static void
255 dopipe(char *p)
256 {
257     if (!redir_authorized(p, "pipe to shell command"))
258         return;
259     if (*p++ != '|') {
260         fprintf(stderr, "Warning: dropped weird pipe %s", p);
261         return;
262     }
263
264     for (; *p && isspace(*p); p++) ;
265     if (*p == 0) {
266         fprintf(stderr, "Redirection lacks a command\n");
267         return;
268     }
269
270     /* strip newline */
271     p[strlen(p) - 1] = 0;
272
273     redir_is_pipe = 1;
274     errno = 0;
275     if ((redir_fp = popen(p, "w")) == NULL) {
276         fprintf(stderr, "Can't redirect to pipe %s%s%s\n",
277                 p, errno ? ": " : "", errno ? strerror(errno) : "");
278     }
279 }
280
281 static int
282 doexecute(char *p)
283 {
284     int fd;
285
286     if (!exec_authorized(p))
287         return -1;
288
289     p = fname(p);
290     if (*p == 0) {
291         fprintf(stderr, "Need a file to execute\n");
292         return -1;
293     }
294
295     if ((fd = open(p, O_RDONLY)) < 0) {
296         fprintf(stderr, "Can't open batch file %s: %s\n",
297                 p, strerror(errno));
298         return -1;
299     }
300
301     return fd;
302 }
303
304 void
305 outch(char c)
306 {
307     if (auxfp)
308         putc(c, auxfp);
309     if (redir_fp)
310         putc(c, redir_fp);
311     else if (eight_bit_clean) {
312         if (c == 14)
313             putso();
314         else if (c == 15)
315             putse();
316         else
317             putchar(c);
318     } else if (c & 0x80) {
319         putso();
320         putchar(c & 0x7f);
321         putse();
322     } else
323         putchar(c);
324 }