/* 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 "lopster.h" #include "callbacks.h" #include "support.h" #include "interface.h" #include "connection.h" #include "global.h" #include "scheme.h" #include "log.h" #include "utils.h" typedef struct { char* dirname; GList* files; } log_net_t; typedef struct { char* filename; off_t size; time_t mtime; GList* sessions; int status; FILE* fd; log_net_t* ln; } log_file_t; typedef struct { long offset; char* sname; log_file_t* file; } log_session_t; static GList* other_logs = NULL; static GtkWidget* log_win = NULL; static log_file_t* current_lf = NULL; log_t *search_log(net_t* net, char *des, int type) { GList *dlist; log_t *log; char str[512]; for (dlist = net?net->logs:other_logs; dlist; dlist = dlist->next) { log = dlist->data; if (!g_strcasecmp(des, log->description) && (type == log->type)) { if (!log->fd) log->fd = fopen(log->filename, "a+"); if (global.options.log_expire && (global.current_time - log->time > global.options.log_expire * 60 * 60)) { fprintf(log->fd, "===========================================\n"); fprintf(log->fd, " Lopster session: %s", ctime(&global.current_time)); fprintf(log->fd, "===========================================\n"); fflush(log->fd); log->time = global.current_time; } return log; } } log = g_malloc(sizeof(log_t)); sprintf(str, "%s/log/", global.options.config_dir); if (net) { strcat(str, net->name); strcat(str, "/"); } if (type == LOG_CHANNEL) { if (!net) strcat(str, "_channels_/"); create_dir(str); strcat(str, des); strcat(str, ".channel"); } else if (type == LOG_PRIVATE) { if (!net) strcat(str, "_private_/"); create_dir(str); strcat(str, des); strcat(str, ".priv"); } else { if (!net) strcat(str, "_general_/"); create_dir(str); strcat(str, des); strcat(str, ".log"); } log->fd = fopen(str, "a+"); log->filename = g_strdup(str); log->description = g_strdup(des); log->type = type; log->time = 0; if (net) net->logs = g_list_append(net->logs, log); else other_logs = g_list_append(other_logs, log); if (log->fd) { log->time = global.current_time; strcpy(str, ctime(&global.current_time)); fprintf(log->fd, "===========================================\n"); fprintf(log->fd, " Lopster session: %s", ctime(&global.current_time)); fprintf(log->fd, "===========================================\n"); fflush(log->fd); } return log; } void l_log(net_t* net, char *des, int type, const char *fmt, ...) { va_list ap; log_t *log; char stime[200]; if ((global.options.logging & (1<fd) return; fprintf(log->fd, "[%s] ", current_time(stime, TIME_HOUR_MIN_SEC)); va_start(ap, fmt); vfprintf(log->fd, fmt, ap); fflush(log->fd); va_end(ap); if (type == LOG_PRIVATE) { fclose(log->fd); log->fd = NULL; } } void close_logs(net_t* net) { GList* dlist; log_t* log; for (dlist = net?net->logs:other_logs; dlist; dlist = dlist->next) { log = dlist->data; if (log->fd) fclose(log->fd); g_free(log->filename); g_free(log->description); g_free(log); } g_list_free(net?net->logs:other_logs); if (net) net->logs = NULL; else other_logs = NULL; } void log_file_destroy(log_file_t* lf) { GList* dlist2; log_session_t* ls; g_free(lf->filename); for (dlist2 = lf->sessions; dlist2; dlist2 = dlist2->next) { ls = dlist2->data; g_free(ls->sname); g_free(ls); } g_list_free(lf->sessions); if (lf->fd) fclose(lf->fd); if (lf->status > 0) gtk_idle_remove(lf->status); g_free(lf); } static void log_net_destroy(gpointer data) { GList* dlist; log_net_t* ln = data; log_file_t* lf; g_free(ln->dirname); for (dlist = ln->files; dlist; dlist = dlist->next) { lf = dlist->data; log_file_destroy(lf); } g_list_free(ln->files); g_free(ln); } void log_show_all() { DIR *dir; struct dirent *entry; char name[2048]; char* dname; int row; GtkCList *clist; struct stat buf; log_net_t* ln; if (!log_win) return; clist = GTK_CLIST(lookup_widget(log_win, "clist39")); sprintf(name, "%s%clog", global.options.config_dir, DIR_SEP); if ((dir = opendir(name)) == NULL) { printf("could not open logdir\n"); return; } while ((entry = readdir(dir)) != NULL) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; dname = g_strdup_printf("%s%c%s", name, DIR_SEP, entry->d_name); stat(dname, &buf); if (buf.st_mode & S_IFDIR) { ln = g_malloc(sizeof(*ln)); ln->dirname = dname; ln->files = NULL; strcpy(tstr[0], entry->d_name); row = gtk_clist_append(clist, list); gtk_clist_set_row_data_full(clist, row, ln, log_net_destroy); } else { g_free(dname); } } closedir(dir); } gint text_compare(GtkCList * clist, gconstpointer ptr1, gconstpointer ptr2) { char *text1 = NULL; char *text2 = NULL; GtkCListRow *row1 = (GtkCListRow *) ptr1; GtkCListRow *row2 = (GtkCListRow *) ptr2; text1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text; text2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text; if (!text2) return (text1 != NULL); if (!text1) return -1; return g_strcasecmp(text1, text2); } gint log_compare(GtkCList * clist, gconstpointer ptr1, gconstpointer ptr2) { log_file_t* lf1; log_file_t* lf2; GtkCListRow *row1 = (GtkCListRow *) ptr1; GtkCListRow *row2 = (GtkCListRow *) ptr2; lf1 = row1->data; if (!lf1) return 0; lf2 = row2->data; if (!lf2) return 0; if (clist->sort_column == 0) { return g_strcasecmp(lf1->filename, lf2->filename); } else if (clist->sort_column == 1) { if (lf1->size < lf2->size) return -1; if (lf1->size > lf2->size) return 1; else return 0; } else if (clist->sort_column == 2) { if (lf1->mtime < lf2->mtime) return -1; if (lf1->mtime > lf2->mtime) return 1; else return 0; } else { return 0; } } void on_list_click_column(GtkCList * clist, gint column, gpointer user_data); void log_show_init() { GtkText *text; GtkWidget* temp; if (log_win) { if (log_win->window) gdk_window_raise(log_win->window); return; } log_win = create_log_win(); text = GTK_TEXT(lookup_widget(log_win, "text16")); gtk_text_set_word_wrap(text, 1); gtk_widget_set_style(GTK_WIDGET(text), global.scheme->style); temp = lookup_widget(log_win, "clist39"); gtk_clist_set_compare_func(GTK_CLIST(temp), text_compare); gtk_clist_set_auto_sort(GTK_CLIST(temp), 1); gtk_signal_connect (GTK_OBJECT (temp), "click_column", GTK_SIGNAL_FUNC (on_list_click_column), NULL); temp = lookup_widget(log_win, "clist40"); gtk_clist_set_compare_func(GTK_CLIST(temp), log_compare); gtk_clist_set_auto_sort(GTK_CLIST(temp), 1); gtk_clist_set_sort_column(GTK_CLIST(temp), 1); gtk_signal_connect (GTK_OBJECT (temp), "click_column", GTK_SIGNAL_FUNC (on_list_click_column), NULL); gtk_widget_show(log_win); log_show_all(); } void log_show_exit() { if (!log_win) return; gtk_widget_destroy(log_win); log_win = NULL; } void log_load_network(log_net_t* ln) { struct dirent *entry; struct stat buf; char* dname; log_file_t* lf; DIR *dir; if (!ln || ln->files) return; if ((dir = opendir(ln->dirname)) == NULL) return; while ((entry = readdir(dir)) != NULL) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; dname = g_strdup_printf("%s%c%s", ln->dirname, DIR_SEP, entry->d_name); stat(dname, &buf); if (buf.st_mode & S_IFREG) { lf = g_malloc(sizeof(*lf)); lf->filename = dname; lf->size = buf.st_size; lf->mtime = buf.st_mtime; lf->sessions = NULL; lf->status = -1; lf->fd = NULL; ln->files = g_list_append(ln->files, lf); lf->ln = ln; } else { g_free(dname); } } closedir(dir); } void log_show_network(GtkCList* clist, int row) { log_net_t* ln; log_file_t* lf; GList* dlist; if (!log_win) return; ln = gtk_clist_get_row_data(clist, row); if (!ln) return; if (!ln->files) log_load_network(ln); clist = GTK_CLIST(lookup_widget(log_win, "clist40")); gtk_clist_clear(clist); gtk_clist_freeze(clist); for (dlist = ln->files; dlist; dlist = dlist->next) { lf = dlist->data; strcpy(tstr[0], get_file_name(lf->filename)); print_size(tstr[1], lf->size); strcpy(tstr[2], ctime(&(lf->mtime))); row = gtk_clist_append(clist, list); gtk_clist_set_row_data(clist, row, lf); } gtk_clist_thaw(clist); } gint log_updater(gpointer data) { log_file_t* lf = data; log_session_t* ls = NULL; GtkCList* clist; long off; int row = -1; int in_session = 0; int number = 0; char line[2048]; clist = GTK_CLIST(lookup_widget(log_win, "clist14")); for (;;) { off = ftell(lf->fd); if (!mfgets(line, sizeof(line), lf->fd)) break; if (line[0] == '=') { if (number > 0) return 1; } else if (!strncmp(line, " Lopster session: ", 18)) { if (in_session) { g_warning("logfile error\n"); return 1; } in_session = 1; number = 0; ls = g_malloc(sizeof(*ls)); ls->offset = off; ls->sname = g_strdup(line+18); ls->file = lf; lf->sessions = g_list_append(lf->sessions, ls); strcpy(tstr[0], line + 18); if (current_lf == lf) { row = gtk_clist_append(clist, list); gtk_clist_set_row_data(clist, row, ls); } } else if (in_session) { number++; } } if (current_lf == lf && row >= 0) gtk_clist_select_row(clist, row, 0); fclose(lf->fd); lf->fd = NULL; gtk_idle_remove(lf->status); lf->status = 0; return 0; } void log_show_file(GtkCList* clist, int row) { log_file_t* lf; log_session_t* ls; GList* dlist; if (!log_win) return; lf = gtk_clist_get_row_data(clist, row); if (!lf) return; current_lf = lf; clist = GTK_CLIST(lookup_widget(log_win, "clist14")); if (lf->status == -1) { gtk_clist_clear(clist); lf->fd = fopen(lf->filename, "r"); lf->status = gtk_idle_add(log_updater, lf); } else { clist = GTK_CLIST(lookup_widget(log_win, "clist14")); gtk_clist_clear(clist); gtk_clist_freeze(clist); row = -1; for (dlist = lf->sessions; dlist; dlist = dlist->next) { ls = dlist->data; strcpy(tstr[0], ls->sname); row = gtk_clist_append(clist, list); gtk_clist_set_row_data(clist, row, ls); } if (row >= 0) gtk_clist_select_row(clist, row, 0); gtk_clist_thaw(clist); } } void log_show_session(GtkCList* clist, int row) { log_session_t* ls; GtkWidget* text; FILE* file; char line[2048]; char str[2048]; int number; char* pos, *pos2; int is_new; if (!log_win) return; ls = gtk_clist_get_row_data(clist, row); if (!ls) return; text = lookup_widget(GTK_WIDGET(log_win), "text16"); gtk_text_freeze(GTK_TEXT(text)); gtk_text_set_point(GTK_TEXT(text), 0); gtk_text_forward_delete(GTK_TEXT(text), gtk_text_get_length(GTK_TEXT(text))); file = fopen(ls->file->filename, "r"); if (!file) return; fseek(file, ls->offset, SEEK_SET); number = 0; while (fgets(line, sizeof(line), file)) { number++; if (line[0] == '=') { if (number > 3) break; text_print_colored(text, global.scheme, "message", line); } else if (!strncmp(line, " Lopster session: ", 18)) { text_print_colored(text, global.scheme, "message", line); } else { is_new = 0; pos2 = line; if (*pos2 == '<') pos = strchr(pos2, '>'); else if (*pos2 == '[') { pos = strchr(pos2, ']'); is_new = 1; } else if (*pos2 == '(') pos = strchr(pos2, ')'); else pos = strchr(pos2, ' '); if (!pos) { text_print_colored(text, global.scheme, "text", pos2); continue; } else pos++; memcpy(str, pos2, pos - pos2); str[pos - pos2] = 0; if (!is_new) { text_print_colored(text, global.scheme, "user", str); text_print_colored(text, global.scheme, "text", pos); continue; } text_print_colored(text, global.scheme, "message", str); if (*pos) pos2 = pos + 1; else pos2 = pos; if (*pos2 == '<') pos2 = strchr(pos2, '>'); else if (*pos2 == '[') pos2 = strchr(pos2, ']'); else if (*pos2 == '(') pos2 = strchr(pos2, ')'); else pos2 = strchr(pos2, ' '); if (!pos2) { text_print_colored(text, global.scheme, "text", pos); continue; } else pos2++; memcpy(str, pos, pos2 - pos); str[pos2 - pos] = 0; text_print_colored(text, global.scheme, "user", str); text_print_colored(text, global.scheme, "text", pos2); } } gtk_text_thaw(GTK_TEXT(text)); fclose(file); } static void log_file_close(log_file_t* lf) { GList* dlist; GList* dlist2; GList* dlist3; net_group_t* ng; net_t* net; log_t* log; log_t* found = NULL; GList** flist = NULL; for (dlist2 = other_logs; dlist2; dlist2 = dlist2->next) { log = dlist2->data; if (!strcmp(log->filename, lf->filename)) { found = log; flist = &(other_logs); break; } } for (dlist = global.net_groups; dlist; dlist = dlist->next) { ng = dlist->data; for (dlist2 = ng->nets; dlist2; dlist2 = dlist2->next) { net = dlist2->data; if (found) break; for (dlist3 = net->logs; dlist3; dlist3 = dlist3->next) { log = dlist3->data; if (!strcmp(log->filename, lf->filename)) { found = log; flist = &(net->logs); break; } } } } if (found && flist) { *flist = g_list_remove(*flist, found); if (found->fd) fclose(found->fd); g_free(found); } } static void on_log_delete(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { GtkCList* clist = user_data; log_file_t* lf; GList* dlist; int row; GtkCList* clist2; char* fname; log_net_t* ln = NULL; dlist = clist->selection; clist2 = GTK_CLIST(lookup_widget(log_win, "clist14")); while (dlist) { row = (int) dlist->data; dlist = dlist->next; lf = gtk_clist_get_row_data(clist, row); log_file_close(lf); gtk_clist_remove(clist, row); if (lf == current_lf) { gtk_clist_clear(clist2); current_lf = NULL; } ln = lf->ln; fname = g_strdup(lf->filename); ln->files = g_list_remove(ln->files, lf); log_file_destroy(lf); unlink(fname); g_free(fname); } if (ln && !ln->files) { clist2 = GTK_CLIST(lookup_widget(log_win, "clist39")); row = gtk_clist_find_row_from_data(clist2, ln); rmdir(ln->dirname); if (row >= 0) gtk_clist_remove(clist2, row); } } static void log_delete_all(char* filename) { GtkCList* clist; GtkCList* clist2; GtkCList* clist3; int row, row2; char* fname; log_net_t* ln; log_file_t* lf; GList* dlist; clist = GTK_CLIST(lookup_widget(log_win, "clist39")); clist2 = GTK_CLIST(lookup_widget(log_win, "clist40")); clist3 = GTK_CLIST(lookup_widget(log_win, "clist14")); for (row = 0; row < clist->rows; row++) { ln = gtk_clist_get_row_data(clist, row); if (!ln) continue; if (!ln->files) log_load_network(ln); for (dlist = ln->files; dlist; dlist = dlist->next) { lf = dlist->data; if (strcmp(get_file_name(lf->filename), filename)) continue; log_file_close(lf); row2 = gtk_clist_find_row_from_data(clist2, lf); if (row2 >= 0) { gtk_clist_remove(clist2, row2); if (lf == current_lf) { gtk_clist_clear(clist3); current_lf = NULL; } } fname = g_strdup(lf->filename); ln->files = g_list_remove(ln->files, lf); log_file_destroy(lf); unlink(fname); g_free(fname); break; } } // now remove all empty folders row = 0; while (row < clist->rows) { ln = gtk_clist_get_row_data(clist, row); if (!ln || ln->files) { row++; continue; } rmdir(ln->dirname); gtk_clist_remove(clist, row); } } static void on_log2_delete(GtkMenuItem * menuitem ATTR_UNUSED, gpointer user_data) { int mode = GPOINTER_TO_INT(user_data); if (mode == 1) log_delete_all("protocol.log"); else if (mode == 2) log_delete_all("Messages.log"); else if (mode == 3) log_delete_all("Global.log"); else if (mode == 4) log_delete_all("Operator.log"); } GtkWidget* create_log_popup(GtkWidget* widget) { GtkWidget* popup; GtkWidget* item; GtkCList* clist = GTK_CLIST(widget); int item_num; char item_str[1024]; popup = gtk_menu_new(); item_num = g_list_length(clist->selection); if (item_num > 1) sprintf(item_str, "Delete Selected (%d)", item_num); else sprintf(item_str, "Delete Logfile"); item = gtk_menu_item_new_with_label(item_str); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_log_delete), clist); return popup; } GtkWidget* create_log2_popup(GtkWidget* widget ATTR_UNUSED) { GtkWidget* popup; GtkWidget* item; char item_str[1024]; popup = gtk_menu_new(); sprintf(item_str, "Delete all protocol.log"); item = gtk_menu_item_new_with_label(item_str); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_log2_delete), (gpointer)1); sprintf(item_str, "Delete all Messages.log"); item = gtk_menu_item_new_with_label(item_str); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_log2_delete), (gpointer)2); sprintf(item_str, "Delete all Global.log"); item = gtk_menu_item_new_with_label(item_str); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_log2_delete), (gpointer)3); sprintf(item_str, "Delete all Operator.log"); item = gtk_menu_item_new_with_label(item_str); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(popup), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(on_log2_delete), (gpointer)4); return popup; }