]> git.pond.sub.org Git - empserver/blob - src/lib/player/login.c
Remove redundant declaration.
[empserver] / src / lib / player / login.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, 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  *  login.c: Allow the player to login
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1994
32  *     Steve McClure, 2000
33  *     Markus Armbruster, 2004-2006
34  */
35
36 #include <config.h>
37
38 #include <string.h>
39 #include "prototypes.h"
40 #include "misc.h"
41 #include "proto.h"
42 #include "player.h"
43 #include "com.h"
44 #include "empthread.h"
45 #include "empio.h"
46 #include "match.h"
47 #include "nsc.h"
48 #include "nat.h"
49 #include "optlist.h"
50 #include "file.h"
51 #include "subs.h"
52 #include "common.h"
53 #include "gen.h"
54
55 #if !defined(_WIN32)
56 #include <netinet/in.h>
57 #endif
58 #include <errno.h>
59
60 static int client_cmd(void);
61 static int coun_cmd(void);
62 static int kill_cmd(void);
63 static int options_cmd(void);
64 static int pass_cmd(void);
65 static int play_cmd(void);
66 static int quit_cmd(void);
67 static int sanc_cmd(void);
68 static int user_cmd(void);
69
70 static struct cmndstr login_coms[] = {
71     {"client client-id...", 0, client_cmd, 0, 0},
72     {"coun country", 0, coun_cmd, 0, 0},
73     {"kill", 0, kill_cmd, 0, 0},
74     {"options option=value...", 0, options_cmd, 0, 0},
75     {"pass password", 0, pass_cmd, 0, 0},
76     {"play [user [country [password]]]", 0, play_cmd, 0, 0},
77     {"quit", 0, quit_cmd, 0, 0},
78     {"sanc", 0, sanc_cmd, 0, 0},
79     {"user name", 0, user_cmd, 0, 0},
80     {0, 0, 0, 0, 0}
81 };
82
83 /*ARGSUSED*/
84 void
85 player_login(void *ud)
86 {
87     char buf[128];
88     char space[128];
89     int ac;
90     int cmd;
91
92     player->proc = empth_self();
93
94     pr_id(player, C_INIT, "Empire server ready\n");
95
96     while (!io_eof(player->iop) && !io_error(player->iop)) {
97         io_output(player->iop, IO_WAIT);
98         if (io_gets(player->iop, buf, sizeof(buf)) < 0) {
99             io_input(player->iop, IO_WAIT);
100             continue;
101         }
102         ac = parse(buf, player->argp, NULL, space, NULL);
103         cmd = comtch(player->argp[0], login_coms, 0);
104         if (cmd < 0) {
105             pr_id(player, C_BADCMD, "Command %s not found\n", player->argp[0]);
106             continue;
107         }
108         switch (login_coms[cmd].c_addr()) {
109         case RET_OK:
110             break;
111         case RET_FAIL:
112             break;
113         case RET_SYN:
114             pr_id(player, C_BADCMD, "Usage %s\n", login_coms[cmd].c_form);
115             break;
116         default:
117             break;
118         }
119         if (player->state >= PS_SHUTDOWN)
120             break;
121     }
122     player->state = PS_SHUTDOWN;
123     if (!io_eof(player->iop)) {
124         pr_id(player, C_EXIT, "so long...\n");
125         io_noblocking(player->iop, 0);
126         while (io_output(player->iop, IO_WAIT) > 0) ;
127     }
128     player_delete(player);
129     empth_exit();
130     /*NOTREACHED*/
131 }
132
133 static int
134 client_cmd(void)
135 {
136     int i;
137
138     if (!player->argp[1])
139         return RET_SYN;
140
141     for (i = 1; player->argp[i]; ++i) {
142         if (i > 1)
143             strncat(player->client, " ", sizeof(player->client) - 1);
144         strncat(player->client, player->argp[i], sizeof(player->client) - 1);
145     }
146     player->client[sizeof(player->client) - 1] = '\0';
147     pr_id(player, C_CMDOK, "talking to %s\n", player->client);
148     return RET_OK;
149 }
150
151 static int
152 user_cmd(void)
153 {
154     if (!player->argp[1])
155         return RET_SYN;
156     strncpy(player->userid, player->argp[1], sizeof(player->userid) - 1);
157     player->userid[sizeof(player->userid) - 1] = '\0';
158     pr_id(player, C_CMDOK, "hello %s\n", player->userid);
159     return RET_OK;
160 }
161
162 static int
163 sanc_cmd(void)
164 {
165     struct nstr_item ni;
166     struct natstr nat;
167     int first = 1;
168
169     if (!opt_BLITZ) {
170         pr_id(player, C_BADCMD, "Command %s not found\n", player->argp[0]);
171         return RET_FAIL;
172     }
173
174     snxtitem_all(&ni, EF_NATION);
175     while (nxtitem(&ni, &nat)) {
176         if (nat.nat_stat != STAT_SANCT)
177             continue;
178         if (first) {
179             pr_id(player, C_DATA,
180                   "The following countries are still in sanctuary:\n");
181             first = 0;
182         }
183         pr_id(player, C_DATA, "%s\n", nat.nat_cnam);
184     }
185     if (first)
186         pr_id(player, C_CMDOK, "There are no countries in sanctuary\n");
187     else
188         pr_id(player, C_CMDOK, "\n");
189     return RET_OK;
190 }
191
192 static int
193 coun_cmd(void)
194 {
195     natid cnum;
196
197     if (!player->argp[1])
198         return RET_SYN;
199     if (natbyname(player->argp[1], &cnum) < 0) {
200         pr_id(player, C_CMDERR, "country %s does not exist\n", player->argp[1]);
201         return RET_FAIL;
202     }
203     player->cnum = cnum;
204     player->authenticated = 0;
205     pr_id(player, C_CMDOK, "country name %s\n", player->argp[1]);
206     return 0;
207 }
208
209 static int
210 pass_cmd(void)
211 {
212     if (!player->argp[1])
213         return RET_SYN;
214     if (player->cnum == 255) {
215         pr_id(player, C_CMDERR, "need country first\n");
216         return RET_FAIL;
217     }
218     if (!natpass(player->cnum, player->argp[1])) {
219         pr_id(player, C_CMDERR, "password bad, logging entry\n");
220         logerror("%s tried country #%d with %s",
221                  praddr(player), player->cnum, player->argp[1]);
222         return RET_FAIL;
223     }
224     player->authenticated = 1;
225     pr_id(player, C_CMDOK, "password ok\n");
226     logerror("%s using country #%d", praddr(player), player->cnum);
227     return RET_OK;
228 }
229
230 static int
231 options_cmd(void)
232 {
233     /*
234      * The option mechanism allows arbitrary string values, but so far
235      * all options are flags in struct player.  Should be easy to
236      * generalize if needed.
237      */
238     struct logoptstr {
239         char *name;
240         int val;
241     };
242     static struct logoptstr login_opts[] = {
243         { "utf-8", PF_UTF8 }
244     };
245     char **ap;
246     char *p;
247     int opt;
248     unsigned i;
249
250     if (!player->argp[1]) {
251         for (i = 0; i < sizeof(login_opts) / sizeof(*login_opts); ++i)
252             pr_id(player, C_DATA, "%s=%d\n",
253                   login_opts[i].name,
254                   (player->flags & login_opts[i].val) != 0);
255         pr_id(player, C_CMDOK, "\n");
256         return RET_OK;
257     }
258
259     for (ap = player->argp+1; *ap; ++ap) {
260         p = strchr(*ap, '=');
261         if (p)
262             *p++ = 0;
263         opt = stmtch(*ap, login_opts,
264                      offsetof(struct logoptstr, name),
265                      sizeof(struct logoptstr));
266         if (opt < 0) {
267             pr_id(player, C_BADCMD, "Option %s not found\n", *ap);
268             return RET_FAIL;
269         }
270         if (!p || atoi(p))
271             player->flags |= login_opts[opt].val;
272         else
273             player->flags &= ~login_opts[opt].val;
274     }
275
276     pr_id(player, C_CMDOK, "Accepted\n");
277
278     return RET_OK;
279 }
280
281 static int
282 may_play(void)
283 {
284     struct natstr *np;
285
286     if (player->cnum == 255 || !player->authenticated) {
287         pr_id(player, C_CMDERR, "need country and password\n");
288         return 0;
289     }
290     /* TODO strstr() cheesy, compare IP against IP/BITS ... */
291     np = getnatp(player->cnum);
292     if (np->nat_stat == STAT_GOD && *privip
293         && !strstr(privip, player->hostaddr)) {
294         logerror("Deity login from untrusted host attempted by %s",
295                  praddr(player));
296         logerror("To allow this, add %s to econfig key privip",
297                  player->hostaddr);
298         pr_id(player, C_EXIT,
299               "Deity login not allowed from this IP!"
300               "  See log for help on how to allow it.\n");
301         return 0;
302     }
303     return 1;
304 }
305
306 static int
307 play_cmd(void)
308 {
309     struct player *other;
310     natid cnum;
311     struct natstr *natp;
312     char **ap;
313
314     ap = player->argp;
315     if (*++ap) {
316         strncpy(player->userid, *ap, sizeof(player->userid) - 1);
317         player->userid[sizeof(player->userid) - 1] = '\0';
318         player->authenticated = 0;
319     }
320     if (*++ap) {
321         if (natbyname(*ap, &cnum) < 0) {
322             pr_id(player, C_CMDERR, "country %s does not exist\n", *ap);
323             return RET_FAIL;
324         }
325     }
326     if (*++ap) {
327         if (!natpass(cnum, *ap)) {
328             pr_id(player, C_CMDERR, "password bad, logging entry\n");
329             logerror("%s tried country #%d with %s",
330                      praddr(player), cnum, *ap);
331             return RET_FAIL;
332         }
333         player->cnum = cnum;
334         player->authenticated = 1;
335     }
336     if (!may_play())
337         return RET_FAIL;
338     other = getplayer((natid)player->cnum);
339     if (other) {
340         natp = getnatp(player->cnum);
341         if (natp->nat_stat != STAT_VIS) {
342             pr_id(player, C_EXIT, "country in use by %s\n", praddr(other));
343         } else {
344             pr_id(player, C_EXIT, "country in use\n");
345         }
346         return RET_FAIL;
347     }
348     logerror("%s logged in as country #%d", praddr(player), player->cnum);
349     pr_id(player, C_INIT, "%d\n", CLIENTPROTO);
350     player_main(player);
351     logerror("%s logged out, country #%d", praddr(player), player->cnum);
352     player->state = PS_SHUTDOWN;
353     return RET_OK;
354 }
355
356 static int
357 kill_cmd(void)
358 {
359     struct player *other;
360
361     if (!may_play())
362         return RET_FAIL;
363     other = getplayer(player->cnum);
364     if (!other) {
365         pr_id(player, C_EXIT, "country not in use\n");
366         return RET_FAIL;
367     }
368     logerror("%s killed country #%d", praddr(player), player->cnum);
369     io_shutdown(other->iop, IO_READ | IO_WRITE);
370     pr_id(player, C_EXIT, "closed socket of offending job\n");
371     return RET_OK;
372 }
373
374 static int
375 quit_cmd(void)
376 {
377     pr_id(player, C_EXIT, "so long\n");
378     io_shutdown(player->iop, IO_READ);
379     return RET_OK;
380 }