Mercurial > pidgin
comparison gtk/plugins/crazychat/cc_network.c @ 14191:009db0b357b5
This is a hand-crafted commit to migrate across subversion revisions
16854:16861, due to some vagaries of the way the original renames were
done. Witness that monotone can do in one revision what svn had to
spread across several.
author | Ethan Blanton <elb@pidgin.im> |
---|---|
date | Sat, 16 Dec 2006 04:59:55 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
14190:366be2ce35a7 | 14191:009db0b357b5 |
---|---|
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; | |
107 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account); | |
108 if (!conv) { | |
109 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name); | |
110 } | |
111 im = gaim_conversation_get_im_data(conv); | |
112 snprintf(buf, BUFSIZ, "%s%s!%d", CRAZYCHAT_INVITE_CODE, | |
113 gaim_network_get_my_ip(-1), cc->tcp_port); | |
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"); | |
134 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account); | |
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); | |
199 assert(inet_aton(gaim_network_get_my_ip(-1), | |
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 | |
211 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, session->name, account); | |
212 if (!conv) { | |
213 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, | |
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, | |
274 gaim_network_get_my_ip(-1)); | |
275 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, args->name, | |
276 args->account); | |
277 if (!conv) { | |
278 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, | |
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); | |
378 assert(inet_aton(gaim_network_get_my_ip(-1), | |
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 } |