/* X-Chat
* Copyright (C) 1998-2006 Peter Zelezny.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#define WANTSOCKET
#define WANTARPA
#include "../common/inet.h"
#include "fe-gtk.h"
#include "../common/hexchat.h"
#include "../common/hexchatc.h"
#include "../common/fe.h"
#include "../common/util.h"
#include "../common/network.h"
#include "gtkutil.h"
#include "palette.h"
#include "maingui.h"
enum /* DCC SEND/RECV */
{
COL_TYPE,
COL_STATUS,
COL_FILE,
COL_SIZE,
COL_POS,
COL_PERC,
COL_SPEED,
COL_ETA,
COL_NICK,
COL_DCC, /* struct DCC * */
COL_COLOR, /* GdkColor */
N_COLUMNS
};
enum /* DCC CHAT */
{
CCOL_STATUS,
CCOL_NICK,
CCOL_RECV,
CCOL_SENT,
CCOL_START,
CCOL_DCC, /* struct DCC * */
CCOL_COLOR, /* GdkColor * */
CN_COLUMNS
};
struct dccwindow
{
GtkWidget *window;
GtkWidget *list;
GtkListStore *store;
GtkTreeSelection *sel;
GtkWidget *abort_button;
GtkWidget *accept_button;
GtkWidget *resume_button;
GtkWidget *open_button;
GtkWidget *clear_button; /* clears aborted and completed requests */
GtkWidget *file_label;
GtkWidget *address_label;
};
struct my_dcc_send
{
struct session *sess;
char *nick;
gint64 maxcps;
int passive;
};
static struct dccwindow dccfwin = {NULL, }; /* file */
static struct dccwindow dcccwin = {NULL, }; /* chat */
static GdkPixbuf *pix_up = NULL; /* down arrow */
static GdkPixbuf *pix_dn = NULL; /* up arrow */
static int win_width = 600;
static int win_height = 256;
static short view_mode; /* 1=download 2=upload 3=both */
#define VIEW_DOWNLOAD 1
#define VIEW_UPLOAD 2
#define VIEW_BOTH 3
static void
proper_unit (guint64 size, char *buf, size_t buf_len)
{
gchar *formatted_str;
GFormatSizeFlags format_flags = G_FORMAT_SIZE_DEFAULT;
#ifndef __APPLE__ /* OS X uses SI */
#ifndef WIN32 /* Windows uses IEC size (with SI format) */
if (prefs.hex_gui_filesize_iec) /* Linux can't decide... */
#endif
format_flags = G_FORMAT_SIZE_IEC_UNITS;
#endif
formatted_str = g_format_size_full (size, format_flags);
g_strlcpy (buf, formatted_str, buf_len);
g_free (formatted_str);
}
static void
dcc_send_filereq_file (struct my_dcc_send *mdc, char *file)
{
if (file)
dcc_send (mdc->sess, mdc->nick, file, mdc->maxcps, mdc->passive);
else
{
g_free (mdc->nick);
g_free (mdc);
}
}
void
fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive)
{
char* tbuf = g_strdup_printf (_("Send file to %s"), nick);
struct my_dcc_send *mdc = g_new (struct my_dcc_send, 1);
mdc->sess = sess;
mdc->nick = g_strdup (nick);
mdc->maxcps = maxcps;
mdc->passive = passive;
gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, prefs.hex_dcc_dir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL);
g_free (tbuf);
}
static void
dcc_prepare_row_chat (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
gboolean update_only)
{
static char pos[16], size[16];
char *date;
date = ctime (&dcc->starttime);
date[strlen (date) - 1] = 0; /* remove the \n */
proper_unit (dcc->pos, pos, sizeof (pos));
proper_unit (dcc->size, size, sizeof (size));
gtk_list_store_set (store, iter,
CCOL_STATUS, _(dccstat[dcc->dccstat].name),
CCOL_NICK, dcc->nick,
CCOL_RECV, pos,
CCOL_SENT, size,
CCOL_START, date,
CCOL_DCC, dcc,
CCOL_COLOR,
dccstat[dcc->dccstat].color == 1 ?
NULL :
colors + dccstat[dcc->dccstat].color,
-1);
}
static void
dcc_prepare_row_send (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
gboolean update_only)
{
static char pos[16], size[16], kbs[14], perc[14], eta[14];
int to_go;
float per;
if (!pix_up)
pix_up = gtk_widget_render_icon (dccfwin.window, "gtk-go-up",
GTK_ICON_SIZE_MENU, NULL);
/* percentage ack'ed */
per = (float) ((dcc->ack * 100.00) / dcc->size);
proper_unit (dcc->size, size, sizeof (size));
proper_unit (dcc->pos, pos, sizeof (pos));
g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
g_snprintf (perc, sizeof (perc), "%.0f%%", per);
if (dcc->cps != 0)
{
to_go = (dcc->size - dcc->ack) / dcc->cps;
g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
to_go / 3600, (to_go / 60) % 60, to_go % 60);
} else
strcpy (eta, "--:--:--");
if (update_only)
gtk_list_store_set (store, iter,
COL_STATUS, _(dccstat[dcc->dccstat].name),
COL_POS, pos,
COL_PERC, perc,
COL_SPEED, kbs,
COL_ETA, eta,
COL_COLOR,
dccstat[dcc->dccstat].color == 1 ?
NULL :
colors + dccstat[dcc->dccstat].color,
-1);
else
gtk_list_store_set (store, iter,
COL_TYPE, pix_up,
COL_STATUS, _(dccstat[dcc->dccstat].name),
COL_FILE, file_part (dcc->file),
COL_SIZE, size,
COL_POS, pos,
COL_PERC, perc,
COL_SPEED, kbs,
COL_ETA, eta,
COL_NICK, dcc->nick,
COL_DCC, dcc,
COL_COLOR,
dccstat[dcc->dccstat].color == 1 ?
NULL :
colors + dccstat[dcc->dccstat].color,
-1);
}
static void
dcc_prepare_row_recv (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
gboolean update_only)
{
static char size[16], pos[16], kbs[16], perc[14], eta[16];
float per;
int to_go;
if (!pix_dn)
pix_dn = gtk_widget_render_icon (dccfwin.window, "gtk-go-down",
GTK_ICON_SIZE_MENU, NULL);
proper_unit (dcc->size, size, sizeof (size));
if (dcc->dccstat == STAT_QUEUED)
proper_unit (dcc->resumable, pos, sizeof (pos));
else
proper_unit (dcc->pos, pos, sizeof (pos));
g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
/* percentage recv'ed */
per = (float) ((dcc->pos * 100.00) / dcc->size);
g_snprintf (perc, sizeof (perc), "%.0f%%", per);
if (dcc->cps != 0)
{
to_go = (dcc->size - dcc->pos) / dcc->cps;
g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
to_go / 3600, (to_go / 60) % 60, to_go % 60);
} else
strcpy (eta, "--:--:--");
if (update_only)
gtk_list_store_set (store, iter,
COL_STATUS, _(dccstat[dcc->dccstat].name),
COL_POS, pos,
COL_PERC, perc,
COL_SPEED, kbs,
COL_ETA, eta,
COL_COLOR,
dccstat[dcc->dccstat].color == 1 ?
NULL :
colors + dccstat[dcc->dccstat].color,
-1);
else
gtk_list_store_set (store, iter,
COL_TYPE, pix_dn,
COL_STATUS, _(dccstat[dcc->dccstat].name),
COL_FILE, file_part (dcc->file),
COL_SIZE, size,
COL_POS, pos,
COL_PERC, perc,
COL_SPEED, kbs,
COL_ETA, eta,
COL_NICK, dcc->nick,
COL_DCC, dcc,
COL_COLOR,
dccstat[dcc->dccstat].color == 1 ?
NULL :
colors + dccstat[dcc->dccstat].color,
-1);
}
static gboolean
dcc_find_row (struct DCC *find_dcc, GtkTreeModel *model, GtkTreeIter *iter, int col)
{
struct DCC *dcc;
if (gtk_tree_model_get_iter_first (model, iter))
{
do
{
gtk_tree_model_get (model, iter, col, &dcc, -1);
if (dcc == find_dcc)
return TRUE;
}
while (gtk_tree_model_iter_next (model, iter));
}
return FALSE;
}
static void
dcc_update_recv (struct DCC *dcc)
{
GtkTreeIter iter;
if (!dccfwin.window)
return;
if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC))
return;
dcc_prepare_row_recv (dcc, dccfwin.store, &iter, TRUE);
}
static void
dcc_update_chat (struct DCC *dcc)
{
GtkTreeIter iter;
if (!dcccwin.window)
return;
if (!dcc_find_row (dcc, GTK_TREE_MODEL (dcccwin.store), &iter, CCOL_DCC))
return;
dcc_prepare_row_chat (dcc, dcccwin.store, &iter, TRUE);
}
static void
dcc_update_send (struct DCC *dcc)
{
GtkTreeIter iter;
if (!dccfwin.window)
return;
if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC))
return;
dcc_prepare_row_send (dcc, dccfwin.store, &iter, TRUE);
}
static void
close_dcc_file_window (GtkWindow *win, gpointer data)
{
dccfwin.window = NULL;
}
static void
dcc_append (struct DCC *dcc, GtkListStore *store, gboolean prepend)
{
GtkTreeIter iter;
if (prepend)
gtk_list_store_prepend (store, &iter);
else
gtk_list_store_append (store, &iter);
if (dcc->type == TYPE_RECV)
dcc_prepare_row_recv (dcc, store, &iter, FALSE);
else
dcc_prepare_row_send (dcc, store, &iter, FALSE);
}
/* Returns aborted and completed transfers. */
static GSList *
dcc_get_completed (void)
{
struct DCC *dcc;
GtkTreeIter iter;
GtkTreeModel *model;
GSList *completed = NULL;
model = GTK_TREE_MODEL (dccfwin.store);
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
{
gtk_tree_model_get (model, &iter, COL_DCC, &dcc, -1);
if (is_dcc_completed (dcc))
completed = g_slist_prepend (completed, dcc);
} while (gtk_tree_model_iter_next (model, &iter));
}
return completed;
}
static gboolean
dcc_completed_transfer_exists (void)
{
gboolean exist;
GSList *comp_list;
comp_list = dcc_get_completed ();
exist = comp_list != NULL;
g_slist_free (comp_list);
return exist;
}
static void
update_clear_button_sensitivity (void)
{
gboolean sensitive = dcc_completed_transfer_exists ();
gtk_widget_set_sensitive (dccfwin.clear_button, sensitive);
}
static void
dcc_fill_window (int flags)
{
struct DCC *dcc;
GSList *list;
GtkTreeIter iter;
int i = 0;
gtk_list_store_clear (GTK_LIST_STORE (dccfwin.store));
if (flags & VIEW_UPLOAD)
{
list = dcc_list;
while (list)
{
dcc = list->data;
if (dcc->type == TYPE_SEND)
{
dcc_append (dcc, dccfwin.store, FALSE);
i++;
}
list = list->next;
}
}
if (flags & VIEW_DOWNLOAD)
{
list = dcc_list;
while (list)
{
dcc = list->data;
if (dcc->type == TYPE_RECV)
{
dcc_append (dcc, dccfwin.store, FALSE);
i++;
}
list = list->next;
}
}
/* if only one entry, select it (so Accept button can work) */
if (i == 1)
{
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dccfwin.store), &iter);
gtk_tree_selection_select_iter (dccfwin.sel, &iter);
}
update_clear_button_sensitivity ();
}
/* return list of selected DCCs */
static GSList *
treeview_get_selected (GtkTreeModel *model, GtkTreeSelection *sel, int column)
{
GtkTreeIter iter;
GSList *list = NULL;
void *ptr;
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
{
if (gtk_tree_selection_iter_is_selected (sel, &iter))
{
gtk_tree_model_get (model, &iter, column, &ptr, -1);
list = g_slist_prepend (list, ptr);
}
}
while (gtk_tree_model_iter_next (model, &iter));
}
return g_slist_reverse (list);
}
static GSList *
dcc_get_selected (void)
{
return