mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-12 08:40:19 +00:00
add xchat r1489
This commit is contained in:
14
src/Makefile.am
Normal file
14
src/Makefile.am
Normal file
@@ -0,0 +1,14 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
EXTRA_DIST = fe-text/fe-text.c \
|
||||
fe-text/README fe-text/fe-text.h version-script
|
||||
|
||||
if DO_TEXT
|
||||
text_fe = fe-text
|
||||
endif
|
||||
|
||||
if DO_GTK
|
||||
gtk_fe = fe-gtk
|
||||
endif
|
||||
|
||||
SUBDIRS = pixmaps common $(gtk_fe) $(text_fe)
|
||||
61
src/common/Makefile.am
Normal file
61
src/common/Makefile.am
Normal file
@@ -0,0 +1,61 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libxchatcommon.a
|
||||
|
||||
INCLUDES = $(COMMON_CFLAGS)
|
||||
|
||||
EXTRA_DIST = \
|
||||
cfgfiles.h \
|
||||
chanopt.h \
|
||||
ctcp.h \
|
||||
dcc.h \
|
||||
fe.h \
|
||||
history.h \
|
||||
identd.c \
|
||||
ignore.h \
|
||||
inbound.h \
|
||||
inet.h \
|
||||
make-te.c \
|
||||
modes.h \
|
||||
msproxy.h \
|
||||
network.h \
|
||||
notify.h \
|
||||
outbound.h \
|
||||
plugin.h \
|
||||
plugin-timer.h \
|
||||
proto-irc.h \
|
||||
server.h \
|
||||
servlist.h \
|
||||
ssl.h \
|
||||
ssl.c \
|
||||
text.h \
|
||||
textenums.h \
|
||||
textevents.h \
|
||||
textevents.in \
|
||||
tree.h \
|
||||
url.h \
|
||||
userlist.h \
|
||||
util.h \
|
||||
xchat.h \
|
||||
xchatc.h \
|
||||
xchat-plugin.h
|
||||
|
||||
if USE_OPENSSL
|
||||
ssl_c = ssl.c
|
||||
endif
|
||||
|
||||
if USE_DBUS
|
||||
dbusdir = dbus
|
||||
libxchatcommon_a_LIBADD = \
|
||||
$(top_builddir)/src/common/dbus/dbus-*.$(OBJEXT)
|
||||
endif
|
||||
SUBDIRS = $(dbusdir) .
|
||||
|
||||
libxchatcommon_a_SOURCES = cfgfiles.c chanopt.c ctcp.c dcc.c history.c ignore.c \
|
||||
inbound.c modes.c msproxy.c network.c notify.c outbound.c \
|
||||
plugin.c plugin-timer.c proto-irc.c server.c servlist.c $(ssl_c) \
|
||||
text.c tree.c url.c userlist.c util.c xchat.c
|
||||
libxchatcommon_a_CFLAGS = $(LIBPROXY_CFLAGS)
|
||||
|
||||
textevents: make-te
|
||||
./make-te < textevents.in > textevents.h 2> textenums.h
|
||||
1105
src/common/cfgfiles.c
Normal file
1105
src/common/cfgfiles.c
Normal file
File diff suppressed because it is too large
Load Diff
55
src/common/cfgfiles.h
Normal file
55
src/common/cfgfiles.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* cfgfiles.h */
|
||||
|
||||
#ifndef XCHAT_CFGFILES_H
|
||||
#define XCHAT_CFGFILES_H
|
||||
|
||||
#include "xchat.h"
|
||||
|
||||
extern char *xdir_fs;
|
||||
extern char *xdir_utf;
|
||||
|
||||
char *cfg_get_str (char *cfg, char *var, char *dest, int dest_len);
|
||||
int cfg_get_bool (char *var);
|
||||
int cfg_get_int_with_result (char *cfg, char *var, int *result);
|
||||
int cfg_get_int (char *cfg, char *var);
|
||||
int cfg_put_int (int fh, int value, char *var);
|
||||
int cfg_get_color (char *cfg, char *var, int *r, int *g, int *b);
|
||||
int cfg_put_color (int fh, int r, int g, int b, char *var);
|
||||
char *get_xdir_fs (void);
|
||||
char *get_xdir_utf8 (void);
|
||||
void load_config (void);
|
||||
int save_config (void);
|
||||
void list_free (GSList ** list);
|
||||
void list_loadconf (char *file, GSList ** list, char *defaultconf);
|
||||
int list_delentry (GSList ** list, char *name);
|
||||
void list_addentry (GSList ** list, char *cmd, char *name);
|
||||
int cmd_set (session *sess, char *tbuf, char *word[], char *word_eol[]);
|
||||
int xchat_open_file (char *file, int flags, int mode, int xof_flags);
|
||||
FILE *xchat_fopen_file (const char *file, const char *mode, int xof_flags);
|
||||
#define XOF_DOMODE 1
|
||||
#define XOF_FULLPATH 2
|
||||
|
||||
#define STRUCT_OFFSET_STR(type,field) \
|
||||
( (unsigned int) (((char *) (&(((type *) NULL)->field)))- ((char *) NULL)) )
|
||||
|
||||
#define STRUCT_OFFSET_INT(type,field) \
|
||||
( (unsigned int) (((int *) (&(((type *) NULL)->field)))- ((int *) NULL)) )
|
||||
|
||||
#define P_OFFSET(field) STRUCT_OFFSET_STR(struct xchatprefs, field),sizeof(prefs.field)
|
||||
#define P_OFFSETNL(field) STRUCT_OFFSET_STR(struct xchatprefs, field)
|
||||
#define P_OFFINT(field) STRUCT_OFFSET_INT(struct xchatprefs, field),0
|
||||
#define P_OFFINTNL(field) STRUCT_OFFSET_INT(struct xchatprefs, field)
|
||||
|
||||
struct prefs
|
||||
{
|
||||
char *name;
|
||||
unsigned short offset;
|
||||
unsigned short len;
|
||||
unsigned short type;
|
||||
};
|
||||
|
||||
#define TYPE_STR 0
|
||||
#define TYPE_INT 1
|
||||
#define TYPE_BOOL 2
|
||||
|
||||
#endif
|
||||
431
src/common/chanopt.c
Normal file
431
src/common/chanopt.c
Normal file
@@ -0,0 +1,431 @@
|
||||
/* per-channel/dialog settings :: /CHANOPT */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "xchat.h"
|
||||
|
||||
#include "cfgfiles.h"
|
||||
#include "server.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
#include "xchatc.h"
|
||||
|
||||
|
||||
static GSList *chanopt_list = NULL;
|
||||
static gboolean chanopt_open = FALSE;
|
||||
static gboolean chanopt_changed = FALSE;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
char *alias; /* old names from 2.8.4 */
|
||||
int offset;
|
||||
} channel_options;
|
||||
|
||||
#define S_F(xx) STRUCT_OFFSET_STR(struct session,xx)
|
||||
|
||||
static const channel_options chanopt[] =
|
||||
{
|
||||
{"alert_beep", "BEEP", S_F(alert_beep)},
|
||||
{"alert_taskbar", NULL, S_F(alert_taskbar)},
|
||||
{"alert_tray", "TRAY", S_F(alert_tray)},
|
||||
|
||||
{"text_hidejoinpart", "CONFMODE", S_F(text_hidejoinpart)},
|
||||
{"text_logging", NULL, S_F(text_logging)},
|
||||
{"text_scrollback", NULL, S_F(text_scrollback)},
|
||||
};
|
||||
|
||||
#undef S_F
|
||||
|
||||
static char *
|
||||
chanopt_value (guint8 val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case SET_OFF:
|
||||
return "OFF";
|
||||
case SET_ON:
|
||||
return "ON";
|
||||
default:
|
||||
return "{unset}";
|
||||
}
|
||||
}
|
||||
|
||||
/* handle the /CHANOPT command */
|
||||
|
||||
int
|
||||
chanopt_command (session *sess, char *tbuf, char *word[], char *word_eol[])
|
||||
{
|
||||
int dots, i = 0, j, p = 0;
|
||||
guint8 val;
|
||||
int offset = 2;
|
||||
char *find;
|
||||
gboolean quiet = FALSE;
|
||||
int newval = -1;
|
||||
|
||||
if (!strcmp (word[2], "-quiet"))
|
||||
{
|
||||
quiet = TRUE;
|
||||
offset++;
|
||||
}
|
||||
|
||||
find = word[offset++];
|
||||
|
||||
if (word[offset][0])
|
||||
{
|
||||
if (!strcasecmp (word[offset], "ON"))
|
||||
newval = 1;
|
||||
else if (!strcasecmp (word[offset], "OFF"))
|
||||
newval = 0;
|
||||
else if (word[offset][0] == 'u')
|
||||
newval = SET_DEFAULT;
|
||||
else
|
||||
newval = atoi (word[offset]);
|
||||
}
|
||||
|
||||
if (!quiet)
|
||||
PrintTextf (sess, "\002Network\002: %s \002Channel\002: %s\n",
|
||||
sess->server->network ? server_get_network (sess->server, TRUE) : _("<none>"),
|
||||
sess->channel[0] ? sess->channel : _("<none>"));
|
||||
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
if (find[0] == 0 || match (find, chanopt[i].name) || (chanopt[i].alias && match (find, chanopt[i].alias)))
|
||||
{
|
||||
if (newval != -1) /* set new value */
|
||||
{
|
||||
*(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = newval;
|
||||
}
|
||||
|
||||
if (!quiet) /* print value */
|
||||
{
|
||||
strcpy (tbuf, chanopt[i].name);
|
||||
p = strlen (tbuf);
|
||||
|
||||
tbuf[p++] = 3;
|
||||
tbuf[p++] = '2';
|
||||
|
||||
dots = 20 - strlen (chanopt[i].name);
|
||||
|
||||
for (j = 0; j < dots; j++)
|
||||
tbuf[p++] = '.';
|
||||
tbuf[p++] = 0;
|
||||
|
||||
val = G_STRUCT_MEMBER (guint8, sess, chanopt[i].offset);
|
||||
PrintTextf (sess, "%s\0033:\017 %s", tbuf, chanopt_value (val));
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* is a per-channel setting set? Or is it UNSET and
|
||||
* the global version is set? */
|
||||
|
||||
gboolean
|
||||
chanopt_is_set (unsigned int global, guint8 per_chan_setting)
|
||||
{
|
||||
if (per_chan_setting == SET_DEFAULT)
|
||||
return global;
|
||||
|
||||
return per_chan_setting;
|
||||
}
|
||||
|
||||
/* additive version */
|
||||
|
||||
gboolean
|
||||
chanopt_is_set_a (unsigned int global, guint8 per_chan_setting)
|
||||
{
|
||||
if (per_chan_setting == SET_DEFAULT)
|
||||
return global;
|
||||
|
||||
return per_chan_setting || global;
|
||||
}
|
||||
|
||||
/* === below is LOADING/SAVING stuff only === */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Per-Channel Alerts */
|
||||
/* use a byte, because we need a pointer to each element */
|
||||
guint8 alert_beep;
|
||||
guint8 alert_taskbar;
|
||||
guint8 alert_tray;
|
||||
|
||||
/* Per-Channel Settings */
|
||||
guint8 text_hidejoinpart;
|
||||
guint8 text_logging;
|
||||
guint8 text_scrollback;
|
||||
|
||||
char *network;
|
||||
char *channel;
|
||||
|
||||
} chanopt_in_memory;
|
||||
|
||||
|
||||
static chanopt_in_memory *
|
||||
chanopt_find (char *network, char *channel, gboolean add_new)
|
||||
{
|
||||
GSList *list;
|
||||
chanopt_in_memory *co;
|
||||
int i;
|
||||
|
||||
for (list = chanopt_list; list; list = list->next)
|
||||
{
|
||||
co = list->data;
|
||||
if (!strcasecmp (co->channel, channel) &&
|
||||
!strcasecmp (co->network, network))
|
||||
return co;
|
||||
}
|
||||
|
||||
if (!add_new)
|
||||
return NULL;
|
||||
|
||||
/* allocate a new one */
|
||||
co = g_malloc0 (sizeof (chanopt_in_memory));
|
||||
co->channel = g_strdup (channel);
|
||||
co->network = g_strdup (network);
|
||||
|
||||
/* set all values to SET_DEFAULT */
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
*(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = SET_DEFAULT;
|
||||
i++;
|
||||
}
|
||||
|
||||
chanopt_list = g_slist_prepend (chanopt_list, co);
|
||||
chanopt_changed = TRUE;
|
||||
|
||||
return co;
|
||||
}
|
||||
|
||||
static void
|
||||
chanopt_add_opt (chanopt_in_memory *co, char *var, int new_value)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
if (!strcmp (var, chanopt[i].name))
|
||||
{
|
||||
*(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = new_value;
|
||||
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* load chanopt.conf from disk into our chanopt_list GSList */
|
||||
|
||||
static void
|
||||
chanopt_load_all (void)
|
||||
{
|
||||
int fh;
|
||||
char buf[256];
|
||||
char *eq;
|
||||
char *network = NULL;
|
||||
chanopt_in_memory *current = NULL;
|
||||
|
||||
/* 1. load the old file into our GSList */
|
||||
fh = xchat_open_file ("chanopt.conf", O_RDONLY, 0, 0);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (waitline (fh, buf, sizeof buf, FALSE) != -1)
|
||||
{
|
||||
eq = strchr (buf, '=');
|
||||
if (!eq)
|
||||
continue;
|
||||
eq[0] = 0;
|
||||
|
||||
if (eq != buf && eq[-1] == ' ')
|
||||
eq[-1] = 0;
|
||||
|
||||
if (!strcmp (buf, "network"))
|
||||
{
|
||||
g_free (network);
|
||||
network = g_strdup (eq + 2);
|
||||
}
|
||||
else if (!strcmp (buf, "channel"))
|
||||
{
|
||||
current = chanopt_find (network, eq + 2, TRUE);
|
||||
chanopt_changed = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current)
|
||||
chanopt_add_opt (current, buf, atoi (eq + 2));
|
||||
}
|
||||
|
||||
}
|
||||
close (fh);
|
||||
g_free (network);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chanopt_load (session *sess)
|
||||
{
|
||||
int i;
|
||||
guint8 val;
|
||||
chanopt_in_memory *co;
|
||||
char *network;
|
||||
|
||||
if (sess->channel[0] == 0)
|
||||
return;
|
||||
|
||||
network = server_get_network (sess->server, FALSE);
|
||||
if (!network)
|
||||
return;
|
||||
|
||||
if (!chanopt_open)
|
||||
{
|
||||
chanopt_open = TRUE;
|
||||
chanopt_load_all ();
|
||||
}
|
||||
|
||||
co = chanopt_find (network, sess->channel, FALSE);
|
||||
if (!co)
|
||||
return;
|
||||
|
||||
/* fill in all the sess->xxxxx fields */
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
val = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset);
|
||||
*(guint8 *)G_STRUCT_MEMBER_P(sess, chanopt[i].offset) = val;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chanopt_save (session *sess)
|
||||
{
|
||||
int i;
|
||||
guint8 vals;
|
||||
guint8 valm;
|
||||
chanopt_in_memory *co;
|
||||
char *network;
|
||||
|
||||
if (sess->channel[0] == 0)
|
||||
return;
|
||||
|
||||
network = server_get_network (sess->server, FALSE);
|
||||
if (!network)
|
||||
return;
|
||||
|
||||
/* 2. reconcile sess with what we loaded from disk */
|
||||
|
||||
co = chanopt_find (network, sess->channel, TRUE);
|
||||
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
vals = G_STRUCT_MEMBER(guint8, sess, chanopt[i].offset);
|
||||
valm = G_STRUCT_MEMBER(guint8, co, chanopt[i].offset);
|
||||
|
||||
if (vals != valm)
|
||||
{
|
||||
*(guint8 *)G_STRUCT_MEMBER_P(co, chanopt[i].offset) = vals;
|
||||
chanopt_changed = TRUE;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanopt_save_one_channel (chanopt_in_memory *co, int fh)
|
||||
{
|
||||
int i;
|
||||
char buf[256];
|
||||
guint8 val;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s = %s\n", "network", co->network);
|
||||
write (fh, buf, strlen (buf));
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s = %s\n", "channel", co->channel);
|
||||
write (fh, buf, strlen (buf));
|
||||
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset);
|
||||
if (val != SET_DEFAULT)
|
||||
{
|
||||
snprintf (buf, sizeof (buf), "%s = %d\n", chanopt[i].name, val);
|
||||
write (fh, buf, strlen (buf));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chanopt_save_all (void)
|
||||
{
|
||||
int i;
|
||||
int num_saved;
|
||||
int fh;
|
||||
GSList *list;
|
||||
chanopt_in_memory *co;
|
||||
guint8 val;
|
||||
|
||||
if (!chanopt_list || !chanopt_changed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fh = xchat_open_file ("chanopt.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (num_saved = 0, list = chanopt_list; list; list = list->next)
|
||||
{
|
||||
co = list->data;
|
||||
|
||||
i = 0;
|
||||
while (i < sizeof (chanopt) / sizeof (channel_options))
|
||||
{
|
||||
val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset);
|
||||
/* not using global/default setting, must save */
|
||||
if (val != SET_DEFAULT)
|
||||
{
|
||||
if (num_saved != 0)
|
||||
write (fh, "\n", 1);
|
||||
|
||||
chanopt_save_one_channel (co, fh);
|
||||
num_saved++;
|
||||
goto cont;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
cont:
|
||||
g_free (co->network);
|
||||
g_free (co->channel);
|
||||
g_free (co);
|
||||
}
|
||||
|
||||
close (fh);
|
||||
|
||||
/* we're quiting, no need to free */
|
||||
|
||||
/*g_slist_free (chanopt_list);
|
||||
chanopt_list = NULL;
|
||||
|
||||
chanopt_open = FALSE;
|
||||
chanopt_changed = FALSE;*/
|
||||
}
|
||||
6
src/common/chanopt.h
Normal file
6
src/common/chanopt.h
Normal file
@@ -0,0 +1,6 @@
|
||||
int chanopt_command (session *sess, char *tbuf, char *word[], char *word_eol[]);
|
||||
gboolean chanopt_is_set (unsigned int global, guint8 per_chan_setting);
|
||||
gboolean chanopt_is_set_a (unsigned int global, guint8 per_chan_setting);
|
||||
void chanopt_save_all (void);
|
||||
void chanopt_save (session *sess);
|
||||
void chanopt_load (session *sess);
|
||||
191
src/common/ctcp.c
Normal file
191
src/common/ctcp.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "xchat.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "util.h"
|
||||
#include "modes.h"
|
||||
#include "outbound.h"
|
||||
#include "ignore.h"
|
||||
#include "inbound.h"
|
||||
#include "dcc.h"
|
||||
#include "text.h"
|
||||
#include "ctcp.h"
|
||||
#include "server.h"
|
||||
#include "xchatc.h"
|
||||
|
||||
|
||||
static void
|
||||
ctcp_reply (session *sess, char *nick, char *word[], char *word_eol[],
|
||||
char *conf)
|
||||
{
|
||||
char tbuf[4096]; /* can receive 2048 from IRC, so this is enough */
|
||||
|
||||
conf = strdup (conf);
|
||||
/* process %C %B etc */
|
||||
check_special_chars (conf, TRUE);
|
||||
auto_insert (tbuf, sizeof (tbuf), conf, word, word_eol, "", "", word_eol[5],
|
||||
server_get_network (sess->server, TRUE), "", "", nick);
|
||||
free (conf);
|
||||
handle_command (sess, tbuf, FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
ctcp_check (session *sess, char *nick, char *word[], char *word_eol[],
|
||||
char *ctcp)
|
||||
{
|
||||
int ret = 0;
|
||||
char *po;
|
||||
struct popup *pop;
|
||||
GSList *list = ctcp_list;
|
||||
|
||||
po = strchr (ctcp, '\001');
|
||||
if (po)
|
||||
*po = 0;
|
||||
|
||||
po = strchr (word_eol[5], '\001');
|
||||
if (po)
|
||||
*po = 0;
|
||||
|
||||
while (list)
|
||||
{
|
||||
pop = (struct popup *) list->data;
|
||||
if (!strcasecmp (ctcp, pop->name))
|
||||
{
|
||||
ctcp_reply (sess, nick, word, word_eol, pop->cmd);
|
||||
ret = 1;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ctcp_handle (session *sess, char *to, char *nick, char *ip,
|
||||
char *msg, char *word[], char *word_eol[], int id)
|
||||
{
|
||||
char *po;
|
||||
session *chansess;
|
||||
server *serv = sess->server;
|
||||
char outbuf[1024];
|
||||
int ctcp_offset = 2;
|
||||
|
||||
if (serv->have_idmsg && (word[4][1] == '+' || word[4][1] == '-') )
|
||||
ctcp_offset = 3;
|
||||
|
||||
/* consider DCC to be different from other CTCPs */
|
||||
if (!strncasecmp (msg, "DCC", 3))
|
||||
{
|
||||
/* but still let CTCP replies override it */
|
||||
if (!ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
|
||||
{
|
||||
if (!ignore_check (word[1], IG_DCC))
|
||||
handle_dcc (sess, nick, word, word_eol);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* consider ACTION to be different from other CTCPs. Check
|
||||
ignore as if it was a PRIV/CHAN. */
|
||||
if (!strncasecmp (msg, "ACTION ", 7))
|
||||
{
|
||||
if (is_channel (serv, to))
|
||||
{
|
||||
/* treat a channel action as a CHAN */
|
||||
if (ignore_check (word[1], IG_CHAN))
|
||||
return;
|
||||
} else
|
||||
{
|
||||
/* treat a private action as a PRIV */
|
||||
if (ignore_check (word[1], IG_PRIV))
|
||||
return;
|
||||
}
|
||||
|
||||
/* but still let CTCP replies override it */
|
||||
if (ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
|
||||
goto generic;
|
||||
|
||||
inbound_action (sess, to, nick, ip, msg + 7, FALSE, id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignore_check (word[1], IG_CTCP))
|
||||
return;
|
||||
|
||||
if (!strcasecmp (msg, "VERSION") && !prefs.hidever)
|
||||
{
|
||||
snprintf (outbuf, sizeof (outbuf), "VERSION xchat "PACKAGE_VERSION" %s",
|
||||
get_cpu_str ());
|
||||
serv->p_nctcp (serv, nick, outbuf);
|
||||
}
|
||||
|
||||
if (!ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
|
||||
{
|
||||
if (!strncasecmp (msg, "SOUND", 5))
|
||||
{
|
||||
po = strchr (word[5], '\001');
|
||||
if (po)
|
||||
po[0] = 0;
|
||||
|
||||
if (is_channel (sess->server, to))
|
||||
{
|
||||
chansess = find_channel (sess->server, to);
|
||||
if (!chansess)
|
||||
chansess = sess;
|
||||
|
||||
EMIT_SIGNAL (XP_TE_CTCPSNDC, chansess, word[5],
|
||||
nick, to, NULL, 0);
|
||||
} else
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CTCPSND, sess->server->front_session, word[5],
|
||||
nick, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/* don't let IRCers specify path */
|
||||
#ifdef WIN32
|
||||
if (strchr (word[5], '/') == NULL && strchr (word[5], '\\') == NULL)
|
||||
#else
|
||||
if (strchr (word[5], '/') == NULL)
|
||||
#endif
|
||||
sound_play (word[5], TRUE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
generic:
|
||||
po = strchr (msg, '\001');
|
||||
if (po)
|
||||
po[0] = 0;
|
||||
|
||||
if (!is_channel (sess->server, to))
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CTCPGEN, sess->server->front_session, msg, nick,
|
||||
NULL, NULL, 0);
|
||||
} else
|
||||
{
|
||||
chansess = find_channel (sess->server, to);
|
||||
if (!chansess)
|
||||
chansess = sess;
|
||||
EMIT_SIGNAL (XP_TE_CTCPGENC, chansess, msg, nick, to, NULL, 0);
|
||||
}
|
||||
}
|
||||
6
src/common/ctcp.h
Normal file
6
src/common/ctcp.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef XCHAT_CTCP_H
|
||||
#define XCHAT_CTCP_H
|
||||
|
||||
void ctcp_handle (session *sess, char *to, char *nick, char *ip, char *msg, char *word[], char *word_eol[], int id);
|
||||
|
||||
#endif
|
||||
56
src/common/dbus/Makefile.am
Normal file
56
src/common/dbus/Makefile.am
Normal file
@@ -0,0 +1,56 @@
|
||||
noinst_LIBRARIES = libxchatdbus.a
|
||||
libxchatdbus_a_SOURCES = \
|
||||
dbus-plugin.c \
|
||||
dbus-plugin.h \
|
||||
dbus-client.c \
|
||||
dbus-client.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
remote-object.xml \
|
||||
apps_xchat_url_handler.schemas \
|
||||
marshallers.list \
|
||||
example.py \
|
||||
org.xchat.service.service.in \
|
||||
README
|
||||
|
||||
BUILT_SOURCES = \
|
||||
marshallers.h \
|
||||
remote-object-glue.h
|
||||
|
||||
CLEANFILES = $(BUILT_SOURCES)
|
||||
|
||||
INCLUDES = $(COMMON_CFLAGS) $(DBUS_CFLAGS)
|
||||
|
||||
noinst_PROGRAMS = example
|
||||
example_SOURCES = example.c
|
||||
example_LDADD = $(DBUS_LIBS) $(GLIB_LIBS)
|
||||
|
||||
remote-object-glue.h: remote-object.xml
|
||||
$(LIBTOOL) --mode=execute $(DBUS_BINDING_TOOL) --prefix=remote_object --mode=glib-server --output=$@ $<
|
||||
|
||||
marshallers.h: marshallers.list
|
||||
$(LIBTOOL) --mode=execute $(GLIB_GENMARSHAL) --header --body $< > $@
|
||||
|
||||
# Dbus service file
|
||||
servicedir = $(DBUS_SERVICES_DIR)
|
||||
service_in_files = org.xchat.service.service.in
|
||||
service_DATA = $(service_in_files:.service.in=.service)
|
||||
|
||||
# Rule to make the service file with bindir expanded
|
||||
$(service_DATA): $(service_in_files) Makefile
|
||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
||||
|
||||
if DO_GCONF
|
||||
GCONF_SCHEMA_CONFIG_SOURCE = `$(GCONFTOOL) --get-default-source`
|
||||
GCONF_SCHEMA_FILE_DIR = $(sysconfdir)/gconf/schemas
|
||||
schemadir = $(GCONF_SCHEMA_FILE_DIR)
|
||||
schema_DATA = apps_xchat_url_handler.schemas
|
||||
install-data-local:
|
||||
if test -z "$(DESTDIR)" ; then \
|
||||
for p in $(schema_DATA) ; do \
|
||||
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$$p; \
|
||||
done \
|
||||
fi
|
||||
else
|
||||
install-data-local:
|
||||
endif
|
||||
198
src/common/dbus/README
Normal file
198
src/common/dbus/README
Normal file
@@ -0,0 +1,198 @@
|
||||
For more help you can see the xchat plugin interface documentation.
|
||||
http://www.xchat.org/docs/plugin20.html
|
||||
WARNING: The dbus interface may change in the future.
|
||||
|
||||
You can use the "/org/xchat/Remote" object with interface "org.xchat.plugin",
|
||||
but his context can be changed by other clients at any moment and
|
||||
you may receive signal asked by other clients. So for more complex usage it's
|
||||
better to get your own remote object. Using "Connect" method on interface
|
||||
"org.xchat.connection"
|
||||
|
||||
Available methods on org.xchat.connection interface:
|
||||
|
||||
"Connect"
|
||||
- Parameters:
|
||||
- gchar*: filename
|
||||
- gchar*: name
|
||||
- gchar*: description
|
||||
- gchar*: version
|
||||
- Returns:
|
||||
- gchar*: Your own object's path.
|
||||
|
||||
"Disconnect"
|
||||
No parameter, no return value. It frees your remote object.
|
||||
|
||||
Available methods on org.xchat.plugin interface:
|
||||
|
||||
"Command"
|
||||
- Parameters:
|
||||
- gchar*: the command name without the "/". (e.g. "nick pseudo")
|
||||
|
||||
"Print"
|
||||
- Parameters:
|
||||
- gchar*: text to print on the xchat window.
|
||||
|
||||
"FindContext"
|
||||
- Parameters:
|
||||
- gchar*: the server name. Can be NULL.
|
||||
- gchar*: the channel name. Can be NULL.
|
||||
- Returns:
|
||||
- guint: context ID
|
||||
|
||||
"GetContext"
|
||||
- Returns:
|
||||
- guint: current context's ID
|
||||
|
||||
"SetContext"
|
||||
- Parameters:
|
||||
- guint: context ID to switch, returned by "FindContext" or "GetContext"
|
||||
- Returns:
|
||||
- gboolean: 1 for success, 0 for failure.
|
||||
|
||||
"GetInfo"
|
||||
- Parameters:
|
||||
- gchar*: ID of the information you want.
|
||||
- Returns:
|
||||
- gchar*: information you requested.
|
||||
|
||||
"GetPrefs"
|
||||
- Parameters:
|
||||
- gchar*: Setting name required.
|
||||
- Returns:
|
||||
- int: 0-Failed 1-Returned a string 2-Returned an Integer
|
||||
3-Returned a Boolean.
|
||||
- gchar*: the information requested if it's a string.
|
||||
- int: the information requested if it's a integer or boolean.
|
||||
|
||||
"HookCommand"
|
||||
- Parameters:
|
||||
- gchar*: Name of the command (without the forward slash).
|
||||
- int: Priority of this command.
|
||||
- gchar*: String of text to display when the user executes /help
|
||||
for this command. May be NULL if you're lazy.
|
||||
- int: Value to returns when the command is catched. See XCHAT_EAT_*.
|
||||
- Returns:
|
||||
- guint: The ID of the hook.
|
||||
|
||||
"HookServer"
|
||||
- Parameters:
|
||||
- gchar*: Name of the server event.
|
||||
- int: Priority of this command.
|
||||
- int: Value to returns when the command is catched. See XCHAT_EAT_*.
|
||||
- Returns:
|
||||
- guint: The ID of the hook.
|
||||
|
||||
"HookPrint"
|
||||
- Parameters:
|
||||
- gchar*: Name of the print event.
|
||||
- int: Priority of this command.
|
||||
- int: Value to returns when the command is catched. See XCHAT_EAT_*.
|
||||
- Returns:
|
||||
- guint: The ID of the hook.
|
||||
|
||||
"Unhook"
|
||||
- Parameters:
|
||||
- guint: ID of the hook to unhook.
|
||||
(the return value of "HookCommand", "HookServer" or "HookPrint")
|
||||
|
||||
"ListGet"
|
||||
- Parameters:
|
||||
- gchar*: The list name.
|
||||
- Returns:
|
||||
- guint: List ID.
|
||||
|
||||
"ListNext"
|
||||
- Parameters:
|
||||
- guint: List ID returned by "ListGet".
|
||||
- Returns:
|
||||
- gboolean: says if there is no more item in the list.
|
||||
|
||||
"ListStr"
|
||||
- Parameters:
|
||||
- guint: List ID returned by "ListGet".
|
||||
- gchar*: Name of the information needed.
|
||||
- Returns:
|
||||
- gchar*: The information requested.
|
||||
Warning: "context" attribut of "channels" list should be get with "ListInt"
|
||||
|
||||
"ListInt"
|
||||
- Parameters:
|
||||
- guint: List ID returned by "ListGet".
|
||||
- gchar*: Name of the information needed.
|
||||
- Returns:
|
||||
- guint: The information requested.
|
||||
|
||||
"ListTime"
|
||||
- Parameters:
|
||||
- guint: List ID returned by "ListGet".
|
||||
- gchar*: Name of the information needed.
|
||||
- Returns:
|
||||
- guint64: The information requested.
|
||||
|
||||
"ListFields"
|
||||
- Parameters:
|
||||
- gchar*: The list name.
|
||||
- Returns:
|
||||
- gchar**: information names in this list.
|
||||
|
||||
"ListFree"
|
||||
- Parameters:
|
||||
- guint: List ID returned by "ListGet".
|
||||
|
||||
"EmitPrint"
|
||||
- Parameters:
|
||||
- gchar*: Text event to print.
|
||||
- gchar**: NULL terminated array of string.
|
||||
- Returns:
|
||||
- gboolean: 1-Success 0-Failure.
|
||||
|
||||
"Nickcmp"
|
||||
- Parameters:
|
||||
- gchar*: String to compare.
|
||||
- gchar*: String to compare.
|
||||
- Returns:
|
||||
- int: An integer less than, equal to, or greater than zero if s1 is found,
|
||||
respectively, to be less than, to match, or be greater than s2.
|
||||
|
||||
"Strip"
|
||||
- Parameters:
|
||||
- gchar*: String to strip.
|
||||
- int: Length of the string (or -1 for NULL terminated).
|
||||
- int: Bit-field of flags: 0-Strip mIRC colors, 1-Strip text attributes.
|
||||
- Returns:
|
||||
- gchar*: striped string.
|
||||
|
||||
"SendModes"
|
||||
- Parameters:
|
||||
- gchar**: NULL terminated array of targets (strings). The names of people
|
||||
whom the action will be performed on.
|
||||
- int: Maximum modes to send per line.
|
||||
- gchar: Mode sign, '-' or '+'.
|
||||
- gchar: Mode char, e.g. 'o' for Ops.
|
||||
|
||||
|
||||
Available signals:
|
||||
|
||||
"ServerSignal"
|
||||
- Parameters:
|
||||
- gchar**: word returned by xchat.
|
||||
- gchar**: word_eol returned bu xchat.
|
||||
- guint: the ID of the hook. (the return value of "HookServer").
|
||||
- guint: the ID of the context where the event come from.
|
||||
|
||||
"CommandSignal"
|
||||
- Parameters:
|
||||
- gchar**: word returned by xchat.
|
||||
- gchar**: word_eol returned bu xchat.
|
||||
- guint: the ID of the hook. (the return value of "HookCommand").
|
||||
- guint: the ID of the context where the event come from.
|
||||
|
||||
"PrintSignal"
|
||||
- Parameters:
|
||||
- gchar**: word returned by xchat.
|
||||
- guint: the ID of the hook. (the return value of "HookPrint").
|
||||
- guint: the ID of the context where the event come from.
|
||||
|
||||
"UnloadSignal"
|
||||
emited when the user asks to unload your program.
|
||||
Please exit(0); when received !
|
||||
37
src/common/dbus/apps_xchat_url_handler.schemas
Normal file
37
src/common/dbus/apps_xchat_url_handler.schemas
Normal file
@@ -0,0 +1,37 @@
|
||||
<gconfschemafile>
|
||||
<schemalist>
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/url-handlers/irc/command</key>
|
||||
<applyto>/desktop/gnome/url-handlers/irc/command</applyto>
|
||||
<owner>xchat</owner>
|
||||
<type>string</type>
|
||||
<default>xchat --existing --url=%u</default>
|
||||
<locale name="C">
|
||||
<short>The handler for "irc://" URLs</short>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/url-handlers/irc/enabled</key>
|
||||
<applyto>/desktop/gnome/url-handlers/irc/enabled</applyto>
|
||||
<owner>xchat</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>Set it at TRUE if you want it activated</short>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/url-handlers/irc/needs_terminal</key>
|
||||
<applyto>/desktop/gnome/url-handlers/irc/needs_terminal</applyto>
|
||||
<owner>xchat</owner>
|
||||
<type>bool</type>
|
||||
<default>false</default>
|
||||
<locale name="C">
|
||||
<short>Run xchat in a terminal?</short>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
</schemalist>
|
||||
</gconfschemafile>
|
||||
118
src/common/dbus/dbus-client.c
Normal file
118
src/common/dbus/dbus-client.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/* dbus-client.c - XChat command-line options for D-Bus
|
||||
* Copyright (C) 2006 Claessens Xavier
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Claessens Xavier
|
||||
* xclaesse@gmail.com
|
||||
*/
|
||||
|
||||
#include <dbus/dbus-glib.h>
|
||||
#include "dbus-client.h"
|
||||
#include "../xchat.h"
|
||||
#include "../xchatc.h"
|
||||
|
||||
#define DBUS_SERVICE "org.xchat.service"
|
||||
#define DBUS_REMOTE "/org/xchat/Remote"
|
||||
#define DBUS_REMOTE_INTERFACE "org.xchat.plugin"
|
||||
|
||||
static void
|
||||
write_error (char *message,
|
||||
GError **error)
|
||||
{
|
||||
if (error == NULL || *error == NULL) {
|
||||
return;
|
||||
}
|
||||
g_printerr ("%s: %s\n", message, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
|
||||
void
|
||||
xchat_remote (void)
|
||||
/* TODO: dbus_g_connection_unref (connection) are commented because it makes
|
||||
* dbus to crash. Fixed in dbus >=0.70 ?!?
|
||||
* https://launchpad.net/distros/ubuntu/+source/dbus/+bug/54375
|
||||
*/
|
||||
{
|
||||
DBusGConnection *connection;
|
||||
DBusGProxy *dbus = NULL;
|
||||
DBusGProxy *remote_object = NULL;
|
||||
gboolean xchat_running;
|
||||
GError *error = NULL;
|
||||
char *command = NULL;
|
||||
|
||||
/* GnomeVFS >=2.15 uses D-Bus and threads, so threads should be
|
||||
* initialised before opening for the first time a D-Bus connection */
|
||||
if (!g_thread_supported ()) {
|
||||
g_thread_init (NULL);
|
||||
}
|
||||
dbus_g_thread_init ();
|
||||
|
||||
/* if there is nothing to do, return now. */
|
||||
if (!arg_existing || !(arg_url || arg_command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
arg_dont_autoconnect = TRUE;
|
||||
|
||||
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
|
||||
if (!connection) {
|
||||
write_error (_("Couldn't connect to session bus"), &error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Checks if xchat is already running */
|
||||
dbus = dbus_g_proxy_new_for_name (connection,
|
||||
DBUS_SERVICE_DBUS,
|
||||
DBUS_PATH_DBUS,
|
||||
DBUS_INTERFACE_DBUS);
|
||||
if (!dbus_g_proxy_call (dbus, "NameHasOwner", &error,
|
||||
G_TYPE_STRING, DBUS_SERVICE,
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_BOOLEAN, &xchat_running,
|
||||
G_TYPE_INVALID)) {
|
||||
write_error (_("Failed to complete NameHasOwner"), &error);
|
||||
xchat_running = FALSE;
|
||||
}
|
||||
g_object_unref (dbus);
|
||||
|
||||
if (!xchat_running) {
|
||||
//dbus_g_connection_unref (connection);
|
||||
return;
|
||||
}
|
||||
|
||||
remote_object = dbus_g_proxy_new_for_name (connection,
|
||||
DBUS_SERVICE,
|
||||
DBUS_REMOTE,
|
||||
DBUS_REMOTE_INTERFACE);
|
||||
|
||||
if (arg_url) {
|
||||
command = g_strdup_printf ("url %s", arg_url);
|
||||
} else if (arg_command) {
|
||||
command = g_strdup (arg_command);
|
||||
}
|
||||
|
||||
if (command) {
|
||||
if (!dbus_g_proxy_call (remote_object, "Command",
|
||||
&error,
|
||||
G_TYPE_STRING, command,
|
||||
G_TYPE_INVALID,G_TYPE_INVALID)) {
|
||||
write_error (_("Failed to complete Command"), &error);
|
||||
}
|
||||
g_free (command);
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
||||
27
src/common/dbus/dbus-client.h
Normal file
27
src/common/dbus/dbus-client.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* dbus-client.h - XChat command-line options for D-Bus
|
||||
* Copyright (C) 2006 Claessens Xavier
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Claessens Xavier
|
||||
* xclaesse@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef __DBUS_PLUGIN_H__
|
||||
#define __DBUS_PLUGIN_H__
|
||||
|
||||
void xchat_remote (void);
|
||||
|
||||
#endif /* __DBUS_PLUGIN_H__ */
|
||||
1087
src/common/dbus/dbus-plugin.c
Normal file
1087
src/common/dbus/dbus-plugin.c
Normal file
File diff suppressed because it is too large
Load Diff
31
src/common/dbus/dbus-plugin.h
Normal file
31
src/common/dbus/dbus-plugin.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* dbus-plugin.c - xchat plugin for remote access using DBUS
|
||||
* Copyright (C) 2006 Claessens Xavier
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Claessens Xavier
|
||||
* xclaesse@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef XCHAT_DBUS_PLUGIN_H
|
||||
#define XCHAT_DBUS_PLUGIN_H
|
||||
|
||||
int dbus_plugin_init (xchat_plugin *plugin_handle,
|
||||
char **plugin_name,
|
||||
char **plugin_desc,
|
||||
char **plugin_version,
|
||||
char *arg);
|
||||
|
||||
#endif
|
||||
201
src/common/dbus/example.c
Normal file
201
src/common/dbus/example.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/* example.c - program to demonstrate some D-BUS stuffs.
|
||||
* Copyright (C) 2006 Claessens Xavier
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Claessens Xavier
|
||||
* xclaesse@gmail.com
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <dbus/dbus-glib.h>
|
||||
#include <stdlib.h>
|
||||
#include "marshallers.h"
|
||||
|
||||
#define DBUS_SERVICE "org.xchat.service"
|
||||
#define DBUS_REMOTE "/org/xchat/Remote"
|
||||
#define DBUS_REMOTE_CONNECTION_INTERFACE "org.xchat.connection"
|
||||
#define DBUS_REMOTE_PLUGIN_INTERFACE "org.xchat.plugin"
|
||||
|
||||
guint command_id;
|
||||
guint server_id;
|
||||
|
||||
static void
|
||||
write_error (char *message,
|
||||
GError **error)
|
||||
{
|
||||
if (error == NULL || *error == NULL) {
|
||||
return;
|
||||
}
|
||||
g_printerr ("%s: %s\n", message, (*error)->message);
|
||||
g_clear_error (error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_server_cb (DBusGProxy *proxy,
|
||||
char *word[],
|
||||
char *word_eol[],
|
||||
guint hook_id,
|
||||
guint context_id,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (hook_id == server_id) {
|
||||
g_print ("message: %s\n", word_eol[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_command_cb (DBusGProxy *proxy,
|
||||
char *word[],
|
||||
char *word_eol[],
|
||||
guint hook_id,
|
||||
guint context_id,
|
||||
gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (hook_id == command_id) {
|
||||
if (!dbus_g_proxy_call (proxy, "Unhook",
|
||||
&error,
|
||||
G_TYPE_UINT, hook_id,
|
||||
G_TYPE_INVALID, G_TYPE_INVALID)) {
|
||||
write_error ("Failed to complete unhook", &error);
|
||||
}
|
||||
/* Now if you write "/test blah" again in the xchat window
|
||||
* you'll get a "Unknown command" error message */
|
||||
g_print ("test command received: %s\n", word_eol[1]);
|
||||
if (!dbus_g_proxy_call (proxy, "Print",
|
||||
&error,
|
||||
G_TYPE_STRING, "test command succeed",
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_INVALID)) {
|
||||
write_error ("Failed to complete Print", &error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unload_cb (void)
|
||||
{
|
||||
g_print ("Good bye !\n");
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
DBusGConnection *connection;
|
||||
DBusGProxy *remote_object;
|
||||
GMainLoop *mainloop;
|
||||
gchar *path;
|
||||
GError *error = NULL;
|
||||
|
||||
g_type_init ();
|
||||
|
||||
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
|
||||
if (connection == NULL) {
|
||||
write_error ("Couldn't connect to session bus", &error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
remote_object = dbus_g_proxy_new_for_name (connection,
|
||||
DBUS_SERVICE,
|
||||
DBUS_REMOTE,
|
||||
DBUS_REMOTE_CONNECTION_INTERFACE);
|
||||
if (!dbus_g_proxy_call (remote_object, "Connect",
|
||||
&error,
|
||||
G_TYPE_STRING, argv[0],
|
||||
G_TYPE_STRING, "example",
|
||||
G_TYPE_STRING, "Example of a D-Bus client",
|
||||
G_TYPE_STRING, "1.0",
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_STRING, &path, G_TYPE_INVALID)) {
|
||||
write_error ("Failed to complete Connect", &error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
g_object_unref (remote_object);
|
||||
|
||||
remote_object = dbus_g_proxy_new_for_name (connection,
|
||||
DBUS_SERVICE,
|
||||
path,
|
||||
DBUS_REMOTE_PLUGIN_INTERFACE);
|
||||
g_free (path);
|
||||
|
||||
if (!dbus_g_proxy_call (remote_object, "HookCommand",
|
||||
&error,
|
||||
G_TYPE_STRING, "test",
|
||||
G_TYPE_INT, 0,
|
||||
G_TYPE_STRING, "Simple D-BUS example",
|
||||
G_TYPE_INT, 1, G_TYPE_INVALID,
|
||||
G_TYPE_UINT, &command_id, G_TYPE_INVALID)) {
|
||||
write_error ("Failed to complete HookCommand", &error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
g_print ("Command hook id=%d\n", command_id);
|
||||
|
||||
if (!dbus_g_proxy_call (remote_object, "HookServer",
|
||||
&error,
|
||||
G_TYPE_STRING, "RAW LINE",
|
||||
G_TYPE_INT, 0,
|
||||
G_TYPE_INT, 0, G_TYPE_INVALID,
|
||||
G_TYPE_UINT, &server_id, G_TYPE_INVALID)) {
|
||||
write_error ("Failed to complete HookServer", &error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
g_print ("Server hook id=%d\n", server_id);
|
||||
|
||||
dbus_g_object_register_marshaller (
|
||||
g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT,
|
||||
G_TYPE_NONE,
|
||||
G_TYPE_STRV, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_UINT,
|
||||
G_TYPE_INVALID);
|
||||
|
||||
dbus_g_object_register_marshaller (
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
G_TYPE_INVALID);
|
||||
|
||||
dbus_g_proxy_add_signal (remote_object, "CommandSignal",
|
||||
G_TYPE_STRV,
|
||||
G_TYPE_STRV,
|
||||
G_TYPE_UINT,
|
||||
G_TYPE_UINT,
|
||||
G_TYPE_INVALID);
|
||||
dbus_g_proxy_connect_signal (remote_object, "CommandSignal",
|
||||
G_CALLBACK (test_command_cb),
|
||||
NULL, NULL);
|
||||
|
||||
dbus_g_proxy_add_signal (remote_object, "ServerSignal",
|
||||
G_TYPE_STRV,
|
||||
G_TYPE_STRV,
|
||||
G_TYPE_UINT,
|
||||
G_TYPE_UINT,
|
||||
G_TYPE_INVALID);
|
||||
dbus_g_proxy_connect_signal (remote_object, "ServerSignal",
|
||||
G_CALLBACK (test_server_cb),
|
||||
NULL, NULL);
|
||||
|
||||
dbus_g_proxy_add_signal (remote_object, "UnloadSignal",
|
||||
G_TYPE_INVALID);
|
||||
dbus_g_proxy_connect_signal (remote_object, "UnloadSignal",
|
||||
G_CALLBACK (unload_cb),
|
||||
NULL, NULL);
|
||||
|
||||
/* Now you can write on the xchat windows: "/test arg1 arg2 ..." */
|
||||
mainloop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (mainloop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
28
src/common/dbus/example.py
Normal file
28
src/common/dbus/example.py
Normal file
@@ -0,0 +1,28 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
import dbus
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
proxy = bus.get_object('org.xchat.service', '/org/xchat/Remote')
|
||||
remote = dbus.Interface(proxy, 'org.xchat.connection')
|
||||
path = remote.Connect ("example.py",
|
||||
"Python example",
|
||||
"Example of a D-Bus client written in python",
|
||||
"1.0")
|
||||
proxy = bus.get_object('org.xchat.service', path)
|
||||
xchat = dbus.Interface(proxy, 'org.xchat.plugin')
|
||||
|
||||
channels = xchat.ListGet ("channels")
|
||||
while xchat.ListNext (channels):
|
||||
name = xchat.ListStr (channels, "channel")
|
||||
print "------- " + name + " -------"
|
||||
xchat.SetContext (xchat.ListInt (channels, "context"))
|
||||
xchat.EmitPrint ("Channel Message", ["John", "Hi there", "@"])
|
||||
users = xchat.ListGet ("users")
|
||||
while xchat.ListNext (users):
|
||||
print "Nick: " + xchat.ListStr (users, "nick")
|
||||
xchat.ListFree (users)
|
||||
xchat.ListFree (channels)
|
||||
|
||||
print xchat.Strip ("\00312Blue\003 \002Bold!\002", -1, 1|2)
|
||||
|
||||
1
src/common/dbus/marshallers.list
Normal file
1
src/common/dbus/marshallers.list
Normal file
@@ -0,0 +1 @@
|
||||
VOID:POINTER,POINTER,UINT,UINT
|
||||
3
src/common/dbus/org.xchat.service.service.in
Normal file
3
src/common/dbus/org.xchat.service.service.in
Normal file
@@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=org.xchat.service
|
||||
Exec=@bindir@/xchat
|
||||
142
src/common/dbus/remote-object.xml
Normal file
142
src/common/dbus/remote-object.xml
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<node name="/">
|
||||
|
||||
<interface name="org.xchat.connection">
|
||||
<method name="Connect">
|
||||
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
|
||||
<arg type="s" name="filename" direction="in"/>
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="s" name="desc" direction="in"/>
|
||||
<arg type="s" name="version" direction="in"/>
|
||||
<arg type="s" name="path" direction="out"/>
|
||||
</method>
|
||||
<method name="Disconnect">
|
||||
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
<interface name="org.xchat.plugin">
|
||||
<method name="Command">
|
||||
<arg type="s" name="command" direction="in"/>
|
||||
</method>
|
||||
<method name="Print">
|
||||
<arg type="s" name="text" direction="in"/>
|
||||
</method>
|
||||
<method name="FindContext">
|
||||
<arg type="s" name="server" direction="in"/>
|
||||
<arg type="s" name="channel" direction="in"/>
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="GetContext">
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="SetContext">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
<arg type="b" name="ret" direction="out"/>
|
||||
</method>
|
||||
<method name="GetInfo">
|
||||
<arg type="s" name="id" direction="in"/>
|
||||
<arg type="s" name="ret_info" direction="out"/>
|
||||
</method>
|
||||
<method name="GetPrefs">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="i" name="ret_type" direction="out"/>
|
||||
<arg type="s" name="ret_str" direction="out"/>
|
||||
<arg type="i" name="ret_int" direction="out"/>
|
||||
</method>
|
||||
<method name="HookCommand">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="i" name="priority" direction="in"/>
|
||||
<arg type="s" name="help_text" direction="in"/>
|
||||
<arg type="i" name="return_value" direction="in"/>
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="HookServer">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="i" name="priority" direction="in"/>
|
||||
<arg type="i" name="return_value" direction="in"/>
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="HookPrint">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="i" name="priority" direction="in"/>
|
||||
<arg type="i" name="return_value" direction="in"/>
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="Unhook">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
</method>
|
||||
<method name="ListGet">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="u" name="ret_id" direction="out"/>
|
||||
</method>
|
||||
<method name="ListNext">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
<arg type="b" name="ret" direction="out"/>
|
||||
</method>
|
||||
<method name="ListStr">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="s" name="ret_str" direction="out"/>
|
||||
</method>
|
||||
<method name="ListInt">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="i" name="ret_int" direction="out"/>
|
||||
</method>
|
||||
<method name="ListTime">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="t" name="ret_time" direction="out"/>
|
||||
</method>
|
||||
<method name="ListFields">
|
||||
<arg type="s" name="name" direction="in"/>
|
||||
<arg type="as" name="ret" direction="out"/>
|
||||
</method>
|
||||
<method name="ListFree">
|
||||
<arg type="u" name="id" direction="in"/>
|
||||
</method>
|
||||
<method name="EmitPrint">
|
||||
<arg type="s" name="event_name" direction="in"/>
|
||||
<arg type="as" name="args" direction="in"/>
|
||||
<arg type="b" name="ret" direction="out"/>
|
||||
</method>
|
||||
<method name="Nickcmp">
|
||||
<arg type="s" name="nick1" direction="in"/>
|
||||
<arg type="s" name="nick2" direction="in"/>
|
||||
<arg type="i" name="ret" direction="out"/>
|
||||
</method>
|
||||
<method name="Strip">
|
||||
<arg type="s" name="str" direction="in"/>
|
||||
<arg type="i" name="len" direction="in"/>
|
||||
<arg type="i" name="flag" direction="in"/>
|
||||
<arg type="s" name="ret_str" direction="out"/>
|
||||
</method>
|
||||
<method name="SendModes">
|
||||
<arg type="as" name="targets" direction="in"/>
|
||||
<arg type="i" name="modes_per_line" direction="in"/>
|
||||
<arg type="y" name="sign" direction="in"/>
|
||||
<arg type="y" name="mode" direction="in"/>
|
||||
</method>
|
||||
|
||||
<signal name="CommandSignal">
|
||||
<arg type="as" name="word"/>
|
||||
<arg type="as" name="word_eol"/>
|
||||
<arg type="u" name="hook_id"/>
|
||||
<arg type="u" name="context_id"/>
|
||||
</signal>
|
||||
<signal name="ServerSignal">
|
||||
<arg type="as" name="word"/>
|
||||
<arg type="as" name="word_eol"/>
|
||||
<arg type="u" name="hook_id"/>
|
||||
<arg type="u" name="context_id"/>
|
||||
</signal>
|
||||
<signal name="PrintSignal">
|
||||
<arg type="as" name="word"/>
|
||||
<arg type="u" name="hook_id"/>
|
||||
<arg type="u" name="context_id"/>
|
||||
</signal>
|
||||
<signal name="UnloadSignal"/>
|
||||
</interface>
|
||||
</node>
|
||||
2587
src/common/dcc.c
Normal file
2587
src/common/dcc.c
Normal file
File diff suppressed because it is too large
Load Diff
117
src/common/dcc.h
Normal file
117
src/common/dcc.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* dcc.h */
|
||||
|
||||
#include <time.h> /* for time_t */
|
||||
|
||||
#ifndef XCHAT_DCC_H
|
||||
#define XCHAT_DCC_H
|
||||
|
||||
#define STAT_QUEUED 0
|
||||
#define STAT_ACTIVE 1
|
||||
#define STAT_FAILED 2
|
||||
#define STAT_DONE 3
|
||||
#define STAT_CONNECTING 4
|
||||
#define STAT_ABORTED 5
|
||||
|
||||
#define TYPE_SEND 0
|
||||
#define TYPE_RECV 1
|
||||
#define TYPE_CHATRECV 2
|
||||
#define TYPE_CHATSEND 3
|
||||
|
||||
#define CPS_AVG_WINDOW 10
|
||||
|
||||
/* can we do 64-bit dcc? */
|
||||
#if defined(G_GINT64_FORMAT) && defined(HAVE_STRTOULL)
|
||||
#define USE_DCC64
|
||||
/* we really get only 63 bits, since st_size is signed */
|
||||
#define DCC_SIZE gint64
|
||||
#define DCC_SFMT G_GINT64_FORMAT
|
||||
#else
|
||||
#define DCC_SIZE unsigned int
|
||||
#define DCC_SFMT "u"
|
||||
#endif
|
||||
|
||||
struct DCC
|
||||
{
|
||||
struct server *serv;
|
||||
struct dcc_chat *dccchat;
|
||||
struct proxy_state *proxy;
|
||||
guint32 addr; /* the 32bit IP number, host byte order */
|
||||
int fp; /* file pointer */
|
||||
int sok;
|
||||
int iotag; /* reading io tag */
|
||||
int wiotag; /* writing/sending io tag */
|
||||
int port;
|
||||
int pasvid; /* mIRC's passive DCC id */
|
||||
int cps;
|
||||
int resume_error;
|
||||
int resume_errno;
|
||||
|
||||
GTimeVal lastcpstv, firstcpstv;
|
||||
DCC_SIZE lastcpspos;
|
||||
int maxcps;
|
||||
|
||||
unsigned char ack_buf[4]; /* buffer for reading 4-byte ack */
|
||||
int ack_pos;
|
||||
|
||||
DCC_SIZE size;
|
||||
DCC_SIZE resumable;
|
||||
DCC_SIZE ack;
|
||||
DCC_SIZE pos;
|
||||
time_t starttime;
|
||||
time_t offertime;
|
||||
time_t lasttime;
|
||||
char *file; /* utf8 */
|
||||
char *destfile; /* utf8 */
|
||||
char *destfile_fs; /* local filesystem encoding */
|
||||
char *nick;
|
||||
unsigned char type; /* 0 = SEND 1 = RECV 2 = CHAT */
|
||||
unsigned char dccstat; /* 0 = QUEUED 1 = ACTIVE 2 = FAILED 3 = DONE */
|
||||
unsigned int resume_sent:1; /* resume request sent */
|
||||
unsigned int fastsend:1;
|
||||
unsigned int ackoffset:1; /* is reciever sending acks as an offset from */
|
||||
/* the resume point? */
|
||||
unsigned int throttled:2; /* 0x1 = per send/get throttle
|
||||
0x2 = global throttle */
|
||||
};
|
||||
|
||||
#define MAX_PROXY_BUFFER 1024
|
||||
struct proxy_state
|
||||
{
|
||||
int phase;
|
||||
unsigned char buffer[MAX_PROXY_BUFFER];
|
||||
int buffersize;
|
||||
int bufferused;
|
||||
};
|
||||
|
||||
struct dcc_chat
|
||||
{
|
||||
char linebuf[2048];
|
||||
int pos;
|
||||
};
|
||||
|
||||
struct dccstat_info
|
||||
{
|
||||
char *name; /* Display name */
|
||||
int color; /* Display color (index into colors[] ) */
|
||||
};
|
||||
|
||||
extern struct dccstat_info dccstat[];
|
||||
|
||||
gboolean is_dcc (struct DCC *dcc);
|
||||
void dcc_abort (session *sess, struct DCC *dcc);
|
||||
void dcc_get (struct DCC *dcc);
|
||||
int dcc_resume (struct DCC *dcc);
|
||||
void dcc_check_timeouts (void);
|
||||
void dcc_change_nick (server *serv, char *oldnick, char *newnick);
|
||||
void dcc_notify_kill (struct server *serv);
|
||||
struct DCC *dcc_write_chat (char *nick, char *text);
|
||||
void dcc_send (struct session *sess, char *to, char *file, int maxcps, int passive);
|
||||
struct DCC *find_dcc (char *nick, char *file, int type);
|
||||
void dcc_get_nick (struct session *sess, char *nick);
|
||||
void dcc_chat (session *sess, char *nick, int passive);
|
||||
void handle_dcc (session *sess, char *nick, char *word[], char *word_eol[]);
|
||||
void dcc_show_list (session *sess);
|
||||
guint32 dcc_get_my_address (void);
|
||||
void dcc_get_with_destfile (struct DCC *dcc, char *utf8file);
|
||||
|
||||
#endif
|
||||
162
src/common/fe.h
Normal file
162
src/common/fe.h
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "userlist.h"
|
||||
#include "dcc.h"
|
||||
|
||||
#ifndef XCHAT_FE_H
|
||||
#define XCHAT_FE_H
|
||||
|
||||
/* for storage of /menu entries */
|
||||
typedef struct
|
||||
{
|
||||
gint32 pos; /* position */
|
||||
gint16 modifier; /* keybinding */
|
||||
gint16 root_offset; /* bytes to offset ->path */
|
||||
|
||||
char is_main; /* is part of the Main menu? (not a popup) */
|
||||
char state; /* state of toggle items */
|
||||
char markup; /* use pango markup? */
|
||||
char enable; /* enabled? sensitivity */
|
||||
|
||||
int key;
|
||||
char *path;
|
||||
char *label;
|
||||
char *cmd;
|
||||
char *ucmd; /* unselect command (toggles) */
|
||||
char *group; /* for radio items or NULL */
|
||||
char *icon; /* filename */
|
||||
} menu_entry;
|
||||
|
||||
int fe_args (int argc, char *argv[]);
|
||||
void fe_init (void);
|
||||
void fe_main (void);
|
||||
void fe_cleanup (void);
|
||||
void fe_exit (void);
|
||||
int fe_timeout_add (int interval, void *callback, void *userdata);
|
||||
void fe_timeout_remove (int tag);
|
||||
void fe_new_window (struct session *sess, int focus);
|
||||
void fe_new_server (struct server *serv);
|
||||
void fe_add_rawlog (struct server *serv, char *text, int len, int outbound);
|
||||
#define FE_MSG_WAIT 1
|
||||
#define FE_MSG_INFO 2
|
||||
#define FE_MSG_WARN 4
|
||||
#define FE_MSG_ERROR 8
|
||||
#define FE_MSG_MARKUP 16
|
||||
void fe_message (char *msg, int flags);
|
||||
#define FIA_READ 1
|
||||
#define FIA_WRITE 2
|
||||
#define FIA_EX 4
|
||||
#define FIA_FD 8
|
||||
int fe_input_add (int sok, int flags, void *func, void *data);
|
||||
void fe_input_remove (int tag);
|
||||
void fe_idle_add (void *func, void *data);
|
||||
void fe_set_topic (struct session *sess, char *topic, char *stripped_topic);
|
||||
void fe_set_hilight (struct session *sess);
|
||||
void fe_set_tab_color (struct session *sess, int col);
|
||||
void fe_flash_window (struct session *sess);
|
||||
void fe_update_mode_buttons (struct session *sess, char mode, char sign);
|
||||
void fe_update_channel_key (struct session *sess);
|
||||
void fe_update_channel_limit (struct session *sess);
|
||||
int fe_is_chanwindow (struct server *serv);
|
||||
void fe_add_chan_list (struct server *serv, char *chan, char *users,
|
||||
char *topic);
|
||||
void fe_chan_list_end (struct server *serv);
|
||||
int fe_is_banwindow (struct session *sess);
|
||||
void fe_add_ban_list (struct session *sess, char *mask, char *who, char *when, int is_exemption);
|
||||
void fe_ban_list_end (struct session *sess, int is_exemption);
|
||||
void fe_notify_update (char *name);
|
||||
void fe_notify_ask (char *name, char *networks);
|
||||
void fe_text_clear (struct session *sess, int lines);
|
||||
void fe_close_window (struct session *sess);
|
||||
void fe_progressbar_start (struct session *sess);
|
||||
void fe_progressbar_end (struct server *serv);
|
||||
void fe_print_text (struct session *sess, char *text, time_t stamp);
|
||||
void fe_userlist_insert (struct session *sess, struct User *newuser, int row, int sel);
|
||||
int fe_userlist_remove (struct session *sess, struct User *user);
|
||||
void fe_userlist_rehash (struct session *sess, struct User *user);
|
||||
void fe_userlist_update (struct session *sess, struct User *user);
|
||||
void fe_userlist_move (struct session *sess, struct User *user, int new_row);
|
||||
void fe_userlist_numbers (struct session *sess);
|
||||
void fe_userlist_clear (struct session *sess);
|
||||
void fe_userlist_set_selected (struct session *sess);
|
||||
void fe_uselect (session *sess, char *word[], int do_clear, int scroll_to);
|
||||
void fe_dcc_add (struct DCC *dcc);
|
||||
void fe_dcc_update (struct DCC *dcc);
|
||||
void fe_dcc_remove (struct DCC *dcc);
|
||||
int fe_dcc_open_recv_win (int passive);
|
||||
int fe_dcc_open_send_win (int passive);
|
||||
int fe_dcc_open_chat_win (int passive);
|
||||
void fe_clear_channel (struct session *sess);
|
||||
void fe_session_callback (struct session *sess);
|
||||
void fe_server_callback (struct server *serv);
|
||||
void fe_url_add (const char *text);
|
||||
void fe_pluginlist_update (void);
|
||||
void fe_buttons_update (struct session *sess);
|
||||
void fe_dlgbuttons_update (struct session *sess);
|
||||
void fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive);
|
||||
void fe_set_channel (struct session *sess);
|
||||
void fe_set_title (struct session *sess);
|
||||
void fe_set_nonchannel (struct session *sess, int state);
|
||||
void fe_set_nick (struct server *serv, char *newnick);
|
||||
void fe_ignore_update (int level);
|
||||
void fe_beep (void);
|
||||
void fe_lastlog (session *sess, session *lastlog_sess, char *sstr, gboolean regexp);
|
||||
void fe_set_lag (server *serv, int lag);
|
||||
void fe_set_throttle (server *serv);
|
||||
void fe_set_away (server *serv);
|
||||
void fe_serverlist_open (session *sess);
|
||||
void fe_get_str (char *prompt, char *def, void *callback, void *ud);
|
||||
void fe_get_int (char *prompt, int def, void *callback, void *ud);
|
||||
#define FRF_WRITE 1 /* save file */
|
||||
#define FRF_MULTIPLE 2 /* multi-select */
|
||||
#define FRF_ADDFOLDER 4 /* add ~/.xchat2 to favourites */
|
||||
#define FRF_CHOOSEFOLDER 8 /* choosing a folder only */
|
||||
#define FRF_FILTERISINITIAL 16 /* unused */
|
||||
#define FRF_NOASKOVERWRITE 32 /* don't ask to overwrite existing files */
|
||||
void fe_get_file (const char *title, char *initial,
|
||||
void (*callback) (void *userdata, char *file), void *userdata,
|
||||
int flags);
|
||||
typedef enum {
|
||||
FE_GUI_HIDE,
|
||||
FE_GUI_SHOW,
|
||||
FE_GUI_FOCUS,
|
||||
FE_GUI_FLASH,
|
||||
FE_GUI_COLOR,
|
||||
FE_GUI_ICONIFY,
|
||||
FE_GUI_MENU,
|
||||
FE_GUI_ATTACH,
|
||||
FE_GUI_APPLY,
|
||||
} fe_gui_action;
|
||||
void fe_ctrl_gui (session *sess, fe_gui_action action, int arg);
|
||||
int fe_gui_info (session *sess, int info_type);
|
||||
void *fe_gui_info_ptr (session *sess, int info_type);
|
||||
void fe_confirm (const char *message, void (*yesproc)(void *), void (*noproc)(void *), void *ud);
|
||||
char *fe_get_inputbox_contents (struct session *sess);
|
||||
int fe_get_inputbox_cursor (struct session *sess);
|
||||
void fe_set_inputbox_contents (struct session *sess, char *text);
|
||||
void fe_set_inputbox_cursor (struct session *sess, int delta, int pos);
|
||||
void fe_open_url (const char *url);
|
||||
void fe_menu_del (menu_entry *);
|
||||
char *fe_menu_add (menu_entry *);
|
||||
void fe_menu_update (menu_entry *);
|
||||
#define FE_SE_CONNECT 0
|
||||
#define FE_SE_LOGGEDIN 1
|
||||
#define FE_SE_DISCONNECT 2
|
||||
#define FE_SE_RECONDELAY 3
|
||||
#define FE_SE_CONNECTING 4
|
||||
void fe_server_event (server *serv, int type, int arg);
|
||||
/* pass NULL filename2 for default xchat icon */
|
||||
void fe_tray_set_flash (const char *filename1, const char *filename2, int timeout);
|
||||
/* pass NULL filename for default xchat icon */
|
||||
void fe_tray_set_file (const char *filename);
|
||||
typedef enum
|
||||
{
|
||||
FE_ICON_NORMAL = 0,
|
||||
FE_ICON_MESSAGE = 2,
|
||||
FE_ICON_HIGHLIGHT = 5,
|
||||
FE_ICON_PRIVMSG = 8,
|
||||
FE_ICON_FILEOFFER = 11
|
||||
} feicon;
|
||||
void fe_tray_set_icon (feicon icon);
|
||||
void fe_tray_set_tooltip (const char *text);
|
||||
void fe_tray_set_balloon (const char *title, const char *text);
|
||||
|
||||
#endif
|
||||
121
src/common/history.c
Normal file
121
src/common/history.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "history.h"
|
||||
|
||||
void
|
||||
history_add (struct history *his, char *text)
|
||||
{
|
||||
if (his->lines[his->realpos])
|
||||
free (his->lines[his->realpos]);
|
||||
his->lines[his->realpos] = strdup (text);
|
||||
his->realpos++;
|
||||
if (his->realpos == HISTORY_SIZE)
|
||||
his->realpos = 0;
|
||||
his->pos = his->realpos;
|
||||
}
|
||||
|
||||
void
|
||||
history_free (struct history *his)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HISTORY_SIZE; i++)
|
||||
{
|
||||
if (his->lines[i])
|
||||
{
|
||||
free (his->lines[i]);
|
||||
his->lines[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
history_down (struct history *his)
|
||||
{
|
||||
int next;
|
||||
|
||||
if (his->pos == his->realpos) /* allow down only after up */
|
||||
return 0;
|
||||
if (his->realpos == 0)
|
||||
{
|
||||
if (his->pos == HISTORY_SIZE - 1)
|
||||
{
|
||||
his->pos = 0;
|
||||
return "";
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (his->pos == his->realpos - 1)
|
||||
{
|
||||
his->pos++;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
next = 0;
|
||||
if (his->pos < HISTORY_SIZE - 1)
|
||||
next = his->pos + 1;
|
||||
|
||||
if (his->lines[next])
|
||||
{
|
||||
his->pos = next;
|
||||
return his->lines[his->pos];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
history_up (struct history *his, char *current_text)
|
||||
{
|
||||
int next;
|
||||
|
||||
if (his->realpos == HISTORY_SIZE - 1)
|
||||
{
|
||||
if (his->pos == 0)
|
||||
return 0;
|
||||
} else
|
||||
{
|
||||
if (his->pos == his->realpos + 1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
next = HISTORY_SIZE - 1;
|
||||
if (his->pos != 0)
|
||||
next = his->pos - 1;
|
||||
|
||||
if (his->lines[next])
|
||||
{
|
||||
if
|
||||
(
|
||||
current_text[0] && strcmp(current_text, his->lines[next]) &&
|
||||
(!his->lines[his->pos] || strcmp(current_text, his->lines[his->pos])) &&
|
||||
(!his->lines[his->realpos] || strcmp(current_text, his->lines[his->pos]))
|
||||
)
|
||||
{
|
||||
history_add (his, current_text);
|
||||
}
|
||||
|
||||
his->pos = next;
|
||||
return his->lines[his->pos];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
src/common/history.h
Normal file
18
src/common/history.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef XCHAT_HISTORY_H
|
||||
#define XCHAT_HISTORY_H
|
||||
|
||||
#define HISTORY_SIZE 100
|
||||
|
||||
struct history
|
||||
{
|
||||
char *lines[HISTORY_SIZE];
|
||||
int pos;
|
||||
int realpos;
|
||||
};
|
||||
|
||||
void history_add (struct history *his, char *text);
|
||||
void history_free (struct history *his);
|
||||
char *history_up (struct history *his, char *current_text);
|
||||
char *history_down (struct history *his);
|
||||
|
||||
#endif
|
||||
89
src/common/identd.c
Normal file
89
src/common/identd.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/* simple identd server for xchat under win32 */
|
||||
|
||||
|
||||
static int identd_is_running = FALSE;
|
||||
|
||||
|
||||
static int
|
||||
identd (char *username)
|
||||
{
|
||||
int sok, read_sok, len;
|
||||
char *p;
|
||||
char buf[256];
|
||||
char outbuf[256];
|
||||
struct sockaddr_in addr;
|
||||
|
||||
sok = socket (AF_INET, SOCK_STREAM, 0);
|
||||
if (sok == INVALID_SOCKET)
|
||||
{
|
||||
free (username);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = 1;
|
||||
setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &len, sizeof (len));
|
||||
|
||||
memset (&addr, 0, sizeof (addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons (113);
|
||||
|
||||
if (bind (sok, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket (sok);
|
||||
free (username);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (listen (sok, 1) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket (sok);
|
||||
free (username);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = sizeof (addr);
|
||||
read_sok = accept (sok, (struct sockaddr *) &addr, &len);
|
||||
closesocket (sok);
|
||||
if (read_sok == INVALID_SOCKET)
|
||||
{
|
||||
free (username);
|
||||
return 0;
|
||||
}
|
||||
|
||||
identd_is_running = FALSE;
|
||||
|
||||
snprintf (outbuf, sizeof (outbuf), "%%\tServicing ident request from %s\n",
|
||||
inet_ntoa (addr.sin_addr));
|
||||
PrintText (current_sess, outbuf);
|
||||
|
||||
recv (read_sok, buf, sizeof (buf) - 1, 0);
|
||||
buf[sizeof (buf) - 1] = 0; /* ensure null termination */
|
||||
|
||||
p = strchr (buf, ',');
|
||||
if (p)
|
||||
{
|
||||
snprintf (outbuf, sizeof (outbuf) - 1, "%d, %d : USERID : UNIX : %s\r\n",
|
||||
atoi (buf), atoi (p + 1), username);
|
||||
outbuf[sizeof (outbuf) - 1] = 0; /* ensure null termination */
|
||||
send (read_sok, outbuf, strlen (outbuf), 0);
|
||||
}
|
||||
|
||||
sleep (1);
|
||||
closesocket (read_sok);
|
||||
free (username);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
identd_start (char *username)
|
||||
{
|
||||
DWORD tid;
|
||||
|
||||
if (identd_is_running == FALSE)
|
||||
{
|
||||
identd_is_running = TRUE;
|
||||
CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) identd,
|
||||
strdup (username), 0, &tid));
|
||||
}
|
||||
}
|
||||
424
src/common/ignore.c
Normal file
424
src/common/ignore.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "xchat.h"
|
||||
#include "ignore.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "fe.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
#include "xchatc.h"
|
||||
|
||||
|
||||
int ignored_ctcp = 0; /* keep a count of all we ignore */
|
||||
int ignored_priv = 0;
|
||||
int ignored_chan = 0;
|
||||
int ignored_noti = 0;
|
||||
int ignored_invi = 0;
|
||||
static int ignored_total = 0;
|
||||
|
||||
/* ignore_exists ():
|
||||
* returns: struct ig, if this mask is in the ignore list already
|
||||
* NULL, otherwise
|
||||
*/
|
||||
struct ignore *
|
||||
ignore_exists (char *mask)
|
||||
{
|
||||
struct ignore *ig = 0;
|
||||
GSList *list;
|
||||
|
||||
list = ignore_list;
|
||||
while (list)
|
||||
{
|
||||
ig = (struct ignore *) list->data;
|
||||
if (!rfc_casecmp (ig->mask, mask))
|
||||
return ig;
|
||||
list = list->next;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/* ignore_add(...)
|
||||
|
||||
* returns:
|
||||
* 0 fail
|
||||
* 1 success
|
||||
* 2 success (old ignore has been changed)
|
||||
*/
|
||||
|
||||
int
|
||||
ignore_add (char *mask, int type)
|
||||
{
|
||||
struct ignore *ig = 0;
|
||||
int change_only = FALSE;
|
||||
|
||||
/* first check if it's already ignored */
|
||||
ig = ignore_exists (mask);
|
||||
if (ig)
|
||||
change_only = TRUE;
|
||||
|
||||
if (!change_only)
|
||||
ig = malloc (sizeof (struct ignore));
|
||||
|
||||
if (!ig)
|
||||
return 0;
|
||||
|
||||
ig->mask = strdup (mask);
|
||||
ig->type = type;
|
||||
|
||||
if (!change_only)
|
||||
ignore_list = g_slist_prepend (ignore_list, ig);
|
||||
fe_ignore_update (1);
|
||||
|
||||
if (change_only)
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
ignore_showlist (session *sess)
|
||||
{
|
||||
struct ignore *ig;
|
||||
GSList *list = ignore_list;
|
||||
char tbuf[256];
|
||||
int i = 0;
|
||||
|
||||
EMIT_SIGNAL (XP_TE_IGNOREHEADER, sess, 0, 0, 0, 0, 0);
|
||||
|
||||
while (list)
|
||||
{
|
||||
ig = list->data;
|
||||
i++;
|
||||
|
||||
snprintf (tbuf, sizeof (tbuf), " %-25s ", ig->mask);
|
||||
if (ig->type & IG_PRIV)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_NOTI)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_CHAN)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_CTCP)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_DCC)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_INVI)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
if (ig->type & IG_UNIG)
|
||||
strcat (tbuf, _("YES "));
|
||||
else
|
||||
strcat (tbuf, _("NO "));
|
||||
strcat (tbuf, "\n");
|
||||
PrintText (sess, tbuf);
|
||||
/*EMIT_SIGNAL (XP_TE_IGNORELIST, sess, ig->mask, 0, 0, 0, 0); */
|
||||
/* use this later, when TE's support 7 args */
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
EMIT_SIGNAL (XP_TE_IGNOREEMPTY, sess, 0, 0, 0, 0, 0);
|
||||
|
||||
EMIT_SIGNAL (XP_TE_IGNOREFOOTER, sess, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* ignore_del()
|
||||
|
||||
* one of the args must be NULL, use mask OR *ig, not both
|
||||
*
|
||||
*/
|
||||
|
||||
int
|
||||
ignore_del (char *mask, struct ignore *ig)
|
||||
{
|
||||
if (!ig)
|
||||
{
|
||||
GSList *list = ignore_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
ig = (struct ignore *) list->data;
|
||||
if (!rfc_casecmp (ig->mask, mask))
|
||||
break;
|
||||
list = list->next;
|
||||
ig = 0;
|
||||
}
|
||||
}
|
||||
if (ig)
|
||||
{
|
||||
ignore_list = g_slist_remove (ignore_list, ig);
|
||||
free (ig->mask);
|
||||
free (ig);
|
||||
fe_ignore_update (1);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check if a msg should be ignored by browsing our ignore list */
|
||||
|
||||
int
|
||||
ignore_check (char *host, int type)
|
||||
{
|
||||
struct ignore *ig;
|
||||
GSList *list = ignore_list;
|
||||
|
||||
/* check if there's an UNIGNORE first, they take precendance. */
|
||||
while (list)
|
||||
{
|
||||
ig = (struct ignore *) list->data;
|
||||
if (ig->type & IG_UNIG)
|
||||
{
|
||||
if (ig->type & type)
|
||||
{
|
||||
if (match (ig->mask, host))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
list = ignore_list;
|
||||
while (list)
|
||||
{
|
||||
ig = (struct ignore *) list->data;
|
||||
|
||||
if (ig->type & type)
|
||||
{
|
||||
if (match (ig->mask, host))
|
||||
{
|
||||
ignored_total++;
|
||||
if (type & IG_PRIV)
|
||||
ignored_priv++;
|
||||
if (type & IG_NOTI)
|
||||
ignored_noti++;
|
||||
if (type & IG_CHAN)
|
||||
ignored_chan++;
|
||||
if (type & IG_CTCP)
|
||||
ignored_ctcp++;
|
||||
if (type & IG_INVI)
|
||||
ignored_invi++;
|
||||
fe_ignore_update (2);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
ignore_read_next_entry (char *my_cfg, struct ignore *ignore)
|
||||
{
|
||||
char tbuf[1024];
|
||||
|
||||
/* Casting to char * done below just to satisfy compiler */
|
||||
|
||||
if (my_cfg)
|
||||
{
|
||||
my_cfg = cfg_get_str (my_cfg, "mask", tbuf, sizeof (tbuf));
|
||||
if (!my_cfg)
|
||||
return NULL;
|
||||
ignore->mask = strdup (tbuf);
|
||||
}
|
||||
if (my_cfg)
|
||||
{
|
||||
my_cfg = cfg_get_str (my_cfg, "type", tbuf, sizeof (tbuf));
|
||||
ignore->type = atoi (tbuf);
|
||||
}
|
||||
return my_cfg;
|
||||
}
|
||||
|
||||
void
|
||||
ignore_load ()
|
||||
{
|
||||
struct ignore *ignore;
|
||||
struct stat st;
|
||||
char *cfg, *my_cfg;
|
||||
int fh, i;
|
||||
|
||||
fh = xchat_open_file ("ignore.conf", O_RDONLY, 0, 0);
|
||||
if (fh != -1)
|
||||
{
|
||||
fstat (fh, &st);
|
||||
if (st.st_size)
|
||||
{
|
||||
cfg = malloc (st.st_size + 1);
|
||||
cfg[0] = '\0';
|
||||
i = read (fh, cfg, st.st_size);
|
||||
if (i >= 0)
|
||||
cfg[i] = '\0';
|
||||
my_cfg = cfg;
|
||||
while (my_cfg)
|
||||
{
|
||||
ignore = malloc (sizeof (struct ignore));
|
||||
memset (ignore, 0, sizeof (struct ignore));
|
||||
if ((my_cfg = ignore_read_next_entry (my_cfg, ignore)))
|
||||
ignore_list = g_slist_prepend (ignore_list, ignore);
|
||||
else
|
||||
free (ignore);
|
||||
}
|
||||
free (cfg);
|
||||
}
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ignore_save ()
|
||||
{
|
||||
char buf[1024];
|
||||
int fh;
|
||||
GSList *temp = ignore_list;
|
||||
struct ignore *ig;
|
||||
|
||||
fh = xchat_open_file ("ignore.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (temp)
|
||||
{
|
||||
ig = (struct ignore *) temp->data;
|
||||
if (!(ig->type & IG_NOSAVE))
|
||||
{
|
||||
snprintf (buf, sizeof (buf), "mask = %s\ntype = %d\n\n",
|
||||
ig->mask, ig->type);
|
||||
write (fh, buf, strlen (buf));
|
||||
}
|
||||
temp = temp->next;
|
||||
}
|
||||
close (fh);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flood_autodialog_timeout (gpointer data)
|
||||
{
|
||||
prefs.autodialog = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
flood_check (char *nick, char *ip, server *serv, session *sess, int what) /*0=ctcp 1=priv */
|
||||
{
|
||||
/*
|
||||
serv
|
||||
int ctcp_counter;
|
||||
time_t ctcp_last_time;
|
||||
prefs
|
||||
unsigned int ctcp_number_limit;
|
||||
unsigned int ctcp_time_limit;
|
||||
*/
|
||||
char buf[512];
|
||||
char real_ip[132];
|
||||
int i;
|
||||
time_t current_time;
|
||||
current_time = time (NULL);
|
||||
|
||||
if (what == 0)
|
||||
{
|
||||
if (serv->ctcp_last_time == 0) /*first ctcp in this server */
|
||||
{
|
||||
serv->ctcp_last_time = time (NULL);
|
||||
serv->ctcp_counter++;
|
||||
} else
|
||||
{
|
||||
if (difftime (current_time, serv->ctcp_last_time) < prefs.ctcp_time_limit) /*if we got the ctcp in the seconds limit */
|
||||
{
|
||||
serv->ctcp_counter++;
|
||||
if (serv->ctcp_counter == prefs.ctcp_number_limit) /*if we reached the maximun numbers of ctcp in the seconds limits */
|
||||
{
|
||||
serv->ctcp_last_time = current_time; /*we got the flood, restore all the vars for next one */
|
||||
serv->ctcp_counter = 0;
|
||||
for (i = 0; i < 128; i++)
|
||||
if (ip[i] == '@')
|
||||
break;
|
||||
snprintf (real_ip, sizeof (real_ip), "*!*%s", &ip[i]);
|
||||
/*ignore_add (char *mask, int priv, int noti, int chan,
|
||||
int ctcp, int invi, int unignore, int no_save) */
|
||||
|
||||
snprintf (buf, sizeof (buf),
|
||||
_("You are being CTCP flooded from %s, ignoring %s\n"),
|
||||
nick, real_ip);
|
||||
PrintText (sess, buf);
|
||||
|
||||
/*FIXME: only ignore ctcp or all?, its ignoring ctcps for now */
|
||||
ignore_add (real_ip, IG_CTCP);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (serv->msg_last_time == 0)
|
||||
{
|
||||
serv->msg_last_time = time (NULL);
|
||||
serv->ctcp_counter++;
|
||||
} else
|
||||
{
|
||||
if (difftime (current_time, serv->msg_last_time) <
|
||||
prefs.msg_time_limit)
|
||||
{
|
||||
serv->msg_counter++;
|
||||
if (serv->msg_counter == prefs.msg_number_limit) /*if we reached the maximun numbers of ctcp in the seconds limits */
|
||||
{
|
||||
snprintf (buf, sizeof (buf),
|
||||
_("You are being MSG flooded from %s, setting gui_auto_open_dialog OFF.\n"),
|
||||
ip);
|
||||
PrintText (sess, buf);
|
||||
serv->msg_last_time = current_time; /*we got the flood, restore all the vars for next one */
|
||||
serv->msg_counter = 0;
|
||||
/*ignore_add (char *mask, int priv, int noti, int chan,
|
||||
int ctcp, int invi, int unignore, int no_save) */
|
||||
|
||||
if (prefs.autodialog)
|
||||
{
|
||||
/*FIXME: only ignore ctcp or all?, its ignoring ctcps for now */
|
||||
prefs.autodialog = 0;
|
||||
/* turn it back on in 30 secs */
|
||||
fe_timeout_add (30000, flood_autodialog_timeout, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
38
src/common/ignore.h
Normal file
38
src/common/ignore.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef XCHAT_IGNORE_H
|
||||
#define XCHAT_IGNORE_H
|
||||
|
||||
extern GSList *ignore_list;
|
||||
|
||||
extern int ignored_ctcp;
|
||||
extern int ignored_priv;
|
||||
extern int ignored_chan;
|
||||
extern int ignored_noti;
|
||||
extern int ignored_invi;
|
||||
|
||||
#define IG_PRIV 1
|
||||
#define IG_NOTI 2
|
||||
#define IG_CHAN 4
|
||||
#define IG_CTCP 8
|
||||
#define IG_INVI 16
|
||||
#define IG_UNIG 32
|
||||
#define IG_NOSAVE 64
|
||||
#define IG_DCC 128
|
||||
|
||||
struct ignore
|
||||
{
|
||||
char *mask;
|
||||
unsigned int type; /* one of more of IG_* ORed together */
|
||||
};
|
||||
|
||||
struct ignore *ignore_exists (char *mask);
|
||||
int ignore_add (char *mask, int type);
|
||||
void ignore_showlist (session *sess);
|
||||
int ignore_del (char *mask, struct ignore *ig);
|
||||
int ignore_check (char *mask, int type);
|
||||
void ignore_load (void);
|
||||
void ignore_save (void);
|
||||
void ignore_gui_open (void);
|
||||
void ignore_gui_update (int level);
|
||||
int flood_check (char *nick, char *ip, server *serv, session *sess, int what);
|
||||
|
||||
#endif
|
||||
1336
src/common/inbound.c
Normal file
1336
src/common/inbound.c
Normal file
File diff suppressed because it is too large
Load Diff
39
src/common/inbound.h
Normal file
39
src/common/inbound.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef XCHAT_INBOUND_H
|
||||
#define XCHAT_INBOUND_H
|
||||
|
||||
void inbound_next_nick (session *sess, char *nick);
|
||||
void inbound_uback (server *serv);
|
||||
void inbound_uaway (server *serv);
|
||||
void inbound_part (server *serv, char *chan, char *user, char *ip, char *reason);
|
||||
void inbound_upart (server *serv, char *chan, char *ip, char *reason);
|
||||
void inbound_ukick (server *serv, char *chan, char *kicker, char *reason);
|
||||
void inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason);
|
||||
void inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id);
|
||||
void inbound_quit (server *serv, char *nick, char *ip, char *reason);
|
||||
void inbound_topicnew (server *serv, char *nick, char *chan, char *topic);
|
||||
void inbound_join (server *serv, char *chan, char *user, char *ip);
|
||||
void inbound_ujoin (server *serv, char *chan, char *nick, char *ip);
|
||||
void inbound_topictime (server *serv, char *chan, char *nick, time_t stamp);
|
||||
void inbound_topic (server *serv, char *chan, char *topic_text);
|
||||
void inbound_user_info_start (session *sess, char *nick);
|
||||
void inbound_user_info (session *sess, char *chan, char *user, char *host, char *servname, char *nick, char *realname, unsigned int away);
|
||||
void inbound_foundip (session *sess, char *ip);
|
||||
int inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, char *banner, int is_exemption);
|
||||
void inbound_ping_reply (session *sess, char *timestring, char *from);
|
||||
void inbound_nameslist (server *serv, char *chan, char *names);
|
||||
int inbound_nameslist_end (server *serv, char *chan);
|
||||
void inbound_away (server *serv, char *nick, char *msg);
|
||||
void inbound_login_start (session *sess, char *nick, char *servname);
|
||||
void inbound_login_end (session *sess, char *text);
|
||||
void inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text, char fromme, int id);
|
||||
void clear_channel (session *sess);
|
||||
void set_topic (session *sess, char *topic, char *stripped_topic);
|
||||
void inbound_privmsg (server *serv, char *from, char *ip, char *text, int id);
|
||||
void inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int fromme, int id);
|
||||
void inbound_newnick (server *serv, char *nick, char *newnick, int quiet);
|
||||
void do_dns (session *sess, char *nick, char *host);
|
||||
void inbound_identified (server *serv);
|
||||
gboolean alert_match_word (char *word, char *masks);
|
||||
gboolean alert_match_text (char *text, char *masks);
|
||||
|
||||
#endif
|
||||
43
src/common/inet.h
Normal file
43
src/common/inet.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* include stuff for internet */
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
#ifdef WANTSOCKET
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef WANTARPA
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef WANTDNS
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#define closesocket close
|
||||
#define set_blocking(sok) fcntl(sok, F_SETFL, 0)
|
||||
#define set_nonblocking(sok) fcntl(sok, F_SETFL, O_NONBLOCK)
|
||||
#define would_block() (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
#define sock_error() (errno)
|
||||
|
||||
#else
|
||||
|
||||
#ifdef USE_IPV6
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <tpipv6.h>
|
||||
#else
|
||||
#include <winsock.h>
|
||||
#endif
|
||||
|
||||
#define set_blocking(sok) { \
|
||||
unsigned long zero = 0; \
|
||||
ioctlsocket (sok, FIONBIO, &zero); \
|
||||
}
|
||||
#define set_nonblocking(sok) { \
|
||||
unsigned long one = 1; \
|
||||
ioctlsocket (sok, FIONBIO, &one); \
|
||||
}
|
||||
#define would_block() (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
#define sock_error WSAGetLastError
|
||||
|
||||
#endif
|
||||
58
src/common/make-te.c
Normal file
58
src/common/make-te.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char name[512];
|
||||
char num[512];
|
||||
char help[512];
|
||||
char def[512];
|
||||
char args[512];
|
||||
char buf[512];
|
||||
char *defines[512];
|
||||
int i = 0, max;
|
||||
|
||||
printf("/* this file is auto generated, edit textevents.in instead! */\n\nconst struct text_event te[] = {\n");
|
||||
while(fgets(name, sizeof(name), stdin))
|
||||
{
|
||||
name[strlen(name)-1] = 0;
|
||||
fgets(num, sizeof(num), stdin);
|
||||
num[strlen(num)-1] = 0;
|
||||
fgets(help, sizeof(help), stdin);
|
||||
help[strlen(help)-1] = 0;
|
||||
fgets(def, sizeof(def), stdin);
|
||||
def[strlen(def)-1] = 0;
|
||||
fgets(args, sizeof(args), stdin);
|
||||
args[strlen(args)-1] = 0;
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
|
||||
if (args[0] == 'n')
|
||||
printf("\n{\"%s\", %s, %d, \n\"%s\"},\n",
|
||||
name, help, atoi(args+1) | 128, def);
|
||||
else
|
||||
printf("\n{\"%s\", %s, %d, \nN_(\"%s\")},\n",
|
||||
name, help, atoi(args), def);
|
||||
defines[i] = strdup (num);
|
||||
i++;
|
||||
}
|
||||
|
||||
printf("};\n");
|
||||
|
||||
fprintf(stderr, "/* this file is auto generated, edit textevents.in instead! */\n\nenum\n{\n");
|
||||
max = i;
|
||||
i = 0;
|
||||
while (i < max)
|
||||
{
|
||||
if (i + 1 < max)
|
||||
{
|
||||
fprintf(stderr, "\t%s,\t\t%s,\n", defines[i], defines[i+1]);
|
||||
i++;
|
||||
} else
|
||||
fprintf(stderr, "\t%s,\n", defines[i]);
|
||||
i++;
|
||||
}
|
||||
fprintf(stderr, "\tNUM_XP\n};\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
836
src/common/modes.c
Normal file
836
src/common/modes.c
Normal file
@@ -0,0 +1,836 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
#include "xchat.h"
|
||||
#include "xchatc.h"
|
||||
#include "modes.h"
|
||||
#include "server.h"
|
||||
#include "text.h"
|
||||
#include "fe.h"
|
||||
#include "util.h"
|
||||
#include "inbound.h"
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
server *serv;
|
||||
char *op;
|
||||
char *deop;
|
||||
char *voice;
|
||||
char *devoice;
|
||||
} mode_run;
|
||||
|
||||
static int is_prefix_char (server * serv, char c);
|
||||
static void record_chan_mode (session *sess, char sign, char mode, char *arg);
|
||||
static char *mode_cat (char *str, char *addition);
|
||||
static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick, char *chan, char *arg, int quiet, int is_324);
|
||||
static int mode_has_arg (server *serv, char sign, char mode);
|
||||
static void mode_print_grouped (session *sess, char *nick, mode_run *mr);
|
||||
static int mode_chanmode_type (server * serv, char mode);
|
||||
|
||||
|
||||
/* word[] - list of nicks.
|
||||
wpos - index into word[]. Where nicks really start.
|
||||
end - index into word[]. Last entry plus one.
|
||||
sign - a char, e.g. '+' or '-'
|
||||
mode - a mode, e.g. 'o' or 'v' */
|
||||
void
|
||||
send_channel_modes (session *sess, char *tbuf, char *word[], int wpos,
|
||||
int end, char sign, char mode, int modes_per_line)
|
||||
{
|
||||
int usable_modes, orig_len, len, wlen, i, max;
|
||||
server *serv = sess->server;
|
||||
|
||||
/* sanity check. IRC RFC says three per line. */
|
||||
if (serv->modes_per_line < 3)
|
||||
serv->modes_per_line = 3;
|
||||
if (modes_per_line < 1)
|
||||
modes_per_line = serv->modes_per_line;
|
||||
|
||||
/* RFC max, minus length of "MODE %s " and "\r\n" and 1 +/- sign */
|
||||
/* 512 - 6 - 2 - 1 - strlen(chan) */
|
||||
max = 503 - strlen (sess->channel);
|
||||
|
||||
while (wpos < end)
|
||||
{
|
||||
tbuf[0] = '\0';
|
||||
orig_len = len = 0;
|
||||
|
||||
/* we'll need this many modechars too */
|
||||
len += modes_per_line;
|
||||
|
||||
/* how many can we fit? */
|
||||
for (i = 0; i < modes_per_line; i++)
|
||||
{
|
||||
/* no more nicks left? */
|
||||
if (wpos + i >= end)
|
||||
break;
|
||||
wlen = strlen (word[wpos + i]) + 1;
|
||||
if (wlen + len > max)
|
||||
break;
|
||||
len += wlen; /* length of our whole string so far */
|
||||
}
|
||||
if (i < 1)
|
||||
return;
|
||||
usable_modes = i; /* this is how many we'll send on this line */
|
||||
|
||||
/* add the +/-modemodemodemode */
|
||||
len = orig_len;
|
||||
tbuf[len] = sign;
|
||||
len++;
|
||||
for (i = 0; i < usable_modes; i++)
|
||||
{
|
||||
tbuf[len] = mode;
|
||||
len++;
|
||||
}
|
||||
tbuf[len] = 0; /* null terminate for the strcat() to work */
|
||||
|
||||
/* add all the nicknames */
|
||||
for (i = 0; i < usable_modes; i++)
|
||||
{
|
||||
strcat (tbuf, " ");
|
||||
strcat (tbuf, word[wpos + i]);
|
||||
}
|
||||
serv->p_mode (serv, sess->channel, tbuf);
|
||||
|
||||
wpos += usable_modes;
|
||||
}
|
||||
}
|
||||
|
||||
/* does 'chan' have a valid prefix? e.g. # or & */
|
||||
|
||||
int
|
||||
is_channel (server * serv, char *chan)
|
||||
{
|
||||
if (strchr (serv->chantypes, chan[0]))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* is the given char a valid nick mode char? e.g. @ or + */
|
||||
|
||||
static int
|
||||
is_prefix_char (server * serv, char c)
|
||||
{
|
||||
int pos = 0;
|
||||
char *np = serv->nick_prefixes;
|
||||
|
||||
while (np[0])
|
||||
{
|
||||
if (np[0] == c)
|
||||
return pos;
|
||||
pos++;
|
||||
np++;
|
||||
}
|
||||
|
||||
if (serv->bad_prefix)
|
||||
{
|
||||
if (strchr (serv->bad_nick_prefixes, c))
|
||||
/* valid prefix char, but mode unknown */
|
||||
return -2;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* returns '@' for ops etc... */
|
||||
|
||||
char
|
||||
get_nick_prefix (server * serv, unsigned int access)
|
||||
{
|
||||
int pos;
|
||||
char c;
|
||||
|
||||
for (pos = 0; pos < USERACCESS_SIZE; pos++)
|
||||
{
|
||||
c = serv->nick_prefixes[pos];
|
||||
if (c == 0)
|
||||
break;
|
||||
if (access & (1 << pos))
|
||||
return c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns the access bitfield for a nickname. E.g.
|
||||
@nick would return 000010 in binary
|
||||
%nick would return 000100 in binary
|
||||
+nick would return 001000 in binary */
|
||||
|
||||
unsigned int
|
||||
nick_access (server * serv, char *nick, int *modechars)
|
||||
{
|
||||
int i;
|
||||
unsigned int access = 0;
|
||||
char *orig = nick;
|
||||
|
||||
while (*nick)
|
||||
{
|
||||
i = is_prefix_char (serv, *nick);
|
||||
if (i == -1)
|
||||
break;
|
||||
|
||||
/* -2 == valid prefix char, but mode unknown */
|
||||
if (i != -2)
|
||||
access |= (1 << i);
|
||||
|
||||
nick++;
|
||||
}
|
||||
|
||||
*modechars = nick - orig;
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
/* returns the access number for a particular mode. e.g.
|
||||
mode 'a' returns 0
|
||||
mode 'o' returns 1
|
||||
mode 'h' returns 2
|
||||
mode 'v' returns 3
|
||||
Also puts the nick-prefix-char in 'prefix' */
|
||||
|
||||
int
|
||||
mode_access (server * serv, char mode, char *prefix)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
while (serv->nick_modes[pos])
|
||||
{
|
||||
if (serv->nick_modes[pos] == mode)
|
||||
{
|
||||
*prefix = serv->nick_prefixes[pos];
|
||||
return pos;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
*prefix = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
record_chan_mode (session *sess, char sign, char mode, char *arg)
|
||||
{
|
||||
/* Somebody needed to acutally update sess->current_modes, needed to
|
||||
play nice with bouncers, and less mode calls. Also keeps modes up
|
||||
to date for scripts */
|
||||
server *serv = sess->server;
|
||||
GString *current = g_string_new(sess->current_modes);
|
||||
gint mode_pos = -1;
|
||||
gchar *current_char = current->str;
|
||||
gint modes_length;
|
||||
gint argument_num = 0;
|
||||
gint argument_offset = 0;
|
||||
gint argument_length = 0;
|
||||
int i = 0;
|
||||
gchar *arguments_start;
|
||||
|
||||
/* find out if the mode currently exists */
|
||||
arguments_start = g_strstr_len(current->str , -1, " ");
|
||||
if (arguments_start) {
|
||||
modes_length = arguments_start - current->str;
|
||||
}
|
||||
else {
|
||||
modes_length = current->len;
|
||||
/* set this to the end of the modes */
|
||||
arguments_start = current->str + current->len;
|
||||
}
|
||||
|
||||
while (mode_pos == -1 && i < modes_length)
|
||||
{
|
||||
if (*current_char == mode)
|
||||
{
|
||||
mode_pos = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
current_char++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if the mode currently exists and has an arg, need to know where
|
||||
* (including leading space) */
|
||||
if (mode_pos != -1 && mode_has_arg(serv, '+', mode))
|
||||
{
|
||||
current_char = current->str;
|
||||
|
||||
i = 0;
|
||||
while (i <= mode_pos)
|
||||
{
|
||||
if (mode_has_arg(serv, '+', *current_char))
|
||||
argument_num++;
|
||||
current_char++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* check through arguments for where to start */
|
||||
current_char = arguments_start;
|
||||
i = 0;
|
||||
while (i < argument_num && *current_char != '\0')
|
||||
{
|
||||
if (*current_char == ' ')
|
||||
i++;
|
||||
if (i != argument_num)
|
||||
current_char++;
|
||||
}
|
||||
argument_offset = current_char - current->str;
|
||||
|
||||
/* how long the existing argument is for this key
|
||||
* important for malloc and strncpy */
|
||||
if (i == argument_num)
|
||||
{
|
||||
argument_length++;
|
||||
current_char++;
|
||||
while (*current_char != '\0' && *current_char != ' ')
|
||||
{
|
||||
argument_length++;
|
||||
current_char++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* two cases, adding and removing a mode, handled differently */
|
||||
if (sign == '+')
|
||||
{
|
||||
if (mode_pos != -1)
|
||||
{
|
||||
/* if it already exists, only need to do something (change)
|
||||
* if there should be a param */
|
||||
if (mode_has_arg(serv, sign, mode))
|
||||
{
|
||||
/* leave the old space there */
|
||||
current = g_string_erase(current, argument_offset+1, argument_length-1);
|
||||
current = g_string_insert(current, argument_offset+1, arg);
|
||||
|
||||
free(sess->current_modes);
|
||||
sess->current_modes = g_string_free(current, FALSE);
|
||||
}
|
||||
}
|
||||
/* mode wasn't there before */
|
||||
else
|
||||
{
|
||||
/* insert the new mode character */
|
||||
current = g_string_insert_c(current, modes_length, mode);
|
||||
|
||||
/* add the argument, with space if there is one */
|
||||
if (mode_has_arg(serv, sign, mode))
|
||||
{
|
||||
current = g_string_append_c(current, ' ');
|
||||
current = g_string_append(current, arg);
|
||||
}
|
||||
|
||||
free(sess->current_modes);
|
||||
sess->current_modes = g_string_free(current, FALSE);
|
||||
}
|
||||
}
|
||||
else if (sign == '-' && mode_pos != -1)
|
||||
{
|
||||
/* remove the argument first if it has one*/
|
||||
if (mode_has_arg(serv, '+', mode))
|
||||
current = g_string_erase(current, argument_offset, argument_length);
|
||||
|
||||
/* remove the mode character */
|
||||
current = g_string_erase(current, mode_pos, 1);
|
||||
|
||||
free(sess->current_modes);
|
||||
sess->current_modes = g_string_free(current, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
mode_cat (char *str, char *addition)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (str)
|
||||
{
|
||||
len = strlen (str) + strlen (addition) + 2;
|
||||
str = realloc (str, len);
|
||||
strcat (str, " ");
|
||||
strcat (str, addition);
|
||||
} else
|
||||
{
|
||||
str = strdup (addition);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/* handle one mode, e.g.
|
||||
handle_single_mode (mr,'+','b',"elite","#warez","banneduser",) */
|
||||
|
||||
static void
|
||||
handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
|
||||
char *chan, char *arg, int quiet, int is_324)
|
||||
{
|
||||
session *sess;
|
||||
server *serv = mr->serv;
|
||||
char outbuf[4];
|
||||
|
||||
outbuf[0] = sign;
|
||||
outbuf[1] = 0;
|
||||
outbuf[2] = mode;
|
||||
outbuf[3] = 0;
|
||||
|
||||
sess = find_channel (serv, chan);
|
||||
if (!sess || !is_channel (serv, chan))
|
||||
{
|
||||
/* got modes for a chan we're not in! probably nickmode +isw etc */
|
||||
sess = serv->front_session;
|
||||
goto genmode;
|
||||
}
|
||||
|
||||
/* is this a nick mode? */
|
||||
if (strchr (serv->nick_modes, mode))
|
||||
{
|
||||
/* update the user in the userlist */
|
||||
userlist_update_mode (sess, /*nickname */ arg, mode, sign);
|
||||
} else
|
||||
{
|
||||
if (!is_324 && !sess->ignore_mode && mode_chanmode_type(serv, mode) >= 1)
|
||||
record_chan_mode (sess, sign, mode, arg);
|
||||
}
|
||||
|
||||
switch (sign)
|
||||
{
|
||||
case '+':
|
||||
switch (mode)
|
||||
{
|
||||
case 'k':
|
||||
safe_strcpy (sess->channelkey, arg, sizeof (sess->channelkey));
|
||||
fe_update_channel_key (sess);
|
||||
fe_update_mode_buttons (sess, mode, sign);
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANSETKEY, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'l':
|
||||
sess->limit = atoi (arg);
|
||||
fe_update_channel_limit (sess);
|
||||
fe_update_mode_buttons (sess, mode, sign);
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'o':
|
||||
if (!quiet)
|
||||
mr->op = mode_cat (mr->op, arg);
|
||||
return;
|
||||
case 'h':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'v':
|
||||
if (!quiet)
|
||||
mr->voice = mode_cat (mr->voice, arg);
|
||||
return;
|
||||
case 'b':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'e':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANEXEMPT, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'I':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
switch (mode)
|
||||
{
|
||||
case 'k':
|
||||
sess->channelkey[0] = 0;
|
||||
fe_update_channel_key (sess);
|
||||
fe_update_mode_buttons (sess, mode, sign);
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANRMKEY, sess, nick, NULL, NULL, NULL, 0);
|
||||
return;
|
||||
case 'l':
|
||||
sess->limit = 0;
|
||||
fe_update_channel_limit (sess);
|
||||
fe_update_mode_buttons (sess, mode, sign);
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL, NULL, 0);
|
||||
return;
|
||||
case 'o':
|
||||
if (!quiet)
|
||||
mr->deop = mode_cat (mr->deop, arg);
|
||||
return;
|
||||
case 'h':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANDEHOP, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'v':
|
||||
if (!quiet)
|
||||
mr->devoice = mode_cat (mr->devoice, arg);
|
||||
return;
|
||||
case 'b':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'e':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
case 'I':
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_CHANRMINVITE, sess, nick, arg, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fe_update_mode_buttons (sess, mode, sign);
|
||||
|
||||
genmode:
|
||||
/* Received umode +e. If we're waiting to send JOIN then send now! */
|
||||
if (mode == 'e' && sign == '+' && !serv->p_cmp (chan, serv->nick))
|
||||
inbound_identified (serv);
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
if (*arg)
|
||||
{
|
||||
char *buf = malloc (strlen (chan) + strlen (arg) + 2);
|
||||
sprintf (buf, "%s %s", chan, arg);
|
||||
EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, buf, 0);
|
||||
free (buf);
|
||||
} else
|
||||
EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, chan, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* does this mode have an arg? like +b +l +o */
|
||||
|
||||
static int
|
||||
mode_has_arg (server * serv, char sign, char mode)
|
||||
{
|
||||
int type;
|
||||
|
||||
/* if it's a nickmode, it must have an arg */
|
||||
if (strchr (serv->nick_modes, mode))
|
||||
return 1;
|
||||
|
||||
type = mode_chanmode_type (serv, mode);
|
||||
switch (type)
|
||||
{
|
||||
case 0: /* type A */
|
||||
case 1: /* type B */
|
||||
return 1;
|
||||
case 2: /* type C */
|
||||
if (sign == '+')
|
||||
return 1;
|
||||
case 3: /* type D */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* what type of chanmode is it? -1 for not in chanmode */
|
||||
static int
|
||||
mode_chanmode_type (server * serv, char mode)
|
||||
{
|
||||
/* see what numeric 005 CHANMODES=xxx said */
|
||||
char *cm = serv->chanmodes;
|
||||
int type = 0;
|
||||
int found = 0;
|
||||
|
||||
while (*cm && !found)
|
||||
{
|
||||
if (*cm == ',')
|
||||
{
|
||||
type++;
|
||||
} else if (*cm == mode)
|
||||
{
|
||||
found = 1;
|
||||
}
|
||||
cm++;
|
||||
}
|
||||
if (found)
|
||||
return type;
|
||||
/* not found? -1 */
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
mode_print_grouped (session *sess, char *nick, mode_run *mr)
|
||||
{
|
||||
/* print all the grouped Op/Deops */
|
||||
if (mr->op)
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0);
|
||||
free (mr->op);
|
||||
mr->op = NULL;
|
||||
}
|
||||
|
||||
if (mr->deop)
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL, 0);
|
||||
free (mr->deop);
|
||||
mr->deop = NULL;
|
||||
}
|
||||
|
||||
if (mr->voice)
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL, 0);
|
||||
free (mr->voice);
|
||||
mr->voice = NULL;
|
||||
}
|
||||
|
||||
if (mr->devoice)
|
||||
{
|
||||
EMIT_SIGNAL (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL, NULL, 0);
|
||||
free (mr->devoice);
|
||||
mr->devoice = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* handle a MODE or numeric 324 from server */
|
||||
|
||||
void
|
||||
handle_mode (server * serv, char *word[], char *word_eol[],
|
||||
char *nick, int numeric_324)
|
||||
{
|
||||
session *sess;
|
||||
char *chan;
|
||||
char *modes;
|
||||
char *argstr;
|
||||
char sign;
|
||||
int len;
|
||||
int arg;
|
||||
int i, num_args;
|
||||
int num_modes;
|
||||
int offset = 3;
|
||||
int all_modes_have_args = FALSE;
|
||||
int using_front_tab = FALSE;
|
||||
mode_run mr;
|
||||
|
||||
mr.serv = serv;
|
||||
mr.op = mr.deop = mr.voice = mr.devoice = NULL;
|
||||
|
||||
/* numeric 324 has everything 1 word later (as opposed to MODE) */
|
||||
if (numeric_324)
|
||||
offset++;
|
||||
|
||||
chan = word[offset];
|
||||
modes = word[offset + 1];
|
||||
if (*modes == ':')
|
||||
modes++;
|
||||
|
||||
if (*modes == 0)
|
||||
return; /* beyondirc's blank modes */
|
||||
|
||||
sess = find_channel (serv, chan);
|
||||
if (!sess)
|
||||
{
|
||||
sess = serv->front_session;
|
||||
using_front_tab = TRUE;
|
||||
}
|
||||
/* remove trailing space */
|
||||
len = strlen (word_eol[offset]) - 1;
|
||||
if (word_eol[offset][len] == ' ')
|
||||
word_eol[offset][len] = 0;
|
||||
|
||||
if (prefs.raw_modes && !numeric_324)
|
||||
EMIT_SIGNAL (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0);
|
||||
|
||||
if (numeric_324 && !using_front_tab)
|
||||
{
|
||||
if (sess->current_modes)
|
||||
free (sess->current_modes);
|
||||
sess->current_modes = strdup (word_eol[offset+1]);
|
||||
}
|
||||
|
||||
sign = *modes;
|
||||
modes++;
|
||||
arg = 1;
|
||||
|
||||
/* count the number of arguments (e.g. after the -o+v) */
|
||||
num_args = 0;
|
||||
i = 1;
|
||||
while ((i + offset + 1) < PDIWORDS)
|
||||
{
|
||||
i++;
|
||||
if (!(*word[i + offset]))
|
||||
break;
|
||||
num_args++;
|
||||
}
|
||||
|
||||
/* count the number of modes (without the -/+ chars */
|
||||
num_modes = 0;
|
||||
i = 0;
|
||||
while (i < strlen (modes))
|
||||
{
|
||||
if (modes[i] != '+' && modes[i] != '-')
|
||||
num_modes++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (num_args == num_modes)
|
||||
all_modes_have_args = TRUE;
|
||||
|
||||
while (*modes)
|
||||
{
|
||||
switch (*modes)
|
||||
{
|
||||
case '-':
|
||||
case '+':
|
||||
/* print all the grouped Op/Deops */
|
||||
mode_print_grouped (sess, nick, &mr);
|
||||
sign = *modes;
|
||||
break;
|
||||
default:
|
||||
argstr = "";
|
||||
if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args+1))
|
||||
{
|
||||
arg++;
|
||||
argstr = word[arg + offset];
|
||||
}
|
||||
handle_single_mode (&mr, sign, *modes, nick, chan,
|
||||
argstr, numeric_324 || prefs.raw_modes,
|
||||
numeric_324);
|
||||
}
|
||||
|
||||
modes++;
|
||||
}
|
||||
|
||||
/* update the title at the end, now that the mode update is internal now */
|
||||
if (!using_front_tab)
|
||||
fe_set_title (sess);
|
||||
|
||||
/* print all the grouped Op/Deops */
|
||||
mode_print_grouped (sess, nick, &mr);
|
||||
}
|
||||
|
||||
/* handle the 005 numeric */
|
||||
|
||||
void
|
||||
inbound_005 (server * serv, char *word[])
|
||||
{
|
||||
int w;
|
||||
char *pre;
|
||||
|
||||
w = 4; /* start at the 4th word */
|
||||
while (w < PDIWORDS && *word[w])
|
||||
{
|
||||
if (strncmp (word[w], "MODES=", 6) == 0)
|
||||
{
|
||||
serv->modes_per_line = atoi (word[w] + 6);
|
||||
} else if (strncmp (word[w], "CHANTYPES=", 10) == 0)
|
||||
{
|
||||
free (serv->chantypes);
|
||||
serv->chantypes = strdup (word[w] + 10);
|
||||
} else if (strncmp (word[w], "CHANMODES=", 10) == 0)
|
||||
{
|
||||
free (serv->chanmodes);
|
||||
serv->chanmodes = strdup (word[w] + 10);
|
||||
} else if (strncmp (word[w], "PREFIX=", 7) == 0)
|
||||
{
|
||||
pre = strchr (word[w] + 7, ')');
|
||||
if (pre)
|
||||
{
|
||||
pre[0] = 0; /* NULL out the ')' */
|
||||
free (serv->nick_prefixes);
|
||||
free (serv->nick_modes);
|
||||
serv->nick_prefixes = strdup (pre + 1);
|
||||
serv->nick_modes = strdup (word[w] + 8);
|
||||
} else
|
||||
{
|
||||
/* bad! some ircds don't give us the modes. */
|
||||
/* in this case, we use it only to strip /NAMES */
|
||||
serv->bad_prefix = TRUE;
|
||||
if (serv->bad_nick_prefixes)
|
||||
free (serv->bad_nick_prefixes);
|
||||
serv->bad_nick_prefixes = strdup (word[w] + 7);
|
||||
}
|
||||
} else if (strncmp (word[w], "WATCH=", 6) == 0)
|
||||
{
|
||||
serv->supports_watch = TRUE;
|
||||
} else if (strncmp (word[w], "NETWORK=", 8) == 0)
|
||||
{
|
||||
/* if (serv->networkname)
|
||||
free (serv->networkname);
|
||||
serv->networkname = strdup (word[w] + 8);*/
|
||||
|
||||
if (serv->server_session->type == SESS_SERVER)
|
||||
{
|
||||
safe_strcpy (serv->server_session->channel, word[w] + 8, CHANLEN);
|
||||
fe_set_channel (serv->server_session);
|
||||
}
|
||||
|
||||
/* use /NICKSERV */
|
||||
if (strcasecmp (word[w] + 8, "RusNet") == 0)
|
||||
serv->nickservtype = 1;
|
||||
else if (strcasecmp (word[w] + 8, "UniBG") == 0)
|
||||
serv->nickservtype = 3;
|
||||
else if (strcasecmp (word[w] + 8, "QuakeNet") == 0)
|
||||
serv->nickservtype = 4;
|
||||
|
||||
} else if (strncmp (word[w], "CASEMAPPING=", 12) == 0)
|
||||
{
|
||||
if (strcmp (word[w] + 12, "ascii") == 0) /* bahamut */
|
||||
serv->p_cmp = (void *)strcasecmp;
|
||||
} else if (strncmp (word[w], "CHARSET=", 8) == 0)
|
||||
{
|
||||
if (strcasecmp (word[w] + 8, "UTF-8") == 0)
|
||||
{
|
||||
server_set_encoding (serv, "UTF-8");
|
||||
}
|
||||
} else if (strcmp (word[w], "NAMESX") == 0)
|
||||
{
|
||||
/* 12345678901234567 */
|
||||
tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17);
|
||||
} else if (strcmp (word[w], "WHOX") == 0)
|
||||
{
|
||||
serv->have_whox = TRUE;
|
||||
} else if (strcmp (word[w], "CAPAB") == 0)
|
||||
{
|
||||
serv->have_capab = TRUE;
|
||||
/* 12345678901234567890 */
|
||||
tcp_send_len (serv, "CAPAB IDENTIFY-MSG\r\n", 20);
|
||||
/* now wait for numeric 290 */
|
||||
} else if (strcmp (word[w], "EXCEPTS") == 0)
|
||||
{
|
||||
#ifndef WIN32
|
||||
serv->have_except = TRUE;
|
||||
#endif
|
||||
} else if (strncmp (word[w], "ELIST=", 6) == 0)
|
||||
{
|
||||
/* supports LIST >< min/max user counts? */
|
||||
if (strchr (word[w] + 6, 'U') || strchr (word[w] + 6, 'u'))
|
||||
serv->use_listargs = TRUE;
|
||||
}
|
||||
|
||||
w++;
|
||||
}
|
||||
}
|
||||
12
src/common/modes.h
Normal file
12
src/common/modes.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef XCHAT_MODES_H
|
||||
#define XCHAT_MODES_H
|
||||
|
||||
int is_channel (server *serv, char *chan);
|
||||
char get_nick_prefix (server *serv, unsigned int access);
|
||||
unsigned int nick_access (server *serv, char *nick, int *modechars);
|
||||
int mode_access (server *serv, char mode, char *prefix);
|
||||
void inbound_005 (server *serv, char *word[]);
|
||||
void handle_mode (server *serv, char *word[], char *word_eol[], char *nick, int numeric_324);
|
||||
void send_channel_modes (session *sess, char *tbuf, char *word[], int start, int end, char sign, char mode, int modes_per_line);
|
||||
|
||||
#endif
|
||||
467
src/common/msproxy.c
Normal file
467
src/common/msproxy.c
Normal file
@@ -0,0 +1,467 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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
|
||||
*
|
||||
* MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
|
||||
* based on Dante source code
|
||||
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
* Inferno Nettverk A/S, Norway. All rights reserved.
|
||||
*/
|
||||
|
||||
/*#define DEBUG_MSPROXY*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define WANTSOCKET
|
||||
#define WANTARPA
|
||||
#include "inet.h"
|
||||
|
||||
#include "xchat.h"
|
||||
#include "network.h"
|
||||
#include "xchatc.h"
|
||||
#include "server.h"
|
||||
#include "msproxy.h"
|
||||
|
||||
|
||||
#ifdef USE_MSPROXY
|
||||
#include <ntlm.h>
|
||||
|
||||
static int
|
||||
send_msprequest(s, state, request, end)
|
||||
int s;
|
||||
struct msproxy_state_t *state;
|
||||
struct msproxy_request_t *request;
|
||||
char *end;
|
||||
{
|
||||
ssize_t w;
|
||||
size_t l;
|
||||
|
||||
request->magic25 = htonl(MSPROXY_VERSION);
|
||||
request->serverack = state->seq_recv;
|
||||
/* don't start incrementing sequence until we are acking packet #2. */
|
||||
request->sequence = (unsigned char)(request->serverack >= 2 ? state->seq_sent + 1 : 0);
|
||||
|
||||
memcpy(request->RWSP, "RWSP", sizeof(request->RWSP));
|
||||
|
||||
l = end - (char *)request;
|
||||
/* all requests must be atleast MSPROXY_MINLENGTH it seems. */
|
||||
if (l < MSPROXY_MINLENGTH) {
|
||||
bzero(end, (size_t)(MSPROXY_MINLENGTH - l));
|
||||
l = MSPROXY_MINLENGTH;
|
||||
}
|
||||
|
||||
if ((w = send(s, request, l, 0)) != l) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("send_msprequest(): send() failed (%d bytes sent instead of %d\n", w, l);
|
||||
perror ("Error is");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
state->seq_sent = request->sequence;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static int
|
||||
recv_mspresponse(s, state, response)
|
||||
int s;
|
||||
struct msproxy_state_t *state;
|
||||
struct msproxy_response_t *response;
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
do {
|
||||
if ((r = recv (s, response, sizeof (*response), 0)) < MSPROXY_MINLENGTH) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("recv_mspresponse(): expected to read atleast %d, read %d\n", MSPROXY_MINLENGTH, r);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if (state->seq_recv == 0)
|
||||
break; /* not started incrementing yet. */
|
||||
#ifdef DEBUG_MSPROXY
|
||||
if (response->sequence == state->seq_recv)
|
||||
printf ("seq_recv: %d, dup response, seqnumber: 0x%x\n", state->seq_recv, response->sequence);
|
||||
#endif
|
||||
} while (response->sequence == state->seq_recv);
|
||||
|
||||
state->seq_recv = response->sequence;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
|
||||
{
|
||||
struct msproxy_request_t req;
|
||||
struct msproxy_response_t res;
|
||||
char *data, *p;
|
||||
char hostname[NT_MAXNAMELEN];
|
||||
char ntdomain[NT_MAXNAMELEN];
|
||||
char challenge[8];
|
||||
netstore *ns_client;
|
||||
int clientport;
|
||||
guint32 destaddr;
|
||||
guint32 flags;
|
||||
|
||||
if (!prefs.proxy_auth || !prefs.proxy_user[0] || !prefs.proxy_pass[0] )
|
||||
return 1;
|
||||
|
||||
/* MS proxy protocol implementation currently doesn't support IPv6 */
|
||||
destaddr = net_getsockaddr_v4 (ns_proxy);
|
||||
if (!destaddr)
|
||||
return 1;
|
||||
|
||||
state->seq_recv = 0;
|
||||
state->seq_sent = 0;
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Connecting to %s:%d via MS proxy\n", serverAddr, port);
|
||||
#endif
|
||||
|
||||
gethostname (hostname, NT_MAXNAMELEN);
|
||||
p = strchr (hostname, '.');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
bzero (&req, sizeof(req));
|
||||
req.clientid = htonl(0x0a000000); /* Initial client ID is always 0x0a */
|
||||
req.command = htons(MSPROXY_HELLO); /* HELLO command */
|
||||
req.packet.hello.magic5 = htons(0x4b00); /* Fill in magic values */
|
||||
req.packet.hello.magic10 = htons(0x1400);
|
||||
req.packet.hello.magic15 = htons(0x0400);
|
||||
req.packet.hello.magic20 = htons(0x5704);
|
||||
req.packet.hello.magic25 = htons(0x0004);
|
||||
req.packet.hello.magic30 = htons(0x0100);
|
||||
req.packet.hello.magic35 = htons(0x4a02);
|
||||
req.packet.hello.magic40 = htons(0x3000);
|
||||
req.packet.hello.magic45 = htons(0x4400);
|
||||
req.packet.hello.magic50 = htons(0x3900);
|
||||
data = req.packet.hello.data;
|
||||
strcpy (data, prefs.proxy_user); /* Append a username */
|
||||
data += strlen (prefs.proxy_user)+2; /* +2 automatically creates second empty string */
|
||||
strcpy (data, MSPROXY_EXECUTABLE); /* Append an application name */
|
||||
data += strlen (MSPROXY_EXECUTABLE)+1;
|
||||
strcpy (data, hostname); /* Append a hostname */
|
||||
data += strlen (hostname)+1;
|
||||
|
||||
if (send_msprequest(sok, state, &req, data) == -1)
|
||||
return 1;
|
||||
|
||||
if (recv_mspresponse(sok, state, &res) == -1)
|
||||
return 1;
|
||||
|
||||
if (strcmp(res.RWSP, "RWSP") != 0) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Received mailformed packet (no RWSP signature)\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntohs(res.command) >> 8 != 0x10) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.command = 10??, is %x", ntohs(res.command));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
state->clientid = htonl(rand());
|
||||
state->serverid = res.serverid;
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("clientid: 0x%x, serverid: 0x%0x\n", state->clientid, state->serverid);
|
||||
printf ("packet #2\n");
|
||||
#endif
|
||||
|
||||
/* almost identical. */
|
||||
req.clientid = state->clientid;
|
||||
req.serverid = state->serverid;
|
||||
|
||||
if (send_msprequest(sok, state, &req, data) == -1)
|
||||
return 1;
|
||||
|
||||
if (recv_mspresponse(sok, state, &res) == -1)
|
||||
return 1;
|
||||
|
||||
if (res.serverid != state->serverid) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected serverid = 0x%x, is 0x%x\n",state->serverid, res.serverid);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res.sequence != 0x01) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.sequence = 0x01, is 0x%x\n", res.sequence);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntohs(res.command) != MSPROXY_USERINFO_ACK) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("packet #3\n");
|
||||
#endif
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.clientid = state->clientid;
|
||||
req.serverid = state->serverid;
|
||||
req.command = htons(MSPROXY_AUTHENTICATE);
|
||||
memcpy(req.packet.auth.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP"));
|
||||
req.packet.auth.bindaddr = htonl(0x02000000);
|
||||
req.packet.auth.msgtype = htonl(0x01000000);
|
||||
/* NTLM flags: 0x80000000 Negotiate LAN Manager key
|
||||
0x10000000 Negotiate sign
|
||||
0x04000000 Request target
|
||||
0x02000000 Negotiate OEM
|
||||
0x00800000 Always sign
|
||||
0x00020000 Negotiate NTLM
|
||||
*/
|
||||
req.packet.auth.flags = htonl(0x06020000);
|
||||
|
||||
if (send_msprequest(sok, state, &req, &req.packet.auth.data) == -1)
|
||||
return 1;
|
||||
|
||||
if (recv_mspresponse(sok, state, &res) == -1)
|
||||
return 1;
|
||||
|
||||
if (res.serverid != state->serverid) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntohs(res.command) != MSPROXY_AUTHENTICATE_ACK) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_AUTHENTICATE_ACK, ntohs(res.command));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
flags = res.packet.auth.flags & htonl(0x00020000); /* Remember if the server supports NTLM */
|
||||
memcpy(challenge, &res.packet.auth.challenge, sizeof(challenge));
|
||||
memcpy(ntdomain, &res.packet.auth.NTLMSSP[res.packet.auth.target.offset], res.packet.auth.target.len);
|
||||
ntdomain[res.packet.auth.target.len] = 0;
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("ntdomain: \"%s\"\n", ntdomain);
|
||||
printf ("packet #4\n");
|
||||
#endif
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.clientid = state->clientid;
|
||||
req.serverid = state->serverid;
|
||||
req.command = htons(MSPROXY_AUTHENTICATE_2); /* Authentication response */
|
||||
req.packet.auth2.magic3 = htons(0x0200); /* Something */
|
||||
memcpy(req.packet.auth2.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP")); /* Start of NTLM message */
|
||||
req.packet.auth2.msgtype = htonl(0x03000000); /* Message type 2 */
|
||||
req.packet.auth2.flags = flags | htonl(0x02000000); /* Choose authentication method */
|
||||
data = req.packet.auth2.data;
|
||||
if (flags) {
|
||||
req.packet.auth2.lm_resp.len = 0; /* We are here if NTLM is supported, */
|
||||
req.packet.auth2.lm_resp.alloc = 0; /* Do not fill in insecure LM response */
|
||||
req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
||||
req.packet.auth2.ntlm_resp.len = 24; /* Fill in NTLM response security buffer */
|
||||
req.packet.auth2.ntlm_resp.alloc = 24;
|
||||
req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
||||
ntlm_smb_nt_encrypt(prefs.proxy_pass, challenge, data); /* Append an NTLM response */
|
||||
data += 24;
|
||||
} else {
|
||||
req.packet.auth2.lm_resp.len = 24; /* Fill in LM response security buffer */
|
||||
req.packet.auth2.lm_resp.alloc = 24;
|
||||
req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
||||
ntlm_smb_encrypt(prefs.proxy_pass, challenge, data); /* Append an LM response */
|
||||
data += 24;
|
||||
req.packet.auth2.ntlm_resp.len = 0; /* NTLM response is empty */
|
||||
req.packet.auth2.ntlm_resp.alloc = 0;
|
||||
req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
||||
}
|
||||
req.packet.auth2.ntdomain_buf.len = strlen(ntdomain); /* Domain name */
|
||||
req.packet.auth2.ntdomain_buf.alloc = req.packet.auth2.ntdomain_buf.len;
|
||||
req.packet.auth2.ntdomain_buf.offset = data - req.packet.auth2.NTLMSSP;
|
||||
strcpy(data, ntdomain);
|
||||
data += req.packet.auth2.ntdomain_buf.len;
|
||||
req.packet.auth2.username_buf.len = strlen(prefs.proxy_user); /* Username */
|
||||
req.packet.auth2.username_buf.alloc = req.packet.auth2.username_buf.len;
|
||||
req.packet.auth2.username_buf.offset = data - req.packet.auth2.NTLMSSP;
|
||||
strcpy(data, prefs.proxy_user);
|
||||
data += req.packet.auth2.username_buf.len;
|
||||
req.packet.auth2.clienthost_buf.len = strlen(hostname); /* Hostname */
|
||||
req.packet.auth2.clienthost_buf.alloc = req.packet.auth2.clienthost_buf.len;
|
||||
req.packet.auth2.clienthost_buf.offset = data - req.packet.auth2.NTLMSSP;
|
||||
strcpy(data, hostname);
|
||||
data += req.packet.auth2.clienthost_buf.len;
|
||||
req.packet.auth2.sessionkey_buf.len = 0; /* Session key (we don't use it) */
|
||||
req.packet.auth2.sessionkey_buf.alloc = 0;
|
||||
req.packet.auth2.sessionkey_buf.offset = data - req.packet.auth2.NTLMSSP;
|
||||
|
||||
if (send_msprequest(sok, state, &req, data) == -1)
|
||||
return 1;
|
||||
|
||||
if (recv_mspresponse(sok, state, &res) == -1)
|
||||
return 1;
|
||||
|
||||
if (res.serverid != state->serverid) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res.clientack != 0x01) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.clientack = 0x01, is 0x%x\n", res.clientack);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntohs(res.command) >> 8 != 0x47) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.command = 47??, is 0x%x\n", ntohs(res.command));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntohs(res.command) == MSPROXY_AUTHENTICATE_2_NAK) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Authentication failed\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("packet #5\n");
|
||||
#endif
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.clientid = state->clientid;
|
||||
req.serverid = state->serverid;
|
||||
req.command = htons(MSPROXY_CONNECT);
|
||||
req.packet.connect.magic2 = htons(0x0200);
|
||||
req.packet.connect.magic6 = htons(0x0200);
|
||||
req.packet.connect.destport = htons(port);
|
||||
req.packet.connect.destaddr = destaddr;
|
||||
data = req.packet.connect.executable;
|
||||
strcpy(data, MSPROXY_EXECUTABLE);
|
||||
data += strlen(MSPROXY_EXECUTABLE) + 1;
|
||||
|
||||
/*
|
||||
* need to tell server what port we will connect from, so we bind our sockets.
|
||||
*/
|
||||
ns_client = net_store_new ();
|
||||
if (!bound) {
|
||||
net_store_fill_any (ns_client);
|
||||
net_bind(ns_client, csok4, csok6);
|
||||
#ifdef DEBUG_MSPROXY
|
||||
perror ("bind() result");
|
||||
#endif
|
||||
}
|
||||
clientport = net_getsockport(csok4, csok6);
|
||||
if (clientport == -1) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Unable to obtain source port\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
req.packet.connect.srcport = clientport;
|
||||
|
||||
if (send_msprequest(sok, state, &req, data) == -1)
|
||||
return 1;
|
||||
|
||||
if (recv_mspresponse(sok, state, &res) == -1)
|
||||
return 1;
|
||||
|
||||
if (ntohs(res.command) != MSPROXY_CONNECT_ACK) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("expected res.command = 0x%x, is 0x%x\n",MSPROXY_CONNECT_ACK, ntohs(res.command));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
net_store_fill_v4 (ns_client, res.packet.connect.clientaddr, res.packet.connect.clientport);
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Connecting...\n");
|
||||
#endif
|
||||
if (net_connect (ns_client, csok4, csok6, csok) != 0) {
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("Failed to connect to port %d\n", htons(res.packet.connect.clientport));
|
||||
#endif
|
||||
net_store_destroy (ns_client);
|
||||
return 1;
|
||||
}
|
||||
net_store_destroy (ns_client);
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("packet #6\n");
|
||||
#endif
|
||||
|
||||
req.clientid = state->clientid;
|
||||
req.serverid = state->serverid;
|
||||
req.command = htons(MSPROXY_USERINFO_ACK);
|
||||
|
||||
if (send_msprequest(sok, state, &req, req.packet.connack.data) == -1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
msproxy_keepalive (void)
|
||||
{
|
||||
server *serv;
|
||||
GSList *list = serv_list;
|
||||
struct msproxy_request_t req;
|
||||
struct msproxy_response_t res;
|
||||
|
||||
while (list)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->connected && (serv->proxy_sok != -1))
|
||||
{
|
||||
#ifdef DEBUG_MSPROXY
|
||||
printf ("sending MS proxy keepalive packet\n");
|
||||
#endif
|
||||
|
||||
bzero(&req, sizeof(req));
|
||||
req.clientid = serv->msp_state.clientid;
|
||||
req.serverid = serv->msp_state.serverid;
|
||||
req.command = htons(MSPROXY_HELLO);
|
||||
|
||||
if (send_msprequest(serv->proxy_sok, &serv->msp_state, &req, req.packet.hello.data) == -1)
|
||||
continue;
|
||||
|
||||
recv_mspresponse(serv->proxy_sok, &serv->msp_state, &res);
|
||||
|
||||
#ifdef DEBUG_MSPROXY
|
||||
if (ntohs(res.command) != MSPROXY_USERINFO_ACK)
|
||||
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
|
||||
#endif
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
257
src/common/msproxy.h
Normal file
257
src/common/msproxy.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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
|
||||
*
|
||||
* MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
|
||||
* based on Dante source code
|
||||
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
* Inferno Nettverk A/S, Norway. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "network.h"
|
||||
|
||||
#define MSPROXY_EXECUTABLE "xchat.exe" /* This probably can be used for access control on the server side */
|
||||
|
||||
#define MSPROXY_MINLENGTH 172 /* minimum length of packet. */
|
||||
#define NT_MAXNAMELEN 17 /* maximum name length (domain etc), comes from NetBIOS */
|
||||
#define MSPROXY_VERSION 0x00010200 /* MS Proxy v2 ? */
|
||||
|
||||
/* Commands / responses */
|
||||
#define MSPROXY_HELLO 0x0500 /* packet 1 from client. */
|
||||
#define MSPROXY_HELLO_ACK 0x1000 /* packet 1 from server. */
|
||||
|
||||
#define MSPROXY_USERINFO_ACK 0x0400 /* packet 2 from server. */
|
||||
|
||||
#define MSPROXY_AUTHENTICATE 0x4700 /* authentication request */
|
||||
#define MSPROXY_AUTHENTICATE_ACK 0x4714 /* authentication challenge */
|
||||
|
||||
#define MSPROXY_AUTHENTICATE_2 0x4701 /* authentication response */
|
||||
#define MSPROXY_AUTHENTICATE_2_ACK 0x4715 /* authentication passed */
|
||||
#define MSPROXY_AUTHENTICATE_2_NAK 0x4716 /* authentication failure */
|
||||
|
||||
#define MSPROXY_CONNECT 0x071e /* connect request. */
|
||||
#define MSPROXY_CONNECT_ACK 0x0703 /* connect request accepted. */
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct ntlm_buffer {
|
||||
guint16 len;
|
||||
guint16 alloc;
|
||||
guint32 offset;
|
||||
};
|
||||
|
||||
struct msproxy_request_t {
|
||||
guint32 clientid; /* 1-4 */
|
||||
guint32 magic25; /* 5-8 */
|
||||
guint32 serverid; /* 9-12 */
|
||||
unsigned char serverack; /* 13: ack of last server packet */
|
||||
char pad10[3]; /* 14-16 */
|
||||
unsigned char sequence; /* 17: sequence # of this packet. */
|
||||
char pad11[7]; /* 18-24 */
|
||||
char RWSP[4]; /* 25-28: 0x52,0x57,0x53,0x50 */
|
||||
char pad15[8]; /* 29-36 */
|
||||
guint16 command; /* 37-38 */
|
||||
|
||||
/* packet specifics start at 39. */
|
||||
union {
|
||||
struct {
|
||||
char pad1[18]; /* 39-56 */
|
||||
guint16 magic3; /* 57-58 */
|
||||
char pad3[114]; /* 59-172 */
|
||||
guint16 magic5; /* 173-174: 0x4b, 0x00 */
|
||||
char pad5[2]; /* 175-176 */
|
||||
guint16 magic10; /* 177-178: 0x14, 0x00 */
|
||||
char pad6[2]; /* 179-180 */
|
||||
guint16 magic15; /* 181-182: 0x04, 0x00 */
|
||||
char pad10[2]; /* 183-184 */
|
||||
guint16 magic16; /* 185-186 */
|
||||
char pad11[2]; /* 187-188 */
|
||||
guint16 magic20; /* 189-190: 0x57, 0x04 */
|
||||
guint16 magic25; /* 191-192: 0x00, 0x04 */
|
||||
guint16 magic30; /* 193-194: 0x01, 0x00 */
|
||||
char pad20[2]; /* 195-196: 0x4a, 0x02 */
|
||||
guint16 magic35; /* 197-198: 0x4a, 0x02 */
|
||||
char pad30[10]; /* 199-208 */
|
||||
guint16 magic40; /* 209-210: 0x30, 0x00 */
|
||||
char pad40[2]; /* 211-212 */
|
||||
guint16 magic45; /* 213-214: 0x44, 0x00 */
|
||||
char pad45[2]; /* 215-216 */
|
||||
guint16 magic50; /* 217-218: 0x39, 0x00 */
|
||||
char pad50[2]; /* 219-220 */
|
||||
char data[256]; /* 221-EOP: a sequence of NULL-terminated strings:
|
||||
- username;
|
||||
- empty string (just a NULL);
|
||||
- application name;
|
||||
- hostname */
|
||||
} hello;
|
||||
|
||||
struct {
|
||||
char pad1[4]; /* 39-42 */
|
||||
guint16 magic2; /* 43-44 */
|
||||
char pad10[12]; /* 45-56 */
|
||||
guint32 bindaddr; /* 57-60: address to bind. */
|
||||
guint16 bindport; /* 61-62: port to bind. */
|
||||
char pad15[2]; /* 63-64 */
|
||||
guint16 magic3; /* 65-66 */
|
||||
guint16 boundport; /* 67-68 */
|
||||
char pad20[104]; /* 69-172 */
|
||||
char NTLMSSP[sizeof("NTLMSSP")]; /* 173-180: "NTLMSSP" */
|
||||
guint32 msgtype; /* 181-184: NTLM message type = 1 */
|
||||
guint32 flags; /* 185-188: NTLM message flags */
|
||||
guint16 magic20; /* 189-190: 0x28, 0x00 */
|
||||
char pad30[2]; /* 191-192 */
|
||||
guint16 magic25; /* 193-194: 0x96, 0x82 */
|
||||
guint16 magic30; /* 195-196: 0x01, 0x00 */
|
||||
char pad40[12]; /* 197-208 */
|
||||
guint16 magic50; /* 209-210: 0x30, 0x00 */
|
||||
char pad50[6]; /* 211-216 */
|
||||
guint16 magic55; /* 217-218: 0x30, 0x00 */
|
||||
char pad55[2]; /* 219-220 */
|
||||
char data[0]; /* Dummy end marker, no real data required */
|
||||
} auth;
|
||||
|
||||
struct {
|
||||
char pad1[4]; /* 39-42 */
|
||||
guint16 magic1; /* 43-44 */
|
||||
guint32 magic2; /* 45-48 */
|
||||
char pad2[8]; /* 49-56 */
|
||||
guint16 magic3; /* 57-58 */
|
||||
char pad3[6]; /* 59-64 */
|
||||
guint16 magic4; /* 65-66 */
|
||||
guint16 boundport; /* 67-68 */
|
||||
char pad4[104]; /* 69-172 */
|
||||
char NTLMSSP[sizeof("NTLMSSP")]; /* 173-180: "NTLMSSP" */
|
||||
guint32 msgtype; /* 181-184: NTLM message type = 3 */
|
||||
struct ntlm_buffer lm_resp; /* 185-192: LM response security buffer */
|
||||
struct ntlm_buffer ntlm_resp; /* 193-200: NTLM response security buffer */
|
||||
struct ntlm_buffer ntdomain_buf; /* 201-208: domain name security buffer */
|
||||
struct ntlm_buffer username_buf; /* 209-216: username security buffer */
|
||||
struct ntlm_buffer clienthost_buf; /* 217-224: hostname security buffer */
|
||||
struct ntlm_buffer sessionkey_buf; /* 225-232: session key security buffer */
|
||||
guint32 flags; /* 233-236: message flags */
|
||||
char data[1024]; /* 237-EOP: data area */
|
||||
} auth2;
|
||||
|
||||
struct {
|
||||
guint16 magic1; /* 39-40 */
|
||||
char pad1[2]; /* 41-42 */
|
||||
guint16 magic2; /* 43-44 */
|
||||
guint32 magic3; /* 45-48 */
|
||||
char pad5[8]; /* 48-56 */
|
||||
guint16 magic6; /* 57-58: 0x0200 */
|
||||
guint16 destport; /* 59-60 */
|
||||
guint32 destaddr; /* 61-64 */
|
||||
char pad10[4]; /* 65-68 */
|
||||
guint16 magic10; /* 69-70 */
|
||||
char pad15[2]; /* 71-72 */
|
||||
guint16 srcport; /* 73-74: port client connects from */
|
||||
char pad20[82]; /* 75-156 */
|
||||
char executable[256]; /* 76-EOP: application name */
|
||||
} connect;
|
||||
|
||||
struct {
|
||||
guint16 magic1; /* 39-40 */
|
||||
char pad5[2]; /* 41-42 */
|
||||
guint16 magic5; /* 43-44 */
|
||||
guint32 magic10; /* 45-48 */
|
||||
char pad10[2]; /* 49-50 */
|
||||
guint16 magic15; /* 51-52 */
|
||||
guint32 magic16; /* 53-56 */
|
||||
guint16 magic20; /* 57-58 */
|
||||
guint16 clientport; /* 59-60: forwarded port. */
|
||||
guint32 clientaddr; /* 61-64: forwarded address. */
|
||||
guint32 magic30; /* 65-68 */
|
||||
guint32 magic35; /* 69-72 */
|
||||
guint16 serverport; /* 73-74: port server will connect to us from. */
|
||||
guint16 srcport; /* 75-76: connect request; port used on client behalf. */
|
||||
guint16 boundport; /* 77-78: bind request; port used on client behalf. */
|
||||
guint32 boundaddr; /* 79-82: addr used on client behalf */
|
||||
char pad30[90]; /* 83-172 */
|
||||
char data[0]; /* End marker */
|
||||
} connack;
|
||||
|
||||
} packet;
|
||||
};
|
||||
|
||||
struct msproxy_response_t {
|
||||
guint32 packetid; /* 1-4 */
|
||||
guint32 magic5; /* 5-8 */
|
||||
guint32 serverid; /* 9-12 */
|
||||
char clientack; /* 13: ack of last client packet. */
|
||||
char pad5[3]; /* 14-16 */
|
||||
unsigned char sequence; /* 17: sequence # of this packet. */
|
||||
char pad10[7]; /* 18-24 */
|
||||
char RWSP[4]; /* 25-28: 0x52,0x57,0x53,0x50 */
|
||||
char pad15[8]; /* 29-36 */
|
||||
guint16 command; /* 37-38 */
|
||||
|
||||
union {
|
||||
struct {
|
||||
char pad5[18]; /* 39-56 */
|
||||
guint16 magic20; /* 57-58: 0x02, 0x00 */
|
||||
char pad10[6]; /* 59-64 */
|
||||
guint16 magic30; /* 65-66: 0x74, 0x01 */
|
||||
char pad15[2]; /* 67-68 */
|
||||
guint16 magic35; /* 69-70: 0x0c, 0x00 */
|
||||
char pad20[6]; /* 71-76 */
|
||||
guint16 magic50; /* 77-78: 0x04, 0x00 */
|
||||
char pad30[6]; /* 79-84 */
|
||||
guint16 magic60; /* 85-86: 0x65, 0x05 */
|
||||
char pad35[2]; /* 87-88 */
|
||||
guint16 magic65; /* 89-90: 0x02, 0x00 */
|
||||
char pad40[8]; /* 91-98 */
|
||||
guint16 udpport; /* 99-100 */
|
||||
guint32 udpaddr; /* 101-104 */
|
||||
} hello;
|
||||
|
||||
struct {
|
||||
char pad1[6]; /* 39-44 */
|
||||
guint32 magic10; /* 45-48 */
|
||||
char pad3[10]; /* 49-58 */
|
||||
guint16 boundport; /* 59-60: port server bound for us. */
|
||||
guint32 boundaddr; /* 61-64: addr server bound for us. */
|
||||
char pad10[4]; /* 65-68 */
|
||||
guint16 magic15; /* 69-70 */
|
||||
char pad15[102]; /* 70-172 */
|
||||
char NTLMSSP[sizeof("NTLMSSP")]; /* 173-180: "NTLMSSP" */
|
||||
guint32 msgtype; /* 181-184: NTLM message type = 2 */
|
||||
struct ntlm_buffer target; /* 185-192: target security buffer */
|
||||
guint32 flags; /* 193-196: NTLM message flags */
|
||||
char challenge[8]; /* 197-204: NTLM challenge request */
|
||||
char context[8]; /* 205-212: NTLM context */
|
||||
char data[1024]; /* 213-EOP: target information data */
|
||||
} auth;
|
||||
|
||||
struct {
|
||||
guint16 magic1; /* 39-40 */
|
||||
char pad5[18]; /* 41-58 */
|
||||
guint16 clientport; /* 59-60: forwarded port. */
|
||||
guint32 clientaddr; /* 61-64: forwarded address. */
|
||||
guint32 magic10; /* 65-68 */
|
||||
guint32 magic15; /* 69-72 */
|
||||
guint16 serverport; /* 73-74: port server will connect to us from. */
|
||||
guint16 srcport; /* 75-76: connect request; port used on client behalf. */
|
||||
guint16 boundport; /* 77-78: bind request; port used on client behalf. */
|
||||
guint32 boundaddr; /* 79-82: addr used on client behalf */
|
||||
char pad10[90]; /* 83-172 */
|
||||
} connect;
|
||||
} packet;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
int traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound);
|
||||
void msproxy_keepalive (void);
|
||||
383
src/common/network.c
Normal file
383
src/common/network.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 2001 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
|
||||
*/
|
||||
|
||||
/* ipv4 and ipv6 networking functions with a common interface */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "../../config.h" /* grab USE_IPV6 and LOOKUPD defines */
|
||||
|
||||
#define WANTSOCKET
|
||||
#define WANTARPA
|
||||
#define WANTDNS
|
||||
#include "inet.h"
|
||||
|
||||
#define NETWORK_PRIVATE
|
||||
#include "network.h"
|
||||
|
||||
#define RAND_INT(n) ((int)(rand() / (RAND_MAX + 1.0) * (n)))
|
||||
|
||||
|
||||
/* ================== COMMON ================= */
|
||||
|
||||
static void
|
||||
net_set_socket_options (int sok)
|
||||
{
|
||||
socklen_t sw;
|
||||
|
||||
sw = 1;
|
||||
setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &sw, sizeof (sw));
|
||||
sw = 1;
|
||||
setsockopt (sok, SOL_SOCKET, SO_KEEPALIVE, (char *) &sw, sizeof (sw));
|
||||
}
|
||||
|
||||
char *
|
||||
net_ip (guint32 addr)
|
||||
{
|
||||
struct in_addr ia;
|
||||
|
||||
ia.s_addr = htonl (addr);
|
||||
return inet_ntoa (ia);
|
||||
}
|
||||
|
||||
void
|
||||
net_store_destroy (netstore * ns)
|
||||
{
|
||||
#ifdef USE_IPV6
|
||||
if (ns->ip6_hostent)
|
||||
freeaddrinfo (ns->ip6_hostent);
|
||||
#endif
|
||||
free (ns);
|
||||
}
|
||||
|
||||
netstore *
|
||||
net_store_new (void)
|
||||
{
|
||||
netstore *ns;
|
||||
|
||||
ns = malloc (sizeof (netstore));
|
||||
memset (ns, 0, sizeof (netstore));
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
#ifndef USE_IPV6
|
||||
|
||||
/* =================== IPV4 ================== */
|
||||
|
||||
/*
|
||||
A note about net_resolve and lookupd:
|
||||
|
||||
Many IRC networks rely on round-robin DNS for load balancing, rotating the list
|
||||
of IP address on each query. However, this method breaks when DNS queries are
|
||||
cached. Mac OS X and Darwin handle DNS lookups through the lookupd daemon, which
|
||||
caches queries in its default configuration: thus, if we always pick the first
|
||||
address, we will be stuck with the same host (which might be down!) until the
|
||||
TTL reaches 0 or lookupd is reset (typically, at reboot). Therefore, we need to
|
||||
pick a random address from the result list, instead of always using the first.
|
||||
*/
|
||||
|
||||
char *
|
||||
net_resolve (netstore * ns, char *hostname, int port, char **real_host)
|
||||
{
|
||||
ns->ip4_hostent = gethostbyname (hostname);
|
||||
if (!ns->ip4_hostent)
|
||||
return NULL;
|
||||
|
||||
memset (&ns->addr, 0, sizeof (ns->addr));
|
||||
#ifdef LOOKUPD
|
||||
int count = 0;
|
||||
while (ns->ip4_hostent->h_addr_list[count]) count++;
|
||||
memcpy (&ns->addr.sin_addr,
|
||||
ns->ip4_hostent->h_addr_list[RAND_INT(count)],
|
||||
ns->ip4_hostent->h_length);
|
||||
#else
|
||||
memcpy (&ns->addr.sin_addr, ns->ip4_hostent->h_addr,
|
||||
ns->ip4_hostent->h_length);
|
||||
#endif
|
||||
ns->addr.sin_port = htons (port);
|
||||
ns->addr.sin_family = AF_INET;
|
||||
|
||||
*real_host = strdup (ns->ip4_hostent->h_name);
|
||||
return strdup (inet_ntoa (ns->addr.sin_addr));
|
||||
}
|
||||
|
||||
int
|
||||
net_connect (netstore * ns, int sok4, int sok6, int *sok_return)
|
||||
{
|
||||
*sok_return = sok4;
|
||||
return connect (sok4, (struct sockaddr *) &ns->addr, sizeof (ns->addr));
|
||||
}
|
||||
|
||||
void
|
||||
net_bind (netstore * tobindto, int sok4, int sok6)
|
||||
{
|
||||
bind (sok4, (struct sockaddr *) &tobindto->addr, sizeof (tobindto->addr));
|
||||
}
|
||||
|
||||
void
|
||||
net_sockets (int *sok4, int *sok6)
|
||||
{
|
||||
*sok4 = socket (AF_INET, SOCK_STREAM, 0);
|
||||
*sok6 = -1;
|
||||
net_set_socket_options (*sok4);
|
||||
}
|
||||
|
||||
void
|
||||
udp_sockets (int *sok4, int *sok6)
|
||||
{
|
||||
*sok4 = socket (AF_INET, SOCK_DGRAM, 0);
|
||||
*sok6 = -1;
|
||||
}
|
||||
|
||||
void
|
||||
net_store_fill_any (netstore *ns)
|
||||
{
|
||||
ns->addr.sin_family = AF_INET;
|
||||
ns->addr.sin_addr.s_addr = INADDR_ANY;
|
||||
ns->addr.sin_port = 0;
|
||||
}
|
||||
|
||||
void
|
||||
net_store_fill_v4 (netstore *ns, guint32 addr, int port)
|
||||
{
|
||||
ns->addr.sin_family = AF_INET;
|
||||
ns->addr.sin_addr.s_addr = addr;
|
||||
ns->addr.sin_port = port;
|
||||
}
|
||||
|
||||
guint32
|
||||
net_getsockaddr_v4 (netstore *ns)
|
||||
{
|
||||
return ns->addr.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
int
|
||||
net_getsockport (int sok4, int sok6)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int len = sizeof (addr);
|
||||
|
||||
if (getsockname (sok4, (struct sockaddr *)&addr, &len) == -1)
|
||||
return -1;
|
||||
return addr.sin_port;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* =================== IPV6 ================== */
|
||||
|
||||
char *
|
||||
net_resolve (netstore * ns, char *hostname, int port, char **real_host)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
char ipstring[MAX_HOSTNAME];
|
||||
char portstring[MAX_HOSTNAME];
|
||||
int ret;
|
||||
|
||||
/* if (ns->ip6_hostent)
|
||||
freeaddrinfo (ns->ip6_hostent);*/
|
||||
|
||||
sprintf (portstring, "%d", port);
|
||||
|
||||
memset (&hints, 0, sizeof (struct addrinfo));
|
||||
hints.ai_family = PF_UNSPEC; /* support ipv6 and ipv4 */
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (port == 0)
|
||||
ret = getaddrinfo (hostname, NULL, &hints, &ns->ip6_hostent);
|
||||
else
|
||||
ret = getaddrinfo (hostname, portstring, &hints, &ns->ip6_hostent);
|
||||
if (ret != 0)
|
||||
return NULL;
|
||||
|
||||
#ifdef LOOKUPD /* See note about lookupd above the IPv4 version of net_resolve. */
|
||||
struct addrinfo *tmp;
|
||||
int count = 0;
|
||||
|
||||
for (tmp = ns->ip6_hostent; tmp; tmp = tmp->ai_next)
|
||||
count ++;
|
||||
|
||||
count = RAND_INT(count);
|
||||
|
||||
while (count--) ns->ip6_hostent = ns->ip6_hostent->ai_next;
|
||||
#endif
|
||||
|
||||
/* find the numeric IP number */
|
||||
ipstring[0] = 0;
|
||||
getnameinfo (ns->ip6_hostent->ai_addr, ns->ip6_hostent->ai_addrlen,
|
||||
ipstring, sizeof (ipstring), NULL, 0, NI_NUMERICHOST);
|
||||
|
||||
if (ns->ip6_hostent->ai_canonname)
|
||||
*real_host = strdup (ns->ip6_hostent->ai_canonname);
|
||||
else
|
||||
*real_host = strdup (hostname);
|
||||
|
||||
return strdup (ipstring);
|
||||
}
|
||||
|
||||
/* the only thing making this interface unclean, this shitty sok4, sok6 business */
|
||||
|
||||
int
|
||||
net_connect (netstore * ns, int sok4, int sok6, int *sok_return)
|
||||
{
|
||||
struct addrinfo *res, *res0;
|
||||
int error = -1;
|
||||
|
||||
res0 = ns->ip6_hostent;
|
||||
|
||||
for (res = res0; res; res = res->ai_next)
|
||||
{
|
||||
/* sok = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sok < 0)
|
||||
continue;*/
|
||||
switch (res->ai_family)
|
||||
{
|
||||
case AF_INET:
|
||||
error = connect (sok4, res->ai_addr, res->ai_addrlen);
|
||||
*sok_return = sok4;
|
||||
break;
|
||||
case AF_INET6:
|
||||
error = connect (sok6, res->ai_addr, res->ai_addrlen);
|
||||
*sok_return = sok6;
|
||||
break;
|
||||
default:
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (error == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
net_bind (netstore * tobindto, int sok4, int sok6)
|
||||
{
|
||||
bind (sok4, tobindto->ip6_hostent->ai_addr,
|
||||
tobindto->ip6_hostent->ai_addrlen);
|
||||
bind (sok6, tobindto->ip6_hostent->ai_addr,
|
||||
tobindto->ip6_hostent->ai_addrlen);
|
||||
}
|
||||
|
||||
void
|
||||
net_sockets (int *sok4, int *sok6)
|
||||
{
|
||||
*sok4 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
*sok6 = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||
net_set_socket_options (*sok4);
|
||||
net_set_socket_options (*sok6);
|
||||
}
|
||||
|
||||
void
|
||||
udp_sockets (int *sok4, int *sok6)
|
||||
{
|
||||
*sok4 = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
*sok6 = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
/* the following functions are used only by MSPROXY and are not
|
||||
proper ipv6 implementations - do not use in new code! */
|
||||
|
||||
void
|
||||
net_store_fill_any (netstore *ns)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
ai = ns->ip6_hostent;
|
||||
if (!ai) {
|
||||
ai = malloc (sizeof (struct addrinfo));
|
||||
memset (ai, 0, sizeof (struct addrinfo));
|
||||
ns->ip6_hostent = ai;
|
||||
}
|
||||
sin = (struct sockaddr_in *)ai->ai_addr;
|
||||
if (!sin) {
|
||||
sin = malloc (sizeof (struct sockaddr_in));
|
||||
memset (sin, 0, sizeof (struct sockaddr_in));
|
||||
ai->ai_addr = (struct sockaddr *)sin;
|
||||
}
|
||||
ai->ai_family = AF_INET;
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_in);
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = INADDR_ANY;
|
||||
sin->sin_port = 0;
|
||||
ai->ai_next = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
net_store_fill_v4 (netstore *ns, guint32 addr, int port)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
ai = ns->ip6_hostent;
|
||||
if (!ai) {
|
||||
ai = malloc (sizeof (struct addrinfo));
|
||||
memset (ai, 0, sizeof (struct addrinfo));
|
||||
ns->ip6_hostent = ai;
|
||||
}
|
||||
sin = (struct sockaddr_in *)ai->ai_addr;
|
||||
if (!sin) {
|
||||
sin = malloc (sizeof (struct sockaddr_in));
|
||||
memset (sin, 0, sizeof (struct sockaddr_in));
|
||||
ai->ai_addr = (struct sockaddr *)sin;
|
||||
}
|
||||
ai->ai_family = AF_INET;
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_in);
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = addr;
|
||||
sin->sin_port = port;
|
||||
ai->ai_next = NULL;
|
||||
}
|
||||
|
||||
guint32
|
||||
net_getsockaddr_v4 (netstore *ns)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
ai = ns->ip6_hostent;
|
||||
|
||||
while (ai->ai_family != AF_INET) {
|
||||
ai = ai->ai_next;
|
||||
if (!ai)
|
||||
return 0;
|
||||
}
|
||||
sin = (struct sockaddr_in *)ai->ai_addr;
|
||||
return sin->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
int
|
||||
net_getsockport (int sok4, int sok6)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int len = sizeof (addr);
|
||||
|
||||
if (getsockname (sok4, (struct sockaddr *)&addr, &len) == -1)
|
||||
return -1;
|
||||
return addr.sin_port;
|
||||
}
|
||||
|
||||
#endif
|
||||
34
src/common/network.h
Normal file
34
src/common/network.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef XCHAT_NETWORK_H
|
||||
#define XCHAT_NETWORK_H
|
||||
|
||||
typedef struct netstore_
|
||||
{
|
||||
#ifdef NETWORK_PRIVATE
|
||||
#ifdef USE_IPV6
|
||||
struct addrinfo *ip6_hostent;
|
||||
#else
|
||||
struct hostent *ip4_hostent;
|
||||
struct sockaddr_in addr;
|
||||
#endif
|
||||
#else
|
||||
int _dummy; /* some compilers don't like empty structs */
|
||||
#endif
|
||||
} netstore;
|
||||
|
||||
#define MAX_HOSTNAME 128
|
||||
|
||||
netstore *net_store_new (void);
|
||||
void net_store_destroy (netstore *ns);
|
||||
int net_connect (netstore *ns, int sok4, int sok6, int *sok_return);
|
||||
char *net_resolve (netstore *ns, char *hostname, int port, char **real_host);
|
||||
void net_bind (netstore *tobindto, int sok4, int sok6);
|
||||
char *net_ip (guint32 addr);
|
||||
void net_sockets (int *sok4, int *sok6);
|
||||
/* functions for MSPROXY only! */
|
||||
void udp_sockets (int *sok4, int *sok6);
|
||||
void net_store_fill_any (netstore *ns);
|
||||
void net_store_fill_v4 (netstore *ns, guint32 addr, int port);
|
||||
guint32 net_getsockaddr_v4 (netstore *ns);
|
||||
int net_getsockport(int sok4, int sok6);
|
||||
|
||||
#endif
|
||||
634
src/common/notify.c
Normal file
634
src/common/notify.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "xchat.h"
|
||||
#include "notify.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "fe.h"
|
||||
#include "server.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
#include "xchatc.h"
|
||||
|
||||
|
||||
GSList *notify_list = 0;
|
||||
int notify_tag = 0;
|
||||
|
||||
|
||||
static char *
|
||||
despacify_dup (char *str)
|
||||
{
|
||||
char *p, *res = malloc (strlen (str) + 1);
|
||||
|
||||
p = res;
|
||||
while (1)
|
||||
{
|
||||
if (*str != ' ')
|
||||
{
|
||||
*p = *str;
|
||||
if (*p == 0)
|
||||
return res;
|
||||
p++;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
notify_netcmp (char *str, void *serv)
|
||||
{
|
||||
char *net = despacify_dup (server_get_network (serv, TRUE));
|
||||
|
||||
if (rfc_casecmp (str, net) == 0)
|
||||
{
|
||||
free (net);
|
||||
return 0; /* finish & return FALSE from token_foreach() */
|
||||
}
|
||||
|
||||
free (net);
|
||||
return 1; /* keep going... */
|
||||
}
|
||||
|
||||
/* monitor this nick on this particular network? */
|
||||
|
||||
static gboolean
|
||||
notify_do_network (struct notify *notify, server *serv)
|
||||
{
|
||||
if (!notify->networks) /* ALL networks for this nick */
|
||||
return TRUE;
|
||||
|
||||
if (token_foreach (notify->networks, ',', notify_netcmp, serv))
|
||||
return FALSE; /* network list doesn't contain this one */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct notify_per_server *
|
||||
notify_find_server_entry (struct notify *notify, struct server *serv)
|
||||
{
|
||||
GSList *list = notify->server_list;
|
||||
struct notify_per_server *servnot;
|
||||
|
||||
while (list)
|
||||
{
|
||||
servnot = (struct notify_per_server *) list->data;
|
||||
if (servnot->server == serv)
|
||||
return servnot;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* not found, should we add it, or is this not a network where
|
||||
we're monitoring this nick? */
|
||||
if (!notify_do_network (notify, serv))
|
||||
return NULL;
|
||||
|
||||
servnot = malloc (sizeof (struct notify_per_server));
|
||||
if (servnot)
|
||||
{
|
||||
memset (servnot, 0, sizeof (struct notify_per_server));
|
||||
servnot->server = serv;
|
||||
servnot->notify = notify;
|
||||
notify->server_list = g_slist_prepend (notify->server_list, servnot);
|
||||
}
|
||||
return servnot;
|
||||
}
|
||||
|
||||
void
|
||||
notify_save (void)
|
||||
{
|
||||
int fh;
|
||||
struct notify *notify;
|
||||
GSList *list = notify_list;
|
||||
|
||||
fh = xchat_open_file ("notify.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
write (fh, notify->name, strlen (notify->name));
|
||||
if (notify->networks)
|
||||
{
|
||||
write (fh, " ", 1);
|
||||
write (fh, notify->networks, strlen (notify->networks));
|
||||
}
|
||||
write (fh, "\n", 1);
|
||||
list = list->next;
|
||||
}
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
notify_load (void)
|
||||
{
|
||||
int fh;
|
||||
char buf[256];
|
||||
char *sep;
|
||||
|
||||
fh = xchat_open_file ("notify.conf", O_RDONLY, 0, 0);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (waitline (fh, buf, sizeof buf, FALSE) != -1)
|
||||
{
|
||||
if (buf[0] != '#' && buf[0] != 0)
|
||||
{
|
||||
sep = strchr (buf, ' ');
|
||||
if (sep)
|
||||
{
|
||||
sep[0] = 0;
|
||||
notify_adduser (buf, sep + 1);
|
||||
}
|
||||
else
|
||||
notify_adduser (buf, NULL);
|
||||
}
|
||||
}
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
|
||||
static struct notify_per_server *
|
||||
notify_find (server *serv, char *nick)
|
||||
{
|
||||
GSList *list = notify_list;
|
||||
struct notify_per_server *servnot;
|
||||
struct notify *notify;
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
|
||||
servnot = notify_find_server_entry (notify, serv);
|
||||
if (!servnot)
|
||||
{
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!serv->p_cmp (notify->name, nick))
|
||||
return servnot;
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
notify_announce_offline (server * serv, struct notify_per_server *servnot,
|
||||
char *nick, int quiet)
|
||||
{
|
||||
session *sess;
|
||||
|
||||
sess = serv->front_session;
|
||||
|
||||
servnot->ison = FALSE;
|
||||
servnot->lastoff = time (0);
|
||||
if (!quiet)
|
||||
EMIT_SIGNAL (XP_TE_NOTIFYOFFLINE, sess, nick, serv->servername,
|
||||
server_get_network (serv, TRUE), NULL, 0);
|
||||
fe_notify_update (nick);
|
||||
fe_notify_update (0);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_announce_online (server * serv, struct notify_per_server *servnot,
|
||||
char *nick)
|
||||
{
|
||||
session *sess;
|
||||
|
||||
sess = serv->front_session;
|
||||
|
||||
servnot->lastseen = time (0);
|
||||
if (servnot->ison)
|
||||
return;
|
||||
|
||||
servnot->ison = TRUE;
|
||||
servnot->laston = time (0);
|
||||
EMIT_SIGNAL (XP_TE_NOTIFYONLINE, sess, nick, serv->servername,
|
||||
server_get_network (serv, TRUE), NULL, 0);
|
||||
fe_notify_update (nick);
|
||||
fe_notify_update (0);
|
||||
|
||||
if (prefs.whois_on_notifyonline)
|
||||
{
|
||||
|
||||
/* Let's do whois with idle time (like in /quote WHOIS %s %s) */
|
||||
|
||||
char *wii_str = malloc (strlen (nick) * 2 + 2);
|
||||
sprintf (wii_str, "%s %s", nick, nick);
|
||||
serv->p_whois (serv, wii_str);
|
||||
free (wii_str);
|
||||
}
|
||||
}
|
||||
|
||||
/* handles numeric 601 */
|
||||
|
||||
void
|
||||
notify_set_offline (server * serv, char *nick, int quiet)
|
||||
{
|
||||
struct notify_per_server *servnot;
|
||||
|
||||
servnot = notify_find (serv, nick);
|
||||
if (!servnot)
|
||||
return;
|
||||
|
||||
notify_announce_offline (serv, servnot, nick, quiet);
|
||||
}
|
||||
|
||||
/* handles numeric 604 and 600 */
|
||||
|
||||
void
|
||||
notify_set_online (server * serv, char *nick)
|
||||
{
|
||||
struct notify_per_server *servnot;
|
||||
|
||||
servnot = notify_find (serv, nick);
|
||||
if (!servnot)
|
||||
return;
|
||||
|
||||
notify_announce_online (serv, servnot, nick);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_watch (server * serv, char *nick, int add)
|
||||
{
|
||||
char tbuf[256];
|
||||
|
||||
snprintf (tbuf, sizeof (tbuf), "WATCH +%s", nick);
|
||||
if (!add)
|
||||
tbuf[6] = '-';
|
||||
serv->p_raw (serv, tbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_watch_all (struct notify *notify, int add)
|
||||
{
|
||||
server *serv;
|
||||
GSList *list = serv_list;
|
||||
while (list)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->connected && serv->end_of_motd && serv->supports_watch &&
|
||||
notify_do_network (notify, serv))
|
||||
notify_watch (serv, notify->name, add);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notify_flush_watches (server * serv, GSList *from, GSList *end)
|
||||
{
|
||||
char tbuf[512];
|
||||
GSList *list;
|
||||
struct notify *notify;
|
||||
|
||||
strcpy (tbuf, "WATCH");
|
||||
|
||||
list = from;
|
||||
while (list != end)
|
||||
{
|
||||
notify = list->data;
|
||||
strcat (tbuf, " +");
|
||||
strcat (tbuf, notify->name);
|
||||
list = list->next;
|
||||
}
|
||||
serv->p_raw (serv, tbuf);
|
||||
}
|
||||
|
||||
/* called when logging in. e.g. when End of motd. */
|
||||
|
||||
void
|
||||
notify_send_watches (server * serv)
|
||||
{
|
||||
struct notify *notify;
|
||||
GSList *list;
|
||||
GSList *point;
|
||||
int len;
|
||||
|
||||
len = 0;
|
||||
point = list = notify_list;
|
||||
while (list)
|
||||
{
|
||||
notify = list->data;
|
||||
|
||||
if (notify_do_network (notify, serv))
|
||||
{
|
||||
len += strlen (notify->name) + 2 /* + and space */;
|
||||
if (len > 500)
|
||||
{
|
||||
notify_flush_watches (serv, point, list);
|
||||
len = strlen (notify->name) + 2;
|
||||
point = list;
|
||||
}
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (point)
|
||||
notify_flush_watches (serv, point, NULL);
|
||||
}
|
||||
|
||||
/* called when receiving a ISON 303 - should this func go? */
|
||||
|
||||
void
|
||||
notify_markonline (server *serv, char *word[])
|
||||
{
|
||||
struct notify *notify;
|
||||
struct notify_per_server *servnot;
|
||||
GSList *list = notify_list;
|
||||
int i, seen;
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
servnot = notify_find_server_entry (notify, serv);
|
||||
if (!servnot)
|
||||
{
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
i = 4;
|
||||
seen = FALSE;
|
||||
while (*word[i])
|
||||
{
|
||||
if (!serv->p_cmp (notify->name, word[i]))
|
||||
{
|
||||
seen = TRUE;
|
||||
notify_announce_online (serv, servnot, notify->name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
/* FIXME: word[] is only a 32 element array, limits notify list to
|
||||
about 27 people */
|
||||
if (i > PDIWORDS - 5)
|
||||
{
|
||||
/*fprintf (stderr, _("*** XCHAT WARNING: notify list too large.\n"));*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!seen && servnot->ison)
|
||||
{
|
||||
notify_announce_offline (serv, servnot, notify->name, FALSE);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
fe_notify_update (0);
|
||||
}
|
||||
|
||||
/* yuck! Old routine for ISON notify */
|
||||
|
||||
static void
|
||||
notify_checklist_for_server (server *serv)
|
||||
{
|
||||
char outbuf[512];
|
||||
struct notify *notify;
|
||||
GSList *list = notify_list;
|
||||
int i = 0;
|
||||
|
||||
strcpy (outbuf, "ISON ");
|
||||
while (list)
|
||||
{
|
||||
notify = list->data;
|
||||
if (notify_do_network (notify, serv))
|
||||
{
|
||||
i++;
|
||||
strcat (outbuf, notify->name);
|
||||
strcat (outbuf, " ");
|
||||
if (strlen (outbuf) > 460)
|
||||
{
|
||||
/* LAME: we can't send more than 512 bytes to the server, but *
|
||||
* if we split it in two packets, our offline detection wouldn't *
|
||||
work */
|
||||
/*fprintf (stderr, _("*** XCHAT WARNING: notify list too large.\n"));*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (i)
|
||||
serv->p_raw (serv, outbuf);
|
||||
}
|
||||
|
||||
int
|
||||
notify_checklist (void) /* check ISON list */
|
||||
{
|
||||
struct server *serv;
|
||||
GSList *list = serv_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->connected && serv->end_of_motd && !serv->supports_watch)
|
||||
{
|
||||
notify_checklist_for_server (serv);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
notify_showlist (struct session *sess)
|
||||
{
|
||||
char outbuf[256];
|
||||
struct notify *notify;
|
||||
GSList *list = notify_list;
|
||||
struct notify_per_server *servnot;
|
||||
int i = 0;
|
||||
|
||||
EMIT_SIGNAL (XP_TE_NOTIFYHEAD, sess, NULL, NULL, NULL, NULL, 0);
|
||||
while (list)
|
||||
{
|
||||
i++;
|
||||
notify = (struct notify *) list->data;
|
||||
servnot = notify_find_server_entry (notify, sess->server);
|
||||
if (servnot && servnot->ison)
|
||||
snprintf (outbuf, sizeof (outbuf), _(" %-20s online\n"), notify->name);
|
||||
else
|
||||
snprintf (outbuf, sizeof (outbuf), _(" %-20s offline\n"), notify->name);
|
||||
PrintText (sess, outbuf);
|
||||
list = list->next;
|
||||
}
|
||||
if (i)
|
||||
{
|
||||
sprintf (outbuf, "%d", i);
|
||||
EMIT_SIGNAL (XP_TE_NOTIFYNUMBER, sess, outbuf, NULL, NULL, NULL, 0);
|
||||
} else
|
||||
EMIT_SIGNAL (XP_TE_NOTIFYEMPTY, sess, NULL, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
int
|
||||
notify_deluser (char *name)
|
||||
{
|
||||
struct notify *notify;
|
||||
struct notify_per_server *servnot;
|
||||
GSList *list = notify_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
if (!rfc_casecmp (notify->name, name))
|
||||
{
|
||||
fe_notify_update (notify->name);
|
||||
/* Remove the records for each server */
|
||||
while (notify->server_list)
|
||||
{
|
||||
servnot = (struct notify_per_server *) notify->server_list->data;
|
||||
notify->server_list =
|
||||
g_slist_remove (notify->server_list, servnot);
|
||||
free (servnot);
|
||||
}
|
||||
notify_list = g_slist_remove (notify_list, notify);
|
||||
notify_watch_all (notify, FALSE);
|
||||
if (notify->networks)
|
||||
free (notify->networks);
|
||||
free (notify->name);
|
||||
free (notify);
|
||||
fe_notify_update (0);
|
||||
return 1;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
notify_adduser (char *name, char *networks)
|
||||
{
|
||||
struct notify *notify = malloc (sizeof (struct notify));
|
||||
if (notify)
|
||||
{
|
||||
memset (notify, 0, sizeof (struct notify));
|
||||
if (strlen (name) >= NICKLEN)
|
||||
{
|
||||
notify->name = malloc (NICKLEN);
|
||||
safe_strcpy (notify->name, name, NICKLEN);
|
||||
} else
|
||||
{
|
||||
notify->name = strdup (name);
|
||||
}
|
||||
if (networks)
|
||||
notify->networks = despacify_dup (networks);
|
||||
notify->server_list = 0;
|
||||
notify_list = g_slist_prepend (notify_list, notify);
|
||||
notify_checklist ();
|
||||
fe_notify_update (notify->name);
|
||||
fe_notify_update (0);
|
||||
notify_watch_all (notify, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
notify_is_in_list (server *serv, char *name)
|
||||
{
|
||||
struct notify *notify;
|
||||
GSList *list = notify_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
if (!serv->p_cmp (notify->name, name))
|
||||
return TRUE;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
notify_isnotify (struct session *sess, char *name)
|
||||
{
|
||||
struct notify *notify;
|
||||
struct notify_per_server *servnot;
|
||||
GSList *list = notify_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
if (!sess->server->p_cmp (notify->name, name))
|
||||
{
|
||||
servnot = notify_find_server_entry (notify, sess->server);
|
||||
if (servnot && servnot->ison)
|
||||
return TRUE;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
notify_cleanup ()
|
||||
{
|
||||
GSList *list = notify_list;
|
||||
GSList *nslist, *srvlist;
|
||||
struct notify *notify;
|
||||
struct notify_per_server *servnot;
|
||||
struct server *serv;
|
||||
int valid;
|
||||
|
||||
while (list)
|
||||
{
|
||||
/* Traverse the list of notify structures */
|
||||
notify = (struct notify *) list->data;
|
||||
nslist = notify->server_list;
|
||||
while (nslist)
|
||||
{
|
||||
/* Look at each per-server structure */
|
||||
servnot = (struct notify_per_server *) nslist->data;
|
||||
|
||||
/* Check the server is valid */
|
||||
valid = FALSE;
|
||||
srvlist = serv_list;
|
||||
while (srvlist)
|
||||
{
|
||||
serv = (struct server *) srvlist->data;
|
||||
if (servnot->server == serv)
|
||||
{
|
||||
valid = serv->connected; /* Only valid if server is too */
|
||||
break;
|
||||
}
|
||||
srvlist = srvlist->next;
|
||||
}
|
||||
if (!valid)
|
||||
{
|
||||
notify->server_list =
|
||||
g_slist_remove (notify->server_list, servnot);
|
||||
free (servnot);
|
||||
nslist = notify->server_list;
|
||||
} else
|
||||
{
|
||||
nslist = nslist->next;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
fe_notify_update (0);
|
||||
}
|
||||
44
src/common/notify.h
Normal file
44
src/common/notify.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef XCHAT_NOTIFY_H
|
||||
#define XCHAT_NOTIFY_H
|
||||
|
||||
struct notify
|
||||
{
|
||||
char *name;
|
||||
char *networks; /* network names, comma sep */
|
||||
GSList *server_list;
|
||||
};
|
||||
|
||||
struct notify_per_server
|
||||
{
|
||||
struct server *server;
|
||||
struct notify *notify;
|
||||
time_t laston;
|
||||
time_t lastseen;
|
||||
time_t lastoff;
|
||||
unsigned int ison:1;
|
||||
};
|
||||
|
||||
extern GSList *notify_list;
|
||||
extern int notify_tag;
|
||||
|
||||
/* the WATCH stuff */
|
||||
void notify_set_online (server * serv, char *nick);
|
||||
void notify_set_offline (server * serv, char *nick, int quiet);
|
||||
void notify_send_watches (server * serv);
|
||||
|
||||
/* the general stuff */
|
||||
void notify_adduser (char *name, char *networks);
|
||||
int notify_deluser (char *name);
|
||||
void notify_cleanup (void);
|
||||
void notify_load (void);
|
||||
void notify_save (void);
|
||||
void notify_showlist (session *sess);
|
||||
gboolean notify_is_in_list (server *serv, char *name);
|
||||
int notify_isnotify (session *sess, char *name);
|
||||
struct notify_per_server *notify_find_server_entry (struct notify *notify, struct server *serv);
|
||||
|
||||
/* the old ISON stuff - remove me? */
|
||||
void notify_markonline (server *serv, char *word[]);
|
||||
int notify_checklist (void);
|
||||
|
||||
#endif
|
||||
4403
src/common/outbound.c
Normal file
4403
src/common/outbound.c
Normal file
File diff suppressed because it is too large
Load Diff
20
src/common/outbound.h
Normal file
20
src/common/outbound.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef XCHAT_OUTBOUND_H
|
||||
#define XCHAT_OUTBOUND_H
|
||||
|
||||
extern const struct commands xc_cmds[];
|
||||
extern GSList *menu_list;
|
||||
|
||||
int auto_insert (char *dest, int destlen, unsigned char *src, char *word[], char *word_eol[],
|
||||
char *a, char *c, char *d, char *e, char *h, char *n, char *s);
|
||||
int handle_command (session *sess, char *cmd, int check_spch);
|
||||
void process_data_init (char *buf, char *cmd, char *word[], char *word_eol[], gboolean handle_quotes, gboolean allow_escape_quotes);
|
||||
void handle_multiline (session *sess, char *cmd, int history, int nocommand);
|
||||
void check_special_chars (char *cmd, int do_ascii);
|
||||
void notc_msg (session *sess);
|
||||
void server_sendpart (server * serv, char *channel, char *reason);
|
||||
void server_sendquit (session * sess);
|
||||
int menu_streq (const char *s1, const char *s2, int def);
|
||||
void open_query (server *serv, char *nick, gboolean focus_existing);
|
||||
gboolean load_perform_file (session *sess, char *file);
|
||||
|
||||
#endif
|
||||
208
src/common/plugin-timer.c
Normal file
208
src/common/plugin-timer.c
Normal file
@@ -0,0 +1,208 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include "xchat-plugin.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define strcasecmp stricmp
|
||||
#endif
|
||||
|
||||
static xchat_plugin *ph; /* plugin handle */
|
||||
static GSList *timer_list = NULL;
|
||||
|
||||
#define STATIC
|
||||
#define HELP \
|
||||
"Usage: TIMER [-refnum <num>] [-repeat <num>] <seconds> <command>\n" \
|
||||
" TIMER [-quiet] -delete <num>"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
xchat_hook *hook;
|
||||
xchat_context *context;
|
||||
char *command;
|
||||
int ref;
|
||||
int repeat;
|
||||
float timeout;
|
||||
unsigned int forever:1;
|
||||
} timer;
|
||||
|
||||
static void
|
||||
timer_del (timer *tim)
|
||||
{
|
||||
timer_list = g_slist_remove (timer_list, tim);
|
||||
free (tim->command);
|
||||
xchat_unhook (ph, tim->hook);
|
||||
free (tim);
|
||||
}
|
||||
|
||||
static void
|
||||
timer_del_ref (int ref, int quiet)
|
||||
{
|
||||
GSList *list;
|
||||
timer *tim;
|
||||
|
||||
list = timer_list;
|
||||
while (list)
|
||||
{
|
||||
tim = list->data;
|
||||
if (tim->ref == ref)
|
||||
{
|
||||
timer_del (tim);
|
||||
if (!quiet)
|
||||
xchat_printf (ph, "Timer %d deleted.\n", ref);
|
||||
return;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
if (!quiet)
|
||||
xchat_print (ph, "No such ref number found.\n");
|
||||
}
|
||||
|
||||
static int
|
||||
timeout_cb (timer *tim)
|
||||
{
|
||||
if (xchat_set_context (ph, tim->context))
|
||||
{
|
||||
xchat_command (ph, tim->command);
|
||||
|
||||
if (tim->forever)
|
||||
return 1;
|
||||
|
||||
tim->repeat--;
|
||||
if (tim->repeat > 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
timer_del (tim);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
timer_add (int ref, float timeout, int repeat, char *command)
|
||||
{
|
||||
timer *tim;
|
||||
GSList *list;
|
||||
|
||||
if (ref == 0)
|
||||
{
|
||||
ref = 1;
|
||||
list = timer_list;
|
||||
while (list)
|
||||
{
|
||||
tim = list->data;
|
||||
if (tim->ref >= ref)
|
||||
ref = tim->ref + 1;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
tim = malloc (sizeof (timer));
|
||||
tim->ref = ref;
|
||||
tim->repeat = repeat;
|
||||
tim->timeout = timeout;
|
||||
tim->command = strdup (command);
|
||||
tim->context = xchat_get_context (ph);
|
||||
tim->forever = FALSE;
|
||||
|
||||
if (repeat == 0)
|
||||
tim->forever = TRUE;
|
||||
|
||||
tim->hook = xchat_hook_timer (ph, timeout * 1000.0, (void *)timeout_cb, tim);
|
||||
timer_list = g_slist_append (timer_list, tim);
|
||||
}
|
||||
|
||||
static void
|
||||
timer_showlist (void)
|
||||
{
|
||||
GSList *list;
|
||||
timer *tim;
|
||||
|
||||
if (timer_list == NULL)
|
||||
{
|
||||
xchat_print (ph, "No timers installed.\n");
|
||||
xchat_print (ph, HELP);
|
||||
return;
|
||||
}
|
||||
/* 00000 00000000 0000000 abc */
|
||||
xchat_print (ph, "\026 Ref# Seconds Repeat Command \026\n");
|
||||
list = timer_list;
|
||||
while (list)
|
||||
{
|
||||
tim = list->data;
|
||||
xchat_printf (ph, "%5d %8.1f %7d %s\n", tim->ref, tim->timeout,
|
||||
tim->repeat, tim->command);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
timer_cb (char *word[], char *word_eol[], void *userdata)
|
||||
{
|
||||
int repeat = 1;
|
||||
float timeout;
|
||||
int offset = 0;
|
||||
int ref = 0;
|
||||
int quiet = FALSE;
|
||||
char *command;
|
||||
|
||||
if (!word[2][0])
|
||||
{
|
||||
timer_showlist ();
|
||||
return XCHAT_EAT_XCHAT;
|
||||
}
|
||||
|
||||
if (strcasecmp (word[2], "-quiet") == 0)
|
||||
{
|
||||
quiet = TRUE;
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (strcasecmp (word[2 + offset], "-delete") == 0)
|
||||
{
|
||||
timer_del_ref (atoi (word[3 + offset]), quiet);
|
||||
return XCHAT_EAT_XCHAT;
|
||||
}
|
||||
|
||||
if (strcasecmp (word[2 + offset], "-refnum") == 0)
|
||||
{
|
||||
ref = atoi (word[3 + offset]);
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
if (strcasecmp (word[2 + offset], "-repeat") == 0)
|
||||
{
|
||||
repeat = atoi (word[3 + offset]);
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
timeout = atof (word[2 + offset]);
|
||||
command = word_eol[3 + offset];
|
||||
|
||||
if (timeout < 0.1 || !command[0])
|
||||
xchat_print (ph, HELP);
|
||||
else
|
||||
timer_add (ref, timeout, repeat, command);
|
||||
|
||||
return XCHAT_EAT_XCHAT;
|
||||
}
|
||||
|
||||
int
|
||||
#ifdef STATIC
|
||||
timer_plugin_init
|
||||
#else
|
||||
xchat_plugin_init
|
||||
#endif
|
||||
(xchat_plugin *plugin_handle, char **plugin_name,
|
||||
char **plugin_desc, char **plugin_version, char *arg)
|
||||
{
|
||||
/* we need to save this for use with any xchat_* functions */
|
||||
ph = plugin_handle;
|
||||
|
||||
*plugin_name = "Timer";
|
||||
*plugin_desc = "IrcII style /TIMER command";
|
||||
*plugin_version = "";
|
||||
|
||||
xchat_hook_command (ph, "TIMER", XCHAT_PRI_NORM, timer_cb, HELP, 0);
|
||||
|
||||
return 1; /* return 1 for success */
|
||||
}
|
||||
7
src/common/plugin-timer.h
Normal file
7
src/common/plugin-timer.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef XCHAT_PLUGIN_TIMER_H
|
||||
#define XCHAT_PLUGIN_TIMER_H
|
||||
|
||||
int timer_plugin_init (xchat_plugin *plugin_handle, char **plugin_name,
|
||||
char **plugin_desc, char **plugin_version, char *arg);
|
||||
|
||||
#endif
|
||||
1569
src/common/plugin.c
Normal file
1569
src/common/plugin.c
Normal file
File diff suppressed because it is too large
Load Diff
132
src/common/plugin.h
Normal file
132
src/common/plugin.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#ifndef XCHAT_COMMONPLUGIN_H
|
||||
#define XCHAT_COMMONPLUGIN_H
|
||||
|
||||
#ifdef PLUGIN_C
|
||||
struct _xchat_plugin
|
||||
{
|
||||
/* Keep these insync with xchat-plugin.h */
|
||||
/* !!don't change the order, to keep binary compat!! */
|
||||
xchat_hook *(*xchat_hook_command) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
const char *help_text,
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_server) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_print) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_timer) (xchat_plugin *ph,
|
||||
int timeout,
|
||||
int (*callback) (void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_fd) (xchat_plugin *ph,
|
||||
int fd,
|
||||
int flags,
|
||||
int (*callback) (int fd, int flags, void *user_data),
|
||||
void *userdata);
|
||||
void *(*xchat_unhook) (xchat_plugin *ph,
|
||||
xchat_hook *hook);
|
||||
void (*xchat_print) (xchat_plugin *ph,
|
||||
const char *text);
|
||||
void (*xchat_printf) (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
void (*xchat_command) (xchat_plugin *ph,
|
||||
const char *command);
|
||||
void (*xchat_commandf) (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
int (*xchat_nickcmp) (xchat_plugin *ph,
|
||||
const char *s1,
|
||||
const char *s2);
|
||||
int (*xchat_set_context) (xchat_plugin *ph,
|
||||
xchat_context *ctx);
|
||||
xchat_context *(*xchat_find_context) (xchat_plugin *ph,
|
||||
const char *servname,
|
||||
const char *channel);
|
||||
xchat_context *(*xchat_get_context) (xchat_plugin *ph);
|
||||
const char *(*xchat_get_info) (xchat_plugin *ph,
|
||||
const char *id);
|
||||
int (*xchat_get_prefs) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
const char **string,
|
||||
int *integer);
|
||||
xchat_list * (*xchat_list_get) (xchat_plugin *ph,
|
||||
const char *name);
|
||||
void (*xchat_list_free) (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
const char * const * (*xchat_list_fields) (xchat_plugin *ph,
|
||||
const char *name);
|
||||
int (*xchat_list_next) (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
const char * (*xchat_list_str) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
int (*xchat_list_int) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
void * (*xchat_plugingui_add) (xchat_plugin *ph,
|
||||
const char *filename,
|
||||
const char *name,
|
||||
const char *desc,
|
||||
const char *version,
|
||||
char *reserved);
|
||||
void (*xchat_plugingui_remove) (xchat_plugin *ph,
|
||||
void *handle);
|
||||
int (*xchat_emit_print) (xchat_plugin *ph,
|
||||
const char *event_name, ...);
|
||||
void *(*xchat_read_fd) (xchat_plugin *ph);
|
||||
time_t (*xchat_list_time) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
char *(*xchat_gettext) (xchat_plugin *ph,
|
||||
const char *msgid);
|
||||
void (*xchat_send_modes) (xchat_plugin *ph,
|
||||
const char **targets,
|
||||
int ntargets,
|
||||
int modes_per_line,
|
||||
char sign,
|
||||
char mode);
|
||||
char *(*xchat_strip) (xchat_plugin *ph,
|
||||
const char *str,
|
||||
int len,
|
||||
int flags);
|
||||
void (*xchat_free) (xchat_plugin *ph,
|
||||
void *ptr);
|
||||
void *(*xchat_dummy4) (xchat_plugin *ph);
|
||||
void *(*xchat_dummy3) (xchat_plugin *ph);
|
||||
void *(*xchat_dummy2) (xchat_plugin *ph);
|
||||
void *(*xchat_dummy1) (xchat_plugin *ph);
|
||||
/* PRIVATE FIELDS! */
|
||||
void *handle; /* from dlopen */
|
||||
char *filename; /* loaded from */
|
||||
char *name;
|
||||
char *desc;
|
||||
char *version;
|
||||
session *context;
|
||||
void *deinit_callback; /* pointer to xchat_plugin_deinit */
|
||||
unsigned int fake:1; /* fake plugin. Added by xchat_plugingui_add() */
|
||||
unsigned int free_strings:1; /* free name,desc,version? */
|
||||
};
|
||||
#endif
|
||||
|
||||
char *plugin_load (session *sess, char *filename, char *arg);
|
||||
void plugin_add (session *sess, char *filename, void *handle, void *init_func, void *deinit_func, char *arg, int fake);
|
||||
int plugin_kill (char *name, int by_filename);
|
||||
void plugin_kill_all (void);
|
||||
void plugin_auto_load (session *sess);
|
||||
int plugin_emit_command (session *sess, char *name, char *word[], char *word_eol[]);
|
||||
int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[]);
|
||||
int plugin_emit_print (session *sess, char *word[]);
|
||||
int plugin_emit_dummy_print (session *sess, char *name);
|
||||
int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, int len, char *string);
|
||||
GList* plugin_command_list(GList *tmp_list);
|
||||
int plugin_show_help (session *sess, char *cmd);
|
||||
void plugin_command_foreach (session *sess, void *userdata, void (*cb) (session *sess, void *userdata, char *name, char *usage));
|
||||
|
||||
#endif
|
||||
1260
src/common/proto-irc.c
Normal file
1260
src/common/proto-irc.c
Normal file
File diff suppressed because it is too large
Load Diff
6
src/common/proto-irc.h
Normal file
6
src/common/proto-irc.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef XCHAT_PROTO_H
|
||||
#define XCHAT_PROTO_H
|
||||
|
||||
void proto_fill_her_up (server *serv);
|
||||
|
||||
#endif
|
||||
2047
src/common/server.c
Normal file
2047
src/common/server.c
Normal file
File diff suppressed because it is too large
Load Diff
26
src/common/server.h
Normal file
26
src/common/server.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef XCHAT_SERVER_H
|
||||
#define XCHAT_SERVER_H
|
||||
|
||||
extern GSList *serv_list;
|
||||
|
||||
/* eventually need to keep the tcp_* functions isolated to server.c */
|
||||
int tcp_send_len (server *serv, char *buf, int len);
|
||||
int tcp_send (server *serv, char *buf);
|
||||
void tcp_sendf (server *serv, char *fmt, ...);
|
||||
int tcp_send_real (void *ssl, int sok, char *encoding, int using_irc, char *buf, int len);
|
||||
|
||||
server *server_new (void);
|
||||
int is_server (server *serv);
|
||||
void server_fill_her_up (server *serv);
|
||||
void server_set_encoding (server *serv, char *new_encoding);
|
||||
void server_set_defaults (server *serv);
|
||||
char *server_get_network (server *serv, gboolean fallback);
|
||||
void server_set_name (server *serv, char *name);
|
||||
void server_free (server *serv);
|
||||
|
||||
void server_away_save_message (server *serv, char *nick, char *msg);
|
||||
struct away_msg *server_away_find_message (server *serv, char *nick);
|
||||
|
||||
void base64_encode (char *to, char *from, unsigned int len);
|
||||
|
||||
#endif
|
||||
1311
src/common/servlist.c
Normal file
1311
src/common/servlist.c
Normal file
File diff suppressed because it is too large
Load Diff
62
src/common/servlist.h
Normal file
62
src/common/servlist.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef XCHAT_SERVLIST_H
|
||||
#define XCHAT_SERVLIST_H
|
||||
|
||||
typedef struct ircserver
|
||||
{
|
||||
char *hostname;
|
||||
} ircserver;
|
||||
|
||||
typedef struct ircnet
|
||||
{
|
||||
char *name;
|
||||
char *nick;
|
||||
char *nick2;
|
||||
char *user;
|
||||
char *real;
|
||||
char *pass;
|
||||
char *autojoin;
|
||||
char *command;
|
||||
char *nickserv;
|
||||
char *comment;
|
||||
char *encoding;
|
||||
GSList *servlist;
|
||||
int selected;
|
||||
guint32 flags;
|
||||
} ircnet;
|
||||
|
||||
extern GSList *network_list;
|
||||
|
||||
#define FLAG_CYCLE 1
|
||||
#define FLAG_USE_GLOBAL 2
|
||||
#define FLAG_USE_SSL 4
|
||||
#define FLAG_AUTO_CONNECT 8
|
||||
#define FLAG_USE_PROXY 16
|
||||
#define FLAG_ALLOW_INVALID 32
|
||||
#define FLAG_FAVORITE 64
|
||||
#define FLAG_COUNT 7
|
||||
|
||||
void servlist_init (void);
|
||||
int servlist_save (void);
|
||||
int servlist_cycle (server *serv);
|
||||
void servlist_connect (session *sess, ircnet *net, gboolean join);
|
||||
int servlist_connect_by_netname (session *sess, char *network, gboolean join);
|
||||
int servlist_auto_connect (session *sess);
|
||||
int servlist_have_auto (void);
|
||||
int servlist_check_encoding (char *charset);
|
||||
void servlist_cleanup (void);
|
||||
|
||||
ircnet *servlist_net_add (char *name, char *comment, int prepend);
|
||||
void servlist_net_remove (ircnet *net);
|
||||
ircnet *servlist_net_find (char *name, int *pos, int (*cmpfunc) (const char *, const char *));
|
||||
ircnet *servlist_net_find_from_server (char *server_name);
|
||||
|
||||
void servlist_server_remove (ircnet *net, ircserver *serv);
|
||||
ircserver *servlist_server_add (ircnet *net, char *name);
|
||||
ircserver *servlist_server_find (ircnet *net, char *name, int *pos);
|
||||
|
||||
void joinlist_split (char *autojoin, GSList **channels, GSList **keys);
|
||||
gboolean joinlist_is_in_list (server *serv, char *channel);
|
||||
void joinlist_free (GSList *channels, GSList *keys);
|
||||
gchar *joinlist_merge (GSList *channels, GSList *keys);
|
||||
|
||||
#endif
|
||||
323
src/common/ssl.c
Normal file
323
src/common/ssl.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* ssl.c v0.0.3
|
||||
* Copyright (C) 2000 -- DaP <profeta@freemail.c3.hu>
|
||||
*
|
||||
* 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 <openssl/ssl.h> /* SSL_() */
|
||||
#include <openssl/err.h> /* ERR_() */
|
||||
#include <time.h> /* asctime() */
|
||||
#include <string.h> /* strncpy() */
|
||||
#include "ssl.h" /* struct cert_info */
|
||||
#include "inet.h"
|
||||
#include "../../config.h" /* HAVE_SNPRINTF */
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
#define snprintf g_snprintf
|
||||
#endif
|
||||
|
||||
/* globals */
|
||||
static struct chiper_info chiper_info; /* static buffer for _SSL_get_cipher_info() */
|
||||
static char err_buf[256]; /* generic error buffer */
|
||||
|
||||
|
||||
/* +++++ Internal functions +++++ */
|
||||
|
||||
static void
|
||||
__SSL_fill_err_buf (char *funcname)
|
||||
{
|
||||
int err;
|
||||
char buf[256];
|
||||
|
||||
|
||||
err = ERR_get_error ();
|
||||
ERR_error_string (err, buf);
|
||||
snprintf (err_buf, sizeof (err_buf), "%s: %s (%d)\n", funcname, buf, err);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
__SSL_critical_error (char *funcname)
|
||||
{
|
||||
__SSL_fill_err_buf (funcname);
|
||||
fprintf (stderr, "%s\n", err_buf);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* +++++ SSL functions +++++ */
|
||||
|
||||
SSL_CTX *
|
||||
_SSL_context_init (void (*info_cb_func), int server)
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
#ifdef WIN32
|
||||
int i, r;
|
||||
#endif
|
||||
|
||||
SSLeay_add_ssl_algorithms ();
|
||||
SSL_load_error_strings ();
|
||||
ctx = SSL_CTX_new (server ? SSLv3_server_method() : SSLv3_client_method ());
|
||||
|
||||
SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_BOTH);
|
||||
SSL_CTX_set_timeout (ctx, 300);
|
||||
|
||||
/* used in SSL_connect(), SSL_accept() */
|
||||
SSL_CTX_set_info_callback (ctx, info_cb_func);
|
||||
|
||||
#ifdef WIN32
|
||||
/* under win32, OpenSSL needs to be seeded with some randomness */
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
r = rand ();
|
||||
RAND_seed ((unsigned char *)&r, sizeof (r));
|
||||
}
|
||||
#endif
|
||||
|
||||
return(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
ASN1_TIME_snprintf (char *buf, int buf_len, ASN1_TIME * tm)
|
||||
{
|
||||
char *expires = NULL;
|
||||
BIO *inMem = BIO_new (BIO_s_mem ());
|
||||
|
||||
ASN1_TIME_print (inMem, tm);
|
||||
BIO_get_mem_data (inMem, &expires);
|
||||
buf[0] = 0;
|
||||
if (expires != NULL)
|
||||
{
|
||||
memset (buf, 0, buf_len);
|
||||
strncpy (buf, expires, 24);
|
||||
}
|
||||
BIO_free (inMem);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
broke_oneline (char *oneline, char *parray[])
|
||||
{
|
||||
char *pt, *ppt;
|
||||
int i;
|
||||
|
||||
|
||||
i = 0;
|
||||
ppt = pt = oneline + 1;
|
||||
while ((pt = strchr (pt, '/')))
|
||||
{
|
||||
*pt = 0;
|
||||
parray[i++] = ppt;
|
||||
ppt = ++pt;
|
||||
}
|
||||
parray[i++] = ppt;
|
||||
parray[i] = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
FIXME: Master-Key, Extensions, CA bits
|
||||
(openssl x509 -text -in servcert.pem)
|
||||
*/
|
||||
int
|
||||
_SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl)
|
||||
{
|
||||
X509 *peer_cert;
|
||||
EVP_PKEY *peer_pkey;
|
||||
/* EVP_PKEY *ca_pkey; */
|
||||
/* EVP_PKEY *tmp_pkey; */
|
||||
char notBefore[64];
|
||||
char notAfter[64];
|
||||
int alg;
|
||||
int sign_alg;
|
||||
|
||||
|
||||
if (!(peer_cert = SSL_get_peer_certificate (ssl)))
|
||||
return (1); /* FATAL? */
|
||||
|
||||
X509_NAME_oneline (X509_get_subject_name (peer_cert), cert_info->subject,
|
||||
sizeof (cert_info->subject));
|
||||
X509_NAME_oneline (X509_get_issuer_name (peer_cert), cert_info->issuer,
|
||||
sizeof (cert_info->issuer));
|
||||
broke_oneline (cert_info->subject, cert_info->subject_word);
|
||||
broke_oneline (cert_info->issuer, cert_info->issuer_word);
|
||||
|
||||
alg = OBJ_obj2nid (peer_cert->cert_info->key->algor->algorithm);
|
||||
sign_alg = OBJ_obj2nid (peer_cert->sig_alg->algorithm);
|
||||
ASN1_TIME_snprintf (notBefore, sizeof (notBefore),
|
||||
X509_get_notBefore (peer_cert));
|
||||
ASN1_TIME_snprintf (notAfter, sizeof (notAfter),
|
||||
X509_get_notAfter (peer_cert));
|
||||
|
||||
peer_pkey = X509_get_pubkey (peer_cert);
|
||||
|
||||
strncpy (cert_info->algorithm,
|
||||
(alg == NID_undef) ? "Unknown" : OBJ_nid2ln (alg),
|
||||
sizeof (cert_info->algorithm));
|
||||
cert_info->algorithm_bits = EVP_PKEY_bits (peer_pkey);
|
||||
strncpy (cert_info->sign_algorithm,
|
||||
(sign_alg == NID_undef) ? "Unknown" : OBJ_nid2ln (sign_alg),
|
||||
sizeof (cert_info->sign_algorithm));
|
||||
/* EVP_PKEY_bits(ca_pkey)); */
|
||||
cert_info->sign_algorithm_bits = 0;
|
||||
strncpy (cert_info->notbefore, notBefore, sizeof (cert_info->notbefore));
|
||||
strncpy (cert_info->notafter, notAfter, sizeof (cert_info->notafter));
|
||||
|
||||
EVP_PKEY_free (peer_pkey);
|
||||
|
||||
/* SSL_SESSION_print_fp(stdout, SSL_get_session(ssl)); */
|
||||
/*
|
||||
if (ssl->session->sess_cert->peer_rsa_tmp) {
|
||||
tmp_pkey = EVP_PKEY_new();
|
||||
EVP_PKEY_assign_RSA(tmp_pkey, ssl->session->sess_cert->peer_rsa_tmp);
|
||||
cert_info->rsa_tmp_bits = EVP_PKEY_bits (tmp_pkey);
|
||||
EVP_PKEY_free(tmp_pkey);
|
||||
} else
|
||||
fprintf(stderr, "REMOTE SIDE DOESN'T PROVIDES ->peer_rsa_tmp\n");
|
||||
*/
|
||||
cert_info->rsa_tmp_bits = 0;
|
||||
|
||||
X509_free (peer_cert);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
struct chiper_info *
|
||||
_SSL_get_cipher_info (SSL * ssl)
|
||||
{
|
||||
SSL_CIPHER *c;
|
||||
|
||||
|
||||
c = SSL_get_current_cipher (ssl);
|
||||
strncpy (chiper_info.version, SSL_CIPHER_get_version (c),
|
||||
sizeof (chiper_info.version));
|
||||
strncpy (chiper_info.chiper, SSL_CIPHER_get_name (c),
|
||||
sizeof (chiper_info.chiper));
|
||||
SSL_CIPHER_get_bits (c, &chiper_info.chiper_bits);
|
||||
|
||||
return (&chiper_info);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_SSL_send (SSL * ssl, char *buf, int len)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
||||
num = SSL_write (ssl, buf, len);
|
||||
|
||||
switch (SSL_get_error (ssl, num))
|
||||
{
|
||||
case SSL_ERROR_SSL: /* setup errno! */
|
||||
/* ??? */
|
||||
__SSL_fill_err_buf ("SSL_write");
|
||||
fprintf (stderr, "%s\n", err_buf);
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
/* ??? */
|
||||
perror ("SSL_write/write");
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* fprintf(stderr, "SSL closed on write\n"); */
|
||||
break;
|
||||
}
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_SSL_recv (SSL * ssl, char *buf, int len)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
||||
num = SSL_read (ssl, buf, len);
|
||||
|
||||
switch (SSL_get_error (ssl, num))
|
||||
{
|
||||
case SSL_ERROR_SSL:
|
||||
/* ??? */
|
||||
__SSL_fill_err_buf ("SSL_read");
|
||||
fprintf (stderr, "%s\n", err_buf);
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
/* ??? */
|
||||
if (!would_block ())
|
||||
perror ("SSL_read/read");
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* fprintf(stdeerr, "SSL closed on read\n"); */
|
||||
break;
|
||||
}
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
|
||||
SSL *
|
||||
_SSL_socket (SSL_CTX *ctx, int sd)
|
||||
{
|
||||
SSL *ssl;
|
||||
|
||||
|
||||
if (!(ssl = SSL_new (ctx)))
|
||||
/* FATAL */
|
||||
__SSL_critical_error ("SSL_new");
|
||||
|
||||
SSL_set_fd (ssl, sd);
|
||||
if (ctx->method == SSLv3_client_method())
|
||||
SSL_set_connect_state (ssl);
|
||||
else
|
||||
SSL_set_accept_state(ssl);
|
||||
|
||||
return (ssl);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
_SSL_set_verify (SSL_CTX *ctx, void *verify_callback, char *cacert)
|
||||
{
|
||||
if (!SSL_CTX_set_default_verify_paths (ctx))
|
||||
{
|
||||
__SSL_fill_err_buf ("SSL_CTX_set_default_verify_paths");
|
||||
return (err_buf);
|
||||
}
|
||||
/*
|
||||
if (cacert)
|
||||
{
|
||||
if (!SSL_CTX_load_verify_locations (ctx, cacert, NULL))
|
||||
{
|
||||
__SSL_fill_err_buf ("SSL_CTX_load_verify_locations");
|
||||
return (err_buf);
|
||||
}
|
||||
}
|
||||
*/
|
||||
SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, verify_callback);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_SSL_close (SSL * ssl)
|
||||
{
|
||||
SSL_set_shutdown (ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
||||
SSL_free (ssl);
|
||||
ERR_remove_state (0); /* free state buffer */
|
||||
}
|
||||
65
src/common/ssl.h
Normal file
65
src/common/ssl.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
...
|
||||
*/
|
||||
|
||||
struct cert_info {
|
||||
char subject[256];
|
||||
char *subject_word[12];
|
||||
char issuer[256];
|
||||
char *issuer_word[12];
|
||||
char algorithm[32];
|
||||
int algorithm_bits;
|
||||
char sign_algorithm[32];
|
||||
int sign_algorithm_bits;
|
||||
char notbefore[32];
|
||||
char notafter[32];
|
||||
|
||||
int rsa_tmp_bits;
|
||||
};
|
||||
|
||||
struct chiper_info {
|
||||
char version[16];
|
||||
char chiper[24];
|
||||
int chiper_bits;
|
||||
};
|
||||
|
||||
SSL_CTX *_SSL_context_init (void (*info_cb_func), int server);
|
||||
#define _SSL_context_free(a) SSL_CTX_free(a);
|
||||
|
||||
SSL *_SSL_socket (SSL_CTX *ctx, int sd);
|
||||
char *_SSL_set_verify (SSL_CTX *ctx, void *(verify_callback), char *cacert);
|
||||
/*
|
||||
int SSL_connect(SSL *);
|
||||
int SSL_accept(SSL *);
|
||||
int SSL_get_fd(SSL *);
|
||||
*/
|
||||
void _SSL_close (SSL * ssl);
|
||||
|
||||
int _SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl);
|
||||
struct chiper_info *_SSL_get_cipher_info (SSL * ssl);
|
||||
|
||||
/*char *_SSL_add_keypair (SSL_CTX *ctx, char *privkey, char *cert);*/
|
||||
/*void _SSL_add_random_keypair(SSL_CTX *ctx, int bits);*/
|
||||
|
||||
int _SSL_send (SSL * ssl, char *buf, int len);
|
||||
int _SSL_recv (SSL * ssl, char *buf, int len);
|
||||
|
||||
/* misc */
|
||||
/*void broke_oneline (char *oneline, char *parray[]);*/
|
||||
|
||||
/*char *_SSL_do_cipher_base64(char *buf, int buf_len, char *key, int operation);*/ /* must be freed */
|
||||
|
||||
/*void *_SSL_get_sess_obj(SSL *ssl, int type);*/ /* NOT must be freed */
|
||||
#define _SSL_get_sess_pkey(a) _SSL_get_sess_obj(a, 0)
|
||||
#define _SSL_get_sess_prkey(a) _SSL_get_sess_obj(a, 1)
|
||||
#define _SSL_get_sess_x509(a) _SSL_get_sess_obj(a, 2)
|
||||
/*char *_SSL_get_obj_base64(void *s, int type);*/ /* must be freed */
|
||||
#define _SSL_get_pkey_base64(a) _SSL_get_obj_base64(a, 0)
|
||||
#define _SSL_get_prkey_base64(a) _SSL_get_obj_base64(a, 1)
|
||||
#define _SSL_get_x509_base64(a) _SSL_get_obj_base64(a, 2)
|
||||
/*char *_SSL_get_ctx_obj_base64(SSL_CTX *ctx, int type);*/ /* must be freed */
|
||||
#define _SSL_get_ctx_pkey_base64(a) _SSL_get_ctx_obj_base64(a, 0)
|
||||
#define _SSL_get_ctx_prkey_base64(a) _SSL_get_ctx_obj_base64(a, 1)
|
||||
#define _SSL_get_ctx_x509_base64(a) _SSL_get_ctx_obj_base64(a, 2)
|
||||
|
||||
/*int _SSL_verify_x509(X509 *x509);*/
|
||||
2309
src/common/text.c
Normal file
2309
src/common/text.c
Normal file
File diff suppressed because it is too large
Load Diff
42
src/common/text.h
Normal file
42
src/common/text.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "textenums.h"
|
||||
|
||||
#ifndef XCHAT_TEXT_H
|
||||
#define XCHAT_TEXT_H
|
||||
|
||||
#define EMIT_SIGNAL(i, sess, a, b, c, d, e) text_emit(i, sess, a, b, c, d)
|
||||
|
||||
struct text_event
|
||||
{
|
||||
char *name;
|
||||
char * const *help;
|
||||
int num_args;
|
||||
char *def;
|
||||
};
|
||||
|
||||
void scrollback_close (session *sess);
|
||||
void scrollback_load (session *sess);
|
||||
|
||||
int text_word_check (char *word, int len);
|
||||
void PrintText (session *sess, char *text);
|
||||
void PrintTextf (session *sess, char *format, ...);
|
||||
void log_close (session *sess);
|
||||
void log_open_or_close (session *sess);
|
||||
void load_text_events (void);
|
||||
void pevent_save (char *fn);
|
||||
int pevt_build_string (const char *input, char **output, int *max_arg);
|
||||
int pevent_load (char *filename);
|
||||
void pevent_make_pntevts (void);
|
||||
void text_emit (int index, session *sess, char *a, char *b, char *c, char *d);
|
||||
int text_emit_by_name (char *name, session *sess, char *a, char *b, char *c, char *d);
|
||||
char *text_validate (char **text, int *len);
|
||||
int get_stamp_str (char *fmt, time_t tim, char **ret);
|
||||
void format_event (session *sess, int index, char **args, char *o, int sizeofo, unsigned int stripcolor_args);
|
||||
char *text_find_format_string (char *name);
|
||||
|
||||
void sound_play (const char *file, gboolean quiet);
|
||||
void sound_play_event (int i);
|
||||
void sound_beep (session *);
|
||||
void sound_load ();
|
||||
void sound_save ();
|
||||
|
||||
#endif
|
||||
76
src/common/textenums.h
Normal file
76
src/common/textenums.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* this file is auto generated, edit textevents.in instead! */
|
||||
|
||||
enum
|
||||
{
|
||||
XP_TE_ADDNOTIFY, XP_TE_BANLIST,
|
||||
XP_TE_BANNED, XP_TE_BEEP,
|
||||
XP_TE_CHANGENICK, XP_TE_CHANACTION,
|
||||
XP_TE_HCHANACTION, XP_TE_CHANBAN,
|
||||
XP_TE_CHANDATE, XP_TE_CHANDEHOP,
|
||||
XP_TE_CHANDEOP, XP_TE_CHANDEVOICE,
|
||||
XP_TE_CHANEXEMPT, XP_TE_CHANHOP,
|
||||
XP_TE_CHANINVITE, XP_TE_CHANLISTHEAD,
|
||||
XP_TE_CHANMSG, XP_TE_CHANMODEGEN,
|
||||
XP_TE_CHANMODES, XP_TE_HCHANMSG,
|
||||
XP_TE_CHANNOTICE, XP_TE_CHANOP,
|
||||
XP_TE_CHANRMEXEMPT, XP_TE_CHANRMINVITE,
|
||||
XP_TE_CHANRMKEY, XP_TE_CHANRMLIMIT,
|
||||
XP_TE_CHANSETKEY, XP_TE_CHANSETLIMIT,
|
||||
XP_TE_CHANUNBAN, XP_TE_CHANVOICE,
|
||||
XP_TE_CONNECTED, XP_TE_CONNECT,
|
||||
XP_TE_CONNFAIL, XP_TE_CTCPGEN,
|
||||
XP_TE_CTCPGENC, XP_TE_CTCPSEND,
|
||||
XP_TE_CTCPSND, XP_TE_CTCPSNDC,
|
||||
XP_TE_DCCCHATABORT, XP_TE_DCCCONCHAT,
|
||||
XP_TE_DCCCHATF, XP_TE_DCCCHATOFFER,
|
||||
XP_TE_DCCCHATOFFERING, XP_TE_DCCCHATREOFFER,
|
||||
XP_TE_DCCCONFAIL, XP_TE_DCCGENERICOFFER,
|
||||
XP_TE_DCCHEAD, XP_TE_MALFORMED,
|
||||
XP_TE_DCCOFFER, XP_TE_DCCIVAL,
|
||||
XP_TE_DCCRECVABORT, XP_TE_DCCRECVCOMP,
|
||||
XP_TE_DCCCONRECV, XP_TE_DCCRECVERR,
|
||||
XP_TE_DCCFILEERR, XP_TE_DCCRENAME,
|
||||
XP_TE_DCCRESUMEREQUEST, XP_TE_DCCSENDABORT,
|
||||
XP_TE_DCCSENDCOMP, XP_TE_DCCCONSEND,
|
||||
XP_TE_DCCSENDFAIL, XP_TE_DCCSENDOFFER,
|
||||
XP_TE_DCCSTALL, XP_TE_DCCTOUT,
|
||||
XP_TE_DELNOTIFY, XP_TE_DISCON,
|
||||
XP_TE_FOUNDIP, XP_TE_GENMSG,
|
||||
XP_TE_IGNOREADD, XP_TE_IGNORECHANGE,
|
||||
XP_TE_IGNOREFOOTER, XP_TE_IGNOREHEADER,
|
||||
XP_TE_IGNOREREMOVE, XP_TE_IGNOREEMPTY,
|
||||
XP_TE_INVITE, XP_TE_INVITED,
|
||||
XP_TE_JOIN, XP_TE_KEYWORD,
|
||||
XP_TE_KICK, XP_TE_KILL,
|
||||
XP_TE_MSGSEND, XP_TE_MOTD,
|
||||
XP_TE_MOTDSKIP, XP_TE_NICKCLASH,
|
||||
XP_TE_NICKFAIL, XP_TE_NODCC,
|
||||
XP_TE_NOCHILD, XP_TE_NOTICE,
|
||||
XP_TE_NOTICESEND, XP_TE_NOTIFYEMPTY,
|
||||
XP_TE_NOTIFYHEAD, XP_TE_NOTIFYNUMBER,
|
||||
XP_TE_NOTIFYOFFLINE, XP_TE_NOTIFYONLINE,
|
||||
XP_TE_OPENDIALOG, XP_TE_PART,
|
||||
XP_TE_PARTREASON, XP_TE_PINGREP,
|
||||
XP_TE_PINGTIMEOUT, XP_TE_PRIVACTION,
|
||||
XP_TE_DPRIVACTION, XP_TE_PRIVMSG,
|
||||
XP_TE_DPRIVMSG, XP_TE_ALREADYPROCESS,
|
||||
XP_TE_QUIT, XP_TE_RAWMODES,
|
||||
XP_TE_WALLOPS, XP_TE_RESOLVINGUSER,
|
||||
XP_TE_SERVERCONNECTED, XP_TE_SERVERERROR,
|
||||
XP_TE_SERVERLOOKUP, XP_TE_SERVNOTICE,
|
||||
XP_TE_SERVTEXT, XP_TE_SSLMESSAGE,
|
||||
XP_TE_STOPCONNECT, XP_TE_TOPIC,
|
||||
XP_TE_NEWTOPIC, XP_TE_TOPICDATE,
|
||||
XP_TE_UKNHOST, XP_TE_USERLIMIT,
|
||||
XP_TE_USERSONCHAN, XP_TE_WHOIS_AUTH,
|
||||
XP_TE_WHOIS5, XP_TE_WHOIS2,
|
||||
XP_TE_WHOIS6, XP_TE_WHOIS_ID,
|
||||
XP_TE_WHOIS4, XP_TE_WHOIS4T,
|
||||
XP_TE_WHOIS1, XP_TE_WHOIS_REALHOST,
|
||||
XP_TE_WHOIS3, XP_TE_WHOIS_SPECIAL,
|
||||
XP_TE_UJOIN, XP_TE_UKICK,
|
||||
XP_TE_UPART, XP_TE_UPARTREASON,
|
||||
XP_TE_UACTION, XP_TE_UINVITE,
|
||||
XP_TE_UCHANMSG, XP_TE_UCHANGENICK,
|
||||
NUM_XP
|
||||
};
|
||||
424
src/common/textevents.h
Normal file
424
src/common/textevents.h
Normal file
@@ -0,0 +1,424 @@
|
||||
/* this file is auto generated, edit textevents.in instead! */
|
||||
|
||||
const struct text_event te[] = {
|
||||
|
||||
{"Add Notify", pevt_generic_nick_help, 1,
|
||||
N_("%C22*%O$t$1 added to notify list.")},
|
||||
|
||||
{"Ban List", pevt_banlist_help, 4,
|
||||
N_("%C22*%O$t$1 Banlist:%C19 $4%C20 $2%C21 $3")},
|
||||
|
||||
{"Banned", pevt_generic_channel_help, 1,
|
||||
N_("%C22*%O$tCannot join%C26 %B$1 %O(You are banned).")},
|
||||
|
||||
{"Beep", pevt_generic_none_help, 128,
|
||||
""},
|
||||
|
||||
{"Change Nick", pevt_changenick_help, 2,
|
||||
N_("%C22*%O$t$1 is now known as $2")},
|
||||
|
||||
{"Channel Action", pevt_chanaction_help, 132,
|
||||
"%C18*$t$1%O $2"},
|
||||
|
||||
{"Channel Action Hilight", pevt_chanaction_help, 132,
|
||||
"%C21*%O$t%C21%B$1%O%C21 $2"},
|
||||
|
||||
{"Channel Ban", pevt_chanban_help, 2,
|
||||
N_("%C22*%O$t$1 sets ban on $2")},
|
||||
|
||||
{"Channel Creation", pevt_chandate_help, 2,
|
||||
N_("%C22*%O$tChannel $1 created on $2")},
|
||||
|
||||
{"Channel DeHalfOp", pevt_chandehop_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O removes channel half-operator status from%C26 $2")},
|
||||
|
||||
{"Channel DeOp", pevt_chandeop_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O removes channel operator status from%C26 $2")},
|
||||
|
||||
{"Channel DeVoice", pevt_chandevoice_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O removes voice from%C26 $2")},
|
||||
|
||||
{"Channel Exempt", pevt_chanexempt_help, 2,
|
||||
N_("%C22*%O$t$1 sets exempt on $2")},
|
||||
|
||||
{"Channel Half-Operator", pevt_chanhop_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O gives channel half-operator status to%C26 $2")},
|
||||
|
||||
{"Channel INVITE", pevt_chaninvite_help, 2,
|
||||
N_("%C22*%O$t$1 sets invite on $2")},
|
||||
|
||||
{"Channel List", pevt_generic_none_help, 0,
|
||||
N_("%UChannel Users Topic")},
|
||||
|
||||
{"Channel Message", pevt_chanmsg_help, 132,
|
||||
"%C18%H<%H$4$1%H>%H%O$t$2"},
|
||||
|
||||
{"Channel Mode Generic", pevt_chanmodegen_help, 4,
|
||||
N_("%C22*%O$t$1 sets mode $2$3 $4")},
|
||||
|
||||
{"Channel Modes", pevt_chanmodes_help, 2,
|
||||
N_("%C22*%O$t%C22Channel $1 modes: $2")},
|
||||
|
||||
{"Channel Msg Hilight", pevt_chanmsg_help, 132,
|
||||
"$4%C21%B%H<%H$1%H>%H%O%C21$t$2"},
|
||||
|
||||
{"Channel Notice", pevt_channotice_help, 131,
|
||||
"%C28-%C29$1/$2%C28-%O$t$3"},
|
||||
|
||||
{"Channel Operator", pevt_chanop_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O gives channel operator status to%C26 $2")},
|
||||
|
||||
{"Channel Remove Exempt", pevt_chanrmexempt_help, 2,
|
||||
N_("%C22*%O$t$1 removes exempt on $2")},
|
||||
|
||||
{"Channel Remove Invite", pevt_chanrminvite_help, 2,
|
||||
N_("%C22*%O$t$1 removes invite on $2")},
|
||||
|
||||
{"Channel Remove Keyword", pevt_chanrmkey_help, 1,
|
||||
N_("%C22*%O$t$1 removes channel keyword")},
|
||||
|
||||
{"Channel Remove Limit", pevt_chanrmlimit_help, 1,
|
||||
N_("%C22*%O$t$1 removes user limit")},
|
||||
|
||||
{"Channel Set Key", pevt_chansetkey_help, 2,
|
||||
N_("%C22*%O$t$1 sets channel keyword to $2")},
|
||||
|
||||
{"Channel Set Limit", pevt_chansetlimit_help, 2,
|
||||
N_("%C22*%O$t$1 sets channel limit to $2")},
|
||||
|
||||
{"Channel UnBan", pevt_chanunban_help, 2,
|
||||
N_("%C22*%O$t$1 removes ban on $2")},
|
||||
|
||||
{"Channel Voice", pevt_chanvoice_help, 2,
|
||||
N_("%C22*%O$t%C26$1%O gives voice to%C26 $2")},
|
||||
|
||||
{"Connected", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$t%C22Connected. Now logging in...")},
|
||||
|
||||
{"Connecting", pevt_connect_help, 3,
|
||||
N_("%C22*%O$t%C22Connecting to $1 ($2) port $3%O...")},
|
||||
|
||||
{"Connection Failed", pevt_connfail_help, 1,
|
||||
N_("%C21*%O$t%C21Connection failed. Error: $1")},
|
||||
|
||||
{"CTCP Generic", pevt_ctcpgen_help, 2,
|
||||
N_("%C22*%O$tReceived a CTCP $1 from $2")},
|
||||
|
||||
{"CTCP Generic to Channel", pevt_ctcpgenc_help, 3,
|
||||
N_("%C22*%O$tReceived a CTCP $1 from $2 (to $3)")},
|
||||
|
||||
{"CTCP Send", pevt_ctcpsend_help, 2,
|
||||
N_("%C19>%O$1%C19<%O$tCTCP $2")},
|
||||
|
||||
{"CTCP Sound", pevt_ctcpsnd_help, 2,
|
||||
N_("%C22*%O$tReceived a CTCP Sound $1 from $2")},
|
||||
|
||||
{"CTCP Sound to Channel", pevt_ctcpsnd_help, 3,
|
||||
N_("%C22*%O$tReceived a CTCP Sound $1 from $2 (to $3)")},
|
||||
|
||||
{"DCC CHAT Abort", pevt_dccchatabort_help, 1,
|
||||
N_("%C22*%O$tDCC CHAT to %C26$1%O aborted.")},
|
||||
|
||||
{"DCC CHAT Connect", pevt_dccchatcon_help, 2,
|
||||
N_("%C22*%O$tDCC CHAT connection established to %C26$1 %C30[%O$2%C30]")},
|
||||
|
||||
{"DCC CHAT Failed", pevt_dccchaterr_help, 4,
|
||||
N_("%C22*%O$tDCC CHAT to %C26$1%O lost ($4).")},
|
||||
|
||||
{"DCC CHAT Offer", pevt_generic_nick_help, 1,
|
||||
N_("%C22*%O$tReceived a DCC CHAT offer from $1")},
|
||||
|
||||
{"DCC CHAT Offering", pevt_generic_nick_help, 1,
|
||||
N_("%C22*%O$tOffering DCC CHAT to $1")},
|
||||
|
||||
{"DCC CHAT Reoffer", pevt_generic_nick_help, 1,
|
||||
N_("%C22*%O$tAlready offering CHAT to $1")},
|
||||
|
||||
{"DCC Conection Failed", pevt_dccconfail_help, 3,
|
||||
N_("%C22*%O$tDCC $1 connect attempt to%C26 $2%O failed (err=$3).")},
|
||||
|
||||
{"DCC Generic Offer", pevt_dccgenericoffer_help, 2,
|
||||
N_("%C22*%O$tReceived '$1%O' from $2")},
|
||||
|
||||
{"DCC Header", pevt_generic_none_help, 0,
|
||||
N_("%C24,18 Type To/From Status Size Pos File ")},
|
||||
|
||||
{"DCC Malformed", pevt_malformed_help, 2,
|
||||
N_("%C22*%O$tReceived a malformed DCC request from %C26$1%O.%010%C22*%O$tContents of packet: $2")},
|
||||
|
||||
{"DCC Offer", pevt_dccoffer_help, 3,
|
||||
N_("%C22*%O$tOffering%C26 $1%O to%C26 $2")},
|
||||
|
||||
{"DCC Offer Not Valid", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tNo such DCC offer.")},
|
||||
|
||||
{"DCC RECV Abort", pevt_dccfileabort_help, 2,
|
||||
N_("%C22*%O$tDCC RECV%C26 $2%O to%C26 $1%O aborted.")},
|
||||
|
||||
{"DCC RECV Complete", pevt_dccrecvcomp_help, 4,
|
||||
N_("%C22*%O$tDCC RECV%C26 $1%O from%C26 $3%O complete %C30[%C26$4%O cps%C30]%O.")},
|
||||
|
||||
{"DCC RECV Connect", pevt_dcccon_help, 3,
|
||||
N_("%C22*%O$tDCC RECV connection established to%C26 $1 %C30[%O$2%C30]")},
|
||||
|
||||
{"DCC RECV Failed", pevt_dccrecverr_help, 4,
|
||||
N_("%C22*%O$tDCC RECV%C26 $1%O from%C26 $3%O failed ($4).")},
|
||||
|
||||
{"DCC RECV File Open Error", pevt_generic_file_help, 2,
|
||||
N_("%C22*%O$tDCC RECV: Cannot open $1 for writing ($2).")},
|
||||
|
||||
{"DCC Rename", pevt_dccrename_help, 2,
|
||||
N_("%C22*%O$tThe file%C26 $1%C already exists, saving it as%C26 $2%O instead.")},
|
||||
|
||||
{"DCC RESUME Request", pevt_dccresumeoffer_help, 3,
|
||||
N_("%C22*%O$t%C26$1 %Ohas requested to resume%C26 $2 %Cfrom%C26 $3%C.")},
|
||||
|
||||
{"DCC SEND Abort", pevt_dccfileabort_help, 2,
|
||||
N_("%C22*%O$tDCC SEND%C26 $2%O to%C26 $1%O aborted.")},
|
||||
|
||||
{"DCC SEND Complete", pevt_dccsendcomp_help, 3,
|
||||
N_("%C22*%O$tDCC SEND%C26 $1%O to%C26 $2%O complete %C30[%C26$3%O cps%C30]%O.")},
|
||||
|
||||
{"DCC SEND Connect", pevt_dcccon_help, 3,
|
||||
N_("%C22*%O$tDCC SEND connection established to%C26 $1 %C30[%O$2%C30]")},
|
||||
|
||||
{"DCC SEND Failed", pevt_dccsendfail_help, 3,
|
||||
N_("%C22*%O$tDCC SEND%C26 $1%O to%C26 $2%O failed. $3")},
|
||||
|
||||
{"DCC SEND Offer", pevt_dccsendoffer_help, 4,
|
||||
N_("%C22*%O$t%C26$1 %Ohas offered%C26 $2 %O(%C26$3 %Obytes)")},
|
||||
|
||||
{"DCC Stall", pevt_dccstall_help, 3,
|
||||
N_("%C22*%O$tDCC $1%C26 $2 %Oto%C26 $3 %Cstalled - aborting.")},
|
||||
|
||||
{"DCC Timeout", pevt_dccstall_help, 3,
|
||||
N_("%C22*%O$tDCC $1%C26 $2 %Oto%C26 $3 %Otimed out - aborting.")},
|
||||
|
||||
{"Delete Notify", pevt_generic_nick_help, 1,
|
||||
N_("%C22*%O$t$1 deleted from notify list.")},
|
||||
|
||||
{"Disconnected", pevt_discon_help, 1,
|
||||
N_("%C22*%O$tDisconnected ($1).")},
|
||||
|
||||
{"Found IP", pevt_foundip_help, 1,
|
||||
N_("%C22*%O$tFound your IP: [$1]")},
|
||||
|
||||
{"Generic Message", pevt_genmsg_help, 130,
|
||||
"$1$t$2"},
|
||||
|
||||
{"Ignore Add", pevt_ignoreaddremove_help, 1,
|
||||
N_("%O%C26$1%O added to ignore list.")},
|
||||
|
||||
{"Ignore Changed", pevt_ignoreaddremove_help, 1,
|
||||
N_("Ignore on %C26$1%O changed.")},
|
||||
|
||||
{"Ignore Footer", pevt_generic_none_help, 0,
|
||||
N_("%C24,18 ")},
|
||||
|
||||
{"Ignore Header", pevt_generic_none_help, 0,
|
||||
N_("%C24,18 Hostmask PRIV NOTI CHAN CTCP DCC INVI UNIG ")},
|
||||
|
||||
{"Ignore Remove", pevt_ignoreaddremove_help, 1,
|
||||
N_("%O%C26$1%O removed from ignore list.")},
|
||||
|
||||
{"Ignorelist Empty", pevt_generic_none_help, 0,
|
||||
N_(" Ignore list is empty.")},
|
||||
|
||||
{"Invite", pevt_generic_channel_help, 1,
|
||||
N_("%C22*%O$tCannot join%C26 %B$1 %O(Channel is invite only).")},
|
||||
|
||||
{"Invited", pevt_invited_help, 3,
|
||||
N_("%C22*%O$tYou have been invited to%C26 $1%O by%C26 $2%C (%C26$3%C)")},
|
||||
|
||||
{"Join", pevt_join_help, 3,
|
||||
N_("%C19*%O$t%C19%B$1 %B($3) has joined $2")},
|
||||
|
||||
{"Keyword", pevt_generic_channel_help, 1,
|
||||
N_("%C22*%O$tCannot join%C26 %B$1 %O(Requires keyword).")},
|
||||
|
||||
{"Kick", pevt_kick_help, 4,
|
||||
N_("%C21*%O$t%C21$1 has kicked $2 from $3 ($4%O%C21)")},
|
||||
|
||||
{"Killed", pevt_kill_help, 2,
|
||||
N_("%C22*%O$tYou have been killed by $1 ($2%O%C22)")},
|
||||
|
||||
{"Message Send", pevt_ctcpsend_help, 130,
|
||||
"%C19>%O$1%C19<%O$t$2"},
|
||||
|
||||
{"Motd", pevt_servertext_help, 129,
|
||||
"%C16*%O$t$1%O"},
|
||||
|
||||
{"MOTD Skipped", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$t%C22MOTD Skipped.")},
|
||||
|
||||
{"Nick Clash", pevt_nickclash_help, 2,
|
||||
N_("%C22*%O$t$1 already in use. Retrying with $2...")},
|
||||
|
||||
{"Nick Failed", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tNickname already in use. Use /NICK to try another.")},
|
||||
|
||||
{"No DCC", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tNo such DCC.")},
|
||||
|
||||
{"No Running Process", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tNo process is currently running")},
|
||||
|
||||
{"Notice", pevt_notice_help, 130,
|
||||
"%C28-%C29$1%C28-%O$t$2"},
|
||||
|
||||
{"Notice Send", pevt_ctcpsend_help, 130,
|
||||
"%C19>%O$1%C19<%O$t$2"},
|
||||
|
||||
{"Notify Empty", pevt_generic_none_help, 0,
|
||||
N_("$tNotify list is empty.")},
|
||||
|
||||
{"Notify Header", pevt_generic_none_help, 0,
|
||||
N_("%C24,18 %B Notify List ")},
|
||||
|
||||
{"Notify Number", pevt_notifynumber_help, 1,
|
||||
N_("%C22*%O$t$1 users in notify list.")},
|
||||
|
||||
{"Notify Offline", pevt_generic_nick_help, 3,
|
||||
N_("%C22*%O$tNotify: $1 is offline ($3).")},
|
||||
|
||||
{"Notify Online", pevt_generic_nick_help, 3,
|
||||
N_("%C22*%O$tNotify: $1 is online ($3).")},
|
||||
|
||||
{"Open Dialog", pevt_generic_none_help, 128,
|
||||
""},
|
||||
|
||||
{"Part", pevt_part_help, 3,
|
||||
N_("%C23*%O$t%C23$1 (%O%C23$2) has left $3")},
|
||||
|
||||
{"Part with Reason", pevt_partreason_help, 4,
|
||||
N_("%C23*%O$t%C23$1 (%O%C23$2) has left $3 (%O%C23%B%B$4%O%C23)")},
|
||||
|
||||
{"Ping Reply", pevt_pingrep_help, 2,
|
||||
N_("%C22*%O$tPing reply from $1: $2 second(s)")},
|
||||
|
||||
{"Ping Timeout", pevt_pingtimeout_help, 1,
|
||||
N_("%C22*%O$tNo ping reply for $1 seconds, disconnecting.")},
|
||||
|
||||
{"Private Action", pevt_privmsg_help, 131,
|
||||
"%C18**$t$3$1%O $2 %C18**"},
|
||||
|
||||
{"Private Action to Dialog", pevt_privmsg_help, 131,
|
||||
"%C18*$t$3$1%O $2"},
|
||||
|
||||
{"Private Message", pevt_privmsg_help, 131,
|
||||
"%C28*%C29$3$1%C28*$t%O$2"},
|
||||
|
||||
{"Private Message to Dialog", pevt_privmsg_help, 131,
|
||||
"%C18%H<%H$3$1%H>%H%O$t$2"},
|
||||
|
||||
{"Process Already Running", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tA process is already running")},
|
||||
|
||||
{"Quit", pevt_quit_help, 3,
|
||||
N_("%C23*%O$t%C23$1 has quit (%O%C23%B%B$2%O%C23)")},
|
||||
|
||||
{"Raw Modes", pevt_rawmodes_help, 2,
|
||||
N_("%C22*%O$t$1 sets modes%B %C30[%O$2%B%C30]")},
|
||||
|
||||
{"Receive Wallops", pevt_privmsg_help, 2,
|
||||
N_("%C28-%C29$1/Wallops%C28-%O$t$2")},
|
||||
|
||||
{"Resolving User", pevt_resolvinguser_help, 2,
|
||||
N_("%C22*%O$tLooking up IP number for%C26 $1%O...")},
|
||||
|
||||
{"Server Connected", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$t%C22Connected.")},
|
||||
|
||||
{"Server Error", pevt_servererror_help, 129,
|
||||
"%C22*%O$t$1"},
|
||||
|
||||
{"Server Lookup", pevt_serverlookup_help, 1,
|
||||
N_("%C22*%O$t%C22Looking up $1")},
|
||||
|
||||
{"Server Notice", pevt_servertext_help, 130,
|
||||
"%C22*%O$t$1"},
|
||||
|
||||
{"Server Text", pevt_servertext_help, 131,
|
||||
"%C22*%O$t$1"},
|
||||
|
||||
{"SSL Message", pevt_sslmessage_help, 130,
|
||||
"%C22*%O$t$1"},
|
||||
|
||||
{"Stop Connection", pevt_sconnect_help, 1,
|
||||
N_("%C22*%O$tStopped previous connection attempt (pid=$1)")},
|
||||
|
||||
{"Topic", pevt_topic_help, 2,
|
||||
N_("%C29*%O$t%C29Topic for $1%C %C29is: $2")},
|
||||
|
||||
{"Topic Change", pevt_newtopic_help, 3,
|
||||
N_("%C22*%O$t$1 has changed the topic to: $2")},
|
||||
|
||||
{"Topic Creation", pevt_topicdate_help, 3,
|
||||
N_("%C29*%O$t%C29Topic for $1%C %C29set by $2%C %C29at $3")},
|
||||
|
||||
{"Unknown Host", pevt_generic_none_help, 0,
|
||||
N_("%C22*%O$tUnknown host. Maybe you misspelled it?")},
|
||||
|
||||
{"User Limit", pevt_generic_channel_help, 1,
|
||||
N_("%C22*%O$tCannot join%C26 %B$1 %O(User limit reached).")},
|
||||
|
||||
{"Users On Channel", pevt_usersonchan_help, 2,
|
||||
N_("%C22*%O$t%C26Users on $1:%C $2")},
|
||||
|
||||
{"WhoIs Authenticated", pevt_whoisauth_help, 3,
|
||||
N_("%C22*%O$t%C28[%O$1%C28] %O$2%C27 $3")},
|
||||
|
||||
{"WhoIs Away Line", pevt_whois5_help, 2,
|
||||
N_("%C22*%O$t%C28[%O$1%C28] %Cis away %C30(%O$2%O%C30)")},
|
||||
|
||||
{"WhoIs Channel/Oper Line", pevt_whois2_help, 2,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O $2")},
|
||||
|
||||
{"WhoIs End", pevt_whois6_help, 1,
|
||||
N_("%C22*%O$t%C28[%O$1%C28] %OEnd of WHOIS list.")},
|
||||
|
||||
{"WhoIs Identified", pevt_whoisid_help, 2,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O $2")},
|
||||
|
||||
{"WhoIs Idle Line", pevt_whois4_help, 2,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O idle%C26 $2")},
|
||||
|
||||
{"WhoIs Idle Line with Signon", pevt_whois4t_help, 3,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O idle%C26 $2%O, signon:%C26 $3")},
|
||||
|
||||
{"WhoIs Name Line", pevt_whois1_help, 4,
|
||||
N_("%C22*%O$t%C28[%O$1%C28] %C30(%O$2@$3%C30)%O: $4")},
|
||||
|
||||
{"WhoIs Real Host", pevt_whoisrealhost_help, 4,
|
||||
N_("%C22*%O$t%C28[%O$1%C28] %Oreal user@host%C27 $2%O, real IP%C27 $3")},
|
||||
|
||||
{"WhoIs Server Line", pevt_whois3_help, 2,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O $2")},
|
||||
|
||||
{"WhoIs Special", pevt_whoisid_help, 3,
|
||||
N_("%C22*%O$t%C28[%O$1%C28]%O $2")},
|
||||
|
||||
{"You Join", pevt_join_help, 3,
|
||||
N_("%C19*%O$t%C19Now talking on $2")},
|
||||
|
||||
{"You Kicked", pevt_ukick_help, 4,
|
||||
N_("%C23*$tYou have been kicked from $2 by $3 ($4%O%C23)")},
|
||||
|
||||
{"You Part", pevt_part_help, 3,
|
||||
N_("%C23*$tYou have left channel $3")},
|
||||
|
||||
{"You Part with Reason", pevt_partreason_help, 4,
|
||||
N_("%C23*$tYou have left channel $3 (%O%C23%B%B$4%O%C23)")},
|
||||
|
||||
{"Your Action", pevt_chanaction_help, 131,
|
||||
"%C18*$t$1%O $2"},
|
||||
|
||||
{"Your Invitation", pevt_uinvite_help, 3,
|
||||
N_("%C22*%O$tYou've invited%C26 $1%O to%C26 $2%O (%C26$3%O)")},
|
||||
|
||||
{"Your Message", pevt_chanmsg_help, 132,
|
||||
"%C31%H<%H$4$1%H>%H%O%C30$t$2"},
|
||||
|
||||
{"Your Nick Changing", pevt_uchangenick_help, 2,
|
||||
N_("%C22*%O$tYou are now known as $2")},
|
||||
};
|
||||
840
src/common/textevents.in
Normal file
840
src/common/textevents.in
Normal file
@@ -0,0 +1,840 @@
|
||||
Add Notify
|
||||
XP_TE_ADDNOTIFY
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$t$1 added to notify list.
|
||||
1
|
||||
|
||||
Ban List
|
||||
XP_TE_BANLIST
|
||||
pevt_banlist_help
|
||||
%C22*%O$t$1 Banlist:%C19 $4%C20 $2%C21 $3
|
||||
4
|
||||
|
||||
Banned
|
||||
XP_TE_BANNED
|
||||
pevt_generic_channel_help
|
||||
%C22*%O$tCannot join%C26 %B$1 %O(You are banned).
|
||||
1
|
||||
|
||||
Beep
|
||||
XP_TE_BEEP
|
||||
pevt_generic_none_help
|
||||
|
||||
n0
|
||||
|
||||
Change Nick
|
||||
XP_TE_CHANGENICK
|
||||
pevt_changenick_help
|
||||
%C22*%O$t$1 is now known as $2
|
||||
2
|
||||
|
||||
Channel Action
|
||||
XP_TE_CHANACTION
|
||||
pevt_chanaction_help
|
||||
%C18*$t$1%O $2
|
||||
n4
|
||||
|
||||
Channel Action Hilight
|
||||
XP_TE_HCHANACTION
|
||||
pevt_chanaction_help
|
||||
%C21*%O$t%C21%B$1%O%C21 $2
|
||||
n4
|
||||
|
||||
Channel Ban
|
||||
XP_TE_CHANBAN
|
||||
pevt_chanban_help
|
||||
%C22*%O$t$1 sets ban on $2
|
||||
2
|
||||
|
||||
Channel Creation
|
||||
XP_TE_CHANDATE
|
||||
pevt_chandate_help
|
||||
%C22*%O$tChannel $1 created on $2
|
||||
2
|
||||
|
||||
Channel DeHalfOp
|
||||
XP_TE_CHANDEHOP
|
||||
pevt_chandehop_help
|
||||
%C22*%O$t%C26$1%O removes channel half-operator status from%C26 $2
|
||||
2
|
||||
|
||||
Channel DeOp
|
||||
XP_TE_CHANDEOP
|
||||
pevt_chandeop_help
|
||||
%C22*%O$t%C26$1%O removes channel operator status from%C26 $2
|
||||
2
|
||||
|
||||
Channel DeVoice
|
||||
XP_TE_CHANDEVOICE
|
||||
pevt_chandevoice_help
|
||||
%C22*%O$t%C26$1%O removes voice from%C26 $2
|
||||
2
|
||||
|
||||
Channel Exempt
|
||||
XP_TE_CHANEXEMPT
|
||||
pevt_chanexempt_help
|
||||
%C22*%O$t$1 sets exempt on $2
|
||||
2
|
||||
|
||||
Channel Half-Operator
|
||||
XP_TE_CHANHOP
|
||||
pevt_chanhop_help
|
||||
%C22*%O$t%C26$1%O gives channel half-operator status to%C26 $2
|
||||
2
|
||||
|
||||
Channel INVITE
|
||||
XP_TE_CHANINVITE
|
||||
pevt_chaninvite_help
|
||||
%C22*%O$t$1 sets invite on $2
|
||||
2
|
||||
|
||||
Channel List
|
||||
XP_TE_CHANLISTHEAD
|
||||
pevt_generic_none_help
|
||||
%UChannel Users Topic
|
||||
0
|
||||
|
||||
Channel Message
|
||||
XP_TE_CHANMSG
|
||||
pevt_chanmsg_help
|
||||
%C18%H<%H$4$1%H>%H%O$t$2
|
||||
n4
|
||||
|
||||
Channel Mode Generic
|
||||
XP_TE_CHANMODEGEN
|
||||
pevt_chanmodegen_help
|
||||
%C22*%O$t$1 sets mode $2$3 $4
|
||||
4
|
||||
|
||||
Channel Modes
|
||||
XP_TE_CHANMODES
|
||||
pevt_chanmodes_help
|
||||
%C22*%O$t%C22Channel $1 modes: $2
|
||||
2
|
||||
|
||||
Channel Msg Hilight
|
||||
XP_TE_HCHANMSG
|
||||
pevt_chanmsg_help
|
||||
$4%C21%B%H<%H$1%H>%H%O%C21$t$2
|
||||
n4
|
||||
|
||||
Channel Notice
|
||||
XP_TE_CHANNOTICE
|
||||
pevt_channotice_help
|
||||
%C28-%C29$1/$2%C28-%O$t$3
|
||||
n3
|
||||
|
||||
Channel Operator
|
||||
XP_TE_CHANOP
|
||||
pevt_chanop_help
|
||||
%C22*%O$t%C26$1%O gives channel operator status to%C26 $2
|
||||
2
|
||||
|
||||
Channel Remove Exempt
|
||||
XP_TE_CHANRMEXEMPT
|
||||
pevt_chanrmexempt_help
|
||||
%C22*%O$t$1 removes exempt on $2
|
||||
2
|
||||
|
||||
Channel Remove Invite
|
||||
XP_TE_CHANRMINVITE
|
||||
pevt_chanrminvite_help
|
||||
%C22*%O$t$1 removes invite on $2
|
||||
2
|
||||
|
||||
Channel Remove Keyword
|
||||
XP_TE_CHANRMKEY
|
||||
pevt_chanrmkey_help
|
||||
%C22*%O$t$1 removes channel keyword
|
||||
1
|
||||
|
||||
Channel Remove Limit
|
||||
XP_TE_CHANRMLIMIT
|
||||
pevt_chanrmlimit_help
|
||||
%C22*%O$t$1 removes user limit
|
||||
1
|
||||
|
||||
Channel Set Key
|
||||
XP_TE_CHANSETKEY
|
||||
pevt_chansetkey_help
|
||||
%C22*%O$t$1 sets channel keyword to $2
|
||||
2
|
||||
|
||||
Channel Set Limit
|
||||
XP_TE_CHANSETLIMIT
|
||||
pevt_chansetlimit_help
|
||||
%C22*%O$t$1 sets channel limit to $2
|
||||
2
|
||||
|
||||
Channel UnBan
|
||||
XP_TE_CHANUNBAN
|
||||
pevt_chanunban_help
|
||||
%C22*%O$t$1 removes ban on $2
|
||||
2
|
||||
|
||||
Channel Voice
|
||||
XP_TE_CHANVOICE
|
||||
pevt_chanvoice_help
|
||||
%C22*%O$t%C26$1%O gives voice to%C26 $2
|
||||
2
|
||||
|
||||
Connected
|
||||
XP_TE_CONNECTED
|
||||
pevt_generic_none_help
|
||||
%C22*%O$t%C22Connected. Now logging in...
|
||||
0
|
||||
|
||||
Connecting
|
||||
XP_TE_CONNECT
|
||||
pevt_connect_help
|
||||
%C22*%O$t%C22Connecting to $1 ($2) port $3%O...
|
||||
3
|
||||
|
||||
Connection Failed
|
||||
XP_TE_CONNFAIL
|
||||
pevt_connfail_help
|
||||
%C21*%O$t%C21Connection failed. Error: $1
|
||||
1
|
||||
|
||||
CTCP Generic
|
||||
XP_TE_CTCPGEN
|
||||
pevt_ctcpgen_help
|
||||
%C22*%O$tReceived a CTCP $1 from $2
|
||||
2
|
||||
|
||||
CTCP Generic to Channel
|
||||
XP_TE_CTCPGENC
|
||||
pevt_ctcpgenc_help
|
||||
%C22*%O$tReceived a CTCP $1 from $2 (to $3)
|
||||
3
|
||||
|
||||
CTCP Send
|
||||
XP_TE_CTCPSEND
|
||||
pevt_ctcpsend_help
|
||||
%C19>%O$1%C19<%O$tCTCP $2
|
||||
2
|
||||
|
||||
CTCP Sound
|
||||
XP_TE_CTCPSND
|
||||
pevt_ctcpsnd_help
|
||||
%C22*%O$tReceived a CTCP Sound $1 from $2
|
||||
2
|
||||
|
||||
CTCP Sound to Channel
|
||||
XP_TE_CTCPSNDC
|
||||
pevt_ctcpsnd_help
|
||||
%C22*%O$tReceived a CTCP Sound $1 from $2 (to $3)
|
||||
3
|
||||
|
||||
DCC CHAT Abort
|
||||
XP_TE_DCCCHATABORT
|
||||
pevt_dccchatabort_help
|
||||
%C22*%O$tDCC CHAT to %C26$1%O aborted.
|
||||
1
|
||||
|
||||
DCC CHAT Connect
|
||||
XP_TE_DCCCONCHAT
|
||||
pevt_dccchatcon_help
|
||||
%C22*%O$tDCC CHAT connection established to %C26$1 %C30[%O$2%C30]
|
||||
2
|
||||
|
||||
DCC CHAT Failed
|
||||
XP_TE_DCCCHATF
|
||||
pevt_dccchaterr_help
|
||||
%C22*%O$tDCC CHAT to %C26$1%O lost ($4).
|
||||
4
|
||||
|
||||
DCC CHAT Offer
|
||||
XP_TE_DCCCHATOFFER
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$tReceived a DCC CHAT offer from $1
|
||||
1
|
||||
|
||||
DCC CHAT Offering
|
||||
XP_TE_DCCCHATOFFERING
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$tOffering DCC CHAT to $1
|
||||
1
|
||||
|
||||
DCC CHAT Reoffer
|
||||
XP_TE_DCCCHATREOFFER
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$tAlready offering CHAT to $1
|
||||
1
|
||||
|
||||
DCC Conection Failed
|
||||
XP_TE_DCCCONFAIL
|
||||
pevt_dccconfail_help
|
||||
%C22*%O$tDCC $1 connect attempt to%C26 $2%O failed (err=$3).
|
||||
3
|
||||
|
||||
DCC Generic Offer
|
||||
XP_TE_DCCGENERICOFFER
|
||||
pevt_dccgenericoffer_help
|
||||
%C22*%O$tReceived '$1%O' from $2
|
||||
2
|
||||
|
||||
DCC Header
|
||||
XP_TE_DCCHEAD
|
||||
pevt_generic_none_help
|
||||
%C24,18 Type To/From Status Size Pos File
|
||||
0
|
||||
|
||||
DCC Malformed
|
||||
XP_TE_MALFORMED
|
||||
pevt_malformed_help
|
||||
%C22*%O$tReceived a malformed DCC request from %C26$1%O.%010%C22*%O$tContents of packet: $2
|
||||
2
|
||||
|
||||
DCC Offer
|
||||
XP_TE_DCCOFFER
|
||||
pevt_dccoffer_help
|
||||
%C22*%O$tOffering%C26 $1%O to%C26 $2
|
||||
3
|
||||
|
||||
DCC Offer Not Valid
|
||||
XP_TE_DCCIVAL
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tNo such DCC offer.
|
||||
0
|
||||
|
||||
DCC RECV Abort
|
||||
XP_TE_DCCRECVABORT
|
||||
pevt_dccfileabort_help
|
||||
%C22*%O$tDCC RECV%C26 $2%O to%C26 $1%O aborted.
|
||||
2
|
||||
|
||||
DCC RECV Complete
|
||||
XP_TE_DCCRECVCOMP
|
||||
pevt_dccrecvcomp_help
|
||||
%C22*%O$tDCC RECV%C26 $1%O from%C26 $3%O complete %C30[%C26$4%O cps%C30]%O.
|
||||
4
|
||||
|
||||
DCC RECV Connect
|
||||
XP_TE_DCCCONRECV
|
||||
pevt_dcccon_help
|
||||
%C22*%O$tDCC RECV connection established to%C26 $1 %C30[%O$2%C30]
|
||||
3
|
||||
|
||||
DCC RECV Failed
|
||||
XP_TE_DCCRECVERR
|
||||
pevt_dccrecverr_help
|
||||
%C22*%O$tDCC RECV%C26 $1%O from%C26 $3%O failed ($4).
|
||||
4
|
||||
|
||||
DCC RECV File Open Error
|
||||
XP_TE_DCCFILEERR
|
||||
pevt_generic_file_help
|
||||
%C22*%O$tDCC RECV: Cannot open $1 for writing ($2).
|
||||
2
|
||||
|
||||
DCC Rename
|
||||
XP_TE_DCCRENAME
|
||||
pevt_dccrename_help
|
||||
%C22*%O$tThe file%C26 $1%C already exists, saving it as%C26 $2%O instead.
|
||||
2
|
||||
|
||||
DCC RESUME Request
|
||||
XP_TE_DCCRESUMEREQUEST
|
||||
pevt_dccresumeoffer_help
|
||||
%C22*%O$t%C26$1 %Ohas requested to resume%C26 $2 %Cfrom%C26 $3%C.
|
||||
3
|
||||
|
||||
DCC SEND Abort
|
||||
XP_TE_DCCSENDABORT
|
||||
pevt_dccfileabort_help
|
||||
%C22*%O$tDCC SEND%C26 $2%O to%C26 $1%O aborted.
|
||||
2
|
||||
|
||||
DCC SEND Complete
|
||||
XP_TE_DCCSENDCOMP
|
||||
pevt_dccsendcomp_help
|
||||
%C22*%O$tDCC SEND%C26 $1%O to%C26 $2%O complete %C30[%C26$3%O cps%C30]%O.
|
||||
3
|
||||
|
||||
DCC SEND Connect
|
||||
XP_TE_DCCCONSEND
|
||||
pevt_dcccon_help
|
||||
%C22*%O$tDCC SEND connection established to%C26 $1 %C30[%O$2%C30]
|
||||
3
|
||||
|
||||
DCC SEND Failed
|
||||
XP_TE_DCCSENDFAIL
|
||||
pevt_dccsendfail_help
|
||||
%C22*%O$tDCC SEND%C26 $1%O to%C26 $2%O failed. $3
|
||||
3
|
||||
|
||||
DCC SEND Offer
|
||||
XP_TE_DCCSENDOFFER
|
||||
pevt_dccsendoffer_help
|
||||
%C22*%O$t%C26$1 %Ohas offered%C26 $2 %O(%C26$3 %Obytes)
|
||||
4
|
||||
|
||||
DCC Stall
|
||||
XP_TE_DCCSTALL
|
||||
pevt_dccstall_help
|
||||
%C22*%O$tDCC $1%C26 $2 %Oto%C26 $3 %Cstalled - aborting.
|
||||
3
|
||||
|
||||
DCC Timeout
|
||||
XP_TE_DCCTOUT
|
||||
pevt_dccstall_help
|
||||
%C22*%O$tDCC $1%C26 $2 %Oto%C26 $3 %Otimed out - aborting.
|
||||
3
|
||||
|
||||
Delete Notify
|
||||
XP_TE_DELNOTIFY
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$t$1 deleted from notify list.
|
||||
1
|
||||
|
||||
Disconnected
|
||||
XP_TE_DISCON
|
||||
pevt_discon_help
|
||||
%C22*%O$tDisconnected ($1).
|
||||
1
|
||||
|
||||
Found IP
|
||||
XP_TE_FOUNDIP
|
||||
pevt_foundip_help
|
||||
%C22*%O$tFound your IP: [$1]
|
||||
1
|
||||
|
||||
Generic Message
|
||||
XP_TE_GENMSG
|
||||
pevt_genmsg_help
|
||||
$1$t$2
|
||||
n2
|
||||
|
||||
Ignore Add
|
||||
XP_TE_IGNOREADD
|
||||
pevt_ignoreaddremove_help
|
||||
%O%C26$1%O added to ignore list.
|
||||
1
|
||||
|
||||
Ignore Changed
|
||||
XP_TE_IGNORECHANGE
|
||||
pevt_ignoreaddremove_help
|
||||
Ignore on %C26$1%O changed.
|
||||
1
|
||||
|
||||
Ignore Footer
|
||||
XP_TE_IGNOREFOOTER
|
||||
pevt_generic_none_help
|
||||
%C24,18
|
||||
0
|
||||
|
||||
Ignore Header
|
||||
XP_TE_IGNOREHEADER
|
||||
pevt_generic_none_help
|
||||
%C24,18 Hostmask PRIV NOTI CHAN CTCP DCC INVI UNIG
|
||||
0
|
||||
|
||||
Ignore Remove
|
||||
XP_TE_IGNOREREMOVE
|
||||
pevt_ignoreaddremove_help
|
||||
%O%C26$1%O removed from ignore list.
|
||||
1
|
||||
|
||||
Ignorelist Empty
|
||||
XP_TE_IGNOREEMPTY
|
||||
pevt_generic_none_help
|
||||
Ignore list is empty.
|
||||
0
|
||||
|
||||
Invite
|
||||
XP_TE_INVITE
|
||||
pevt_generic_channel_help
|
||||
%C22*%O$tCannot join%C26 %B$1 %O(Channel is invite only).
|
||||
1
|
||||
|
||||
Invited
|
||||
XP_TE_INVITED
|
||||
pevt_invited_help
|
||||
%C22*%O$tYou have been invited to%C26 $1%O by%C26 $2%C (%C26$3%C)
|
||||
3
|
||||
|
||||
Join
|
||||
XP_TE_JOIN
|
||||
pevt_join_help
|
||||
%C19*%O$t%C19%B$1 %B($3) has joined $2
|
||||
3
|
||||
|
||||
Keyword
|
||||
XP_TE_KEYWORD
|
||||
pevt_generic_channel_help
|
||||
%C22*%O$tCannot join%C26 %B$1 %O(Requires keyword).
|
||||
1
|
||||
|
||||
Kick
|
||||
XP_TE_KICK
|
||||
pevt_kick_help
|
||||
%C21*%O$t%C21$1 has kicked $2 from $3 ($4%O%C21)
|
||||
4
|
||||
|
||||
Killed
|
||||
XP_TE_KILL
|
||||
pevt_kill_help
|
||||
%C22*%O$tYou have been killed by $1 ($2%O%C22)
|
||||
2
|
||||
|
||||
Message Send
|
||||
XP_TE_MSGSEND
|
||||
pevt_ctcpsend_help
|
||||
%C19>%O$1%C19<%O$t$2
|
||||
n2
|
||||
|
||||
Motd
|
||||
XP_TE_MOTD
|
||||
pevt_servertext_help
|
||||
%C16*%O$t$1%O
|
||||
n1
|
||||
|
||||
MOTD Skipped
|
||||
XP_TE_MOTDSKIP
|
||||
pevt_generic_none_help
|
||||
%C22*%O$t%C22MOTD Skipped.
|
||||
0
|
||||
|
||||
Nick Clash
|
||||
XP_TE_NICKCLASH
|
||||
pevt_nickclash_help
|
||||
%C22*%O$t$1 already in use. Retrying with $2...
|
||||
2
|
||||
|
||||
Nick Failed
|
||||
XP_TE_NICKFAIL
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tNickname already in use. Use /NICK to try another.
|
||||
0
|
||||
|
||||
No DCC
|
||||
XP_TE_NODCC
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tNo such DCC.
|
||||
0
|
||||
|
||||
No Running Process
|
||||
XP_TE_NOCHILD
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tNo process is currently running
|
||||
0
|
||||
|
||||
Notice
|
||||
XP_TE_NOTICE
|
||||
pevt_notice_help
|
||||
%C28-%C29$1%C28-%O$t$2
|
||||
n2
|
||||
|
||||
Notice Send
|
||||
XP_TE_NOTICESEND
|
||||
pevt_ctcpsend_help
|
||||
%C19>%O$1%C19<%O$t$2
|
||||
n2
|
||||
|
||||
Notify Empty
|
||||
XP_TE_NOTIFYEMPTY
|
||||
pevt_generic_none_help
|
||||
$tNotify list is empty.
|
||||
0
|
||||
|
||||
Notify Header
|
||||
XP_TE_NOTIFYHEAD
|
||||
pevt_generic_none_help
|
||||
%C24,18 %B Notify List
|
||||
0
|
||||
|
||||
Notify Number
|
||||
XP_TE_NOTIFYNUMBER
|
||||
pevt_notifynumber_help
|
||||
%C22*%O$t$1 users in notify list.
|
||||
1
|
||||
|
||||
Notify Offline
|
||||
XP_TE_NOTIFYOFFLINE
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$tNotify: $1 is offline ($3).
|
||||
3
|
||||
|
||||
Notify Online
|
||||
XP_TE_NOTIFYONLINE
|
||||
pevt_generic_nick_help
|
||||
%C22*%O$tNotify: $1 is online ($3).
|
||||
3
|
||||
|
||||
Open Dialog
|
||||
XP_TE_OPENDIALOG
|
||||
pevt_generic_none_help
|
||||
|
||||
n0
|
||||
|
||||
Part
|
||||
XP_TE_PART
|
||||
pevt_part_help
|
||||
%C23*%O$t%C23$1 (%O%C23$2) has left $3
|
||||
3
|
||||
|
||||
Part with Reason
|
||||
XP_TE_PARTREASON
|
||||
pevt_partreason_help
|
||||
%C23*%O$t%C23$1 (%O%C23$2) has left $3 (%O%C23%B%B$4%O%C23)
|
||||
4
|
||||
|
||||
Ping Reply
|
||||
XP_TE_PINGREP
|
||||
pevt_pingrep_help
|
||||
%C22*%O$tPing reply from $1: $2 second(s)
|
||||
2
|
||||
|
||||
Ping Timeout
|
||||
XP_TE_PINGTIMEOUT
|
||||
pevt_pingtimeout_help
|
||||
%C22*%O$tNo ping reply for $1 seconds, disconnecting.
|
||||
1
|
||||
|
||||
Private Action
|
||||
XP_TE_PRIVACTION
|
||||
pevt_privmsg_help
|
||||
%C18**$t$3$1%O $2 %C18**
|
||||
n3
|
||||
|
||||
Private Action to Dialog
|
||||
XP_TE_DPRIVACTION
|
||||
pevt_privmsg_help
|
||||
%C18*$t$3$1%O $2
|
||||
n3
|
||||
|
||||
Private Message
|
||||
XP_TE_PRIVMSG
|
||||
pevt_privmsg_help
|
||||
%C28*%C29$3$1%C28*$t%O$2
|
||||
n3
|
||||
|
||||
Private Message to Dialog
|
||||
XP_TE_DPRIVMSG
|
||||
pevt_privmsg_help
|
||||
%C18%H<%H$3$1%H>%H%O$t$2
|
||||
n3
|
||||
|
||||
Process Already Running
|
||||
XP_TE_ALREADYPROCESS
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tA process is already running
|
||||
0
|
||||
|
||||
Quit
|
||||
XP_TE_QUIT
|
||||
pevt_quit_help
|
||||
%C23*%O$t%C23$1 has quit (%O%C23%B%B$2%O%C23)
|
||||
3
|
||||
|
||||
Raw Modes
|
||||
XP_TE_RAWMODES
|
||||
pevt_rawmodes_help
|
||||
%C22*%O$t$1 sets modes%B %C30[%O$2%B%C30]
|
||||
2
|
||||
|
||||
Receive Wallops
|
||||
XP_TE_WALLOPS
|
||||
pevt_privmsg_help
|
||||
%C28-%C29$1/Wallops%C28-%O$t$2
|
||||
2
|
||||
|
||||
Resolving User
|
||||
XP_TE_RESOLVINGUSER
|
||||
pevt_resolvinguser_help
|
||||
%C22*%O$tLooking up IP number for%C26 $1%O...
|
||||
2
|
||||
|
||||
Server Connected
|
||||
XP_TE_SERVERCONNECTED
|
||||
pevt_generic_none_help
|
||||
%C22*%O$t%C22Connected.
|
||||
0
|
||||
|
||||
Server Error
|
||||
XP_TE_SERVERERROR
|
||||
pevt_servererror_help
|
||||
%C22*%O$t$1
|
||||
n1
|
||||
|
||||
Server Lookup
|
||||
XP_TE_SERVERLOOKUP
|
||||
pevt_serverlookup_help
|
||||
%C22*%O$t%C22Looking up $1
|
||||
1
|
||||
|
||||
Server Notice
|
||||
XP_TE_SERVNOTICE
|
||||
pevt_servertext_help
|
||||
%C22*%O$t$1
|
||||
n2
|
||||
|
||||
Server Text
|
||||
XP_TE_SERVTEXT
|
||||
pevt_servertext_help
|
||||
%C22*%O$t$1
|
||||
n3
|
||||
|
||||
SSL Message
|
||||
XP_TE_SSLMESSAGE
|
||||
pevt_sslmessage_help
|
||||
%C22*%O$t$1
|
||||
n2
|
||||
|
||||
Stop Connection
|
||||
XP_TE_STOPCONNECT
|
||||
pevt_sconnect_help
|
||||
%C22*%O$tStopped previous connection attempt (pid=$1)
|
||||
1
|
||||
|
||||
Topic
|
||||
XP_TE_TOPIC
|
||||
pevt_topic_help
|
||||
%C29*%O$t%C29Topic for $1%C %C29is: $2
|
||||
2
|
||||
|
||||
Topic Change
|
||||
XP_TE_NEWTOPIC
|
||||
pevt_newtopic_help
|
||||
%C22*%O$t$1 has changed the topic to: $2
|
||||
3
|
||||
|
||||
Topic Creation
|
||||
XP_TE_TOPICDATE
|
||||
pevt_topicdate_help
|
||||
%C29*%O$t%C29Topic for $1%C %C29set by $2%C %C29at $3
|
||||
3
|
||||
|
||||
Unknown Host
|
||||
XP_TE_UKNHOST
|
||||
pevt_generic_none_help
|
||||
%C22*%O$tUnknown host. Maybe you misspelled it?
|
||||
0
|
||||
|
||||
User Limit
|
||||
XP_TE_USERLIMIT
|
||||
pevt_generic_channel_help
|
||||
%C22*%O$tCannot join%C26 %B$1 %O(User limit reached).
|
||||
1
|
||||
|
||||
Users On Channel
|
||||
XP_TE_USERSONCHAN
|
||||
pevt_usersonchan_help
|
||||
%C22*%O$t%C26Users on $1:%C $2
|
||||
2
|
||||
|
||||
WhoIs Authenticated
|
||||
XP_TE_WHOIS_AUTH
|
||||
pevt_whoisauth_help
|
||||
%C22*%O$t%C28[%O$1%C28] %O$2%C27 $3
|
||||
3
|
||||
|
||||
WhoIs Away Line
|
||||
XP_TE_WHOIS5
|
||||
pevt_whois5_help
|
||||
%C22*%O$t%C28[%O$1%C28] %Cis away %C30(%O$2%O%C30)
|
||||
2
|
||||
|
||||
WhoIs Channel/Oper Line
|
||||
XP_TE_WHOIS2
|
||||
pevt_whois2_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O $2
|
||||
2
|
||||
|
||||
WhoIs End
|
||||
XP_TE_WHOIS6
|
||||
pevt_whois6_help
|
||||
%C22*%O$t%C28[%O$1%C28] %OEnd of WHOIS list.
|
||||
1
|
||||
|
||||
WhoIs Identified
|
||||
XP_TE_WHOIS_ID
|
||||
pevt_whoisid_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O $2
|
||||
2
|
||||
|
||||
WhoIs Idle Line
|
||||
XP_TE_WHOIS4
|
||||
pevt_whois4_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O idle%C26 $2
|
||||
2
|
||||
|
||||
WhoIs Idle Line with Signon
|
||||
XP_TE_WHOIS4T
|
||||
pevt_whois4t_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O idle%C26 $2%O, signon:%C26 $3
|
||||
3
|
||||
|
||||
WhoIs Name Line
|
||||
XP_TE_WHOIS1
|
||||
pevt_whois1_help
|
||||
%C22*%O$t%C28[%O$1%C28] %C30(%O$2@$3%C30)%O: $4
|
||||
4
|
||||
|
||||
WhoIs Real Host
|
||||
XP_TE_WHOIS_REALHOST
|
||||
pevt_whoisrealhost_help
|
||||
%C22*%O$t%C28[%O$1%C28] %Oreal user@host%C27 $2%O, real IP%C27 $3
|
||||
4
|
||||
|
||||
WhoIs Server Line
|
||||
XP_TE_WHOIS3
|
||||
pevt_whois3_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O $2
|
||||
2
|
||||
|
||||
WhoIs Special
|
||||
XP_TE_WHOIS_SPECIAL
|
||||
pevt_whoisid_help
|
||||
%C22*%O$t%C28[%O$1%C28]%O $2
|
||||
3
|
||||
|
||||
You Join
|
||||
XP_TE_UJOIN
|
||||
pevt_join_help
|
||||
%C19*%O$t%C19Now talking on $2
|
||||
3
|
||||
|
||||
You Kicked
|
||||
XP_TE_UKICK
|
||||
pevt_ukick_help
|
||||
%C23*$tYou have been kicked from $2 by $3 ($4%O%C23)
|
||||
4
|
||||
|
||||
You Part
|
||||
XP_TE_UPART
|
||||
pevt_part_help
|
||||
%C23*$tYou have left channel $3
|
||||
3
|
||||
|
||||
You Part with Reason
|
||||
XP_TE_UPARTREASON
|
||||
pevt_partreason_help
|
||||
%C23*$tYou have left channel $3 (%O%C23%B%B$4%O%C23)
|
||||
4
|
||||
|
||||
Your Action
|
||||
XP_TE_UACTION
|
||||
pevt_chanaction_help
|
||||
%C18*$t$1%O $2
|
||||
n3
|
||||
|
||||
Your Invitation
|
||||
XP_TE_UINVITE
|
||||
pevt_uinvite_help
|
||||
%C22*%O$tYou've invited%C26 $1%O to%C26 $2%O (%C26$3%O)
|
||||
3
|
||||
|
||||
Your Message
|
||||
XP_TE_UCHANMSG
|
||||
pevt_chanmsg_help
|
||||
%C31%H<%H$4$1%H>%H%O%C30$t$2
|
||||
n4
|
||||
|
||||
Your Nick Changing
|
||||
XP_TE_UCHANGENICK
|
||||
pevt_uchangenick_help
|
||||
%C22*%O$tYou are now known as $2
|
||||
2
|
||||
|
||||
215
src/common/tree.c
Normal file
215
src/common/tree.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
This is used for quick userlist insertion and lookup. It's not really
|
||||
a tree, but it could be :)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tree.h"
|
||||
|
||||
#define ARRAY_GROW 32
|
||||
|
||||
struct _tree
|
||||
{
|
||||
int elements;
|
||||
int array_size;
|
||||
void **array;
|
||||
tree_cmp_func *cmp;
|
||||
void *data;
|
||||
};
|
||||
|
||||
tree *
|
||||
tree_new (tree_cmp_func *cmp, void *data)
|
||||
{
|
||||
tree *t = calloc (1, sizeof (tree));
|
||||
t->cmp = cmp;
|
||||
t->data = data;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
tree_destroy (tree *t)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
if (t->array)
|
||||
free (t->array);
|
||||
free (t);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tree_find_insertion_pos (tree *t, void *key, int *done)
|
||||
{
|
||||
int c, u, l, idx;
|
||||
|
||||
if (t->elements < 1)
|
||||
{
|
||||
*done = 1;
|
||||
t->array[0] = key;
|
||||
t->elements++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (t->elements < 2)
|
||||
{
|
||||
*done = 1;
|
||||
c = t->cmp (key, t->array[0], t->data);
|
||||
if (c == 0)
|
||||
return -1;
|
||||
t->elements++;
|
||||
if (c > 0)
|
||||
{
|
||||
t->array[1] = key;
|
||||
return 1;
|
||||
}
|
||||
t->array[1] = t->array[0];
|
||||
t->array[0] = key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*done = 0;
|
||||
|
||||
c = t->cmp (key, t->array[0], t->data);
|
||||
if (c < 0)
|
||||
return 0; /* prepend */
|
||||
|
||||
c = t->cmp (key, t->array[t->elements - 1], t->data);
|
||||
if (c > 0)
|
||||
return t->elements; /* append */
|
||||
|
||||
l = 0;
|
||||
u = t->elements - 1;
|
||||
while (1)
|
||||
{
|
||||
idx = (l + u) / 2;
|
||||
c = t->cmp (key, t->array[idx], t->data);
|
||||
|
||||
if (0 > c)
|
||||
u = idx;
|
||||
else if (0 < c && 0 > t->cmp (key, t->array[idx+1], t->data))
|
||||
return idx + 1;
|
||||
else if (c == 0)
|
||||
return -1;
|
||||
else
|
||||
l = idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tree_insert_at_pos (tree *t, void *key, int pos)
|
||||
{
|
||||
int post_bytes;
|
||||
|
||||
/* append is easy */
|
||||
if (pos != t->elements)
|
||||
{
|
||||
post_bytes = (t->elements - pos) * sizeof (void *);
|
||||
memmove (&t->array[pos + 1], &t->array[pos], post_bytes);
|
||||
}
|
||||
|
||||
t->array[pos] = key;
|
||||
t->elements++;
|
||||
}
|
||||
|
||||
static void *
|
||||
mybsearch (const void *key, void **array, size_t nmemb,
|
||||
int (*compar) (const void *, const void *, void *data), void *data, int *pos)
|
||||
{
|
||||
int l, u, idx;
|
||||
int comparison;
|
||||
|
||||
l = 0;
|
||||
u = nmemb;
|
||||
while (l < u)
|
||||
{
|
||||
idx = (l + u) / 2;
|
||||
comparison = (*compar) (key, array[idx], data);
|
||||
if (comparison < 0)
|
||||
u = idx;
|
||||
else if (comparison > 0)
|
||||
l = idx + 1;
|
||||
else
|
||||
{
|
||||
*pos = idx;
|
||||
return array[idx];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
tree_find (tree *t, void *key, tree_cmp_func *cmp, void *data, int *pos)
|
||||
{
|
||||
if (!t || !t->array)
|
||||
return NULL;
|
||||
|
||||
return mybsearch (key, &t->array[0], t->elements, cmp, data, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
tree_remove_at_pos (tree *t, int pos)
|
||||
{
|
||||
int post_bytes;
|
||||
|
||||
t->elements--;
|
||||
if (pos != t->elements)
|
||||
{
|
||||
post_bytes = (t->elements - pos) * sizeof (void *);
|
||||
memmove (&t->array[pos], &t->array[pos + 1], post_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tree_remove (tree *t, void *key, int *pos)
|
||||
{
|
||||
void *data;
|
||||
|
||||
data = tree_find (t, key, t->cmp, t->data, pos);
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
tree_remove_at_pos (t, *pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
tree_foreach (tree *t, tree_traverse_func *func, void *data)
|
||||
{
|
||||
int j;
|
||||
|
||||
if (!t || !t->array)
|
||||
return;
|
||||
|
||||
for (j = 0; j < t->elements; j++)
|
||||
{
|
||||
if (!func (t->array[j], data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tree_insert (tree *t, void *key)
|
||||
{
|
||||
int pos, done;
|
||||
|
||||
if (!t)
|
||||
return -1;
|
||||
|
||||
if (t->array_size < t->elements + 1)
|
||||
{
|
||||
int new_size = t->array_size + ARRAY_GROW;
|
||||
|
||||
t->array = realloc (t->array, sizeof (void *) * new_size);
|
||||
t->array_size = new_size;
|
||||
}
|
||||
|
||||
pos = tree_find_insertion_pos (t, key, &done);
|
||||
if (!done && pos != -1)
|
||||
tree_insert_at_pos (t, key, pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
16
src/common/tree.h
Normal file
16
src/common/tree.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef XCHAT_TREE_H
|
||||
#define XCHAT_TREE_H
|
||||
|
||||
typedef struct _tree tree;
|
||||
|
||||
typedef int (tree_cmp_func) (const void *keya, const void *keyb, void *data);
|
||||
typedef int (tree_traverse_func) (const void *key, void *data);
|
||||
|
||||
tree *tree_new (tree_cmp_func *cmp, void *data);
|
||||
void tree_destroy (tree *t);
|
||||
void *tree_find (tree *t, void *key, tree_cmp_func *cmp, void *data, int *pos);
|
||||
int tree_remove (tree *t, void *key, int *pos);
|
||||
void tree_foreach (tree *t, tree_traverse_func *func, void *data);
|
||||
int tree_insert (tree *t, void *key);
|
||||
|
||||
#endif
|
||||
280
src/common/url.c
Normal file
280
src/common/url.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "xchat.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "fe.h"
|
||||
#include "tree.h"
|
||||
#include "url.h"
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
void *url_tree = NULL;
|
||||
|
||||
|
||||
static int
|
||||
url_free (char *url, void *data)
|
||||
{
|
||||
free (url);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
url_clear (void)
|
||||
{
|
||||
tree_foreach (url_tree, (tree_traverse_func *)url_free, NULL);
|
||||
tree_destroy (url_tree);
|
||||
url_tree = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
url_save_cb (char *url, FILE *fd)
|
||||
{
|
||||
fprintf (fd, "%s\n", url);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
url_save (const char *fname, const char *mode, gboolean fullpath)
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
if (fullpath)
|
||||
fd = xchat_fopen_file (fname, mode, XOF_FULLPATH);
|
||||
else
|
||||
fd = xchat_fopen_file (fname, mode, 0);
|
||||
if (fd == NULL)
|
||||
return;
|
||||
|
||||
tree_foreach (url_tree, (tree_traverse_func *)url_save_cb, fd);
|
||||
fclose (fd);
|
||||
}
|
||||
|
||||
void
|
||||
url_autosave (void)
|
||||
{
|
||||
url_save ("url.save", "a", FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
url_find (char *urltext)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (tree_find (url_tree, urltext, (tree_cmp_func *)strcasecmp, NULL, &pos))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
url_add (char *urltext, int len)
|
||||
{
|
||||
char *data = malloc (len + 1);
|
||||
if (!data)
|
||||
return;
|
||||
memcpy (data, urltext, len);
|
||||
data[len] = 0;
|
||||
|
||||
if (data[len - 1] == '.') /* chop trailing dot */
|
||||
{
|
||||
len--;
|
||||
data[len] = 0;
|
||||
}
|
||||
if (data[len - 1] == ')') /* chop trailing ) */
|
||||
data[len - 1] = 0;
|
||||
|
||||
if (url_find (data))
|
||||
{
|
||||
free (data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url_tree)
|
||||
url_tree = tree_new ((tree_cmp_func *)strcasecmp, NULL);
|
||||
|
||||
tree_insert (url_tree, data);
|
||||
fe_url_add (data);
|
||||
}
|
||||
|
||||
/* check if a word is clickable. This is called on mouse motion events, so
|
||||
keep it FAST! This new version was found to be almost 3x faster than
|
||||
2.4.4 release. */
|
||||
|
||||
int
|
||||
url_check_word (char *word, int len)
|
||||
{
|
||||
#define D(x) (x), ((sizeof (x)) - 1)
|
||||
static const struct {
|
||||
const char *s;
|
||||
int len;
|
||||
}
|
||||
prefix[] = {
|
||||
{ D("irc.") },
|
||||
{ D("ftp.") },
|
||||
{ D("www.") },
|
||||
{ D("irc://") },
|
||||
{ D("ftp://") },
|
||||
{ D("http://") },
|
||||
{ D("https://") },
|
||||
{ D("file://") },
|
||||
{ D("rtsp://") },
|
||||
{ D("ut2004://") },
|
||||
},
|
||||
suffix[] = {
|
||||
{ D(".org") },
|
||||
{ D(".net") },
|
||||
{ D(".com") },
|
||||
{ D(".edu") },
|
||||
{ D(".html") },
|
||||
{ D(".info") },
|
||||
{ D(".name") },
|
||||
};
|
||||
#undef D
|
||||
const char *at, *dot;
|
||||
int i, dots;
|
||||
|
||||
if (len > 1 && word[1] == '#' && strchr("@+^%*#", word[0]))
|
||||
return WORD_CHANNEL;
|
||||
|
||||
if ((word[0] == '#' || word[0] == '&') && word[1] != '#' && word[1] != 0)
|
||||
return WORD_CHANNEL;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(prefix); i++)
|
||||
{
|
||||
int l;
|
||||
|
||||
l = prefix[i].len;
|
||||
if (len > l)
|
||||
{
|
||||
int j;
|
||||
|
||||
/* This is pretty much strncasecmp(). */
|
||||
for (j = 0; j < l; j++)
|
||||
{
|
||||
unsigned char c = word[j];
|
||||
if (tolower(c) != prefix[i].s[j])
|
||||
break;
|
||||
}
|
||||
if (j == l)
|
||||
return WORD_URL;
|
||||
}
|
||||
}
|
||||
|
||||
at = strchr (word, '@'); /* check for email addy */
|
||||
dot = strrchr (word, '.');
|
||||
if (at && dot)
|
||||
{
|
||||
if (at < dot)
|
||||
{
|
||||
if (strchr (word, '*'))
|
||||
return WORD_HOST;
|
||||
else
|
||||
return WORD_EMAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if it's an IP number */
|
||||
dots = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (word[i] == '.' && i > 0)
|
||||
dots++; /* allow 127.0.0.1:80 */
|
||||
else if (!isdigit ((unsigned char) word[i]) && word[i] != ':')
|
||||
{
|
||||
dots = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dots == 3)
|
||||
return WORD_HOST;
|
||||
|
||||
if (len > 5)
|
||||
{
|
||||
for (i = 0; i < G_N_ELEMENTS(suffix); i++)
|
||||
{
|
||||
int l;
|
||||
|
||||
l = suffix[i].len;
|
||||
if (len > l)
|
||||
{
|
||||
const unsigned char *p = &word[len - l];
|
||||
int j;
|
||||
|
||||
/* This is pretty much strncasecmp(). */
|
||||
for (j = 0; j < l; j++)
|
||||
{
|
||||
if (tolower(p[j]) != suffix[i].s[j])
|
||||
break;
|
||||
}
|
||||
if (j == l)
|
||||
return WORD_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
if (word[len - 3] == '.' &&
|
||||
isalpha ((unsigned char) word[len - 2]) &&
|
||||
isalpha ((unsigned char) word[len - 1]))
|
||||
return WORD_HOST;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
url_check_line (char *buf, int len)
|
||||
{
|
||||
char *po = buf;
|
||||
char *start;
|
||||
int wlen;
|
||||
|
||||
if (buf[0] == ':' && buf[1] != 0)
|
||||
po++;
|
||||
|
||||
start = po;
|
||||
|
||||
/* check each "word" (space separated) */
|
||||
while (1)
|
||||
{
|
||||
switch (po[0])
|
||||
{
|
||||
case 0:
|
||||
case ' ':
|
||||
wlen = po - start;
|
||||
if (wlen > 2)
|
||||
{
|
||||
if (url_check_word (start, wlen) == WORD_URL)
|
||||
{
|
||||
url_add (start, wlen);
|
||||
}
|
||||
}
|
||||
if (po[0] == 0)
|
||||
return;
|
||||
po++;
|
||||
start = po;
|
||||
break;
|
||||
|
||||
default:
|
||||
po++;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/common/url.h
Normal file
19
src/common/url.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef XCHAT_URL_H
|
||||
#define XCHAT_URL_H
|
||||
|
||||
extern void *url_tree;
|
||||
|
||||
#define WORD_URL 1
|
||||
#define WORD_NICK 2
|
||||
#define WORD_CHANNEL 3
|
||||
#define WORD_HOST 4
|
||||
#define WORD_EMAIL 5
|
||||
#define WORD_DIALOG -1
|
||||
|
||||
void url_clear (void);
|
||||
void url_save (const char *fname, const char *mode, gboolean fullpath);
|
||||
void url_autosave (void);
|
||||
int url_check_word (char *word, int len);
|
||||
void url_check_line (char *buf, int len);
|
||||
|
||||
#endif
|
||||
454
src/common/userlist.c
Normal file
454
src/common/userlist.c
Normal file
@@ -0,0 +1,454 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xchat.h"
|
||||
#include "modes.h"
|
||||
#include "fe.h"
|
||||
#include "notify.h"
|
||||
#include "tree.h"
|
||||
#include "xchatc.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
static int
|
||||
nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2)
|
||||
{
|
||||
unsigned int access1 = user1->access;
|
||||
unsigned int access2 = user2->access;
|
||||
int pos;
|
||||
|
||||
if (access1 != access2)
|
||||
{
|
||||
for (pos = 0; pos < USERACCESS_SIZE; pos++)
|
||||
{
|
||||
if ((access1&(1<<pos)) && (access2&(1<<pos)))
|
||||
break;
|
||||
if ((access1&(1<<pos)) && !(access2&(1<<pos)))
|
||||
return -1;
|
||||
if (!(access1&(1<<pos)) && (access2&(1<<pos)))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return serv->p_cmp (user1->nick, user2->nick);
|
||||
}
|
||||
|
||||
static int
|
||||
nick_cmp_alpha (struct User *user1, struct User *user2, server *serv)
|
||||
{
|
||||
return serv->p_cmp (user1->nick, user2->nick);
|
||||
}
|
||||
|
||||
static int
|
||||
nick_cmp (struct User *user1, struct User *user2, server *serv)
|
||||
{
|
||||
switch (prefs.userlist_sort)
|
||||
{
|
||||
case 0:
|
||||
return nick_cmp_az_ops (serv, user1, user2);
|
||||
case 1:
|
||||
return serv->p_cmp (user1->nick, user2->nick);
|
||||
case 2:
|
||||
return -1 * nick_cmp_az_ops (serv, user1, user2);
|
||||
case 3:
|
||||
return -1 * serv->p_cmp (user1->nick, user2->nick);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
insert name in appropriate place in linked list. Returns row number or:
|
||||
-1: duplicate
|
||||
*/
|
||||
|
||||
static int
|
||||
userlist_insertname (session *sess, struct User *newuser)
|
||||
{
|
||||
if (!sess->usertree)
|
||||
{
|
||||
sess->usertree = tree_new ((tree_cmp_func *)nick_cmp, sess->server);
|
||||
sess->usertree_alpha = tree_new ((tree_cmp_func *)nick_cmp_alpha, sess->server);
|
||||
}
|
||||
|
||||
tree_insert (sess->usertree_alpha, newuser);
|
||||
return tree_insert (sess->usertree, newuser);
|
||||
}
|
||||
|
||||
void
|
||||
userlist_set_away (struct session *sess, char *nick, unsigned int away)
|
||||
{
|
||||
struct User *user;
|
||||
|
||||
user = userlist_find (sess, nick);
|
||||
if (user)
|
||||
{
|
||||
if (user->away != away)
|
||||
{
|
||||
user->away = away;
|
||||
/* rehash GUI */
|
||||
fe_userlist_rehash (sess, user);
|
||||
if (away)
|
||||
fe_userlist_update (sess, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
userlist_add_hostname (struct session *sess, char *nick, char *hostname,
|
||||
char *realname, char *servername, unsigned int away)
|
||||
{
|
||||
struct User *user;
|
||||
|
||||
user = userlist_find (sess, nick);
|
||||
if (user)
|
||||
{
|
||||
if (!user->hostname && hostname)
|
||||
user->hostname = strdup (hostname);
|
||||
if (!user->realname && realname)
|
||||
user->realname = strdup (realname);
|
||||
if (!user->servername && servername)
|
||||
user->servername = strdup (servername);
|
||||
|
||||
if (away != 0xff)
|
||||
{
|
||||
if (prefs.showhostname_in_userlist || user->away != away)
|
||||
{
|
||||
user->away = away;
|
||||
fe_userlist_rehash (sess, user);
|
||||
}
|
||||
user->away = away;
|
||||
}
|
||||
|
||||
fe_userlist_update (sess, user);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
free_user (struct User *user, gpointer data)
|
||||
{
|
||||
if (user->realname)
|
||||
free (user->realname);
|
||||
if (user->hostname)
|
||||
free (user->hostname);
|
||||
if (user->servername)
|
||||
free (user->servername);
|
||||
free (user);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_free (session *sess)
|
||||
{
|
||||
tree_foreach (sess->usertree, (tree_traverse_func *)free_user, NULL);
|
||||
tree_destroy (sess->usertree);
|
||||
tree_destroy (sess->usertree_alpha);
|
||||
|
||||
sess->usertree = NULL;
|
||||
sess->usertree_alpha = NULL;
|
||||
sess->me = NULL;
|
||||
|
||||
sess->ops = 0;
|
||||
sess->hops = 0;
|
||||
sess->voices = 0;
|
||||
sess->total = 0;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_clear (session *sess)
|
||||
{
|
||||
fe_userlist_clear (sess);
|
||||
userlist_free (sess);
|
||||
fe_userlist_numbers (sess);
|
||||
}
|
||||
|
||||
static int
|
||||
find_cmp (const char *name, struct User *user, server *serv)
|
||||
{
|
||||
return serv->p_cmp ((char *)name, user->nick);
|
||||
}
|
||||
|
||||
struct User *
|
||||
userlist_find (struct session *sess, char *name)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (sess->usertree_alpha)
|
||||
return tree_find (sess->usertree_alpha, name,
|
||||
(tree_cmp_func *)find_cmp, sess->server, &pos);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct User *
|
||||
userlist_find_global (struct server *serv, char *name)
|
||||
{
|
||||
struct User *user;
|
||||
session *sess;
|
||||
GSList *list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = (session *) list->data;
|
||||
if (sess->server == serv)
|
||||
{
|
||||
user = userlist_find (sess, name);
|
||||
if (user)
|
||||
return user;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
update_counts (session *sess, struct User *user, char prefix,
|
||||
int level, int offset)
|
||||
{
|
||||
switch (prefix)
|
||||
{
|
||||
case '@':
|
||||
user->op = level;
|
||||
sess->ops += offset;
|
||||
break;
|
||||
case '%':
|
||||
user->hop = level;
|
||||
sess->hops += offset;
|
||||
break;
|
||||
case '+':
|
||||
user->voice = level;
|
||||
sess->voices += offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
userlist_update_mode (session *sess, char *name, char mode, char sign)
|
||||
{
|
||||
int access;
|
||||
int offset = 0;
|
||||
int level;
|
||||
int pos;
|
||||
char prefix;
|
||||
struct User *user;
|
||||
|
||||
user = userlist_find (sess, name);
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
/* remove from binary trees, before we loose track of it */
|
||||
tree_remove (sess->usertree, user, &pos);
|
||||
tree_remove (sess->usertree_alpha, user, &pos);
|
||||
|
||||
/* which bit number is affected? */
|
||||
access = mode_access (sess->server, mode, &prefix);
|
||||
|
||||
if (sign == '+')
|
||||
{
|
||||
level = TRUE;
|
||||
if (!(user->access & (1 << access)))
|
||||
{
|
||||
offset = 1;
|
||||
user->access |= (1 << access);
|
||||
}
|
||||
} else
|
||||
{
|
||||
level = FALSE;
|
||||
if (user->access & (1 << access))
|
||||
{
|
||||
offset = -1;
|
||||
user->access &= ~(1 << access);
|
||||
}
|
||||
}
|
||||
|
||||
/* now what is this users highest prefix? e.g. @ for ops */
|
||||
user->prefix[0] = get_nick_prefix (sess->server, user->access);
|
||||
|
||||
/* update the various counts using the CHANGED prefix only */
|
||||
update_counts (sess, user, prefix, level, offset);
|
||||
|
||||
/* insert it back into its new place */
|
||||
tree_insert (sess->usertree_alpha, user);
|
||||
pos = tree_insert (sess->usertree, user);
|
||||
|
||||
/* let GTK move it too */
|
||||
fe_userlist_move (sess, user, pos);
|
||||
fe_userlist_numbers (sess);
|
||||
}
|
||||
|
||||
int
|
||||
userlist_change (struct session *sess, char *oldname, char *newname)
|
||||
{
|
||||
struct User *user = userlist_find (sess, oldname);
|
||||
int pos;
|
||||
|
||||
if (user)
|
||||
{
|
||||
tree_remove (sess->usertree, user, &pos);
|
||||
tree_remove (sess->usertree_alpha, user, &pos);
|
||||
|
||||
safe_strcpy (user->nick, newname, NICKLEN);
|
||||
|
||||
tree_insert (sess->usertree_alpha, user);
|
||||
|
||||
fe_userlist_move (sess, user, tree_insert (sess->usertree, user));
|
||||
fe_userlist_numbers (sess);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
userlist_remove (struct session *sess, char *name)
|
||||
{
|
||||
struct User *user;
|
||||
int pos;
|
||||
|
||||
user = userlist_find (sess, name);
|
||||
if (!user)
|
||||
return FALSE;
|
||||
|
||||
if (user->voice)
|
||||
sess->voices--;
|
||||
if (user->op)
|
||||
sess->ops--;
|
||||
if (user->hop)
|
||||
sess->hops--;
|
||||
sess->total--;
|
||||
fe_userlist_numbers (sess);
|
||||
fe_userlist_remove (sess, user);
|
||||
|
||||
if (user == sess->me)
|
||||
sess->me = NULL;
|
||||
|
||||
tree_remove (sess->usertree, user, &pos);
|
||||
tree_remove (sess->usertree_alpha, user, &pos);
|
||||
free_user (user, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_add (struct session *sess, char *name, char *hostname)
|
||||
{
|
||||
struct User *user;
|
||||
int row, prefix_chars;
|
||||
unsigned int acc;
|
||||
|
||||
acc = nick_access (sess->server, name, &prefix_chars);
|
||||
|
||||
notify_set_online (sess->server, name + prefix_chars);
|
||||
|
||||
user = malloc (sizeof (struct User));
|
||||
memset (user, 0, sizeof (struct User));
|
||||
|
||||
user->access = acc;
|
||||
|
||||
/* assume first char is the highest level nick prefix */
|
||||
if (prefix_chars)
|
||||
user->prefix[0] = name[0];
|
||||
|
||||
/* add it to our linked list */
|
||||
if (hostname)
|
||||
user->hostname = strdup (hostname);
|
||||
safe_strcpy (user->nick, name + prefix_chars, NICKLEN);
|
||||
/* is it me? */
|
||||
if (!sess->server->p_cmp (user->nick, sess->server->nick))
|
||||
user->me = TRUE;
|
||||
row = userlist_insertname (sess, user);
|
||||
|
||||
/* duplicate? some broken servers trigger this */
|
||||
if (row == -1)
|
||||
{
|
||||
if (user->hostname)
|
||||
free (user->hostname);
|
||||
free (user);
|
||||
return;
|
||||
}
|
||||
|
||||
sess->total++;
|
||||
|
||||
/* most ircds don't support multiple modechars infront of the nickname
|
||||
for /NAMES - though they should. */
|
||||
while (prefix_chars)
|
||||
{
|
||||
update_counts (sess, user, name[0], TRUE, 1);
|
||||
name++;
|
||||
prefix_chars--;
|
||||
}
|
||||
|
||||
if (user->me)
|
||||
sess->me = user;
|
||||
|
||||
fe_userlist_insert (sess, user, row, FALSE);
|
||||
fe_userlist_numbers (sess);
|
||||
}
|
||||
|
||||
static int
|
||||
rehash_cb (struct User *user, session *sess)
|
||||
{
|
||||
fe_userlist_rehash (sess, user);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_rehash (session *sess)
|
||||
{
|
||||
tree_foreach (sess->usertree_alpha, (tree_traverse_func *)rehash_cb, sess);
|
||||
}
|
||||
|
||||
static int
|
||||
flat_cb (struct User *user, GSList **list)
|
||||
{
|
||||
*list = g_slist_prepend (*list, user);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GSList *
|
||||
userlist_flat_list (session *sess)
|
||||
{
|
||||
GSList *list = NULL;
|
||||
|
||||
tree_foreach (sess->usertree_alpha, (tree_traverse_func *)flat_cb, &list);
|
||||
return g_slist_reverse (list);
|
||||
}
|
||||
|
||||
static int
|
||||
double_cb (struct User *user, GList **list)
|
||||
{
|
||||
*list = g_list_prepend(*list, user);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GList *
|
||||
userlist_double_list(session *sess)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
tree_foreach (sess->usertree_alpha, (tree_traverse_func *)double_cb, &list);
|
||||
return list;
|
||||
}
|
||||
41
src/common/userlist.h
Normal file
41
src/common/userlist.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <time.h>
|
||||
|
||||
#ifndef XCHAT_USERLIST_H
|
||||
#define XCHAT_USERLIST_H
|
||||
|
||||
struct User
|
||||
{
|
||||
char nick[NICKLEN];
|
||||
char *hostname;
|
||||
char *realname;
|
||||
char *servername;
|
||||
time_t lasttalk;
|
||||
unsigned int access; /* axs bit field */
|
||||
char prefix[2]; /* @ + % */
|
||||
unsigned int op:1;
|
||||
unsigned int hop:1;
|
||||
unsigned int voice:1;
|
||||
unsigned int me:1;
|
||||
unsigned int away:1;
|
||||
unsigned int selected:1;
|
||||
};
|
||||
|
||||
#define USERACCESS_SIZE 12
|
||||
|
||||
int userlist_add_hostname (session *sess, char *nick,
|
||||
char *hostname, char *realname,
|
||||
char *servername, unsigned int away);
|
||||
void userlist_set_away (session *sess, char *nick, unsigned int away);
|
||||
struct User *userlist_find (session *sess, char *name);
|
||||
struct User *userlist_find_global (server *serv, char *name);
|
||||
void userlist_clear (session *sess);
|
||||
void userlist_free (session *sess);
|
||||
void userlist_add (session *sess, char *name, char *hostname);
|
||||
int userlist_remove (session *sess, char *name);
|
||||
int userlist_change (session *sess, char *oldname, char *newname);
|
||||
void userlist_update_mode (session *sess, char *name, char mode, char sign);
|
||||
GSList *userlist_flat_list (session *sess);
|
||||
GList *userlist_double_list (session *sess);
|
||||
void userlist_rehash (session *sess);
|
||||
|
||||
#endif
|
||||
1729
src/common/util.c
Normal file
1729
src/common/util.c
Normal file
File diff suppressed because it is too large
Load Diff
54
src/common/util.h
Normal file
54
src/common/util.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/************************************************************************
|
||||
* This technique was borrowed in part from the source code to
|
||||
* ircd-hybrid-5.3 to implement case-insensitive string matches which
|
||||
* are fully compliant with Section 2.2 of RFC 1459, the copyright
|
||||
* of that code being (C) 1990 Jarkko Oikarinen and under the GPL.
|
||||
*
|
||||
* A special thanks goes to Mr. Okarinen for being the one person who
|
||||
* seems to have ever noticed this section in the original RFC and
|
||||
* written code for it. Shame on all the rest of you (myself included).
|
||||
*
|
||||
* --+ Dagmar d'Surreal
|
||||
*/
|
||||
|
||||
#ifndef XCHAT_UTIL_H
|
||||
#define XCHAT_UTIL_H
|
||||
|
||||
#define rfc_tolower(c) (rfc_tolowertab[(unsigned char)(c)])
|
||||
|
||||
extern const unsigned char rfc_tolowertab[];
|
||||
|
||||
int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr);
|
||||
char *expand_homedir (char *file);
|
||||
void path_part (char *file, char *path, int pathlen);
|
||||
int match (const char *mask, const char *string);
|
||||
char *file_part (char *file);
|
||||
void for_files (char *dirname, char *mask, void callback (char *file));
|
||||
int rfc_casecmp (const char *, const char *);
|
||||
int rfc_ncasecmp (char *, char *, int);
|
||||
int buf_get_line (char *, char **, int *, int len);
|
||||
char *nocasestrstr (const char *text, const char *tofind);
|
||||
char *country (char *);
|
||||
void country_search (char *pattern, void *ud, void (*print)(void *, char *, ...));
|
||||
char *get_cpu_str (void);
|
||||
int util_exec (const char *cmd);
|
||||
int util_execv (char * const argv[]);
|
||||
#define STRIP_COLOR 1
|
||||
#define STRIP_ATTRIB 2
|
||||
#define STRIP_HIDDEN 4
|
||||
#define STRIP_ESCMARKUP 8
|
||||
#define STRIP_ALL 7
|
||||
gchar *strip_color (const char *text, int len, int flags);
|
||||
int strip_color2 (const char *src, int len, char *dst, int flags);
|
||||
int strip_hidden_attribute (char *src, char *dst);
|
||||
char *errorstring (int err);
|
||||
int waitline (int sok, char *buf, int bufsize, int);
|
||||
unsigned long make_ping_time (void);
|
||||
void move_file_utf8 (char *src_dir, char *dst_dir, char *fname, int dccpermissions);
|
||||
int mkdir_utf8 (char *dir);
|
||||
int token_foreach (char *str, char sep, int (*callback) (char *str, void *ud), void *ud);
|
||||
guint32 str_hash (const char *key);
|
||||
guint32 str_ihash (const unsigned char *key);
|
||||
void safe_strcpy (char *dest, const char *src, int bytes_left);
|
||||
|
||||
#endif
|
||||
334
src/common/xchat-plugin.h
Normal file
334
src/common/xchat-plugin.h
Normal file
@@ -0,0 +1,334 @@
|
||||
/* You can distribute this header with your plugins for easy compilation */
|
||||
#ifndef XCHAT_PLUGIN_H
|
||||
#define XCHAT_PLUGIN_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define XCHAT_IFACE_MAJOR 1
|
||||
#define XCHAT_IFACE_MINOR 9
|
||||
#define XCHAT_IFACE_MICRO 11
|
||||
#define XCHAT_IFACE_VERSION ((XCHAT_IFACE_MAJOR * 10000) + \
|
||||
(XCHAT_IFACE_MINOR * 100) + \
|
||||
(XCHAT_IFACE_MICRO))
|
||||
|
||||
#define XCHAT_PRI_HIGHEST 127
|
||||
#define XCHAT_PRI_HIGH 64
|
||||
#define XCHAT_PRI_NORM 0
|
||||
#define XCHAT_PRI_LOW (-64)
|
||||
#define XCHAT_PRI_LOWEST (-128)
|
||||
|
||||
#define XCHAT_FD_READ 1
|
||||
#define XCHAT_FD_WRITE 2
|
||||
#define XCHAT_FD_EXCEPTION 4
|
||||
#define XCHAT_FD_NOTSOCKET 8
|
||||
|
||||
#define XCHAT_EAT_NONE 0 /* pass it on through! */
|
||||
#define XCHAT_EAT_XCHAT 1 /* don't let xchat see this event */
|
||||
#define XCHAT_EAT_PLUGIN 2 /* don't let other plugins see this event */
|
||||
#define XCHAT_EAT_ALL (XCHAT_EAT_XCHAT|XCHAT_EAT_PLUGIN) /* don't let anything see this event */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _xchat_plugin xchat_plugin;
|
||||
typedef struct _xchat_list xchat_list;
|
||||
typedef struct _xchat_hook xchat_hook;
|
||||
#ifndef PLUGIN_C
|
||||
typedef struct _xchat_context xchat_context;
|
||||
#endif
|
||||
|
||||
#ifndef PLUGIN_C
|
||||
struct _xchat_plugin
|
||||
{
|
||||
/* these are only used on win32 */
|
||||
xchat_hook *(*xchat_hook_command) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
const char *help_text,
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_server) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_print) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_timer) (xchat_plugin *ph,
|
||||
int timeout,
|
||||
int (*callback) (void *user_data),
|
||||
void *userdata);
|
||||
xchat_hook *(*xchat_hook_fd) (xchat_plugin *ph,
|
||||
int fd,
|
||||
int flags,
|
||||
int (*callback) (int fd, int flags, void *user_data),
|
||||
void *userdata);
|
||||
void *(*xchat_unhook) (xchat_plugin *ph,
|
||||
xchat_hook *hook);
|
||||
void (*xchat_print) (xchat_plugin *ph,
|
||||
const char *text);
|
||||
void (*xchat_printf) (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
void (*xchat_command) (xchat_plugin *ph,
|
||||
const char *command);
|
||||
void (*xchat_commandf) (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
int (*xchat_nickcmp) (xchat_plugin *ph,
|
||||
const char *s1,
|
||||
const char *s2);
|
||||
int (*xchat_set_context) (xchat_plugin *ph,
|
||||
xchat_context *ctx);
|
||||
xchat_context *(*xchat_find_context) (xchat_plugin *ph,
|
||||
const char *servname,
|
||||
const char *channel);
|
||||
xchat_context *(*xchat_get_context) (xchat_plugin *ph);
|
||||
const char *(*xchat_get_info) (xchat_plugin *ph,
|
||||
const char *id);
|
||||
int (*xchat_get_prefs) (xchat_plugin *ph,
|
||||
const char *name,
|
||||
const char **string,
|
||||
int *integer);
|
||||
xchat_list * (*xchat_list_get) (xchat_plugin *ph,
|
||||
const char *name);
|
||||
void (*xchat_list_free) (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
const char * const * (*xchat_list_fields) (xchat_plugin *ph,
|
||||
const char *name);
|
||||
int (*xchat_list_next) (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
const char * (*xchat_list_str) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
int (*xchat_list_int) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
void * (*xchat_plugingui_add) (xchat_plugin *ph,
|
||||
const char *filename,
|
||||
const char *name,
|
||||
const char *desc,
|
||||
const char *version,
|
||||
char *reserved);
|
||||
void (*xchat_plugingui_remove) (xchat_plugin *ph,
|
||||
void *handle);
|
||||
int (*xchat_emit_print) (xchat_plugin *ph,
|
||||
const char *event_name, ...);
|
||||
int (*xchat_read_fd) (xchat_plugin *ph,
|
||||
void *src,
|
||||
char *buf,
|
||||
int *len);
|
||||
time_t (*xchat_list_time) (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
char *(*xchat_gettext) (xchat_plugin *ph,
|
||||
const char *msgid);
|
||||
void (*xchat_send_modes) (xchat_plugin *ph,
|
||||
const char **targets,
|
||||
int ntargets,
|
||||
int modes_per_line,
|
||||
char sign,
|
||||
char mode);
|
||||
char *(*xchat_strip) (xchat_plugin *ph,
|
||||
const char *str,
|
||||
int len,
|
||||
int flags);
|
||||
void (*xchat_free) (xchat_plugin *ph,
|
||||
void *ptr);
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
xchat_hook *
|
||||
xchat_hook_command (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
const char *help_text,
|
||||
void *userdata);
|
||||
|
||||
xchat_hook *
|
||||
xchat_hook_server (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], char *word_eol[], void *user_data),
|
||||
void *userdata);
|
||||
|
||||
xchat_hook *
|
||||
xchat_hook_print (xchat_plugin *ph,
|
||||
const char *name,
|
||||
int pri,
|
||||
int (*callback) (char *word[], void *user_data),
|
||||
void *userdata);
|
||||
|
||||
xchat_hook *
|
||||
xchat_hook_timer (xchat_plugin *ph,
|
||||
int timeout,
|
||||
int (*callback) (void *user_data),
|
||||
void *userdata);
|
||||
|
||||
xchat_hook *
|
||||
xchat_hook_fd (xchat_plugin *ph,
|
||||
int fd,
|
||||
int flags,
|
||||
int (*callback) (int fd, int flags, void *user_data),
|
||||
void *userdata);
|
||||
|
||||
void *
|
||||
xchat_unhook (xchat_plugin *ph,
|
||||
xchat_hook *hook);
|
||||
|
||||
void
|
||||
xchat_print (xchat_plugin *ph,
|
||||
const char *text);
|
||||
|
||||
void
|
||||
xchat_printf (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
|
||||
void
|
||||
xchat_command (xchat_plugin *ph,
|
||||
const char *command);
|
||||
|
||||
void
|
||||
xchat_commandf (xchat_plugin *ph,
|
||||
const char *format, ...);
|
||||
|
||||
int
|
||||
xchat_nickcmp (xchat_plugin *ph,
|
||||
const char *s1,
|
||||
const char *s2);
|
||||
|
||||
int
|
||||
xchat_set_context (xchat_plugin *ph,
|
||||
xchat_context *ctx);
|
||||
|
||||
xchat_context *
|
||||
xchat_find_context (xchat_plugin *ph,
|
||||
const char *servname,
|
||||
const char *channel);
|
||||
|
||||
xchat_context *
|
||||
xchat_get_context (xchat_plugin *ph);
|
||||
|
||||
const char *
|
||||
xchat_get_info (xchat_plugin *ph,
|
||||
const char *id);
|
||||
|
||||
int
|
||||
xchat_get_prefs (xchat_plugin *ph,
|
||||
const char *name,
|
||||
const char **string,
|
||||
int *integer);
|
||||
|
||||
xchat_list *
|
||||
xchat_list_get (xchat_plugin *ph,
|
||||
const char *name);
|
||||
|
||||
void
|
||||
xchat_list_free (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
|
||||
const char * const *
|
||||
xchat_list_fields (xchat_plugin *ph,
|
||||
const char *name);
|
||||
|
||||
int
|
||||
xchat_list_next (xchat_plugin *ph,
|
||||
xchat_list *xlist);
|
||||
|
||||
const char *
|
||||
xchat_list_str (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
|
||||
int
|
||||
xchat_list_int (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
|
||||
time_t
|
||||
xchat_list_time (xchat_plugin *ph,
|
||||
xchat_list *xlist,
|
||||
const char *name);
|
||||
|
||||
void *
|
||||
xchat_plugingui_add (xchat_plugin *ph,
|
||||
const char *filename,
|
||||
const char *name,
|
||||
const char *desc,
|
||||
const char *version,
|
||||
char *reserved);
|
||||
|
||||
void
|
||||
xchat_plugingui_remove (xchat_plugin *ph,
|
||||
void *handle);
|
||||
|
||||
int
|
||||
xchat_emit_print (xchat_plugin *ph,
|
||||
const char *event_name, ...);
|
||||
|
||||
char *
|
||||
xchat_gettext (xchat_plugin *ph,
|
||||
const char *msgid);
|
||||
|
||||
void
|
||||
xchat_send_modes (xchat_plugin *ph,
|
||||
const char **targets,
|
||||
int ntargets,
|
||||
int modes_per_line,
|
||||
char sign,
|
||||
char mode);
|
||||
|
||||
char *
|
||||
xchat_strip (xchat_plugin *ph,
|
||||
const char *str,
|
||||
int len,
|
||||
int flags);
|
||||
|
||||
void
|
||||
xchat_free (xchat_plugin *ph,
|
||||
void *ptr);
|
||||
|
||||
#if !defined(PLUGIN_C) && defined(WIN32)
|
||||
#ifndef XCHAT_PLUGIN_HANDLE
|
||||
#define XCHAT_PLUGIN_HANDLE (ph)
|
||||
#endif
|
||||
#define xchat_hook_command ((XCHAT_PLUGIN_HANDLE)->xchat_hook_command)
|
||||
#define xchat_hook_server ((XCHAT_PLUGIN_HANDLE)->xchat_hook_server)
|
||||
#define xchat_hook_print ((XCHAT_PLUGIN_HANDLE)->xchat_hook_print)
|
||||
#define xchat_hook_timer ((XCHAT_PLUGIN_HANDLE)->xchat_hook_timer)
|
||||
#define xchat_hook_fd ((XCHAT_PLUGIN_HANDLE)->xchat_hook_fd)
|
||||
#define xchat_unhook ((XCHAT_PLUGIN_HANDLE)->xchat_unhook)
|
||||
#define xchat_print ((XCHAT_PLUGIN_HANDLE)->xchat_print)
|
||||
#define xchat_printf ((XCHAT_PLUGIN_HANDLE)->xchat_printf)
|
||||
#define xchat_command ((XCHAT_PLUGIN_HANDLE)->xchat_command)
|
||||
#define xchat_commandf ((XCHAT_PLUGIN_HANDLE)->xchat_commandf)
|
||||
#define xchat_nickcmp ((XCHAT_PLUGIN_HANDLE)->xchat_nickcmp)
|
||||
#define xchat_set_context ((XCHAT_PLUGIN_HANDLE)->xchat_set_context)
|
||||
#define xchat_find_context ((XCHAT_PLUGIN_HANDLE)->xchat_find_context)
|
||||
#define xchat_get_context ((XCHAT_PLUGIN_HANDLE)->xchat_get_context)
|
||||
#define xchat_get_info ((XCHAT_PLUGIN_HANDLE)->xchat_get_info)
|
||||
#define xchat_get_prefs ((XCHAT_PLUGIN_HANDLE)->xchat_get_prefs)
|
||||
#define xchat_list_get ((XCHAT_PLUGIN_HANDLE)->xchat_list_get)
|
||||
#define xchat_list_free ((XCHAT_PLUGIN_HANDLE)->xchat_list_free)
|
||||
#define xchat_list_fields ((XCHAT_PLUGIN_HANDLE)->xchat_list_fields)
|
||||
#define xchat_list_str ((XCHAT_PLUGIN_HANDLE)->xchat_list_str)
|
||||
#define xchat_list_int ((XCHAT_PLUGIN_HANDLE)->xchat_list_int)
|
||||
#define xchat_list_time ((XCHAT_PLUGIN_HANDLE)->xchat_list_time)
|
||||
#define xchat_list_next ((XCHAT_PLUGIN_HANDLE)->xchat_list_next)
|
||||
#define xchat_plugingui_add ((XCHAT_PLUGIN_HANDLE)->xchat_plugingui_add)
|
||||
#define xchat_plugingui_remove ((XCHAT_PLUGIN_HANDLE)->xchat_plugingui_remove)
|
||||
#define xchat_emit_print ((XCHAT_PLUGIN_HANDLE)->xchat_emit_print)
|
||||
#define xchat_gettext ((XCHAT_PLUGIN_HANDLE)->xchat_gettext)
|
||||
#define xchat_send_modes ((XCHAT_PLUGIN_HANDLE)->xchat_send_modes)
|
||||
#define xchat_strip ((XCHAT_PLUGIN_HANDLE)->xchat_strip)
|
||||
#define xchat_free ((XCHAT_PLUGIN_HANDLE)->xchat_free)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
951
src/common/xchat.c
Normal file
951
src/common/xchat.c
Normal file
@@ -0,0 +1,951 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define WANTSOCKET
|
||||
#include "inet.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include "xchat.h"
|
||||
#include "fe.h"
|
||||
#include "util.h"
|
||||
#include "cfgfiles.h"
|
||||
#include "chanopt.h"
|
||||
#include "ignore.h"
|
||||
#include "xchat-plugin.h"
|
||||
#include "plugin.h"
|
||||
#include "plugin-timer.h"
|
||||
#include "notify.h"
|
||||
#include "server.h"
|
||||
#include "servlist.h"
|
||||
#include "outbound.h"
|
||||
#include "text.h"
|
||||
#include "url.h"
|
||||
#include "xchatc.h"
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h> /* SSL_() */
|
||||
#include "ssl.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_MSPROXY
|
||||
#include "msproxy.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIBPROXY
|
||||
#include <proxy.h>
|
||||
#endif
|
||||
|
||||
GSList *popup_list = 0;
|
||||
GSList *button_list = 0;
|
||||
GSList *dlgbutton_list = 0;
|
||||
GSList *command_list = 0;
|
||||
GSList *ctcp_list = 0;
|
||||
GSList *replace_list = 0;
|
||||
GSList *sess_list = 0;
|
||||
GSList *dcc_list = 0;
|
||||
GSList *ignore_list = 0;
|
||||
GSList *usermenu_list = 0;
|
||||
GSList *urlhandler_list = 0;
|
||||
GSList *tabmenu_list = 0;
|
||||
|
||||
static int in_xchat_exit = FALSE;
|
||||
int xchat_is_quitting = FALSE;
|
||||
/* command-line args */
|
||||
int arg_dont_autoconnect = FALSE;
|
||||
int arg_skip_plugins = FALSE;
|
||||
char *arg_url = NULL;
|
||||
char *arg_command = NULL;
|
||||
gint arg_existing = FALSE;
|
||||
|
||||
#ifdef USE_DBUS
|
||||
#include "dbus/dbus-client.h"
|
||||
#include "dbus/dbus-plugin.h"
|
||||
#endif /* USE_DBUS */
|
||||
|
||||
struct session *current_tab;
|
||||
struct session *current_sess = 0;
|
||||
struct xchatprefs prefs;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
SSL_CTX *ctx = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIBPROXY
|
||||
pxProxyFactory *libproxy_factory;
|
||||
#endif
|
||||
|
||||
int
|
||||
is_session (session * sess)
|
||||
{
|
||||
return g_slist_find (sess_list, sess) ? 1 : 0;
|
||||
}
|
||||
|
||||
session *
|
||||
find_dialog (server *serv, char *nick)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
session *sess;
|
||||
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
if (sess->server == serv && sess->type == SESS_DIALOG)
|
||||
{
|
||||
if (!serv->p_cmp (nick, sess->channel))
|
||||
return (sess);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
session *
|
||||
find_channel (server *serv, char *chan)
|
||||
{
|
||||
session *sess;
|
||||
GSList *list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
if ((!serv || serv == sess->server) && sess->type != SESS_DIALOG)
|
||||
{
|
||||
if (!serv->p_cmp (chan, sess->channel))
|
||||
return sess;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lagcheck_update (void)
|
||||
{
|
||||
server *serv;
|
||||
GSList *list = serv_list;
|
||||
|
||||
if (!prefs.lagometer)
|
||||
return;
|
||||
|
||||
while (list)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->lag_sent)
|
||||
fe_set_lag (serv, -1);
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lag_check (void)
|
||||
{
|
||||
server *serv;
|
||||
GSList *list = serv_list;
|
||||
unsigned long tim;
|
||||
char tbuf[128];
|
||||
time_t now = time (0);
|
||||
int lag;
|
||||
|
||||
tim = make_ping_time ();
|
||||
|
||||
while (list)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->connected && serv->end_of_motd)
|
||||
{
|
||||
lag = now - serv->ping_recv;
|
||||
if (prefs.pingtimeout && lag > prefs.pingtimeout && lag > 0)
|
||||
{
|
||||
sprintf (tbuf, "%d", lag);
|
||||
EMIT_SIGNAL (XP_TE_PINGTIMEOUT, serv->server_session, tbuf, NULL,
|
||||
NULL, NULL, 0);
|
||||
if (prefs.autoreconnect)
|
||||
serv->auto_reconnect (serv, FALSE, -1);
|
||||
} else
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), "LAG%lu", tim);
|
||||
serv->p_ping (serv, "", tbuf);
|
||||
serv->lag_sent = tim;
|
||||
fe_set_lag (serv, -1);
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
away_check (void)
|
||||
{
|
||||
session *sess;
|
||||
GSList *list;
|
||||
int full, sent, loop = 0;
|
||||
|
||||
if (!prefs.away_track || prefs.away_size_max < 1)
|
||||
return 1;
|
||||
|
||||
doover:
|
||||
/* request an update of AWAY status of 1 channel every 30 seconds */
|
||||
full = TRUE;
|
||||
sent = 0; /* number of WHOs (users) requested */
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
|
||||
if (sess->server->connected &&
|
||||
sess->type == SESS_CHANNEL &&
|
||||
sess->channel[0] &&
|
||||
sess->total <= prefs.away_size_max)
|
||||
{
|
||||
if (!sess->done_away_check)
|
||||
{
|
||||
full = FALSE;
|
||||
|
||||
/* if we're under 31 WHOs, send another channels worth */
|
||||
if (sent < 31 && !sess->doing_who)
|
||||
{
|
||||
sess->done_away_check = TRUE;
|
||||
sess->doing_who = TRUE;
|
||||
/* this'll send a WHO #channel */
|
||||
sess->server->p_away_status (sess->server, sess->channel);
|
||||
sent += sess->total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* done them all, reset done_away_check to FALSE and start over */
|
||||
if (full)
|
||||
{
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
sess->done_away_check = FALSE;
|
||||
list = list->next;
|
||||
}
|
||||
loop++;
|
||||
if (loop < 2)
|
||||
goto doover;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
xchat_misc_checks (void) /* this gets called every 1/2 second */
|
||||
{
|
||||
static int count = 0;
|
||||
#ifdef USE_MSPROXY
|
||||
static int count2 = 0;
|
||||
#endif
|
||||
|
||||
count++;
|
||||
|
||||
lagcheck_update (); /* every 500ms */
|
||||
|
||||
if (count % 2)
|
||||
dcc_check_timeouts (); /* every 1 second */
|
||||
|
||||
if (count >= 60) /* every 30 seconds */
|
||||
{
|
||||
if (prefs.lagometer)
|
||||
lag_check ();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_MSPROXY
|
||||
count2++;
|
||||
if (count2 >= 720) /* 720 every 6 minutes */
|
||||
{
|
||||
msproxy_keepalive ();
|
||||
count2 = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* executed when the first irc window opens */
|
||||
|
||||
static void
|
||||
irc_init (session *sess)
|
||||
{
|
||||
static int done_init = FALSE;
|
||||
char buf[512];
|
||||
|
||||
if (done_init)
|
||||
return;
|
||||
|
||||
done_init = TRUE;
|
||||
|
||||
plugin_add (sess, NULL, NULL, timer_plugin_init, NULL, NULL, FALSE);
|
||||
|
||||
#ifdef USE_PLUGIN
|
||||
if (!arg_skip_plugins)
|
||||
plugin_auto_load (sess); /* autoload ~/.xchat *.so */
|
||||
#endif
|
||||
|
||||
#ifdef USE_DBUS
|
||||
plugin_add (sess, NULL, NULL, dbus_plugin_init, NULL, NULL, FALSE);
|
||||
#endif
|
||||
|
||||
if (prefs.notify_timeout)
|
||||
notify_tag = fe_timeout_add (prefs.notify_timeout * 1000,
|
||||
notify_checklist, 0);
|
||||
|
||||
fe_timeout_add (prefs.away_timeout * 1000, away_check, 0);
|
||||
fe_timeout_add (500, xchat_misc_checks, 0);
|
||||
|
||||
if (arg_url != NULL)
|
||||
{
|
||||
snprintf (buf, sizeof (buf), "server %s", arg_url);
|
||||
handle_command (sess, buf, FALSE);
|
||||
g_free (arg_url); /* from GOption */
|
||||
}
|
||||
|
||||
if (arg_command != NULL)
|
||||
{
|
||||
g_free (arg_command);
|
||||
}
|
||||
|
||||
/* load -e ~/.xchat2/startup.txt */
|
||||
snprintf (buf, sizeof (buf), "%s/%s", get_xdir_fs (), "startup.txt");
|
||||
load_perform_file (sess, buf);
|
||||
}
|
||||
|
||||
static session *
|
||||
session_new (server *serv, char *from, int type, int focus)
|
||||
{
|
||||
session *sess;
|
||||
|
||||
sess = malloc (sizeof (struct session));
|
||||
memset (sess, 0, sizeof (struct session));
|
||||
|
||||
sess->server = serv;
|
||||
sess->logfd = -1;
|
||||
sess->scrollfd = -1;
|
||||
sess->type = type;
|
||||
|
||||
sess->alert_beep = SET_DEFAULT;
|
||||
sess->alert_taskbar = SET_DEFAULT;
|
||||
sess->alert_tray = SET_DEFAULT;
|
||||
|
||||
sess->text_hidejoinpart = SET_DEFAULT;
|
||||
sess->text_logging = SET_DEFAULT;
|
||||
sess->text_scrollback = SET_DEFAULT;
|
||||
|
||||
if (from != NULL)
|
||||
safe_strcpy (sess->channel, from, CHANLEN);
|
||||
|
||||
sess_list = g_slist_prepend (sess_list, sess);
|
||||
|
||||
fe_new_window (sess, focus);
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
session *
|
||||
new_ircwindow (server *serv, char *name, int type, int focus)
|
||||
{
|
||||
session *sess;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SESS_SERVER:
|
||||
serv = server_new ();
|
||||
if (prefs.use_server_tab)
|
||||
sess = session_new (serv, name, SESS_SERVER, focus);
|
||||
else
|
||||
sess = session_new (serv, name, SESS_CHANNEL, focus);
|
||||
serv->server_session = sess;
|
||||
serv->front_session = sess;
|
||||
break;
|
||||
case SESS_DIALOG:
|
||||
sess = session_new (serv, name, type, focus);
|
||||
log_open_or_close (sess);
|
||||
break;
|
||||
default:
|
||||
/* case SESS_CHANNEL:
|
||||
case SESS_NOTICES:
|
||||
case SESS_SNOTICES:*/
|
||||
sess = session_new (serv, name, type, focus);
|
||||
break;
|
||||
}
|
||||
|
||||
irc_init (sess);
|
||||
scrollback_load (sess);
|
||||
chanopt_load (sess);
|
||||
plugin_emit_dummy_print (sess, "Open Context");
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
static void
|
||||
exec_notify_kill (session * sess)
|
||||
{
|
||||
#ifndef WIN32
|
||||
struct nbexec *re;
|
||||
if (sess->running_exec != NULL)
|
||||
{
|
||||
re = sess->running_exec;
|
||||
sess->running_exec = NULL;
|
||||
kill (re->childpid, SIGKILL);
|
||||
waitpid (re->childpid, NULL, WNOHANG);
|
||||
fe_input_remove (re->iotag);
|
||||
close (re->myfd);
|
||||
if (re->linebuf)
|
||||
free(re->linebuf);
|
||||
free (re);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
send_quit_or_part (session * killsess)
|
||||
{
|
||||
int willquit = TRUE;
|
||||
GSList *list;
|
||||
session *sess;
|
||||
server *killserv = killsess->server;
|
||||
|
||||
/* check if this is the last session using this server */
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = (session *) list->data;
|
||||
if (sess->server == killserv && sess != killsess)
|
||||
{
|
||||
willquit = FALSE;
|
||||
list = 0;
|
||||
} else
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (xchat_is_quitting)
|
||||
willquit = TRUE;
|
||||
|
||||
if (killserv->connected)
|
||||
{
|
||||
if (willquit)
|
||||
{
|
||||
if (!killserv->sent_quit)
|
||||
{
|
||||
killserv->flush_queue (killserv);
|
||||
server_sendquit (killsess);
|
||||
killserv->sent_quit = TRUE;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (killsess->type == SESS_CHANNEL && killsess->channel[0] &&
|
||||
!killserv->sent_quit)
|
||||
{
|
||||
server_sendpart (killserv, killsess->channel, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
session_free (session *killsess)
|
||||
{
|
||||
server *killserv = killsess->server;
|
||||
session *sess;
|
||||
GSList *list;
|
||||
|
||||
plugin_emit_dummy_print (killsess, "Close Context");
|
||||
|
||||
if (current_tab == killsess)
|
||||
current_tab = NULL;
|
||||
|
||||
if (killserv->server_session == killsess)
|
||||
killserv->server_session = NULL;
|
||||
|
||||
if (killserv->front_session == killsess)
|
||||
{
|
||||
/* front_session is closed, find a valid replacement */
|
||||
killserv->front_session = NULL;
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = (session *) list->data;
|
||||
if (sess != killsess && sess->server == killserv)
|
||||
{
|
||||
killserv->front_session = sess;
|
||||
if (!killserv->server_session)
|
||||
killserv->server_session = sess;
|
||||
break;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!killserv->server_session)
|
||||
killserv->server_session = killserv->front_session;
|
||||
|
||||
sess_list = g_slist_remove (sess_list, killsess);
|
||||
|
||||
if (killsess->type == SESS_CHANNEL)
|
||||
userlist_free (killsess);
|
||||
|
||||
exec_notify_kill (killsess);
|
||||
|
||||
log_close (killsess);
|
||||
scrollback_close (killsess);
|
||||
chanopt_save (killsess);
|
||||
|
||||
send_quit_or_part (killsess);
|
||||
|
||||
history_free (&killsess->history);
|
||||
if (killsess->topic)
|
||||
free (killsess->topic);
|
||||
if (killsess->current_modes)
|
||||
free (killsess->current_modes);
|
||||
|
||||
fe_session_callback (killsess);
|
||||
|
||||
if (current_sess == killsess)
|
||||
{
|
||||
current_sess = NULL;
|
||||
if (sess_list)
|
||||
current_sess = sess_list->data;
|
||||
}
|
||||
|
||||
free (killsess);
|
||||
|
||||
if (!sess_list && !in_xchat_exit)
|
||||
xchat_exit (); /* sess_list is empty, quit! */
|
||||
|
||||
list = sess_list;
|
||||
while (list)
|
||||
{
|
||||
sess = (session *) list->data;
|
||||
if (sess->server == killserv)
|
||||
return; /* this server is still being used! */
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
server_free (killserv);
|
||||
}
|
||||
|
||||
static void
|
||||
free_sessions (void)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
|
||||
while (list)
|
||||
{
|
||||
fe_close_window (list->data);
|
||||
list = sess_list;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char defaultconf_ctcp[] =
|
||||
"NAME TIME\n" "CMD nctcp %s TIME %t\n\n"\
|
||||
"NAME PING\n" "CMD nctcp %s PING %d\n\n";
|
||||
|
||||
static char defaultconf_replace[] =
|
||||
"NAME teh\n" "CMD the\n\n";
|
||||
/* "NAME r\n" "CMD are\n\n"\
|
||||
"NAME u\n" "CMD you\n\n"*/
|
||||
|
||||
static char defaultconf_commands[] =
|
||||
"NAME ACTION\n" "CMD me &2\n\n"\
|
||||
"NAME AME\n" "CMD allchan me &2\n\n"\
|
||||
"NAME ANICK\n" "CMD allserv nick &2\n\n"\
|
||||
"NAME AMSG\n" "CMD allchan say &2\n\n"\
|
||||
"NAME BANLIST\n" "CMD quote MODE %c +b\n\n"\
|
||||
"NAME CHAT\n" "CMD dcc chat %2\n\n"\
|
||||
"NAME DIALOG\n" "CMD query %2\n\n"\
|
||||
"NAME DMSG\n" "CMD msg =%2 &3\n\n"\
|
||||
"NAME EXIT\n" "CMD quit\n\n"\
|
||||
"NAME GREP\n" "CMD lastlog -r &2\n\n"\
|
||||
"NAME J\n" "CMD join &2\n\n"\
|
||||
"NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\
|
||||
"NAME LEAVE\n" "CMD part &2\n\n"\
|
||||
"NAME M\n" "CMD msg &2\n\n"\
|
||||
"NAME ONOTICE\n" "CMD notice @%c &2\n\n"\
|
||||
"NAME RAW\n" "CMD quote &2\n\n"\
|
||||
"NAME SERVHELP\n" "CMD quote HELP\n\n"\
|
||||
"NAME SPING\n" "CMD ping\n\n"\
|
||||
"NAME SQUERY\n" "CMD quote SQUERY %2 :&3\n\n"\
|
||||
"NAME SSLSERVER\n" "CMD server -ssl &2\n\n"\
|
||||
"NAME SV\n" "CMD echo xchat %v %m\n\n"\
|
||||
"NAME UMODE\n" "CMD mode %n &2\n\n"\
|
||||
"NAME UPTIME\n" "CMD quote STATS u\n\n"\
|
||||
"NAME VER\n" "CMD ctcp %2 VERSION\n\n"\
|
||||
"NAME VERSION\n" "CMD ctcp %2 VERSION\n\n"\
|
||||
"NAME WALLOPS\n" "CMD quote WALLOPS :&2\n\n"\
|
||||
"NAME WII\n" "CMD quote WHOIS %2 %2\n\n";
|
||||
|
||||
static char defaultconf_urlhandlers[] =
|
||||
"NAME Open Link in Opera\n" "CMD !opera -remote 'openURL(%s)'\n\n";
|
||||
|
||||
#ifdef USE_SIGACTION
|
||||
/* Close and open log files on SIGUSR1. Usefull for log rotating */
|
||||
|
||||
static void
|
||||
sigusr1_handler (int signal, siginfo_t *si, void *un)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
session *sess;
|
||||
|
||||
while (list)
|
||||
{
|
||||
sess = list->data;
|
||||
log_open_or_close (sess);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute /SIGUSR2 when SIGUSR2 received */
|
||||
|
||||
static void
|
||||
sigusr2_handler (int signal, siginfo_t *si, void *un)
|
||||
{
|
||||
session *sess = current_sess;
|
||||
|
||||
if (sess)
|
||||
handle_command (sess, "SIGUSR2", FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
static gint
|
||||
xchat_auto_connect (gpointer userdata)
|
||||
{
|
||||
servlist_auto_connect (NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xchat_init (void)
|
||||
{
|
||||
char buf[3068];
|
||||
const char *cs = NULL;
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsadata;
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if (WSAStartup(0x0202, &wsadata) != 0)
|
||||
{
|
||||
MessageBox (NULL, "Cannot find winsock 2.2+", "Error", MB_OK);
|
||||
exit (0);
|
||||
}
|
||||
#else
|
||||
WSAStartup(0x0101, &wsadata);
|
||||
#endif /* !USE_IPV6 */
|
||||
#endif /* !WIN32 */
|
||||
|
||||
#ifdef USE_SIGACTION
|
||||
struct sigaction act;
|
||||
|
||||
/* ignore SIGPIPE's */
|
||||
act.sa_handler = SIG_IGN;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigaction (SIGPIPE, &act, NULL);
|
||||
|
||||
/* Deal with SIGUSR1's & SIGUSR2's */
|
||||
act.sa_sigaction = sigusr1_handler;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigaction (SIGUSR1, &act, NULL);
|
||||
|
||||
act.sa_sigaction = sigusr2_handler;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigaction (SIGUSR2, &act, NULL);
|
||||
#else
|
||||
#ifndef WIN32
|
||||
/* good enough for these old systems */
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (g_get_charset (&cs))
|
||||
prefs.utf8_locale = TRUE;
|
||||
|
||||
load_text_events ();
|
||||
sound_load ();
|
||||
notify_load ();
|
||||
ignore_load ();
|
||||
|
||||
snprintf (buf, sizeof (buf),
|
||||
"NAME %s~%s~\n" "CMD query %%s\n\n"\
|
||||
"NAME %s~%s~\n" "CMD send %%s\n\n"\
|
||||
"NAME %s~%s~\n" "CMD whois %%s %%s\n\n"\
|
||||
"NAME %s~%s~\n" "CMD notify -n ASK %%s\n\n"\
|
||||
|
||||
"NAME SUB\n" "CMD %s\n\n"\
|
||||
"NAME %s\n" "CMD op %%a\n\n"\
|
||||
"NAME %s\n" "CMD deop %%a\n\n"\
|
||||
"NAME SEP\n" "CMD \n\n"\
|
||||
"NAME %s\n" "CMD voice %%a\n\n"\
|
||||
"NAME %s\n" "CMD devoice %%a\n"\
|
||||
"NAME SEP\n" "CMD \n\n"\
|
||||
"NAME SUB\n" "CMD %s\n\n"\
|
||||
"NAME %s\n" "CMD kick %%s\n\n"\
|
||||
"NAME %s\n" "CMD ban %%s\n\n"\
|
||||
"NAME SEP\n" "CMD \n\n"\
|
||||
"NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\
|
||||
"NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\
|
||||
"NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\
|
||||
"NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\
|
||||
"NAME SEP\n" "CMD \n\n"\
|
||||
"NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\
|
||||
"NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\
|
||||
"NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\
|
||||
"NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\
|
||||
"NAME ENDSUB\n" "CMD \n\n"\
|
||||
"NAME ENDSUB\n" "CMD \n\n",
|
||||
|
||||
_("_Open Dialog Window"), "xchat-dialog",
|
||||
_("_Send a File"), "gtk-floppy",
|
||||
_("_User Info (WhoIs)"), "gtk-info",
|
||||
_("_Add to Friends List"), "gtk-add",
|
||||
_("O_perator Actions"),
|
||||
|
||||
_("Give Ops"),
|
||||
_("Take Ops"),
|
||||
_("Give Voice"),
|
||||
_("Take Voice"),
|
||||
|
||||
_("Kick/Ban"),
|
||||
_("Kick"),
|
||||
_("Ban"),
|
||||
_("Ban"),
|
||||
_("Ban"),
|
||||
_("Ban"),
|
||||
_("Ban"),
|
||||
_("KickBan"),
|
||||
_("KickBan"),
|
||||
_("KickBan"),
|
||||
_("KickBan"));
|
||||
|
||||
list_loadconf ("popup.conf", &popup_list, buf);
|
||||
|
||||
snprintf (buf, sizeof (buf),
|
||||
"NAME %s\n" "CMD part\n\n"
|
||||
"NAME %s\n" "CMD getstr # join \"%s\"\n\n"
|
||||
"NAME %s\n" "CMD quote LINKS\n\n"
|
||||
"NAME %s\n" "CMD ping\n\n"
|
||||
"NAME TOGGLE %s\n" "CMD irc_hide_version\n\n",
|
||||
_("Leave Channel"),
|
||||
_("Join Channel..."),
|
||||
_("Enter Channel to Join:"),
|
||||
_("Server Links"),
|
||||
_("Ping Server"),
|
||||
_("Hide Version"));
|
||||
list_loadconf ("usermenu.conf", &usermenu_list, buf);
|
||||
|
||||
snprintf (buf, sizeof (buf),
|
||||
"NAME %s\n" "CMD op %%a\n\n"
|
||||
"NAME %s\n" "CMD deop %%a\n\n"
|
||||
"NAME %s\n" "CMD ban %%s\n\n"
|
||||
"NAME %s\n" "CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n"
|
||||
"NAME %s\n" "CMD send %%s\n\n"
|
||||
"NAME %s\n" "CMD query %%s\n\n",
|
||||
_("Op"),
|
||||
_("DeOp"),
|
||||
_("Ban"),
|
||||
_("Kick"),
|
||||
_("bye"),
|
||||
_("Enter reason to kick %s:"),
|
||||
_("Sendfile"),
|
||||
_("Dialog"));
|
||||
list_loadconf ("buttons.conf", &button_list, buf);
|
||||
|
||||
snprintf (buf, sizeof (buf),
|
||||
"NAME %s\n" "CMD whois %%s %%s\n\n"
|
||||
"NAME %s\n" "CMD send %%s\n\n"
|
||||
"NAME %s\n" "CMD dcc chat %%s\n\n"
|
||||
"NAME %s\n" "CMD clear\n\n"
|
||||
"NAME %s\n" "CMD ping %%s\n\n",
|
||||
_("WhoIs"),
|
||||
_("Send"),
|
||||
_("Chat"),
|
||||
_("Clear"),
|
||||
_("Ping"));
|
||||
list_loadconf ("dlgbuttons.conf", &dlgbutton_list, buf);
|
||||
|
||||
list_loadconf ("tabmenu.conf", &tabmenu_list, NULL);
|
||||
list_loadconf ("ctcpreply.conf", &ctcp_list, defaultconf_ctcp);
|
||||
list_loadconf ("commands.conf", &command_list, defaultconf_commands);
|
||||
list_loadconf ("replace.conf", &replace_list, defaultconf_replace);
|
||||
list_loadconf ("urlhandlers.conf", &urlhandler_list,
|
||||
defaultconf_urlhandlers);
|
||||
|
||||
servlist_init (); /* load server list */
|
||||
|
||||
/* if we got a URL, don't open the server list GUI */
|
||||
if (!prefs.slist_skip && !arg_url)
|
||||
fe_serverlist_open (NULL);
|
||||
|
||||
/* turned OFF via -a arg */
|
||||
if (!arg_dont_autoconnect)
|
||||
{
|
||||
/* do any auto connects */
|
||||
if (!servlist_have_auto ()) /* if no new windows open .. */
|
||||
{
|
||||
/* and no serverlist gui ... */
|
||||
if (prefs.slist_skip || arg_url)
|
||||
/* we'll have to open one. */
|
||||
new_ircwindow (NULL, NULL, SESS_SERVER, 0);
|
||||
} else
|
||||
{
|
||||
fe_idle_add (xchat_auto_connect, NULL);
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (prefs.slist_skip || arg_url)
|
||||
new_ircwindow (NULL, NULL, SESS_SERVER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xchat_exit (void)
|
||||
{
|
||||
xchat_is_quitting = TRUE;
|
||||
in_xchat_exit = TRUE;
|
||||
plugin_kill_all ();
|
||||
fe_cleanup ();
|
||||
if (prefs.autosave)
|
||||
{
|
||||
save_config ();
|
||||
if (prefs.save_pevents)
|
||||
pevent_save (NULL);
|
||||
}
|
||||
if (prefs.autosave_url)
|
||||
url_autosave ();
|
||||
sound_save ();
|
||||
notify_save ();
|
||||
ignore_save ();
|
||||
free_sessions ();
|
||||
chanopt_save_all ();
|
||||
servlist_cleanup ();
|
||||
fe_exit ();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
static int
|
||||
child_handler (gpointer userdata)
|
||||
{
|
||||
int pid = GPOINTER_TO_INT (userdata);
|
||||
|
||||
if (waitpid (pid, 0, WNOHANG) == pid)
|
||||
return 0; /* remove timeout handler */
|
||||
return 1; /* keep the timeout handler */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
xchat_exec (const char *cmd)
|
||||
{
|
||||
#ifdef WIN32
|
||||
util_exec (cmd);
|
||||
#else
|
||||
int pid = util_exec (cmd);
|
||||
if (pid != -1)
|
||||
/* zombie avoiding system. Don't ask! it has to be like this to work
|
||||
with zvt (which overrides the default handler) */
|
||||
fe_timeout_add (1000, child_handler, GINT_TO_POINTER (pid));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
xchat_execv (char * const argv[])
|
||||
{
|
||||
#ifdef WIN32
|
||||
util_execv (argv);
|
||||
#else
|
||||
int pid = util_execv (argv);
|
||||
if (pid != -1)
|
||||
/* zombie avoiding system. Don't ask! it has to be like this to work
|
||||
with zvt (which overrides the default handler) */
|
||||
fe_timeout_add (1000, child_handler, GINT_TO_POINTER (pid));
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
srand (time (0)); /* CL: do this only once! */
|
||||
|
||||
#ifdef SOCKS
|
||||
SOCKSinit (argv[0]);
|
||||
#endif
|
||||
|
||||
ret = fe_args (argc, argv);
|
||||
if (ret != -1)
|
||||
return ret;
|
||||
|
||||
#ifdef USE_DBUS
|
||||
xchat_remote ();
|
||||
#endif
|
||||
|
||||
load_config ();
|
||||
|
||||
#ifdef USE_LIBPROXY
|
||||
libproxy_factory = px_proxy_factory_new();
|
||||
#endif
|
||||
|
||||
fe_init ();
|
||||
|
||||
xchat_init ();
|
||||
|
||||
fe_main ();
|
||||
|
||||
#ifdef USE_LIBPROXY
|
||||
px_proxy_factory_free(libproxy_factory);
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
if (ctx)
|
||||
_SSL_context_free (ctx);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DEBUG
|
||||
xchat_mem_list ();
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
WSACleanup ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
575
src/common/xchat.h
Normal file
575
src/common/xchat.h
Normal file
@@ -0,0 +1,575 @@
|
||||
#include "../../config.h"
|
||||
|
||||
#include <glib/gslist.h>
|
||||
#include <glib/glist.h>
|
||||
#include <glib/gutils.h>
|
||||
#include <glib/giochannel.h>
|
||||
#include <glib/gstrfuncs.h>
|
||||
#include <time.h> /* need time_t */
|
||||
|
||||
#ifndef XCHAT_H
|
||||
#define XCHAT_H
|
||||
|
||||
#include "history.h"
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
#define snprintf g_snprintf
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_VSNPRINTF
|
||||
#define vsnprintf g_vsnprintf
|
||||
#endif
|
||||
|
||||
#ifdef USE_DEBUG
|
||||
#define malloc(n) xchat_malloc(n, __FILE__, __LINE__)
|
||||
#define realloc(n, m) xchat_realloc(n, m, __FILE__, __LINE__)
|
||||
#define free(n) xchat_dfree(n, __FILE__, __LINE__)
|
||||
#define strdup(n) xchat_strdup(n, __FILE__, __LINE__)
|
||||
void *xchat_malloc (int size, char *file, int line);
|
||||
void *xchat_strdup (char *str, char *file, int line);
|
||||
void xchat_dfree (void *buf, char *file, int line);
|
||||
void *xchat_realloc (char *old, int len, char *file, int line);
|
||||
#endif
|
||||
|
||||
#ifdef SOCKS
|
||||
#ifdef __sgi
|
||||
#include <sys/time.h>
|
||||
#define INCLUDE_PROTOTYPES 1
|
||||
#endif
|
||||
#include <socks.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h> /* SSL_() */
|
||||
#endif
|
||||
|
||||
#ifdef __EMX__ /* for o/s 2 */
|
||||
#define OFLAGS O_BINARY
|
||||
#define strcasecmp stricmp
|
||||
#define strncasecmp strnicmp
|
||||
#define PATH_MAX MAXPATHLEN
|
||||
#define FILEPATH_LEN_MAX MAXPATHLEN
|
||||
#endif
|
||||
|
||||
/* force a 32bit CMP.L */
|
||||
#define CMPL(a, c0, c1, c2, c3) (a == (guint32)(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)))
|
||||
#define WORDL(c0, c1, c2, c3) (guint32)(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24))
|
||||
#define WORDW(c0, c1) (guint16)(c0 | (c1 << 8))
|
||||
|
||||
#ifdef WIN32 /* for win32 */
|
||||
#define OFLAGS O_BINARY
|
||||
#define sleep(t) _sleep(t*1000)
|
||||
#include <direct.h>
|
||||
#define F_OK 0
|
||||
#define X_OK 1
|
||||
#define W_OK 2
|
||||
#define R_OK 4
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) ((m) & _S_IFDIR)
|
||||
#endif
|
||||
#define NETWORK_PRIVATE
|
||||
#else /* for unix */
|
||||
#define OFLAGS 0
|
||||
#endif
|
||||
|
||||
#define FONTNAMELEN 127
|
||||
#define PATHLEN 255
|
||||
#define DOMAINLEN 100
|
||||
#define NICKLEN 64 /* including the NULL, so 63 really */
|
||||
#define CHANLEN 300
|
||||
#define PDIWORDS 32
|
||||
#define USERNAMELEN 10
|
||||
#define HIDDEN_CHAR 8 /* invisible character for xtext */
|
||||
|
||||
#if defined(ENABLE_NLS) && !defined(_)
|
||||
# include <libintl.h>
|
||||
# define _(x) gettext(x)
|
||||
# ifdef gettext_noop
|
||||
# define N_(String) gettext_noop (String)
|
||||
# else
|
||||
# define N_(String) (String)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(_)
|
||||
# define N_(String) (String)
|
||||
# define _(x) (x)
|
||||
#endif
|
||||
|
||||
struct nbexec
|
||||
{
|
||||
int myfd;
|
||||
int childpid;
|
||||
int tochannel; /* making this int keeps the struct 4-byte aligned */
|
||||
int iotag;
|
||||
char *linebuf;
|
||||
int buffill;
|
||||
struct session *sess;
|
||||
};
|
||||
|
||||
struct xchatprefs
|
||||
{
|
||||
char nick1[NICKLEN];
|
||||
char nick2[NICKLEN];
|
||||
char nick3[NICKLEN];
|
||||
char realname[127];
|
||||
char username[127];
|
||||
char nick_suffix[4]; /* Only ever holds a one-character string. */
|
||||
char awayreason[256];
|
||||
char quitreason[256];
|
||||
char partreason[256];
|
||||
char font_normal[FONTNAMELEN + 1];
|
||||
char doubleclickuser[256];
|
||||
char sounddir[PATHLEN + 1];
|
||||
char soundcmd[PATHLEN + 1];
|
||||
char background[PATHLEN + 1];
|
||||
char dccdir[PATHLEN + 1];
|
||||
char dcc_completed_dir[PATHLEN + 1];
|
||||
char irc_extra_hilight[300];
|
||||
char irc_no_hilight[300];
|
||||
char irc_nick_hilight[300];
|
||||
char dnsprogram[72];
|
||||
char hostname[127];
|
||||
char cmdchar[4];
|
||||
char logmask[256];
|
||||
char stamp_format[64];
|
||||
char timestamp_log_format[64];
|
||||
char irc_id_ytext[64];
|
||||
char irc_id_ntext[64];
|
||||
|
||||
char proxy_host[64];
|
||||
int proxy_port;
|
||||
int proxy_type; /* 0=disabled, 1=wingate 2=socks4, 3=socks5, 4=http */
|
||||
int proxy_use; /* 0=all 1=IRC_ONLY 2=DCC_ONLY */
|
||||
unsigned int proxy_auth;
|
||||
char proxy_user[32];
|
||||
char proxy_pass[32];
|
||||
|
||||
int first_dcc_send_port;
|
||||
int last_dcc_send_port;
|
||||
|
||||
int tint_red;
|
||||
int tint_green;
|
||||
int tint_blue;
|
||||
|
||||
int away_timeout;
|
||||
int away_size_max;
|
||||
|
||||
int gui_pane_left_size;
|
||||
int gui_pane_right_size;
|
||||
|
||||
int gui_ulist_pos;
|
||||
int tab_pos;
|
||||
|
||||
int _tabs_position;
|
||||
int tab_layout;
|
||||
int max_auto_indent;
|
||||
int dcc_blocksize;
|
||||
int max_lines;
|
||||
int notify_timeout;
|
||||
int dcctimeout;
|
||||
int dccstalltimeout;
|
||||
int dcc_global_max_get_cps;
|
||||
int dcc_global_max_send_cps;
|
||||
int dcc_max_get_cps;
|
||||
int dcc_max_send_cps;
|
||||
int mainwindow_left;
|
||||
int mainwindow_top;
|
||||
int mainwindow_width;
|
||||
int mainwindow_height;
|
||||
int completion_sort;
|
||||
int gui_win_state;
|
||||
int gui_url_mod;
|
||||
int gui_usermenu;
|
||||
int gui_join_dialog;
|
||||
int gui_quit_dialog;
|
||||
int dialog_left;
|
||||
int dialog_top;
|
||||
int dialog_width;
|
||||
int dialog_height;
|
||||
int dccpermissions;
|
||||
int recon_delay;
|
||||
int bantype;
|
||||
int userlist_sort;
|
||||
guint32 local_ip;
|
||||
guint32 dcc_ip;
|
||||
char dcc_ip_str[DOMAINLEN + 1];
|
||||
|
||||
unsigned int tab_small;
|
||||
unsigned int tab_sort;
|
||||
unsigned int mainwindow_save;
|
||||
unsigned int perc_color;
|
||||
unsigned int perc_ascii;
|
||||
unsigned int autosave;
|
||||
unsigned int autodialog;
|
||||
unsigned int autosave_url;
|
||||
unsigned int autoreconnect;
|
||||
unsigned int autoreconnectonfail;
|
||||
unsigned int invisible;
|
||||
unsigned int servernotice;
|
||||
unsigned int wallops;
|
||||
unsigned int skipmotd;
|
||||
unsigned int autorejoin;
|
||||
unsigned int colorednicks;
|
||||
unsigned int chanmodebuttons;
|
||||
unsigned int userlistbuttons;
|
||||
unsigned int showhostname_in_userlist;
|
||||
unsigned int nickcompletion;
|
||||
unsigned int completion_amount;
|
||||
unsigned int tabchannels;
|
||||
unsigned int paned_userlist;
|
||||
unsigned int autodccchat;
|
||||
unsigned int autodccsend;
|
||||
unsigned int autoresume;
|
||||
unsigned int autoopendccsendwindow;
|
||||
unsigned int autoopendccrecvwindow;
|
||||
unsigned int autoopendccchatwindow;
|
||||
unsigned int transparent;
|
||||
unsigned int stripcolor;
|
||||
unsigned int timestamp;
|
||||
unsigned int fastdccsend;
|
||||
unsigned int dcc_send_fillspaces;
|
||||
unsigned int dcc_remove;
|
||||
unsigned int slist_fav;
|
||||
unsigned int slist_skip;
|
||||
unsigned int slist_select;
|
||||
unsigned int filterbeep;
|
||||
|
||||
unsigned int input_balloon_chans;
|
||||
unsigned int input_balloon_hilight;
|
||||
unsigned int input_balloon_priv;
|
||||
unsigned int input_balloon_time;
|
||||
|
||||
unsigned int input_beep_chans;
|
||||
unsigned int input_beep_hilight;
|
||||
unsigned int input_beep_priv;
|
||||
|
||||
unsigned int input_flash_chans;
|
||||
unsigned int input_flash_hilight;
|
||||
unsigned int input_flash_priv;
|
||||
|
||||
unsigned int input_tray_chans;
|
||||
unsigned int input_tray_hilight;
|
||||
unsigned int input_tray_priv;
|
||||
|
||||
unsigned int truncchans;
|
||||
unsigned int privmsgtab;
|
||||
unsigned int irc_join_delay;
|
||||
unsigned int logging;
|
||||
unsigned int timestamp_logs;
|
||||
unsigned int newtabstofront;
|
||||
unsigned int dccwithnick;
|
||||
unsigned int hidever;
|
||||
unsigned int ip_from_server;
|
||||
unsigned int raw_modes;
|
||||
unsigned int show_away_once;
|
||||
unsigned int show_away_message;
|
||||
unsigned int auto_unmark_away;
|
||||
unsigned int away_track;
|
||||
unsigned int userhost;
|
||||
unsigned int irc_whois_front;
|
||||
unsigned int use_server_tab;
|
||||
unsigned int notices_tabs;
|
||||
unsigned int style_namelistgad;
|
||||
unsigned int style_inputbox;
|
||||
unsigned int windows_as_tabs;
|
||||
unsigned int indent_nicks;
|
||||
unsigned int text_replay;
|
||||
unsigned int show_marker;
|
||||
unsigned int show_separator;
|
||||
unsigned int thin_separator;
|
||||
unsigned int auto_indent;
|
||||
unsigned int wordwrap;
|
||||
unsigned int gui_input_spell;
|
||||
unsigned int gui_tray;
|
||||
unsigned int gui_tray_flags;
|
||||
unsigned int gui_tweaks;
|
||||
unsigned int _gui_ulist_left;
|
||||
unsigned int throttle;
|
||||
unsigned int topicbar;
|
||||
unsigned int hideuserlist;
|
||||
unsigned int hidemenu;
|
||||
unsigned int perlwarnings;
|
||||
unsigned int lagometer;
|
||||
unsigned int throttlemeter;
|
||||
unsigned int pingtimeout;
|
||||
unsigned int whois_on_notifyonline;
|
||||
unsigned int wait_on_exit;
|
||||
unsigned int confmode;
|
||||
unsigned int utf8_locale;
|
||||
unsigned int identd;
|
||||
|
||||
unsigned int ctcp_number_limit; /*flood */
|
||||
unsigned int ctcp_time_limit; /*seconds of floods */
|
||||
|
||||
unsigned int msg_number_limit; /*same deal */
|
||||
unsigned int msg_time_limit;
|
||||
|
||||
/* Tells us if we need to save, only when they've been edited.
|
||||
This is so that we continue using internal defaults (which can
|
||||
change in the next release) until the user edits them. */
|
||||
unsigned int save_pevents:1;
|
||||
};
|
||||
|
||||
/* Session types */
|
||||
#define SESS_SERVER 1
|
||||
#define SESS_CHANNEL 2
|
||||
#define SESS_DIALOG 3
|
||||
#define SESS_NOTICES 4
|
||||
#define SESS_SNOTICES 5
|
||||
|
||||
/* Per-Channel Settings */
|
||||
#define SET_OFF 0
|
||||
#define SET_ON 1
|
||||
#define SET_DEFAULT 2 /* use global setting */
|
||||
|
||||
typedef struct session
|
||||
{
|
||||
/* Per-Channel Alerts */
|
||||
/* use a byte, because we need a pointer to each element */
|
||||
guint8 alert_beep;
|
||||
guint8 alert_taskbar;
|
||||
guint8 alert_tray;
|
||||
|
||||
/* Per-Channel Settings */
|
||||
guint8 text_hidejoinpart;
|
||||
guint8 text_logging;
|
||||
guint8 text_scrollback;
|
||||
|
||||
struct server *server;
|
||||
void *usertree_alpha; /* pure alphabetical tree */
|
||||
void *usertree; /* ordered with Ops first */
|
||||
struct User *me; /* points to myself in the usertree */
|
||||
char channel[CHANLEN];
|
||||
char waitchannel[CHANLEN]; /* waiting to join channel (/join sent) */
|
||||
char willjoinchannel[CHANLEN]; /* will issue /join for this channel */
|
||||
char channelkey[64]; /* XXX correct max length? */
|
||||
int limit; /* channel user limit */
|
||||
int logfd;
|
||||
int scrollfd; /* scrollback filedes */
|
||||
int scrollwritten; /* number of lines written */
|
||||
|
||||
char lastnick[NICKLEN]; /* last nick you /msg'ed */
|
||||
|
||||
struct history history;
|
||||
|
||||
int ops; /* num. of ops in channel */
|
||||
int hops; /* num. of half-oped users */
|
||||
int voices; /* num. of voiced people */
|
||||
int total; /* num. of users in channel */
|
||||
|
||||
char *quitreason;
|
||||
char *topic;
|
||||
char *current_modes; /* free() me */
|
||||
|
||||
int mode_timeout_tag;
|
||||
|
||||
struct session *lastlog_sess;
|
||||
struct nbexec *running_exec;
|
||||
|
||||
struct session_gui *gui; /* initialized by fe_new_window */
|
||||
struct restore_gui *res;
|
||||
|
||||
int type; /* SESS_* */
|
||||
|
||||
int new_data:1; /* new data avail? (purple tab) */
|
||||
int nick_said:1; /* your nick mentioned? (blue tab) */
|
||||
int msg_said:1; /* new msg available? (red tab) */
|
||||
|
||||
int ignore_date:1;
|
||||
int ignore_mode:1;
|
||||
int ignore_names:1;
|
||||
int end_of_names:1;
|
||||
int doing_who:1; /* /who sent on this channel */
|
||||
int done_away_check:1; /* done checking for away status changes */
|
||||
unsigned int lastlog_regexp:1; /* this is a lastlog and using regexp */
|
||||
} session;
|
||||
|
||||
struct msproxy_state_t
|
||||
{
|
||||
gint32 clientid;
|
||||
gint32 serverid;
|
||||
unsigned char seq_recv; /* seq number of last packet recv. */
|
||||
unsigned char seq_sent; /* seq number of last packet sent. */
|
||||
};
|
||||
|
||||
typedef struct server
|
||||
{
|
||||
/* server control operations (in server*.c) */
|
||||
void (*connect)(struct server *, char *hostname, int port, int no_login);
|
||||
void (*disconnect)(struct session *, int sendquit, int err);
|
||||
int (*cleanup)(struct server *);
|
||||
void (*flush_queue)(struct server *);
|
||||
void (*auto_reconnect)(struct server *, int send_quit, int err);
|
||||
/* irc protocol functions (in proto*.c) */
|
||||
void (*p_inline)(struct server *, char *buf, int len);
|
||||
void (*p_invite)(struct server *, char *channel, char *nick);
|
||||
void (*p_cycle)(struct server *, char *channel, char *key);
|
||||
void (*p_ctcp)(struct server *, char *to, char *msg);
|
||||
void (*p_nctcp)(struct server *, char *to, char *msg);
|
||||
void (*p_quit)(struct server *, char *reason);
|
||||
void (*p_kick)(struct server *, char *channel, char *nick, char *reason);
|
||||
void (*p_part)(struct server *, char *channel, char *reason);
|
||||
void (*p_ns_identify)(struct server *, char *pass);
|
||||
void (*p_ns_ghost)(struct server *, char *usname, char *pass);
|
||||
void (*p_join)(struct server *, char *channel, char *key);
|
||||
void (*p_join_list)(struct server *, GSList *channels, GSList *keys);
|
||||
void (*p_login)(struct server *, char *user, char *realname);
|
||||
void (*p_join_info)(struct server *, char *channel);
|
||||
void (*p_mode)(struct server *, char *target, char *mode);
|
||||
void (*p_user_list)(struct server *, char *channel);
|
||||
void (*p_away_status)(struct server *, char *channel);
|
||||
void (*p_whois)(struct server *, char *nicks);
|
||||
void (*p_get_ip)(struct server *, char *nick);
|
||||
void (*p_get_ip_uh)(struct server *, char *nick);
|
||||
void (*p_set_back)(struct server *);
|
||||
void (*p_set_away)(struct server *, char *reason);
|
||||
void (*p_message)(struct server *, char *channel, char *text);
|
||||
void (*p_action)(struct server *, char *channel, char *act);
|
||||
void (*p_notice)(struct server *, char *channel, char *text);
|
||||
void (*p_topic)(struct server *, char *channel, char *topic);
|
||||
void (*p_list_channels)(struct server *, char *arg, int min_users);
|
||||
void (*p_change_nick)(struct server *, char *new_nick);
|
||||
void (*p_names)(struct server *, char *channel);
|
||||
void (*p_ping)(struct server *, char *to, char *timestring);
|
||||
/* void (*p_set_away)(struct server *);*/
|
||||
int (*p_raw)(struct server *, char *raw);
|
||||
int (*p_cmp)(const char *s1, const char *s2);
|
||||
|
||||
int port;
|
||||
int sok; /* is equal to sok4 or sok6 (the one we are using) */
|
||||
int sok4; /* tcp4 socket */
|
||||
int sok6; /* tcp6 socket */
|
||||
int proxy_type;
|
||||
int proxy_sok; /* Additional information for MS Proxy beast */
|
||||
int proxy_sok4;
|
||||
int proxy_sok6;
|
||||
struct msproxy_state_t msp_state;
|
||||
int id; /* unique ID number (for plugin API) */
|
||||
#ifdef USE_OPENSSL
|
||||
SSL *ssl;
|
||||
int ssl_do_connect_tag;
|
||||
#else
|
||||
void *ssl;
|
||||
#endif
|
||||
int childread;
|
||||
int childwrite;
|
||||
int childpid;
|
||||
int iotag;
|
||||
int recondelay_tag; /* reconnect delay timeout */
|
||||
int joindelay_tag; /* waiting before we send JOIN */
|
||||
char hostname[128]; /* real ip number */
|
||||
char servername[128]; /* what the server says is its name */
|
||||
char password[86];
|
||||
char nick[NICKLEN];
|
||||
char linebuf[2048]; /* RFC says 512 chars including \r\n */
|
||||
char *last_away_reason;
|
||||
int pos; /* current position in linebuf */
|
||||
int nickcount;
|
||||
int nickservtype; /* 0=/MSG nickserv 1=/NICKSERV 2=/NS */
|
||||
|
||||
char *chantypes; /* for 005 numeric - free me */
|
||||
char *chanmodes; /* for 005 numeric - free me */
|
||||
char *nick_prefixes; /* e.g. "*@%+" */
|
||||
char *nick_modes; /* e.g. "aohv" */
|
||||
char *bad_nick_prefixes; /* for ircd that doesn't give the modes */
|
||||
int modes_per_line; /* 6 on undernet, 4 on efnet etc... */
|
||||
|
||||
void *network; /* points to entry in servlist.c or NULL! */
|
||||
|
||||
GSList *outbound_queue;
|
||||
time_t next_send; /* cptr->since in ircu */
|
||||
time_t prev_now; /* previous now-time */
|
||||
int sendq_len; /* queue size */
|
||||
int lag; /* milliseconds */
|
||||
|
||||
struct session *front_session; /* front-most window/tab */
|
||||
struct session *server_session; /* server window/tab */
|
||||
|
||||
struct server_gui *gui; /* initialized by fe_new_server */
|
||||
|
||||
unsigned int ctcp_counter; /*flood */
|
||||
time_t ctcp_last_time;
|
||||
|
||||
unsigned int msg_counter; /*counts the msg tab opened in a certain time */
|
||||
time_t msg_last_time;
|
||||
|
||||
/*time_t connect_time;*/ /* when did it connect? */
|
||||
time_t lag_sent;
|
||||
time_t ping_recv; /* when we last got a ping reply */
|
||||
time_t away_time; /* when we were marked away */
|
||||
|
||||
char *encoding; /* NULL for system */
|
||||
char *autojoin; /* list of channels & keys to join */
|
||||
|
||||
unsigned int motd_skipped:1;
|
||||
unsigned int connected:1;
|
||||
unsigned int connecting:1;
|
||||
unsigned int no_login:1;
|
||||
unsigned int skip_next_userhost:1;/* used for "get my ip from server" */
|
||||
unsigned int skip_next_whois:1; /* hide whois output */
|
||||
unsigned int inside_whois:1;
|
||||
unsigned int doing_dns:1; /* /dns has been done */
|
||||
unsigned int end_of_motd:1; /* end of motd reached (logged in) */
|
||||
unsigned int sent_quit:1; /* sent a QUIT already? */
|
||||
unsigned int use_listargs:1; /* undernet and dalnet need /list >0,<10000 */
|
||||
unsigned int is_away:1;
|
||||
unsigned int reconnect_away:1; /* whether to reconnect in is_away state */
|
||||
unsigned int dont_use_proxy:1; /* to proxy or not to proxy */
|
||||
unsigned int supports_watch:1; /* supports the WATCH command */
|
||||
unsigned int bad_prefix:1; /* gave us a bad PREFIX= 005 number */
|
||||
unsigned int have_namesx:1; /* 005 tokens NAMESX and UHNAMES */
|
||||
unsigned int have_uhnames:1;
|
||||
unsigned int have_whox:1; /* have undernet's WHOX features */
|
||||
unsigned int have_capab:1; /* supports CAPAB (005 tells us) */
|
||||
unsigned int have_idmsg:1; /* freenode's IDENTIFY-MSG */
|
||||
unsigned int have_except:1; /* ban exemptions +e */
|
||||
unsigned int using_cp1255:1; /* encoding is CP1255/WINDOWS-1255? */
|
||||
unsigned int using_irc:1; /* encoding is "IRC" (CP1252/UTF-8 hybrid)? */
|
||||
unsigned int use_who:1; /* whether to use WHO command to get dcc_ip */
|
||||
#ifdef USE_OPENSSL
|
||||
unsigned int use_ssl:1; /* is server SSL capable? */
|
||||
unsigned int accept_invalid_cert:1;/* ignore result of server's cert. verify */
|
||||
#endif
|
||||
} server;
|
||||
|
||||
typedef int (*cmd_callback) (struct session * sess, char *tbuf, char *word[],
|
||||
char *word_eol[]);
|
||||
|
||||
struct commands
|
||||
{
|
||||
char *name;
|
||||
cmd_callback callback;
|
||||
char needserver;
|
||||
char needchannel;
|
||||
gint16 handle_quotes;
|
||||
char *help;
|
||||
};
|
||||
|
||||
struct away_msg
|
||||
{
|
||||
struct server *server;
|
||||
char nick[NICKLEN];
|
||||
char *message;
|
||||
};
|
||||
|
||||
/* not just for popups, but used for usercommands, ctcp replies,
|
||||
userlist buttons etc */
|
||||
|
||||
struct popup
|
||||
{
|
||||
char *cmd;
|
||||
char *name;
|
||||
};
|
||||
|
||||
/* CL: get a random int in the range [0..n-1]. DON'T use rand() % n, it gives terrible results. */
|
||||
#define RAND_INT(n) ((int)(rand() / (RAND_MAX + 1.0) * (n)))
|
||||
|
||||
#if defined(WIN32) && GLIB_CHECK_VERSION(2,4,0)
|
||||
#define xchat_filename_from_utf8 g_locale_from_utf8
|
||||
#define xchat_filename_to_utf8 g_locale_to_utf8
|
||||
#else
|
||||
#define xchat_filename_from_utf8 g_filename_from_utf8
|
||||
#define xchat_filename_to_utf8 g_filename_to_utf8
|
||||
#endif
|
||||
|
||||
#endif
|
||||
39
src/common/xchatc.h
Normal file
39
src/common/xchatc.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef XCHAT_C_H
|
||||
#define XCHAT_C_H
|
||||
|
||||
extern struct xchatprefs prefs;
|
||||
|
||||
extern int xchat_is_quitting;
|
||||
extern gint arg_skip_plugins; /* command-line args */
|
||||
extern gint arg_dont_autoconnect;
|
||||
extern char *arg_url;
|
||||
extern char *arg_command;
|
||||
extern gint arg_existing;
|
||||
|
||||
extern session *current_sess;
|
||||
extern session *current_tab;
|
||||
|
||||
extern GSList *popup_list;
|
||||
extern GSList *button_list;
|
||||
extern GSList *dlgbutton_list;
|
||||
extern GSList *command_list;
|
||||
extern GSList *ctcp_list;
|
||||
extern GSList *replace_list;
|
||||
extern GSList *sess_list;
|
||||
extern GSList *dcc_list;
|
||||
extern GSList *ignore_list;
|
||||
extern GSList *usermenu_list;
|
||||
extern GSList *urlhandler_list;
|
||||
extern GSList *tabmenu_list;
|
||||
|
||||
session * find_channel (server *serv, char *chan);
|
||||
session * find_dialog (server *serv, char *nick);
|
||||
session * new_ircwindow (server *serv, char *name, int type, int focus);
|
||||
int is_session (session * sess);
|
||||
void session_free (session *killsess);
|
||||
void lag_check (void);
|
||||
void xchat_exit (void);
|
||||
void xchat_exec (const char *cmd);
|
||||
void xchat_execv (char * const argv[]);
|
||||
|
||||
#endif
|
||||
32
src/fe-gtk/Makefile.am
Normal file
32
src/fe-gtk/Makefile.am
Normal file
@@ -0,0 +1,32 @@
|
||||
localedir = $(datadir)/locale
|
||||
|
||||
bin_PROGRAMS = xchat
|
||||
|
||||
INCLUDES = $(GUI_CFLAGS) -DG_DISABLE_CAST_CHECKS -DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
xchat_LDADD = ../common/libxchatcommon.a $(GUI_LIBS)
|
||||
|
||||
EXTRA_DIST = \
|
||||
about.h ascii.h banlist.h chanlist.h chanview.h chanview-tabs.c \
|
||||
chanview-tree.c custom-list.h editlist.h fe-gtk.h fkeys.h gtkutil.h joind.h \
|
||||
maingui.h menu.h mmx_cmod.S mmx_cmod.h notifygui.h palette.h pixmaps.h \
|
||||
plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-spell-entry.h \
|
||||
textgui.h urlgrab.h userlistgui.h xtext.h
|
||||
|
||||
if USE_MMX
|
||||
mmx_cmod_S = mmx_cmod.S
|
||||
endif
|
||||
|
||||
if DO_PLUGIN
|
||||
plugingui_c = plugingui.c
|
||||
endif
|
||||
|
||||
if USE_LIBSEXY
|
||||
sexy_spell_entry_c = sexy-spell-entry.c
|
||||
endif
|
||||
|
||||
xchat_SOURCES = about.c ascii.c banlist.c chanlist.c chanview.c custom-list.c \
|
||||
dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \
|
||||
maingui.c $(mmx_cmod_S) notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
|
||||
rawlog.c search.c servlistgui.c setup.c $(sexy_spell_entry_c) textgui.c \
|
||||
urlgrab.c userlistgui.c xtext.c
|
||||
161
src/fe-gtk/about.c
Normal file
161
src/fe-gtk/about.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
|
||||
#ifdef USE_XLIB
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/util.h"
|
||||
#include "palette.h"
|
||||
#include "pixmaps.h"
|
||||
#include "gtkutil.h"
|
||||
#include "about.h"
|
||||
|
||||
|
||||
#if 0 /*def USE_GNOME*/
|
||||
|
||||
void
|
||||
menu_about (GtkWidget * wid, gpointer sess)
|
||||
{
|
||||
char buf[512];
|
||||
const gchar *author[] = { "Peter Zelezny <zed@xchat.org>", 0 };
|
||||
|
||||
(snprintf) (buf, sizeof (buf),
|
||||
"An IRC Client for UNIX.\n\n"
|
||||
"This binary was compiled on "__DATE__"\n"
|
||||
"Using GTK %d.%d.%d X %d\n"
|
||||
"Running on %s",
|
||||
gtk_major_version, gtk_minor_version, gtk_micro_version,
|
||||
#ifdef USE_XLIB
|
||||
VendorRelease (GDK_DISPLAY ()), get_cpu_str());
|
||||
#else
|
||||
666, get_cpu_str());
|
||||
#endif
|
||||
|
||||
gtk_widget_show (gnome_about_new ("X-Chat", PACKAGE_VERSION,
|
||||
"(C) 1998-2005 Peter Zelezny", author, buf, 0));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static GtkWidget *about = 0;
|
||||
|
||||
static int
|
||||
about_close (void)
|
||||
{
|
||||
about = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
menu_about (GtkWidget * wid, gpointer sess)
|
||||
{
|
||||
GtkWidget *vbox, *label, *hbox;
|
||||
char buf[512];
|
||||
const char *locale = NULL;
|
||||
extern GtkWindow *parent_window; /* maingui.c */
|
||||
|
||||
if (about)
|
||||
{
|
||||
gtk_window_present (GTK_WINDOW (about));
|
||||
return;
|
||||
}
|
||||
|
||||
about = gtk_dialog_new ();
|
||||
gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_resizable (GTK_WINDOW (about), FALSE);
|
||||
gtk_window_set_title (GTK_WINDOW (about), _("About "DISPLAY_NAME));
|
||||
if (parent_window)
|
||||
gtk_window_set_transient_for (GTK_WINDOW (about), parent_window);
|
||||
g_signal_connect (G_OBJECT (about), "destroy",
|
||||
G_CALLBACK (about_close), 0);
|
||||
|
||||
vbox = GTK_DIALOG (about)->vbox;
|
||||
|
||||
wid = gtk_image_new_from_pixbuf (pix_xchat);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), wid);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
g_get_charset (&locale);
|
||||
(snprintf) (buf, sizeof (buf),
|
||||
"<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n\n"
|
||||
"%s\n\n"
|
||||
#ifdef WIN32
|
||||
/* leave this message to avoid time wasting bug reports! */
|
||||
"This version is unofficial and comes with no support.\n\n"
|
||||
#endif
|
||||
"%s\n"
|
||||
"<b>Charset</b>: %s "
|
||||
#ifdef WIN32
|
||||
"<b>GTK+</b>: %i.%i.%i\n"
|
||||
#else
|
||||
"<b>Renderer</b>: %s\n"
|
||||
#endif
|
||||
"<b>Compiled</b>: "__DATE__"\n\n"
|
||||
"<small>\302\251 1998-2010 Peter \305\275elezn\303\275 <zed@xchat.org></small>",
|
||||
_("A multiplatform IRC Client"),
|
||||
get_cpu_str(),
|
||||
locale,
|
||||
#ifdef WIN32
|
||||
gtk_major_version,
|
||||
gtk_minor_version,
|
||||
gtk_micro_version
|
||||
#else
|
||||
#ifdef USE_XFT
|
||||
"Xft"
|
||||
#else
|
||||
"Pango"
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
|
||||
wid = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
|
||||
GTK_WIDGET_SET_FLAGS (GTK_WIDGET (wid), GTK_CAN_DEFAULT);
|
||||
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (about)->action_area), wid, 0, 0, 0);
|
||||
gtk_widget_grab_default (wid);
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (gtkutil_destroy), about);
|
||||
|
||||
gtk_widget_show_all (about);
|
||||
}
|
||||
#endif
|
||||
1
src/fe-gtk/about.h
Normal file
1
src/fe-gtk/about.h
Normal file
@@ -0,0 +1 @@
|
||||
void menu_about (GtkWidget * wid, gpointer sess);
|
||||
177
src/fe-gtk/ascii.c
Normal file
177
src/fe-gtk/ascii.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkeditable.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "ascii.h"
|
||||
#include "maingui.h"
|
||||
|
||||
static const unsigned char table[]=
|
||||
{
|
||||
/* Line 1 */ '\n',
|
||||
0xc2,0xa1,0xc2,0xbf,0xc2,0xa2,0xc2,0xa3,0xe2,0x82,0xac,0xc2,0xa5,0xc2,0xa6,0xc2,
|
||||
0xa7,0xc2,0xa8,0xc2,0xa9,0xc2,0xae,0xc2,0xaa,0xc2,0xab,0xc2,0xbb,0xc2,0xac,0xc2,
|
||||
0xad,0xc2,0xaf,0xe2,0x99,0xaa,
|
||||
/* Line 2 */ '\n',
|
||||
0xc2,0xba,0xc2,0xb9,0xc2,0xb2,0xc2,0xb3,0xc2,0xb4,0xc2,0xb5,0xc3,0x9e,0xc3,0xbe,
|
||||
0xc2,0xb6,0xc2,0xb7,0xc2,0xb8,0xc2,0xb0,0xc2,0xbc,0xc2,0xbd,0xc2,0xbe,0xc3,0x97,
|
||||
0xc2,0xb1,0xc3,0xb7,
|
||||
/* Line 3 */ '\n',
|
||||
0xc3,0x80,0xc3,0x81,0xc3,0x82,0xc3,0x83,0xc3,0x84,0xc3,0x85,0xc3,0x86,0xc4,0x82,
|
||||
0xc4,0x84,0x20,0xc3,0x87,0xc4,0x86,0xc4,0x8c,0xc5,0x92,0x20,0xc4,0x8e,0xc4,0x90,
|
||||
0x20,
|
||||
/* Line 4 */ '\n',
|
||||
0xc3,0xa0,0xc3,0xa1,0xc3,0xa2,0xc3,0xa3,0xc3,0xa4,0xc3,0xa5,0xc3,0xa6,0xc4,0x83,
|
||||
0xc4,0x85,0x20,0xc3,0xa7,0xc4,0x87,0xc4,0x8d,0xc5,0x93,0x20,0xc4,0x8f,0xc4,0x91,
|
||||
0x20,
|
||||
/* Line 5 */ '\n',
|
||||
0xc3,0x88,0xc3,0x89,0xc3,0x8a,0xc3,0x8b,0xc4,0x98,0xc4,0x9a,0x20,0xc4,0x9e,0x20,
|
||||
0xc3,0x8c,0xc3,0x8d,0xc3,0x8e,0xc3,0x8f,0xc4,0xb0,0x20,0xc4,0xb9,0xc4,0xbd,0xc5,
|
||||
0x81,
|
||||
/* Line 6 */ '\n',
|
||||
0xc3,0xa8,0xc3,0xa9,0xc3,0xaa,0xc3,0xab,0xc4,0x99,0xc4,0x9b,0x20,0xc4,0x9f,0x20,
|
||||
0xc3,0xac,0xc3,0xad,0xc3,0xae,0xc3,0xaf,0xc4,0xb1,0x20,0xc4,0xba,0xc4,0xbe,0xc5,
|
||||
0x82,
|
||||
/* Line 7 */ '\n',
|
||||
0xc3,0x91,0xc5,0x83,0xc5,0x87,0x20,0xc3,0x92,0xc3,0x93,0xc3,0x94,0xc3,0x95,0xc3,
|
||||
0x96,0xc3,0x98,0xc5,0x90,0x20,0xc5,0x94,0xc5,0x98,0x20,0xc5,0x9a,0xc5,0x9e,0xc5,
|
||||
0xa0,
|
||||
/* Line 8 */ '\n',
|
||||
0xc3,0xb1,0xc5,0x84,0xc5,0x88,0x20,0xc3,0xb2,0xc3,0xb3,0xc3,0xb4,0xc3,0xb5,0xc3,
|
||||
0xb6,0xc3,0xb8,0xc5,0x91,0x20,0xc5,0x95,0xc5,0x99,0x20,0xc5,0x9b,0xc5,0x9f,0xc5,
|
||||
0xa1,
|
||||
/* Line 9 */ '\n',
|
||||
0x20,0xc5,0xa2,0xc5,0xa4,0x20,0xc3,0x99,0xc3,0x9a,0xc3,0x9b,0xc5,0xb2,0xc3,0x9c,
|
||||
0xc5,0xae,0xc5,0xb0,0x20,0xc3,0x9d,0xc3,0x9f,0x20,0xc5,0xb9,0xc5,0xbb,0xc5,0xbd,
|
||||
/* Line 10 */ '\n',
|
||||
0x20,0xc5,0xa3,0xc5,0xa5,0x20,0xc3,0xb9,0xc3,0xba,0xc3,0xbb,0xc5,0xb3,0xc3,0xbc,
|
||||
0xc5,0xaf,0xc5,0xb1,0x20,0xc3,0xbd,0xc3,0xbf,0x20,0xc5,0xba,0xc5,0xbc,0xc5,0xbe,
|
||||
/* Line 11 */ '\n',
|
||||
0xd0,0x90,0xd0,0x91,0xd0,0x92,0xd0,0x93,0xd0,0x94,0xd0,0x95,0xd0,0x81,0xd0,0x96,
|
||||
0xd0,0x97,0xd0,0x98,0xd0,0x99,0xd0,0x9a,0xd0,0x9b,0xd0,0x9c,0xd0,0x9d,0xd0,0x9e,
|
||||
0xd0,0x9f,0xd0,0xa0,
|
||||
/* Line 12 */ '\n',
|
||||
0xd0,0xb0,0xd0,0xb1,0xd0,0xb2,0xd0,0xb3,0xd0,0xb4,0xd0,0xb5,0xd1,0x91,0xd0,0xb6,
|
||||
0xd0,0xb7,0xd0,0xb8,0xd0,0xb9,0xd0,0xba,0xd0,0xbb,0xd0,0xbc,0xd0,0xbd,0xd0,0xbe,
|
||||
0xd0,0xbf,0xd1,0x80,
|
||||
/* Line 13 */ '\n',
|
||||
0xd0,0xa1,0xd0,0xa2,0xd0,0xa3,0xd0,0xa4,0xd0,0xa5,0xd0,0xa6,0xd0,0xa7,0xd0,0xa8,
|
||||
0xd0,0xa9,0xd0,0xaa,0xd0,0xab,0xd0,0xac,0xd0,0xad,0xd0,0xae,0xd0,0xaf,
|
||||
/* Line 14 */ '\n',
|
||||
0xd1,0x81,0xd1,0x82,0xd1,0x83,0xd1,0x84,0xd1,0x85,0xd1,0x86,0xd1,0x87,0xd1,0x88,
|
||||
0xd1,0x89,0xd1,0x8a,0xd1,0x8b,0xd1,0x8c,0xd1,0x8d,0xd1,0x8e,0xd1,0x8f,0
|
||||
};
|
||||
|
||||
|
||||
static gboolean
|
||||
ascii_enter (GtkWidget * wid, GdkEventCrossing *event, GtkWidget *label)
|
||||
{
|
||||
char buf[64];
|
||||
const char *text;
|
||||
gunichar u;
|
||||
|
||||
text = gtk_button_get_label (GTK_BUTTON (wid));
|
||||
u = g_utf8_get_char (text);
|
||||
sprintf (buf, "%s U+%04X", text, u);
|
||||
gtk_label_set_text (GTK_LABEL (label), buf);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
ascii_click (GtkWidget * wid, gpointer userdata)
|
||||
{
|
||||
int tmp_pos;
|
||||
const char *text;
|
||||
|
||||
if (current_sess)
|
||||
{
|
||||
text = gtk_button_get_label (GTK_BUTTON (wid));
|
||||
wid = current_sess->gui->input_box;
|
||||
tmp_pos = SPELL_ENTRY_GET_POS (wid);
|
||||
SPELL_ENTRY_INSERT (wid, text, -1, &tmp_pos);
|
||||
SPELL_ENTRY_SET_POS (wid, tmp_pos);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ascii_open (void)
|
||||
{
|
||||
int i, len;
|
||||
const unsigned char *table_pos;
|
||||
char name[8];
|
||||
GtkWidget *frame, *label, *but, *hbox = NULL, *vbox, *win;
|
||||
|
||||
win = mg_create_generic_tab ("charmap", _("Character Chart"), TRUE, TRUE,
|
||||
NULL, NULL, 0, 0, &vbox, NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (win), 5);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
|
||||
table_pos = table;
|
||||
i = 0;
|
||||
while (table_pos[0] != 0)
|
||||
{
|
||||
if (table_pos[0] == '\n' || i == 0)
|
||||
{
|
||||
table_pos++;
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
len = g_utf8_skip[table_pos[0]];
|
||||
memcpy (name, table_pos, len);
|
||||
name[len] = 0;
|
||||
|
||||
but = gtk_button_new_with_label (name);
|
||||
gtk_widget_set_size_request (but, 28, -1);
|
||||
g_signal_connect (G_OBJECT (but), "clicked",
|
||||
G_CALLBACK (ascii_click), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
G_CALLBACK (ascii_enter), label);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), but, 0, 0, 0);
|
||||
gtk_widget_show (but);
|
||||
|
||||
table_pos += len;
|
||||
}
|
||||
|
||||
frame = gtk_frame_new ("");
|
||||
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
||||
gtk_container_add (GTK_CONTAINER (frame), label);
|
||||
gtk_widget_show (label);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
1
src/fe-gtk/ascii.h
Normal file
1
src/fe-gtk/ascii.h
Normal file
@@ -0,0 +1 @@
|
||||
void ascii_open (void);
|
||||
418
src/fe-gtk/banlist.c
Normal file
418
src/fe-gtk/banlist.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/modes.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "banlist.h"
|
||||
|
||||
/* model for the banlist tree */
|
||||
enum
|
||||
{
|
||||
MASK_COLUMN,
|
||||
FROM_COLUMN,
|
||||
DATE_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkTreeView *
|
||||
get_view (struct session *sess)
|
||||
{
|
||||
return GTK_TREE_VIEW (sess->res->banlist_treeview);
|
||||
}
|
||||
|
||||
static GtkListStore *
|
||||
get_store (struct session *sess)
|
||||
{
|
||||
return GTK_LIST_STORE (gtk_tree_view_get_model (get_view (sess)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
supports_exempt (server *serv)
|
||||
{
|
||||
char *cm = serv->chanmodes;
|
||||
|
||||
if (serv->have_except)
|
||||
return TRUE;
|
||||
|
||||
if (!cm)
|
||||
return FALSE;
|
||||
|
||||
while (*cm)
|
||||
{
|
||||
if (*cm == ',')
|
||||
break;
|
||||
if (*cm == 'e')
|
||||
return TRUE;
|
||||
cm++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
fe_add_ban_list (struct session *sess, char *mask, char *who, char *when, int is_exempt)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
char buf[512];
|
||||
|
||||
store = get_store (sess);
|
||||
gtk_list_store_append (store, &iter);
|
||||
|
||||
if (is_exempt)
|
||||
{
|
||||
snprintf (buf, sizeof (buf), "(EX) %s", mask);
|
||||
gtk_list_store_set (store, &iter, 0, buf, 1, who, 2, when, -1);
|
||||
} else
|
||||
{
|
||||
gtk_list_store_set (store, &iter, 0, mask, 1, who, 2, when, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_ban_list_end (struct session *sess, int is_exemption)
|
||||
{
|
||||
gtk_widget_set_sensitive (sess->res->banlist_butRefresh, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* * Performs the actual refresh operations.
|
||||
* */
|
||||
static void
|
||||
banlist_do_refresh (struct session *sess)
|
||||
{
|
||||
char tbuf[256];
|
||||
if (sess->server->connected)
|
||||
{
|
||||
GtkListStore *store;
|
||||
|
||||
gtk_widget_set_sensitive (sess->res->banlist_butRefresh, FALSE);
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, "XChat: Ban List (%s, %s)",
|
||||
sess->channel, sess->server->servername);
|
||||
mg_set_title (sess->res->banlist_window, tbuf);
|
||||
|
||||
store = get_store (sess);
|
||||
gtk_list_store_clear (store);
|
||||
|
||||
handle_command (sess, "ban", FALSE);
|
||||
#ifdef WIN32
|
||||
if (0)
|
||||
#else
|
||||
if (supports_exempt (sess->server))
|
||||
#endif
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), "quote mode %s +e", sess->channel);
|
||||
handle_command (sess, tbuf, FALSE);
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
fe_message (_("Not connected."), FE_MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_refresh (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
/* JG NOTE: Didn't see actual use of wid here, so just forwarding
|
||||
* * this to chanlist_do_refresh because I use it without any widget
|
||||
* * param in chanlist_build_gui_list when the user presses enter
|
||||
* * or apply for the first time if the list has not yet been
|
||||
* * received.
|
||||
* */
|
||||
banlist_do_refresh (sess);
|
||||
}
|
||||
|
||||
static int
|
||||
banlist_unban_inner (gpointer none, struct session *sess, int do_exempts)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreeIter iter;
|
||||
char tbuf[2048];
|
||||
char **masks, *tmp, *space;
|
||||
int num_sel, i;
|
||||
|
||||
/* grab the list of selected items */
|
||||
model = GTK_TREE_MODEL (get_store (sess));
|
||||
sel = gtk_tree_view_get_selection (get_view (sess));
|
||||
num_sel = 0;
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (sel, &iter))
|
||||
num_sel++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
if (num_sel < 1)
|
||||
return 0;
|
||||
|
||||
/* create an array of all the masks */
|
||||
masks = calloc (1, num_sel * sizeof (char *));
|
||||
|
||||
i = 0;
|
||||
gtk_tree_model_get_iter_first (model, &iter);
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (sel, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, MASK_COLUMN, &masks[i], -1);
|
||||
space = strchr (masks[i], ' ');
|
||||
|
||||
if (do_exempts)
|
||||
{
|
||||
if (space)
|
||||
{
|
||||
/* remove the "(EX) " */
|
||||
tmp = masks[i];
|
||||
masks[i] = g_strdup (space + 1);
|
||||
g_free (tmp);
|
||||
i++;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (!space)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
|
||||
/* and send to server */
|
||||
if (do_exempts)
|
||||
send_channel_modes (sess, tbuf, masks, 0, i, '-', 'e', 0);
|
||||
else
|
||||
send_channel_modes (sess, tbuf, masks, 0, i, '-', 'b', 0);
|
||||
|
||||
/* now free everything, and refresh banlist */
|
||||
for (i=0; i < num_sel; i++)
|
||||
g_free (masks[i]);
|
||||
free (masks);
|
||||
|
||||
return num_sel;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_unban (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
num += banlist_unban_inner (wid, sess, FALSE);
|
||||
num += banlist_unban_inner (wid, sess, TRUE);
|
||||
|
||||
if (num < 1)
|
||||
{
|
||||
fe_message (_("You must select some bans."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
banlist_do_refresh (sess);
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_clear_cb (GtkDialog *dialog, gint response, gpointer sess)
|
||||
{
|
||||
GtkTreeSelection *sel;
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
|
||||
if (response == GTK_RESPONSE_OK)
|
||||
{
|
||||
sel = gtk_tree_view_get_selection (get_view (sess));
|
||||
gtk_tree_selection_select_all (sel);
|
||||
banlist_unban (NULL, sess);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_clear (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (NULL, 0,
|
||||
GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
|
||||
_("Are you sure you want to remove all bans in %s?"), sess->channel);
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (banlist_clear_cb), sess);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_add_selected_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
GSList **lp = data;
|
||||
GSList *list = NULL;
|
||||
GtkTreeIter *copy;
|
||||
|
||||
if (!lp) return;
|
||||
list = *lp;
|
||||
copy = g_malloc (sizeof (GtkTreeIter));
|
||||
g_return_if_fail (copy != NULL);
|
||||
*copy = *iter;
|
||||
|
||||
list = g_slist_append (list, copy);
|
||||
*(GSList **)data = list;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_crop (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkTreeSelection *select;
|
||||
GSList *list = NULL, *node;
|
||||
int num_sel;
|
||||
|
||||
/* remember which bans are selected */
|
||||
select = gtk_tree_view_get_selection (get_view (sess));
|
||||
/* gtk_tree_selected_get_selected_rows() isn't present in gtk 2.0.x */
|
||||
gtk_tree_selection_selected_foreach (select, banlist_add_selected_cb,
|
||||
&list);
|
||||
|
||||
num_sel = g_slist_length (list);
|
||||
/* select all, then unselect those that we remembered */
|
||||
if (num_sel)
|
||||
{
|
||||
gtk_tree_selection_select_all (select);
|
||||
|
||||
for (node = list; node; node = node->next)
|
||||
gtk_tree_selection_unselect_iter (select, node->data);
|
||||
|
||||
g_slist_foreach (list, (GFunc)g_free, NULL);
|
||||
g_slist_free (list);
|
||||
|
||||
banlist_unban (NULL, sess);
|
||||
} else
|
||||
fe_message (_("You must select some bans."), FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
banlist_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeSelection *select;
|
||||
GtkTreeViewColumn *col;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store), NULL,
|
||||
MASK_COLUMN, _("Mask"),
|
||||
FROM_COLUMN, _("From"),
|
||||
DATE_COLUMN, _("Date"), -1);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), MASK_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
gtk_tree_view_column_set_min_width (col, 300);
|
||||
gtk_tree_view_column_set_sort_column_id (col, MASK_COLUMN);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), FROM_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
gtk_tree_view_column_set_sort_column_id (col, FROM_COLUMN);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), DATE_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
|
||||
select = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
||||
gtk_tree_selection_set_mode (select, GTK_SELECTION_MULTIPLE);
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_closegui (GtkWidget *wid, session *sess)
|
||||
{
|
||||
if (is_session (sess))
|
||||
sess->res->banlist_window = 0;
|
||||
}
|
||||
|
||||
void
|
||||
banlist_opengui (struct session *sess)
|
||||
{
|
||||
GtkWidget *vbox1;
|
||||
GtkWidget *bbox;
|
||||
char tbuf[256];
|
||||
|
||||
if (sess->res->banlist_window)
|
||||
{
|
||||
mg_bring_tofront (sess->res->banlist_window);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sess->type != SESS_CHANNEL)
|
||||
{
|
||||
fe_message (_("You can only open the Ban List window while in a channel tab."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, _("XChat: Ban List (%s)"),
|
||||
sess->server->servername);
|
||||
|
||||
sess->res->banlist_window = mg_create_generic_tab ("BanList", tbuf, FALSE,
|
||||
TRUE, banlist_closegui, sess, 550, 200, &vbox1, sess->server);
|
||||
|
||||
/* create banlist view */
|
||||
sess->res->banlist_treeview = banlist_treeview_new (vbox1);
|
||||
|
||||
bbox = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
|
||||
gtk_box_pack_end (GTK_BOX (vbox1), bbox, 0, 0, 0);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
gtkutil_button (bbox, GTK_STOCK_REMOVE, 0, banlist_unban, sess,
|
||||
_("Remove"));
|
||||
gtkutil_button (bbox, GTK_STOCK_REMOVE, 0, banlist_crop, sess,
|
||||
_("Crop"));
|
||||
gtkutil_button (bbox, GTK_STOCK_CLEAR, 0, banlist_clear, sess,
|
||||
_("Clear"));
|
||||
|
||||
sess->res->banlist_butRefresh = gtkutil_button (bbox, GTK_STOCK_REFRESH, 0, banlist_refresh, sess, _("Refresh"));
|
||||
|
||||
banlist_do_refresh (sess);
|
||||
|
||||
gtk_widget_show (sess->res->banlist_window);
|
||||
}
|
||||
1
src/fe-gtk/banlist.h
Normal file
1
src/fe-gtk/banlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void banlist_opengui (session *sess);
|
||||
943
src/fe-gtk/chanlist.c
Normal file
943
src/fe-gtk/chanlist.c
Normal file
@@ -0,0 +1,943 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkalignment.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcheckbutton.h>
|
||||
#include <gtk/gtkcombobox.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
#include <gtk/gtkspinbutton.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtktable.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkvseparator.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/server.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
|
||||
|
||||
#include "custom-list.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
COL_CHANNEL,
|
||||
COL_USERS,
|
||||
COL_TOPIC,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
#ifndef CUSTOM_LIST
|
||||
typedef struct /* this is now in custom-list.h */
|
||||
{
|
||||
char *topic;
|
||||
char *collation_key;
|
||||
guint32 pos;
|
||||
guint32 users;
|
||||
/* channel string lives beyond "users" */
|
||||
#define GET_CHAN(row) (((char *)row)+sizeof(chanlistrow))
|
||||
}
|
||||
chanlistrow;
|
||||
#endif
|
||||
|
||||
#define GET_MODEL(xserv) (gtk_tree_view_get_model(GTK_TREE_VIEW(xserv->gui->chanlist_list)))
|
||||
|
||||
|
||||
static gboolean
|
||||
chanlist_match (server *serv, const char *str)
|
||||
{
|
||||
switch (serv->gui->chanlist_search_type)
|
||||
{
|
||||
case 1:
|
||||
return match (GTK_ENTRY (serv->gui->chanlist_wild)->text, str);
|
||||
#ifndef WIN32
|
||||
case 2:
|
||||
if (!serv->gui->have_regex)
|
||||
return 0;
|
||||
/* regex returns 0 if it's a match: */
|
||||
return !regexec (&serv->gui->chanlist_match_regex, str, 1, NULL, REG_NOTBOL);
|
||||
#endif
|
||||
default: /* case 0: */
|
||||
return nocasestrstr (str, GTK_ENTRY (serv->gui->chanlist_wild)->text) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the caption to reflect the number of users and channels
|
||||
*/
|
||||
static void
|
||||
chanlist_update_caption (server *serv)
|
||||
{
|
||||
gchar tbuf[256];
|
||||
|
||||
snprintf (tbuf, sizeof tbuf,
|
||||
_("Displaying %d/%d users on %d/%d channels."),
|
||||
serv->gui->chanlist_users_shown_count,
|
||||
serv->gui->chanlist_users_found_count,
|
||||
serv->gui->chanlist_channels_shown_count,
|
||||
serv->gui->chanlist_channels_found_count);
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (serv->gui->chanlist_label), tbuf);
|
||||
serv->gui->chanlist_caption_is_stale = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_update_buttons (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_channels_shown_count)
|
||||
{
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_join, TRUE);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_savelist, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_join, FALSE);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_savelist, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_reset_counters (server *serv)
|
||||
{
|
||||
serv->gui->chanlist_users_found_count = 0;
|
||||
serv->gui->chanlist_users_shown_count = 0;
|
||||
serv->gui->chanlist_channels_found_count = 0;
|
||||
serv->gui->chanlist_channels_shown_count = 0;
|
||||
|
||||
chanlist_update_caption (serv);
|
||||
chanlist_update_buttons (serv);
|
||||
}
|
||||
|
||||
/* free up our entire linked list and all the nodes */
|
||||
|
||||
static void
|
||||
chanlist_data_free (server *serv)
|
||||
{
|
||||
GSList *rows;
|
||||
chanlistrow *data;
|
||||
|
||||
if (serv->gui->chanlist_data_stored_rows)
|
||||
{
|
||||
for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
|
||||
rows = rows->next)
|
||||
{
|
||||
data = rows->data;
|
||||
g_free (data->topic);
|
||||
g_free (data->collation_key);
|
||||
free (data);
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_data_stored_rows);
|
||||
serv->gui->chanlist_data_stored_rows = NULL;
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
}
|
||||
|
||||
/* add any rows we received from the server in the last 0.25s to the GUI */
|
||||
|
||||
static void
|
||||
chanlist_flush_pending (server *serv)
|
||||
{
|
||||
GSList *list = serv->gui->chanlist_pending_rows;
|
||||
GtkTreeModel *model;
|
||||
chanlistrow *row;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
if (serv->gui->chanlist_caption_is_stale)
|
||||
chanlist_update_caption (serv);
|
||||
return;
|
||||
}
|
||||
model = GET_MODEL (serv);
|
||||
|
||||
while (list)
|
||||
{
|
||||
row = list->data;
|
||||
custom_list_append (CUSTOM_LIST (model), row);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
chanlist_update_caption (serv);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_timeout (server *serv)
|
||||
{
|
||||
chanlist_flush_pending (serv);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a data row into the gui GtkTreeView, if and only if the row matches
|
||||
* the user and regex/search requirements.
|
||||
*/
|
||||
static void
|
||||
chanlist_place_row_in_gui (server *serv, chanlistrow *next_row, gboolean force)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
|
||||
/* First, update the 'found' counter values */
|
||||
serv->gui->chanlist_users_found_count += next_row->users;
|
||||
serv->gui->chanlist_channels_found_count++;
|
||||
|
||||
if (serv->gui->chanlist_channels_shown_count == 1)
|
||||
/* join & save buttons become live */
|
||||
chanlist_update_buttons (serv);
|
||||
|
||||
if (next_row->users < serv->gui->chanlist_minusers)
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_row->users > serv->gui->chanlist_maxusers
|
||||
&& serv->gui->chanlist_maxusers > 0)
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GTK_ENTRY (serv->gui->chanlist_wild)->text[0])
|
||||
{
|
||||
/* Check what the user wants to match. If both buttons or _neither_
|
||||
* button is checked, look for match in both by default.
|
||||
*/
|
||||
if (serv->gui->chanlist_match_wants_channel ==
|
||||
serv->gui->chanlist_match_wants_topic)
|
||||
{
|
||||
if (!chanlist_match (serv, GET_CHAN (next_row))
|
||||
&& !chanlist_match (serv, next_row->topic))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (serv->gui->chanlist_match_wants_channel)
|
||||
{
|
||||
if (!chanlist_match (serv, GET_CHAN (next_row)))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (serv->gui->chanlist_match_wants_topic)
|
||||
{
|
||||
if (!chanlist_match (serv, next_row->topic))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (force || serv->gui->chanlist_channels_shown_count < 20)
|
||||
{
|
||||
model = GET_MODEL (serv);
|
||||
/* makes it appear fast :) */
|
||||
custom_list_append (CUSTOM_LIST (model), next_row);
|
||||
chanlist_update_caption (serv);
|
||||
}
|
||||
else
|
||||
/* add it to GUI at the next update interval */
|
||||
serv->gui->chanlist_pending_rows = g_slist_prepend (serv->gui->chanlist_pending_rows, next_row);
|
||||
|
||||
/* Update the 'shown' counter values */
|
||||
serv->gui->chanlist_users_shown_count += next_row->users;
|
||||
serv->gui->chanlist_channels_shown_count++;
|
||||
}
|
||||
|
||||
/* Performs the LIST download from the IRC server. */
|
||||
|
||||
static void
|
||||
chanlist_do_refresh (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
|
||||
if (!serv->connected)
|
||||
{
|
||||
fe_message (_("Not connected."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_refresh, FALSE);
|
||||
|
||||
chanlist_data_free (serv);
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
/* can we request a list with minusers arg? */
|
||||
if (serv->use_listargs)
|
||||
{
|
||||
/* yes - it will download faster */
|
||||
serv->p_list_channels (serv, "", serv->gui->chanlist_minusers);
|
||||
/* don't allow the spin button below this value from now on */
|
||||
serv->gui->chanlist_minusers_downloaded = serv->gui->chanlist_minusers;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* download all, filter minusers locally only */
|
||||
serv->p_list_channels (serv, "", 1);
|
||||
serv->gui->chanlist_minusers_downloaded = 1;
|
||||
}
|
||||
|
||||
/* gtk_spin_button_set_range ((GtkSpinButton *)serv->gui->chanlist_min_spin,
|
||||
serv->gui->chanlist_minusers_downloaded, 999999);*/
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_refresh (GtkWidget * wid, server *serv)
|
||||
{
|
||||
chanlist_do_refresh (serv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the gui GtkTreeView with stored items from the GSList.
|
||||
*/
|
||||
static void
|
||||
chanlist_build_gui_list (server *serv)
|
||||
{
|
||||
GSList *rows;
|
||||
|
||||
/* first check if the list is present */
|
||||
if (serv->gui->chanlist_data_stored_rows == NULL)
|
||||
{
|
||||
/* start a download */
|
||||
chanlist_do_refresh (serv);
|
||||
return;
|
||||
}
|
||||
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
|
||||
/* discard pending rows FIXME: free the structs? */
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
|
||||
/* Reset the counters */
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
/* Refill the list */
|
||||
for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
|
||||
rows = rows->next)
|
||||
{
|
||||
chanlist_place_row_in_gui (serv, rows->data, TRUE);
|
||||
}
|
||||
|
||||
custom_list_resort ((CustomList *)GET_MODEL (serv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts incoming channel data from inbound.c, allocates new space for a
|
||||
* chanlistrow, adds it to our linked list and calls chanlist_place_row_in_gui.
|
||||
*/
|
||||
void
|
||||
fe_add_chan_list (server *serv, char *chan, char *users, char *topic)
|
||||
{
|
||||
chanlistrow *next_row;
|
||||
int len = strlen (chan) + 1;
|
||||
|
||||
/* we allocate the struct and channel string in one go */
|
||||
next_row = malloc (sizeof (chanlistrow) + len);
|
||||
memcpy (((char *)next_row) + sizeof (chanlistrow), chan, len);
|
||||
next_row->topic = strip_color (topic, -1, STRIP_ALL);
|
||||
next_row->collation_key = g_utf8_collate_key (chan, len-1);
|
||||
if (!(next_row->collation_key))
|
||||
next_row->collation_key = g_strdup (chan);
|
||||
next_row->users = atoi (users);
|
||||
|
||||
/* add this row to the data */
|
||||
serv->gui->chanlist_data_stored_rows =
|
||||
g_slist_prepend (serv->gui->chanlist_data_stored_rows, next_row);
|
||||
|
||||
/* _possibly_ add the row to the gui */
|
||||
chanlist_place_row_in_gui (serv, next_row, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
fe_chan_list_end (server *serv)
|
||||
{
|
||||
/* download complete */
|
||||
chanlist_flush_pending (serv);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_refresh, TRUE);
|
||||
custom_list_resort ((CustomList *)GET_MODEL (serv));
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_search_pressed (GtkButton * button, server *serv)
|
||||
{
|
||||
chanlist_build_gui_list (serv);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_find_cb (GtkWidget * wid, server *serv)
|
||||
{
|
||||
#ifndef WIN32
|
||||
/* recompile the regular expression. */
|
||||
if (serv->gui->have_regex)
|
||||
{
|
||||
serv->gui->have_regex = 0;
|
||||
regfree (&serv->gui->chanlist_match_regex);
|
||||
}
|
||||
|
||||
if (regcomp (&serv->gui->chanlist_match_regex, GTK_ENTRY (wid)->text,
|
||||
REG_ICASE | REG_EXTENDED | REG_NOSUB) == 0)
|
||||
serv->gui->have_regex = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_match_channel_button_toggled (GtkWidget * wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_match_wants_channel = GTK_TOGGLE_BUTTON (wid)->active;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_match_wants_topic = GTK_TOGGLE_BUTTON (wid)->active;
|
||||
}
|
||||
|
||||
static char *
|
||||
chanlist_get_selected (server *serv, gboolean get_topic)
|
||||
{
|
||||
char *chan;
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list));
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_selection_get_selected (sel, &model, &iter))
|
||||
return NULL;
|
||||
|
||||
gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1);
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_join (GtkWidget * wid, server *serv)
|
||||
{
|
||||
char tbuf[CHANLEN + 6];
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
{
|
||||
if (serv->connected && (strcmp (chan, "*") != 0))
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), "join %s", chan);
|
||||
handle_command (serv->server_session, tbuf, FALSE);
|
||||
} else
|
||||
gdk_beep ();
|
||||
g_free (chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_filereq_done (server *serv, char *file)
|
||||
{
|
||||
time_t t = time (0);
|
||||
int fh, users;
|
||||
char *chan, *topic;
|
||||
char buf[1024];
|
||||
GtkTreeModel *model = GET_MODEL (serv);
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
fh = xchat_open_file (file, O_TRUNC | O_WRONLY | O_CREAT, 0600,
|
||||
XOF_DOMODE | XOF_FULLPATH);
|
||||
if (fh == -1)
|
||||
return;
|
||||
|
||||
snprintf (buf, sizeof buf, "XChat Channel List: %s - %s\n",
|
||||
serv->servername, ctime (&t));
|
||||
write (fh, buf, strlen (buf));
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (model, &iter,
|
||||
COL_CHANNEL, &chan,
|
||||
COL_USERS, &users,
|
||||
COL_TOPIC, &topic, -1);
|
||||
snprintf (buf, sizeof buf, "%-16s %-5d%s\n", chan, users, topic);
|
||||
g_free (chan);
|
||||
g_free (topic);
|
||||
write (fh, buf, strlen (buf));
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
close (fh);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_save (GtkWidget * wid, server *serv)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model = GET_MODEL (serv);
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
gtkutil_file_req (_("Select an output filename"), chanlist_filereq_done,
|
||||
serv, NULL, FRF_WRITE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_flash (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_refresh->state != GTK_STATE_ACTIVE)
|
||||
gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_ACTIVE);
|
||||
else
|
||||
gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_PRELIGHT);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_minusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_minusers = gtk_spin_button_get_value_as_int (wid);
|
||||
|
||||
if (serv->gui->chanlist_minusers < serv->gui->chanlist_minusers_downloaded)
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag == 0)
|
||||
serv->gui->chanlist_flash_tag = g_timeout_add (500, (GSourceFunc)chanlist_flash, serv);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_maxusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_maxusers = gtk_spin_button_get_value_as_int (wid);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_dclick_cb (GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *column, gpointer data)
|
||||
{
|
||||
chanlist_join (0, (server *) data); /* double clicked a row */
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_menu_destroy (GtkWidget *menu, gpointer userdata)
|
||||
{
|
||||
gtk_widget_destroy (menu);
|
||||
g_object_unref (menu);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_copychannel (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, chan);
|
||||
g_free (chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_copytopic (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *topic = chanlist_get_selected (serv, TRUE);
|
||||
if (topic)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, topic);
|
||||
g_free (topic);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv)
|
||||
{
|
||||
GtkWidget *menu;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreePath *path;
|
||||
char *chan;
|
||||
|
||||
if (event->button != 3)
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
|
||||
return FALSE;
|
||||
|
||||
/* select what they right-clicked on */
|
||||
sel = gtk_tree_view_get_selection (tree);
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
if (event->window)
|
||||
gtk_menu_set_screen (GTK_MENU (menu), gdk_drawable_get_screen (event->window));
|
||||
g_object_ref (menu);
|
||||
g_object_ref_sink (menu);
|
||||
g_object_unref (menu);
|
||||
g_signal_connect (G_OBJECT (menu), "selection-done",
|
||||
G_CALLBACK (chanlist_menu_destroy), NULL);
|
||||
mg_create_icon_item (_("_Join Channel"), GTK_STOCK_JUMP_TO, menu,
|
||||
chanlist_join, serv);
|
||||
mg_create_icon_item (_("_Copy Channel Name"), GTK_STOCK_COPY, menu,
|
||||
chanlist_copychannel, serv);
|
||||
mg_create_icon_item (_("Copy _Topic Text"), GTK_STOCK_COPY, menu,
|
||||
chanlist_copytopic, serv);
|
||||
|
||||
chan = chanlist_get_selected (serv, FALSE);
|
||||
menu_addfavoritemenu (serv, menu, chan);
|
||||
g_free (chan);
|
||||
|
||||
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_destroy_widget (GtkWidget *wid, server *serv)
|
||||
{
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
chanlist_data_free (serv);
|
||||
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
|
||||
if (serv->gui->chanlist_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_tag);
|
||||
serv->gui->chanlist_tag = 0;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if (serv->gui->have_regex)
|
||||
{
|
||||
regfree (&serv->gui->chanlist_match_regex);
|
||||
serv->gui->have_regex = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_closegui (GtkWidget *wid, server *serv)
|
||||
{
|
||||
if (is_server (serv))
|
||||
serv->gui->chanlist_window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_add_column (GtkWidget *tree, int textcol, int size, char *title, gboolean right_justified)
|
||||
{
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *col;
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (right_justified)
|
||||
g_object_set (G_OBJECT (renderer), "xalign", (gfloat) 1.0, NULL);
|
||||
g_object_set (G_OBJECT (renderer), "ypad", (gint) 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title,
|
||||
renderer, "text", textcol, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), textcol);
|
||||
gtk_tree_view_column_set_sort_column_id (col, textcol);
|
||||
gtk_tree_view_column_set_resizable (col, TRUE);
|
||||
if (textcol != COL_TOPIC)
|
||||
{
|
||||
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_fixed_width (col, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_combo_cb (GtkWidget *combo, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_search_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
|
||||
}
|
||||
|
||||
void
|
||||
chanlist_opengui (server *serv, int do_refresh)
|
||||
{
|
||||
GtkWidget *vbox, *hbox, *table, *wid, *view;
|
||||
char tbuf[256];
|
||||
GtkListStore *store;
|
||||
|
||||
if (serv->gui->chanlist_window)
|
||||
{
|
||||
mg_bring_tofront (serv->gui->chanlist_window);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, _("XChat: Channel List (%s)"),
|
||||
server_get_network (serv, TRUE));
|
||||
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
serv->gui->chanlist_tag = 0;
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
serv->gui->chanlist_data_stored_rows = NULL;
|
||||
|
||||
if (!serv->gui->chanlist_minusers)
|
||||
serv->gui->chanlist_minusers = 5;
|
||||
|
||||
if (!serv->gui->chanlist_maxusers)
|
||||
serv->gui->chanlist_maxusers = 9999;
|
||||
|
||||
serv->gui->chanlist_window =
|
||||
mg_create_generic_tab ("ChanList", tbuf, FALSE, TRUE, chanlist_closegui,
|
||||
serv, 640, 480, &vbox, serv);
|
||||
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
|
||||
gtk_box_set_spacing (GTK_BOX (vbox), 12);
|
||||
|
||||
/* make a label to store the user/channel info */
|
||||
wid = gtk_label_new (NULL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_label = wid;
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
store = (GtkListStore *) custom_list_new();
|
||||
view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view->parent),
|
||||
GTK_SHADOW_IN);
|
||||
serv->gui->chanlist_list = view;
|
||||
|
||||
g_signal_connect (G_OBJECT (view), "row_activated",
|
||||
G_CALLBACK (chanlist_dclick_cb), serv);
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (chanlist_button_cb), serv);
|
||||
|
||||
chanlist_add_column (view, COL_CHANNEL, 96, _("Channel"), FALSE);
|
||||
chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
|
||||
chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE);
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
|
||||
/* this is a speed up, but no horizontal scrollbar :( */
|
||||
/*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/
|
||||
gtk_widget_show (view);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
table = gtk_table_new (4, 4, FALSE);
|
||||
gtk_table_set_col_spacings (GTK_TABLE (table), 12);
|
||||
gtk_table_set_row_spacings (GTK_TABLE (table), 3);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), table, 0, 1, 0);
|
||||
gtk_widget_show (table);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_FIND, 0, chanlist_search_pressed, serv,
|
||||
_("_Search"));
|
||||
serv->gui->chanlist_search = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 3, 4,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_REFRESH, 0, chanlist_refresh, serv,
|
||||
_("_Download List"));
|
||||
serv->gui->chanlist_refresh = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 2, 3,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_SAVE_AS, 0, chanlist_save, serv,
|
||||
_("Save _List..."));
|
||||
serv->gui->chanlist_savelist = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_JUMP_TO, 0, chanlist_join, serv,
|
||||
_("_Join Channel"));
|
||||
serv->gui->chanlist_join = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 0, 1,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Show only:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 3, 4,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 9);
|
||||
gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 3, 4,
|
||||
GTK_FILL, GTK_FILL, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
wid = gtk_label_new (_("channels with"));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_minusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
G_CALLBACK (chanlist_minusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_min_spin = wid;
|
||||
|
||||
wid = gtk_label_new (_("to"));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_maxusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
G_CALLBACK (chanlist_maxusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_label_new (_("users."));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Look in:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 2, 3,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 12);
|
||||
gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 3,
|
||||
GTK_FILL, GTK_FILL, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
wid = gtk_check_button_new_with_label (_("Channel name"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "toggled",
|
||||
GTK_SIGNAL_FUNC
|
||||
(chanlist_match_channel_button_toggled), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_check_button_new_with_label (_("Topic"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "toggled",
|
||||
GTK_SIGNAL_FUNC (chanlist_match_topic_button_toggled),
|
||||
serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
serv->gui->chanlist_match_wants_channel = 1;
|
||||
serv->gui->chanlist_match_wants_topic = 1;
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Search type:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_combo_box_new_text ();
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Simple Search"));
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Pattern Match (Wildcards)"));
|
||||
#ifndef WIN32
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Regular Expression"));
|
||||
#endif
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (wid), serv->gui->chanlist_search_type);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
g_signal_connect (G_OBJECT (wid), "changed",
|
||||
G_CALLBACK (chanlist_combo_cb), serv);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Find:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 0, 1,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_entry_new_with_max_length (255);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "changed",
|
||||
GTK_SIGNAL_FUNC (chanlist_find_cb), serv);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "activate",
|
||||
GTK_SIGNAL_FUNC (chanlist_search_pressed),
|
||||
(gpointer) serv);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 0, 1,
|
||||
GTK_EXPAND | GTK_FILL, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_wild = wid;
|
||||
|
||||
chanlist_find_cb (wid, serv);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_vseparator_new ();
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 2, 3, 0, 5,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
g_signal_connect (G_OBJECT (serv->gui->chanlist_window), "destroy",
|
||||
G_CALLBACK (chanlist_destroy_widget), serv);
|
||||
|
||||
/* reset the counters. */
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
serv->gui->chanlist_tag = g_timeout_add (250, (GSourceFunc)chanlist_timeout, serv);
|
||||
|
||||
if (do_refresh)
|
||||
chanlist_do_refresh (serv);
|
||||
|
||||
chanlist_update_buttons (serv);
|
||||
gtk_widget_show (serv->gui->chanlist_window);
|
||||
gtk_widget_grab_focus (serv->gui->chanlist_refresh);
|
||||
}
|
||||
1
src/fe-gtk/chanlist.h
Normal file
1
src/fe-gtk/chanlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void chanlist_opengui (server *serv, int do_refresh);
|
||||
779
src/fe-gtk/chanview-tabs.c
Normal file
779
src/fe-gtk/chanview-tabs.c
Normal file
@@ -0,0 +1,779 @@
|
||||
/* file included in chanview.c */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *outer; /* outer box */
|
||||
GtkWidget *inner; /* inner box */
|
||||
GtkWidget *b1; /* button1 */
|
||||
GtkWidget *b2; /* button2 */
|
||||
} tabview;
|
||||
|
||||
static void chanview_populate (chanview *cv);
|
||||
|
||||
/* ignore "toggled" signal? */
|
||||
static int ignore_toggle = FALSE;
|
||||
static int tab_left_is_moving = 0;
|
||||
static int tab_right_is_moving = 0;
|
||||
|
||||
/* userdata for gobjects used here:
|
||||
*
|
||||
* tab (togglebuttons inside boxes):
|
||||
* "u" userdata passed to tab-focus callback function (sess)
|
||||
* "c" the tab's (chan *)
|
||||
*
|
||||
* box (family box)
|
||||
* "f" family
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* GtkViewports request at least as much space as their children do.
|
||||
* If we don't intervene here, the GtkViewport will be granted its
|
||||
* request, even at the expense of resizing the top-level window.
|
||||
*/
|
||||
static void
|
||||
cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview *cv)
|
||||
{
|
||||
if (!cv->vertical)
|
||||
requisition->width = 1;
|
||||
else
|
||||
requisition->height = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
GtkWidget *inner;
|
||||
gint viewport_size;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
if (adj->upper <= viewport_size)
|
||||
{
|
||||
gtk_widget_hide (((tabview *)cv)->b1);
|
||||
gtk_widget_hide (((tabview *)cv)->b2);
|
||||
} else
|
||||
{
|
||||
gtk_widget_show (((tabview *)cv)->b1);
|
||||
gtk_widget_show (((tabview *)cv)->b2);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
tab_search_offset (GtkWidget *inner, gint start_offset,
|
||||
gboolean forward, gboolean vertical)
|
||||
{
|
||||
GList *boxes;
|
||||
GList *tabs;
|
||||
GtkWidget *box;
|
||||
GtkWidget *button;
|
||||
gint found;
|
||||
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
if (!forward && boxes)
|
||||
boxes = g_list_last (boxes);
|
||||
|
||||
while (boxes)
|
||||
{
|
||||
box = ((GtkBoxChild *)boxes->data)->widget;
|
||||
boxes = (forward ? boxes->next : boxes->prev);
|
||||
|
||||
tabs = GTK_BOX (box)->children;
|
||||
if (!forward && tabs)
|
||||
tabs = g_list_last (tabs);
|
||||
|
||||
while (tabs)
|
||||
{
|
||||
button = ((GtkBoxChild *)tabs->data)->widget;
|
||||
tabs = (forward ? tabs->next : tabs->prev);
|
||||
|
||||
if (!GTK_IS_TOGGLE_BUTTON (button))
|
||||
continue;
|
||||
|
||||
found = (vertical ? button->allocation.y : button->allocation.x);
|
||||
if ((forward && found > start_offset) ||
|
||||
(!forward && found < start_offset))
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
gint viewport_size;
|
||||
gfloat new_value;
|
||||
GtkWidget *inner;
|
||||
gfloat i;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
new_value = tab_search_offset (inner, adj->value, 0, cv->vertical);
|
||||
|
||||
if (new_value + viewport_size > adj->upper)
|
||||
new_value = adj->upper - viewport_size;
|
||||
|
||||
if (!tab_left_is_moving)
|
||||
{
|
||||
tab_left_is_moving = 1;
|
||||
|
||||
for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
|
||||
{
|
||||
gtk_adjustment_set_value (adj, i);
|
||||
while (g_main_pending ())
|
||||
g_main_iteration (TRUE);
|
||||
}
|
||||
|
||||
gtk_adjustment_set_value (adj, new_value);
|
||||
|
||||
tab_left_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
|
||||
}
|
||||
else
|
||||
{
|
||||
tab_left_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
gint viewport_size;
|
||||
gfloat new_value;
|
||||
GtkWidget *inner;
|
||||
gfloat i;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
new_value = tab_search_offset (inner, adj->value, 1, cv->vertical);
|
||||
|
||||
if (new_value == 0 || new_value + viewport_size > adj->upper)
|
||||
new_value = adj->upper - viewport_size;
|
||||
|
||||
if (!tab_right_is_moving)
|
||||
{
|
||||
tab_right_is_moving = 1;
|
||||
|
||||
for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1)
|
||||
{
|
||||
gtk_adjustment_set_value (adj, i);
|
||||
while (g_main_pending ())
|
||||
g_main_iteration (TRUE);
|
||||
}
|
||||
|
||||
gtk_adjustment_set_value (adj, new_value);
|
||||
|
||||
tab_right_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
|
||||
}
|
||||
else
|
||||
{
|
||||
tab_right_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_scroll_cb (GtkWidget *widget, GdkEventScroll *event, gpointer cv)
|
||||
{
|
||||
/* mouse wheel scrolling */
|
||||
if (event->direction == GDK_SCROLL_UP)
|
||||
tab_scroll_left_up_clicked (widget, cv);
|
||||
else if (event->direction == GDK_SCROLL_DOWN)
|
||||
tab_scroll_right_down_clicked (widget, cv);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_xclick_cb (GtkWidget *button, chanview *cv)
|
||||
{
|
||||
cv->cb_xbutton (cv, cv->focused, cv->focused->tag, cv->focused->userdata);
|
||||
}
|
||||
|
||||
/* make a Scroll (arrow) button */
|
||||
|
||||
static GtkWidget *
|
||||
make_sbutton (GtkArrowType type, void *click_cb, void *userdata)
|
||||
{
|
||||
GtkWidget *button, *arrow;
|
||||
|
||||
button = gtk_button_new ();
|
||||
arrow = gtk_arrow_new (type, GTK_SHADOW_NONE);
|
||||
gtk_container_add (GTK_CONTAINER (button), arrow);
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
g_signal_connect (G_OBJECT (button), "clicked",
|
||||
G_CALLBACK (click_cb), userdata);
|
||||
g_signal_connect (G_OBJECT (button), "scroll_event",
|
||||
G_CALLBACK (tab_scroll_cb), userdata);
|
||||
gtk_widget_show (arrow);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_init (chanview *cv)
|
||||
{
|
||||
GtkWidget *box, *hbox = NULL;
|
||||
GtkWidget *viewport;
|
||||
GtkWidget *outer;
|
||||
GtkWidget *button;
|
||||
|
||||
if (cv->vertical)
|
||||
outer = gtk_vbox_new (0, 0);
|
||||
else
|
||||
outer = gtk_hbox_new (0, 0);
|
||||
((tabview *)cv)->outer = outer;
|
||||
g_signal_connect (G_OBJECT (outer), "size_allocate",
|
||||
G_CALLBACK (cv_tabs_sizealloc), cv);
|
||||
/* gtk_container_set_border_width (GTK_CONTAINER (outer), 2);*/
|
||||
gtk_widget_show (outer);
|
||||
|
||||
viewport = gtk_viewport_new (0, 0);
|
||||
gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
|
||||
g_signal_connect (G_OBJECT (viewport), "size_request",
|
||||
G_CALLBACK (cv_tabs_sizerequest), cv);
|
||||
g_signal_connect (G_OBJECT (viewport), "scroll_event",
|
||||
G_CALLBACK (tab_scroll_cb), cv);
|
||||
gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0);
|
||||
gtk_widget_show (viewport);
|
||||
|
||||
if (cv->vertical)
|
||||
box = gtk_vbox_new (FALSE, 0);
|
||||
else
|
||||
box = gtk_hbox_new (FALSE, 0);
|
||||
((tabview *)cv)->inner = box;
|
||||
gtk_container_add (GTK_CONTAINER (viewport), box);
|
||||
gtk_widget_show (box);
|
||||
|
||||
/* if vertical, the buttons can be side by side */
|
||||
if (cv->vertical)
|
||||
{
|
||||
hbox = gtk_hbox_new (FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (outer), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
}
|
||||
|
||||
/* make the Scroll buttons */
|
||||
((tabview *)cv)->b2 = make_sbutton (cv->vertical ?
|
||||
GTK_ARROW_UP : GTK_ARROW_LEFT,
|
||||
tab_scroll_left_up_clicked,
|
||||
cv);
|
||||
|
||||
((tabview *)cv)->b1 = make_sbutton (cv->vertical ?
|
||||
GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
|
||||
tab_scroll_right_down_clicked,
|
||||
cv);
|
||||
|
||||
if (hbox)
|
||||
{
|
||||
gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b2);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b1);
|
||||
} else
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b2, 0, 0, 0);
|
||||
gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0);
|
||||
}
|
||||
|
||||
button = gtkutil_button (outer, GTK_STOCK_CLOSE, NULL, cv_tabs_xclick_cb,
|
||||
cv, 0);
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), outer);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_postinit (chanview *cv)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
tab_add_sorted (chanview *cv, GtkWidget *box, GtkWidget *tab, chan *ch)
|
||||
{
|
||||
GList *list;
|
||||
GtkBoxChild *child;
|
||||
int i = 0;
|
||||
void *b;
|
||||
|
||||
if (!cv->sorted)
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_widget_show (tab);
|
||||
return;
|
||||
}
|
||||
|
||||
/* sorting TODO:
|
||||
* - move tab if renamed (dialogs) */
|
||||
|
||||
/* userdata, passed to mg_tabs_compare() */
|
||||
b = ch->userdata;
|
||||
|
||||
list = GTK_BOX (box)->children;
|
||||
while (list)
|
||||
{
|
||||
child = list->data;
|
||||
if (!GTK_IS_SEPARATOR (child->widget))
|
||||
{
|
||||
void *a = g_object_get_data (G_OBJECT (child->widget), "u");
|
||||
|
||||
if (ch->tag == 0 && cv->cb_compare (a, b) > 0)
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_box_reorder_child (GTK_BOX (box), tab, i);
|
||||
gtk_widget_show (tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* append */
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_box_reorder_child (GTK_BOX (box), tab, i);
|
||||
gtk_widget_show (tab);
|
||||
}
|
||||
|
||||
/* remove empty boxes and separators */
|
||||
|
||||
static void
|
||||
cv_tabs_prune (chanview *cv)
|
||||
{
|
||||
GList *boxes, *children;
|
||||
GtkWidget *box, *inner;
|
||||
GtkBoxChild *child;
|
||||
int empty;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
box = child->widget;
|
||||
boxes = boxes->next;
|
||||
|
||||
/* check if the box is empty (except a vseperator) */
|
||||
empty = TRUE;
|
||||
children = GTK_BOX (box)->children;
|
||||
while (children)
|
||||
{
|
||||
if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
|
||||
{
|
||||
empty = FALSE;
|
||||
break;
|
||||
}
|
||||
children = children->next;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
gtk_widget_destroy (box);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tab_add_real (chanview *cv, GtkWidget *tab, chan *ch)
|
||||
{
|
||||
GList *boxes, *children;
|
||||
GtkWidget *sep, *box, *inner;
|
||||
GtkBoxChild *child;
|
||||
int empty;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
/* see if a family for this tab already exists */
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
box = child->widget;
|
||||
|
||||
if (g_object_get_data (G_OBJECT (box), "f") == ch->family)
|
||||
{
|
||||
tab_add_sorted (cv, box, tab, ch);
|
||||
gtk_widget_queue_resize (inner->parent);
|
||||
return;
|
||||
}
|
||||
|
||||
boxes = boxes->next;
|
||||
|
||||
/* check if the box is empty (except a vseperator) */
|
||||
empty = TRUE;
|
||||
children = GTK_BOX (box)->children;
|
||||
while (children)
|
||||
{
|
||||
if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
|
||||
{
|
||||
empty = FALSE;
|
||||
break;
|
||||
}
|
||||
children = children->next;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
gtk_widget_destroy (box);
|
||||
}
|
||||
|
||||
/* create a new family box */
|
||||
if (cv->vertical)
|
||||
{
|
||||
/* vertical */
|
||||
box = gtk_vbox_new (FALSE, 0);
|
||||
sep = gtk_hseparator_new ();
|
||||
} else
|
||||
{
|
||||
/* horiz */
|
||||
box = gtk_hbox_new (FALSE, 0);
|
||||
sep = gtk_vseparator_new ();
|
||||
}
|
||||
|
||||
gtk_box_pack_end (GTK_BOX (box), sep, 0, 0, 4);
|
||||
gtk_widget_show (sep);
|
||||
gtk_box_pack_start (GTK_BOX (inner), box, 0, 0, 0);
|
||||
g_object_set_data (G_OBJECT (box), "f", ch->family);
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_widget_show (tab);
|
||||
gtk_widget_show (box);
|
||||
gtk_widget_queue_resize (inner->parent);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_ignore_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* called when a tab is clicked (button down) */
|
||||
|
||||
static void
|
||||
tab_pressed_cb (GtkToggleButton *tab, chan *ch)
|
||||
{
|
||||
chan *old_tab;
|
||||
int is_switching = TRUE;
|
||||
chanview *cv = ch->cv;
|
||||
|
||||
ignore_toggle = TRUE;
|
||||
/* de-activate the old tab */
|
||||
old_tab = cv->focused;
|
||||
if (old_tab && old_tab->impl)
|
||||
{
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (old_tab->impl), FALSE);
|
||||
if (old_tab == ch)
|
||||
is_switching = FALSE;
|
||||
}
|
||||
gtk_toggle_button_set_active (tab, TRUE);
|
||||
ignore_toggle = FALSE;
|
||||
cv->focused = ch;
|
||||
|
||||
if (/*tab->active*/is_switching)
|
||||
/* call the focus callback */
|
||||
cv->cb_focus (cv, ch, ch->tag, ch->userdata);
|
||||
}
|
||||
|
||||
/* called for keyboard tab toggles only */
|
||||
static void
|
||||
tab_toggled_cb (GtkToggleButton *tab, chan *ch)
|
||||
{
|
||||
if (ignore_toggle)
|
||||
return;
|
||||
|
||||
/* activated a tab via keyboard */
|
||||
tab_pressed_cb (tab, ch);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch)
|
||||
{
|
||||
return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event);
|
||||
}
|
||||
|
||||
static void *
|
||||
cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
|
||||
{
|
||||
GtkWidget *but;
|
||||
|
||||
but = gtk_toggle_button_new_with_label (name);
|
||||
gtk_widget_set_name (but, "xchat-tab");
|
||||
g_object_set_data (G_OBJECT (but), "c", ch);
|
||||
/* used to trap right-clicks */
|
||||
g_signal_connect (G_OBJECT (but), "button_press_event",
|
||||
G_CALLBACK (tab_click_cb), ch);
|
||||
/* avoid prelights */
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "leave_notify_event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "pressed",
|
||||
G_CALLBACK (tab_pressed_cb), ch);
|
||||
/* for keyboard */
|
||||
g_signal_connect (G_OBJECT (but), "toggled",
|
||||
G_CALLBACK (tab_toggled_cb), ch);
|
||||
g_object_set_data (G_OBJECT (but), "u", ch->userdata);
|
||||
|
||||
tab_add_real (cv, but, ch);
|
||||
|
||||
return but;
|
||||
}
|
||||
|
||||
/* traverse all the family boxes of tabs
|
||||
*
|
||||
* A "group" is basically:
|
||||
* GtkV/HBox
|
||||
* `-GtkViewPort
|
||||
* `-GtkV/HBox (inner box)
|
||||
* `- GtkBox (family box)
|
||||
* `- GtkToggleButton
|
||||
* `- GtkToggleButton
|
||||
* `- ...
|
||||
* `- GtkBox
|
||||
* `- GtkToggleButton
|
||||
* `- GtkToggleButton
|
||||
* `- ...
|
||||
* `- ...
|
||||
*
|
||||
* */
|
||||
|
||||
static int
|
||||
tab_group_for_each_tab (chanview *cv,
|
||||
int (*callback) (GtkWidget *tab, int num, int usernum),
|
||||
int usernum)
|
||||
{
|
||||
GList *tabs;
|
||||
GList *boxes;
|
||||
GtkBoxChild *child;
|
||||
GtkBox *innerbox;
|
||||
int i;
|
||||
|
||||
innerbox = (GtkBox *) ((tabview *)cv)->inner;
|
||||
boxes = innerbox->children;
|
||||
i = 0;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
tabs = GTK_BOX (child->widget)->children;
|
||||
|
||||
while (tabs)
|
||||
{
|
||||
child = tabs->data;
|
||||
|
||||
if (!GTK_IS_SEPARATOR (child->widget))
|
||||
{
|
||||
if (callback (child->widget, i, usernum) != -1)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
tabs = tabs->next;
|
||||
}
|
||||
|
||||
boxes = boxes->next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
tab_check_focus_cb (GtkWidget *tab, int num, int unused)
|
||||
{
|
||||
if (GTK_TOGGLE_BUTTON (tab)->active)
|
||||
return num;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* returns the currently focused tab number */
|
||||
|
||||
static int
|
||||
tab_group_get_cur_page (chanview *cv)
|
||||
{
|
||||
return tab_group_for_each_tab (cv, tab_check_focus_cb, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_focus (chan *ch)
|
||||
{
|
||||
if (ch->impl)
|
||||
/* focus the new one (tab_pressed_cb defocuses the old one) */
|
||||
tab_pressed_cb (GTK_TOGGLE_BUTTON (ch->impl), ch);
|
||||
}
|
||||
|
||||
static int
|
||||
tab_focus_num_cb (GtkWidget *tab, int num, int want)
|
||||
{
|
||||
if (num == want)
|
||||
{
|
||||
cv_tabs_focus (g_object_get_data (G_OBJECT (tab), "c"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_change_orientation (chanview *cv)
|
||||
{
|
||||
/* cleanup the old one */
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
/* now rebuild a new tabbar or tree */
|
||||
cv->func_init (cv);
|
||||
chanview_populate (cv);
|
||||
}
|
||||
|
||||
/* switch to the tab number specified */
|
||||
|
||||
static void
|
||||
cv_tabs_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
if (relative)
|
||||
{
|
||||
max = cv->size;
|
||||
i = tab_group_get_cur_page (cv) + num;
|
||||
/* make it wrap around at both ends */
|
||||
if (i < 0)
|
||||
i = max - 1;
|
||||
if (i >= max)
|
||||
i = 0;
|
||||
tab_group_for_each_tab (cv, tab_focus_num_cb, i);
|
||||
return;
|
||||
}
|
||||
|
||||
tab_group_for_each_tab (cv, tab_focus_num_cb, num);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_remove (chan *ch)
|
||||
{
|
||||
gtk_widget_destroy (ch->impl);
|
||||
ch->impl = NULL;
|
||||
|
||||
cv_tabs_prune (ch->cv);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_move (chan *ch, int delta)
|
||||
{
|
||||
int i, pos = 0;
|
||||
GList *list;
|
||||
GtkWidget *parent = ((GtkWidget *)ch->impl)->parent;
|
||||
|
||||
i = 0;
|
||||
for (list = GTK_BOX (parent)->children; list; list = list->next)
|
||||
{
|
||||
GtkBoxChild *child_entry;
|
||||
|
||||
child_entry = list->data;
|
||||
if (child_entry->widget == ch->impl)
|
||||
pos = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
pos = (pos - delta) % i;
|
||||
gtk_box_reorder_child (GTK_BOX (parent), ch->impl, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_move_family (chan *ch, int delta)
|
||||
{
|
||||
int i, pos = 0;
|
||||
GList *list;
|
||||
GtkWidget *box = NULL;
|
||||
|
||||
/* find position of tab's family */
|
||||
i = 0;
|
||||
for (list = GTK_BOX (((tabview *)ch->cv)->inner)->children; list; list = list->next)
|
||||
{
|
||||
GtkBoxChild *child_entry;
|
||||
void *fam;
|
||||
|
||||
child_entry = list->data;
|
||||
fam = g_object_get_data (G_OBJECT (child_entry->widget), "f");
|
||||
if (fam == ch->family)
|
||||
{
|
||||
box = child_entry->widget;
|
||||
pos = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
pos = (pos - delta) % i;
|
||||
gtk_box_reorder_child (GTK_BOX (box->parent), box, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_cleanup (chanview *cv)
|
||||
{
|
||||
if (cv->box)
|
||||
gtk_widget_destroy (((tabview *)cv)->outer);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
gtk_label_set_attributes (GTK_LABEL (GTK_BIN (ch->impl)->child), list);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_rename (chan *ch, char *name)
|
||||
{
|
||||
PangoAttrList *attr;
|
||||
GtkWidget *tab = ch->impl;
|
||||
|
||||
attr = gtk_label_get_attributes (GTK_LABEL (GTK_BIN (tab)->child));
|
||||
if (attr)
|
||||
pango_attr_list_ref (attr);
|
||||
|
||||
gtk_button_set_label (GTK_BUTTON (tab), name);
|
||||
gtk_widget_queue_resize (tab->parent->parent->parent);
|
||||
|
||||
if (attr)
|
||||
{
|
||||
gtk_label_set_attributes (GTK_LABEL (GTK_BIN (tab)->child), attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tabs_is_collapsed (chan *ch)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static chan *
|
||||
cv_tabs_get_parent (chan *ch)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
364
src/fe-gtk/chanview-tree.c
Normal file
364
src/fe-gtk/chanview-tree.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/* file included in chanview.c */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkTreeView *tree;
|
||||
GtkWidget *scrollw; /* scrolledWindow */
|
||||
} treeview;
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "maingui.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
|
||||
static void /* row-activated, when a row is double clicked */
|
||||
cv_tree_activated_cb (GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *column, gpointer data)
|
||||
{
|
||||
if (gtk_tree_view_row_expanded (view, path))
|
||||
gtk_tree_view_collapse_row (view, path);
|
||||
else
|
||||
gtk_tree_view_expand_row (view, path, FALSE);
|
||||
}
|
||||
|
||||
static void /* row selected callback */
|
||||
cv_tree_sel_cb (GtkTreeSelection *sel, chanview *cv)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
chan *ch;
|
||||
|
||||
if (gtk_tree_selection_get_selected (sel, &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_CHAN, &ch, -1);
|
||||
|
||||
cv->focused = ch;
|
||||
cv->cb_focus (cv, ch, ch->tag, ch->userdata);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tree_click_cb (GtkTreeView *tree, GdkEventButton *event, chanview *cv)
|
||||
{
|
||||
chan *ch;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
int ret = FALSE;
|
||||
|
||||
if (event->button != 3 && event->state == 0)
|
||||
return FALSE;
|
||||
|
||||
sel = gtk_tree_view_get_selection (tree);
|
||||
if (gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
|
||||
{
|
||||
if (event->button == 2)
|
||||
{
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
}
|
||||
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (cv->store), &iter, path))
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
ret = cv->cb_contextmenu (cv, ch, ch->tag, ch->userdata, event);
|
||||
}
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_init (chanview *cv)
|
||||
{
|
||||
GtkWidget *view, *win;
|
||||
GtkCellRenderer *renderer;
|
||||
static const GtkTargetEntry dnd_src_target[] =
|
||||
{
|
||||
{"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
static const GtkTargetEntry dnd_dest_target[] =
|
||||
{
|
||||
{"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
/*gtk_container_set_border_width (GTK_CONTAINER (win), 1);*/
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), win);
|
||||
gtk_widget_show (win);
|
||||
|
||||
view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cv->store));
|
||||
gtk_widget_set_name (view, "xchat-tree");
|
||||
if (cv->style)
|
||||
gtk_widget_set_style (view, cv->style);
|
||||
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/
|
||||
GTK_WIDGET_UNSET_FLAGS (view, GTK_CAN_FOCUS);
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
||||
#if GTK_CHECK_VERSION(2,10,0)
|
||||
if (!(prefs.gui_tweaks & 8))
|
||||
gtk_tree_view_set_enable_tree_lines (GTK_TREE_VIEW (view), TRUE);
|
||||
#endif
|
||||
gtk_container_add (GTK_CONTAINER (win), view);
|
||||
|
||||
/* icon column */
|
||||
if (cv->use_icons)
|
||||
{
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
|
||||
-1, NULL, renderer,
|
||||
"pixbuf", COL_PIXBUF, NULL);
|
||||
}
|
||||
|
||||
/* main column */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
|
||||
-1, NULL, renderer,
|
||||
"text", COL_NAME, "attributes", COL_ATTR, NULL);
|
||||
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (view))),
|
||||
"changed", G_CALLBACK (cv_tree_sel_cb), cv);
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (cv_tree_click_cb), cv);
|
||||
g_signal_connect (G_OBJECT (view), "row-activated",
|
||||
G_CALLBACK (cv_tree_activated_cb), NULL);
|
||||
|
||||
gtk_drag_dest_set (view, GTK_DEST_DEFAULT_ALL, dnd_dest_target, 1,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
gtk_drag_source_set (view, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_COPY);
|
||||
|
||||
#ifndef WIN32
|
||||
g_signal_connect (G_OBJECT (view), "drag_begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
#endif
|
||||
|
||||
((treeview *)cv)->tree = GTK_TREE_VIEW (view);
|
||||
((treeview *)cv)->scrollw = win;
|
||||
gtk_widget_show (view);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_postinit (chanview *cv)
|
||||
{
|
||||
gtk_tree_view_expand_all (((treeview *)cv)->tree);
|
||||
}
|
||||
|
||||
static void *
|
||||
cv_tree_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
|
||||
if (parent)
|
||||
{
|
||||
/* expand the parent node */
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (cv->store), parent);
|
||||
if (path)
|
||||
{
|
||||
gtk_tree_view_expand_row (((treeview *)cv)->tree, path, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_change_orientation (chanview *cv)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_focus (chan *ch)
|
||||
{
|
||||
GtkTreeView *tree = ((treeview *)ch->cv)->tree;
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (tree);
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter parent;
|
||||
GdkRectangle cell_rect;
|
||||
GdkRectangle vis_rect;
|
||||
gint dest_y;
|
||||
|
||||
/* expand the parent node */
|
||||
if (gtk_tree_model_iter_parent (model, &parent, &ch->iter))
|
||||
{
|
||||
path = gtk_tree_model_get_path (model, &parent);
|
||||
if (path)
|
||||
{
|
||||
/*if (!gtk_tree_view_row_expanded (tree, path))
|
||||
{
|
||||
gtk_tree_path_free (path);
|
||||
return;
|
||||
}*/
|
||||
gtk_tree_view_expand_row (tree, path, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
path = gtk_tree_model_get_path (model, &ch->iter);
|
||||
if (path)
|
||||
{
|
||||
/* This full section does what
|
||||
* gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0.5);
|
||||
* does, except it only scrolls the window if the provided cell is
|
||||
* not visible. Basic algorithm taken from gtktreeview.c */
|
||||
|
||||
/* obtain information to see if the cell is visible */
|
||||
gtk_tree_view_get_background_area (tree, path, NULL, &cell_rect);
|
||||
gtk_tree_view_get_visible_rect (tree, &vis_rect);
|
||||
|
||||
/* The cordinates aren't offset correctly */
|
||||
gtk_tree_view_widget_to_tree_coords( tree, cell_rect.x, cell_rect.y, NULL, &cell_rect.y );
|
||||
|
||||
/* only need to scroll if out of bounds */
|
||||
if (cell_rect.y < vis_rect.y ||
|
||||
cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
|
||||
{
|
||||
dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * 0.5);
|
||||
if (dest_y < 0)
|
||||
dest_y = 0;
|
||||
gtk_tree_view_scroll_to_point (tree, -1, dest_y);
|
||||
}
|
||||
/* theft done, now make it focused like */
|
||||
gtk_tree_view_set_cursor (tree, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
chan *ch;
|
||||
|
||||
if (relative)
|
||||
{
|
||||
num += cv_find_number_of_chan (cv, cv->focused);
|
||||
num %= cv->size;
|
||||
/* make it wrap around at both ends */
|
||||
if (num < 0)
|
||||
num = cv->size - 1;
|
||||
}
|
||||
|
||||
ch = cv_find_chan_by_number (cv, num);
|
||||
if (ch)
|
||||
cv_tree_focus (ch);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_remove (chan *ch)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
move_row (chan *ch, int delta, GtkTreeIter *parent)
|
||||
{
|
||||
GtkTreeStore *store = ch->cv->store;
|
||||
GtkTreeIter *src = &ch->iter;
|
||||
GtkTreeIter dest = ch->iter;
|
||||
GtkTreePath *dest_path;
|
||||
|
||||
if (delta < 0) /* down */
|
||||
{
|
||||
if (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &dest))
|
||||
gtk_tree_store_swap (store, src, &dest);
|
||||
else /* move to top */
|
||||
gtk_tree_store_move_after (store, src, NULL);
|
||||
|
||||
} else
|
||||
{
|
||||
dest_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &dest);
|
||||
if (gtk_tree_path_prev (dest_path))
|
||||
{
|
||||
gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &dest, dest_path);
|
||||
gtk_tree_store_swap (store, src, &dest);
|
||||
} else
|
||||
{ /* move to bottom */
|
||||
gtk_tree_store_move_before (store, src, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_path_free (dest_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move (chan *ch, int delta)
|
||||
{
|
||||
GtkTreeIter parent;
|
||||
|
||||
/* do nothing if this is a server row */
|
||||
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (ch->cv->store), &parent, &ch->iter))
|
||||
move_row (ch, delta, &parent);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move_family (chan *ch, int delta)
|
||||
{
|
||||
move_row (ch, delta, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_cleanup (chanview *cv)
|
||||
{
|
||||
if (cv->box)
|
||||
/* kill the scrolled window */
|
||||
gtk_widget_destroy (((treeview *)cv)->scrollw);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
/* nothing to do, it's already set in the store */
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_rename (chan *ch, char *name)
|
||||
{
|
||||
/* nothing to do, it's already renamed in the store */
|
||||
}
|
||||
|
||||
static chan *
|
||||
cv_tree_get_parent (chan *ch)
|
||||
{
|
||||
chan *parent_ch = NULL;
|
||||
GtkTreeIter parent;
|
||||
|
||||
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (ch->cv->store), &parent, &ch->iter))
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (ch->cv->store), &parent, COL_CHAN, &parent_ch, -1);
|
||||
}
|
||||
|
||||
return parent_ch;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tree_is_collapsed (chan *ch)
|
||||
{
|
||||
chan *parent = cv_tree_get_parent (ch);
|
||||
GtkTreePath *path = NULL;
|
||||
gboolean ret;
|
||||
|
||||
if (parent == NULL)
|
||||
return FALSE;
|
||||
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (parent->cv->store),
|
||||
&parent->iter);
|
||||
ret = !gtk_tree_view_row_expanded (((treeview *)parent->cv)->tree, path);
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
643
src/fe-gtk/chanview.c
Normal file
643
src/fe-gtk/chanview.c
Normal file
@@ -0,0 +1,643 @@
|
||||
/* abstract channel view: tabs or tree or anything you like */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "chanview.h"
|
||||
#include "gtkutil.h"
|
||||
|
||||
|
||||
/* treeStore columns */
|
||||
|
||||
#define COL_NAME 0 /* (char *) */
|
||||
#define COL_CHAN 1 /* (chan *) */
|
||||
#define COL_ATTR 2 /* (PangoAttrList *) */
|
||||
#define COL_PIXBUF 3 /* (GdkPixbuf *) */
|
||||
|
||||
struct _chanview
|
||||
{
|
||||
/* impl scratch area */
|
||||
char implscratch[sizeof (void *) * 8];
|
||||
|
||||
GtkTreeStore *store;
|
||||
int size; /* number of channels in view */
|
||||
|
||||
GtkWidget *box; /* the box we destroy when changing implementations */
|
||||
GtkStyle *style; /* style used for tree */
|
||||
chan *focused; /* currently focused channel */
|
||||
int trunc_len;
|
||||
|
||||
/* callbacks */
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata);
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata);
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *);
|
||||
int (*cb_compare) (void *a, void *b);
|
||||
|
||||
/* impl */
|
||||
void (*func_init) (chanview *);
|
||||
void (*func_postinit) (chanview *);
|
||||
void *(*func_add) (chanview *, chan *, char *, GtkTreeIter *);
|
||||
void (*func_move_focus) (chanview *, gboolean, int);
|
||||
void (*func_change_orientation) (chanview *);
|
||||
void (*func_remove) (chan *);
|
||||
void (*func_move) (chan *, int delta);
|
||||
void (*func_move_family) (chan *, int delta);
|
||||
void (*func_focus) (chan *);
|
||||
void (*func_set_color) (chan *, PangoAttrList *);
|
||||
void (*func_rename) (chan *, char *);
|
||||
gboolean (*func_is_collapsed) (chan *);
|
||||
chan *(*func_get_parent) (chan *);
|
||||
void (*func_cleanup) (chanview *);
|
||||
|
||||
unsigned int sorted:1;
|
||||
unsigned int vertical:1;
|
||||
unsigned int use_icons:1;
|
||||
};
|
||||
|
||||
struct _chan
|
||||
{
|
||||
chanview *cv; /* our owner */
|
||||
GtkTreeIter iter;
|
||||
void *userdata; /* session * */
|
||||
void *family; /* server * or null */
|
||||
void *impl; /* togglebutton or null */
|
||||
GdkPixbuf *icon;
|
||||
short allow_closure; /* allow it to be closed when it still has children? */
|
||||
short tag;
|
||||
};
|
||||
|
||||
static chan *cv_find_chan_by_number (chanview *cv, int num);
|
||||
static int cv_find_number_of_chan (chanview *cv, chan *find_ch);
|
||||
|
||||
|
||||
/* ======= TABS ======= */
|
||||
|
||||
#include "chanview-tabs.c"
|
||||
|
||||
|
||||
/* ======= TREE ======= */
|
||||
|
||||
#include "chanview-tree.c"
|
||||
|
||||
|
||||
/* ==== ABSTRACT CHANVIEW ==== */
|
||||
|
||||
static char *
|
||||
truncate_tab_name (char *name, int max)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (max > 2 && g_utf8_strlen (name, -1) > max)
|
||||
{
|
||||
/* truncate long channel names */
|
||||
buf = malloc (strlen (name) + 4);
|
||||
strcpy (buf, name);
|
||||
g_utf8_offset_to_pointer (buf, max)[0] = 0;
|
||||
strcat (buf, "..");
|
||||
return buf;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/* iterate through a model, into 1 depth of children */
|
||||
|
||||
static void
|
||||
model_foreach_1 (GtkTreeModel *model, void (*func)(void *, GtkTreeIter *),
|
||||
void *userdata)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
func (userdata, &iter);
|
||||
if (gtk_tree_model_iter_children (model, &inner, &iter))
|
||||
{
|
||||
do
|
||||
func (userdata, &inner);
|
||||
while (gtk_tree_model_iter_next (model, &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_pop_cb (chanview *cv, GtkTreeIter *iter)
|
||||
{
|
||||
chan *ch;
|
||||
char *name;
|
||||
PangoAttrList *attr;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter,
|
||||
COL_NAME, &name, COL_CHAN, &ch, COL_ATTR, &attr, -1);
|
||||
ch->impl = cv->func_add (cv, ch, name, NULL);
|
||||
if (attr)
|
||||
{
|
||||
cv->func_set_color (ch, attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_populate (chanview *cv)
|
||||
{
|
||||
model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_pop_cb, cv);
|
||||
}
|
||||
|
||||
void
|
||||
chanview_set_impl (chanview *cv, int type)
|
||||
{
|
||||
/* cleanup the old one */
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
cv->func_init = cv_tabs_init;
|
||||
cv->func_postinit = cv_tabs_postinit;
|
||||
cv->func_add = cv_tabs_add;
|
||||
cv->func_move_focus = cv_tabs_move_focus;
|
||||
cv->func_change_orientation = cv_tabs_change_orientation;
|
||||
cv->func_remove = cv_tabs_remove;
|
||||
cv->func_move = cv_tabs_move;
|
||||
cv->func_move_family = cv_tabs_move_family;
|
||||
cv->func_focus = cv_tabs_focus;
|
||||
cv->func_set_color = cv_tabs_set_color;
|
||||
cv->func_rename = cv_tabs_rename;
|
||||
cv->func_is_collapsed = cv_tabs_is_collapsed;
|
||||
cv->func_get_parent = cv_tabs_get_parent;
|
||||
cv->func_cleanup = cv_tabs_cleanup;
|
||||
break;
|
||||
|
||||
default:
|
||||
cv->func_init = cv_tree_init;
|
||||
cv->func_postinit = cv_tree_postinit;
|
||||
cv->func_add = cv_tree_add;
|
||||
cv->func_move_focus = cv_tree_move_focus;
|
||||
cv->func_change_orientation = cv_tree_change_orientation;
|
||||
cv->func_remove = cv_tree_remove;
|
||||
cv->func_move = cv_tree_move;
|
||||
cv->func_move_family = cv_tree_move_family;
|
||||
cv->func_focus = cv_tree_focus;
|
||||
cv->func_set_color = cv_tree_set_color;
|
||||
cv->func_rename = cv_tree_rename;
|
||||
cv->func_is_collapsed = cv_tree_is_collapsed;
|
||||
cv->func_get_parent = cv_tree_get_parent;
|
||||
cv->func_cleanup = cv_tree_cleanup;
|
||||
break;
|
||||
}
|
||||
|
||||
/* now rebuild a new tabbar or tree */
|
||||
cv->func_init (cv);
|
||||
|
||||
chanview_populate (cv);
|
||||
|
||||
cv->func_postinit (cv);
|
||||
|
||||
/* force re-focus */
|
||||
if (cv->focused)
|
||||
cv->func_focus (cv->focused);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_free_ch (chanview *cv, GtkTreeIter *iter)
|
||||
{
|
||||
chan *ch;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_CHAN, &ch, -1);
|
||||
free (ch);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_destroy_store (chanview *cv) /* free every (chan *) in the store */
|
||||
{
|
||||
model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_free_ch, cv);
|
||||
g_object_unref (cv->store);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_destroy (chanview *cv)
|
||||
{
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
if (cv->box)
|
||||
gtk_widget_destroy (cv->box);
|
||||
|
||||
chanview_destroy_store (cv);
|
||||
free (cv);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_box_destroy_cb (GtkWidget *box, chanview *cv)
|
||||
{
|
||||
cv->box = NULL;
|
||||
chanview_destroy (cv);
|
||||
}
|
||||
|
||||
chanview *
|
||||
chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons,
|
||||
GtkStyle *style)
|
||||
{
|
||||
chanview *cv;
|
||||
|
||||
cv = calloc (1, sizeof (chanview));
|
||||
cv->store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
|
||||
PANGO_TYPE_ATTR_LIST, GDK_TYPE_PIXBUF);
|
||||
cv->style = style;
|
||||
cv->box = gtk_hbox_new (0, 0);
|
||||
cv->trunc_len = trunc_len;
|
||||
cv->sorted = sort;
|
||||
cv->use_icons = use_icons;
|
||||
gtk_widget_show (cv->box);
|
||||
chanview_set_impl (cv, type);
|
||||
|
||||
g_signal_connect (G_OBJECT (cv->box), "destroy",
|
||||
G_CALLBACK (chanview_box_destroy_cb), cv);
|
||||
|
||||
return cv;
|
||||
}
|
||||
|
||||
/* too lazy for signals */
|
||||
|
||||
void
|
||||
chanview_set_callbacks (chanview *cv,
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata),
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata),
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
|
||||
int (*cb_compare) (void *a, void *b))
|
||||
{
|
||||
cv->cb_focus = cb_focus;
|
||||
cv->cb_xbutton = cb_xbutton;
|
||||
cv->cb_contextmenu = cb_contextmenu;
|
||||
cv->cb_compare = cb_compare;
|
||||
}
|
||||
|
||||
/* find a place to insert this new entry, based on the compare function */
|
||||
|
||||
static void
|
||||
chanview_insert_sorted (chanview *cv, GtkTreeIter *add_iter, GtkTreeIter *parent, void *ud)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
chan *ch;
|
||||
|
||||
if (cv->sorted && gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &iter, parent))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
if (ch->tag == 0 && cv->cb_compare (ch->userdata, ud) > 0)
|
||||
{
|
||||
gtk_tree_store_insert_before (cv->store, add_iter, parent, &iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
gtk_tree_store_append (cv->store, add_iter, parent);
|
||||
}
|
||||
|
||||
/* find a parent node with the same "family" pointer (i.e. the Server tab) */
|
||||
|
||||
static int
|
||||
chanview_find_parent (chanview *cv, void *family, GtkTreeIter *search_iter, chan *avoid)
|
||||
{
|
||||
chan *search_ch;
|
||||
|
||||
/* find this new row's parent, if any */
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), search_iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), search_iter,
|
||||
COL_CHAN, &search_ch, -1);
|
||||
if (family == search_ch->family && search_ch != avoid /*&&
|
||||
gtk_tree_store_iter_depth (cv->store, search_iter) == 0*/)
|
||||
return TRUE;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), search_iter));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static chan *
|
||||
chanview_add_real (chanview *cv, char *name, void *family, void *userdata,
|
||||
gboolean allow_closure, int tag, GdkPixbuf *icon,
|
||||
chan *ch, chan *avoid)
|
||||
{
|
||||
GtkTreeIter parent_iter;
|
||||
GtkTreeIter iter;
|
||||
gboolean has_parent = FALSE;
|
||||
|
||||
if (chanview_find_parent (cv, family, &parent_iter, avoid))
|
||||
{
|
||||
chanview_insert_sorted (cv, &iter, &parent_iter, userdata);
|
||||
has_parent = TRUE;
|
||||
} else
|
||||
{
|
||||
gtk_tree_store_append (cv->store, &iter, NULL);
|
||||
}
|
||||
|
||||
if (!ch)
|
||||
{
|
||||
ch = calloc (1, sizeof (chan));
|
||||
ch->userdata = userdata;
|
||||
ch->family = family;
|
||||
ch->cv = cv;
|
||||
ch->allow_closure = allow_closure;
|
||||
ch->tag = tag;
|
||||
ch->icon = icon;
|
||||
}
|
||||
memcpy (&(ch->iter), &iter, sizeof (iter));
|
||||
|
||||
gtk_tree_store_set (cv->store, &iter, COL_NAME, name, COL_CHAN, ch,
|
||||
COL_PIXBUF, icon, -1);
|
||||
|
||||
cv->size++;
|
||||
if (!has_parent)
|
||||
ch->impl = cv->func_add (cv, ch, name, NULL);
|
||||
else
|
||||
ch->impl = cv->func_add (cv, ch, name, &parent_iter);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
chan *
|
||||
chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon)
|
||||
{
|
||||
char *new_name;
|
||||
chan *ret;
|
||||
|
||||
new_name = truncate_tab_name (name, cv->trunc_len);
|
||||
|
||||
ret = chanview_add_real (cv, new_name, family, userdata, allow_closure, tag, icon, NULL, NULL);
|
||||
|
||||
if (new_name != name)
|
||||
free (new_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
chanview_get_size (chanview *cv)
|
||||
{
|
||||
return cv->size;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
chanview_get_box (chanview *cv)
|
||||
{
|
||||
return cv->box;
|
||||
}
|
||||
|
||||
void
|
||||
chanview_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
cv->func_move_focus (cv, relative, num);
|
||||
}
|
||||
|
||||
GtkOrientation
|
||||
chanview_get_orientation (chanview *cv)
|
||||
{
|
||||
return (cv->vertical ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
|
||||
void
|
||||
chanview_set_orientation (chanview *cv, gboolean vertical)
|
||||
{
|
||||
if (vertical != cv->vertical)
|
||||
{
|
||||
cv->vertical = vertical;
|
||||
cv->func_change_orientation (cv);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
chan_get_tag (chan *ch)
|
||||
{
|
||||
return ch->tag;
|
||||
}
|
||||
|
||||
void *
|
||||
chan_get_userdata (chan *ch)
|
||||
{
|
||||
return ch->userdata;
|
||||
}
|
||||
|
||||
void
|
||||
chan_focus (chan *ch)
|
||||
{
|
||||
if (ch->cv->focused == ch)
|
||||
return;
|
||||
|
||||
ch->cv->func_focus (ch);
|
||||
}
|
||||
|
||||
void
|
||||
chan_move (chan *ch, int delta)
|
||||
{
|
||||
ch->cv->func_move (ch, delta);
|
||||
}
|
||||
|
||||
void
|
||||
chan_move_family (chan *ch, int delta)
|
||||
{
|
||||
ch->cv->func_move_family (ch, delta);
|
||||
}
|
||||
|
||||
void
|
||||
chan_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
gtk_tree_store_set (ch->cv->store, &ch->iter, COL_ATTR, list, -1);
|
||||
ch->cv->func_set_color (ch, list);
|
||||
}
|
||||
|
||||
void
|
||||
chan_rename (chan *ch, char *name, int trunc_len)
|
||||
{
|
||||
char *new_name;
|
||||
|
||||
new_name = truncate_tab_name (name, trunc_len);
|
||||
|
||||
gtk_tree_store_set (ch->cv->store, &ch->iter, COL_NAME, new_name, -1);
|
||||
ch->cv->func_rename (ch, new_name);
|
||||
ch->cv->trunc_len = trunc_len;
|
||||
|
||||
if (new_name != name)
|
||||
free (new_name);
|
||||
}
|
||||
|
||||
/* this thing is overly complicated */
|
||||
|
||||
static int
|
||||
cv_find_number_of_chan (chanview *cv, chan *find_ch)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
chan *ch;
|
||||
int i = 0;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
if (ch == find_ch)
|
||||
return i;
|
||||
i++;
|
||||
|
||||
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1);
|
||||
if (ch == find_ch)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
return 0; /* WARNING */
|
||||
}
|
||||
|
||||
/* this thing is overly complicated too */
|
||||
|
||||
static chan *
|
||||
cv_find_chan_by_number (chanview *cv, int num)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
chan *ch;
|
||||
int i = 0;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i == num)
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
return ch;
|
||||
}
|
||||
i++;
|
||||
|
||||
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i == num)
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1);
|
||||
return ch;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
chan_emancipate_children (chan *ch)
|
||||
{
|
||||
char *name;
|
||||
chan *childch;
|
||||
GtkTreeIter childiter;
|
||||
PangoAttrList *attr;
|
||||
|
||||
while (gtk_tree_model_iter_children (GTK_TREE_MODEL (ch->cv->store), &childiter, &ch->iter))
|
||||
{
|
||||
/* remove and re-add all the children, but avoid using "ch" as parent */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (ch->cv->store), &childiter,
|
||||
COL_NAME, &name, COL_CHAN, &childch, COL_ATTR, &attr, -1);
|
||||
ch->cv->func_remove (childch);
|
||||
gtk_tree_store_remove (ch->cv->store, &childiter);
|
||||
ch->cv->size--;
|
||||
chanview_add_real (childch->cv, name, childch->family, childch->userdata, childch->allow_closure, childch->tag, childch->icon, childch, ch);
|
||||
if (attr)
|
||||
{
|
||||
childch->cv->func_set_color (childch, attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
chan_remove (chan *ch, gboolean force)
|
||||
{
|
||||
chan *new_ch;
|
||||
int i, num;
|
||||
extern int xchat_is_quitting;
|
||||
|
||||
if (xchat_is_quitting) /* avoid lots of looping on exit */
|
||||
return TRUE;
|
||||
|
||||
/* is this ch allowed to be closed while still having children? */
|
||||
if (!force &&
|
||||
gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ch->cv->store), &ch->iter) &&
|
||||
!ch->allow_closure)
|
||||
return FALSE;
|
||||
|
||||
chan_emancipate_children (ch);
|
||||
ch->cv->func_remove (ch);
|
||||
|
||||
/* is it the focused one? */
|
||||
if (ch->cv->focused == ch)
|
||||
{
|
||||
ch->cv->focused = NULL;
|
||||
|
||||
/* try to move the focus to some other valid channel */
|
||||
num = cv_find_number_of_chan (ch->cv, ch);
|
||||
/* move to the one left of the closing tab */
|
||||
new_ch = cv_find_chan_by_number (ch->cv, num - 1);
|
||||
if (new_ch && new_ch != ch)
|
||||
{
|
||||
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
|
||||
} else
|
||||
{
|
||||
/* if it fails, try focus from tab 0 and up */
|
||||
for (i = 0; i < ch->cv->size; i++)
|
||||
{
|
||||
new_ch = cv_find_chan_by_number (ch->cv, i);
|
||||
if (new_ch && new_ch != ch)
|
||||
{
|
||||
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ch->cv->size--;
|
||||
gtk_tree_store_remove (ch->cv->store, &ch->iter);
|
||||
free (ch);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
chan_is_collapsed (chan *ch)
|
||||
{
|
||||
return ch->cv->func_is_collapsed (ch);
|
||||
}
|
||||
|
||||
chan *
|
||||
chan_get_parent (chan *ch)
|
||||
{
|
||||
return ch->cv->func_get_parent (ch);
|
||||
}
|
||||
31
src/fe-gtk/chanview.h
Normal file
31
src/fe-gtk/chanview.h
Normal file
@@ -0,0 +1,31 @@
|
||||
typedef struct _chanview chanview;
|
||||
typedef struct _chan chan;
|
||||
|
||||
chanview *chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons, GtkStyle *style);
|
||||
void chanview_set_callbacks (chanview *cv,
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata),
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata),
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
|
||||
int (*cb_compare) (void *a, void *b));
|
||||
void chanview_set_impl (chanview *cv, int type);
|
||||
chan *chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon);
|
||||
int chanview_get_size (chanview *cv);
|
||||
GtkWidget *chanview_get_box (chanview *cv);
|
||||
void chanview_move_focus (chanview *cv, gboolean relative, int num);
|
||||
GtkOrientation chanview_get_orientation (chanview *cv);
|
||||
void chanview_set_orientation (chanview *cv, gboolean vertical);
|
||||
|
||||
int chan_get_tag (chan *ch);
|
||||
void *chan_get_userdata (chan *ch);
|
||||
void chan_focus (chan *ch);
|
||||
void chan_move (chan *ch, int delta);
|
||||
void chan_move_family (chan *ch, int delta);
|
||||
void chan_set_color (chan *ch, PangoAttrList *list);
|
||||
void chan_rename (chan *ch, char *new_name, int trunc_len);
|
||||
gboolean chan_remove (chan *ch, gboolean force);
|
||||
gboolean chan_is_collapsed (chan *ch);
|
||||
chan * chan_get_parent (chan *ch);
|
||||
|
||||
#define FOCUS_NEW_ALL 1
|
||||
#define FOCUS_NEW_ONLY_ASKED 2
|
||||
#define FOCUS_NEW_NONE 0
|
||||
753
src/fe-gtk/custom-list.c
Normal file
753
src/fe-gtk/custom-list.c
Normal file
@@ -0,0 +1,753 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "custom-list.h"
|
||||
|
||||
/* indent -i3 -ci3 -ut -ts3 -bli0 -c0 custom-list.c */
|
||||
|
||||
/* boring declarations of local functions */
|
||||
|
||||
static void custom_list_init (CustomList * pkg_tree);
|
||||
|
||||
static void custom_list_class_init (CustomListClass * klass);
|
||||
|
||||
static void custom_list_tree_model_init (GtkTreeModelIface * iface);
|
||||
|
||||
static void custom_list_finalize (GObject * object);
|
||||
|
||||
static GtkTreeModelFlags custom_list_get_flags (GtkTreeModel * tree_model);
|
||||
|
||||
static gint custom_list_get_n_columns (GtkTreeModel * tree_model);
|
||||
|
||||
static GType custom_list_get_column_type (GtkTreeModel * tree_model,
|
||||
gint index);
|
||||
|
||||
static gboolean custom_list_get_iter (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreePath * path);
|
||||
|
||||
static GtkTreePath *custom_list_get_path (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static void custom_list_get_value (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
gint column, GValue * value);
|
||||
|
||||
static gboolean custom_list_iter_next (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gboolean custom_list_iter_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * parent);
|
||||
|
||||
static gboolean custom_list_iter_has_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gint custom_list_iter_n_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gboolean custom_list_iter_nth_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * parent, gint n);
|
||||
|
||||
static gboolean custom_list_iter_parent (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * child);
|
||||
|
||||
/* -- GtkTreeSortable interface functions -- */
|
||||
|
||||
static gboolean custom_list_sortable_get_sort_column_id (GtkTreeSortable *
|
||||
sortable,
|
||||
gint * sort_col_id,
|
||||
GtkSortType * order);
|
||||
|
||||
static void custom_list_sortable_set_sort_column_id (GtkTreeSortable *
|
||||
sortable,
|
||||
gint sort_col_id,
|
||||
GtkSortType order);
|
||||
|
||||
static void custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
|
||||
gint sort_col_id,
|
||||
GtkTreeIterCompareFunc
|
||||
sort_func, gpointer user_data,
|
||||
GtkDestroyNotify
|
||||
destroy_func);
|
||||
|
||||
static void custom_list_sortable_set_default_sort_func (GtkTreeSortable *
|
||||
sortable,
|
||||
GtkTreeIterCompareFunc
|
||||
sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify
|
||||
destroy_func);
|
||||
|
||||
static gboolean custom_list_sortable_has_default_sort_func (GtkTreeSortable *
|
||||
sortable);
|
||||
|
||||
|
||||
|
||||
static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
|
||||
|
||||
|
||||
static void
|
||||
custom_list_sortable_init (GtkTreeSortableIface * iface)
|
||||
{
|
||||
iface->get_sort_column_id = custom_list_sortable_get_sort_column_id;
|
||||
iface->set_sort_column_id = custom_list_sortable_set_sort_column_id;
|
||||
iface->set_sort_func = custom_list_sortable_set_sort_func; /* NOT SUPPORTED */
|
||||
iface->set_default_sort_func = custom_list_sortable_set_default_sort_func; /* NOT SUPPORTED */
|
||||
iface->has_default_sort_func = custom_list_sortable_has_default_sort_func; /* NOT SUPPORTED */
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_type: here we register our new type and its interfaces
|
||||
* with the type system. If you want to implement
|
||||
* additional interfaces like GtkTreeSortable, you
|
||||
* will need to do it here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GType
|
||||
custom_list_get_type (void)
|
||||
{
|
||||
static GType custom_list_type = 0;
|
||||
|
||||
if (custom_list_type)
|
||||
return custom_list_type;
|
||||
|
||||
/* Some boilerplate type registration stuff */
|
||||
if (1)
|
||||
{
|
||||
static const GTypeInfo custom_list_info = {
|
||||
sizeof (CustomListClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
(GClassInitFunc) custom_list_class_init,
|
||||
NULL, /* class finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (CustomList),
|
||||
0, /* n_preallocs */
|
||||
(GInstanceInitFunc) custom_list_init
|
||||
};
|
||||
|
||||
custom_list_type =
|
||||
g_type_register_static (G_TYPE_OBJECT, "CustomList",
|
||||
&custom_list_info, (GTypeFlags) 0);
|
||||
}
|
||||
|
||||
/* Here we register our GtkTreeModel interface with the type system */
|
||||
if (1)
|
||||
{
|
||||
static const GInterfaceInfo tree_model_info = {
|
||||
(GInterfaceInitFunc) custom_list_tree_model_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_MODEL,
|
||||
&tree_model_info);
|
||||
}
|
||||
|
||||
/* Add GtkTreeSortable interface */
|
||||
if (1)
|
||||
{
|
||||
static const GInterfaceInfo tree_sortable_info = {
|
||||
(GInterfaceInitFunc) custom_list_sortable_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
g_type_add_interface_static (custom_list_type,
|
||||
GTK_TYPE_TREE_SORTABLE,
|
||||
&tree_sortable_info);
|
||||
}
|
||||
|
||||
return custom_list_type;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_class_init: more boilerplate GObject/GType stuff.
|
||||
* Init callback for the type system,
|
||||
* called once when our new class is created.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_class_init (CustomListClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
|
||||
parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
|
||||
object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = custom_list_finalize;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_tree_model_init: init callback for the interface registration
|
||||
* in custom_list_get_type. Here we override
|
||||
* the GtkTreeModel interface functions that
|
||||
* we implement.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_tree_model_init (GtkTreeModelIface * iface)
|
||||
{
|
||||
iface->get_flags = custom_list_get_flags;
|
||||
iface->get_n_columns = custom_list_get_n_columns;
|
||||
iface->get_column_type = custom_list_get_column_type;
|
||||
iface->get_iter = custom_list_get_iter;
|
||||
iface->get_path = custom_list_get_path;
|
||||
iface->get_value = custom_list_get_value;
|
||||
iface->iter_next = custom_list_iter_next;
|
||||
iface->iter_children = custom_list_iter_children;
|
||||
iface->iter_has_child = custom_list_iter_has_child;
|
||||
iface->iter_n_children = custom_list_iter_n_children;
|
||||
iface->iter_nth_child = custom_list_iter_nth_child;
|
||||
iface->iter_parent = custom_list_iter_parent;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_init: this is called everytime a new custom list object
|
||||
* instance is created (we do that in custom_list_new).
|
||||
* Initialise the list structure's fields here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_init (CustomList * custom_list)
|
||||
{
|
||||
custom_list->n_columns = CUSTOM_LIST_N_COLUMNS;
|
||||
|
||||
custom_list->column_types[0] = G_TYPE_STRING; /* CUSTOM_LIST_COL_NAME */
|
||||
custom_list->column_types[1] = G_TYPE_UINT; /* CUSTOM_LIST_COL_USERS */
|
||||
custom_list->column_types[2] = G_TYPE_STRING; /* CUSTOM_LIST_COL_TOPIC */
|
||||
|
||||
custom_list->num_rows = 0;
|
||||
custom_list->num_alloc = 0;
|
||||
custom_list->rows = NULL;
|
||||
|
||||
custom_list->sort_id = SORT_ID_CHANNEL;
|
||||
custom_list->sort_order = GTK_SORT_ASCENDING;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_finalize: this is called just before a custom list is
|
||||
* destroyed. Free dynamically allocated memory here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_finalize (GObject * object)
|
||||
{
|
||||
custom_list_clear (CUSTOM_LIST (object));
|
||||
|
||||
/* must chain up - finalize parent */
|
||||
(*parent_class->finalize) (object);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_flags: tells the rest of the world whether our tree model
|
||||
* has any special characteristics. In our case,
|
||||
* we have a list model (instead of a tree), and each
|
||||
* tree iter is valid as long as the row in question
|
||||
* exists, as it only contains a pointer to our struct.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GtkTreeModelFlags
|
||||
custom_list_get_flags (GtkTreeModel * tree_model)
|
||||
{
|
||||
return (GTK_TREE_MODEL_LIST_ONLY /*| GTK_TREE_MODEL_ITERS_PERSIST */ );
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_n_columns: tells the rest of the world how many data
|
||||
* columns we export via the tree model interface
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gint
|
||||
custom_list_get_n_columns (GtkTreeModel * tree_model)
|
||||
{
|
||||
return 3;/*CUSTOM_LIST (tree_model)->n_columns;*/
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_column_type: tells the rest of the world which type of
|
||||
* data an exported model column contains
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GType
|
||||
custom_list_get_column_type (GtkTreeModel * tree_model, gint index)
|
||||
{
|
||||
return CUSTOM_LIST (tree_model)->column_types[index];
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_iter: converts a tree path (physical position) into a
|
||||
* tree iter structure (the content of the iter
|
||||
* fields will only be used internally by our model).
|
||||
* We simply store a pointer to our chanlistrow
|
||||
* structure that represents that row in the tree iter.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_get_iter (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreePath * path)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
chanlistrow *record;
|
||||
gint n;
|
||||
|
||||
n = gtk_tree_path_get_indices (path)[0];
|
||||
if (n >= custom_list->num_rows || n < 0)
|
||||
return FALSE;
|
||||
|
||||
record = custom_list->rows[n];
|
||||
|
||||
/* We simply store a pointer to our custom record in the iter */
|
||||
iter->user_data = record;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_path: converts a tree iter into a tree path (ie. the
|
||||
* physical position of that row in the list).
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GtkTreePath *
|
||||
custom_list_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
chanlistrow *record;
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, record->pos);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_value: Returns a row's exported data columns
|
||||
* (_get_value is what gtk_tree_model_get uses)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_get_value (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, gint column, GValue * value)
|
||||
{
|
||||
chanlistrow *record;
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
if (custom_list->num_rows == 0)
|
||||
return;
|
||||
|
||||
g_value_init (value, custom_list->column_types[column]);
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case CUSTOM_LIST_COL_NAME:
|
||||
g_value_set_static_string (value, GET_CHAN (record));
|
||||
break;
|
||||
|
||||
case CUSTOM_LIST_COL_USERS:
|
||||
g_value_set_uint (value, record->users);
|
||||
break;
|
||||
|
||||
case CUSTOM_LIST_COL_TOPIC:
|
||||
g_value_set_static_string (value, record->topic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_next: Takes an iter structure and sets it to point
|
||||
* to the next row.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_next (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
chanlistrow *record, *nextrecord;
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
/* Is this the last record in the list? */
|
||||
if ((record->pos + 1) >= custom_list->num_rows)
|
||||
return FALSE;
|
||||
|
||||
nextrecord = custom_list->rows[(record->pos + 1)];
|
||||
|
||||
g_assert (nextrecord != NULL);
|
||||
g_assert (nextrecord->pos == (record->pos + 1));
|
||||
|
||||
iter->user_data = nextrecord;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_children: Returns TRUE or FALSE depending on whether
|
||||
* the row specified by 'parent' has any children.
|
||||
* If it has children, then 'iter' is set to
|
||||
* point to the first child. Special case: if
|
||||
* 'parent' is NULL, then the first top-level
|
||||
* row should be returned if it exists.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * parent)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* this is a list, nodes have no children */
|
||||
if (parent)
|
||||
return FALSE;
|
||||
|
||||
/* parent == NULL is a special case; we need to return the first top-level row */
|
||||
/* No rows => no first row */
|
||||
if (custom_list->num_rows == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Set iter to first item in list */
|
||||
iter->user_data = custom_list->rows[0];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_has_child: Returns TRUE or FALSE depending on whether
|
||||
* the row specified by 'iter' has any children.
|
||||
* We only have a list and thus no children.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_has_child (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_n_children: Returns the number of children the row
|
||||
* specified by 'iter' has. This is usually 0,
|
||||
* as we only have a list and thus do not have
|
||||
* any children to any rows. A special case is
|
||||
* when 'iter' is NULL, in which case we need
|
||||
* to return the number of top-level nodes,
|
||||
* ie. the number of rows in our list.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gint
|
||||
custom_list_iter_n_children (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* special case: if iter == NULL, return number of top-level rows */
|
||||
if (!iter)
|
||||
return custom_list->num_rows;
|
||||
|
||||
return 0; /* otherwise, this is easy again for a list */
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_nth_child: If the row specified by 'parent' has any
|
||||
* children, set 'iter' to the n-th child and
|
||||
* return TRUE if it exists, otherwise FALSE.
|
||||
* A special case is when 'parent' is NULL, in
|
||||
* which case we need to set 'iter' to the n-th
|
||||
* row if it exists.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_nth_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * parent, gint n)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* a list has only top-level rows */
|
||||
if (parent)
|
||||
return FALSE;
|
||||
|
||||
/* special case: if parent == NULL, set iter to n-th top-level row */
|
||||
if (n >= custom_list->num_rows)
|
||||
return FALSE;
|
||||
|
||||
iter->user_data = custom_list->rows[n];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_parent: Point 'iter' to the parent node of 'child'. As
|
||||
* we have a list and thus no children and no
|
||||
* parents of children, we can just return FALSE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_parent (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * child)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_list_sortable_get_sort_column_id (GtkTreeSortable * sortable,
|
||||
gint * sort_col_id,
|
||||
GtkSortType * order)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (sortable);
|
||||
|
||||
if (sort_col_id)
|
||||
*sort_col_id = custom_list->sort_id;
|
||||
|
||||
if (order)
|
||||
*order = custom_list->sort_order;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_sort_column_id (GtkTreeSortable * sortable,
|
||||
gint sort_col_id, GtkSortType order)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (sortable);
|
||||
|
||||
if (custom_list->sort_id == sort_col_id
|
||||
&& custom_list->sort_order == order)
|
||||
return;
|
||||
|
||||
custom_list->sort_id = sort_col_id;
|
||||
custom_list->sort_order = order;
|
||||
|
||||
custom_list_resort (custom_list);
|
||||
|
||||
/* emit "sort-column-changed" signal to tell any tree views
|
||||
* that the sort column has changed (so the little arrow
|
||||
* in the column header of the sort column is drawn
|
||||
* in the right column) */
|
||||
|
||||
gtk_tree_sortable_sort_column_changed (sortable);
|
||||
}
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
|
||||
gint sort_col_id,
|
||||
GtkTreeIterCompareFunc sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify destroy_func)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_default_sort_func (GtkTreeSortable * sortable,
|
||||
GtkTreeIterCompareFunc sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify destroy_func)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_list_sortable_has_default_sort_func (GtkTreeSortable * sortable)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* fast as possible compare func for sorting.
|
||||
TODO: If fast enough, use a unicode collation key and strcmp. */
|
||||
|
||||
#define TOSML(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
|
||||
|
||||
static inline int
|
||||
fast_ascii_stricmp (const char *s1, const char *s2)
|
||||
{
|
||||
int c1, c2;
|
||||
|
||||
while (*s1 && *s2)
|
||||
{
|
||||
c1 = (int) (unsigned char) TOSML (*s1);
|
||||
c2 = (int) (unsigned char) TOSML (*s2);
|
||||
if (c1 != c2)
|
||||
return (c1 - c2);
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
|
||||
}
|
||||
|
||||
static gint
|
||||
custom_list_qsort_compare_func (chanlistrow ** a, chanlistrow ** b,
|
||||
CustomList * custom_list)
|
||||
{
|
||||
if (custom_list->sort_order == GTK_SORT_DESCENDING)
|
||||
{
|
||||
chanlistrow **tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
if (custom_list->sort_id == SORT_ID_USERS)
|
||||
{
|
||||
return (*a)->users - (*b)->users;
|
||||
}
|
||||
|
||||
if (custom_list->sort_id == SORT_ID_TOPIC)
|
||||
{
|
||||
return fast_ascii_stricmp ((*a)->topic, (*b)->topic);
|
||||
}
|
||||
|
||||
return strcmp ((*a)->collation_key, (*b)->collation_key);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_new: This is what you use in your own code to create a
|
||||
* new custom list tree model for you to use.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
CustomList *
|
||||
custom_list_new (void)
|
||||
{
|
||||
return (CustomList *) g_object_new (CUSTOM_TYPE_LIST, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_append (CustomList * custom_list, chanlistrow * newrecord)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
gulong newsize;
|
||||
guint pos;
|
||||
|
||||
if (custom_list->num_rows >= custom_list->num_alloc)
|
||||
{
|
||||
custom_list->num_alloc += 64;
|
||||
newsize = custom_list->num_alloc * sizeof (chanlistrow *);
|
||||
custom_list->rows = g_realloc (custom_list->rows, newsize);
|
||||
}
|
||||
|
||||
/* TODO: Binary search insert? */
|
||||
|
||||
pos = custom_list->num_rows;
|
||||
custom_list->rows[pos] = newrecord;
|
||||
custom_list->num_rows++;
|
||||
newrecord->pos = pos;
|
||||
|
||||
/* inform the tree view and other interested objects
|
||||
* (e.g. tree row references) that we have inserted
|
||||
* a new row, and where it was inserted */
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, newrecord->pos);
|
||||
/* custom_list_get_iter(GTK_TREE_MODEL(custom_list), &iter, path);*/
|
||||
iter.user_data = newrecord;
|
||||
gtk_tree_model_row_inserted (GTK_TREE_MODEL (custom_list), path, &iter);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_resort (CustomList * custom_list)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
gint *neworder, i;
|
||||
|
||||
if (custom_list->num_rows < 2)
|
||||
return;
|
||||
|
||||
/* resort */
|
||||
g_qsort_with_data (custom_list->rows,
|
||||
custom_list->num_rows,
|
||||
sizeof (chanlistrow *),
|
||||
(GCompareDataFunc) custom_list_qsort_compare_func,
|
||||
custom_list);
|
||||
|
||||
/* let other objects know about the new order */
|
||||
neworder = malloc (sizeof (gint) * custom_list->num_rows);
|
||||
|
||||
for (i = custom_list->num_rows - 1; i >= 0; i--)
|
||||
{
|
||||
/* Note that the API reference might be wrong about
|
||||
* this, see bug number 124790 on bugs.gnome.org.
|
||||
* Both will work, but one will give you 'jumpy'
|
||||
* selections after row reordering. */
|
||||
/* neworder[(custom_list->rows[i])->pos] = i; */
|
||||
neworder[i] = (custom_list->rows[i])->pos;
|
||||
(custom_list->rows[i])->pos = i;
|
||||
}
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (custom_list), path, NULL,
|
||||
neworder);
|
||||
gtk_tree_path_free (path);
|
||||
free (neworder);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_clear (CustomList * custom_list)
|
||||
{
|
||||
int i, max = custom_list->num_rows - 1;
|
||||
GtkTreePath *path;
|
||||
|
||||
for (i = max; i >= 0; i--)
|
||||
{
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, custom_list->rows[i]->pos);
|
||||
gtk_tree_model_row_deleted (GTK_TREE_MODEL (custom_list), path);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
custom_list->num_rows = 0;
|
||||
custom_list->num_alloc = 0;
|
||||
|
||||
g_free (custom_list->rows);
|
||||
custom_list->rows = NULL;
|
||||
}
|
||||
85
src/fe-gtk/custom-list.h
Normal file
85
src/fe-gtk/custom-list.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef _custom_list_h_included_
|
||||
#define _custom_list_h_included_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Some boilerplate GObject defines. 'klass' is used
|
||||
* instead of 'class', because 'class' is a C++ keyword */
|
||||
|
||||
#define CUSTOM_TYPE_LIST (custom_list_get_type ())
|
||||
#define CUSTOM_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CUSTOM_TYPE_LIST, CustomList))
|
||||
#define CUSTOM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_TYPE_LIST, CustomListClass))
|
||||
#define CUSTOM_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_TYPE_LIST))
|
||||
#define CUSTOM_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_TYPE_LIST))
|
||||
#define CUSTOM_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_TYPE_LIST, CustomListClass))
|
||||
|
||||
/* The data columns that we export via the tree model interface */
|
||||
|
||||
enum
|
||||
{
|
||||
CUSTOM_LIST_COL_NAME,
|
||||
CUSTOM_LIST_COL_USERS,
|
||||
CUSTOM_LIST_COL_TOPIC,
|
||||
CUSTOM_LIST_N_COLUMNS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SORT_ID_CHANNEL,
|
||||
SORT_ID_USERS,
|
||||
SORT_ID_TOPIC
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
char *collation_key;
|
||||
guint32 pos; /* pos within the array */
|
||||
guint32 users;
|
||||
/* channel string lives beyond "users" */
|
||||
#define GET_CHAN(row) (((char *)row)+sizeof(chanlistrow))
|
||||
}
|
||||
chanlistrow;
|
||||
|
||||
typedef struct _CustomList CustomList;
|
||||
typedef struct _CustomListClass CustomListClass;
|
||||
|
||||
|
||||
|
||||
/* CustomList: this structure contains everything we need for our
|
||||
* model implementation. You can add extra fields to
|
||||
* this structure, e.g. hashtables to quickly lookup
|
||||
* rows or whatever else you might need, but it is
|
||||
* crucial that 'parent' is the first member of the
|
||||
* structure. */
|
||||
struct _CustomList
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
guint num_rows; /* number of rows that we have used */
|
||||
guint num_alloc; /* number of rows allocated */
|
||||
chanlistrow **rows; /* a dynamically allocated array of pointers to the
|
||||
* CustomRecord structure for each row */
|
||||
|
||||
gint n_columns;
|
||||
GType column_types[CUSTOM_LIST_N_COLUMNS];
|
||||
|
||||
gint sort_id;
|
||||
GtkSortType sort_order;
|
||||
};
|
||||
|
||||
|
||||
/* CustomListClass: more boilerplate GObject stuff */
|
||||
|
||||
struct _CustomListClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
CustomList *custom_list_new (void);
|
||||
void custom_list_append (CustomList *, chanlistrow *);
|
||||
void custom_list_resort (CustomList *);
|
||||
void custom_list_clear (CustomList *);
|
||||
|
||||
#endif /* _custom_list_h_included_ */
|
||||
1098
src/fe-gtk/dccgui.c
Normal file
1098
src/fe-gtk/dccgui.c
Normal file
File diff suppressed because it is too large
Load Diff
409
src/fe-gtk/editlist.c
Normal file
409
src/fe-gtk/editlist.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvseparator.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/fe.h"
|
||||
#include "menu.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "editlist.h"
|
||||
|
||||
|
||||
static GtkWidget *editlist_gui_entry_name;
|
||||
static GtkWidget *editlist_gui_entry_cmd;
|
||||
static GtkWidget *editlist_gui_window;
|
||||
static GtkWidget *editlist_gui_list;
|
||||
static GSList *editlist_list;
|
||||
static char *editlist_file;
|
||||
static char *editlist_help;
|
||||
|
||||
|
||||
|
||||
static void
|
||||
editlist_gui_load (GtkWidget * listgad)
|
||||
{
|
||||
gchar *nnew[2];
|
||||
GSList *list = editlist_list;
|
||||
struct popup *pop;
|
||||
|
||||
while (list)
|
||||
{
|
||||
pop = (struct popup *) list->data;
|
||||
nnew[0] = pop->name;
|
||||
nnew[1] = pop->cmd;
|
||||
gtk_clist_append (GTK_CLIST (listgad), nnew);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_row_unselected (GtkWidget * clist, gint row, gint column,
|
||||
GdkEventButton * even, gpointer none)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_name), "");
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_cmd), "");
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_row_selected (GtkWidget * clist, gint row, gint column,
|
||||
GdkEventButton * even, gpointer none)
|
||||
{
|
||||
char *name, *cmd;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
gtk_clist_get_text (GTK_CLIST (clist), row, 0, &name);
|
||||
gtk_clist_get_text (GTK_CLIST (clist), row, 1, &cmd);
|
||||
|
||||
name = strdup (name);
|
||||
cmd = strdup (cmd);
|
||||
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_name), name);
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_cmd), cmd);
|
||||
|
||||
free (name);
|
||||
free (cmd);
|
||||
} else
|
||||
{
|
||||
editlist_gui_row_unselected (0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_handle_cmd (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
const char *reply;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
reply = gtk_entry_get_text (GTK_ENTRY (igad));
|
||||
gtk_clist_set_text (GTK_CLIST (editlist_gui_list), row, 1, reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_handle_name (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
const char *ctcp;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
ctcp = gtk_entry_get_text (GTK_ENTRY (igad));
|
||||
gtk_clist_set_text (GTK_CLIST (editlist_gui_list), row, 0, ctcp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_addnew (GtkWidget * igad)
|
||||
{
|
||||
int i;
|
||||
gchar *nnew[2];
|
||||
|
||||
nnew[0] = _("*NEW*");
|
||||
nnew[1] = _("EDIT ME");
|
||||
|
||||
i = gtk_clist_append (GTK_CLIST (editlist_gui_list), nnew);
|
||||
gtk_clist_select_row (GTK_CLIST (editlist_gui_list), i, 0);
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), i, 0, 0.5, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_delete (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
gtk_clist_unselect_all (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_remove (GTK_CLIST (editlist_gui_list), row);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_save (GtkWidget * igad)
|
||||
{
|
||||
int fh, i = 0;
|
||||
char buf[512];
|
||||
char *a, *b;
|
||||
|
||||
fh = xchat_open_file (editlist_file, O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (!gtk_clist_get_text (GTK_CLIST (editlist_gui_list), i, 0, &a))
|
||||
break;
|
||||
gtk_clist_get_text (GTK_CLIST (editlist_gui_list), i, 1, &b);
|
||||
snprintf (buf, sizeof (buf), "NAME %s\nCMD %s\n\n", a, b);
|
||||
write (fh, buf, strlen (buf));
|
||||
i++;
|
||||
}
|
||||
close (fh);
|
||||
gtk_widget_destroy (editlist_gui_window);
|
||||
if (editlist_list == replace_list)
|
||||
{
|
||||
list_free (&replace_list);
|
||||
list_loadconf (editlist_file, &replace_list, 0);
|
||||
} else if (editlist_list == popup_list)
|
||||
{
|
||||
list_free (&popup_list);
|
||||
list_loadconf (editlist_file, &popup_list, 0);
|
||||
} else if (editlist_list == button_list)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
struct session *sess;
|
||||
list_free (&button_list);
|
||||
list_loadconf (editlist_file, &button_list, 0);
|
||||
while (list)
|
||||
{
|
||||
sess = (struct session *) list->data;
|
||||
fe_buttons_update (sess);
|
||||
list = list->next;
|
||||
}
|
||||
} else if (editlist_list == dlgbutton_list)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
struct session *sess;
|
||||
list_free (&dlgbutton_list);
|
||||
list_loadconf (editlist_file, &dlgbutton_list, 0);
|
||||
while (list)
|
||||
{
|
||||
sess = (struct session *) list->data;
|
||||
fe_dlgbuttons_update (sess);
|
||||
list = list->next;
|
||||
}
|
||||
} else if (editlist_list == ctcp_list)
|
||||
{
|
||||
list_free (&ctcp_list);
|
||||
list_loadconf (editlist_file, &ctcp_list, 0);
|
||||
} else if (editlist_list == command_list)
|
||||
{
|
||||
list_free (&command_list);
|
||||
list_loadconf (editlist_file, &command_list, 0);
|
||||
} else if (editlist_list == usermenu_list)
|
||||
{
|
||||
list_free (&usermenu_list);
|
||||
list_loadconf (editlist_file, &usermenu_list, 0);
|
||||
usermenu_update ();
|
||||
} else
|
||||
{
|
||||
list_free (&urlhandler_list);
|
||||
list_loadconf (editlist_file, &urlhandler_list, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_help (GtkWidget * igad)
|
||||
{
|
||||
/* if (editlist_help)*/
|
||||
fe_message (editlist_help, FE_MSG_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_sort (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
gtk_clist_unselect_row (GTK_CLIST (editlist_gui_list), row, 0);
|
||||
gtk_clist_sort (GTK_CLIST (editlist_gui_list));
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_movedown (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
char *temp;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
if (!gtk_clist_get_text (GTK_CLIST (editlist_gui_list), row + 1, 0, &temp))
|
||||
return;
|
||||
gtk_clist_freeze (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_swap_rows (GTK_CLIST (editlist_gui_list), row, row + 1);
|
||||
gtk_clist_thaw (GTK_CLIST (editlist_gui_list));
|
||||
row++;
|
||||
if (!gtk_clist_row_is_visible (GTK_CLIST (editlist_gui_list), row) !=
|
||||
GTK_VISIBILITY_FULL)
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), row, 0, 0.9, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_moveup (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1 && row > 0)
|
||||
{
|
||||
gtk_clist_freeze (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_swap_rows (GTK_CLIST (editlist_gui_list), row - 1, row);
|
||||
gtk_clist_thaw (GTK_CLIST (editlist_gui_list));
|
||||
row--;
|
||||
if (gtk_clist_row_is_visible (GTK_CLIST (editlist_gui_list), row) !=
|
||||
GTK_VISIBILITY_FULL)
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), row, 0, 0.1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_close (void)
|
||||
{
|
||||
editlist_gui_window = 0;
|
||||
}
|
||||
|
||||
void
|
||||
editlist_gui_open (char *title1, char *title2, GSList * list, char *title, char *wmclass,
|
||||
char *file, char *help)
|
||||
{
|
||||
gchar *titles[2];
|
||||
GtkWidget *vbox, *hbox, *button;
|
||||
|
||||
if (title1)
|
||||
{
|
||||
titles[0] = title1;
|
||||
titles[1] = title2;
|
||||
} else
|
||||
{
|
||||
titles[0] = _("Name");
|
||||
titles[1] = _("Command");
|
||||
}
|
||||
|
||||
if (editlist_gui_window)
|
||||
{
|
||||
mg_bring_tofront (editlist_gui_window);
|
||||
return;
|
||||
}
|
||||
|
||||
editlist_list = list;
|
||||
editlist_file = file;
|
||||
editlist_help = help;
|
||||
|
||||
editlist_gui_window =
|
||||
mg_create_generic_tab (wmclass, title, TRUE, FALSE,
|
||||
editlist_gui_close, NULL, 450, 250, &vbox, 0);
|
||||
|
||||
editlist_gui_list = gtkutil_clist_new (2, titles, vbox, GTK_POLICY_ALWAYS,
|
||||
editlist_gui_row_selected, 0,
|
||||
editlist_gui_row_unselected, 0,
|
||||
GTK_SELECTION_BROWSE);
|
||||
gtk_clist_set_column_width (GTK_CLIST (editlist_gui_list), 0, 90);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_GO_UP, 0, editlist_gui_moveup,
|
||||
0, _("Move Up"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_GO_DOWN, 0, editlist_gui_movedown,
|
||||
0, _("Move Dn"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtk_vseparator_new ();
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
gtk_widget_show (button);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_CANCEL, 0, gtkutil_destroy,
|
||||
editlist_gui_window, _("Cancel"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_SAVE, 0, editlist_gui_save,
|
||||
0, _("Save"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_ADD, 0, editlist_gui_addnew,
|
||||
0, _("Add New"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_REMOVE, 0, editlist_gui_delete,
|
||||
0, _("Delete"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtk_vseparator_new ();
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
gtk_widget_show (button);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_SORT_ASCENDING, 0, editlist_gui_sort,
|
||||
0, _("Sort"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_HELP, 0, editlist_gui_help,
|
||||
0, _("Help"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
if (!help)
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
editlist_gui_entry_name = gtk_entry_new_with_max_length (82);
|
||||
gtk_widget_set_usize (editlist_gui_entry_name, 96, 0);
|
||||
gtk_signal_connect (GTK_OBJECT (editlist_gui_entry_name), "changed",
|
||||
GTK_SIGNAL_FUNC (editlist_gui_handle_name), 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), editlist_gui_entry_name, 0, 0, 0);
|
||||
gtk_widget_show (editlist_gui_entry_name);
|
||||
|
||||
editlist_gui_entry_cmd = gtk_entry_new_with_max_length (255);
|
||||
gtk_signal_connect (GTK_OBJECT (editlist_gui_entry_cmd), "changed",
|
||||
GTK_SIGNAL_FUNC (editlist_gui_handle_cmd), 0);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), editlist_gui_entry_cmd);
|
||||
gtk_widget_show (editlist_gui_entry_cmd);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
editlist_gui_load (editlist_gui_list);
|
||||
|
||||
gtk_widget_show (editlist_gui_window);
|
||||
}
|
||||
1
src/fe-gtk/editlist.h
Normal file
1
src/fe-gtk/editlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void editlist_gui_open (char *title1, char *title2, GSList * list, char *title, char *wmclass, char *file, char *help);
|
||||
1063
src/fe-gtk/fe-gtk.c
Normal file
1063
src/fe-gtk/fe-gtk.c
Normal file
File diff suppressed because it is too large
Load Diff
197
src/fe-gtk/fe-gtk.h
Normal file
197
src/fe-gtk/fe-gtk.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "../../config.h"
|
||||
|
||||
#ifdef WIN32
|
||||
/* If you're compiling this for Windows, your release is un-official
|
||||
* and not condoned. Please don't use the XChat name. Make up your
|
||||
* own name! */
|
||||
#define DISPLAY_NAME "XChat-Unofficial"
|
||||
#else
|
||||
#define DISPLAY_NAME "XChat"
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_NLS) && !defined(_)
|
||||
# include <libintl.h>
|
||||
# define _(x) gettext(x)
|
||||
# ifdef gettext_noop
|
||||
# define N_(String) gettext_noop (String)
|
||||
# else
|
||||
# define N_(String) (String)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(ENABLE_NLS) && defined(_)
|
||||
# undef _
|
||||
# define N_(String) (String)
|
||||
# define _(x) (x)
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtksignal.h>
|
||||
|
||||
#undef gtk_signal_connect
|
||||
#define gtk_signal_connect g_signal_connect
|
||||
|
||||
#define flag_t flag_wid[0]
|
||||
#define flag_n flag_wid[1]
|
||||
#define flag_s flag_wid[2]
|
||||
#define flag_i flag_wid[3]
|
||||
#define flag_p flag_wid[4]
|
||||
#define flag_m flag_wid[5]
|
||||
#define flag_l flag_wid[6]
|
||||
#define flag_k flag_wid[7]
|
||||
#define flag_b flag_wid[8]
|
||||
#define NUM_FLAG_WIDS 9
|
||||
|
||||
struct server_gui
|
||||
{
|
||||
GtkWidget *rawlog_window;
|
||||
GtkWidget *rawlog_textlist;
|
||||
|
||||
/* join dialog */
|
||||
GtkWidget *joind_win;
|
||||
GtkWidget *joind_entry;
|
||||
GtkWidget *joind_radio1;
|
||||
GtkWidget *joind_radio2;
|
||||
GtkWidget *joind_check;
|
||||
|
||||
/* chanlist variables */
|
||||
GtkWidget *chanlist_wild; /* GtkEntry */
|
||||
GtkWidget *chanlist_window;
|
||||
GtkWidget *chanlist_list;
|
||||
GtkWidget *chanlist_label;
|
||||
GtkWidget *chanlist_min_spin; /* minusers GtkSpinButton */
|
||||
GtkWidget *chanlist_refresh; /* buttons */
|
||||
GtkWidget *chanlist_join;
|
||||
GtkWidget *chanlist_savelist;
|
||||
GtkWidget *chanlist_search;
|
||||
|
||||
GSList *chanlist_data_stored_rows; /* stored list so it can be resorted */
|
||||
GSList *chanlist_pending_rows;
|
||||
gint chanlist_tag;
|
||||
gint chanlist_flash_tag;
|
||||
|
||||
gboolean chanlist_match_wants_channel; /* match in channel name */
|
||||
gboolean chanlist_match_wants_topic; /* match in topic */
|
||||
|
||||
#ifndef WIN32
|
||||
regex_t chanlist_match_regex; /* compiled regular expression here */
|
||||
unsigned int have_regex;
|
||||
#endif
|
||||
|
||||
guint chanlist_users_found_count; /* users total for all channels */
|
||||
guint chanlist_users_shown_count; /* users total for displayed channels */
|
||||
guint chanlist_channels_found_count; /* channel total for /LIST operation */
|
||||
guint chanlist_channels_shown_count; /* total number of displayed
|
||||
channels */
|
||||
|
||||
int chanlist_maxusers;
|
||||
int chanlist_minusers;
|
||||
int chanlist_minusers_downloaded; /* used by LIST IRC command */
|
||||
int chanlist_search_type; /* 0=simple 1=pattern/wildcard 2=regexp */
|
||||
gboolean chanlist_caption_is_stale;
|
||||
};
|
||||
|
||||
/* this struct is persistant even when delinking/relinking */
|
||||
|
||||
typedef struct restore_gui
|
||||
{
|
||||
/* banlist stuff */
|
||||
GtkWidget *banlist_window;
|
||||
GtkWidget *banlist_treeview;
|
||||
GtkWidget *banlist_butRefresh;
|
||||
|
||||
void *tab; /* (chan *) */
|
||||
|
||||
/* information stored when this tab isn't front-most */
|
||||
void *user_model; /* for filling the GtkTreeView */
|
||||
void *buffer; /* xtext_Buffer */
|
||||
char *input_text; /* input text buffer (while not-front tab) */
|
||||
char *topic_text; /* topic GtkEntry buffer */
|
||||
char *key_text;
|
||||
char *limit_text;
|
||||
gfloat old_ul_value; /* old userlist value (for adj) */
|
||||
gfloat lag_value; /* lag-o-meter */
|
||||
char *lag_text; /* lag-o-meter text */
|
||||
char *lag_tip; /* lag-o-meter tooltip */
|
||||
gfloat queue_value; /* outbound queue meter */
|
||||
char *queue_text; /* outbound queue text */
|
||||
char *queue_tip; /* outbound queue tooltip */
|
||||
short flag_wid_state[NUM_FLAG_WIDS];
|
||||
unsigned int c_graph:1; /* connecting graph, is there one? */
|
||||
} restore_gui;
|
||||
|
||||
typedef struct session_gui
|
||||
{
|
||||
GtkWidget
|
||||
*xtext,
|
||||
*vscrollbar,
|
||||
*window, /* toplevel */
|
||||
*topic_entry,
|
||||
*note_book,
|
||||
*main_table,
|
||||
*user_tree, /* GtkTreeView */
|
||||
*user_box, /* userlist box */
|
||||
*button_box_parent,
|
||||
*button_box, /* userlist buttons' box */
|
||||
*dialogbutton_box,
|
||||
*topicbutton_box,
|
||||
*meter_box, /* all the meters inside this */
|
||||
*lagometer,
|
||||
*laginfo,
|
||||
*throttlemeter,
|
||||
*throttleinfo,
|
||||
*topic_bar,
|
||||
*hpane_left,
|
||||
*hpane_right,
|
||||
*vpane_left,
|
||||
*vpane_right,
|
||||
*menu,
|
||||
*bar, /* connecting progress bar */
|
||||
*nick_box, /* contains label to the left of input_box */
|
||||
*nick_label,
|
||||
*op_xpm, /* icon to the left of nickname */
|
||||
*namelistinfo, /* label above userlist */
|
||||
*input_box,
|
||||
*flag_wid[NUM_FLAG_WIDS], /* channelmode buttons */
|
||||
*limit_entry, /* +l */
|
||||
*key_entry; /* +k */
|
||||
|
||||
#define MENU_ID_NUM 12
|
||||
GtkWidget *menu_item[MENU_ID_NUM+1]; /* some items we may change state of */
|
||||
|
||||
void *chanview; /* chanview.h */
|
||||
|
||||
int bartag; /*connecting progressbar timeout */
|
||||
|
||||
int pane_left_size; /*last position of the pane*/
|
||||
int pane_right_size;
|
||||
|
||||
guint16 is_tab; /* is tab or toplevel? */
|
||||
guint16 ul_hidden; /* userlist hidden? */
|
||||
|
||||
} session_gui;
|
||||
|
||||
extern GdkPixmap *channelwin_pix;
|
||||
extern GdkPixmap *dialogwin_pix;
|
||||
|
||||
|
||||
#ifdef USE_GTKSPELL
|
||||
char *SPELL_ENTRY_GET_TEXT (GtkWidget *entry);
|
||||
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_text_buffer_set_text (gtk_text_view_get_buffer(GTK_TEXT_VIEW(e)),txt,-1);
|
||||
#define SPELL_ENTRY_SET_EDITABLE(e,v) gtk_text_view_set_editable(GTK_TEXT_VIEW(e), v)
|
||||
int SPELL_ENTRY_GET_POS (GtkWidget *entry);
|
||||
void SPELL_ENTRY_SET_POS (GtkWidget *entry, int pos);
|
||||
void SPELL_ENTRY_INSERT (GtkWidget *entry, const char *text, int len, int *pos);
|
||||
#else
|
||||
#define SPELL_ENTRY_GET_TEXT(e) (GTK_ENTRY(e)->text)
|
||||
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_entry_set_text(GTK_ENTRY(e),txt)
|
||||
#define SPELL_ENTRY_SET_EDITABLE(e,v) gtk_editable_set_editable(GTK_EDITABLE(e),v)
|
||||
#define SPELL_ENTRY_GET_POS(e) gtk_editable_get_position(GTK_EDITABLE(e))
|
||||
#define SPELL_ENTRY_SET_POS(e,p) gtk_editable_set_position(GTK_EDITABLE(e),p);
|
||||
#define SPELL_ENTRY_INSERT(e,t,l,p) gtk_editable_insert_text(GTK_EDITABLE(e),t,l,p)
|
||||
#endif
|
||||
1814
src/fe-gtk/fkeys.c
Normal file
1814
src/fe-gtk/fkeys.c
Normal file
File diff suppressed because it is too large
Load Diff
5
src/fe-gtk/fkeys.h
Normal file
5
src/fe-gtk/fkeys.h
Normal file
@@ -0,0 +1,5 @@
|
||||
void key_init (void);
|
||||
void key_dialog_show (void);
|
||||
int key_handle_key_press (GtkWidget * wid, GdkEventKey * evt, session *sess);
|
||||
int key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
|
||||
session *sess);
|
||||
675
src/fe-gtk/gtkutil.c
Normal file
675
src/fe-gtk/gtkutil.c
Normal file
@@ -0,0 +1,675 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64 /* allow selection of large files */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtktooltips.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkspinbutton.h>
|
||||
#include <gtk/gtkclipboard.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcellrenderertoggle.h>
|
||||
#include <gtk/gtkversion.h>
|
||||
#include <gtk/gtkfilechooserdialog.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/util.h"
|
||||
#include "gtkutil.h"
|
||||
#include "pixmaps.h"
|
||||
|
||||
/* gtkutil.c, just some gtk wrappers */
|
||||
|
||||
extern void path_part (char *file, char *path, int pathlen);
|
||||
|
||||
|
||||
struct file_req
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
void *userdata;
|
||||
filereqcallback callback;
|
||||
int flags; /* FRF_* flags */
|
||||
};
|
||||
|
||||
static char last_dir[256] = "";
|
||||
|
||||
|
||||
static void
|
||||
gtkutil_file_req_destroy (GtkWidget * wid, struct file_req *freq)
|
||||
{
|
||||
freq->callback (freq->userdata, NULL);
|
||||
free (freq);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_check_file (char *file, struct file_req *freq)
|
||||
{
|
||||
struct stat st;
|
||||
int axs = FALSE;
|
||||
|
||||
path_part (file, last_dir, sizeof (last_dir));
|
||||
|
||||
/* check if the file is readable or writable */
|
||||
if (freq->flags & FRF_WRITE)
|
||||
{
|
||||
if (access (last_dir, W_OK) == 0)
|
||||
axs = TRUE;
|
||||
} else
|
||||
{
|
||||
if (stat (file, &st) != -1)
|
||||
{
|
||||
if (!S_ISDIR (st.st_mode) || (freq->flags & FRF_CHOOSEFOLDER))
|
||||
axs = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (axs)
|
||||
{
|
||||
char *utf8_file;
|
||||
/* convert to UTF8. It might be converted back to locale by
|
||||
server.c's g_convert */
|
||||
utf8_file = xchat_filename_to_utf8 (file, -1, NULL, NULL, NULL);
|
||||
if (utf8_file)
|
||||
{
|
||||
freq->callback (freq->userdata, utf8_file);
|
||||
g_free (utf8_file);
|
||||
} else
|
||||
{
|
||||
fe_message ("Filename encoding is corrupt.", FE_MSG_ERROR);
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (freq->flags & FRF_WRITE)
|
||||
fe_message (_("Cannot write to that file."), FE_MSG_ERROR);
|
||||
else
|
||||
fe_message (_("Cannot read that file."), FE_MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_file_req_done (GtkWidget * wid, struct file_req *freq)
|
||||
{
|
||||
GSList *files, *cur;
|
||||
GtkFileChooser *fs = GTK_FILE_CHOOSER (freq->dialog);
|
||||
|
||||
if (freq->flags & FRF_MULTIPLE)
|
||||
{
|
||||
files = cur = gtk_file_chooser_get_filenames (fs);
|
||||
while (cur)
|
||||
{
|
||||
gtkutil_check_file (cur->data, freq);
|
||||
g_free (cur->data);
|
||||
cur = cur->next;
|
||||
}
|
||||
if (files)
|
||||
g_slist_free (files);
|
||||
} else
|
||||
{
|
||||
if (freq->flags & FRF_CHOOSEFOLDER)
|
||||
gtkutil_check_file (gtk_file_chooser_get_current_folder (fs), freq);
|
||||
else
|
||||
gtkutil_check_file (gtk_file_chooser_get_filename (fs), freq);
|
||||
}
|
||||
|
||||
/* this should call the "destroy" cb, where we free(freq) */
|
||||
gtk_widget_destroy (freq->dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
gtkutil_file_req_done (dialog, freq);
|
||||
break;
|
||||
|
||||
case GTK_RESPONSE_CANCEL:
|
||||
/* this should call the "destroy" cb, where we free(freq) */
|
||||
gtk_widget_destroy (freq->dialog);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter,
|
||||
int flags)
|
||||
{
|
||||
struct file_req *freq;
|
||||
GtkWidget *dialog;
|
||||
extern char *get_xdir_fs (void);
|
||||
|
||||
if (flags & FRF_WRITE)
|
||||
{
|
||||
dialog = gtk_file_chooser_dialog_new (title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
if (filter && filter[0]) /* filter becomes initial name when saving */
|
||||
{
|
||||
char temp[1024];
|
||||
path_part (filter, temp, sizeof (temp));
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), temp);
|
||||
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), file_part (filter));
|
||||
}
|
||||
#if GTK_CHECK_VERSION(2,8,0)
|
||||
if (!(flags & FRF_NOASKOVERWRITE))
|
||||
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
dialog = gtk_file_chooser_dialog_new (title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
if (flags & FRF_MULTIPLE)
|
||||
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
if (last_dir[0])
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_dir);
|
||||
if (flags & FRF_ADDFOLDER)
|
||||
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
|
||||
get_xdir_fs (), NULL);
|
||||
if (flags & FRF_CHOOSEFOLDER)
|
||||
{
|
||||
gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter && (flags & FRF_FILTERISINITIAL))
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), filter);
|
||||
}
|
||||
|
||||
freq = malloc (sizeof (struct file_req));
|
||||
freq->dialog = dialog;
|
||||
freq->flags = flags;
|
||||
freq->callback = callback;
|
||||
freq->userdata = userdata;
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_file_req_response), freq);
|
||||
g_signal_connect (G_OBJECT (dialog), "destroy",
|
||||
G_CALLBACK (gtkutil_file_req_destroy), (gpointer) freq);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_destroy (GtkWidget * igad, GtkWidget * dgad)
|
||||
{
|
||||
gtk_widget_destroy (dgad);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_get_str_response (GtkDialog *dialog, gint arg1, gpointer entry)
|
||||
{
|
||||
void (*callback) (int cancel, char *text, void *user_data);
|
||||
char *text;
|
||||
void *user_data;
|
||||
|
||||
text = (char *) gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
callback = g_object_get_data (G_OBJECT (dialog), "cb");
|
||||
user_data = g_object_get_data (G_OBJECT (dialog), "ud");
|
||||
|
||||
switch (arg1)
|
||||
{
|
||||
case GTK_RESPONSE_REJECT:
|
||||
callback (TRUE, text, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
callback (FALSE, text, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_str_enter (GtkWidget *entry, GtkWidget *dialog)
|
||||
{
|
||||
gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
}
|
||||
|
||||
void
|
||||
fe_get_str (char *msg, char *def, void *callback, void *userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *label;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (msg, NULL, 0,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->vbox), TRUE);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
hbox = gtk_hbox_new (TRUE, 0);
|
||||
|
||||
g_object_set_data (G_OBJECT (dialog), "cb", callback);
|
||||
g_object_set_data (G_OBJECT (dialog), "ud", userdata);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
g_signal_connect (G_OBJECT (entry), "activate",
|
||||
G_CALLBACK (gtkutil_str_enter), dialog);
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), def);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), entry, 0, 0, 0);
|
||||
|
||||
label = gtk_label_new (msg);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), label, 0, 0, 0);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_get_str_response), entry);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
|
||||
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_get_number_response (GtkDialog *dialog, gint arg1, gpointer spin)
|
||||
{
|
||||
void (*callback) (int cancel, int value, void *user_data);
|
||||
int num;
|
||||
void *user_data;
|
||||
|
||||
num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin));
|
||||
callback = g_object_get_data (G_OBJECT (dialog), "cb");
|
||||
user_data = g_object_get_data (G_OBJECT (dialog), "ud");
|
||||
|
||||
switch (arg1)
|
||||
{
|
||||
case GTK_RESPONSE_REJECT:
|
||||
callback (TRUE, num, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
callback (FALSE, num, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_get_int (char *msg, int def, void *callback, void *userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *spin;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *label;
|
||||
GtkAdjustment *adj;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (msg, NULL, 0,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->vbox), TRUE);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
hbox = gtk_hbox_new (TRUE, 0);
|
||||
|
||||
g_object_set_data (G_OBJECT (dialog), "cb", callback);
|
||||
g_object_set_data (G_OBJECT (dialog), "ud", userdata);
|
||||
|
||||
spin = gtk_spin_button_new (NULL, 1, 0);
|
||||
adj = gtk_spin_button_get_adjustment ((GtkSpinButton*)spin);
|
||||
adj->lower = 0;
|
||||
adj->upper = 1024;
|
||||
adj->step_increment = 1;
|
||||
gtk_adjustment_changed (adj);
|
||||
gtk_spin_button_set_value ((GtkSpinButton*)spin, def);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), spin, 0, 0, 0);
|
||||
|
||||
label = gtk_label_new (msg);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), label, 0, 0, 0);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_get_number_response), spin);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
|
||||
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callback,
|
||||
void *userdata, char *labeltext)
|
||||
{
|
||||
GtkWidget *wid, *img, *bbox;
|
||||
|
||||
wid = gtk_button_new ();
|
||||
|
||||
if (labeltext)
|
||||
{
|
||||
gtk_button_set_label (GTK_BUTTON (wid), labeltext);
|
||||
gtk_button_set_image (GTK_BUTTON (wid), gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU));
|
||||
gtk_button_set_use_underline (GTK_BUTTON (wid), TRUE);
|
||||
if (box)
|
||||
gtk_container_add (GTK_CONTAINER (box), wid);
|
||||
}
|
||||
else
|
||||
{
|
||||
bbox = gtk_hbox_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (wid), bbox);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
img = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU);
|
||||
if (stock == GTK_STOCK_GOTO_LAST)
|
||||
gtk_widget_set_usize (img, 10, 6);
|
||||
gtk_container_add (GTK_CONTAINER (bbox), img);
|
||||
gtk_widget_show (img);
|
||||
gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
|
||||
}
|
||||
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (callback), userdata);
|
||||
gtk_widget_show (wid);
|
||||
if (tip)
|
||||
add_tip (wid, tip);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_label_new (char *text, GtkWidget * box)
|
||||
{
|
||||
GtkWidget *label = gtk_label_new (text);
|
||||
gtk_container_add (GTK_CONTAINER (box), label);
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_entry_new (int max, GtkWidget * box, void *callback,
|
||||
gpointer userdata)
|
||||
{
|
||||
GtkWidget *entry = gtk_entry_new_with_max_length (max);
|
||||
gtk_container_add (GTK_CONTAINER (box), entry);
|
||||
if (callback)
|
||||
g_signal_connect (G_OBJECT (entry), "changed",
|
||||
G_CALLBACK (callback), userdata);
|
||||
gtk_widget_show (entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_clist_new (int columns, char *titles[],
|
||||
GtkWidget * box, int policy,
|
||||
void *select_callback, gpointer select_userdata,
|
||||
void *unselect_callback,
|
||||
gpointer unselect_userdata, int selection_mode)
|
||||
{
|
||||
GtkWidget *clist, *win;
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), win);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_AUTOMATIC, policy);
|
||||
gtk_widget_show (win);
|
||||
|
||||
if (titles)
|
||||
clist = gtk_clist_new_with_titles (columns, titles);
|
||||
else
|
||||
clist = gtk_clist_new (columns);
|
||||
|
||||
gtk_clist_set_selection_mode (GTK_CLIST (clist), selection_mode);
|
||||
gtk_clist_column_titles_passive (GTK_CLIST (clist));
|
||||
gtk_container_add (GTK_CONTAINER (win), clist);
|
||||
if (select_callback)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (clist), "select_row",
|
||||
G_CALLBACK (select_callback), select_userdata);
|
||||
}
|
||||
if (unselect_callback)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (clist), "unselect_row",
|
||||
G_CALLBACK (unselect_callback), unselect_userdata);
|
||||
}
|
||||
gtk_widget_show (clist);
|
||||
|
||||
return clist;
|
||||
}
|
||||
|
||||
int
|
||||
gtkutil_clist_selection (GtkWidget * clist)
|
||||
{
|
||||
if (GTK_CLIST (clist)->selection)
|
||||
return GPOINTER_TO_INT(GTK_CLIST (clist)->selection->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
int_compare (const int * elem1, const int * elem2)
|
||||
{
|
||||
return (*elem1) - (*elem2);
|
||||
}
|
||||
|
||||
int
|
||||
gtkutil_clist_multiple_selection (GtkWidget * clist, int ** rows, const int max_rows)
|
||||
{
|
||||
int i = 0;
|
||||
GList *tmp_clist;
|
||||
*rows = malloc (sizeof (int) * max_rows );
|
||||
memset( *rows, -1, max_rows * sizeof(int) );
|
||||
|
||||
for( tmp_clist = GTK_CLIST(clist)->selection;
|
||||
tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++)
|
||||
{
|
||||
(*rows)[i] = GPOINTER_TO_INT( tmp_clist->data );
|
||||
}
|
||||
qsort(*rows, i, sizeof(int), (void *)int_compare);
|
||||
return i;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
add_tip (GtkWidget * wid, char *text)
|
||||
{
|
||||
static GtkTooltips *tip = NULL;
|
||||
if (!tip)
|
||||
tip = gtk_tooltips_new ();
|
||||
gtk_tooltips_set_tip (tip, wid, text, 0);
|
||||
}
|
||||
|
||||
void
|
||||
show_and_unfocus (GtkWidget * wid)
|
||||
{
|
||||
GTK_WIDGET_UNSET_FLAGS (wid, GTK_CAN_FOCUS);
|
||||
gtk_widget_show (wid);
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_set_icon (GtkWidget *win)
|
||||
{
|
||||
gtk_window_set_icon (GTK_WINDOW (win), pix_xchat);
|
||||
}
|
||||
|
||||
extern GtkWidget *parent_window; /* maingui.c */
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_window_new (char *title, char *role, int width, int height, int flags)
|
||||
{
|
||||
GtkWidget *win;
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtkutil_set_icon (win);
|
||||
#ifdef WIN32
|
||||
gtk_window_set_wmclass (GTK_WINDOW (win), "XChat", "xchat");
|
||||
#endif
|
||||
gtk_window_set_title (GTK_WINDOW (win), title);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), width, height);
|
||||
gtk_window_set_role (GTK_WINDOW (win), role);
|
||||
if (flags & 1)
|
||||
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE);
|
||||
if ((flags & 2) && parent_window)
|
||||
{
|
||||
gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG);
|
||||
gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent_window));
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
/* pass NULL as selection to paste to both clipboard & X11 text */
|
||||
void
|
||||
gtkutil_copy_to_clipboard (GtkWidget *widget, GdkAtom selection,
|
||||
const gchar *str)
|
||||
{
|
||||
GtkWidget *win;
|
||||
GtkClipboard *clip, *clip2;
|
||||
|
||||
win = gtk_widget_get_toplevel (GTK_WIDGET (widget));
|
||||
if (GTK_WIDGET_TOPLEVEL (win))
|
||||
{
|
||||
int len = strlen (str);
|
||||
|
||||
if (selection)
|
||||
{
|
||||
clip = gtk_widget_get_clipboard (win, selection);
|
||||
gtk_clipboard_set_text (clip, str, len);
|
||||
} else
|
||||
{
|
||||
/* copy to both primary X selection and clipboard */
|
||||
clip = gtk_widget_get_clipboard (win, GDK_SELECTION_PRIMARY);
|
||||
clip2 = gtk_widget_get_clipboard (win, GDK_SELECTION_CLIPBOARD);
|
||||
gtk_clipboard_set_text (clip, str, len);
|
||||
gtk_clipboard_set_text (clip2, str, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Treeview util functions */
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_treeview_new (GtkWidget *box, GtkTreeModel *model,
|
||||
GtkTreeCellDataFunc mapper, ...)
|
||||
{
|
||||
GtkWidget *win, *view;
|
||||
GtkCellRenderer *renderer = NULL;
|
||||
GtkTreeViewColumn *col;
|
||||
va_list args;
|
||||
int col_id = 0;
|
||||
GType type;
|
||||
char *title, *attr;
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), win);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_show (win);
|
||||
|
||||
view = gtk_tree_view_new_with_model (model);
|
||||
/* the view now has a ref on the model, we can unref it */
|
||||
g_object_unref (G_OBJECT (model));
|
||||
gtk_container_add (GTK_CONTAINER (win), view);
|
||||
|
||||
va_start (args, mapper);
|
||||
for (col_id = va_arg (args, int); col_id != -1; col_id = va_arg (args, int))
|
||||
{
|
||||
type = gtk_tree_model_get_column_type (model, col_id);
|
||||
switch (type)
|
||||
{
|
||||
case G_TYPE_BOOLEAN:
|
||||
renderer = gtk_cell_renderer_toggle_new ();
|
||||
attr = "active";
|
||||
break;
|
||||
case G_TYPE_STRING: /* fall through */
|
||||
default:
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
attr = "text";
|
||||
break;
|
||||
}
|
||||
|
||||
title = va_arg (args, char *);
|
||||
if (mapper) /* user-specified function to set renderer attributes */
|
||||
{
|
||||
col = gtk_tree_view_column_new_with_attributes (title, renderer, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (col, renderer, mapper,
|
||||
GINT_TO_POINTER (col_id), NULL);
|
||||
} else
|
||||
{
|
||||
/* just set the typical attribute for this type of renderer */
|
||||
col = gtk_tree_view_column_new_with_attributes (title, renderer,
|
||||
attr, col_id, NULL);
|
||||
}
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtkutil_treemodel_string_to_iter (GtkTreeModel *model, gchar *pathstr, GtkTreeIter *iter_ret)
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
|
||||
gboolean success;
|
||||
|
||||
success = gtk_tree_model_get_iter (model, iter_ret, path);
|
||||
gtk_tree_path_free (path);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*gboolean
|
||||
gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret)
|
||||
{
|
||||
GtkTreeModel *store;
|
||||
GtkTreeSelection *select;
|
||||
|
||||
select = gtk_tree_view_get_selection (view);
|
||||
return gtk_tree_selection_get_selected (select, &store, iter_ret);
|
||||
}*/
|
||||
|
||||
gboolean
|
||||
gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...)
|
||||
{
|
||||
GtkTreeModel *store;
|
||||
GtkTreeSelection *select;
|
||||
gboolean has_selected;
|
||||
va_list args;
|
||||
|
||||
select = gtk_tree_view_get_selection (view);
|
||||
has_selected = gtk_tree_selection_get_selected (select, &store, iter_ret);
|
||||
|
||||
if (has_selected) {
|
||||
va_start (args, iter_ret);
|
||||
gtk_tree_model_get_valist (store, iter_ret, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
return has_selected;
|
||||
}
|
||||
|
||||
39
src/fe-gtk/gtkutil.h
Normal file
39
src/fe-gtk/gtkutil.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
|
||||
typedef void (*filereqcallback) (void *, char *file);
|
||||
|
||||
#define FRF_WRITE 1
|
||||
#define FRF_MULTIPLE 2
|
||||
#define FRF_ADDFOLDER 4
|
||||
#define FRF_CHOOSEFOLDER 8
|
||||
#define FRF_FILTERISINITIAL 16
|
||||
#define FRF_NOASKOVERWRITE 32
|
||||
|
||||
void gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, int flags);
|
||||
void gtkutil_destroy (GtkWidget * igad, GtkWidget * dgad);
|
||||
GtkWidget *gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callback,
|
||||
void *userdata, char *labeltext);
|
||||
void gtkutil_label_new (char *text, GtkWidget * box);
|
||||
GtkWidget *gtkutil_entry_new (int max, GtkWidget * box, void *callback,
|
||||
gpointer userdata);
|
||||
GtkWidget *gtkutil_clist_new (int columns, char *titles[], GtkWidget * box,
|
||||
int policy, void *select_callback,
|
||||
gpointer select_userdata,
|
||||
void *unselect_callback,
|
||||
gpointer unselect_userdata, int selection_mode);
|
||||
int gtkutil_clist_selection (GtkWidget * clist);
|
||||
int gtkutil_clist_multiple_selection (GtkWidget * clist,
|
||||
int ** rows, const int max_rows);
|
||||
void add_tip (GtkWidget * wid, char *text);
|
||||
void show_and_unfocus (GtkWidget * wid);
|
||||
void gtkutil_set_icon (GtkWidget *win);
|
||||
GtkWidget *gtkutil_window_new (char *title, char *role, int width, int height, int flags);
|
||||
void gtkutil_copy_to_clipboard (GtkWidget *widget, GdkAtom selection,
|
||||
const gchar *str);
|
||||
GtkWidget *gtkutil_treeview_new (GtkWidget *box, GtkTreeModel *model,
|
||||
GtkTreeCellDataFunc mapper, ...);
|
||||
gboolean gtkutil_treemodel_string_to_iter (GtkTreeModel *model, gchar *pathstr, GtkTreeIter *iter_ret);
|
||||
gboolean gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret);
|
||||
gboolean gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...);
|
||||
|
||||
449
src/fe-gtk/ignoregui.c
Normal file
449
src/fe-gtk/ignoregui.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkcheckbutton.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkhseparator.h>
|
||||
#include <gtk/gtkversion.h>
|
||||
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcellrenderertoggle.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/ignore.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/fe.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
|
||||
enum
|
||||
{
|
||||
MASK_COLUMN,
|
||||
CHAN_COLUMN,
|
||||
PRIV_COLUMN,
|
||||
NOTICE_COLUMN,
|
||||
CTCP_COLUMN,
|
||||
DCC_COLUMN,
|
||||
INVITE_COLUMN,
|
||||
UNIGNORE_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkWidget *ignorewin = 0;
|
||||
|
||||
static GtkWidget *num_ctcp;
|
||||
static GtkWidget *num_priv;
|
||||
static GtkWidget *num_chan;
|
||||
static GtkWidget *num_noti;
|
||||
static GtkWidget *num_invi;
|
||||
|
||||
static GtkTreeModel *
|
||||
get_store (void)
|
||||
{
|
||||
return gtk_tree_view_get_model (g_object_get_data (G_OBJECT (ignorewin), "view"));
|
||||
}
|
||||
|
||||
static int
|
||||
ignore_get_flags (GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
gboolean chan, priv, noti, ctcp, dcc, invi, unig;
|
||||
int flags = 0;
|
||||
|
||||
gtk_tree_model_get (model, iter, 1, &chan, 2, &priv, 3, ¬i,
|
||||
4, &ctcp, 5, &dcc, 6, &invi, 7, &unig, -1);
|
||||
if (chan)
|
||||
flags |= IG_CHAN;
|
||||
if (priv)
|
||||
flags |= IG_PRIV;
|
||||
if (noti)
|
||||
flags |= IG_NOTI;
|
||||
if (ctcp)
|
||||
flags |= IG_CTCP;
|
||||
if (dcc)
|
||||
flags |= IG_DCC;
|
||||
if (invi)
|
||||
flags |= IG_INVI;
|
||||
if (unig)
|
||||
flags |= IG_UNIG;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
mask_edited (GtkCellRendererText *render, gchar *path, gchar *new, gpointer dat)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
char *old;
|
||||
int flags;
|
||||
|
||||
gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &old, -1);
|
||||
|
||||
if (!strcmp (old, new)) /* no change */
|
||||
;
|
||||
else if (ignore_exists (new)) /* duplicate, ignore */
|
||||
fe_message (_("That mask already exists."), FE_MSG_ERROR);
|
||||
else
|
||||
{
|
||||
/* delete old mask, and add new one with original flags */
|
||||
ignore_del (old, NULL);
|
||||
flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
|
||||
ignore_add (new, flags);
|
||||
|
||||
/* update tree */
|
||||
gtk_list_store_set (store, &iter, MASK_COLUMN, new, -1);
|
||||
}
|
||||
g_free (old);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
option_toggled (GtkCellRendererToggle *render, gchar *path, gpointer data)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
int col_id = GPOINTER_TO_INT (data);
|
||||
gboolean active;
|
||||
char *mask;
|
||||
int flags;
|
||||
|
||||
gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
|
||||
|
||||
/* update model */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, col_id, &active, -1);
|
||||
gtk_list_store_set (store, &iter, col_id, !active, -1);
|
||||
|
||||
/* update ignore list */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &mask, -1);
|
||||
flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
|
||||
if (ignore_add (mask, flags) != 2)
|
||||
g_warning ("ignore treeview is out of sync!\n");
|
||||
|
||||
g_free (mask);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
ignore_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkCellRenderer *render;
|
||||
int col_id;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store),
|
||||
NULL,
|
||||
MASK_COLUMN, _("Mask"),
|
||||
CHAN_COLUMN, _("Channel"),
|
||||
PRIV_COLUMN, _("Private"),
|
||||
NOTICE_COLUMN, _("Notice"),
|
||||
CTCP_COLUMN, _("CTCP"),
|
||||
DCC_COLUMN, _("DCC"),
|
||||
INVITE_COLUMN, _("Invite"),
|
||||
UNIGNORE_COLUMN, _("Unignore"),
|
||||
-1);
|
||||
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
|
||||
gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), TRUE);
|
||||
|
||||
/* attach to signals and customise columns */
|
||||
for (col_id=0; (col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_id));
|
||||
col_id++)
|
||||
{
|
||||
GList *list = gtk_tree_view_column_get_cell_renderers (col);
|
||||
GList *tmp;
|
||||
|
||||
for (tmp = list; tmp; tmp = tmp->next)
|
||||
{
|
||||
render = tmp->data;
|
||||
if (col_id > 0) /* it's a toggle button column */
|
||||
{
|
||||
g_signal_connect (render, "toggled", G_CALLBACK (option_toggled),
|
||||
GINT_TO_POINTER (col_id));
|
||||
} else /* mask column */
|
||||
{
|
||||
g_object_set (G_OBJECT (render), "editable", TRUE, NULL);
|
||||
g_signal_connect (render, "edited", G_CALLBACK (mask_edited), NULL);
|
||||
/* make this column sortable */
|
||||
gtk_tree_view_column_set_sort_column_id (col, col_id);
|
||||
gtk_tree_view_column_set_min_width (col, 272);
|
||||
}
|
||||
/* centre titles */
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
}
|
||||
|
||||
g_list_free (list);
|
||||
}
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_delete_entry_clicked (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
|
||||
GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
char *mask = NULL;
|
||||
|
||||
if (gtkutil_treeview_get_selected (view, &iter, 0, &mask, -1))
|
||||
{
|
||||
/* delete this row, select next one */
|
||||
#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
|
||||
gtk_list_store_remove (store, &iter);
|
||||
#else
|
||||
if (gtk_list_store_remove (store, &iter))
|
||||
{
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
|
||||
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
|
||||
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
#endif
|
||||
|
||||
ignore_del (mask, NULL);
|
||||
g_free (mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_store_new (int cancel, char *mask, gpointer data)
|
||||
{
|
||||
GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
int flags = IG_CHAN | IG_PRIV | IG_NOTI | IG_CTCP | IG_DCC | IG_INVI;
|
||||
|
||||
if (cancel)
|
||||
return;
|
||||
/* check if it already exists */
|
||||
if (ignore_exists (mask))
|
||||
{
|
||||
fe_message (_("That mask already exists."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ignore_add (mask, flags);
|
||||
|
||||
gtk_list_store_append (store, &iter);
|
||||
/* ignore everything by default */
|
||||
gtk_list_store_set (store, &iter, 0, mask, 1, TRUE, 2, TRUE, 3, TRUE,
|
||||
4, TRUE, 5, TRUE, 6, TRUE, 7, FALSE, -1);
|
||||
/* make sure the new row is visible and selected */
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
|
||||
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
|
||||
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_clear_entry_clicked (GtkWidget * wid, gpointer unused)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
char *mask;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
|
||||
{
|
||||
/* remove from ignore_list */
|
||||
do
|
||||
{
|
||||
mask = NULL;
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, MASK_COLUMN, &mask, -1);
|
||||
ignore_del (mask, NULL);
|
||||
g_free (mask);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
|
||||
|
||||
/* remove from GUI */
|
||||
gtk_list_store_clear (store);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_new_entry_clicked (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
fe_get_str (_("Enter mask to ignore:"), "nick!userid@host.com",
|
||||
ignore_store_new, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
close_ignore_gui_callback ()
|
||||
{
|
||||
ignore_save ();
|
||||
ignorewin = 0;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
ignore_stats_entry (GtkWidget * box, char *label, int value)
|
||||
{
|
||||
GtkWidget *wid;
|
||||
char buf[16];
|
||||
|
||||
sprintf (buf, "%d", value);
|
||||
gtkutil_label_new (label, box);
|
||||
wid = gtkutil_entry_new (16, box, 0, 0);
|
||||
gtk_widget_set_size_request (wid, 30, -1);
|
||||
gtk_editable_set_editable (GTK_EDITABLE (wid), FALSE);
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (wid), FALSE);
|
||||
gtk_entry_set_text (GTK_ENTRY (wid), buf);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
void
|
||||
ignore_gui_open ()
|
||||
{
|
||||
GtkWidget *vbox, *box, *stat_box, *frame;
|
||||
GtkWidget *view;
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
GSList *temp = ignore_list;
|
||||
char *mask;
|
||||
gboolean private, chan, notice, ctcp, dcc, invite, unignore;
|
||||
|
||||
if (ignorewin)
|
||||
{
|
||||
mg_bring_tofront (ignorewin);
|
||||
return;
|
||||
}
|
||||
|
||||
ignorewin =
|
||||
mg_create_generic_tab ("IgnoreList", _("XChat: Ignore list"),
|
||||
FALSE, TRUE, close_ignore_gui_callback,
|
||||
NULL, 600, 256, &vbox, 0);
|
||||
|
||||
view = ignore_treeview_new (vbox);
|
||||
g_object_set_data (G_OBJECT (ignorewin), "view", view);
|
||||
|
||||
frame = gtk_frame_new (_("Ignore Stats:"));
|
||||
gtk_widget_show (frame);
|
||||
|
||||
stat_box = gtk_hbox_new (0, 2);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (stat_box), 6);
|
||||
gtk_container_add (GTK_CONTAINER (frame), stat_box);
|
||||
gtk_widget_show (stat_box);
|
||||
|
||||
num_chan = ignore_stats_entry (stat_box, _("Channel:"), ignored_chan);
|
||||
num_priv = ignore_stats_entry (stat_box, _("Private:"), ignored_priv);
|
||||
num_noti = ignore_stats_entry (stat_box, _("Notice:"), ignored_noti);
|
||||
num_ctcp = ignore_stats_entry (stat_box, _("CTCP:"), ignored_ctcp);
|
||||
num_invi = ignore_stats_entry (stat_box, _("Invite:"), ignored_invi);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, 5);
|
||||
|
||||
box = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
||||
gtk_widget_show (box);
|
||||
|
||||
gtkutil_button (box, GTK_STOCK_NEW, 0, ignore_new_entry_clicked, 0,
|
||||
_("Add..."));
|
||||
gtkutil_button (box, GTK_STOCK_DELETE, 0, ignore_delete_entry_clicked,
|
||||
0, _("Delete"));
|
||||
gtkutil_button (box, GTK_STOCK_CLEAR, 0, ignore_clear_entry_clicked,
|
||||
0, _("Clear"));
|
||||
|
||||
store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
|
||||
|
||||
while (temp)
|
||||
{
|
||||
struct ignore *ignore = temp->data;
|
||||
|
||||
mask = ignore->mask;
|
||||
chan = (ignore->type & IG_CHAN);
|
||||
private = (ignore->type & IG_PRIV);
|
||||
notice = (ignore->type & IG_NOTI);
|
||||
ctcp = (ignore->type & IG_CTCP);
|
||||
dcc = (ignore->type & IG_DCC);
|
||||
invite = (ignore->type & IG_INVI);
|
||||
unignore = (ignore->type & IG_UNIG);
|
||||
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
MASK_COLUMN, mask,
|
||||
CHAN_COLUMN, chan,
|
||||
PRIV_COLUMN, private,
|
||||
NOTICE_COLUMN, notice,
|
||||
CTCP_COLUMN, ctcp,
|
||||
DCC_COLUMN, dcc,
|
||||
INVITE_COLUMN, invite,
|
||||
UNIGNORE_COLUMN, unignore,
|
||||
-1);
|
||||
|
||||
temp = temp->next;
|
||||
}
|
||||
gtk_widget_show (ignorewin);
|
||||
}
|
||||
|
||||
void
|
||||
fe_ignore_update (int level)
|
||||
{
|
||||
/* some ignores have changed via /ignore, we should update
|
||||
the gui now */
|
||||
/* level 1 = the list only. */
|
||||
/* level 2 = the numbers only. */
|
||||
/* for now, ignore level 1, since the ignore GUI isn't realtime,
|
||||
only saved when you click OK */
|
||||
char buf[16];
|
||||
|
||||
if (level == 2 && ignorewin)
|
||||
{
|
||||
sprintf (buf, "%d", ignored_ctcp);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_ctcp), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_noti);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_noti), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_chan);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_chan), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_invi);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_invi), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_priv);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_priv), buf);
|
||||
}
|
||||
}
|
||||
257
src/fe-gtk/joind.c
Normal file
257
src/fe-gtk/joind.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/* Copyright (c) 2005 Peter Zelezny
|
||||
All Rights Reserved.
|
||||
|
||||
joind.c - The Join Dialog.
|
||||
|
||||
Popups up when you connect without any autojoin channels and helps you
|
||||
to find or join a channel.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gtk/gtkbbox.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/server.h"
|
||||
#include "../common/fe.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "chanlist.h"
|
||||
|
||||
|
||||
static void
|
||||
joind_radio2_cb (GtkWidget *radio, server *serv)
|
||||
{
|
||||
if (GTK_TOGGLE_BUTTON (radio)->active)
|
||||
{
|
||||
gtk_widget_grab_focus (serv->gui->joind_entry);
|
||||
gtk_editable_set_position (GTK_EDITABLE (serv->gui->joind_entry), 999);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
joind_entryenter_cb (GtkWidget *entry, GtkWidget *ok)
|
||||
{
|
||||
gtk_widget_grab_focus (ok);
|
||||
}
|
||||
|
||||
static void
|
||||
joind_entryfocus_cb (GtkWidget *entry, GdkEventFocus *event, server *serv)
|
||||
{
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (serv->gui->joind_radio2), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
joind_destroy_cb (GtkWidget *win, server *serv)
|
||||
{
|
||||
if (is_server (serv))
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
joind_ok_cb (GtkWidget *ok, server *serv)
|
||||
{
|
||||
if (!is_server (serv))
|
||||
{
|
||||
gtk_widget_destroy (gtk_widget_get_toplevel (ok));
|
||||
return;
|
||||
}
|
||||
|
||||
/* do nothing */
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_radio1)->active)
|
||||
goto xit;
|
||||
|
||||
/* join specific channel */
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_radio2)->active)
|
||||
{
|
||||
char *text = GTK_ENTRY (serv->gui->joind_entry)->text;
|
||||
if (strlen (text) < 2)
|
||||
{
|
||||
fe_message (_("Channel name too short, try again."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
serv->p_join (serv, text, "");
|
||||
goto xit;
|
||||
}
|
||||
|
||||
/* channel list */
|
||||
chanlist_opengui (serv, TRUE);
|
||||
|
||||
xit:
|
||||
prefs.gui_join_dialog = 0;
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_check)->active)
|
||||
prefs.gui_join_dialog = 1;
|
||||
|
||||
gtk_widget_destroy (serv->gui->joind_win);
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
joind_show_dialog (server *serv)
|
||||
{
|
||||
GtkWidget *dialog1;
|
||||
GtkWidget *dialog_vbox1;
|
||||
GtkWidget *vbox1;
|
||||
GtkWidget *hbox1;
|
||||
GtkWidget *image1;
|
||||
GtkWidget *vbox2;
|
||||
GtkWidget *label;
|
||||
GtkWidget *radiobutton1;
|
||||
GtkWidget *radiobutton2;
|
||||
GtkWidget *radiobutton3;
|
||||
GSList *radiobutton1_group;
|
||||
GtkWidget *hbox2;
|
||||
GtkWidget *entry1;
|
||||
GtkWidget *checkbutton1;
|
||||
GtkWidget *dialog_action_area1;
|
||||
GtkWidget *okbutton1;
|
||||
char buf[256];
|
||||
char buf2[256];
|
||||
|
||||
serv->gui->joind_win = dialog1 = gtk_dialog_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (dialog1), _("XChat: Connection Complete"));
|
||||
gtk_window_set_type_hint (GTK_WINDOW (dialog1), GDK_WINDOW_TYPE_HINT_DIALOG);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog1), GTK_WIN_POS_MOUSE);
|
||||
|
||||
dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
|
||||
gtk_widget_show (dialog_vbox1);
|
||||
|
||||
vbox1 = gtk_vbox_new (FALSE, 0);
|
||||
gtk_widget_show (vbox1);
|
||||
gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1, TRUE, TRUE, 0);
|
||||
|
||||
hbox1 = gtk_hbox_new (FALSE, 0);
|
||||
gtk_widget_show (hbox1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
|
||||
|
||||
image1 = gtk_image_new_from_stock ("gtk-yes", GTK_ICON_SIZE_DIALOG);
|
||||
gtk_widget_show (image1);
|
||||
gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, TRUE, 24);
|
||||
gtk_misc_set_alignment (GTK_MISC (image1), 0.5, 0.06);
|
||||
|
||||
vbox2 = gtk_vbox_new (FALSE, 10);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox2), 6);
|
||||
gtk_widget_show (vbox2);
|
||||
gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
|
||||
|
||||
snprintf (buf2, sizeof (buf2), _("Connection to %s complete."),
|
||||
server_get_network (serv, TRUE));
|
||||
snprintf (buf, sizeof (buf), "\n<b>%s</b>", buf2);
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
label = gtk_label_new (_("In the Server-List window, no channel (chat room) has been entered to be automatically joined for this network."));
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
GTK_LABEL (label)->wrap = TRUE;
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
label = gtk_label_new (_("What would you like to do next?"));
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
serv->gui->joind_radio1 = radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("_Nothing, I'll join a channel later."));
|
||||
gtk_widget_show (radiobutton1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), radiobutton1, FALSE, FALSE, 0);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1));
|
||||
|
||||
hbox2 = gtk_hbox_new (FALSE, 0);
|
||||
gtk_widget_show (hbox2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
|
||||
|
||||
serv->gui->joind_radio2 = radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("_Join this channel:"));
|
||||
gtk_widget_show (radiobutton2);
|
||||
gtk_box_pack_start (GTK_BOX (hbox2), radiobutton2, FALSE, FALSE, 0);
|
||||
gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2));
|
||||
|
||||
serv->gui->joind_entry = entry1 = gtk_entry_new ();
|
||||
gtk_entry_set_text (GTK_ENTRY (entry1), "#");
|
||||
gtk_widget_show (entry1);
|
||||
gtk_box_pack_start (GTK_BOX (hbox2), entry1, TRUE, TRUE, 8);
|
||||
|
||||
snprintf (buf, sizeof (buf), "<small> %s</small>",
|
||||
_("If you know the name of the channel you want to join, enter it here."));
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("O_pen the Channel-List window."));
|
||||
gtk_widget_show (radiobutton3);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), radiobutton3, FALSE, FALSE, 0);
|
||||
gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3));
|
||||
|
||||
snprintf (buf, sizeof (buf), "<small> %s</small>",
|
||||
_("Retrieving the Channel-List may take a minute or two."));
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
serv->gui->joind_check = checkbutton1 = gtk_check_button_new_with_mnemonic (_("_Always show this dialog after connecting."));
|
||||
if (prefs.gui_join_dialog)
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton1), TRUE);
|
||||
gtk_widget_show (checkbutton1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), checkbutton1, FALSE, FALSE, 0);
|
||||
|
||||
dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
|
||||
gtk_widget_show (dialog_action_area1);
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END);
|
||||
|
||||
okbutton1 = gtk_button_new_from_stock ("gtk-ok");
|
||||
gtk_widget_show (okbutton1);
|
||||
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog1)->action_area), okbutton1, FALSE, TRUE, 0);
|
||||
GTK_WIDGET_SET_FLAGS (okbutton1, GTK_CAN_DEFAULT);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog1), "destroy",
|
||||
G_CALLBACK (joind_destroy_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "focus_in_event",
|
||||
G_CALLBACK (joind_entryfocus_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "activate",
|
||||
G_CALLBACK (joind_entryenter_cb), okbutton1);
|
||||
g_signal_connect (G_OBJECT (radiobutton2), "toggled",
|
||||
G_CALLBACK (joind_radio2_cb), serv);
|
||||
g_signal_connect (G_OBJECT (okbutton1), "clicked",
|
||||
G_CALLBACK (joind_ok_cb), serv);
|
||||
|
||||
gtk_widget_grab_focus (okbutton1);
|
||||
gtk_widget_show_all (dialog1);
|
||||
}
|
||||
|
||||
void
|
||||
joind_open (server *serv)
|
||||
{
|
||||
if (prefs.gui_join_dialog)
|
||||
joind_show_dialog (serv);
|
||||
}
|
||||
|
||||
void
|
||||
joind_close (server *serv)
|
||||
{
|
||||
if (serv->gui->joind_win)
|
||||
{
|
||||
gtk_widget_destroy (serv->gui->joind_win);
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
}
|
||||
2
src/fe-gtk/joind.h
Normal file
2
src/fe-gtk/joind.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void joind_open (server *serv);
|
||||
void joind_close (server *serv);
|
||||
3811
src/fe-gtk/maingui.c
Normal file
3811
src/fe-gtk/maingui.c
Normal file
File diff suppressed because it is too large
Load Diff
33
src/fe-gtk/maingui.h
Normal file
33
src/fe-gtk/maingui.h
Normal file
@@ -0,0 +1,33 @@
|
||||
extern GtkStyle *input_style;
|
||||
extern GtkWidget *parent_window;
|
||||
|
||||
void mg_changui_new (session *sess, restore_gui *res, int tab, int focus);
|
||||
void mg_update_xtext (GtkWidget *wid);
|
||||
void mg_open_quit_dialog (gboolean minimize_button);
|
||||
void mg_switch_page (int relative, int num);
|
||||
void mg_move_tab (session *, int delta);
|
||||
void mg_move_tab_family (session *, int delta);
|
||||
void mg_bring_tofront (GtkWidget *vbox);
|
||||
void mg_bring_tofront_sess (session *sess);
|
||||
void mg_decide_userlist (session *sess, gboolean switch_to_current);
|
||||
void mg_set_topic_tip (session *sess);
|
||||
GtkWidget *mg_create_generic_tab (char *name, char *title, int force_toplevel, int link_buttons, void *close_callback, void *userdata, int width, int height, GtkWidget **vbox_ret, void *family);
|
||||
void mg_set_title (GtkWidget *button, char *title);
|
||||
void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away);
|
||||
void mg_apply_setup (void);
|
||||
void mg_close_sess (session *);
|
||||
void mg_tab_close (session *sess);
|
||||
void mg_detach (session *sess, int mode);
|
||||
void mg_progressbar_create (session_gui *gui);
|
||||
void mg_progressbar_destroy (session_gui *gui);
|
||||
void mg_dnd_drop_file (session *sess, char *target, char *uri);
|
||||
void mg_change_layout (int type);
|
||||
void mg_update_meters (session_gui *);
|
||||
void mg_inputbox_cb (GtkWidget *igad, session_gui *gui);
|
||||
void mg_create_icon_item (char *label, char *stock, GtkWidget *menu, void *callback, void *userdata);
|
||||
GtkWidget *mg_submenu (GtkWidget *menu, char *text);
|
||||
/* DND */
|
||||
gboolean mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata);
|
||||
void mg_drag_end_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata);
|
||||
gboolean mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data);
|
||||
gboolean mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user