home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Divers
/
yagirc-0.51.tar.gz
/
yagirc-0.51.tar
/
yagirc-0.51
/
irc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-05-11
|
32KB
|
1,271 lines
/*
irc.c : Main IRC related file - all kinds of stuff
Copyright (C) 1998 Timo Sirainen
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.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include "os.h"
#include "gui.h"
#include "irc.h"
#include "dcc.h"
#include "network.h"
#include "commands.h"
#include "events.h"
#include "txt.h"
#include "script.h"
#include "params.h"
#include "misc.h"
char *ret_texts[] =
{
"", /* error */
"", /* ok */
_("Not enough parameters given\n"),
_("Not connected to IRC server yet\n"),
_("Not joined to any channels yet\n"),
_("Error: getsockname() failed\n"),
_("Error: listen() failed\n"),
_("Multiple matches found, be more specific\n"),
};
typedef int (*OUT_CALL_FUNC)(char *);
typedef void (*IN_CALL_FUNC)(char *);
struct OUT_CMD_STRUCT
{
char *name;
OUT_CALL_FUNC func;
} OUT_CMD_STRUCT;
struct IN_CMD_STRUCT
{
char *name;
IN_CALL_FUNC func;
} IN_CMD_STRUCT;
static struct OUT_CMD_STRUCT out_cmds[] =
{
{ "SERVER", (OUT_CALL_FUNC) irccmd_server },
{ "SERVERS", (OUT_CALL_FUNC) irccmd_servers },
{ "DISCONNECT", (OUT_CALL_FUNC) irccmd_disconnect },
{ "CTCP", (OUT_CALL_FUNC) irccmd_ctcp },
{ "ME", (OUT_CALL_FUNC) irccmd_me },
{ "DCC", (OUT_CALL_FUNC) irccmd_dcc },
{ "WINDOW", (OUT_CALL_FUNC) irccmd_window },
{ "LOAD", (OUT_CALL_FUNC) irccmd_load },
{ "UNLOAD", (OUT_CALL_FUNC) irccmd_unload },
{ "QUOTE", (OUT_CALL_FUNC) irccmd_quote },
{ "ISON", (OUT_CALL_FUNC) irccmd_ison },
{ "MSG", (OUT_CALL_FUNC) irccmd_msg },
{ "NOTICE", (OUT_CALL_FUNC) irccmd_notice },
{ "QUIT", (OUT_CALL_FUNC) irccmd_quit },
{ "JOIN", (OUT_CALL_FUNC) irccmd_join },
{ "WHOIS", (OUT_CALL_FUNC) irccmd_whois },
{ "QUERY", (OUT_CALL_FUNC) irccmd_query },
{ "UNQUERY", (OUT_CALL_FUNC) irccmd_unquery },
{ "PART", (OUT_CALL_FUNC) irccmd_part },
{ "MODE", (OUT_CALL_FUNC) irccmd_mode },
{ NULL, NULL }
};
static struct IN_CMD_STRUCT in_cmds[] =
{
{ "PRIVMSG", (IN_CALL_FUNC) eirc_privmsg },
{ "NOTICE", (IN_CALL_FUNC) eirc_notice },
{ "NICK", (IN_CALL_FUNC) eirc_nick },
{ "QUIT", (IN_CALL_FUNC) eirc_quit },
{ "JOIN", (IN_CALL_FUNC) eirc_join },
{ "PART", (IN_CALL_FUNC) eirc_part },
{ "PING", (IN_CALL_FUNC) eirc_ping },
{ "PONG", (IN_CALL_FUNC) eirc_pong },
{ "MODE", (IN_CALL_FUNC) eirc_mode },
{ "KICK", (IN_CALL_FUNC) eirc_kick },
{ "INVITE", (IN_CALL_FUNC) eirc_invite },
{ "TOPIC", (IN_CALL_FUNC) eirc_new_topic },
{ "ERROR", (IN_CALL_FUNC) eirc_error },
{ NULL, NULL }
};
/* callback function list for numeric irc server replies */
#define MAX_NUMCMDS 600
static IN_CALL_FUNC in_numcmds[MAX_NUMCMDS];
static int ircwindows;
GList *winlist; /* List of windows */
GList *servlist; /* List of servers */
GList *scriptlist; /* List of scripts loaded */
GList *aliases; /* List of command aliases */
GList *ignores; /* Ignore list */
GList *notifies; /* Notify list */
WINDOW_REC *curwin; /* current window */
SERVER_REC *cserver; /* current server */
/* Write text to window - convert color codes */
void drawtext(WINDOW_REC *win, int type, char *str, ...)
{
va_list args;
char *out;
int pros, newln;
g_return_if_fail(win != NULL);
g_return_if_fail(str != NULL);
va_start(args, str);
out = win->textbuf+win->textbuflen; pros = 0; newln = 0;
switch (type)
{
case TXT_TYPE_NOTICE:
out += sprintf(out, "\004%c - ", 11);
break;
case TXT_TYPE_ERROR:
out += sprintf(out, "\004%c - ", 12);
break;
case TXT_TYPE_SERVER_TEXT:
out += sprintf(out, "\004%c - ", 13);
break;
case TXT_TYPE_DCC:
out += sprintf(out, "\004%c", 14);
break;
}
for (; *str != '\0'; str++)
{
if (*str == '\n')
{
newln = 1;
break;
}
if (*str != '%')
{
*out++ = *str;
continue;
}
if (*++str == '\0') break;
switch (*str)
{
/* standard parameters */
case 's':
{
char *s = (char *) va_arg(args, char *);
if (s)
out += sprintf(out, "%s", s);
break;
}
case 'd':
{
int d = (int) va_arg(args, int);
out += sprintf(out, "%d", d);
break;
}
case 'f':
{
double f = (double) va_arg(args, double);
out += sprintf(out, "%0.2f", f);
break;
}
case 'c':
{
char c = (char )va_arg(args, int);
*out++ = c;
break;
}
case 'u':
{
unsigned int d = (unsigned int) va_arg(args, unsigned int);
out += sprintf(out, "%u", d);
break;
}
case 'l':
{
unsigned long d = (unsigned long) va_arg(args, unsigned long);
if (*++str != 'd' && *str != 'u')
{
out += sprintf(out, "%ld", d);
str--;
}
else
{
if (*str == 'd')
out += sprintf(out, "%ld", d);
else
out += sprintf(out, "%lu", d);
}
break;
}
/* colors, to reset color back to default use %n */
case 'K':
*out++ = 3;
*out++ = BBLACK;
break;
case 'k':
*out++ = 3;
*out++ = BLACK;
break;
case 'B':
*out++ = 3;
*out++ = BBLUE;
break;
case 'b':
*out++ = 3;
*out++ = BLUE;
break;
case 'G':
*out++ = 3;
*out++ = BGREEN;
break;
case 'g':
*out++ = 3;
*out++ = GREEN;
break;
case 'A':
*out++ = 3;
*out++ = BCYAN;
break;
case 'a':
*out++ = 3;
*out++ = CYAN;
break;
case 'R':
*out++ = 3;
*out++ = BRED;
break;
case 'r':
*out++ = 3;
*out++ = RED;
break;
case 'M':
*out++ = 3;
*out++ = BMAGENTA;
break;
case 'm':
*out++ = 3;
*out++ = MAGENTA;
break;
case 'Y':
*out++ = 3;
*out++ = BYELLOW;
break;
case 'y':
*out++ = 3;
*out++ = YELLOW;
break;
case 'W':
*out++ = 3;
*out++ = BWHITE;
break;
case 'w':
*out++ = 3;
*out++ = WHITE;
break;
case 'n':
*out++ = 4;
*out++ = 15;
break;
/* others */
case '!':
/* Italic on/off */
*out++ = 31;
break;
case '_':
/* BOLD on/off */
*out++ = 2;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
*out++ = 4;
*out++ = (*str-'0')+1;
break;
case '%':
*out++ = '%';
break;
default:
*out++ = '%';
*out++ = *str;
break;
}
}
va_end(args);
*out = '\0';
win->textbuflen = (int) (out-win->textbuf);
if (newln)
{
win->textbuflen = 0;
win->drawfunc(win->textbuf, win->drawfuncdata);
}
if (curwin != win && win->new_data != 1)
{
win->new_data = 1;
gui_update_statusbar(NULL);
}
}
/* clear buffer */
void cleartextbuf(WINDOW_REC *win)
{
win->textbuflen = 0;
}
/* Destroy IRC channel record */
void irc_chan_free(CHAN_REC *chan)
{
g_return_if_fail(chan != NULL);
g_free(chan->name);
if (chan->topic != NULL) g_free(chan->topic);
if (chan->nicks != NULL)
{
GList *tmp;
for (tmp = g_list_first(chan->nicks); tmp != NULL; tmp = tmp->next)
g_free(tmp->data);
g_list_free(chan->nicks);
}
if (chan->window != NULL && chan->window->chanlist != NULL)
chan->window->chanlist = g_list_remove(chan->window->chanlist, chan);
g_free(chan);
}
/* Find server record */
SERVER_REC *irc_get_server(int handle)
{
GList *tmp;
for (tmp = g_list_first(servlist); tmp != NULL; tmp = tmp->next)
{
SERVER_REC *server;
server = (SERVER_REC *) tmp->data;
if (server->handle == handle) return server;
}
return NULL;
}
/* Find channel record of chan in server serv, if serv==NULL, find from all servers */
CHAN_REC *channel_joined(SERVER_REC *serv, char *chan)
{
GList *link, *winl;
char *ptr;
g_return_val_if_fail(chan != NULL, NULL);
ptr = strchr(chan, ' ');
if (ptr != NULL)
{
int tag;
*ptr = '\0';
if (sscanf(ptr+1, "%d", &tag) == 1)
{
serv = irc_get_server(tag);
if (serv == NULL) return NULL; /* no such server.. */
}
}
if (*chan == '=')
serv = NULL; /* Trying to find DCC query - server doesn't matter */
for (winl = g_list_first(winlist); winl != NULL; winl = winl->next)
{
WINDOW_REC *win;
win = (WINDOW_REC *) winl->data;
for (link = g_list_first(win->chanlist); link != NULL; link = link->next)
{
CHAN_REC *ch;
ch = (CHAN_REC *) link->data;
if ((serv == NULL || serv == ch->server) &&
strcasecmp(chan, ch->name) == 0)
{
/* found one! */
if (ptr != NULL) *ptr = ' ';
return ch;
}
}
}
if (ptr != NULL) *ptr = ' ';
return NULL;
}
/* IRC nick comparision for sort functions */
int irc_nicks_compare(char *p1, char *p2)
{
if (p1 == NULL) return -1;
if (p2 == NULL) return 1;
if (*p1 == '@' && *p2 != '@') return -1;
if (*p2 == '@' && *p1 != '@') return 1;
return strcasecmp(p1, p2);
}
/* Parse command line sent by server */
static int irc_parse_line(SERVER_REC *serv, char *str)
{
char *cmd;
int cmdnum, n;
WINDOW_REC *win;
g_return_val_if_fail(serv != NULL, 2);
g_return_val_if_fail(str != NULL, 2);
if ((curwin->curchan != NULL && curwin->curchan->server == serv) ||
curwin->defserv == serv)
{
win = curwin;
}
else
{
if (serv->defwin != NULL)
win = serv->defwin;
else
{
/* server has no default window.. */
win = curwin;
drawtext(curwin, TXT_TYPE_DEFAULT, "[%s(%d)] ", serv->name, serv->handle);
}
}
eserver = serv; edefwin = win;
esendnick = NULL;
esendaddr = NULL;
if (*str == ':')
{
/* read prefix.. */
esendnick = ++str;
while (*str != '\0' && *str != ' ')
{
if (*str == '!')
{
*str = '\0';
esendaddr = str+1;
}
str++;
}
if (*str == ' ')
{
*str++ = '\0';
while (*str == ' ') str++;
}
}
if (*str == '\0') return 1; /* empty line */
#ifdef USE_SCRIPT
script_event(eserver, NULL, NULL, "SERVERMSG", str);
#endif
/* get command.. */
cmd = str;
while (*str != '\0' && *str != ' ') str++;
if (*str == ' ') *str++ = '\0';
while (*str == ' ') str++;
#ifdef USE_SCRIPT
/* Call script events */
if (!script_event(eserver, esendnick == NULL ? "" : esendnick, esendaddr, cmd, str))
{
/* script handled this event all by itself, don't call build in events */
return 1;
}
#endif
cmdnum = -1;
if (isdigit(*cmd))
{
/* numeric command */
if (sscanf(cmd, "%d", &cmdnum) != 1) cmdnum = -1;
if (cmdnum < MAX_NUMCMDS && in_numcmds[cmdnum] != NULL)
{
in_numcmds[cmdnum](str);
cleartextbuf(curwin);
return 1;
}
/* skip targets */
while (*str != '\0' && *str != ' ') str++;
while (*str == ' ') str++;
/*drawtext(edefwin, "%s ", cmd);*/
}
else
{
/* named command.. */
for (n = 0; in_cmds[n].name != NULL; n++)
{
if (strcasecmp(in_cmds[n].name, cmd) == 0)
{
in_cmds[n].func(str);
cleartextbuf(curwin);
return 1;
}
}
drawtext(win, TXT_TYPE_SERVER_TEXT, "(%s)", cmd);
}
if (*str == ':')
str++;
else
{
char *ptr;
ptr = strstr(str, " :");
if (ptr != NULL)
{
*ptr = '\0';
drawtext(win, TXT_TYPE_SERVER_TEXT, "%s %s\n", str, ptr+2);
return 1;
}
}
drawtext(win, TXT_TYPE_SERVER_TEXT, "%s\n", str);
return 1;
}
/* Read line from server */
static int irc_receive_line(SERVER_REC *serv, char *str)
{
int ret;
g_return_val_if_fail(serv != NULL, -1);
g_return_val_if_fail(str != NULL, -1);
ret = read_line(1, serv->handle, str, serv->buf, sizeof(serv->buf), &serv->bufpos);
if (ret == -1)
{
/* connection lost */
drawtext(curwin->defserv == serv ? curwin : serv->defwin,
TXT_TYPE_SERVER_TEXT, IRCTXT_CONNECTION_LOST, serv->name);
irc_server_disconnect(serv);
}
return ret;
}
/* Send command to IRC server */
int irc_send_cmd(SERVER_REC *serv, char *cmd)
{
char str[512];
int len;
g_return_val_if_fail(cmd != NULL, 2);
if (serv == NULL || !serv->connected)
{
drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_NOT_CONNECTED);
return 0;
}
/* just check that we don't send any longer commands than 512 bytes.. */
len = 0;
while (*cmd != '\0' && len < 510)
{
str[len] = *cmd++;
len++;
}
str[len++] = 13; str[len] = 10;
return net_transmit(serv->handle, str, len);
}
/* Check if nick is in ignore list */
int irc_is_ignored(char *nick, int type)
{
GList *tmp;
for (tmp = g_list_first(ignores); tmp != NULL; tmp = tmp->next)
{
char *data, *ptr;
data = g_strdup((char *) tmp->data);
ptr = strchr(data, ' ');
if (ptr == NULL)
{
g_free(data);
continue; /* illegal ignore list entry */
}
*ptr++ = '\0';
if (strcasecmp(data, nick) == 0)
{
strupr(ptr);
if (strstr(ptr, "ALL") != NULL)
{
g_free(data);
return 1;
}
switch (type)
{
case IGNORE_PUBLIC:
if (strstr(ptr, "PUBLIC") != NULL)
{
g_free(data);
return 1;
}
break;
case IGNORE_PRIVATE:
if (strstr(ptr, "PRIVATE") != NULL)
{
g_free(data);
return 1;
}
break;
case IGNORE_CTCP:
if (strstr(ptr, "CTCP") != NULL)
{
g_free(data);
return 1;
}
break;
}
g_free(data);
return 0;
}
g_free(data);
}
return 0;
}
/* strip all extra characted from nick */
static char *strip_nick(char *nick)
{
char *ptr;
int n;
g_return_val_if_fail(nick != NULL, NULL);
for (n = 0, ptr = nick; *ptr != '\0'; ptr++)
if (isalnum(*ptr)) nick[n++] = toupper(*ptr);
nick[n] = '\0';
return nick;
}
/* find best matching nick for nick completion */
static char *find_nick(GList *list, char *pnick)
{
char *tmp;
int pnicklen;
char *best, *besttmp;
int bestlen;
g_return_val_if_fail(pnick != NULL, NULL);
if (list == NULL) return NULL;
besttmp = best = NULL; bestlen = 0;
pnick = strip_nick(g_strdup(pnick));
if (*pnick == '\0')
{
g_free(pnick);
return NULL;
}
pnicklen = strlen(pnick);
list = g_list_first(list);
while (list != NULL)
{
tmp = g_strdup((char *) list->data);
strip_nick(tmp);
if (strncmp(tmp, pnick, pnicklen) == 0)
{
if (((strlen(tmp) > bestlen ||
(strlen(tmp) == bestlen && strstr(besttmp, "BOT") != NULL))) &&
curwin->defserv != NULL && strcasecmp(list->data, curwin->defserv->nick) != 0)
{
best = (char *) list->data;
if (besttmp != NULL) g_free(besttmp);
besttmp = tmp;
bestlen = strlen(tmp);
if (bestlen == pnicklen)
{
/* identical match */
break;
}
}
}
else
{
g_free(tmp);
}
list = list->next;
}
if (besttmp != NULL) g_free(besttmp);
g_free(pnick);
if (best != NULL && isircflag(*best)) best++; /* skip @ or + */
return best;
}
/* Write text to channel */
static int irc_write_text(CHAN_REC *chan, char *text)
{
char str[512], *ptr;
int len;
g_return_val_if_fail(text != NULL, 0);
if (chan == NULL)
{
drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_NOT_JOINED);
return 0;
}
if (*text == '\0') return 1;
/* first parameter: channel name */
len = sprintf(str, "%s ", chan->name);
/* check for nick completion */
for (ptr = text; *ptr != '\0' && *ptr != ' '; ptr++)
{
if (*ptr == ':')
{
char *nick;
if (ptr == text) break;
*ptr = '\0';
nick = find_nick(chan->nicks, text);
if (nick == NULL)
{
/* No such nick.. */
*ptr = ':';
break;
}
text = ptr+1;
len += sprintf(str+len, "%s:", nick);
break;
}
}
strncpy(str+len, text, 510-len); str[510] = '\0';
irccmd_msg(str);
return 1;
}
/* Parse outgoing line */
int irc_parse_outgoing(CHAN_REC *chan, char *line)
{
SERVER_REC *serv;
char str[512], *cmd, *ptr;
int n, ret;
GList *tmp;
g_return_val_if_fail(line != NULL, 0);
if (*line != '/')
{
/* write text to channel */
irc_write_text(chan, line);
return 1;
}
/* must be some command.. */
strncpy(str, line+1, sizeof(str)-1); str[sizeof(str)-1] = '\0';
ptr = strchr(str, ' ');
if (ptr != NULL) *ptr++ = '\0'; else ptr = "";
cmd = str;
if (*cmd == '/')
{
/* //command overrides any aliases/scripts */
cmd++;
}
else
{
/* check if there's an alias for command */
for (tmp = g_list_first(aliases); tmp != NULL; tmp = tmp->next)
{
ALIAS_REC *al;
al = (ALIAS_REC *) tmp->data;
if (strcasecmp(al->alias, str) == 0)
{
cmd = al->cmd;
break;
}
}
#ifdef USE_SCRIPT
if (!script_event(cserver, NULL, NULL, cmd, ptr)) return 1;
#endif
}
/* check if command is found */
ret = RET_ERROR;
for (n = 0; out_cmds[n].name != NULL; n++)
{
if (strcasecmp(out_cmds[n].name, cmd) == 0)
{
ret = out_cmds[n].func(ptr);
break;
}
}
if (out_cmds[n].name == NULL)
{
/* command not found - just send it like it was typed */
serv = chan == NULL ? curwin->defserv : chan->server;
if (serv != NULL) irc_send_cmd(serv, line+1);
return 1;
}
if (ret == RET_ERR_PARAM)
{
/* internal error! */
drawtext(chan != NULL ? chan->window : curwin, TXT_TYPE_ERROR,
"%_Internal error%_: g_return_val_if_fail() function failed!\n");
}
else if (ret != RET_OK && ret != RET_ERROR)
{
/* print error message */
drawtext(chan != NULL ? chan->window : curwin, TXT_TYPE_ERROR, ret_texts[ret]);
}
return 1;
}
/* Initialize new IRC window */
WINDOW_REC *irc_window_new(SERVER_REC *server, WINDOW_REC *win)
{
WINDOW_REC *w;
w = (WINDOW_REC *) g_malloc(sizeof(WINDOW_REC));
memset(w, 0, sizeof(WINDOW_REC));
w->num = ++ircwindows;
w->textbuflen = 0;
w->chanlist = NULL;
w->curchan = NULL;
w->defserv = NULL;
w->defserv = server;
gui_window_init(w, win);
winlist = g_list_append(winlist, w);
irc_window_focus(w);
return w;
}
/* Select new "default server window" for server messages */
void irc_select_new_server_window(WINDOW_REC *win)
{
GList *tmp;
/* closing the default window for server .. need new one.. */
for (tmp = g_list_first(winlist); tmp != NULL; tmp = tmp->next)
{
WINDOW_REC *w;
w = (WINDOW_REC *) tmp->data;
if (w->defserv == win->defserv && w != win)
{
/* found one! */
win->defserv->defwin = w;
break;
}
}
if (tmp == NULL)
{
/* none found.. */
win->defserv->defwin = NULL;
}
}
/* Close IRC window, sends part messages for every channel in window */
void irc_window_close(WINDOW_REC *win)
{
GList *tmp;
g_return_if_fail(win != NULL);
if (win->defserv != NULL && win->defserv->defwin == win)
irc_select_new_server_window(win);
/* Send PART command for every channel in this window */
while ((tmp = g_list_first(win->chanlist)) != NULL)
{
CHAN_REC *chan;
chan = (CHAN_REC *) tmp->data;
if (chan->server != NULL && chan->server->connected)
{
if (*chan->name == '#' || *chan->name == '&')
irccmd_part(chan->name);
}
gui_channel_part(chan, NULL);
irc_chan_free(chan);
}
/* renumber windows */
for (tmp = g_list_nth(winlist, win->num); tmp != NULL; tmp = tmp->next)
{
WINDOW_REC *w;
w = (WINDOW_REC *) tmp->data;
w->num--;
}
ircwindows--;
winlist = g_list_remove(winlist, win);
gui_window_deinit(win);
g_free(win);
if (winlist == NULL)
{
/* this was the last window.. */
curwin = NULL;
}
else
{
/* select new window */
tmp = g_list_first(winlist);
if (tmp->data == win) tmp = tmp->next;
if (tmp == NULL)
{
/* this was the last window anyway */
curwin = NULL;
}
else
{
/* new window found, great. */
curwin = (WINDOW_REC *) tmp->data;
gui_window_select(curwin);
}
}
}
/* Window got focus */
void irc_window_focus(WINDOW_REC *win)
{
curwin = win;
if (win == NULL)
cserver = NULL;
else
{
cserver = win->curchan != NULL ? win->curchan->server : win->defserv;
gui_window_update(win);
if (win->new_data)
{
GList *tmp;
/* remove hilight from all channels in this window */
for (tmp = g_list_first(win->chanlist); tmp != NULL; tmp = tmp->next)
{
CHAN_REC *chan;
chan = (CHAN_REC *) tmp->data;
if (chan->new_data)
{
chan->new_data = 0;
gui_channel_dehilight(chan);
}
}
win->new_data = 0;
gui_update_statusbar(NULL);
}
}
}
void irc_init(void)
{
winlist = NULL; servlist = NULL; scriptlist = NULL;
ircwindows = 0;
/* set numeric commands */
memset(in_numcmds, 0, sizeof(in_numcmds));
in_numcmds[1] = (IN_CALL_FUNC) eirc_welcome;
in_numcmds[4] = (IN_CALL_FUNC) eirc_connected;
in_numcmds[303] = (IN_CALL_FUNC) eirc_ison;
in_numcmds[332] = (IN_CALL_FUNC) eirc_topic;
in_numcmds[333] = (IN_CALL_FUNC) eirc_topic_info;
in_numcmds[352] = (IN_CALL_FUNC) eirc_who;
in_numcmds[353] = (IN_CALL_FUNC) eirc_names_list;
in_numcmds[366] = (IN_CALL_FUNC) eirc_end_of_names;
in_numcmds[367] = (IN_CALL_FUNC) eirc_ban_list;
in_numcmds[368] = (IN_CALL_FUNC) eirc_end_of_banlist;
in_numcmds[405] = (IN_CALL_FUNC) eirc_too_many_channels;
in_numcmds[433] = (IN_CALL_FUNC) eirc_nick_in_use;
in_numcmds[437] = (IN_CALL_FUNC) eirc_nick_unavailable;
in_numcmds[471] = (IN_CALL_FUNC) eirc_channel_is_full;
in_numcmds[473] = (IN_CALL_FUNC) eirc_invite_only;
in_numcmds[474] = (IN_CALL_FUNC) eirc_banned;
in_numcmds[475] = (IN_CALL_FUNC) eirc_bad_channel_key;
in_numcmds[476] = (IN_CALL_FUNC) eirc_bad_channel_mask;
events_init();
dcc_init();
#ifdef USE_SCRIPT
script_init();
#endif
}
void irc_deinit(void)
{
GList *tmp;
#ifdef USE_SCRIPT
script_deinit();
#endif
while (servlist != NULL)
irc_server_disconnect((SERVER_REC *) servlist->data);
if (winlist != NULL)
{
/* close every unclosed window properly.. */
while ((tmp = g_list_first(winlist)) != NULL)
irc_window_close((WINDOW_REC *) tmp->data);
winlist = NULL;
}
events_deinit();
dcc_deinit();
}
/* Select any other channel in window except the one we are now */
int irc_select_new_channel(WINDOW_REC *window)
{
GList *tmp;
g_return_val_if_fail(window != NULL, 0);
for (tmp = g_list_first(window->chanlist); tmp != NULL; tmp = tmp->next)
if (tmp->data != window->curchan) break;
window->curchan = tmp == NULL ? NULL : tmp->data;
if (tmp == NULL)
{
/* no more channels */
return 0;
}
if (*window->curchan->name == '#' || *window->curchan->name == '&')
drawtext(window, TXT_TYPE_NOTICE, IRCTXT_TALKING_IN,
window->curchan->name);
else
drawtext(window, TXT_TYPE_NOTICE, IRCTXT_QUERYING,
window->curchan->name);
irc_window_focus(window);
return 1;
}
/* input function: handle incoming server messages */
static void irc_parse_incoming(SERVER_REC *server)
{
char str[512];
g_return_if_fail(server != NULL);
while (irc_receive_line(server, str) > 0)
irc_parse_line(server, str);
}
/* timeout function: send /ISON commands to server to check if someone in
notify list is in IRC */
static int irc_timeout_func(SERVER_REC *serv)
{
char tmp[512];
int pos;
GList *list;
g_return_val_if_fail(serv != NULL, 0);
if (serv->ison_reqs != 0)
{
/* still not received all replies to previous /ISON commands.. */
return 1;
}
list = g_list_first(notifies);
if (list == NULL) return 1; /* no-one in notify list */
pos = sprintf(tmp, "ISON :");
while (list != NULL)
{
int len;
len = strlen((char *) list->data);
if (pos+len+1 > 510)
{
irc_send_cmd(serv, tmp);
serv->ison_reqs++;
pos = sprintf(tmp, "ISON :");
}
if (tmp[pos-1] == ':')
pos += sprintf(tmp+pos, "%s", (char *) list->data);
else
pos += sprintf(tmp+pos, " %s", (char *) list->data);
list = list->next;
}
irc_send_cmd(serv, tmp);
serv->ison_reqs++;
return 1;
}
/* Connect to IRC server */
SERVER_REC *irc_server_connect(char *name, int port)
{
int handle;
SERVER_REC *serv;
char tmp[512];
g_return_val_if_fail(name != NULL, NULL);
if (*name == '\0') return NULL;
if (port == 0) port = 6667;
handle = net_connect(name, port);
if (handle == -1)
{
drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_CANT_CONNECT, name, port);
return NULL;
}
serv = (SERVER_REC *) g_malloc(sizeof(SERVER_REC));
serv->name = g_strdup(name);
serv->nick = g_strdup(default_nick);
serv->handle = handle;
serv->readtag = gui_input_add(handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) irc_parse_incoming, serv);
serv->timetag = gui_timeout_new(10000, (GUI_TIMEOUT_FUNC) irc_timeout_func, serv);
serv->bufpos = 0;
serv->connected = 1;
sprintf(tmp, "NICK %s", serv->nick);
irc_send_cmd(serv, tmp);
sprintf(tmp, "USER %s - - :%s", user_name, real_name);
irc_send_cmd(serv, tmp);
serv->connected = 0;
servlist = g_list_append(servlist, serv);
return serv;
}
/* Disconnect from IRC server */
void irc_server_disconnect(SERVER_REC *server)
{
GList *tmp, *nextwin;
#ifdef USE_SCRIPT
char *str;
#endif
g_return_if_fail(server != NULL);
if (server->handle != -1) net_disconnect(server->handle);
gui_input_remove(server->readtag);
gui_timeout_remove(server->timetag);
server->connected = 0;
for (tmp = nextwin = g_list_first(winlist); nextwin != NULL; tmp = nextwin)
{
WINDOW_REC *win;
GList *chan;
int exists;
nextwin = tmp->next;
win = (WINDOW_REC *) tmp->data;
if (win->defserv == server) win->defserv = NULL;
exists = 0;
for (chan = g_list_first(win->chanlist); chan != NULL;)
{
CHAN_REC *ch;
ch = (CHAN_REC *) chan->data;
if (ch->server == server && *ch->name != '=')
{
chan = chan->next;
if (ch == win->curchan) win->curchan = NULL;
gui_channel_part(ch, NULL);
irc_chan_free(ch);
}
else
{
chan = chan->next;
exists = 1;
}
}
if (!exists)
{
/* no channels left in this window */
if (g_list_first(winlist)->next != NULL) irc_window_close(win);
}
else
{
if (win->curchan == NULL) irc_select_new_channel(win);
}
}
irc_window_focus(curwin);
gui_connected(server, 0);
#ifdef USE_SCRIPT
str = (char *) g_malloc(strlen(server->name)+20);
sprintf(str, "%d %s", server->handle, server->name);
script_event(server, "", "", "DISCONNECTED", str);
g_free(str);
#endif
servlist = g_list_remove(servlist, server);
if (server->nick != NULL) g_free(server->nick);
g_free(server->name);
g_free(server);
}