/* Copyright (C) 2000-2003 Markus Lausser (sgop@users.sf.net) This is free software distributed under the terms of the GNU Public License. See the file COPYING for details. */ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_LIBLCONV_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "support.h" #include "global.h" #include "lopster.h" #include "transfer.h" #include "share2.h" #include "server.h" #include "browse.h" #include "handler.h" #include "log.h" #include "preferences.h" #include "dialog.h" #include "connection.h" #include "napster.h" #include "utils.h" #ifdef THREADS_ENABLED # include #endif //#define RESOLVE_DEBUG static int destroying = 0; int server_login(gpointer data, gint source, GdkInputCondition condition); int connection_timeout(gpointer data); void socket_remove_clist(socket_t * socket); typedef struct { call_func_t func; void* data; char* address; unsigned int ip_long; } resolve_data_t; typedef struct { time_t last_resolved; unsigned int ip; } resolve_hist_t; static GList* ResolveDone = NULL; static GHashTable* ResolveHT = NULL; #ifdef THREADS_ENABLED static GList* PendingTasks = NULL; static pthread_mutex_t resolver = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t resolver2 = PTHREAD_MUTEX_INITIALIZER; #endif void resolve_check() { resolve_data_t* data; GList* dlist; if (!ResolveDone) return; #ifdef THREADS_ENABLED pthread_mutex_lock(&resolver2); #endif for (dlist = ResolveDone; dlist; dlist = dlist->next) { data = dlist->data; data->func(data->data, data->ip_long); g_free(data->address); g_free(data); } g_list_free(ResolveDone); ResolveDone = NULL; #ifdef THREADS_ENABLED pthread_mutex_unlock(&resolver2); #endif } static void resolve_success(resolve_data_t* data, int real) { resolve_hist_t* hist; #ifdef THREADS_ENABLED pthread_mutex_lock(&resolver2); #endif #ifdef RESOLVE_DEBUG printf("[RESOLVE] done [%s]->[%s]\n", data->address, (data->ip_long==INADDR_NONE)?"ERROR":ntoa(data->ip_long)); #endif // save data in list for processing outside the threads ResolveDone = g_list_append(ResolveDone, data); // store result in hash table only if we did a real resolve if (real) { if (ResolveHT) { hist = g_hash_table_lookup (ResolveHT, data->address); } else { ResolveHT = g_hash_table_new(g_str_hash, g_str_equal); hist = NULL; } if (!hist) { hist = g_malloc(sizeof(resolve_hist_t)); g_hash_table_insert (ResolveHT, g_strdup(data->address), hist); } hist->last_resolved = global.current_time; hist->ip = data->ip_long; } #ifdef THREADS_ENABLED pthread_mutex_unlock(&resolver2); #endif } #define RESOLVE_INT 600 // 10 minutes static gboolean resolve_lookup(resolve_data_t* data) { resolve_hist_t* hist; gboolean result; #ifdef THREADS_ENABLED pthread_mutex_lock(&resolver2); #endif // store result in hash table only if we did a real resolve if (ResolveHT) { hist = g_hash_table_lookup (ResolveHT, data->address); } else { hist = NULL; } if (!hist) { // not entry found result = FALSE; } else if (hist->last_resolved + RESOLVE_INT < global.current_time) { // out of date entry found #ifdef RESOLVE_DEBUG printf("[RESOLVE] host '%s' cached but outdated\n", data->address); #endif result = FALSE; } else { data->ip_long = hist->ip; #ifdef RESOLVE_DEBUG printf("[RESOLVE] host '%s' recently cached\n", data->address); #endif result = TRUE; } #ifdef THREADS_ENABLED pthread_mutex_unlock(&resolver2); #endif return result; } #ifdef THREADS_ENABLED static void* gethostbyname_thread(void *args) { gboolean done = FALSE; // we resolve as long as we have pending tasks (void)args; do { resolve_data_t* tdata = NULL; // get the next task pthread_mutex_lock(&resolver); if (!PendingTasks) { tdata = NULL; } else { tdata = PendingTasks->data; PendingTasks = g_list_remove(PendingTasks, tdata); } if (!PendingTasks) done = TRUE; pthread_mutex_unlock(&resolver); // now resolve the address if (tdata) { struct hostent* h; unsigned long addr; h = gethostbyname(tdata->address); if (h) addr = ((struct in_addr *) (h->h_addr_list[0]))->s_addr; else addr = INADDR_NONE; tdata->ip_long = addr; resolve_success(tdata, 1); } } while (!done); #ifdef RESOLVE_DEBUG printf("[RESOLVE] about to shut thread down\n"); #endif return NULL; } void my_gethostbyname(resolve_data_t* tdata) { pthread_mutex_lock(&resolver); // if list is empty, create a new thread. if (!PendingTasks) { pthread_t the_thread; pthread_attr_t the_thread_attr; pthread_attr_init(&the_thread_attr); // make thread to free all data on exit pthread_attr_setdetachstate(&the_thread_attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&the_thread, &the_thread_attr, gethostbyname_thread, NULL)) { printf("[RESOLVE] could not create thread\n"); tdata->ip_long = INADDR_NONE; resolve_success(tdata, 0); } else { #ifdef RESOLVE_DEBUG printf("[RESOLVE] created new thread\n"); #endif PendingTasks = g_list_append(PendingTasks, tdata); } } else { PendingTasks = g_list_append(PendingTasks, tdata); } pthread_mutex_unlock(&resolver); } #endif void resolve_address(char *address, call_func_t func, void* data) { unsigned long addr; resolve_data_t* tdata; if (!address || *address == 0) return; #ifdef RESOLVE_DEBUG printf("[RESOLVE] address '%s'\n", address); #endif tdata = g_malloc(sizeof(*tdata)); tdata->func = func; tdata->data = data; tdata->address = g_strdup(address); tdata->ip_long = INADDR_NONE; // return if we have found a recent IP in the hash table if (resolve_lookup(tdata)) { resolve_success(tdata, 0); return; } addr = inet_addr(address); if (addr != INADDR_NONE) { tdata->ip_long = addr; resolve_success(tdata, 0); return; } #ifdef THREADS_ENABLED /****************************/ my_gethostbyname(tdata); #else /*********************************************/ { struct hostent* h = gethostbyname(address); if (h) addr = ((struct in_addr *) (h->h_addr_list[0]))->s_addr; else addr = INADDR_NONE; tdata->ip_long = addr; resolve_success(tdata, 1); } #endif /********************************************/ return; } socket_t *socket_new(int type) { socket_t *sock; sock = g_malloc(sizeof(socket_t)); sock->ip_long = 0; sock->port = 0; sock->fd = -1; sock->input = -1; sock->output = -1; sock->timer = -1; sock->cnt = 0; sock->data = NULL; sock->type = type; switch (type) { case S_SHARE: sock->max_cnt = 50; break; case S_BROWSE: sock->max_cnt = 30; break; case S_DOWNLOAD: sock->max_cnt = global.network.transfer_timeout_down; // dummy value, will be set later when starting up and downloads break; case S_UPLOAD: sock->max_cnt = global.network.transfer_timeout_up; // dummy value, will be set later when starting up and downloads break; case S_SERVER: // connection timeout. will be reset after login sock->max_cnt = 30; break; case S_HTTP: sock->max_cnt = 20; break; case S_DATA: sock->max_cnt = 0; break; default: sock->max_cnt = 100; break; } global.sockets = g_list_append(global.sockets, sock); return sock; } static void socket_end_real(socket_t * socket, int data) { transfer_t *trans; download_t *download; browse_t *browse; int tim; if (socket->fd >= 0) { close(socket->fd); socket->fd = -1; } if (socket->input >= 0) { gdk_input_remove(socket->input); socket->input = -1; } if (socket->output >= 0) { gdk_input_remove(socket->output); socket->output = -1; } if (socket->timer >= 0) { gtk_timeout_remove(socket->timer); socket->timer = -1; } switch (socket->type) { case S_DOWNLOAD: download = socket->data; if (!download) break; // test // if (download->data->status == data) break; if (!destroying && (tim = download_to_retry(download, data)) >= 0) { if (socket->timer >= 0) g_warning("TIMEOUT ACTIVE"); socket->timer = gtk_timeout_add(tim, download_timeout, socket); } download_end(socket, data); break; case S_SHARE: trans = TRANS(socket->data); if (!trans) break; if (trans->status == data) break; share_end(socket, data); break; case S_UPLOAD: trans = TRANS(socket->data); if (!trans) break; if (trans->status == data) break; upload_end(socket, data); break; case S_HTTP: if (socket->data == (void*)(-1)) napigator_leave(0); break; case S_SERVER: break; case S_BROWSE: browse = socket->data; if (!data && browse) { #ifdef CONNECTION_DEBUG printf("p2p browse failed, starting old one\n"); #endif browse->status = 0; // mark as in progress // browse_update_user(FILE_TREE(browse)); // browse_clear_files(browse); command_send(browse->net, CMD_BROWSE, FILE_TREE(browse)->name); } break; default: break; } } void socket_end(socket_t * socket, int data) { switch (socket->type) { case S_DOWNLOAD: if (download_to_destroy(socket, data)) { socket_destroy(socket, data); } else { socket_end_real(socket, data); } break; case S_UPLOAD: case S_SHARE: if (upload_to_destroy(socket, data)) { socket_destroy(socket, data); } else { socket_end_real(socket, data); } break; case S_HTTP: socket_end_real(socket, data); break; case S_SERVER: socket_end_real(socket, data); break; case S_BROWSE: socket_end_real(socket, data); break; default: socket_end_real(socket, data); break; } } void socket_destroy(socket_t * socket, int data) { share_t* share; upload_t* upload; download_t* download; if (!socket) return; destroying = 1; #ifdef CONNECTION_DEBUG printf("destroying socket [%s:%d][%d]\n", ntoa(socket->ip_long), ntohs(socket->port), socket->fd); #endif socket_end_real(socket, data); global.sockets = g_list_remove(global.sockets, socket); socket_remove_clist(socket); switch (socket->type) { case S_DOWNLOAD: download = socket->data; if (!download) break; if (download->resume) { download->resume->downloads = g_list_remove(download->resume->downloads, socket); } download_hide(socket); download_destroy(socket->data); break; case S_UPLOAD: upload = socket->data; if (!upload) break; upload_hide(socket); upload_destroy(socket->data); break; case S_SHARE: share = socket->data; if (!share) break; share_hide(socket); share_destroy(socket->data); break; case S_BROWSE: break; case S_SERVER: break; default: break; } destroying = 0; g_free(socket); } char *socket_get_name(socket_t * socket) { switch (socket->type) { case S_SHARE: return "Share"; case S_BROWSE: return "Browse"; case S_HTTP: return "Http"; case S_SERVER: return "Server"; case S_DATA: return "Data"; case S_UPLOAD: return "Upload"; case S_DOWNLOAD: return "Download"; #ifdef ENABLE_WHITEBOARD case S_WHITEBOARD: return "Whiteboard"; #endif case S_UNKNOWN: return "Unknown"; default: return "Ooops"; } } int connect_socket(socket_t * s, char *proto, int type) { struct protoent *protocol; struct sockaddr_in server; int sock; int res; int result; protocol = getprotobyname(proto); if (!protocol) return 0; sock = socket(AF_INET, type, protocol->p_proto); if (sock < 0) return 0; s->fd = sock; s->cnt = 0; server.sin_family = AF_INET; server.sin_addr.s_addr = s->ip_long; server.sin_port = s->port; if (server.sin_addr.s_addr == (unsigned int)-1) return 0; if (ban_networks_scan(ntoa(s->ip_long))) return 0; #ifdef CONNECTION_DEBUG printf("connecting to [%s:%d]", ntoa(s->ip_long), ntohs(s->port)); #endif fcntl(sock, F_SETFL, O_NONBLOCK); res = connect(sock, (struct sockaddr *) &server, sizeof(server)); // make sure to read errno now, cause printf() could reset // errno result = ((res == 0) || (errno == EINPROGRESS)); #ifdef CONNECTION_DEBUG printf(" result: %d %d\n", res, errno); #endif return result; } gint server_send(gpointer data, gint source, GdkInputCondition condition); gboolean net_has_outgoing(net_t* net); int command_send(net_t* net, gint16 type, ...) { va_list ap; GList* dlist = NULL; if (type < 0) { g_warning("command out of bounds %d", type); return 1; } if (!net && global.net_active) { dlist = global.net_active; net = dlist->data; } while (net) { if (!net->out_buffer) net->out_buffer = buffer_new(4096); // we already might want to send commands when connecting (ping // from IRC _before_ welcome) so check NET_ONLINE() instead of // NET_CONNECTED() NOTE: i really dont know why i've declared // NET_ONLINE to check the connection and NET_CONNECTED to check // the online status. stupid if (NET_ONLINE(net)) { va_start(ap, type); net->command_pack(net, type, ap); va_end(ap); if (net->socket->output == -1 && net_has_outgoing(net)) { net->socket->output = gdk_input_add(net->socket->fd, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(server_send), net->socket); } } else { g_warning("net is not online"); } if (dlist) { dlist = dlist->next; if (dlist) net = dlist->data; else net = NULL; } else { net = NULL; } } return 0; } int command_send_unimportant(net_t* net, gint16 type, ...) { va_list ap; GList* dlist = NULL; if (type < 0) { g_warning("command out of bounds %d", type); return 1; } if (!net && global.net_active) { dlist = global.net_active; net = dlist->data; } while (net) { if (!net->temp_buffer) net->temp_buffer = buffer_new(4096); if (NET_ONLINE(net)) { int pos; int size; buffer_t* buffer = net->out_buffer; // switch buffers; net->out_buffer = net->temp_buffer; // leave space for length buffer_need_space(net->out_buffer, sizeof(size)); pos = net->out_buffer->datasize; net->out_buffer->datasize += sizeof(size); va_start(ap, type); net->command_pack(net, type, ap); va_end(ap); size = net->out_buffer->datasize - pos - 4; if (size > 0) { memcpy(net->out_buffer->data+pos, &size, sizeof(size)); } else { printf("--WARNING size <= 0\n"); net->out_buffer->datasize -= sizeof(size); } net->out_buffer = buffer; if (net->socket->output == -1 && net_has_outgoing(net)) { net->socket->output = gdk_input_add(net->socket->fd, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(server_send), net->socket); } } else { g_warning("net is not online"); } if (dlist) { dlist = dlist->next; if (dlist) net = dlist->data; else net = NULL; } else { net = NULL; } } return 0; } gint get_con_type(gpointer data, gint source, GdkInputCondition condition) { #ifndef HAVE_LIBLCONV_H char buffer[1025]; #else char buffer[2049]; char lconv_buf[2049]; #endif int cnt; socket_t *socket = (socket_t *) data; if (condition != GDK_INPUT_READ) { socket_destroy(socket, 0); return 1; } gdk_input_remove(socket->input); cnt = 0; switch (cnt = recv(source, buffer, 1024, MSG_PEEK)) { case -1: socket_destroy(socket, 0); return 1; case 0: socket_destroy(socket, 0); return 1; default: break; } buffer[cnt] = 0; if (cnt < 3) { socket_destroy(socket, 0); return 1; } #ifdef HAVE_LIBLCONV_H if (global.options.use_iconv) { lconv_conv(LCONV_SPEC, local_codeset, global.options.dest_codeset, lconv_buf, buffer); strcpy(buffer, lconv_buf); } #endif #ifdef TRANSFER_DEBUG printf("in [%s] %d\n", buffer, cnt); while (cnt >= 0) { printf(" - [%1c] [%3d]\n", buffer[cnt], buffer[cnt]); cnt--; } #endif if (!strncmp(buffer, "GETLIST", 7)) { if (recv_safe(source, buffer, 7, 7) != 7) { socket_destroy(socket, 0); return 1; } // client_message(NULL, "Someone is browsing your files"); socket->type = S_SHARE; socket->max_cnt = 30; socket->output = gdk_input_add(socket->fd, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(share_fw_get_info), socket); } else if (!strncmp(buffer, "SENDLIST", 8)) { if (recv_safe(source, buffer, 8, 8) != 8) { socket_destroy(socket, 0); return 1; } // client_message(NULL, "Someone is sending his shared list"); socket->type = S_BROWSE; socket->max_cnt = 30; socket->input = gdk_input_add(socket->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(get_browse_nick), socket); } else if (!strncmp(buffer, "GET", 3)) { if (recv_safe(source, buffer, 3, 3) != 3) { socket_destroy(socket, 0); return 1; } #ifdef TRANSFER_DEBUG printf("got GET\n"); #endif socket->type = S_UPLOAD; socket->input = gdk_input_add(socket->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(upload_fw_get_info), socket); } else if (!strncmp(buffer, "SEND", 4)) { if (recv_safe(source, buffer, 4, 4) != 4) { socket_destroy(socket, 0); return 1; } #ifdef TRANSFER_DEBUG printf("got SEND\n"); #endif socket->type = S_DOWNLOAD; socket->input = gdk_input_add(socket->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(download_fw_get_info), socket); #ifdef ALLOW_REMOTE_CONTROL } else if (!strncmp(buffer, "LOGIN\r\n", 7)) { if (recv_safe(source, buffer, 7, 7) != 7) { socket_destroy(socket, 0); return 1; } #ifdef TRANSFER_DEBUG printf("got LOGIN\n"); #endif socket->type = S_REMOTE; socket->output = gdk_input_add(socket->fd, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(remote_send_login), socket); #endif } else { l_log(NULL, "invalid_requests", LOG_OTHER, "[%s][%s]\n", ntoa(socket->ip_long), buffer); // printf("**sending INVALID REQUEST [%s]\n", buffer); send_safe(source, "INVALID REQUEST", 0, strlen("INVALID REQUEST")); socket_destroy(socket, 0); } return 1; } static int handle_incoming(gpointer data, gint source ATTR_UNUSED, GdkInputCondition condition ATTR_UNUSED) { struct sockaddr_in from; int len; socket_t *ssocket = data; socket_t *socket; if (condition != GDK_INPUT_READ) { printf("broken data port\n"); if (global.upload_socket) socket_destroy(global.upload_socket, 0); global.upload_socket = NULL; return 1; } socket = socket_new(S_UNKNOWN); len = sizeof(from); socket->fd = accept(ssocket->fd, (struct sockaddr *) &from, &len); if (socket->fd < 0) { socket_destroy(socket, S_DELETE); return 1; } fcntl(socket->fd, F_SETFL, O_NONBLOCK); socket->ip_long = from.sin_addr.s_addr; socket->port = from.sin_port; // printf("got ip %lu\n", socket->ip_long); if (send_safe(socket->fd, "1", 1, 1) != 1) { socket_destroy(socket, S_DELETE); return 1; } #ifdef CONNECTION_DEBUG printf("accepted new connection [%s]\n", ntoa(socket->ip_long)); #endif socket->input = gdk_input_add(socket->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(get_con_type), socket); return 1; } void create_upload_port(gint16 port, int send) { int len = 1; int sock; int protocol; struct sockaddr_in server; int save; if (global.upload_socket) socket_destroy(global.upload_socket, 0); global.upload_socket = NULL; if ((port == 0) || global.network.firewall) { if (send) { command_send(NULL, CMD_CHANGE_DATA_PORT, 0); setup_preferences(P_GENERAL); } return; } protocol = getprotobyname("IP")->p_proto; sock = socket(AF_INET, SOCK_STREAM, protocol); if (sock < 0) { port_dialog("Could not create socket"); create_upload_port(0, 1); return; } global.upload_socket = socket_new(S_DATA); global.upload_socket->ip_long = htonl(INADDR_ANY); global.upload_socket->fd = sock; global.upload_socket->cnt = 0; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &len, sizeof(len)); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &len, sizeof(len)); server.sin_addr.s_addr = global.upload_socket->ip_long; server.sin_family = AF_INET; if (port_is_allowed(global.allowed_ports, port)) { global.upload_socket->port = htons(port); server.sin_port = global.upload_socket->port; if (bind(sock, (struct sockaddr *) &server, sizeof(server)) >= 0) goto success; client_message("Error", "Could not setup port %d", port); } else { client_message("Error", "Port %d is not in the list of allowed ports", port); } client_message("Message", "Searching for port...."); send = 1; save = get_next_port(global.allowed_ports, port); if (save == port) goto failure; // only one port in list port = save; while (1) { global.upload_socket->port = htons(port); server.sin_port = global.upload_socket->port; if (bind(sock, (struct sockaddr *) &server, sizeof(server)) >= 0) goto success; port = get_next_port(global.allowed_ports, port); if (port == save) break; } goto failure; success: listen(sock, 10 + global.limit.max_uploads + global.limit.max_downloads); setup_preferences(P_GENERAL); if (send) { command_send(NULL, CMD_CHANGE_DATA_PORT, ntohs(global.upload_socket->port)); } global.upload_socket->input = gdk_input_add(sock, GDK_INPUT_READ, GTK_SIGNAL_FUNC(handle_incoming), global.upload_socket); setup_preferences(P_GENERAL); return; failure: port_dialog("No available Port found"); create_upload_port(0, 1); return; } void socket_insert_clist(socket_t * socket) { GtkCList *clist; int row; if (!global.socket_win) return; strcpy(tstr[0], socket_get_name(socket)); sprintf(tstr[1], "%s", ntoa(socket->ip_long)); sprintf(tstr[2], "%d", ntohs(socket->port)); sprintf(tstr[3], "%d/%d", socket->cnt, socket->max_cnt); if (socket->fd == -1) strcpy(tstr[4], "inactive"); else sprintf(tstr[4], "%d", socket->fd); if (socket->timer == -1) strcpy(tstr[5], "inactive"); else sprintf(tstr[5], "%d", socket->timer); clist = GTK_CLIST(lookup_widget(global.socket_win, "clist20")); row = gtk_clist_append(clist, list); gtk_clist_set_row_data(clist, row, (gpointer) socket); } void socket_remove_clist(socket_t * socket) { GtkCList *clist; int row; if (!global.socket_win) return; clist = GTK_CLIST(lookup_widget(global.socket_win, "clist20")); row = gtk_clist_find_row_from_data(clist, socket); if (row < 0) return; gtk_clist_remove(clist, row); clist = GTK_CLIST(lookup_widget(global.socket_win, "clist21")); if (gtk_object_get_data(GTK_OBJECT(clist), "socket") == socket) { socket_show_clist(NULL); } } void socket_update_clist(socket_t * socket) { GtkCList *clist; int row; char str[1024]; if (!global.socket_win) return; clist = GTK_CLIST(lookup_widget(global.socket_win, "clist20")); row = gtk_clist_find_row_from_data(clist, socket); if (row < 0) { socket_insert_clist(socket); return; } sprintf(str, "%s", ntoa(socket->ip_long)); gtk_clist_set_text(clist, row, 1, str); sprintf(str, "%d", ntohs(socket->port)); gtk_clist_set_text(clist, row, 2, str); sprintf(str, "%d/%d", socket->cnt, socket->max_cnt); gtk_clist_set_text(clist, row, 3, str); if (socket->fd == -1) strcpy(str, "inactive"); else sprintf(str, "%d", socket->fd); gtk_clist_set_text(clist, row, 4, str); if (socket->timer == -1) strcpy(str, "inactive"); else sprintf(str, "%d", socket->timer); gtk_clist_set_text(clist, row, 5, str); clist = GTK_CLIST(lookup_widget(global.socket_win, "clist21")); if (gtk_object_get_data(GTK_OBJECT(clist), "socket") == socket) socket_show_clist(socket); } static void copy_string(char *dest, const char *source) { if (source) strcpy(dest, source); else strcpy(dest, "_????_"); } void socket_show_clist(socket_t * socket) { GtkCList *clist; server_t *server; net_t *net; upload_t *upload; download_t* download; file_tree_t *browse; char str[1024]; char str1[1024]; char str2[1024]; user_info_t* userinfo; if (!global.socket_win) return; if (!socket) return; clist = GTK_CLIST(lookup_widget(global.socket_win, "clist21")); gtk_clist_clear(clist); gtk_clist_freeze(clist); gtk_object_set_data(GTK_OBJECT(clist), "socket", socket); if (socket) { switch (socket->type) { case S_BROWSE: copy_string(tstr[0], "Browsing"); browse = socket->data; copy_string(tstr[1], browse->name); gtk_clist_append(clist, list); break; case S_SERVER: net = socket->data; if (!net || !net->active_server) { copy_string(tstr[0], "Server"); copy_string(tstr[1], NULL); gtk_clist_append(clist, list); break; } server = net->active_server; copy_string(tstr[0], "Address"); copy_string(tstr[1], server->address); gtk_clist_append(clist, list); copy_string(tstr[0], "Port"); sprintf(tstr[1], "%d", server->port); gtk_clist_append(clist, list); copy_string(tstr[0], "Nick"); copy_string(tstr[1], net->user.username); gtk_clist_append(clist, list); copy_string(tstr[0], "Metaserver"); copy_string(tstr[1], (server->flags&SERVER_REDIRECT) ? "Yes" : "No"); gtk_clist_append(clist, list); copy_string(tstr[0], "Type"); copy_string(tstr[1], Network[net->subtype]); gtk_clist_append(clist, list); copy_string(tstr[0], "Version"); if (net->subtype == N_OPENNAP || net->subtype == N_OPENNAP_NG) { sprintf(tstr[1], "%d.%d", net->major, net->minor); gtk_clist_append(clist, list); } else if (net->subtype == N_SLAVANAP || net->subtype == N_CLEANNAP) { sprintf(tstr[1], "%d.%d.%d", net->major, net->minor, net->micro); gtk_clist_append(clist, list); } break; case S_DOWNLOAD: download = socket->data; if (!download) { copy_string(tstr[0], "Download"); copy_string(tstr[1], NULL); gtk_clist_append(clist, list); break; } userinfo = TRANS(download)->user_info; copy_string(tstr[0], "Type"); copy_string(tstr[1], "Download"); gtk_clist_append(clist, list); copy_string(tstr[0], "Filename"); copy_string(tstr[1], download->file->filename); gtk_clist_append(clist, list); copy_string(tstr[0], "Longname"); copy_string(tstr[1], download->file->longname); gtk_clist_append(clist, list); copy_string(tstr[0], "User"); copy_string(tstr[1], userinfo->nick); gtk_clist_append(clist, list); copy_string(tstr[0], "Linespeed"); copy_string(tstr[1], LineSpeed(userinfo->linespeed)); gtk_clist_append(clist, list); if (download->csegment) { file_segment_t* segment = download->csegment->data; int full_size = segment->stop-segment->start; copy_string(tstr[0], "Progress"); sprintf(tstr[1], "%d/%d [%.2f%%]", segment->size, full_size, (full_size) ? ((double) (segment->size) * 100.0 / (double) (full_size)) : .0); gtk_clist_append(clist, list); } copy_string(tstr[0], "Status"); copy_string(tstr[1], status_names(TRANS(download)->status)); gtk_clist_append(clist, list); copy_string(tstr[0], "Dcc"); copy_string(tstr[1], (TRANS(download)->is_dcc) ? "Yes" : "No"); gtk_clist_append(clist, list); if (userinfo->max[0] == 0) strcpy(str1, "No limit"); else if (userinfo->max[0] == -1) { sprintf(str1, "%d allowed", global.limit.default_downloads); } else sprintf(str1, "%d allowed", userinfo->max[0]); if (userinfo->limit[0]) sprintf(str2, "%s Bandwidth limit", print_speed(str, userinfo->limit[0], 1)); else strcpy(str2, "No bandwidth limit"); copy_string(tstr[0], "Userinfo"); sprintf(tstr[1], "[%d running][%s][%s]", userinfo->cur[0], str1, str2); gtk_clist_append(clist, list); break; case S_UPLOAD: upload = socket->data; if (!upload) { copy_string(tstr[0], "Transfer"); copy_string(tstr[1], NULL); gtk_clist_append(clist, list); break; } userinfo = TRANS(upload)->user_info; copy_string(tstr[0], "Type"); copy_string(tstr[1], "Upload"); gtk_clist_append(clist, list); copy_string(tstr[0], "Filename"); copy_string(tstr[1], upload->file->filename); gtk_clist_append(clist, list); copy_string(tstr[0], "Longname"); copy_string(tstr[1], upload->file->longname); gtk_clist_append(clist, list); copy_string(tstr[0], "User"); copy_string(tstr[1], userinfo->nick); gtk_clist_append(clist, list); copy_string(tstr[0], "Linespeed"); copy_string(tstr[1], LineSpeed(userinfo->linespeed)); gtk_clist_append(clist, list); if (upload->segment) { int lsize = upload->segment->stop-upload->segment->start; copy_string(tstr[0], "Progress"); sprintf(tstr[1], "%d/%d [%.2f%%]", upload->segment->size, lsize, (lsize) ? ((double) (upload->segment->size) * 100.0 / (double) (lsize)) : .0); gtk_clist_append(clist, list); } copy_string(tstr[0], "Status"); copy_string(tstr[1], status_names(TRANS(upload)->status)); gtk_clist_append(clist, list); copy_string(tstr[0], "Dcc"); copy_string(tstr[1], (TRANS(upload)->is_dcc) ? "Yes" : "No"); gtk_clist_append(clist, list); if (userinfo->max[1] == 0) strcpy(str1, "No limit"); else if (userinfo->max[1] == -1) { sprintf(str1, "%d allowed", global.limit.default_uploads); } else sprintf(str1, "%d allowed", userinfo->max[1]); if (userinfo->limit[1]) sprintf(str2, "%s Bandwidth limit", print_speed(str, userinfo->limit[1], 1)); else strcpy(str2, "No bandwidth limit"); copy_string(tstr[0], "Userinfo"); sprintf(tstr[1], "[%d running][%s][%s]", userinfo->cur[1], str1, str2); gtk_clist_append(clist, list); break; #ifdef ENABLE_WHITEBOARD case S_WHITEBOARD: copy_string(tstr[0], "Not implemented yet"); copy_string(tstr[1], ""); gtk_clist_append(clist, list); break; #endif default: copy_string(tstr[0], "No data"); copy_string(tstr[1], ""); gtk_clist_append(clist, list); break; } } gtk_clist_thaw(clist); } int send_safe(int fd, const char* buf, int min, int max) { int sent = 0; int res; do { res = send(fd, buf+sent, max-sent, MSG_NOSIGNAL); if (res == -1) { if (errno != EWOULDBLOCK && errno != EINTR && errno != EAGAIN) { g_warning("send error, errno:%d", errno); return -1; } } else { sent += res; } } while (sent < min); return sent; } int recv_safe(int fd, char* buf, int min, int max) { int have_read = 0; int res; do { res = recv(fd, buf+have_read, max-have_read, MSG_NOSIGNAL); if (res == -1) { // catch blocks and interrupts if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { g_warning("recv error, errno:%d", errno); return -1; } } else if (res == 0) { // should be EOF, closed by peer // g_warning("0 bytes received"); return -1; } else { have_read += res; } } while (have_read < min); return have_read; }