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