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