Mercurial > pidgin
view plugins/crazychat/cc_network.c @ 11927:0d9c6d2ad64b
[gaim-migrate @ 14218]
Better error handling for Howl when your mDNS daemon is not running
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Mon, 31 Oct 2005 06:06:14 +0000 |
parents | 17142948653e |
children |
line wrap: on
line source
#include <assert.h> #include <errno.h> #include <string.h> #include <gtk/gtk.h> #include "conversation.h" #include "network.h" #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/time.h> #include <unistd.h> #include <fcntl.h> #include "cc_network.h" #include "cc_interface.h" #include "util.h" /* --- begin constant definitions --- */ #define NETWORK_TIMEOUT_DELAY 40 /* in ms */ #define MAX_ACCEPT_CHECKS 1000 /* --- begin type declarations --- */ struct accept_args { GaimAccount *account; struct crazychat *cc; char *name; guint32 peer_ip; guint16 peer_port; }; struct sock_accept_args { GaimAccount *account; struct cc_session *session; }; /* --- begin function prototypes --- */ /** * Creates a server socket and sends a response to the peer. * @param account the gaim account sending the ready msg * @param session the peer CrazyChat session */ static void cc_net_send_ready(GaimAccount *account, struct cc_session *session); /** * Handles responses from the CrazyChat session invite dialog box. * @param dialog the dialog box * @param response the dialog box button response * @param args account, crazychat global data, peer name */ static void invite_handler(GtkDialog *dialog, gint response, struct accept_args *args); /** * Periodically checks the server socket for peer's connection. Gives up * after a set number of checks. * @param args peer session and account * @return TRUE to continue checking, FALSE to stop */ static gboolean accept_cb(struct sock_accept_args *args); /** * Initialize CrazyChat network session. Sets up the UDP socket and port. * @param account the account the session is part of * @param session the CrazyChat network session */ static void init_cc_net_session(GaimAccount *account, struct cc_session *session); /** * Handles checking the network for new feature data and sending out the * latest features. * @param session the session we're checking for network traffic */ static gboolean network_cb(struct cc_session *session); /** * Generates random bytes in the user specified byte buffer. * @param buf the byte buffer * @param len length of the byte buffer */ static void generate_randomness(uint8_t buf[], unsigned int len); /** * Sends data over a socket. * @param s socket file descriptor * @param buf data buffer * @param len data buffer length * @return number of bytes sent or -1 if an error occurred */ static int __send(int s, char *buf, int len); /* --- begin function definitions --- */ void cc_net_send_invite(struct crazychat *cc, char *name, GaimAccount *account) { struct cc_session *session; GaimConversation *conv; GaimConvIm *im; char buf[BUFSIZ]; session = cc_find_session(cc, name); if (session) return; /* already have a session with this guy */ session = cc_add_session(cc, name); session->state = INVITE; conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account); if (!conv) { conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name); } im = gaim_conversation_get_im_data(conv); snprintf(buf, BUFSIZ, "%s%s!%d", CRAZYCHAT_INVITE_CODE, gaim_network_get_my_ip(-1), cc->tcp_port); Debug("Sent invite to %s for port: %d\n", name, cc->tcp_port); gaim_conv_im_send(im, buf); } void cc_net_recv_invite(GaimAccount *account, struct crazychat *cc, char *name, const char *peer_ip, const char *peer_port) { struct cc_session *session; GaimConversation *conv; GaimConvWindow *convwin; char buf[BUFSIZ]; struct accept_args *args; assert(cc); assert(name); Debug("Received a CrazyChat session invite from %s on port %s!\n", name, peer_port); session = cc_find_session(cc, name); if (!session) { Debug("Creating a CrazyChat session invite dialog box!\n"); conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, name, account); if (conv) convwin = gaim_conversation_get_window(conv); else convwin = NULL; /* pop gtk window asking if want to accept */ GtkWidget *dialog = gtk_dialog_new_with_buttons("CrazyChat Session Invite", GTK_WINDOW(convwin), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); snprintf(buf, BUFSIZ, "Would you like to CRaZYchAT with %s?", name); GtkWidget *label = gtk_label_new(buf); gtk_container_add(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label); args = (struct accept_args*)malloc(sizeof(*args)); args->account = account; args->cc = cc; args->name = strdup(name); assert(inet_aton(peer_ip, (struct in_addr*)&args->peer_ip)); args->peer_port = atoi(peer_port); g_signal_connect(GTK_OBJECT(dialog), "response", G_CALLBACK(invite_handler), args); gtk_widget_show_all(dialog); } } void cc_net_recv_accept(GaimAccount *account, struct crazychat *cc, char *name, const char *peer_ip) { struct cc_session *session; struct in_addr peer_addr; assert(cc); assert(name); Debug("Received a CrazyChat session accept!\n"); session = cc_find_session(cc, name); if (session && session->state == INVITE) { session->state = ACCEPTED; assert(inet_aton(peer_ip, &peer_addr)); session->peer_ip = peer_addr.s_addr; cc_net_send_ready(account, session); } } static void cc_net_send_ready(GaimAccount *account, struct cc_session *session) { struct sock_accept_args *args; assert(session); Debug("Initializing the server socket and sending ready message\n"); /* create the server socket */ session->tcp_sock = socket(AF_INET, SOCK_STREAM, 0); assert(session->tcp_sock != -1); int reuse = 1; assert(setsockopt(session->tcp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) != -1); struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(session->cc->tcp_port); assert(inet_aton(gaim_network_get_my_ip(-1), &my_addr.sin_addr)); memset(&my_addr.sin_zero, 0, sizeof(my_addr.sin_zero)); assert(bind(session->tcp_sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) != -1); Debug("Listening on port: %d\n", my_addr.sin_port); assert(listen(session->tcp_sock, 1) != -1); /* socket created, send the ready message */ GaimConversation *conv; GaimConvIm *im; conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, session->name, account); if (!conv) { conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, session->name); } im = gaim_conversation_get_im_data(conv); gaim_conv_im_send(im, CRAZYCHAT_READY_CODE); /* register timer callback for checking socket connection */ args = (struct sock_accept_args*)malloc(sizeof(*args)); args->session = session; args->account = account; session->udp_sock = MAX_ACCEPT_CHECKS; session->timer_id = g_timeout_add(NETWORK_TIMEOUT_DELAY, (GSourceFunc)accept_cb, args); } void cc_net_recv_ready(GaimAccount *account, struct crazychat *cc, char *name) { struct cc_session *session; struct sockaddr_in server_addr, my_addr; int sock; assert(cc); assert(name); Debug("Received a CrazyChat session ready!\n"); session = cc_find_session(cc, name); if (session && session->state == ACCEPTED) { /* connect to peer */ session->tcp_sock = socket(AF_INET, SOCK_STREAM, 0); assert(session->tcp_sock != -1); server_addr.sin_family = AF_INET; server_addr.sin_port = session->peer_port; server_addr.sin_addr.s_addr = session->peer_ip; memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); assert(connect(session->tcp_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != -1); Debug("Connecting to peer on port %d\n", session->peer_port); /* now set state */ session->state = CONNECTED; init_cc_net_session(account, session); } } static void invite_handler(GtkDialog *dialog, gint response, struct accept_args *args) { struct cc_session *session; char buf[BUFSIZ]; GaimConversation *conv; GaimConvIm *im; if (response == GTK_RESPONSE_ACCEPT) { assert(args); session = cc_find_session(args->cc, args->name); assert(!session); session = cc_add_session(args->cc, args->name); session->state = ACCEPTED; session->peer_ip = args->peer_ip; session->peer_port = args->peer_port; snprintf(buf, BUFSIZ, "%s%s", CRAZYCHAT_ACCEPT_CODE, gaim_network_get_my_ip(-1)); conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, args->name, args->account); if (!conv) { conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, args->account, args->name); } im = gaim_conversation_get_im_data(conv); gaim_conv_im_send(im, buf); } free(args->name); free(args); gtk_widget_destroy(GTK_WIDGET(dialog)); } static gboolean accept_cb(struct sock_accept_args *args) { fd_set fds; struct timeval zero; int ret; GaimAccount *account; struct cc_session *session; assert(args); account = args->account; session = args->session; assert(account); assert(session); /* set select to check on our tcp socket */ FD_ZERO(&fds); FD_SET(session->tcp_sock, &fds); memset(&zero, 0, sizeof(zero)); /* check socket */ ret = select(session->tcp_sock+1,&fds, NULL, NULL, &zero); assert(ret != -1); if (ret) { /* got something to check */ Debug("Checking pending connection\n"); int sock; struct sockaddr_in client_addr; socklen_t sin_size; sin_size = sizeof(client_addr); sock = accept(session->tcp_sock, (struct sockaddr*)&client_addr, &sin_size); assert(sock != -1); /* check if it's a match */ if (client_addr.sin_addr.s_addr == session->peer_ip) { /* cool, we're set */ Debug("Accepted tcp connect from %s\n", session->name); close(session->tcp_sock); session->tcp_sock = sock; session->state = CONNECTED; session->timer_id = 0; init_cc_net_session(account, session); Debug("Will start sending to port %d\n", session->peer_port); free(args); return FALSE; } } session->udp_sock--; if (!session->udp_sock) { /* timed out */ /* remove session from session list */ cc_remove_session(session->cc, session); free(args); return FALSE; } return TRUE; } static void init_cc_net_session(GaimAccount *account, struct cc_session *session) { struct sockaddr_in my_addr; struct sockaddr_in peer_addr; int reuse; /* send/obtain the udp port information */ assert(__send(session->tcp_sock, (char*)&session->cc->udp_port, sizeof(session->cc->udp_port)) == sizeof(session->cc->udp_port)); assert(recv(session->tcp_sock, (char*)&session->peer_port, sizeof(session->peer_port), 0) == sizeof(session->peer_port)); Debug("Established a CrazyChat session with %s!\n", session->name); /* connect the udp sockets */ session->udp_sock = socket(AF_INET, SOCK_DGRAM, 0); assert(!setsockopt(session->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(session->cc->udp_port); assert(inet_aton(gaim_network_get_my_ip(-1), &my_addr.sin_addr)); memset(my_addr.sin_zero, 0, sizeof(my_addr.sin_zero)); assert(!bind(session->udp_sock, (struct sockaddr*)&my_addr, sizeof(my_addr))); session->peer.sin_family = AF_INET; session->peer.sin_port = htons(session->peer_port); session->peer.sin_addr.s_addr = session->peer_ip; memset(&session->peer.sin_zero, 0, sizeof(session->peer.sin_zero)); Debug("Bound udp sock to port %d, connecting to port %d\n", session->cc->udp_port, session->peer_port); memset(&session->features, 0, sizeof(session->features)); session->output = init_output(&session->features, session); session->filter = Filter_Initialize(); /* initialize timer callback */ session->timer_id = g_timeout_add(NETWORK_TIMEOUT_DELAY, (GSourceFunc)network_cb, session); /* initialize input subsystem if not initialized */ if (!session->cc->features_state) { session->cc->input_data = init_input(session->cc); session->cc->features_state = 1; } } static gboolean network_cb(struct cc_session *session) { fd_set fds; struct timeval zero; int ret; int command; struct cc_features *features; assert(session); Debug("Checking for data\n"); /* set select to check on our tcp socket */ FD_ZERO(&fds); FD_SET(session->tcp_sock, &fds); memset(&zero, 0, sizeof(zero)); /* check tcp socket */ ret = select(session->tcp_sock+1, &fds, NULL, NULL, &zero); assert(ret != -1); while (ret) { ret = recv(session->tcp_sock, &command, sizeof(command), 0); assert(ret != -1); if (!ret) { /* tcp connection closed, destroy connection */ gtk_widget_destroy(session->output->widget); return FALSE; } assert(ret == sizeof(command)); FD_ZERO(&fds); FD_SET(session->tcp_sock, &fds); ret = select(session->tcp_sock+1, &fds, NULL, NULL, &zero); assert(ret != -1); } /* set select to check on our udp socket */ FD_ZERO(&fds); FD_SET(session->udp_sock, &fds); memset(&zero, 0, sizeof(zero)); /* check udp socket */ ret = select(session->udp_sock+1, &fds, NULL, NULL, &zero); assert(ret != -1); features = &session->features; while (ret) { /* have data, let's copy it for output */ struct sockaddr_in from; int fromlen; ret = recvfrom(session->udp_sock, &session->features, sizeof(session->features), 0, (struct sockaddr*)&from, &fromlen); Debug("Received %d bytes from port %d\n", ret, ntohs(from.sin_port)); filter(features, session->filter); Debug("\thead size: %d\n", features->head_size); Debug("\topen: left(%s), right(%s), mouth(%s)\n", features->left_eye_open ? "yes" : "no", features->right_eye_open ? "yes" : "no", features->mouth_open ? "yes" : "no"); Debug("\thead rotation: x(%d), y(%d), z(%d)\n", features->head_x_rot, features->head_y_rot, features->head_z_rot); Debug("\tx(%d), y(%d)\n", features->x, features->y); if (ret == -1) { perror("wtf:"); } assert(ret != -1); FD_ZERO(&fds); FD_SET(session->udp_sock, &fds); ret = select(session->udp_sock+1, &fds, NULL, NULL, &zero); assert(ret != -1); } #ifdef _DISABLE_QT_ struct cc_features bogus; features = &bogus; generate_randomness((uint8_t*)features, sizeof(*features)); #else features = &session->cc->input_data->face; #endif assert(sendto(session->udp_sock, (char*)features, sizeof(*features), 0, (struct sockaddr*)&session->peer, sizeof(session->peer)) == sizeof(*features)); Debug("Sent %d bytes\n", sizeof(*features)); Debug("\thead size: %d\n", features->head_size); Debug("\topen: left(%s), right(%s), mouth(%s)\n", features->left_eye_open ? "yes" : "no", features->right_eye_open ? "yes" : "no", features->mouth_open ? "yes" : "no"); Debug("\thead rotation: x(%d), y(%d), z(%d)\n", features->head_x_rot, features->head_y_rot, features->head_z_rot); Debug("\tx(%d), y(%d)\n", features->x, features->y); /* clear easter egg */ features->mode = 0; return TRUE; } static void generate_randomness(uint8_t buf[], unsigned int len) { int fd; fd = open("/dev/random", O_RDONLY); assert(fd != -1); assert(read(fd, buf, len) == len); close(fd); } static int __send(int s, char *buf, int len) { int total = 0; /* how many bytes we've sent */ int bytesleft = len; /* how many we have left to send */ int n; while (total < len) { n = send(s, buf + total, bytesleft, 0); if (n == -1) { Debug("ERROR: %s\n", strerror(errno)); return -1; } total += n; bytesleft -= n; } return total; }