11232
|
1 #include <assert.h>
|
|
2 #include <errno.h>
|
|
3 #include <string.h>
|
|
4 #include <gtk/gtk.h>
|
|
5 #include "conversation.h"
|
|
6 #include "network.h"
|
|
7 #include <sys/socket.h>
|
|
8 #include <sys/types.h>
|
|
9 #include <netinet/in.h>
|
|
10 #include <sys/time.h>
|
|
11 #include <unistd.h>
|
|
12 #include <fcntl.h>
|
|
13 #include "cc_network.h"
|
|
14 #include "cc_interface.h"
|
|
15 #include "util.h"
|
|
16
|
|
17 /* --- begin constant definitions --- */
|
|
18
|
|
19 #define NETWORK_TIMEOUT_DELAY 40 /* in ms */
|
|
20 #define MAX_ACCEPT_CHECKS 1000
|
|
21
|
|
22 /* --- begin type declarations --- */
|
|
23
|
|
24 struct accept_args {
|
|
25 GaimAccount *account;
|
|
26 struct crazychat *cc;
|
|
27 char *name;
|
|
28 guint32 peer_ip;
|
|
29 guint16 peer_port;
|
|
30 };
|
|
31
|
|
32 struct sock_accept_args {
|
|
33 GaimAccount *account;
|
|
34 struct cc_session *session;
|
|
35 };
|
|
36
|
|
37 /* --- begin function prototypes --- */
|
|
38
|
|
39 /**
|
|
40 * Creates a server socket and sends a response to the peer.
|
|
41 * @param account the gaim account sending the ready msg
|
|
42 * @param session the peer CrazyChat session
|
|
43 */
|
|
44 static void cc_net_send_ready(GaimAccount *account, struct cc_session *session);
|
|
45
|
|
46 /**
|
|
47 * Handles responses from the CrazyChat session invite dialog box.
|
|
48 * @param dialog the dialog box
|
|
49 * @param response the dialog box button response
|
|
50 * @param args account, crazychat global data, peer name
|
|
51 */
|
|
52 static void invite_handler(GtkDialog *dialog, gint response,
|
|
53 struct accept_args *args);
|
|
54
|
|
55 /**
|
|
56 * Periodically checks the server socket for peer's connection. Gives up
|
|
57 * after a set number of checks.
|
|
58 * @param args peer session and account
|
|
59 * @return TRUE to continue checking, FALSE to stop
|
|
60 */
|
|
61 static gboolean accept_cb(struct sock_accept_args *args);
|
|
62
|
|
63 /**
|
|
64 * Initialize CrazyChat network session. Sets up the UDP socket and port.
|
|
65 * @param account the account the session is part of
|
|
66 * @param session the CrazyChat network session
|
|
67 */
|
|
68 static void init_cc_net_session(GaimAccount *account,
|
|
69 struct cc_session *session);
|
|
70
|
|
71 /**
|
|
72 * Handles checking the network for new feature data and sending out the
|
|
73 * latest features.
|
|
74 * @param session the session we're checking for network traffic
|
|
75 */
|
|
76 static gboolean network_cb(struct cc_session *session);
|
|
77
|
|
78 /**
|
|
79 * Generates random bytes in the user specified byte buffer.
|
|
80 * @param buf the byte buffer
|
|
81 * @param len length of the byte buffer
|
|
82 */
|
|
83 static void generate_randomness(uint8_t buf[], unsigned int len);
|
|
84
|
|
85 /**
|
|
86 * Sends data over a socket.
|
|
87 * @param s socket file descriptor
|
|
88 * @param buf data buffer
|
|
89 * @param len data buffer length
|
|
90 * @return number of bytes sent or -1 if an error occurred
|
|
91 */
|
|
92 static int __send(int s, char *buf, int len);
|
|
93
|
|
94 /* --- begin function definitions --- */
|
|
95
|
|
96 void cc_net_send_invite(struct crazychat *cc, char *name, GaimAccount *account)
|
|
97 {
|
|
98 struct cc_session *session;
|
|
99 GaimConversation *conv;
|
|
100 GaimConvIm *im;
|
|
101 char buf[BUFSIZ];
|
|
102
|
|
103 session = cc_find_session(cc, name);
|
|
104 if (session) return; /* already have a session with this guy */
|
|
105 session = cc_add_session(cc, name);
|
|
106 session->state = INVITE;
|
11338
|
107 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account);
|
11232
|
108 if (!conv) {
|
11338
|
109 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name);
|
11232
|
110 }
|
|
111 im = gaim_conversation_get_im_data(conv);
|
|
112 snprintf(buf, BUFSIZ, "%s%s!%d", CRAZYCHAT_INVITE_CODE,
|
11272
|
113 gaim_network_get_my_ip(-1), cc->tcp_port);
|
11232
|
114 Debug("Sent invite to %s for port: %d\n", name, cc->tcp_port);
|
|
115 gaim_conv_im_send(im, buf);
|
|
116 }
|
|
117
|
|
118 void cc_net_recv_invite(GaimAccount *account, struct crazychat *cc, char *name,
|
|
119 const char *peer_ip, const char *peer_port)
|
|
120 {
|
|
121 struct cc_session *session;
|
|
122 GaimConversation *conv;
|
|
123 GaimConvWindow *convwin;
|
|
124 char buf[BUFSIZ];
|
|
125 struct accept_args *args;
|
|
126
|
|
127 assert(cc);
|
|
128 assert(name);
|
|
129 Debug("Received a CrazyChat session invite from %s on port %s!\n",
|
|
130 name, peer_port);
|
|
131 session = cc_find_session(cc, name);
|
|
132 if (!session) {
|
|
133 Debug("Creating a CrazyChat session invite dialog box!\n");
|
11338
|
134 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account);
|
11232
|
135 if (conv) convwin = gaim_conversation_get_window(conv);
|
|
136 else convwin = NULL;
|
|
137 /* pop gtk window asking if want to accept */
|
|
138 GtkWidget *dialog =
|
|
139 gtk_dialog_new_with_buttons("CrazyChat Session Invite",
|
|
140 GTK_WINDOW(convwin),
|
|
141 GTK_DIALOG_MODAL |
|
|
142 GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
143 GTK_STOCK_OK,
|
|
144 GTK_RESPONSE_ACCEPT,
|
|
145 GTK_STOCK_CANCEL,
|
|
146 GTK_RESPONSE_REJECT,
|
|
147 NULL);
|
|
148 snprintf(buf, BUFSIZ, "Would you like to CRaZYchAT with %s?", name);
|
|
149 GtkWidget *label = gtk_label_new(buf);
|
|
150 gtk_container_add(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
|
|
151 label);
|
|
152 args = (struct accept_args*)malloc(sizeof(*args));
|
|
153 args->account = account;
|
|
154 args->cc = cc;
|
|
155 args->name = strdup(name);
|
|
156 assert(inet_aton(peer_ip, (struct in_addr*)&args->peer_ip));
|
|
157 args->peer_port = atoi(peer_port);
|
|
158
|
|
159 g_signal_connect(GTK_OBJECT(dialog), "response",
|
|
160 G_CALLBACK(invite_handler), args);
|
|
161
|
|
162 gtk_widget_show_all(dialog);
|
|
163 }
|
|
164 }
|
|
165
|
|
166 void cc_net_recv_accept(GaimAccount *account, struct crazychat *cc, char *name,
|
|
167 const char *peer_ip)
|
|
168 {
|
|
169 struct cc_session *session;
|
|
170 struct in_addr peer_addr;
|
|
171
|
|
172 assert(cc);
|
|
173 assert(name);
|
|
174 Debug("Received a CrazyChat session accept!\n");
|
|
175 session = cc_find_session(cc, name);
|
|
176 if (session && session->state == INVITE) {
|
|
177 session->state = ACCEPTED;
|
|
178 assert(inet_aton(peer_ip, &peer_addr));
|
|
179 session->peer_ip = peer_addr.s_addr;
|
|
180 cc_net_send_ready(account, session);
|
|
181 }
|
|
182 }
|
|
183
|
|
184 static void cc_net_send_ready(GaimAccount *account, struct cc_session *session)
|
|
185 {
|
|
186 struct sock_accept_args *args;
|
|
187
|
|
188 assert(session);
|
|
189 Debug("Initializing the server socket and sending ready message\n");
|
|
190 /* create the server socket */
|
|
191 session->tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
192 assert(session->tcp_sock != -1);
|
|
193 int reuse = 1;
|
|
194 assert(setsockopt(session->tcp_sock, SOL_SOCKET, SO_REUSEADDR,
|
|
195 &reuse, sizeof(int)) != -1);
|
|
196 struct sockaddr_in my_addr;
|
|
197 my_addr.sin_family = AF_INET;
|
|
198 my_addr.sin_port = htons(session->cc->tcp_port);
|
11272
|
199 assert(inet_aton(gaim_network_get_my_ip(-1),
|
11232
|
200 &my_addr.sin_addr));
|
|
201 memset(&my_addr.sin_zero, 0, sizeof(my_addr.sin_zero));
|
|
202 assert(bind(session->tcp_sock, (struct sockaddr*)&my_addr,
|
|
203 sizeof(my_addr)) != -1);
|
|
204 Debug("Listening on port: %d\n", my_addr.sin_port);
|
|
205 assert(listen(session->tcp_sock, 1) != -1);
|
|
206
|
|
207 /* socket created, send the ready message */
|
|
208 GaimConversation *conv;
|
|
209 GaimConvIm *im;
|
|
210
|
11338
|
211 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, session->name, account);
|
11232
|
212 if (!conv) {
|
11338
|
213 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account,
|
11232
|
214 session->name);
|
|
215 }
|
|
216 im = gaim_conversation_get_im_data(conv);
|
|
217 gaim_conv_im_send(im, CRAZYCHAT_READY_CODE);
|
|
218
|
|
219 /* register timer callback for checking socket connection */
|
|
220 args = (struct sock_accept_args*)malloc(sizeof(*args));
|
|
221 args->session = session;
|
|
222 args->account = account;
|
|
223 session->udp_sock = MAX_ACCEPT_CHECKS;
|
|
224 session->timer_id = g_timeout_add(NETWORK_TIMEOUT_DELAY,
|
|
225 (GSourceFunc)accept_cb, args);
|
|
226 }
|
|
227
|
|
228 void cc_net_recv_ready(GaimAccount *account, struct crazychat *cc, char *name)
|
|
229 {
|
|
230 struct cc_session *session;
|
|
231 struct sockaddr_in server_addr, my_addr;
|
|
232 int sock;
|
|
233
|
|
234 assert(cc);
|
|
235 assert(name);
|
|
236 Debug("Received a CrazyChat session ready!\n");
|
|
237 session = cc_find_session(cc, name);
|
|
238 if (session && session->state == ACCEPTED) {
|
|
239 /* connect to peer */
|
|
240 session->tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
241 assert(session->tcp_sock != -1);
|
|
242 server_addr.sin_family = AF_INET;
|
|
243 server_addr.sin_port = session->peer_port;
|
|
244 server_addr.sin_addr.s_addr = session->peer_ip;
|
|
245 memset(&(server_addr.sin_zero), 0,
|
|
246 sizeof(server_addr.sin_zero));
|
|
247 assert(connect(session->tcp_sock,
|
|
248 (struct sockaddr*)&server_addr,
|
|
249 sizeof(server_addr)) != -1);
|
|
250 Debug("Connecting to peer on port %d\n", session->peer_port);
|
|
251
|
|
252 /* now set state */
|
|
253 session->state = CONNECTED;
|
|
254 init_cc_net_session(account, session);
|
|
255 }
|
|
256 }
|
|
257
|
|
258 static void invite_handler(GtkDialog *dialog, gint response, struct accept_args *args)
|
|
259 {
|
|
260 struct cc_session *session;
|
|
261 char buf[BUFSIZ];
|
|
262 GaimConversation *conv;
|
|
263 GaimConvIm *im;
|
|
264
|
|
265 if (response == GTK_RESPONSE_ACCEPT) {
|
|
266 assert(args);
|
|
267 session = cc_find_session(args->cc, args->name);
|
|
268 assert(!session);
|
|
269 session = cc_add_session(args->cc, args->name);
|
|
270 session->state = ACCEPTED;
|
|
271 session->peer_ip = args->peer_ip;
|
|
272 session->peer_port = args->peer_port;
|
|
273 snprintf(buf, BUFSIZ, "%s%s", CRAZYCHAT_ACCEPT_CODE,
|
11272
|
274 gaim_network_get_my_ip(-1));
|
11338
|
275 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, args->name,
|
11232
|
276 args->account);
|
|
277 if (!conv) {
|
11338
|
278 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM,
|
11232
|
279 args->account, args->name);
|
|
280 }
|
|
281 im = gaim_conversation_get_im_data(conv);
|
|
282 gaim_conv_im_send(im, buf);
|
|
283 }
|
|
284 free(args->name);
|
|
285 free(args);
|
|
286 gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
287 }
|
|
288
|
|
289 static gboolean accept_cb(struct sock_accept_args *args)
|
|
290 {
|
|
291 fd_set fds;
|
|
292 struct timeval zero;
|
|
293 int ret;
|
|
294 GaimAccount *account;
|
|
295 struct cc_session *session;
|
|
296
|
|
297 assert(args);
|
|
298 account = args->account;
|
|
299 session = args->session;
|
|
300 assert(account);
|
|
301 assert(session);
|
|
302
|
|
303 /* set select to check on our tcp socket */
|
|
304 FD_ZERO(&fds);
|
|
305 FD_SET(session->tcp_sock, &fds);
|
|
306 memset(&zero, 0, sizeof(zero));
|
|
307
|
|
308 /* check socket */
|
|
309 ret = select(session->tcp_sock+1,&fds, NULL, NULL, &zero);
|
|
310 assert(ret != -1);
|
|
311
|
|
312 if (ret) { /* got something to check */
|
|
313 Debug("Checking pending connection\n");
|
|
314 int sock;
|
|
315 struct sockaddr_in client_addr;
|
|
316 socklen_t sin_size;
|
|
317
|
|
318 sin_size = sizeof(client_addr);
|
|
319 sock = accept(session->tcp_sock,
|
|
320 (struct sockaddr*)&client_addr, &sin_size);
|
|
321 assert(sock != -1);
|
|
322
|
|
323 /* check if it's a match */
|
|
324 if (client_addr.sin_addr.s_addr == session->peer_ip) {
|
|
325 /* cool, we're set */
|
|
326 Debug("Accepted tcp connect from %s\n", session->name);
|
|
327 close(session->tcp_sock);
|
|
328 session->tcp_sock = sock;
|
|
329 session->state = CONNECTED;
|
|
330 session->timer_id = 0;
|
|
331 init_cc_net_session(account, session);
|
|
332 Debug("Will start sending to port %d\n",
|
|
333 session->peer_port);
|
|
334 free(args);
|
|
335 return FALSE;
|
|
336 }
|
|
337 }
|
|
338
|
|
339 session->udp_sock--;
|
|
340
|
|
341 if (!session->udp_sock) { /* timed out */
|
|
342 /* remove session from session list */
|
|
343 cc_remove_session(session->cc, session);
|
|
344 free(args);
|
|
345 return FALSE;
|
|
346 }
|
|
347
|
|
348 return TRUE;
|
|
349 }
|
|
350
|
|
351 static void init_cc_net_session(GaimAccount *account,
|
|
352 struct cc_session *session)
|
|
353 {
|
|
354 struct sockaddr_in my_addr;
|
|
355 struct sockaddr_in peer_addr;
|
|
356 int reuse;
|
|
357
|
|
358 /* send/obtain the udp port information */
|
|
359
|
|
360 assert(__send(session->tcp_sock, (char*)&session->cc->udp_port,
|
|
361 sizeof(session->cc->udp_port)) ==
|
|
362 sizeof(session->cc->udp_port));
|
|
363 assert(recv(session->tcp_sock, (char*)&session->peer_port,
|
|
364 sizeof(session->peer_port), 0) ==
|
|
365 sizeof(session->peer_port));
|
|
366
|
|
367 Debug("Established a CrazyChat session with %s!\n", session->name);
|
|
368
|
|
369 /* connect the udp sockets */
|
|
370
|
|
371 session->udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
372
|
|
373 assert(!setsockopt(session->udp_sock, SOL_SOCKET, SO_REUSEADDR,
|
|
374 &reuse, sizeof(reuse)));
|
|
375
|
|
376 my_addr.sin_family = AF_INET;
|
|
377 my_addr.sin_port = htons(session->cc->udp_port);
|
11272
|
378 assert(inet_aton(gaim_network_get_my_ip(-1),
|
11232
|
379 &my_addr.sin_addr));
|
|
380 memset(my_addr.sin_zero, 0, sizeof(my_addr.sin_zero));
|
|
381 assert(!bind(session->udp_sock, (struct sockaddr*)&my_addr,
|
|
382 sizeof(my_addr)));
|
|
383 session->peer.sin_family = AF_INET;
|
|
384 session->peer.sin_port = htons(session->peer_port);
|
|
385 session->peer.sin_addr.s_addr = session->peer_ip;
|
|
386 memset(&session->peer.sin_zero, 0, sizeof(session->peer.sin_zero));
|
|
387
|
|
388 Debug("Bound udp sock to port %d, connecting to port %d\n",
|
|
389 session->cc->udp_port, session->peer_port);
|
|
390
|
|
391 memset(&session->features, 0, sizeof(session->features));
|
|
392
|
|
393 session->output = init_output(&session->features, session);
|
|
394
|
|
395 session->filter = Filter_Initialize();
|
|
396
|
|
397 /* initialize timer callback */
|
|
398 session->timer_id = g_timeout_add(NETWORK_TIMEOUT_DELAY,
|
|
399 (GSourceFunc)network_cb, session);
|
|
400
|
|
401 /* initialize input subsystem if not initialized */
|
|
402 if (!session->cc->features_state) {
|
|
403 session->cc->input_data = init_input(session->cc);
|
|
404 session->cc->features_state = 1;
|
|
405 }
|
|
406 }
|
|
407
|
|
408 static gboolean network_cb(struct cc_session *session)
|
|
409 {
|
|
410 fd_set fds;
|
|
411 struct timeval zero;
|
|
412 int ret;
|
|
413 int command;
|
|
414 struct cc_features *features;
|
|
415
|
|
416 assert(session);
|
|
417
|
|
418 Debug("Checking for data\n");
|
|
419
|
|
420 /* set select to check on our tcp socket */
|
|
421 FD_ZERO(&fds);
|
|
422 FD_SET(session->tcp_sock, &fds);
|
|
423 memset(&zero, 0, sizeof(zero));
|
|
424
|
|
425 /* check tcp socket */
|
|
426 ret = select(session->tcp_sock+1, &fds, NULL, NULL, &zero);
|
|
427 assert(ret != -1);
|
|
428
|
|
429 while (ret) {
|
|
430 ret = recv(session->tcp_sock, &command, sizeof(command), 0);
|
|
431 assert(ret != -1);
|
|
432 if (!ret) {
|
|
433 /* tcp connection closed, destroy connection */
|
|
434 gtk_widget_destroy(session->output->widget);
|
|
435 return FALSE;
|
|
436 }
|
|
437 assert(ret == sizeof(command));
|
|
438
|
|
439 FD_ZERO(&fds);
|
|
440 FD_SET(session->tcp_sock, &fds);
|
|
441 ret = select(session->tcp_sock+1, &fds, NULL, NULL, &zero);
|
|
442 assert(ret != -1);
|
|
443 }
|
|
444
|
|
445 /* set select to check on our udp socket */
|
|
446 FD_ZERO(&fds);
|
|
447 FD_SET(session->udp_sock, &fds);
|
|
448 memset(&zero, 0, sizeof(zero));
|
|
449
|
|
450 /* check udp socket */
|
|
451 ret = select(session->udp_sock+1, &fds, NULL, NULL, &zero);
|
|
452 assert(ret != -1);
|
|
453
|
|
454 features = &session->features;
|
|
455
|
|
456 while (ret) { /* have data, let's copy it for output */
|
|
457 struct sockaddr_in from;
|
|
458 int fromlen;
|
|
459 ret = recvfrom(session->udp_sock, &session->features,
|
|
460 sizeof(session->features),
|
|
461 0, (struct sockaddr*)&from, &fromlen);
|
|
462 Debug("Received %d bytes from port %d\n", ret,
|
|
463 ntohs(from.sin_port));
|
|
464 filter(features, session->filter);
|
|
465 Debug("\thead size: %d\n", features->head_size);
|
|
466 Debug("\topen: left(%s), right(%s), mouth(%s)\n",
|
|
467 features->left_eye_open ? "yes" : "no",
|
|
468 features->right_eye_open ? "yes" : "no",
|
|
469 features->mouth_open ? "yes" : "no");
|
|
470 Debug("\thead rotation: x(%d), y(%d), z(%d)\n",
|
|
471 features->head_x_rot, features->head_y_rot,
|
|
472 features->head_z_rot);
|
|
473 Debug("\tx(%d), y(%d)\n", features->x, features->y);
|
|
474 if (ret == -1) {
|
|
475 perror("wtf:");
|
|
476 }
|
|
477 assert(ret != -1);
|
|
478
|
|
479 FD_ZERO(&fds);
|
|
480 FD_SET(session->udp_sock, &fds);
|
|
481 ret = select(session->udp_sock+1, &fds, NULL, NULL, &zero);
|
|
482 assert(ret != -1);
|
|
483 }
|
|
484
|
|
485 #ifdef _DISABLE_QT_
|
|
486 struct cc_features bogus;
|
|
487 features = &bogus;
|
|
488 generate_randomness((uint8_t*)features, sizeof(*features));
|
|
489 #else
|
|
490 features = &session->cc->input_data->face;
|
|
491 #endif
|
|
492 assert(sendto(session->udp_sock, (char*)features,
|
|
493 sizeof(*features), 0, (struct sockaddr*)&session->peer,
|
|
494 sizeof(session->peer)) == sizeof(*features));
|
|
495 Debug("Sent %d bytes\n", sizeof(*features));
|
|
496 Debug("\thead size: %d\n", features->head_size);
|
|
497 Debug("\topen: left(%s), right(%s), mouth(%s)\n",
|
|
498 features->left_eye_open ? "yes" : "no",
|
|
499 features->right_eye_open ? "yes" : "no",
|
|
500 features->mouth_open ? "yes" : "no");
|
|
501 Debug("\thead rotation: x(%d), y(%d), z(%d)\n",
|
|
502 features->head_x_rot, features->head_y_rot,
|
|
503 features->head_z_rot);
|
|
504 Debug("\tx(%d), y(%d)\n", features->x, features->y);
|
|
505
|
|
506 /* clear easter egg */
|
|
507 features->mode = 0;
|
|
508
|
|
509 return TRUE;
|
|
510 }
|
|
511
|
|
512 static void generate_randomness(uint8_t buf[], unsigned int len)
|
|
513 {
|
|
514 int fd;
|
|
515
|
|
516 fd = open("/dev/random", O_RDONLY);
|
|
517 assert(fd != -1);
|
|
518
|
|
519 assert(read(fd, buf, len) == len);
|
|
520 close(fd);
|
|
521 }
|
|
522
|
|
523 static int __send(int s, char *buf, int len)
|
|
524 {
|
|
525 int total = 0; /* how many bytes we've sent */
|
|
526 int bytesleft = len; /* how many we have left to send */
|
|
527 int n;
|
|
528
|
|
529 while (total < len) {
|
|
530 n = send(s, buf + total, bytesleft, 0);
|
|
531 if (n == -1) {
|
|
532 Debug("ERROR: %s\n", strerror(errno));
|
|
533 return -1;
|
|
534 }
|
|
535 total += n;
|
|
536 bytesleft -= n;
|
|
537 }
|
|
538
|
|
539 return total;
|
|
540 }
|