/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #define WANTSOCKET #define WANTARPA #include "../common/inet.h" #include "fe-gtk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/xchat.h" #include "../common/xchatc.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 *file_label; GtkWidget *address_label; }; struct my_dcc_send { struct session *sess; char *nick; int 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 #define KILOBYTE 1024 #define MEGABYTE (KILOBYTE * 1024) #define GIGABYTE (MEGABYTE * 1024) static void proper_unit (DCC_SIZE size, char *buf, int buf_len) { if (size <= KILOBYTE) { snprintf (buf, buf_len, "%"DCC_SFMT"B", size); } else if (size > KILOBYTE && size <= MEGABYTE) { snprintf (buf, buf_len, "%"DCC_SFMT"kB", size / KILOBYTE); } else { snprintf (buf, buf_len, "%.2fMB", (float)size / MEGABYTE); } } 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 { free (mdc->nick); free (mdc); } } void fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive) { char tbuf[128]; struct my_dcc_send *mdc; mdc = malloc (sizeof (*mdc)); mdc->sess = sess; mdc->nick = strdup (nick); mdc->maxcps = maxcps; mdc->passive = passive; snprintf (tbuf, sizeof tbuf, _("Send file to %s"), nick); gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, prefs.dccdir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL); } static void dcc_prepare_row_chat (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter, gboolean update_only) { static char pos[16], siz[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, siz, sizeof (siz)); gtk_list_store_set (store, iter, CCOL_STATUS, _(dccstat[dcc->dccstat].name), CCOL_NICK, dcc->nick, CCOL_RECV, pos, CCOL_SENT, siz, 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)); snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024); /* proper_unit (dcc->ack, ack, sizeof (ack));*/ snprintf (perc, sizeof (perc), "%.0f%%", per); if (dcc->cps != 0) { to_go = (dcc->size - dcc->ack) / dcc->cps; 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)); snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024); /* percentage recv'ed */ per = (float) ((dcc->pos * 100.00) / dcc->size); snprintf (perc, sizeof (perc), "%.0f%%", per); if (dcc->cps != 0) { to_go = (dcc->size - dcc->pos) / dcc->cps; 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); } 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); } } /* 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 treeview_get_selected (GTK_TREE_MODEL (dccfwin.store), dccfwin.sel, COL_DCC); } static void resume_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; char buf[512]; GSList *list; list = dcc_get_selected (); if (!list) return; dcc = list->data; g_slist_free (list); if (dcc->type == TYPE_RECV && !dcc_resume (dcc)) { switch (dcc->resume_error) { case 0: /* unknown error */ fe_message (_("That file is not resumable."), FE_MSG_ERROR); break; case 1: snprintf (buf, sizeof (buf), _( "Cannot access file: %s\n" "%s.\n" "Resuming not possible."), dcc->destfile, errorstring (dcc->resume_errno)); fe_message (buf, FE_MSG_ERROR); break; case 2: fe_message (_("File in download directory is larger " "than file offered. Resuming not possible."), FE_MSG_ERROR); break; case 3: fe_message (_("Cannot resume the same file from two people."), FE_MSG_ERROR); } } } static void abort_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_get_selected (); for (; list; list = list->next) { dcc = list->data; dcc_abort (dcc->serv->front_session, dcc); } g_slist_free (start); } static void accept_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_get_selected (); for (; list; list = list->next) { dcc = list->data; if (dcc->type != TYPE_SEND) dcc_get (dcc); } g_slist_free (start); } static void browse_folder (char *dir) { #ifdef WIN32 /* no need for file:// in ShellExecute() */ fe_open_url (dir); #else char buf[512]; snprintf (buf, sizeof (buf), "file://%s", dir); fe_open_url (buf); #endif } static void browse_dcc_folder (void) { if (prefs.dcc_completed_dir[0]) browse_folder (prefs.dcc_completed_dir); else browse_folder (prefs.dccdir); } static void dcc_details_populate (struct DCC *dcc) { char buf[128]; if (!dcc) { gtk_label_set_text (GTK_LABEL (dccfwin.file_label), NULL); gtk_label_set_text (GTK_LABEL (dccfwin.address_label), NULL); return; } /* full path */ if (dcc->type == TYPE_RECV) gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->destfile); else gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->file); /* address and port */ snprintf (buf, sizeof (buf), "%s : %d", net_ip (dcc->addr), dcc->port); gtk_label_set_text (GTK_LABEL (dccfwin.address_label), buf); } static void dcc_row_cb (GtkTreeSelection *sel, gpointer user_data) { struct DCC *dcc; GSList *list; list = dcc_get_selected (); if (!list) { gtk_widget_set_sensitive (dccfwin.accept_button, FALSE); gtk_widget_set_sensitive (dccfwin.resume_button, FALSE); gtk_widget_set_sensitive (dccfwin.abort_button, FALSE); dcc_details_populate (NULL); return; } gtk_widget_set_sensitive (dccfwin.abort_button, TRUE); if (list->next) /* multi selection */ { gtk_widget_set_sensitive (dccfwin.accept_button, TRUE); gtk_widget_set_sensitive (dccfwin.resume_button, TRUE); dcc_details_populate (list->data); } else { /* turn OFF/ON appropriate buttons */ dcc = list->data; if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_RECV) { gtk_widget_set_sensitive (dccfwin.accept_button, TRUE); gtk_widget_set_sensitive (dccfwin.resume_button, TRUE); } else { gtk_widget_set_sensitive (dccfwin.accept_button, FALSE); gtk_widget_set_sensitive (dccfwin.resume_button, FALSE); } dcc_details_populate (dcc); } g_slist_free (list); } static void dcc_dclick_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { struct DCC *dcc; GSList *list; list = dcc_get_selected (); if (!list) return; dcc = list->data; g_slist_free (list); if (dcc->type == TYPE_RECV) { accept_clicked (0, 0); return; } switch (dcc->dccstat) { case STAT_FAILED: case STAT_ABORTED: case STAT_DONE: dcc_abort (dcc->serv->front_session, dcc); } } static void dcc_add_column (GtkWidget *tree, int textcol, int colorcol, char *title, gboolean right_justified) { GtkCellRenderer *renderer; renderer = gtk_cell_renderer_text_new (); if (right_justified) g_object_set (G_OBJECT (renderer), "xalign", (float) 1.0, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title, renderer, "text", textcol, "foreground-gdk", colorcol, NULL); gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1); } static GtkWidget * dcc_detail_label (char *text, GtkWidget *box, int num) { GtkWidget *label; char buf[64]; label = gtk_label_new (NULL); snprintf (buf, sizeof (buf), "%s", text); gtk_label_set_markup (GTK_LABEL (label), buf); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_table_attach (GTK_TABLE (box), label, 0, 1, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0); label = gtk_label_new (NULL); gtk_label_set_selectable (GTK_LABEL (label), TRUE); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_table_attach (GTK_TABLE (box), label, 1, 2, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0); return label; } static void dcc_exp_cb (GtkWidget *exp, GtkWidget *box) { #if GTK_CHECK_VERSION(2,20,0) if (gtk_widget_get_visible (box)) #else if (GTK_WIDGET_VISIBLE (box)) #endif gtk_widget_hide (box); else gtk_widget_show (box); } static void dcc_toggle (GtkWidget *item, gpointer data) { if (GTK_TOGGLE_BUTTON (item)->active) { view_mode = GPOINTER_TO_INT (data); dcc_fill_window (GPOINTER_TO_INT (data)); } } static gboolean dcc_configure_cb (GtkWindow *win, GdkEventConfigure *event, gpointer data) { /* remember the window size */ gtk_window_get_size (win, &win_width, &win_height); return FALSE; } int fe_dcc_open_recv_win (int passive) { GtkWidget *radio, *table, *vbox, *bbox, *view, *exp, *detailbox; GtkListStore *store; GSList *group; if (dccfwin.window) { if (!passive) mg_bring_tofront (dccfwin.window); return TRUE; } dccfwin.window = mg_create_generic_tab ("Transfers", _(DISPLAY_NAME": Uploads and Downloads"), FALSE, TRUE, close_dcc_file_window, NULL, win_width, win_height, &vbox, 0); gtk_container_set_border_width (GTK_CONTAINER (dccfwin.window), 3); gtk_box_set_spacing (GTK_BOX (vbox), 3); store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR); view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); /* Up/Down Icon column */ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, NULL, gtk_cell_renderer_pixbuf_new (), "pixbuf", COL_TYPE, NULL); dcc_add_column (view, COL_STATUS, COL_COLOR, _("Status"), FALSE); dcc_add_column (view, COL_FILE, COL_COLOR, _("File"), FALSE); dcc_add_column (view, COL_SIZE, COL_COLOR, _("Size"), TRUE); dcc_add_column (view, COL_POS, COL_COLOR, _("Position"), TRUE); dcc_add_column (view, COL_PERC, COL_COLOR, "%", TRUE); dcc_add_column (view, COL_SPEED, COL_COLOR, "KB/s", TRUE); dcc_add_column (view, COL_ETA, COL_COLOR, _("ETA"), FALSE); dcc_add_column (view, COL_NICK, COL_COLOR, _("Nick"), FALSE); gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_FILE), TRUE); gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_NICK), TRUE); dccfwin.list = view; dccfwin.store = store; dccfwin.sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (vi
/* HexChat
 * Copyright (C) 1998-2010 Peter Zelezny.
 * Copyright (C) 2009-2013 Berke Viktor.
 *
 * 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
 */

/* per-channel/dialog settings :: /CHANOPT */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif

#include "hexchat.h"

#include "cfgfiles.h"
#include "server.h"
#include "text.h"
#include "util.h"
#include "hexchatc.h"


static GSList *chanopt_list = NULL;
static gboolean chanopt_open = FALSE;
static gbool