add xchat r1489

This commit is contained in:
berkeviktor@aol.com
2011-02-24 04:14:30 +01:00
parent f16af8be94
commit 4a6ceffb98
245 changed files with 324678 additions and 0 deletions

14
src/Makefile.am Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

55
src/common/cfgfiles.h Normal file
View 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
View 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
View 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
View 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
View 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

View 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
View 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 !

View 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>

View 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);
}

View 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__ */

File diff suppressed because it is too large Load Diff

View 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
View 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;
}

View 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)

View File

@@ -0,0 +1 @@
VOID:POINTER,POINTER,UINT,UINT

View File

@@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.xchat.service
Exec=@bindir@/xchat

View 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

File diff suppressed because it is too large Load Diff

117
src/common/dcc.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

39
src/common/inbound.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

20
src/common/outbound.h Normal file
View 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
View 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 */
}

View 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

File diff suppressed because it is too large Load Diff

132
src/common/plugin.h Normal file
View 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

File diff suppressed because it is too large Load Diff

6
src/common/proto-irc.h Normal file
View 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

File diff suppressed because it is too large Load Diff

26
src/common/server.h Normal file
View 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

File diff suppressed because it is too large Load Diff

62
src/common/servlist.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

42
src/common/text.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

54
src/common/util.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &lt;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
View File

@@ -0,0 +1 @@
void menu_about (GtkWidget * wid, gpointer sess);

177
src/fe-gtk/ascii.c Normal file
View 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
View File

@@ -0,0 +1 @@
void ascii_open (void);

418
src/fe-gtk/banlist.c Normal file
View 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
View File

@@ -0,0 +1 @@
void banlist_opengui (session *sess);

943
src/fe-gtk/chanlist.c Normal file
View 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
View File

@@ -0,0 +1 @@
void chanlist_opengui (server *serv, int do_refresh);

779
src/fe-gtk/chanview-tabs.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

409
src/fe-gtk/editlist.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

197
src/fe-gtk/fe-gtk.h Normal file
View 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

File diff suppressed because it is too large Load Diff

5
src/fe-gtk/fkeys.h Normal file
View 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
View 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
View 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
View 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, &noti,
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
View 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
View File

@@ -0,0 +1,2 @@
void joind_open (server *serv);
void joind_close (server *serv);

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
View 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