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