forked from zid/Diablo-MUD
/
client.c
191 lines (156 loc) · 3.41 KB
/
client.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "client.h"
#include "socket.h"
#include "buffer.h"
#include "login.h"
#include "character.h"
struct client {
struct buffer *buffer;
struct sockinfo *si;
struct character *ch;
int state;
};
static struct client **clients;
static int maxfd;
static void client_send(int cfd, const char *msg, int msg_len)
{
int r;
r = socket_send(cfd, msg, msg_len);
/* The client has disconnected, destroy it */
if(!r)
client_destroy(cfd);
}
static void login_ask_username(int cfd)
{
const char msg[] = "username: ";
client_send(cfd, msg, sizeof(msg) - 1);
}
static void login_ask_password(int cfd)
{
const char msg[] = "password: ";
client_send(cfd, msg, sizeof(msg) - 1);
}
static void send_prompt(int cfd)
{
const char msg[] = "\n> ";
client_send(cfd, msg, sizeof(msg) - 1);
}
static void handle_username(client *c)
{
const char *buf;
buf = buffer_get(c->buffer);
character_set_username(c->ch, buf);
}
static void parse(int cfd, client *c)
{
switch(c->state)
{
case CONNECTING:
login_send_banner(cfd);
login_ask_username(cfd);
c->state = USERNAME;
break;
case USERNAME:
handle_username(c);
login_ask_password(cfd);
c->state = PASSWORD;
break;
case PASSWORD:
/* TODO: check the username and password */
login_send_motd(cfd);
c->state = CONNECTED;
send_prompt(cfd);
break;
case CONNECTED:
/* TODO: parse the command prompt */
send_prompt(cfd);
break;
}
}
int client_init(int s)
{
sockinfo *i;
struct client *c;
int newfd;
i = socket_accept(s);
if(!i)
return 0;
/* We have the connection details, but not the actual
* file decriptor itself, use socket.c to extract it
* for us.
*/
newfd = socket_get(i);
/* First new client - list needs creating */
if(!clients)
{
clients = calloc(newfd, sizeof(struct client *));
maxfd = newfd;
}
/* This fd is the largest fd we've ever seen
* so the client array won't be big enough for it.
* Reallocate it big enough for the new fd
* and zero all of the memory between the
* previous biggest and the new biggest.
*/
if(newfd > maxfd)
{
clients = realloc(clients,
sizeof(struct client *) * (newfd + 1));
memset(&clients[maxfd + 1], 0,
(newfd - maxfd) * sizeof(struct client *));
maxfd = newfd;
}
c = malloc(sizeof(struct client));
c->si = i;
c->buffer = buffer_init();
c->ch = character_init();
c->state = CONNECTING;
clients[newfd] = c;
parse(newfd, c);
/* The server wants this fd so it can update the
* file descriptor read set.
*/
return newfd;
}
void client_destroy(int s)
{
client *c;
c = clients[s];
clients[s] = NULL;
socket_free(c->si);
buffer_free(c->buffer);
character_free(c->ch);
free(c);
}
/* s is the file descriptor of a client that has
* pending data. Turn it back into a struct client
* using the lookup table.
* Read data up to 256 characters from the socket.
* Returns the status of the client, true for alive
* or false for dead.
*/
int client_handle(int s)
{
client *c;
char buf[256];
int r;
c = clients[s];
/* Read up to 256 bytes from the client */
r = socket_read(s, buf, 256);
if(r == 0)
{
/* 0 bytes read means the client has disconnected */
client_destroy(s);
return 0;
}
/* Returns whether the buffer has something useful in it */
r = buffer_add(c->buffer, buf, r);
if(r)
{
// printf("Client %d said '%s'\n", s, buffer_get(c->buffer));
parse(s, c);
}
return 1;
}