/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lopster.h" #include "connection.h" #include "global.h" #include "search.h" #include "transfer.h" #include "resume.h" #include "interface.h" #include "support.h" #include "callbacks.h" #include "browse.h" #include "hotlist.h" #include "commands.h" #include "chat.h" #include "dirselect.h" #include "scheme.h" #include "handler.h" #include "resume.h" #include "server.h" #include "preferences.h" #include "log.h" #include "statistic.h" #include "utils.h" #define ACCESS_HASH_LENGTH 16 #define COLORS 8 #define BAND_SIZE 6 #define BAND_WIDTH 2048 static const int steps[BAND_SIZE] = { 1, 5, 30, 180, 720, 4320 }; struct _band_t { GtkWidget* area; GdkPixmap* pixmap; double** val1; // p2p double** val2; // server int* pos; int* size; double* temp1; double* temp2; int* tno; int sssss; int step; int real_step; int smooth; int mode; int show; int need_update; }; static band_t* popup_band = NULL; static GdkColor color1[COLORS] = { { 0, 0x0000, 0x5000, 0xa000 }, { 0, 0x0000, 0x4b00, 0x9800 }, { 0, 0x0000, 0x4800, 0x9000 }, { 0, 0x0000, 0x4400, 0x8800 }, { 0, 0x0000, 0x4000, 0x8000 }, { 0, 0x0000, 0x3b00, 0x7800 }, { 0, 0x0000, 0x3800, 0x7000 }, { 0, 0x0000, 0x3400, 0x6800 } }; static GdkColor color2[COLORS] = { { 0, 0x0000, 0xa000, 0x5000 }, { 0, 0x0000, 0x9800, 0x4b00 }, { 0, 0x0000, 0x9000, 0x4800 }, { 0, 0x0000, 0x8800, 0x4400 }, { 0, 0x0000, 0x8000, 0x4000 }, { 0, 0x0000, 0x7800, 0x3b00 }, { 0, 0x0000, 0x7000, 0x3800 }, { 0, 0x0000, 0x6800, 0x3400 } }; static GdkColor color3 = { 0, 0x0000, 0x0000, 0x0000 }; static GdkColor color4 = { 0, 0xaaaa, 0xaaaa, 0xaaaa }; static GdkColor color5 = { 0, 0xcccc, 0x0000, 0x0000 }; static GdkGC *gc1[COLORS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static GdkGC *gc2[COLORS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static GdkGC *gc3 = NULL; static GdkGC *gc4 = NULL; static GdkGC *gc5 = NULL; static GdkFont *font1; static int cleanup_timeout = -1; static band_t* band_new(int up) { band_t* result; int i1; result = g_malloc(sizeof(*result)); if (!up) result->area = lookup_widget(global.win, "drawingarea3"); else result->area = lookup_widget(global.win, "drawingarea4"); if (!GTK_WIDGET_REALIZED(result->area)) gtk_widget_realize(result->area); result->pixmap = NULL; result->val1 = g_malloc(sizeof(double*) * BAND_SIZE); result->val2 = g_malloc(sizeof(double*) * BAND_SIZE); result->size = g_malloc(sizeof(int) * BAND_SIZE); result->pos = g_malloc(sizeof(int) * BAND_SIZE); result->temp1 = g_malloc(sizeof(double) * BAND_SIZE); result->temp2 = g_malloc(sizeof(double) * BAND_SIZE); result->tno = g_malloc(sizeof(double) * BAND_SIZE); for (i1 = 0; i1 < BAND_SIZE; i1++) { result->val1[i1] = g_malloc(sizeof(double)*BAND_WIDTH); memset(result->val1[i1], 0, sizeof(double) * BAND_WIDTH); result->val2[i1] = g_malloc(sizeof(double)*BAND_WIDTH); memset(result->val2[i1], 0, sizeof(double) * BAND_WIDTH); result->pos[i1] = 0; result->size[i1] = BAND_WIDTH; result->temp1[i1] = 0; result->temp2[i1] = 0; result->tno[i1] = 0; } result->step = global.options.graph_interval[up]; if (result->step < 0) result->real_step = 0; else result->real_step = result->step; result->smooth = global.options.graph_smooth[up]; result->mode = global.options.graph_mode[up]; result->show = global.options.graph_show[up]; result->need_update = 1; return result; } static void band_new_vals(band_t* band, long val1, long val2) { int i1; for (i1 = 0; i1 < BAND_SIZE; i1++) { band->temp1[i1] += val1; band->temp2[i1] += val2; band->tno[i1]++; if (band->tno[i1] % steps[i1] == 0) { band->val1[i1][band->pos[i1]] = band->temp1[i1] / steps[i1]; band->val2[i1][band->pos[i1]] = band->temp2[i1] / steps[i1]; band->temp1[i1] = 0; band->temp2[i1] = 0; band->tno[i1] = 0; band->pos[i1]++; if (band->pos[i1] >= band->size[i1]) band->pos[i1] = 0; if (i1 == band->real_step) band->need_update = 1; } } } #define BX2 10 #define BY1 20 #define BY2 20 static void band_draw_graph(band_t* band, int up) { int step, si; int width, height; int pos; double max; double sum1, sum2; int unit; int sindex; int i1, i2, i2x=0/*avoid compiler warning*/, i3, i4, ix; char str[1024]; long bytes1, bytes2; int add; int theight; int* old_val1; int* old_val2; int old1; int old2; char* text; time_t tim; int dwidth, dheight; int BX1 = 40; // just a guess char str1[128]; char str2[128]; if (!global.win->window) return; if (!band->area || !band->area->window) return; width = band->area->allocation.width; height = band->area->allocation.height; if (width <= 0 || height <= 0) return; dwidth = width-BX1-BX2; dheight = height-BY1-BY2; if (band->step < 0 && band->real_step < BAND_SIZE-1 && steps[band->real_step]*dwidth < global.current_time - global.start_time) band->real_step++; if (band->step < 0 && band->real_step > 0 && steps[band->real_step-1]*dwidth > global.current_time - global.start_time) band->real_step--; step = band->real_step; si = band->smooth; if (band->pixmap) { gdk_window_get_size(band->pixmap, &i1, &i2); } else { i1 = i2 = 0; } if (!band->pixmap || i1 != width || i2 != height) { if (band->pixmap) gdk_pixmap_unref(band->pixmap); band->pixmap = gdk_pixmap_new (band->area->window, width, height, -1); } else if (!band->need_update) { goto just_paint; } // draw the bars theight = gdk_string_height(font1, "1,23KB"); gdk_draw_rectangle(band->pixmap, gc3, TRUE, 0, 0, width, height); sum1 = global.statistic.incomplete[0]; sum2 = global.statistic.incomplete[1]; if (sum2 > 0 && !up) { print_size(str1, sum2); text = g_strdup_printf("%.1f%% of %s completed", sum1 / sum2 * 100, str1); pos = gdk_string_width(font1, text); gdk_draw_string(band->pixmap, font1, gc4, width-BX2-pos, BY1 - 5, text); g_free(text); } old_val1 = g_malloc(si*sizeof(double)); memset(old_val1, 0, sizeof(double) * si); old_val2 = g_malloc(si*sizeof(double)); memset(old_val2, 0, sizeof(double) * si); sum1 = sum2 = 0; max = 0; pos = band->pos[step]-1; for (i1 = 0; i1 < width + si; i1++, pos--) { if (pos < 0) pos = band->size[step]-1; sindex = pos%si; if (band->show != 0) { sum1 -= old_val1[sindex]; old_val1[sindex] = band->val1[step][pos]; sum1 += old_val1[sindex]; } if (band->show != 1) { sum2 -= old_val2[sindex]; old_val2[sindex] = band->val2[step][pos]; sum2 += old_val2[sindex]; } if (sum1 > max) max = sum1; if (sum2 > max) max = sum2; } max /= si; if (max <= 0) max = 1024; unit = 8; while ((dheight-1) * unit < 25 * max) unit *= 2; max = ((int)((max-1)/unit)+1)*unit; i4 = 0; while (1) { i4 += unit; i3 = (int)((double)(dheight-1) * i4 / max); if (dheight < i3) break; print_size(str, i4); i1 = gdk_string_width(font1, str); if (i1 > BX1) BX1 = i1; } BX1 += 2*4; dwidth = width-BX1-BX2; // draw border gdk_draw_line(band->pixmap, gc4, BX1-1, BY1-1, width-BX2, BY1-1); gdk_draw_line(band->pixmap, gc4, BX1-1, height-BY2, width-BX2, height-BY2); gdk_draw_line(band->pixmap, gc4, width-BX2, BY1-1, width-BX2, height-BY2); gdk_draw_line(band->pixmap, gc4, BX1-1, BY1-1, BX1-1, height-BY2); for (i1 = dwidth-60; i1 >= 0; i1 -= 60, pos--) { print_time_unit(str, (dwidth - i1) * steps[step]); gdk_draw_line(band->pixmap, gc4, BX1+i1, dheight+BY1, BX1+i1, dheight+BY1+theight+2); gdk_draw_string(band->pixmap, font1, gc4, BX1+i1+2, dheight+BY1+theight, str); } if (global.current_time <= global.start_time) tim = global.start_time + 1; else tim = global.current_time; if (!up) { print_size(str1, global.statistic.total[0]); print_speed(str2, global.statistic.total[0] / (tim - global.start_time), 1); text = g_strdup_printf("Total Down: %s (%s)", str1, str2); } else { print_size(str1, global.statistic.total[1]); print_speed(str2, global.statistic.total[1] / (tim - global.start_time), 1); text = g_strdup_printf("Total Up: %s (%s)", str1, str2); } gdk_draw_string(band->pixmap, font1, gc4, BX1, BY1 - 5, text); g_free(text); pos = band->pos[step]; sum1 = sum2 = 0; bytes1 = bytes2 = 0; old1 = old2 = -1; if (max > 0) { memset(old_val1, 0, sizeof(double) * si); memset(old_val2, 0, sizeof(double) * si); for (i1 = 1; i1 <= si; i1++, pos--) { // the first value is invalid and will be written at // sindex=(dwidth-1)%si, this value will be overwritten // immediately on first entrance of next loop if (pos < 0) pos = band->size[step]-1; sindex = (dwidth-i1)%si; old_val1[sindex] = band->val1[step][pos]; old_val2[sindex] = band->val2[step][pos]; sum1 += band->val1[step][pos]; sum2 += band->val2[step][pos]; } for (i1 = dwidth - 1; i1 >= 0; i1--, pos--) { if (pos < 0) pos = band->size[step]-1; sindex = i1%si; sum1 -= old_val1[sindex]; old_val1[sindex] = band->val1[step][pos]; sum1 += old_val1[sindex]; bytes1 += old_val1[(i1+si-1)%si]; sum2 -= old_val2[sindex]; old_val2[sindex] = band->val2[step][pos]; sum2 += old_val2[sindex]; bytes2 += old_val2[sindex]; if (band->show != 0) { i2 = (int)((double)(dheight-1) * sum1 / max / si); if (band->mode == 2) { for (ix = 0; ix < COLORS; ix++) { i4 = (i2*(COLORS-ix)/COLORS); if (i4 <= 0) break; gdk_draw_line(band->pixmap, gc1[ix], i1+BX1, dheight+BY1-1 - i4, i1+BX1, dheight+BY1-1); } } else if (band->mode == 1) { if (i2 > 0 && old1 >= 0) { gdk_draw_line(band->pixmap, gc1[0], i1+BX1+1, dheight+BY1-1 - old1, i1+BX1, dheight+BY1-1 - i2); } } else { if (i2 > 0) { gdk_draw_line(band->pixmap, gc1[0], i1+BX1, dheight+BY1-1 - i2, i1+BX1, dheight+BY1-1); } } old1 = i2; } if (band->show != 1) { i2x = (int)((double)dheight * (sum2) / max / si); if (band->mode == 2) { for (ix = 0; ix < COLORS; ix++) { i4 = (i2x*(COLORS-ix)/COLORS); if (i4 <= 0) break; gdk_draw_line(band->pixmap, gc2[ix], i1+BX1, dheight+BY1-1 - i4, i1+BX1, dheight+BY1-1); } } else if (band->mode == 1) { if (i2x > 0 && old2 >= 0) { gdk_draw_line(band->pixmap, gc2[0], i1+BX1+1, dheight+BY1-1 - old2, i1+BX1, dheight+BY1-1 - i2x); } } else { if (i2x > 0) { gdk_draw_line(band->pixmap, gc2[0], i1+BX1, dheight+BY1-1 - i2x, i1+BX1, dheight+BY1-1); } } old2 = i2x; } if (band->show == 0) i2 = i2x; else if (band->show != 1 && i2x > i2) i2 = i2x; if ((dwidth - i1) % 60 == 0) { add = unit / 4; } else { add = unit; } i4 = 0; while (1) { i4 += add; i3 = (int)((double)(dheight-1) * i4 / max); if (i3 >= dheight-1) break; if (i3 > i2 || !((dwidth-i1) % 10) || add != unit) { gdk_draw_point(band->pixmap, gc4, i1+BX1, dheight+BY1-1 - i3); } } if (band->show != 0) { i3 = (int)((double)bytes1 / (dwidth - i1) / max * dheight); gdk_draw_point(band->pixmap, gc5, i1+BX1, dheight+BY1-1 - i3); } } } i4 = 0; while (1) { i4 += unit; i3 = (int)((double)(dheight-1) * i4 / max); if (dheight < i3) break; print_size(str, i4); i2 = gdk_string_width(font1, str); gdk_draw_string(band->pixmap, font1, gc4, BX1-i2-4, dheight+BY1-1 - i3 +theight, str); gdk_draw_line(band->pixmap, gc4, BX1-11, dheight+BY1-1 - i3, BX1-1, dheight+BY1-1 - i3); } g_free(old_val1); g_free(old_val2); band->need_update = 0; just_paint: gdk_draw_pixmap (band->area->window, band->area->style->black_gc, band->pixmap, 0,0,0,0, width, height); } long statistic_last_value(statistic_t* stat, int up) { int pos; double sum; int i1; band_t* band; if (!stat || !stat->band[up]) return 0; band = stat->band[up]; pos = band->pos[0]-1; sum = 0; for (i1 = 0; i1 < 10; i1++, pos--) { if (pos < 0) pos = band->size[0]-1; sum += band->val1[0][pos]; } sum /= 10; return (long)sum; } GtkCTreeNode* access_ctree_insert_access(access_t* parent, access_t * access, int depth); static int hash_key (char *name) { unsigned long h = 0; if (!name) return 0; for (; *name; name++) { h = h + tolower (*name); } return (h%ACCESS_HASH_LENGTH); } void statistic_init(statistic_t * stat) { int i1; stat->incomplete[0] = .0; stat->incomplete[1] = .0; stat->total[0] = .0; stat->total[1] = .0; stat->band[0] = band_new(0); stat->band[1] = band_new(1); if (!GTK_WIDGET_REALIZED(global.win)) gtk_widget_realize(global.win); for (i1 = 0; i1 < COLORS; i1++) { gdk_color_alloc(gtk_widget_get_colormap(global.win), &color1[i1]); gc1[i1] = gdk_gc_new(global.win->window); gdk_gc_set_foreground(gc1[i1], &color1[i1]); gdk_color_alloc(gtk_widget_get_colormap(global.win), &color2[i1]); gc2[i1] = gdk_gc_new(global.win->window); gdk_gc_set_foreground(gc2[i1], &color2[i1]); } gdk_color_alloc(gtk_widget_get_colormap(global.win), &color3); gc3 = gdk_gc_new(global.win->window); gdk_gc_set_foreground(gc3, &color3); gdk_color_alloc(gtk_widget_get_colormap(global.win), &color4); gc4 = gdk_gc_new(global.win->window); gdk_gc_set_foreground(gc4, &color4); gdk_color_alloc(gtk_widget_get_colormap(global.win), &color5); gc5 = gdk_gc_new(global.win->window); gdk_gc_set_foreground(gc5, &color5); font1 = gdk_fontset_load ("-Adobe-Helvetica-medium-R-Normal--*-100-*-*-*-*-iso8859-*,*-medium-R-Normal--*-100-*"); access_load(); } void statistic_update(statistic_t * stat) { GList *dlist; resume_t *resume; // calcing incomplete stat->incomplete[0] = .0; stat->incomplete[1] = .0; for (dlist = global.incomplete; dlist; dlist = dlist->next) { resume = dlist->data; if (resume->flags & RESUME_FINISHED) continue; if (resume->comp_size) { stat->incomplete[0] += resume->inc_size; stat->incomplete[1] += resume->comp_size; } } stat->total[0] += global.down_width.bytes[0]; stat->total[1] += global.up_width.bytes[0]; band_new_vals(stat->band[0], global.down_width.bytes[0], global.down_width.bytes[1]); band_new_vals(stat->band[1], global.up_width.bytes[0], global.up_width.bytes[1]); } void statistic_draw_graphs(statistic_t* stat, int expose) { if (stat->band[0] && (expose || stat->band[0]->tno[stat->band[0]->real_step] == 0)) band_draw_graph(stat->band[0], 0); if (stat->band[1] && (expose || stat->band[1]->tno[stat->band[1]->real_step] == 0)) band_draw_graph(stat->band[1], 1); } void statistic_output(statistic_t * stat) { statistic_draw_graphs(stat, 0); } void statistic_log(statistic_t * stat) { char str[1204]; char str2[1204]; time_t tim; if (global.current_time <= global.start_time) tim = global.start_time+1; else tim = global.current_time; l_log(NULL, "statistic", LOG_OTHER, "Uptime : %s\n", print_time(str, tim - global.start_time)); l_log(NULL, "statistic", LOG_OTHER, "Bytes downloaded : %.0f (%s) (%s)\n", stat->total[0], print_size(str, stat->total[0]), print_speed(str2, stat->total[0] / (tim - global.start_time), 1)); l_log(NULL, "statistic", LOG_OTHER, "Bytes uploaded : %.0f (%s) (%s)\n", stat->total[1], print_size(str, stat->total[1]), print_speed(str2, stat->total[1] / (tim - global.start_time), 1)); } int statistic_get_no_steps() { return BAND_SIZE; } static access_t *access_new(char *name) { access_t *result; result = g_malloc(sizeof(access_t)); result->name = g_strdup(name); result->last = 0; result->accesses = 0; result->access_list = NULL; result->done = 0.; result->parent = NULL; result->temp = 0; return result; } static access_t *access_search(access_t * access, char *name) { GList *dlist; access_t *sub; int key; if (!access->access_list) return NULL; key = hash_key(name); for (dlist = access->access_list[key]; dlist; dlist = dlist->next) { sub = dlist->data; if (!strcmp(sub->name, name)) return sub; } return NULL; } static access_t* access_add(access_t * access, char *name, int depth) { access_t *sub; int key; sub = access_search(access, name); if (!sub) { sub = access_new(name); access_ctree_insert_access(access, sub, depth); if (!access->access_list) { access->access_list = g_malloc(ACCESS_HASH_LENGTH * sizeof(access_t*)); for (key = 0; key < ACCESS_HASH_LENGTH; key++) access->access_list[key] = NULL; } key = hash_key(sub->name); access->access_list[key] = g_list_prepend(access->access_list[key], sub); sub->parent = access; } return sub; } static void access_touch(access_t * access) { while (access) { access->last = global.current_time; access_ctree_update(NULL, access); access = access->parent; } } void access_destroy(access_t * access, int depth) { GList *dlist; access_t *sub; int i1; if (depth == 0) return; if (access->access_list) { for (i1 = 0; i1 < ACCESS_HASH_LENGTH; i1++) { for (dlist = access->access_list[i1]; dlist; dlist = dlist->next) { sub = dlist->data; access_destroy(sub, depth-1); } g_list_free(access->access_list[i1]); } g_free(access->access_list); } if (access->name) g_free(access->name); g_free(access); } static void access_remove(access_t* child) { int key; access_t* parent; GtkCTreeNode* node; static GtkCTree* ctree = NULL; parent = child->parent; key = hash_key(child->name); parent->access_list[key] = g_list_remove(parent->access_list[key], child); if (!ctree) ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); node = gtk_ctree_find_by_row_data(ctree, NULL, child); if (node) gtk_ctree_remove_node(ctree, node); access_destroy(child, -1); } static GtkCTreeNode* access_find_node(access_t* access) { GtkCTreeNode* node; GtkCTree* ctree; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); node = gtk_ctree_find_by_row_data(ctree, NULL, access); return node; } void access_new_request(upload_t *upload) { access_t *access1; access_t *access2; transfer_t* trans; trans = TRANS(upload); if (trans->is_dcc) return; if (global.options.access_timeout <= 0) return; if (!upload->access) { if (global.statistic.access_format == 0) { access1 = access_add(global.statistic.file_access, upload->file->longname, 1); access2 = access_add(access1, trans->user_info->nick, 2); } else { access1 = access_add(global.statistic.file_access, trans->user_info->nick, 1); access2 = access_add(access1, upload->file->longname, 2); } upload->access = access2; upload->node = access_find_node(access2); } else { access1 = upload->access->parent; access2 = upload->access; } // if this was a new user to this file, then increase file and global counter if (!access2->accesses) { global.statistic.file_access->accesses++; access1->accesses++; } access2->accesses++; access_touch(access2); if (global.statistic.file_access->accesses <= 1) access_cleanup(10); // access_save(); } void access_finished_request(upload_t* upload) { access_t* access1; GtkCTreeNode* node; if (!upload->access || !upload->segment) return; access1 = upload->access; node = upload->node; while (access1 && node) { access1->done += upload->segment->size; access_ctree_update(node, access1); access1 = access1->parent; node = GTK_CTREE_ROW(node)->parent; } } access_t* access_get_access(GtkCTreeNode* node) { GtkCTree* ctree; access_t* access; if (!node) return NULL; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); access = gtk_ctree_node_get_row_data(ctree, node); return access; } static GtkCTreeNode* access_ctree_insert_real(GtkCTree* ctree, GtkCTreeNode* node, access_t * access, int depth) { GtkCTreeNode *node2; char str[100]; if (global.statistic.access_format == 0) { if (depth == 1) strcpy(tstr[0], get_short_name(access->name)); else strcpy(tstr[0], access->name); } else { if (depth == 2) strcpy(tstr[0], get_short_name(access->name)); else strcpy(tstr[0], access->name); } sprintf(tstr[1], "%d", access->accesses); sprintf(tstr[2], "%s", print_size(str, access->done)); if (access->last) { strcpy(tstr[3], ctime(&access->last)); tstr[3][strlen(tstr[3]) - 1] = 0; } else strcpy(tstr[3], "None yet"); if (depth == 1) { node2 = gtk_ctree_insert_node(ctree, node, NULL, list, 5, global.pix.folder, global.pix.folderb, global.pix.folder_open, global.pix.folder_openb, FALSE, FALSE); } else { node2 = gtk_ctree_insert_node(ctree, node, NULL, list, 5, NULL, NULL, NULL, NULL, FALSE, FALSE); } gtk_ctree_node_set_row_data(ctree, node2, access); return node2; } GtkCTreeNode* access_ctree_insert_node(GtkCTreeNode* node, access_t * access, int depth) { static GtkCTree* ctree = NULL; if (!ctree) ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); return access_ctree_insert_real(ctree, node, access, depth); } GtkCTreeNode* access_ctree_insert_access(access_t* parent, access_t * access, int depth) { static GtkCTree* ctree = NULL; GtkCTreeNode* node = NULL; if (!ctree) ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); if (parent) node = gtk_ctree_find_by_row_data(ctree, NULL, parent); return access_ctree_insert_real(ctree, node, access, depth); } void access_ctree_update(GtkCTreeNode* node, access_t * access) { GtkCTree *ctree; char str[100]; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); if (!node) node = gtk_ctree_find_by_row_data(ctree, NULL, access); if (!node) return; sprintf(tstr[0], "%d", access->accesses); gtk_ctree_node_set_text(ctree, node, 1, tstr[0]); sprintf(tstr[0], "%s", print_size(str, access->done+access->temp)); gtk_ctree_node_set_text(ctree, node, 2, tstr[0]); access->temp = 0; if (access->last) { strcpy(tstr[0], ctime(&access->last)); tstr[0][strlen(tstr[0]) - 1] = 0; } else strcpy(tstr[0], "None yet"); gtk_ctree_node_set_text(ctree, node, 3, tstr[0]); } void upload_remove_access(access_t* access); static gint access_ctree_remove_old(gpointer data ATTR_UNUSED) { GtkCTree *ctree; access_t *access1; access_t *access2; GList *dlist; GList *dlist2; int i1, i2; GList* to_remove = NULL; GList* to_update = NULL; long new_timeout = 0; // when next item has to be removed int temp; if (!global.options.access_cleanup) { #ifdef STAT_DEBUG printf("[ACCESS] cleanup canceled\n"); #endif return 0; } if (!global.statistic.file_access) return 0; if (!global.statistic.file_access->access_list) return 0; #ifdef STAT_DEBUG printf("[ACCESS] cleaning up now\n"); #endif ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); gtk_clist_freeze(GTK_CLIST(ctree)); for (i1 = 0; i1 < ACCESS_HASH_LENGTH; i1++) { for (dlist = global.statistic.file_access->access_list[i1]; dlist; dlist = dlist->next) { access1 = dlist->data; if (!access1->access_list) continue; for (i2 = 0; i2 < ACCESS_HASH_LENGTH; i2++) { for (dlist2 = access1->access_list[i2]; dlist2; dlist2 = dlist2->next) { access2 = dlist2->data; temp = global.options.access_timeout *60 * 60 - global.current_time + access2->last; if (temp <= 0) to_remove = g_list_prepend(to_remove, access2); else if (!new_timeout || temp < new_timeout) new_timeout = temp; } } } } i1 = i2 = 0; for (dlist = to_remove; dlist; dlist = dlist->next) { access1 = dlist->data; access2 = access1->parent; upload_remove_access(access1); access2->accesses--; global.statistic.file_access->accesses--; access2->done -= access1->done; global.statistic.file_access->done -= access1->done; access_remove(access1); i1++; if (access2->accesses <= 0) { access_remove(access2); i2++; } else if (!g_list_find(to_update, access2)) { g_list_prepend(to_update, access2); } } g_list_free(to_remove); #ifdef STAT_DEBUG printf("[ACCESS] removed %d leafs and %d parents\n", i1, i2); #endif if (to_update) { for (dlist = to_update; dlist; dlist = dlist->next) { access1 = to_update->data; access_ctree_update(NULL, access1); } g_list_free(to_update); } if (global.statistic.file_access->accesses == 0) global.statistic.file_access->last = 0; access_ctree_update(NULL, global.statistic.file_access); gtk_clist_thaw(GTK_CLIST(ctree)); if (new_timeout > 0) access_cleanup(new_timeout); return 0; } void access_cleanup(int when) { if (cleanup_timeout >= 0) gtk_timeout_remove(cleanup_timeout); if (when > 3600) when = 3600; #ifdef STAT_DEBUG printf("[ACCESS] next cleanup is in %d seconds\n", when); #endif cleanup_timeout = gtk_timeout_add(when*1000, access_ctree_remove_old, NULL); } access_t *access_add2(access_t * access, char *name, int search) { access_t *sub; int key; if (search) sub = access_search(access, name); else sub = NULL; if (!sub) { sub = access_new(name); if (!access->access_list) { access->access_list = g_malloc(ACCESS_HASH_LENGTH * sizeof(access_t*)); for (key = 0; key < ACCESS_HASH_LENGTH; key++) access->access_list[key] = NULL; } key = hash_key(name); access->access_list[key] = g_list_prepend(access->access_list[key], sub); sub->parent = access; } return sub; } gint access_load_idle(gpointer data) { FILE *fd = (FILE *) data; char line[2048]; int cnt = 0; static access_t *current = NULL; access_t *access; char* name; char* last; char* accesses; char* done; while (mfgets(line, sizeof(line), fd)) { cnt++; if (!(name = arg(line, 2))) continue; if (!strcasecmp(name, "AccessFormat")) { last = arg(NULL, 2); if (last) { global.statistic.access_format = atoi(last); if (global.statistic.file_access->name) g_free(global.statistic.file_access->name); if (global.statistic.access_format) global.statistic.file_access->name = g_strdup("User Request Statistic"); else global.statistic.file_access->name = g_strdup("File Request Statistic"); } continue; } else if (*name == '-') { if (!current) continue; name = arg(NULL, 2); } else { current = global.statistic.file_access; } last = arg(NULL, 2); accesses = arg(NULL, 2); done = arg(NULL, 2); if (!accesses) continue; access = access_add2(current, name, 0); access->access_list = NULL; if (current == global.statistic.file_access) { current = access; } else { access->last = strtoul(last, NULL, 10); if (current->last < access->last) current->last = access->last; if (global.statistic.file_access->last < access->last) global.statistic.file_access->last = access->last; access->accesses = atoi(accesses); current->accesses++; global.statistic.file_access->accesses++; if (done) access->done = strtod(done, NULL); if (access->done < 100) access->done = 0; current->done += access->done; global.statistic.file_access->done += access->done; } if (cnt > 10) return 1; } fclose(fd); global.status.access_read = 1; access_show(global.statistic.file_access); access_cleanup(10); return 0; } void access_load() { FILE *fd; char *fname; global.statistic.file_access = NULL; global.statistic.access_format = 0; global.statistic.file_access = access_new("File Request Statistic"); fname = g_strdup_printf("%s/access.list", global.options.config_dir); if ((fd = fopen(fname, "r")) == NULL) { global.status.access_read = 1; g_free(fname); access_show(global.statistic.file_access); return; } g_free(fname); gtk_idle_add(access_load_idle, (gpointer) fd); } void access_save() { GList *dlist; GList *dlist2; char *fname; char *fname_new; FILE *fd; access_t *access1; access_t *access2; int i1, i2; fname = g_strdup_printf("%s/access.list", global.options.config_dir); if (!global.statistic.file_access->access_list) { unlink(fname); g_free(fname); return; } fname_new = g_strdup_printf("%s/access.list.new", global.options.config_dir); if ((fd = fopen(fname_new, "w")) == NULL) { g_warning("Could not write [%s]\n", fname); g_free(fname); g_free(fname_new); return; } fprintf(fd, "AccessFormat %d\n", global.statistic.access_format); for (i1 = 0; i1 < ACCESS_HASH_LENGTH; i1++) { for (dlist = global.statistic.file_access->access_list[i1]; dlist; dlist = dlist->next) { access1 = dlist->data; if (!access1->access_list || !access1->accesses) continue; qfprintf(fd, "%S %lu %d", access1->name, access1->last, access1->accesses); fprintf(fd, " %f\n", access1->done); for (i2 = 0; i2 < ACCESS_HASH_LENGTH; i2++) { for (dlist2 = access1->access_list[i2]; dlist2; dlist2 = dlist2->next) { access2 = dlist2->data; qfprintf(fd, "- %S %lu %d", access2->name, access2->last, access2->accesses); fprintf(fd, " %f\n", access2->done); } } } } if (!ferror(fd)) rename(fname_new, fname); else { g_warning("Could not write [%s]\n", fname); } fclose(fd); g_free(fname); g_free(fname_new); } static upload_t* list_remove_upload(GList** uploads, access_t* access) { GList* dlist; upload_t* upload; for (dlist = *uploads; dlist; dlist = dlist->next) { upload = dlist->data; if (upload->access == access) { *uploads = g_list_remove(*uploads, upload); return upload; } } return NULL; } void access_show(access_t* file_access) { GList* dlist; GList* dlist2; access_t* access1, *access2; GtkCTree *ctree; int i1, i2; GtkCTreeNode* node1; GtkCTreeNode* node2; GtkCTreeNode* node3; GList* uploads = NULL; socket_t* socket; upload_t* upload; if (!file_access) return; // printf("access_show(): %ld\n", time(NULL)); ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); gtk_clist_freeze(GTK_CLIST(ctree)); node1 = access_ctree_insert_node(NULL, file_access, 0); if (!file_access->access_list) { gtk_clist_thaw(GTK_CLIST(ctree)); return; } // finding all uploads with an access for (dlist = global.sockets; dlist; dlist = dlist->next) { socket = dlist->data; if (!socket || socket->type != S_UPLOAD) continue; upload = socket->data; if (!upload || !upload->access) continue; uploads = g_list_append(uploads, upload); } for (i1 = 0; i1 < ACCESS_HASH_LENGTH; i1++) { // printf("showing %d %p\n", i1, node1); for (dlist = file_access->access_list[i1]; dlist; dlist = dlist->next) { access1 = dlist->data; node2 = access_ctree_insert_node(node1, access1, 1); if (!access1->access_list) continue; for (i2 = 0; i2 < ACCESS_HASH_LENGTH; i2++) { for (dlist2 = access1->access_list[i2]; dlist2; dlist2 = dlist2->next) { access2 = dlist2->data; node3 = access_ctree_insert_node(node2, access2, 2); upload = list_remove_upload(&uploads, access2); // updating the node in the upload struct if (upload) upload->node = access_find_node(access2); } } } } gtk_clist_thaw(GTK_CLIST(ctree)); // printf("access_show_done(): %ld\n", time(NULL)); } static void access_convert() { GtkCTree *ctree; GList* dlist; GList* dlist2; access_t* acc1, *acc2; access_t* access1; access_t* file_access = NULL; int i1, i2; int key; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); gtk_clist_clear(GTK_CLIST(ctree)); if (global.statistic.access_format) { global.statistic.access_format = 0; file_access = access_new("File Request Statistic"); } else { global.statistic.access_format = 1; file_access = access_new("User Request Statistic"); } if (global.statistic.file_access->access_list) { for (i1 = 0; i1 < ACCESS_HASH_LENGTH; i1++) { for (dlist = global.statistic.file_access->access_list[i1]; dlist; dlist = dlist->next) { acc1 = dlist->data; if (!acc1->access_list) continue; for (i2 = 0; i2 < ACCESS_HASH_LENGTH; i2++) { for (dlist2 = acc1->access_list[i2]; dlist2; dlist2 = dlist2->next) { acc2 = dlist2->data; access1 = access_add2(file_access, acc2->name, 1); g_free(acc2->name); acc2->name = g_strdup(acc1->name); if (!access1->access_list) { access1->access_list = g_malloc(ACCESS_HASH_LENGTH * sizeof(access_t*)); for (key = 0; key < ACCESS_HASH_LENGTH; key++) access1->access_list[key] = NULL; } key = hash_key(acc2->name); access1->access_list[key] = g_list_prepend(access1->access_list[key], acc2); acc2->parent = access1; access1->done += acc2->done; file_access->done += acc2->done; if (access1->last < acc2->last) { access1->last = acc2->last; } if (file_access->last < acc2->last) file_access->last = acc2->last; access1->accesses++; file_access->accesses++; } } } } } access_destroy(global.statistic.file_access, 2); global.statistic.file_access = file_access; access_show(file_access); access_save(); } void on_access_collapse(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data ATTR_UNUSED) { GtkCTree *ctree; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); gtk_ctree_collapse_recursive(ctree, NULL); } void on_access_expand(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data ATTR_UNUSED) { GtkCTree *ctree; ctree = GTK_CTREE(lookup_widget(global.win, "ctree3")); gtk_ctree_expand_recursive(ctree, NULL); } void on_access_mode(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data ATTR_UNUSED) { access_convert(); } GtkWidget *create_access_popup(void) { GtkCTree *ctree; GtkCTreeNode *node; GtkWidget *popup; GtkWidget *user_popup; GtkWidget *item; GtkWidget *separator; GtkAccelGroup *popup_accels; access_t* access; int mode; ctree = GTK_CTREE(global.popup_list); node = gtk_ctree_node_nth(ctree, global.popup_row); if (!node) return NULL; access = gtk_ctree_node_get_row_data(ctree, node); popup = gtk_menu_new(); gtk_object_set_data(GTK_OBJECT(popup), "popup", popup); popup_accels = gtk_menu_ensure_uline_accel_group(GTK_MENU(popup)); if (global.statistic.access_format == 0) { if (global.popup_row == 0) mode = 0; else if (GTK_CTREE_ROW(node)->children == NULL) mode = 2; // user else if (GTK_CTREE_ROW(node)->parent) mode = 1; else mode = 0; } else { if (global.popup_row == 0) mode = 0; else if (GTK_CTREE_ROW(node)->children == NULL) mode = 1; // file else if (GTK_CTREE_ROW(node)->parent) mode = 2; else mode = 0; } if (mode != 0) { if (mode == 1) { item = create_open_menu(mtype_get(access->name), access->name, 1); gtk_container_add(GTK_CONTAINER(popup), item); } else if (mode == 2) { item = gtk_menu_item_new_with_label("User Menu"); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); user_popup = create_user_popup(M_ACCESS, access); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), user_popup); } separator = gtk_menu_item_new(); gtk_widget_show(separator); gtk_container_add(GTK_CONTAINER(popup), separator); gtk_widget_set_sensitive(separator, FALSE); } item = gtk_menu_item_new_with_label("Expand"); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_access_expand), NULL); item = gtk_menu_item_new_with_label("Collapse"); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_access_collapse), NULL); separator = gtk_menu_item_new(); gtk_widget_show(separator); gtk_container_add(GTK_CONTAINER(popup), separator); gtk_widget_set_sensitive(separator, FALSE); if (global.statistic.access_format == 0) item = gtk_menu_item_new_with_label("Show by user"); else item = gtk_menu_item_new_with_label("Show by file"); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_access_mode), NULL); return popup; } static void on_graph_mode(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { int val = (int)user_data; if (popup_band->mode == val) return; popup_band->mode = val; popup_band->need_update = 1; if (popup_band == global.statistic.band[0]) global.options.graph_mode[0] = val; else global.options.graph_mode[1] = val; } static void on_graph_interval(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { int val = (int)user_data; if (popup_band->step == val) return; popup_band->step = val; if (val < 0) popup_band->real_step = 0; else popup_band->real_step = val; popup_band->need_update = 1; if (popup_band == global.statistic.band[0]) global.options.graph_interval[0] = val; else global.options.graph_interval[1] = val; } static void on_graph_smooth(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { int val = (int)user_data; if (popup_band->smooth == val) return; popup_band->smooth = val; popup_band->need_update = 1; if (popup_band == global.statistic.band[0]) global.options.graph_smooth[0] = val; else global.options.graph_smooth[1] = val; } static void on_graph_show(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { int val = (int)user_data; if (popup_band->show == val) return; popup_band->show = val; popup_band->need_update = 1; if (popup_band == global.statistic.band[0]) global.options.graph_show[0] = val; else global.options.graph_show[1] = val; } GtkWidget* create_band_popup(band_t* band) { GSList *group; GtkWidget* popup; GtkWidget* item[51]; GtkWidget* popup2; char str[1024]; int i1; if (!band) return NULL; popup_band = band; popup = gtk_menu_new(); // Mode submenu item[0] = gtk_menu_item_new_with_label("Mode"); gtk_widget_show(item[0]); gtk_container_add(GTK_CONTAINER(popup), item[0]); popup2 = gtk_menu_new(); gtk_widget_show(popup2); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item[0]), popup2); group = NULL; item[0] = gtk_radio_menu_item_new_with_label (group, "Solid"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[0])); gtk_widget_show (item[0]); gtk_container_add (GTK_CONTAINER (popup2), item[0]); if (band->mode == 0) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[0]), TRUE); item[1] = gtk_radio_menu_item_new_with_label (group, "Lines"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[1])); gtk_widget_show (item[1]); gtk_container_add (GTK_CONTAINER (popup2), item[1]); if (band->mode == 1) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[1]), TRUE); item[2] = gtk_radio_menu_item_new_with_label (group, "Shaded"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[2])); gtk_widget_show (item[2]); gtk_container_add (GTK_CONTAINER (popup2), item[2]); if (band->mode == 2) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[2]), TRUE); for (i1 = 0; i1 < 3; i1++) { gtk_signal_connect(GTK_OBJECT(item[i1]), "activate", GTK_SIGNAL_FUNC(on_graph_mode), (void*)(i1)); } // Interval submenu item[0] = gtk_menu_item_new_with_label("Interval"); gtk_widget_show(item[0]); gtk_container_add(GTK_CONTAINER(popup), item[0]); popup2 = gtk_menu_new(); gtk_widget_show(popup2); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item[0]), popup2); group = 0; item[0] = gtk_radio_menu_item_new_with_label (group, "Auto adjust"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[0])); gtk_widget_show (item[0]); gtk_container_add (GTK_CONTAINER (popup2), item[0]); item[1] = gtk_menu_item_new(); gtk_widget_show(item[1]); gtk_container_add(GTK_CONTAINER(popup2), item[1]); if (band->step == -1) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[0]), TRUE); for (i1 = 0; i1 < BAND_SIZE; i1++) { print_time_unit(str, steps[i1]); item[i1+1] = gtk_radio_menu_item_new_with_label (group, str); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[i1+1])); gtk_widget_show (item[i1+1]); gtk_container_add (GTK_CONTAINER (popup2), item[i1+1]); if (band->step == i1) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[i1+1]), TRUE); } for (i1 = 0; i1 <= BAND_SIZE; i1++) { gtk_signal_connect(GTK_OBJECT(item[i1]), "activate", GTK_SIGNAL_FUNC(on_graph_interval), (void*)(i1-1)); } // Smooth submenu item[0] = gtk_menu_item_new_with_label("Smooth"); gtk_widget_show(item[0]); gtk_container_add(GTK_CONTAINER(popup), item[0]); popup2 = gtk_menu_new(); gtk_widget_show(popup2); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item[0]), popup2); group = NULL; for (i1 = 1; i1 <= 50; i1++) { sprintf(str, "%d", i1); item[i1] = gtk_radio_menu_item_new_with_label (group, str); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[i1])); gtk_widget_show (item[i1]); gtk_container_add (GTK_CONTAINER (popup2), item[i1]); if (band->smooth == i1) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[i1]), TRUE); if (i1 >= 10) i1 += 1; if (i1 >= 30) i1 += 2; } for (i1 = 1; i1 <= 50; i1++) { gtk_signal_connect(GTK_OBJECT(item[i1]), "activate", GTK_SIGNAL_FUNC(on_graph_smooth), (void*)(i1)); if (i1 >= 10) i1 += 1; if (i1 >= 30) i1 += 2; } // Display submenu item[0] = gtk_menu_item_new_with_label("Display"); gtk_widget_show(item[0]); gtk_container_add(GTK_CONTAINER(popup), item[0]); popup2 = gtk_menu_new(); gtk_widget_show(popup2); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item[0]), popup2); group = NULL; item[0] = gtk_radio_menu_item_new_with_label (group, "Server"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[0])); gtk_widget_show (item[0]); gtk_container_add (GTK_CONTAINER (popup2), item[0]); if (band->show == 0) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[0]), TRUE); item[1] = gtk_radio_menu_item_new_with_label (group, "Transfers"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[1])); gtk_widget_show (item[1]); gtk_container_add (GTK_CONTAINER (popup2), item[1]); if (band->show == 1) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[1]), TRUE); item[2] = gtk_radio_menu_item_new_with_label (group, "Both"); group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item[2])); gtk_widget_show (item[2]); gtk_container_add (GTK_CONTAINER (popup2), item[2]); if (band->show == 2) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item[2]), TRUE); for (i1 = 0; i1 < 3; i1++) { gtk_signal_connect(GTK_OBJECT(item[i1]), "activate", GTK_SIGNAL_FUNC(on_graph_show), (void*)(i1)); } return popup; }