]> git.pond.sub.org Git - empserver/blob - src/client/main.c
(saveargv): Lame attempt at protecting users who foolishly specify
[empserver] / src / client / main.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  main.c: client main function
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Steve McClure, 1998
33  *     Ron Koenderink, 2004-2005
34  *     Markus Armbruster, 2005
35  */
36
37 #include <config.h>
38
39 #include <errno.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #if defined(_WIN32)
45 #include <io.h>
46 #else
47 #include <pwd.h>
48 #include <sys/time.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #endif
52
53 #include "ioqueue.h"
54 #include "misc.h"
55 #include "proto.h"
56 #include "tags.h"
57
58 #ifdef _WIN32
59 HANDLE hStdIn;
60 #endif
61
62 #define RETRY   3
63
64 int eight_bit_clean;
65 int sock;
66
67 static volatile sig_atomic_t interrupt;
68 static void intr(int sig);
69 static int handleintr(int);
70
71 int
72 main(int ac, char **argv)
73 {
74 #ifdef _WIN32
75     WORD wVersionRequested;
76     WSADATA WsaData;
77     int err;
78     fd_set readfds;
79     struct timeval tm;
80     DWORD stdinmode;
81     SECURITY_ATTRIBUTES security;
82     int bRedirected = 0;
83     char unamebuf[128];
84 #else
85     struct sigaction sa;
86     fd_set mask;
87     fd_set savemask;
88     int retry = 0;
89 #endif
90     struct ioqueue server;
91     int i, j;
92     char *ptr;
93     char *auxout_fname;
94     FILE *auxout_fp;
95     int n;
96     char *cname;
97     char *pname;
98     char *uname;
99     char *host;
100     char *port;
101     int send_kill = 0;
102     int utf8 = 0;
103
104 #ifdef _WIN32
105     /*
106      * stdout is unbuffered under Windows if connected to a character
107      * device, and putchar() screws up when printing multibyte strings
108      * bytewise to an unbuffered stream.  Switch stdout to line-
109      * buffered mode.  Unfortunately, ISO C allows implementations to
110      * screw that up, and of course Windows does.  Manual flushing
111      * after each prompt is required.
112      */
113     setvbuf(stdout, NULL, _IOLBF, 4096);
114     wVersionRequested = MAKEWORD(2, 0);
115     err = WSAStartup(wVersionRequested, &WsaData);
116     if (err != 0) {
117         printf("WSAStartup Failed, error code %d\n", err);
118         return FALSE;
119     }
120 #else
121     FD_ZERO(&mask);
122     FD_ZERO(&savemask);
123 #endif
124     auxout_fname = NULL;
125     auxout_fp = NULL;
126     for (i = j = 1; i < ac; ++i) {
127         ptr = argv[i];
128         if (strcmp(ptr, "-2") == 0) {
129             if (i + 1 >= ac) {
130                 fprintf(stderr, "-2: Missing filename!\n");
131                 exit(1);
132             }
133             auxout_fname = argv[i + 1];
134             ++i;
135             continue;
136         } else if (strcmp(ptr, "-k") == 0) {
137             send_kill = 1;
138             continue;
139         } else if (strcmp(ptr, "-u") == 0) {
140             utf8 = eight_bit_clean = 1;
141             continue;
142         }
143         argv[j] = argv[i];
144         ++j;
145     }
146     ac = j;
147     if (auxout_fname && (auxout_fp = fopen(auxout_fname, "a")) == NULL) {
148         fprintf(stderr, "Unable to open %s for append\n", auxout_fname);
149         exit(1);
150     }
151     getsose();
152     port = getenv("EMPIREPORT");
153     if (!port)
154         port = empireport;
155     host = getenv("EMPIREHOST");
156     if (!host)
157         host = empirehost;
158     sock = tcp_connect(host, port);
159     cname = getenv("COUNTRY");
160     if (ac > 1)
161         cname = argv[1];
162     pname = getenv("PLAYER");
163     if (ac > 2)
164         pname = argv[2];
165     uname = getenv("LOGNAME");
166     if (uname == NULL) {
167 #ifndef _WIN32
168         struct passwd *pwd;
169
170         pwd = getpwuid(getuid());
171         if (pwd == NULL) {
172             fprintf(stderr, "You don't exist.  Go away\n");
173             exit(1);
174         }
175         uname = pwd->pw_name;
176 #else
177         DWORD unamesize;
178
179         unamesize = sizeof(unamebuf);
180         if (GetUserName(unamebuf, &unamesize)) {
181             uname = unamebuf;
182             if ((unamesize <= 0 ) || (strlen(uname) <= 0))
183                 uname = "nobody";
184         } else
185             uname = "nobody";
186 #endif
187     }
188     if (!login(sock, uname, cname, pname, send_kill, utf8)) {
189 #ifdef _WIN32
190         closesocket(sock);
191 #else
192         close(sock);
193 #endif
194         exit(1);
195     }
196     ioq_init(&server, 2048);
197     io_init();
198 #ifndef _WIN32
199     FD_ZERO(&mask);
200     FD_SET(0, &savemask);
201     FD_SET(sock, &savemask);
202     sigemptyset(&sa.sa_mask);
203     sa.sa_flags = 0;
204     sa.sa_handler = intr;
205     sigaction(SIGINT, &sa, NULL);
206     sa.sa_handler = SIG_IGN;
207     sigaction(SIGPIPE, &sa, NULL);
208     while (FD_ISSET(sock, &savemask)) {
209         mask = savemask;
210         n = select(sock + 1, &mask, NULL, NULL, NULL);
211         if (interrupt) {
212             if (!handleintr(sock))
213                 break;
214             errno = 0;
215         }
216         if (n <= 0) {
217             if (errno == EINTR) {
218                 perror("select");
219                 (void)close(sock);
220                 FD_CLR(sock, &savemask);
221             }
222         } else {
223             if (FD_ISSET(0, &mask)) {
224                 if (!termio(0, sock, auxout_fp)) {
225                     if (retry++ >= RETRY) {
226                         FD_CLR(0, &savemask);
227                     }
228                 } else {
229                     retry = 0;
230                 }
231             }
232             if (FD_ISSET(sock, &mask)) {
233                 if (!serverio(sock, &server))
234                     FD_CLR(sock, &savemask);
235                 else
236                     servercmd(&server, auxout_fp);
237             }
238         }
239     }
240 #else  /* _WIN32 */
241     signal(SIGINT, intr);
242
243     bRedirected = 0;
244     tm.tv_sec = 0;
245     tm.tv_usec = 1000;
246
247     if (!isatty(fileno(stdin)))
248         bRedirected = 1;
249     else {
250         security.nLength = sizeof(SECURITY_ATTRIBUTES);
251         security.lpSecurityDescriptor = NULL;
252         security.bInheritHandle = TRUE;
253         hStdIn = CreateFile("CONIN$",
254                             GENERIC_READ | GENERIC_WRITE,
255                             FILE_SHARE_READ | FILE_SHARE_WRITE,
256                             &security, OPEN_EXISTING, (DWORD) NULL, NULL);
257         
258         if (hStdIn == INVALID_HANDLE_VALUE) {
259             printf("Error getting hStdIn.\n");
260             fflush(stdout);
261             exit(-3);
262         }
263         
264         err = GetConsoleMode(hStdIn, &stdinmode);
265         if (!err) {
266             printf("Error getting console mode.\n");
267             fflush(stdout);
268             exit(-4);
269         } else {
270             stdinmode |= ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT;
271             err = SetConsoleMode(hStdIn, stdinmode);
272             if (!err) {
273                 printf("Error setting console mode.\n");
274                 fflush(stdout);
275                 exit(-5);
276             }
277         }
278     }
279     while (1) {
280         FD_ZERO(&readfds);
281         FD_SET(sock, &readfds);
282         n = select(sock + 1, &readfds, NULL, NULL, &tm);
283         if (interrupt) {
284             if (!handleintr(sock))
285                 break;
286             errno = 0;
287         }
288         if (n < 0) {
289             if (errno == EINTR) {
290                 errno = WSAGetLastError();
291                 perror("select");
292                 (void)closesocket(sock);
293                 break;
294             }
295         } else {
296             if (bRedirected == 1) {
297                 if (!termio(0, sock, auxout_fp))
298                     bRedirected = -1;
299             } else if (bRedirected == 0) {
300                 if (WaitForSingleObject(hStdIn, 10) != WAIT_TIMEOUT) {
301                     termio(-1, sock, auxout_fp);
302                     FlushConsoleInputBuffer(hStdIn);
303                 }
304             }
305             if (FD_ISSET(sock, &readfds)) {
306                 if (!serverio(sock, &server))
307                     break;
308                 else
309                     servercmd(&server, auxout_fp);
310             }
311         }
312     }
313     if (bRedirected == 0)
314         CloseHandle(hStdIn);
315 #endif /* _WIN32 */
316     ioq_drain(&server);
317 #ifdef _WIN32
318     (void)closesocket(sock);
319 #else
320     (void)close(sock);
321 #endif
322     return 0;                   /* Shut the compiler up */
323 }
324
325 static void
326 intr(int sig)
327 {
328     interrupt = 1;
329 #ifdef _WIN32
330     signal(SIGINT, intr);
331 #endif
332 }
333
334 static int
335 handleintr(int s)
336 {
337     if (interrupt) {
338         /* tacky, but it works */
339 #if !defined(_WIN32)
340         if (write(s, "\naborted\n", 1 + 7 + 1) <= 0)
341 #else
342         if (send(s, "\naborted\n", 1 + 7 + 1, 0) <= 0)
343 #endif
344             return 0;
345         interrupt = 0;
346     }
347     return 1;
348 }