From 3725c872c6384d7e14aaca5d45ff514e86f3dd0c Mon Sep 17 00:00:00 2001 From: Akari Date: Mon, 7 Feb 2011 12:36:56 +0200 Subject: [PATCH] imported irc.h/c from nexuiz version --- irc.c | 698 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ irc.h | 69 ++++++ 2 files changed, 767 insertions(+) create mode 100755 irc.c create mode 100755 irc.h diff --git a/irc.c b/irc.c new file mode 100755 index 00000000..4ad80cb5 --- /dev/null +++ b/irc.c @@ -0,0 +1,698 @@ +// irc.c +// +// Copyright 2011 Akari +// +// 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. + +#include +#include +#include + +#include "quakedef.h" +#include "irc.h" +#include "cvar.h" + +cvar_t irc_server = {CVAR_SAVE, "irc_server", "", "IRC server to connect to"}; +cvar_t irc_port = {CVAR_SAVE, "irc_port", "6667", "Port of the IRC server"}; +cvar_t irc_password = {CVAR_SAVE, "irc_password", "", "IRC server password"}; +cvar_t irc_nick = {CVAR_SAVE, "irc_nick", "", "Your nickname to use on IRC. Note: this cvar only defines your prefered nick, do NOT use this to change your nickname while connected, use irc_chnick instead"}; +cvar_t irc_connected = {CVAR_READONLY, "irc_connected", "0", "IRC connection state (0 = not connected, 1 = connecting, 2 = connected)"}; +cvar_t irc_msgprefix = {CVAR_SAVE, "irc_msgprefix", "^5IRC^0|^7", "What all IRC events will be prefixed with when printed to the console"}; +cvar_t irc_chatwindow = {CVAR_SAVE, "irc_chatwindow", "2", "0 = IRC messages will be printed in the console only, 1 = IRC messages will go to the chat window, 2 = Only hilights and private messages will appear in the chat window"}; +cvar_t irc_numeric_errorsonly = {CVAR_SAVE, "irc_numeric_errorsonly", "0", "If 1, any numeric event below 400 won't be printed'"}; +cvar_t irc_save_target = {CVAR_SAVE, "irc_save_target", "1", "Whether to save target for irc_messagemode or not"}; +cvar_t irc_current_nick = {CVAR_READONLY, "irc_current_nick", "", "Holds your current IRC nick"}; +cvar_t irc_hilights = {CVAR_SAVE, "irc_hilights", "", "Space separated list of words to hilight"}; +cvar_t irc_autochangetarget = {CVAR_SAVE, "irc_autochangetarget", "0", "if 1, will automatically change target for irc_messagemode when a hilight or a private message is recieved, so you can reply instantly. Requires irc_save_target"}; + +irc_session_t *irc_session_global = NULL; +int irc_msgmode; +char last_channel[MAX_INPUTLINE]; + +// +// Initialization +// + +void CL_Irc_Init(void) +{ + Cvar_RegisterVariable(&irc_server); + Cvar_RegisterVariable(&irc_port); + Cvar_RegisterVariable(&irc_password); + Cvar_RegisterVariable(&irc_nick); + Cvar_RegisterVariable(&irc_connected); + Cvar_RegisterVariable(&irc_msgprefix); + Cvar_RegisterVariable(&irc_chatwindow); + Cvar_RegisterVariable(&irc_numeric_errorsonly); + Cvar_RegisterVariable(&irc_save_target); + Cvar_RegisterVariable(&irc_current_nick); + Cvar_RegisterVariable(&irc_hilights); + Cvar_RegisterVariable(&irc_autochangetarget); + + Cmd_AddCommand ("irc_connect", CL_Irc_Connect_f, "Connects you to the IRC server"); + Cmd_AddCommand ("irc_disconnect", CL_Irc_Disconnect_f, "Disconnects you from the IRC server"); + Cmd_AddCommand ("irc_say", CL_Irc_Say_f, "Sends a privmsg to a channel or nick"); + Cmd_AddCommand ("irc_notice", CL_Irc_Notice_f, "Sends a notice to a channel or nick"); + Cmd_AddCommand ("irc_me", CL_Irc_Me_f, "Sends a CTCP ACTION (/me) to a channel or nick"); + Cmd_AddCommand ("irc_join", CL_Irc_Join_f, "Joins an IRC channel"); + Cmd_AddCommand ("irc_part", CL_Irc_Part_f, "Parts an IRC channel"); + Cmd_AddCommand ("irc_names", CL_Irc_Names_f, "Lists users of an IRC channel"); + Cmd_AddCommand ("irc_messagemode", CL_Irc_MessageMode_f, "Interactive prompt for sending IRC messages. Meant to be bound to a key"); + Cmd_AddCommand ("irc_chnick", CL_Irc_ChNick_f, "Changes your nick"); + Cmd_AddCommand ("irc_raw", CL_Irc_Raw_f, "Sends a raw string to the IRC server"); +} + +// +// Function that starts IRC main loop in a thread +// + +static void IRC_Thread(void *p) +{ + if(irc_run(irc_session_global)) + { + Con_Printf("%s^1Error: ^7%s\n", + irc_msgprefix.string, + irc_strerror(irc_errno(irc_session_global)) + ); + + CL_Irc_Disconnect_f(); + return; + } +} + +// +// irc_ console commands callbacks +// + +static void CL_Irc_Connect_f(void) +{ + irc_callbacks_t cb; + pthread_t irc_thread; + + switch(irc_connected.integer) + { + case 1: + Con_Printf("%sAlready connecting to %s:%i\n", + irc_msgprefix.string, + irc_server.string, + irc_port.integer + ); + return; + + case 2: + Con_Printf("%sAlready connected to %s:%i.\n", + irc_msgprefix.string, + irc_server.string, + irc_port.integer + ); + return; + } + + if(NOTSET(irc_server)) + { + Con_Printf("Please set the irc_server variable\n"); + return; + } + + if(NOTSET(irc_nick)) + { + Con_Printf("Please set the irc_nick variable\n"); + return; + } + + if(NOTSET(irc_port)) + { + Con_Printf("Please set the irc_port variable\n"); + return; + } + + Cvar_SetQuick(&irc_connected, "1"); + + memset(&cb, 0, sizeof(cb)); + cb.event_connect = event_connect; + cb.event_numeric = event_numeric; + cb.event_privmsg = event_privmsg; + cb.event_channel = event_channel; + cb.event_nick = event_nick; + cb.event_quit = event_quit; + cb.event_join = event_join; + cb.event_part = event_part; + cb.event_mode = event_mode; + cb.event_umode = event_umode; + cb.event_topic = event_topic; + cb.event_kick = event_kick; + cb.event_notice = event_notice; + cb.event_invite = event_invite; + + Cvar_SetQuick(&irc_current_nick, irc_nick.string); + + irc_session_global = irc_create_session(&cb); + irc_option_set(irc_session_global, LIBIRC_OPTION_STRIPNICKS); + if(irc_connect( + irc_session_global, + irc_server.string, + irc_port.integer, + NOTSET(irc_password)? NULL : irc_password.string, + irc_nick.string, + "dpirc", "DPIRC user" + )) + { //Connection failed + Con_Printf("%s^1Connection failed: ^7%s\n", + irc_msgprefix.string, + irc_strerror(irc_errno(irc_session_global)) + ); + + CL_Irc_Disconnect_f(); + return; + } + + pthread_create (&irc_thread, NULL, (void *) &IRC_Thread, NULL); +} + +static void CL_Irc_Disconnect_f(void) +{ + if(!irc_connected.integer) + { + Con_Printf("%sNot connected\n", + irc_msgprefix.string + ); + } + + Con_Printf("^1Disconnected from the IRC server\n"); + irc_cmd_quit(irc_session_global, "Disconnected"); + irc_destroy_session(irc_session_global); + Cvar_SetQuick(&irc_connected, "0"); +} + +static void CL_Irc_Say_Universal_f(void) +{ + int cmdlen, i, j, space; + const char *cmd, *dest; + char message[MAX_INPUTLINE]; + + if(Cmd_Argc() < 3) switch(irc_msgmode) + { + case MSGMODE_PRIVMSG: + Con_Printf("Usage: irc_say channel_or_nick message\n"); + return; + case MSGMODE_NOTICE: + Con_Printf("Usage: irc_notice channel_or_nick message\n"); + return; + case MSGMODE_ACTION: + Con_Printf("Usage: irc_me channel_or_nick message\n"); + return; + } + + cmd = Cmd_Args(); + cmdlen = strlen(cmd); + + space = 0; + for(i = 0, j = 0; i < cmdlen; ++i) + { + if(space) + { + message[j++] = cmd[i]; + } + else if(cmd[i] == ' ') ++space; + } + + message[j] = '\0'; + + dest = Cmd_Argv(1); + + switch(irc_msgmode) + { + case MSGMODE_PRIVMSG: + irc_cmd_msg(irc_session_global, dest, message); + + if(ISCHANNEL(dest)) Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n", + CHATWINDOW, + irc_msgprefix.string, + dest, + irc_current_nick.string, + message + ); else Con_Printf("%s%s^1Privmsg to ^2%s^7: ^3%s\n", + CHATWINDOW_URGENT, + irc_msgprefix.string, + dest, + message + ); + + break; + + case MSGMODE_NOTICE: + irc_cmd_notice(irc_session_global, dest, message); + + if(!ISCHANNEL(dest)) Con_Printf("%s%sNotice to ^2%s^7: ^9%s\n", + CHATWINDOW, + irc_msgprefix.string, + dest, + message + ); else Con_Printf("%s%s^3%s^0|^9-^2%s^9- %s\n", + CHATWINDOW, + irc_msgprefix.string, + dest, + irc_current_nick.string, + message + ); + + break; + + case MSGMODE_ACTION: //to-do + irc_cmd_me(irc_session_global, dest, message); + break; + } +} + +static void CL_Irc_Raw_f(void) +{ + if(Cmd_Argc() < 2) + { + Con_Printf("Usage: irc_raw command\n"); + return; + } + + irc_send_raw(irc_session_global, "%s", Cmd_Args()); +} + +static void CL_Irc_Say_f(void) +{ + MUSTCONNECT + irc_msgmode = MSGMODE_PRIVMSG; + CL_Irc_Say_Universal_f(); +} + +static void CL_Irc_Notice_f(void) +{ + MUSTCONNECT + irc_msgmode = MSGMODE_NOTICE; + CL_Irc_Say_Universal_f(); +} + +static void CL_Irc_Me_f(void) +{ + MUSTCONNECT + irc_msgmode = MSGMODE_ACTION; + CL_Irc_Say_Universal_f(); +} + +static void CL_Irc_Join_f(void) +{ + int argc = Cmd_Argc(); + const char *channel; + + MUSTCONNECT + + if(argc < 2) + { + Con_Printf("Usage: irc_join channel [key]\n"); + return; + } + + channel = Cmd_Argv(1); + + if(argc > 2) + irc_cmd_join(irc_session_global, channel, Cmd_Argv(2)); + else + irc_cmd_join(irc_session_global, channel, NULL); +} + +static void CL_Irc_Part_f(void) +{ + MUSTCONNECT + + if(Cmd_Argc() < 2) + { + Con_Printf("Usage: irc_part channel\n"); + return; + } + + irc_cmd_part(irc_session_global, Cmd_Argv(1)); +} + +static void CL_Irc_Names_f(void) +{ + MUSTCONNECT + + if(Cmd_Argc() < 2) + { + Con_Printf("Usage: irc_names channel\n"); + return; + } + + irc_cmd_names(irc_session_global, Cmd_Argv(1)); +} + +void CL_Irc_MessageMode_f(void) +{ + const char *tmp; + + key_dest = key_message; + chat_mode = 2; + chat_bufferlen = 0; + chat_buffer[0] = 0; + + if(irc_save_target.integer) + { + tmp = Irc_GetLastChannel(); + strlcpy(chat_buffer, tmp, sizeof(chat_buffer)); + chat_bufferlen = strlen(chat_buffer); + } +} + +void CL_Irc_ChNick_f(void) +{ + MUSTCONNECT + + if(Cmd_Argc() < 2) + { + Con_Printf("Usage: irc_chnick nick\n"); + return; + } + + irc_cmd_nick(irc_session_global, Cmd_Argv(1)); +} + + +void Irc_SetLastChannel(const char *value) +{ + strlcpy(last_channel, value, sizeof(last_channel)); +} + +const char* Irc_GetLastChannel(void) +{ + return last_channel; +} + + +void Irc_SendMessage(const char *message) +{ + const char* dest = Irc_GetLastChannel(); + + MUSTCONNECT + + irc_cmd_msg(irc_session_global, dest, message); + + if(ISCHANNEL(dest)) Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n", + CHATWINDOW, + irc_msgprefix.string, + dest, + irc_current_nick.string, + message + ); else Con_Printf("%s%s^1Privmsg to ^2%s^7: ^3%s\n", + CHATWINDOW_URGENT, + irc_msgprefix.string, + dest, + message + ); +} + +// +// IRC events +// + +IRCEVENT(event_connect) +{ + Cvar_SetQuick(&irc_connected, "2"); + Con_Printf("%sConnected to %s:%i\n", + irc_msgprefix.string, + irc_server.string, + irc_port.integer + ); +} + +IRCNUMEVENT(event_numeric) +{ + //Get our initial nick from the welcome message + if(event == 001) + Cvar_SetQuick(&irc_current_nick, params[0]); + + if (!irc_numeric_errorsonly.integer || event > 400) + { + Con_Printf("%s^3%d ^2%s ^1%s^7 %s %s %s\n", + irc_msgprefix.string, + event, + origin ? origin : "^8(unknown)", + params[0], + count > 1 ? params[1] : "", + count > 2 ? params[2] : "", + count > 3 ? params[3] : "" + ); + } +} + +IRCEVENT(event_privmsg) +{ + char* msgstr = ""; + + if(count > 1) + msgstr = irc_color_strip_from_mirc(params[1]); + + UPDATETARGET(origin) + + Con_Printf("%s%s^1Privmsg from ^2%s^7: ^3%s\n", + CHATWINDOW_URGENT, + irc_msgprefix.string, + origin, + msgstr + ); + + if(count > 1) free(msgstr); +} + + +IRCEVENT(event_channel) +{ + char* msgstr = ""; + + //weird shit + if(!ISCHANNEL(params[0])) + return event_privmsg(session, event, origin, params, count); + + if(count > 1) + msgstr = irc_color_strip_from_mirc(params[1]); + + if(Irc_CheckHilight(msgstr)) + { + UPDATETARGET(params[0]) + + Con_Printf("%s%s^3%s^0|^7<^2%s^7> ^1%s\n", + CHATWINDOW_URGENT, + irc_msgprefix.string, + params[0], + origin, + msgstr + ); + } + else Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n", + CHATWINDOW, + irc_msgprefix.string, + params[0], + origin, + msgstr + ); + + if(count > 1) free(msgstr); +} + +IRCEVENT(event_nick) +{ + //printf("%sorigin -> %s (%s, %s)\n", origin, params[0], irc_current_nick.string, irc_nick.string); + //crash! + //cvar_t dumb; + //Cvar_SetQuick(&dumb, NULL); + + if(!strncmp(origin, irc_current_nick.string, 512)) //Our nick is changed + { + Cvar_SetQuick(&irc_current_nick, params[0]); + + Con_Printf("%s%s^7Your nick is now ^2%s\n", + CHATWINDOW, + irc_msgprefix.string, + params[0] + ); + + return; + } + + Con_Printf("%s%s^2%s^7 is now known as ^2%s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[0] + ); +} + +IRCEVENT(event_quit) +{ + Con_Printf("%s%s^2%s^7 has quit IRC: ^2%s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + count > 1? params[0] : "^8(quit message missing)" + ); +} + +IRCEVENT(event_join) +{ + Con_Printf("%s%s^2%s^7 has joined ^3%s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[0] + ); +} + +IRCEVENT(event_part) +{ + Con_Printf("%s%s^2%s^7 has left ^3%s^7: %s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[0], + count > 1? params[1] : "^8(part message missing)" + ); +} + +IRCEVENT(event_mode) +{ + char paramstring[MAX_INPUTLINE] = ""; + unsigned int i; + + for(i = 2; i < count; i++) + { + strlcat(paramstring, params[i], sizeof(paramstring)); + strlcat(paramstring, " ", sizeof(paramstring)); + } + + Con_Printf("%s%s^2%s^7 set mode on ^3%s^7: %s %s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[0], + params[1], + paramstring + ); +} + +IRCEVENT(event_umode) +{ + Con_Printf("%sUsermode changed for ^2%s^7: %s\n", + irc_msgprefix.string, + origin, + params[0] + ); +} + +IRCEVENT(event_topic) +{ + if(count > 1) Con_Printf("%s%s^2%s^7 changed the topic of ^3%s^7: %s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[0], + params[1] + ); +} + +IRCEVENT(event_kick) +{ + Con_Printf("%s%s^2%s^7 has kicked ^2%s^7 out of ^3%s^7: %s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + count > 1? params[1] : "^8(nick missing)", //libircclient documentation says params[1] is optinal. + params[0], + count > 2? params[2] : "^8(random kick victim! yay!)" + ); +} + +IRCEVENT(event_notice) +{ + char* msgstr = ""; + + if(count > 1) + msgstr = irc_color_strip_from_mirc(params[1]); + + if(!ISCHANNEL(params[0])) Con_Printf("%s%sNotice from ^2%s^7: ^9%s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + msgstr + ); else Con_Printf("%s%s^3%s^0|^9-^2%s^9- %s\n", + CHATWINDOW, + irc_msgprefix.string, + params[0], + origin, + msgstr + ); + + if(count > 1) free(msgstr); +} + +IRCEVENT(event_invite) +{ + Con_Printf("%s%s^2%s^7 invites you to ^3%s\n", + CHATWINDOW, + irc_msgprefix.string, + origin, + params[1] + ); +} + +// +// Functions that checks if a message contains hilights +// + +qboolean Irc_CheckHilight(const char *msg) +{ + int start, idx, len; + char buffer[512]; + + if(strcasestr(msg, irc_current_nick.string)) + return TRUE; //Contains our nick + + len = strlen(irc_hilights.string); + start = 0; + + for(idx = 0; idx < len; ++idx) + { + if(irc_hilights.string[idx] == ' ') + { + strlcpy(buffer, irc_hilights.string+start, idx+1); + if(strcasestr(msg, buffer)) + return TRUE; //Contains a word from hilight list + + start = idx+1; + } + } + + //Catch the final word + strlcpy(buffer, irc_hilights.string+start, idx+1); + if(strcasestr(msg, buffer)) + return TRUE; //Contains a word from hilight list + + return FALSE; +} + +// +// Functions that should have been in libircclient +// + +int irc_cmd_nick(irc_session_t * session, const char *nick) +{ + if(!nick) + { + //session->lasterror = LIBIRC_ERR_STATE; Won't compile + return 1; + } + + return irc_send_raw(session, "NICK %s", nick); +} diff --git a/irc.h b/irc.h new file mode 100755 index 00000000..fd23c9c0 --- /dev/null +++ b/irc.h @@ -0,0 +1,69 @@ +// irc.h +// +// Copyright 2011 Akari +// +// 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. + + +#ifndef IRCINCLUDED +#define IRCINCLUDED + +static void CL_Irc_Connect_f(void); +static void CL_Irc_Disconnect_f(void); +static void CL_Irc_Say_Universal_f(void); +static void CL_Irc_Notice_f(void); +static void CL_Irc_Me_f(void); +static void CL_Irc_Say_f(void); +static void CL_Irc_Join_f(void); +static void CL_Irc_Part_f(void); +static void CL_Irc_Names_f(void); +static void CL_Irc_MessageMode_f(void); +static void CL_Irc_ChNick_f(void); +static void CL_Irc_Raw_f(void); +void CL_Irc_Init (void); +static void IRC_Thread(void *p); +void Irc_SetLastChannel(const char *value); +const char* Irc_GetLastChannel(void); +void Irc_SendMessage(const char *msg); +int irc_cmd_nick(irc_session_t * session, const char *nick); +qboolean Irc_CheckHilight(const char* msg); + +#define IRCEVENT(funcname) void funcname(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) +#define IRCNUMEVENT(funcname) void funcname (irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count) +IRCEVENT(event_connect); +IRCNUMEVENT(event_numeric); +IRCEVENT(event_privmsg); +IRCEVENT(event_channel); +IRCEVENT(event_nick); +IRCEVENT(event_quit); +IRCEVENT(event_join); +IRCEVENT(event_part); +IRCEVENT(event_mode); +IRCEVENT(event_umode); +IRCEVENT(event_topic); +IRCEVENT(event_kick); +IRCEVENT(event_notice); +IRCEVENT(event_invite); + +#define MSGMODE_PRIVMSG 0 +#define MSGMODE_NOTICE 1 +#define MSGMODE_ACTION 2 + +#define ISCHANNEL(c) (c[0] == '#' || c[0] == '&') +#define CHATWINDOW (irc_chatwindow.integer == 1? "\001" : "") +#define CHATWINDOW_URGENT (irc_chatwindow.integer? "\001" : "") +#define MUSTCONNECT if(irc_connected.integer != 2) { Con_Printf("Not connected to an IRC server\n"); return; } +#define UPDATETARGET(c) if(irc_autochangetarget.integer) Irc_SetLastChannel(c); +#define NOTSET(cvar) (!strlen((cvar).string)) + +extern char *strcasestr (__const char*, __const char*); + +#endif -- 2.39.2