2005-11-15 Carl Worth <cworth@cworth.org>
[ttt] / src / ttt-client.c
1 /* ttt-client.c - client handling code for tic-tac-toe game server
2  *
3  * Copyright © 2005 Carl Worth
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Author: Carl Worth <cworth@cworth.org>
20  */
21
22 #include "ttt-client.h"
23
24 #include "ttt-server.h"
25 #include "ttt-socket.h"
26 #include "ttt-error.h"
27
28 struct _ttt_client {
29     pthread_mutex_t mutex;
30     pthread_t thread;
31
32     ttt_server_t *server;
33     int socket;
34
35     int id;
36
37     char buf[TTT_CLIENT_BUF_SIZE];
38     char *buf_head;
39     char *buf_tail;
40
41     char *request;
42     int request_size;
43     int request_len;
44 };
45
46 static void
47 _ttt_client_init (ttt_client_t  *client,
48                   ttt_server_t  *server,
49                   int            socket)
50 {
51     pthread_mutex_init (&client->mutex, NULL);
52
53     client->server = server;
54     client->socket = socket;
55
56     client->buf_head = client->buf;
57     client->buf_tail = client->buf;
58
59     client->request = NULL;
60     client->request_size = 0;
61     client->request_len = 0;
62
63     /* XXX: Probably want to register only as the result of the HELO
64        command.  Not only will that match the protocol correctly, but
65        it will also eliminate a race condition here. */
66     client->id = ttt_server_register_client (server, client);
67 }
68
69 static void
70 _ttt_client_fini (ttt_client_t *client)
71 {
72     pthread_mutex_lock (&client->mutex);
73
74     ttt_server_unregister_client (client->server, client);
75
76     shutdown (client->socket, SHUT_RDWR);
77
78     free (client->request);
79
80     pthread_mutex_unlock (&client->mutex);
81
82     pthread_mutex_destroy (&client->mutex);
83 }
84
85 static void
86 _append_to_request (ttt_client_t        *client,
87                     const char          *buf,
88                     int                  size)
89 {
90     int size_needed = client->request_len + size;
91
92     if (size_needed > client->request_size) {
93         if (client->request_size == 0) {
94             client->request_size = size_needed;
95         } else {
96             while (size_needed > client->request_size)
97                 client->request_size *= 2;
98         }
99
100         client->request = xrealloc (client->request, client->request_size);
101     }
102
103     memcpy (client->request + client->request_len,
104             buf, size);
105
106     client->request_len += size;
107 }
108
109 static ttt_status_t
110 _read_into_request_until (ttt_client_t *client, char delimeter)
111 {
112     ttt_bool_t found_delimeter = FALSE;
113     int bytes_read;
114     char *s;
115
116     client->request_len = 0;
117
118     while (1) {
119
120         if (client->buf_tail >= client->buf_head) {
121             bytes_read = xread (client->socket,
122                                 client->buf,
123                                 TTT_CLIENT_BUF_SIZE);
124             if (bytes_read == 0)
125                 return TTT_STATUS_EOF;
126             client->buf_head = client->buf;
127             client->buf_tail = client->buf_head + bytes_read;
128         }
129
130         for (s = client->buf_head; s < client->buf_tail; s++) {
131             if (*s == delimeter) {
132                 found_delimeter = TRUE;
133                 s++;
134                 break;
135             }
136         }
137
138         _append_to_request (client,
139                             client->buf_head,
140                             s - client->buf_head);
141         client->buf_head = s;
142
143         if (found_delimeter)
144             return TTT_STATUS_SUCCESS;
145     }
146 }
147
148 static ttt_status_t
149 _read_request (ttt_client_t *client)
150 {
151     ttt_status_t status;
152     static const char null_terminator = '\0';
153
154     status = _read_into_request_until (client, '\n');
155     if (status)
156         return status;
157
158     _append_to_request (client, &null_terminator, 1);
159
160     return TTT_STATUS_SUCCESS;
161 }
162
163 static ttt_error_t
164 _execute_request (ttt_client_t *client)
165 {
166     ttt_server_broadcast (client->server, client->request);
167
168     return TTT_ERROR_NONE;
169 }
170
171 static void *
172 _handle_requests_thread (void *closure)
173 {
174     ttt_status_t status;
175     ttt_error_t error;
176     ttt_client_t *client = closure;
177
178     while (1) {
179
180         status = _read_request (client);
181         if (status == TTT_STATUS_EOF)
182             break;
183         if (status)
184             ASSERT_NOT_REACHED;
185
186         error = _execute_request (client);
187         if (error)
188             ttt_client_send (client, ttt_error_string (error));
189     }
190
191     _ttt_client_fini (client);
192     free (client);
193
194     return (void *) 0;
195 }
196
197 /* Exported: See ttt-client.h for documentation. */
198 void
199 ttt_client_new (void *closure, int client_socket)
200 {
201     ttt_server_t *server = closure;
202     ttt_client_t *client;
203     int err;
204
205     client = xmalloc (sizeof (ttt_client_t));
206     
207     _ttt_client_init (client, server, client_socket);
208
209     err = pthread_create (&client->thread, NULL,
210                           _handle_requests_thread, client);
211     if (err != 0) {
212         fprintf (stderr, "Error: pthread_create failed: %s. Aborting.\n",
213                  strerror (err));
214         exit (1);
215     }
216 }
217
218 /* Exported: See ttt-client.h for documentation. */
219 void
220 ttt_client_send (ttt_client_t *client, const char *message)
221 {
222     pthread_mutex_lock (&client->mutex);
223
224     ttt_socket_write (client->socket, message, strlen (message));
225
226     pthread_mutex_unlock (&client->mutex);
227 }
228
229 /* Exported: See ttt-client.h for documentation. */
230 int
231 ttt_client_get_id (ttt_client_t *client)
232 {
233     return client->id;
234 }