/* PureAdmin
* Copyright (C) 2003 Isak Savo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Callback-functions for the main window GUI.
*
* Copyright (C) 2003- Isak Savo
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "mainwin.h"
#include "globals.h"
#include "srv_comm.h"
#include "gui_helper.h"
#include "helper.h"
#include "cfg.h"
#include "logfile.h"
/** Private functions **/
/* Called for all remaining elements in the hash table. It adds those entries to
* a GList */
static gboolean rmd_users_func (gchar *key, gchar *val, GList **lst_p)
{
*lst_p = g_list_append (*lst_p, g_strdup(key));
return TRUE;
}
static GList *find_completed_activities (GList *old, GList *new)
{
GList *node;
GList *rv = NULL;
if (old == NULL)
return NULL;
if (new == NULL)
return old;
node = old;
while (node)
{
guint id_oldlist = ((PActivity*) node->data)->id;
GList *new2 = new;
/* Search the entire new list for any occurrance of the current activity in the old list */
while (new2)
{
guint id_newlist = ((PActivity*) new2->data)->id;
if (id_oldlist > id_newlist) {
/* No need to search the list anymore, we know it doesn't exist in the new list since
* both lists are sorted by ID*/
rv = g_list_append (rv, node->data);
break;
}
if (id_oldlist == id_newlist)
/* The same activity exists in both lists, nothing more to do this iteration */
break;
new2 = g_list_next (new2);
}
node = g_list_next (node);
}
return rv;
}
struct usrlist_diff {
GList *new_users;
GList *rmd_users;
};
static struct usrlist_diff *compare_user_lists (GList *L1, GList *L2)
{
GHashTable *h;
GList *node;
POnlineUser *u;
struct usrlist_diff *rv = g_new0 (struct usrlist_diff, 1);
h = g_hash_table_new_full ((GHashFunc) g_str_hash,
(GEqualFunc) g_str_equal,
(GDestroyNotify) g_free,
NULL);
node = L1;
while (node) {
/* Add all elements from list 1 to a hash table with username@host as key and random junk as value
* (we're only interested in O(1) lookup of users, not any values) */
gchar *tmp;
u = (POnlineUser *) node->data;
tmp = g_strconcat (u->username, "@", u->remote_addr, NULL);
g_hash_table_insert (h, tmp, "random junk");
node = node->next;
}
node = L2;
while (node)
{
gchar *tmp;
u = (POnlineUser *) node->data;
tmp = g_strconcat (u->username, "@", u->remote_addr, NULL);
if (g_hash_table_lookup (h, tmp)) {
/* User:Address pair existed earlier */
g_hash_table_remove (h, tmp);
} else {
/* Whooo, a new user! */
rv->new_users = g_list_append (rv->new_users, tmp);
g_hash_table_remove (h, tmp);
}
node = node->next;
}
if (g_hash_table_size (h) > 0) {
/* One or more users have disconnected */
g_hash_table_foreach_remove (h,(GHRFunc) rmd_users_func, (gpointer) &(rv->rmd_users));
}
return rv;
}
void user_list_select_user (const gchar *user, const gchar *remote_addr)
{
GtkTreeModel *model = NULL;
GtkTreeIter iter;
gboolean more;
model = gtk_tree_view_get_model (GTK_TREE_VIEW(MW("tree_users")));
g_return_if_fail (user != NULL && *user != '\0');
g_return_if_fail (model != NULL);
more = gtk_tree_model_get_iter_first (model, &iter);
while (more)
{
gchar *cur_user, *cur_addr;
gtk_tree_model_get(model, &iter,
COL_USR_USER, &cur_user,
COL_USR_HOST, &cur_addr,
-1);
if (g_str_equal (cur_user, user) && g_str_equal (cur_addr, remote_addr))
{
gtk_tree_selection_select_iter (gtk_tree_view_get_selection
(GTK_TREE_VIEW(MW("tree_users"))),
&iter);
break;
}
more = gtk_tree_model_iter_next (model, &iter);
}
}
void init_gui_mainwindow (void)
{
GtkWidget *pane;
GdkPixbuf *pbuf;
GList *list = NULL;
gint i;
#define NUM_WINDOW_ICONS 4
const gchar *icons[NUM_WINDOW_ICONS] = { "pureadmin-16x16.png", "pureadmin-24x24.png",
"pureadmin-32x32.png", "pureadmin-48x48.png" };
if (cfg.save_window_geometry == FALSE)
{
pur_log_nfo ("Will not restore window geometry, option turned off");
return;
}
/* Set position */
gtk_window_move (GTK_WINDOW (MW("main_window")), cfg.win_pos[0], cfg.win_pos[1]);
/* Set size */
gtk_window_resize (GTK_WINDOW (MW("main_window")), cfg.win_size[0], cfg.win_size[1]);
/* Divider position */
pane = MW("hpaned1");
if (!pane) {
pur_log_wrn ("Unable to get hpaned widget");
}
else
gtk_paned_set_position (GTK_PANED (pane), cfg.div_pos);
for (i = 0; i < NUM_WINDOW_ICONS; i++)
{
GError *err;
gchar *fname;
err = NULL;
fname = g_build_filename (pixmapdir, icons[i], NULL);
pbuf = gdk_pixbuf_new_from_file (fname, &err);
if (!pbuf) {
pur_log_wrn ("Couldn't load icon file %s: %s", icons[i], err->message);
g_error_free (err);
}
list = g_list_append (list,(gpointer) pbuf);
g_free (fname);
}
if (list)
{
GList *node = list;
gtk_window_set_default_icon_list (list);
while (node) {
g_object_unref (G_OBJECT(node->data));
node = g_list_next (node);
}
g_list_free (list);
}
else
pur_log_wrn ("No icons existed in %s", pixmapdir);
show_status_icon ("PureAdmin started successfully");
#undef NUM_WINDOW_ICONS
}
void init_gui_logwindow (void)
{
GtkWidget *tmp, *lbl;
GtkTextBuffer *text;
pur_log_dbg ("Initializing gui_logwindow");
tmp = MW("logview");
text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tmp));
logview_tags[LOG_INFO] = gtk_text_buffer_create_tag (text, "info",
"foreground", "blue", NULL);
logview_tags[LOG_WARNING] = gtk_text_buffer_create_tag (text, "warning",
"foreground", "#2e9344", NULL);
logview_tags[LOG_ERROR] = gtk_text_buffer_create_tag (text, "error",
"foreground", "red", NULL);
logview_tags[LOG_NOTICE] = gtk_text_buffer_create_tag (text, "notice",
"foreground", "#a427a9", NULL);
logview_tags[LOG_DEBUG] = gtk_text_buffer_create_tag (text, "debug",
"foreground", "#899f22", NULL);
lbl = MW("lbl_logfilename");
#ifdef HAVE_LIBFAM
gtk_label_set_text (GTK_LABEL (lbl), cfg.logfile);
#else
/* Disable logview controls and print a message explaining why! */
log_display_error_text (0);
#endif
}
void init_gui_activities (void)
{
GtkListStore *model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeView *tree_activity;
//GtkTreeSelection *selection;
pur_log_dbg ("Initializing gui_activities");
/* Set-up treeview */
tree_activity = GTK_TREE_VIEW (MW("tree_activity"));
g_return_if_fail (tree_activity != NULL);
model = gtk_list_store_new (N_ACT_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT);
gtk_tree_view_set_model (GTK_TREE_VIEW (tree_activity), GTK_TREE_MODEL (model));
g_object_unref (G_OBJECT (model));
gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_activity)),
GTK_SELECTION_SINGLE);
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
"pixbuf", COL_ACT_ICON,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(tree_activity), column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
"text", COL_ACT_TEXT,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(tree_activity), column);
/* /\* Hook-up some signals *\/ */
/* selection = gtk_tree_view_get_selection (tree_activity); */
/* g_signal_connect ((gpointer) selection, "changed", */
/* G_CALLBACK (booh), */
/* NULL); */
}
void init_gui_online_users (void)
{
gint i;
gchar *titles[] = {
" ", _("User"), _("Host"), _("Connections")
};
GtkListStore *model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeView *tree_users;
/* Set-up treeview */
pur_log_dbg ("Initializing gui_online_users");
tree_users = GTK_TREE_VIEW (MW("tree_users"));
model = gtk_list_store_new (N_USR_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
gtk_tree_view_set_model (GTK_TREE_VIEW (tree_users), GTK_TREE_MODEL (model));
g_object_unref (G_OBJECT (model));
gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_users)),
GTK_SELECTION_SINGLE);
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes (titles[0], renderer,
"pixbuf", COL_USR_ICON,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(tree_users), column);
for (i = 1; i < N_USR_COLUMNS; i++)
{
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes (titles[i], renderer,
"text", i,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(tree_users), column);
}
/* Hook-up some signals */
/* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (main_treeview));
g_signal_connect ((gpointer) selection, "changed",
G_CALLBACK (on_list_selection_changed),
NULL);*/
}
gboolean timeout_update_activity (gpointer data)
{
GList *act_list, *usr_list, *node;
PActivity *activity;
POnlineUser *user;
struct usrlist_diff *diff = NULL;
static GdkPixbuf *icon_up = NULL;
static GdkPixbuf *icon_down = NULL;
static GdkPixbuf *icon_user = NULL;
static GtkTreeView *tree_activity = NULL, *tree_users = NULL;
static GtkTreeModel *model = NULL, *model_users = NULL;
static GtkTreeSelection *sel = NULL, *sel_users = NULL;
static GList *prev_usr_list;
static GList *prev_act_list;
GtkTreeIter iter;
GtkTreePath *act_path, *usr_path;
PGuiActivityRow arow;
PGuiUserRow urow;
gchar *percent;
gboolean restore_selection_act = FALSE, restore_selection_usr = FALSE;
if (G_UNLIKELY (!has_initialized(FALSE)))
return TRUE;
if (G_UNLIKELY (prog_exiting))
{
g_print ("Timeout aborting\n");
return FALSE;
}
if (G_UNLIKELY (!icon_up))
{
icon_up = create_pixbuf ("up.png");
icon_down = create_pixbuf ("down.png");
icon_user = create_pixbuf ("user.png");
}
if (G_UNLIKELY (!tree_activity))
{
tree_activity = (GtkTreeView *) MW("tree_activity");
model = gtk_tree_view_get_model (tree_activity);
sel = gtk_tree_view_get_selection (tree_activity);
tree_users = (GtkTreeView *) MW("tree_users");
model_users = gtk_tree_view_get_model (tree_users);
sel_users = gtk_tree_view_get_selection (tree_users);
}
restore_selection_act = gtk_tree_selection_get_selected (sel, &model, &iter);
if (restore_selection_act)
act_path = gtk_tree_model_get_path (model, &iter);
else
act_path = NULL;
restore_selection_usr = gtk_tree_selection_get_selected (sel_users, &model_users, &iter);
if (restore_selection_usr)
usr_path = gtk_tree_model_get_path (model_users, &iter);
else
usr_path = NULL;
gui_clear_activity_list ();
gui_clear_users_list ();
act_list = srv_get_activities ();
node = g_list_first (act_list);
while (node)
{
activity = (PActivity *) node->data;
if (activity->state == FTPWHO_STATE_DOWNLOAD)
arow.icon = icon_down;
else if (activity->state == FTPWHO_STATE_UPLOAD)
arow.icon = icon_up;
else
{
/* Only append actual actions (DL/UL) */
node = g_list_next (node);
continue;
}
arow.id = activity->id;
if (activity->download_total_size > 0)
percent = g_strdup_printf ("%.2f", 100 * (double) activity->download_current_size / (double) activity->download_total_size);
else
percent = g_strdup ("??");
arow.text = g_strdup_printf ("%s (%s):'%s' - (%s%%)", activity->username, activity->remote_addr,
activity->filename, percent);
gui_add_to_activities (arow);
node = g_list_next (node);
g_free (arow.text);
g_free (percent);
}
if (restore_selection_act)
{
gtk_tree_selection_select_path (sel, act_path);
g_signal_emit_by_name ((gpointer) tree_activity, "cursor_changed", tree_activity, NULL);
}
/* Update users list */
usr_list = srv_get_connected_users (g_list_first (act_list));
node = usr_list;
urow.icon = icon_user;
while (node)
{
user = (POnlineUser *) node->data;
urow.user = string_from_vuser_locale (user->username);
urow.host = g_strdup (user->remote_addr);
urow.num_connections = user->num_connections;
gui_add_to_online_users (urow);
g_free (urow.user);
g_free (urow.host);
node = g_list_next (node);
}
diff = compare_user_lists (prev_usr_list, usr_list);
if (diff->new_users || diff->rmd_users) {
GString *msg = g_string_new ("");
gint len = g_list_length (diff->new_users);
if (len == 1) {
gchar *tmp = pur_elipzise ((gchar *) diff->new_users->data, 20);
g_string_append_printf (msg, "New connection from %s\n", tmp);
}
else if (len > 1)
g_string_append_printf (msg, "%d new connections\n", len);
len = g_list_length (diff->rmd_users);
if (len == 1) {
gchar *tmp = pur_elipzise ((gchar *) diff->rmd_users->data, 20);
g_string_append_printf (msg, "%s has disconnected\n", tmp);
}
else if (len > 1)
g_string_append_printf (msg, "%d users has disconnected\n", len);
if (msg->str[msg->len-1] == '\n')
g_string_truncate (msg, msg->len - 1);
show_status_icon (msg->str);
g_string_free (msg, TRUE);
g_list_foreach (diff->new_users, (GFunc) g_free, NULL);
g_list_foreach (diff->rmd_users, (GFunc) g_free, NULL);
g_list_free (diff->new_users);
g_list_free (diff->rmd_users);
g_free (diff);
}
free_pouser_list (prev_usr_list);
prev_usr_list = usr_list;
if (cfg.always_notify_activities)
{
GList *done_activities = find_completed_activities(prev_act_list, act_list);
node = done_activities;
while (node)
{
PActivity *act = (PActivity *) node->data;
gchar *body = g_strdup_printf (_("User '%s' has finished %s the file '%s'"), act->username,
act->state == FTPWHO_STATE_DOWNLOAD ? _("downloading") : _("uploading"),
act->filename);
send_notification (_("FTP Activity Completed"), body);
node = g_list_next (node);
}
}
free_activity_list (prev_act_list);
prev_act_list = act_list;
if (restore_selection_usr)
gtk_tree_selection_select_path (sel_users, usr_path);
// gui_update_menu_sensivity ();
/* Always return TRUE */
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1