1. /**
  2. * Copyright (C) 2007 by Marcelo Jorge Vieira (metal) <metal@alucinados.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. *
  19. * Public License can be found at http://www.gnu.org/copyleft/gpl.html
  20. *
  21. * @author Thadeu Cascardo <cascardo@minaslivre.org>
  22. * @author Marcelo Jorge Vieira (metal) <metal@alucinados.com>
  23. *
  24. */
  25.  
  26. #include <glib.h>
  27. #include "velha.h"
  28.  
  29. gboolean myread (GIOChannel *, GIOCondition, gpointer);
  30.  
  31. typedef struct _client_t client_t;
  32.  
  33. struct _client_t
  34. {
  35. char *table;
  36. gint game;
  37. char turn;
  38. GString *buffer;
  39. client_t *partner;
  40. GIOChannel *channel;
  41. guint watch;
  42. };
  43.  
  44. GList *clients = NULL;
  45.  
  46. struct
  47. {
  48. char *name;
  49. void (*func) (char*, int r, client_t*);
  50. }
  51. commands[] =
  52. {
  53. { "play", play_cb },
  54. { "start", start_cb },
  55. { "join", join_cb },
  56. { "list", list_cb },
  57. { "quit", quit_cb },
  58. { NULL, NULL }
  59. };
  60.  
  61.  
  62. client_t *
  63. client_new (int fd)
  64. {
  65. client_t *client;
  66.  
  67. client = g_slice_new (client_t);
  68. client->table = NULL;
  69. client->game = -1;
  70. client->buffer = g_string_sized_new (BUFSIZ);
  71. client->partner = NULL;
  72. client->channel = g_io_channel_unix_new (fd);
  73. g_io_channel_set_close_on_unref (client->channel, TRUE);
  74. g_io_channel_set_flags (client->channel, g_io_channel_get_flags (client->channel) | G_IO_FLAG_NONBLOCK, NULL);
  75. client->watch = g_io_add_watch (client->channel, G_IO_IN, myread, client);
  76. return client;
  77. }
  78.  
  79. void client_destroy (client_t *client)
  80. {
  81. clients = g_list_remove (clients, client);
  82. if (client->table)
  83. g_slice_free1 (9, client->table);
  84. if (client->buffer)
  85. g_string_free (client->buffer, TRUE);
  86. if (client->channel)
  87. g_io_channel_unref (client->channel);
  88. if (client->watch)
  89. g_source_remove (client->watch);
  90. g_slice_free (client_t, client);
  91. }
  92.  
  93. void
  94. client_write (client_t *client, GString *str)
  95. {
  96. int fd;
  97.  
  98. fd = g_io_channel_unix_get_fd (client->channel);
  99. write (fd, str->str, str->len);
  100. }
  101.  
  102. void
  103. cancel_game (client_t *client)
  104. {
  105. clients = g_list_remove (clients, client);
  106.  
  107. if (client->partner)
  108. {
  109. GString *str;;
  110. str = g_string_new ("CANCEL\r\n");
  111. client_write (client->partner, str);
  112. g_string_free (str, TRUE);
  113. client_destroy (client->partner);
  114. client->partner = NULL;
  115. client->table = NULL;
  116. }
  117.  
  118. if (client->table)
  119. {
  120. g_free (client->table);
  121. client->table = NULL;
  122. }
  123. client->game = -1;
  124. }
  125.  
  126. gint
  127. client_compare (client_t * a, client_t * b)
  128. {
  129. return a->game - b->game;
  130. }
  131.  
  132. gboolean
  133. new_game (client_t *client)
  134. {
  135. GList *l;
  136. GList *last;
  137. client_t *other;
  138. gint prev = -1;
  139.  
  140. if (clients == NULL)
  141. {
  142. client->game = 0;
  143. clients = g_list_prepend (clients, client);
  144. return TRUE;
  145. }
  146.  
  147. for (l = g_list_first (clients); l != NULL; l = g_list_next (l))
  148. {
  149. other = l->data;
  150. if (other->game > prev + 1)
  151. {
  152. client->game = prev + 1;
  153. clients = g_list_insert_sorted (l, client, (GCompareFunc) client_compare);
  154. return TRUE;
  155. }
  156. prev = other->game;
  157. last = l;
  158. }
  159.  
  160. if (l == NULL)
  161. {
  162. other = last->data;
  163. if (other->game == G_MAXINT)
  164. return FALSE;
  165. client->game = other->game + 1;
  166. clients = g_list_insert_sorted (last, client, (GCompareFunc) client_compare);
  167. return TRUE;
  168. }
  169. return FALSE;
  170. }
  171.  
  172. void
  173. start_cb (char *buffer, int len, client_t *client)
  174. {
  175. if (client->game != -1)
  176. {
  177. cancel_game (client);
  178. }
  179.  
  180. if (new_game (client) == FALSE)
  181. {
  182. cancel_game (client);
  183. client_destroy (client);
  184. }
  185. else
  186. {
  187. GString *str;
  188. str = g_string_sized_new (BUFSIZ);
  189. g_string_printf (str, "%d\r\n", client->game);
  190. client_write (client, str);
  191. g_string_free (str, TRUE);
  192. }
  193. }
  194.  
  195. void
  196. quit_cb (char *buffer, int len, client_t *client)
  197. {
  198. cancel_game (client);
  199. client_destroy (client);
  200. }
  201.  
  202. void
  203. win_game (client_t *client)
  204. {
  205. GString *str;
  206.  
  207. str = g_string_new ("200 WIN\r\n");
  208. client_write (client, str);
  209. str = g_string_assign (str, "200 LOOSE\r\n");
  210. client_write (client->partner, str);
  211. g_string_free (str, TRUE);
  212. cancel_game (client);
  213. client_destroy (client);
  214. }
  215.  
  216. void tie_game (client_t *client)
  217. {
  218. GString *str;
  219.  
  220. str = g_string_new ("200 TIE\r\n");
  221. client_write (client, str);
  222. client_write (client->partner, str);
  223. g_string_free (str, TRUE);
  224. cancel_game (client);
  225. client_destroy (client);
  226. }
  227.  
  228. void
  229. check_game (client_t *client, int pos)
  230. {
  231. int line;
  232. int column;
  233.  
  234. line = pos % 3;
  235. column = pos / 3;
  236.  
  237. if ((client->table[INDEX(column, 0)] == client->table[INDEX(column, 1)] &&
  238. client->table[INDEX(column, 0)] == client->table[INDEX(column, 2)]) ||
  239. (client->table[INDEX(0, line)] == client->table[INDEX(1, line)] &&
  240. client->table[INDEX(0, line)] == client->table[INDEX(2, line)]) ||
  241. ((pos == 4 || pos % 4 == 2) &&
  242. client->table[INDEX(0,2)] == client->table[INDEX(1,1)] &&
  243. client->table[INDEX(0,2)] == client->table[INDEX(2,0)]) ||
  244. ((pos == 4 || pos % 4 == 0) &&
  245. client->table[INDEX(2,2)] == client->table[INDEX(1,1)] &&
  246. client->table[INDEX(2,2)] == client->table[INDEX(0,0)]))
  247. {
  248. win_game (client);
  249. }
  250. else if (memchr (client->table, 0, 9) == NULL)
  251. {
  252. tie_game (client);
  253. }
  254. }
  255.  
  256. void
  257. play_cb (char *buffer, int len, client_t *client)
  258. {
  259. char *p;
  260. char *end;
  261. int pos;
  262. GString *str;
  263.  
  264. if (client->partner == NULL)
  265. {
  266. GString *str;
  267. str = g_string_new ("400 Wait for your partner\r\n");
  268. client_write (client, str);
  269. g_string_free (str, TRUE);
  270. return;
  271. }
  272.  
  273. if (client->turn == 0)
  274. {
  275. GString *str;
  276. str = g_string_new ("400 It is not your turn! Duh!\r\n");
  277. client_write (client, str);
  278. g_string_free (str, TRUE);
  279. return;
  280. }
  281. buffer[len - 1] = 0;
  282. errno = 0;
  283. p = buffer + sizeof ("play");
  284. pos = strtol (p, &end, 0);
  285.  
  286. if (end == p || errno != 0 || pos < 0 || pos > 8)
  287. {
  288. GString *str;
  289. str = g_string_new ("400 Invalid message format\r\n");
  290. client_write (client, str);
  291. g_string_free (str, TRUE);
  292. return;
  293. }
  294.  
  295. if (client->table[pos] != 0)
  296. {
  297. GString *str;
  298. str = g_string_new ("400 Already played. Choose another.\r\n");
  299. client_write (client, str);
  300. g_string_free (str, TRUE);
  301. return;
  302. }
  303.  
  304. client->table[pos] = client->turn;
  305. client->partner->turn = client->turn == 1 ? 2 : 1;
  306. client->turn = 0;
  307. str = g_string_sized_new (BUFSIZ);
  308. g_string_printf (str, "PLAY %d\r\n", pos);
  309. client_write (client->partner, str);
  310. g_string_printf (str, "200 OK\r\n");
  311. client_write (client, str);
  312. g_string_free (str, TRUE);
  313. check_game (client, pos);
  314. }
  315.  
  316. void
  317. join_cb (char *buffer, int len, client_t *client)
  318. {
  319. char *p;
  320. char *end;
  321. gint game;
  322. GList *l;
  323. client_t *other;
  324. GString *str;
  325.  
  326. if (client->game)
  327. cancel_game (client);
  328.  
  329. buffer[len - 1] = 0;
  330. errno = 0;
  331. p = buffer + sizeof ("join");
  332. game = strtol (p, &end, 0);
  333.  
  334. if (end == p || errno != 0 || game < 0 || game > G_MAXINT)
  335. {
  336. str = g_string_new ("400 Invalid message format\r\n");
  337. client_write (client, str);
  338. g_string_free (str, TRUE);
  339. return;
  340. }
  341.  
  342. client->game = game;
  343. l = g_list_find_custom (clients, client, (GCompareFunc) client_compare);
  344.  
  345. if (l == NULL)
  346. {
  347. str = g_string_new ("404 Game not found\r\n");
  348. client_write (client, str);
  349. g_string_free (str, TRUE);
  350. return;
  351. }
  352.  
  353. other = l->data;
  354.  
  355. if (other->partner)
  356. {
  357. str = g_string_new ("403 Game already started! Go away!\r\n");
  358. client_write (client, str);
  359. g_string_free (str, TRUE);
  360. return;
  361. }
  362.  
  363. other->partner = client;
  364. client->partner = other;
  365. client->table = other->table = g_slice_alloc0 (9);
  366. client->turn = 1;
  367. other->turn = 0;
  368. str = g_string_new ("200 OK\r\n");
  369. client_write (client, str);
  370. g_string_free (str, TRUE);
  371. }
  372.  
  373. void
  374. list_cb (char *buffer, int len, client_t* client)
  375. {
  376. GString *str;
  377. GList *l;
  378. client_t *other;
  379.  
  380. str = g_string_sized_new (BUFSIZ);
  381. g_string_append (str, "START\r\n");
  382.  
  383. for (l = g_list_first (clients); l != NULL; l = g_list_next (l))
  384. {
  385. other = l->data;
  386. g_string_append_printf (str, "%d\r\n", other->game);
  387. }
  388.  
  389. g_string_append (str, "END\r\n");
  390. client_write (client, str);
  391. g_string_free (str, TRUE);
  392. }
  393.  
  394. gboolean
  395. myboo (client_t *client)
  396. {
  397. int n;
  398. char *p;
  399. char *buffer;
  400. int i;
  401.  
  402. for (n = 0, p = client->buffer->str; n < client->buffer->len; n++, p++)
  403. {
  404. if (*p == '\n' || (*p == '\r' && ++n < client->buffer->len && *(++p) == '\n'))
  405. break;
  406. }
  407.  
  408. if (n == client->buffer->len)
  409. {
  410. //g_debug ("Time to learn how to count, cascardo.");
  411. return FALSE;
  412. }
  413.  
  414. buffer = g_strndup (client->buffer->str, n + 1);
  415. g_string_erase (client->buffer, 0, n + 1);
  416. g_debug ("Command %s received", buffer);
  417.  
  418. for (i = 0; commands[i].name != NULL; i++)
  419. {
  420. //g_debug ("%d %s", i, commands[i].name);
  421. if (g_ascii_strncasecmp (commands[i].name, buffer, strlen (commands[i].name)) == 0)
  422. {
  423. //g_debug ("%s running", commands[i].name);
  424. commands[i].func (buffer, n + 1, client);
  425. break;
  426. }
  427. }
  428.  
  429. g_free (buffer);
  430. return TRUE;
  431. }
  432.  
  433. gboolean
  434. myread (GIOChannel *channel, GIOCondition cond, gpointer data)
  435. {
  436. client_t *client;
  437. int fd;
  438. char buffer[BUFSIZ];
  439. int r;
  440.  
  441. //g_debug ("New data");
  442. client = data;
  443. fd = g_io_channel_unix_get_fd (channel);
  444. r = read (fd, buffer, BUFSIZ);
  445.  
  446. if ((r < 0 && errno != EAGAIN) || r == 0)
  447. {
  448. g_debug ("Error or finished connection.");
  449. cancel_game (client);
  450. client_destroy (client);
  451. return FALSE;
  452. }
  453.  
  454. g_string_append_len (client->buffer, buffer, r);
  455.  
  456. while (myboo (client));
  457.  
  458. return TRUE;
  459. }
  460.  
  461. gboolean
  462. myaccept (GIOChannel *channel, GIOCondition cond, gpointer data)
  463. {
  464. int fd;
  465. int newfd;
  466. client_t *client;
  467.  
  468. fd = g_io_channel_unix_get_fd (channel);
  469. newfd = accept (fd, NULL, 0);
  470. client = client_new (newfd);
  471.  
  472. return TRUE;
  473. }
  474.  
  475. int main (int argc, char **argv)
  476. {
  477. int fd;
  478. GIOChannel *channel;
  479. struct sockaddr_in saddr;
  480. int reuseaddr ;
  481.  
  482. fd = socket (PF_INET, SOCK_STREAM, 0);
  483. saddr.sin_family = AF_INET;
  484. saddr.sin_port = htons (5555);
  485. saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
  486. reuseaddr = 1;
  487. setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr));
  488. bind (fd, (struct sockaddr*) &saddr, sizeof (struct sockaddr_in));
  489. listen (fd, 5);
  490. channel = g_io_channel_unix_new (fd);
  491. g_io_add_watch (channel, G_IO_IN, myaccept, NULL);
  492. g_main_loop_run (g_main_loop_new (g_main_context_default (), TRUE));
  493.  
  494. return 0;
  495. }
  496.