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