summaryrefslogtreecommitdiff
path: root/src/irc/core/irc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/core/irc.c')
-rw-r--r--src/irc/core/irc.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
new file mode 100644
index 00000000..c25f32b6
--- /dev/null
+++ b/src/irc/core/irc.c
@@ -0,0 +1,440 @@
+/*
+ irc.c : irssi
+
+ Copyright (C) 1999 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "network.h"
+#include "line-split.h"
+#include "rawlog.h"
+
+#include "irc.h"
+#include "irc-server.h"
+#include "channels.h"
+#include "server-redirect.h"
+
+char *current_server_event;
+static int signal_send_command;
+static int signal_default_event;
+static int signal_server_event;
+static int signal_server_incoming;
+
+static void cmd_send(IRC_SERVER_REC *server, const char *cmd, int send_now, int immediate)
+{
+ char str[513], *ptr;
+ int len, ret;
+
+ server->cmdcount++;
+
+ if (send_now)
+ rawlog_output(server->rawlog, cmd);
+
+ /* just check that we don't send any longer commands than 512 bytes.. */
+ strncpy(str, cmd, 510);
+ len = strlen(cmd);
+ str[len++] = 13; str[len++] = 10; str[len] = '\0';
+
+ ptr = str;
+ if (send_now) {
+ ret = net_transmit(server->handle, str, len);
+ if (ret == len) {
+ g_get_current_time(&server->last_cmd);
+ return;
+ }
+
+ /* we didn't transmit all data, try again a bit later.. */
+ ptr += ret;
+ server->cmd_last_split = TRUE;
+ }
+
+ /* add to queue */
+ ptr = g_strdup(ptr);
+ if (!immediate)
+ server->cmdqueue = g_slist_append(server->cmdqueue, ptr);
+ else if (send_now)
+ server->cmdqueue = g_slist_prepend(server->cmdqueue, ptr);
+ else
+ server->cmdqueue = g_slist_insert(server->cmdqueue, ptr, 1);
+}
+
+/* Send command to IRC server */
+void irc_send_cmd(IRC_SERVER_REC *server, const char *cmd)
+{
+ int send_now;
+
+ g_return_if_fail(cmd != NULL);
+ if (server == NULL) return;
+
+ send_now = !server->cmd_last_split &&
+ (server->cmdcount < server->max_cmds_at_once ||
+ server->cmd_queue_speed <= 0);
+
+ cmd_send(server, cmd, send_now, FALSE);
+}
+
+/* Send command to IRC server */
+void irc_send_cmdv(IRC_SERVER_REC *server, const char *cmd, ...)
+{
+ va_list args;
+ char *str;
+
+ va_start(args, cmd);
+
+ str = g_strdup_vprintf(cmd, args);
+ irc_send_cmd(server, str);
+ g_free(str);
+
+ va_end(args);
+}
+
+/* Send command to server immediately bypassing all flood protections
+ and queues. */
+void irc_send_cmd_now(IRC_SERVER_REC *server, const char *cmd)
+{
+ g_return_if_fail(cmd != NULL);
+ if (server == NULL) return;
+
+ cmd_send(server, cmd, !server->cmd_last_split, TRUE);
+}
+
+static char *split_nicks(const char *cmd, char **pre, char **nicks, char **post, int arg)
+{
+ char *p;
+
+ *pre = g_strdup(cmd);
+ *post = *nicks = NULL;
+ for (p = *pre; *p != '\0'; p++) {
+ if (!isspace(*p))
+ continue;
+
+ if (arg == 1) {
+ /* text after nicks */
+ *p++ = '\0';
+ while (isspace(*p)) p++;
+ *post = p;
+ break;
+ }
+
+ /* find nicks */
+ while (isspace(p[1])) p++;
+ if (--arg == 1) {
+ *p = '\0';
+ *nicks = p+1;
+ }
+ }
+
+ return *pre;
+}
+
+void irc_send_cmd_split(IRC_SERVER_REC *server, const char *cmd,
+ int nickarg, int max_nicks)
+{
+ char *str, *pre, *post, *nicks;
+ char **nicklist, **tmp;
+ GString *nickstr;
+ int count;
+
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(cmd != NULL);
+
+ str = split_nicks(cmd, &pre, &nicks, &post, nickarg);
+
+ /* split the nicks */
+ nickstr = g_string_new(NULL);
+ nicklist = g_strsplit(nicks, ",", -1); count = 0;
+
+ tmp = nicklist;
+ for (;; tmp++) {
+ if (*tmp != NULL) {
+ g_string_sprintfa(nickstr, "%s,", *tmp);
+ if (++count < max_nicks)
+ continue;
+ }
+
+ count = 0;
+ g_string_truncate(nickstr, nickstr->len-1);
+ irc_send_cmdv(server, post == NULL ? "%s %s" : "%s %s %s",
+ pre, nickstr->str, post);
+ g_string_truncate(nickstr, 0);
+
+ if (*tmp == NULL || tmp[1] == NULL)
+ break;
+ }
+ g_strfreev(nicklist);
+ g_string_free(nickstr, TRUE);
+
+ g_free(str);
+}
+
+/* Nick can be in format "servertag/nick" - Update `nick' to
+ position "nick" and return "servertag" which you need to free */
+char *irc_nick_get_server(char **nick)
+{
+ char *ptr, *tag;
+
+ ptr = strchr(*nick, '/');
+ if (ptr == NULL) return NULL;
+ if (ptr == *nick) {
+ (*nick)++;
+ return NULL;
+ }
+
+ tag = g_strndup(*nick, (int) (ptr-*nick));
+ *nick = ptr+1;
+
+ return tag;
+}
+
+/* Get next parameter */
+char *event_get_param(char **data)
+{
+ char *pos;
+
+ g_return_val_if_fail(data != NULL, NULL);
+ g_return_val_if_fail(*data != NULL, NULL);
+
+ if (**data == ':') {
+ /* last parameter */
+ pos = *data;
+ *data += strlen(*data);
+ return pos+1;
+ }
+
+ pos = *data;
+ while (**data != '\0' && **data != ' ') (*data)++;
+ if (**data == ' ') *(*data)++ = '\0';
+
+ return pos;
+}
+
+/* Get count parameters from data */
+char *event_get_params(const char *data, int count, ...)
+{
+ char **str, *tmp, *duprec, *datad;
+ gboolean rest;
+ va_list args;
+
+ g_return_val_if_fail(data != NULL, NULL);
+
+ va_start(args, count);
+ duprec = datad = g_strdup(data);
+
+ rest = count & PARAM_FLAG_GETREST;
+ count = PARAM_WITHOUT_FLAGS(count);
+
+ while (count-- > 0) {
+ str = (char **) va_arg(args, char **);
+ if (count == 0 && rest) {
+ /* put the rest to last parameter */
+ tmp = *datad == ':' ? datad+1 : datad;
+ } else {
+ tmp = event_get_param(&datad);
+ }
+ if (str != NULL) *str = tmp;
+ }
+ va_end(args);
+
+ return duprec;
+}
+
+static void irc_server_event(const char *line, IRC_SERVER_REC *server, const char *nick, const char *address)
+{
+ char *event, *args, *callcmd;
+ GSList *list;
+
+ g_return_if_fail(line != NULL);
+
+ /* get command.. */
+ event = g_strconcat("event ", line, NULL);
+ args = strchr(event+6, ' ');
+ if (args != NULL) *args++ = '\0'; else args = "";
+ while (*args == ' ') args++;
+
+ list = server_redirect_getqueue((SERVER_REC *) server, event, args);
+ if (list == NULL)
+ callcmd = g_strdup(event);
+ else {
+ /* event is redirected somewhere else.. */
+ REDIRECT_REC *rec;
+
+ rec = list->data;
+ callcmd = g_strdup(rec->name);
+ rawlog_redirect(server->rawlog, callcmd);
+ server_redirect_remove_next((SERVER_REC *) server, event, list);
+ }
+
+ current_server_event = event+6;
+ g_strdown(callcmd);
+ if (!signal_emit(callcmd, 4, args, server, nick, address))
+ signal_emit_id(signal_default_event, 4, line, server, nick, address);
+ current_server_event = NULL;
+
+ g_free(callcmd);
+ g_free(event);
+}
+
+/* Read line from server */
+static int irc_receive_line(SERVER_REC *server, char **str)
+{
+ char tmpbuf[512];
+ int recvlen, ret;
+
+ g_return_val_if_fail(server != NULL, -1);
+ g_return_val_if_fail(str != NULL, -1);
+
+ recvlen = net_receive(server->handle, tmpbuf, sizeof(tmpbuf));
+
+ ret = line_split(tmpbuf, recvlen, str, (LINEBUF_REC **) &server->buffer);
+ if (ret == -1) {
+ /* connection lost */
+ server->connection_lost = TRUE;
+ server_disconnect(server);
+ }
+ return ret;
+}
+
+static char *irc_parse_prefix(char *line, char **nick, char **address)
+{
+ *nick = *address = NULL;
+
+ if (*line != ':')
+ return line;
+
+ *nick = ++line;
+ while (*line != '\0' && *line != ' ') {
+ if (*line == '!') {
+ *line = '\0';
+ *address = line+1;
+ }
+ line++;
+ }
+
+ if (*line == ' ') {
+ *line++ = '\0';
+ while (*line == ' ') line++;
+ }
+
+ return line;
+}
+
+/* Parse command line sent by server */
+static void irc_parse_incoming_line(IRC_SERVER_REC *server, char *line)
+{
+ char *nick, *address;
+
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(line != NULL);
+
+ line = irc_parse_prefix(line, &nick, &address);
+ if (*line != '\0')
+ signal_emit_id(signal_server_event, 4, line, server, nick, address);
+}
+
+/* input function: handle incoming server messages */
+static void irc_parse_incoming(SERVER_REC *server)
+{
+ char *str;
+
+ g_return_if_fail(server != NULL);
+
+ while (irc_receive_line(server, &str) > 0) {
+ rawlog_input(server->rawlog, str);
+ signal_emit_id(signal_server_incoming, 2, server, str);
+ }
+}
+
+static void irc_init_server(IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ server->readtag =
+ g_input_add(server->handle, G_INPUT_READ,
+ (GInputFunction) irc_parse_incoming, server);
+}
+
+static void irc_deinit_server(IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ if (server->readtag > 0)
+ g_source_remove(server->readtag);
+}
+
+#define isoptchan(a) \
+ (ischannel((a)[0]) || ((a)[0] == '*' && ((a)[1] == '\0' || (a)[1] == ' ')))
+
+static char *irc_cmd_get_func(const char *data, int *count, va_list *vargs)
+{
+ WI_IRC_REC *item;
+ CHANNEL_REC *channel;
+ char *ret, *args, *chan, *p;
+
+ if ((*count & PARAM_FLAG_OPTCHAN) == 0)
+ return g_strdup(data);
+
+ *count &= ~PARAM_FLAG_OPTCHAN;
+ item = (WI_IRC_REC *) va_arg(*vargs, WI_IRC_REC *);
+ channel = irc_item_channel(item);
+
+ /* change first argument in data to full channel name. */
+ p = args = g_strdup(data);
+
+ chan = isoptchan(args) ? cmd_get_param(&args) : NULL;
+ if (chan != NULL && *chan == '!') {
+ /* whenever trying to send something to !channel,
+ change it to the real joined !XXXXXchannel */
+ channel = channel_find(channel->server, chan);
+ if (channel != NULL) chan = channel->name;
+ }
+
+ if (chan == NULL || strcmp(chan, "*") == 0) {
+ chan = channel == NULL ? "*" : channel->name;
+ }
+
+ ret = g_strconcat(chan, " ", args, NULL);
+ g_free(p);
+ return ret;
+}
+
+void irc_irc_init(void)
+{
+ cmd_get_add_func(irc_cmd_get_func);
+
+ signal_add("server event", (SIGNAL_FUNC) irc_server_event);
+ signal_add("server connected", (SIGNAL_FUNC) irc_init_server);
+ signal_add_first("server disconnected", (SIGNAL_FUNC) irc_deinit_server);
+ signal_add("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
+
+ current_server_event = NULL;
+ signal_send_command = module_get_uniq_id_str("signals", "send command");
+ signal_default_event = module_get_uniq_id_str("signals", "default event");
+ signal_server_event = module_get_uniq_id_str("signals", "server event");
+ signal_server_incoming = module_get_uniq_id_str("signals", "server incoming");
+}
+
+void irc_irc_deinit(void)
+{
+ signal_remove("server event", (SIGNAL_FUNC) irc_server_event);
+ signal_remove("server connected", (SIGNAL_FUNC) irc_init_server);
+ signal_remove("server disconnected", (SIGNAL_FUNC) irc_deinit_server);
+ signal_remove("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
+
+ module_uniq_destroy("IRC");
+ module_uniq_destroy("IRC SERVER");
+}