00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "client.h"
00029 #include "cl_irc.h"
00030 #include "ui/ui_main.h"
00031 #include "ui/ui_nodes.h"
00032 #include "ui/ui_popup.h"
00033 #include "multiplayer/mp_chatmessages.h"
00034
00035 #ifdef _WIN32
00036 # include <winerror.h>
00037 #else
00038 # include <netinet/in.h>
00039 # include <arpa/inet.h>
00040 # include <netdb.h>
00041 # include <fcntl.h>
00042 #endif
00043
00044 static cvar_t *irc_server;
00045 static cvar_t *irc_port;
00046 static cvar_t *irc_channel;
00047 static cvar_t *irc_nick;
00048 static cvar_t *irc_user;
00049 static cvar_t *irc_password;
00050 static cvar_t *irc_topic;
00051 static cvar_t *irc_defaultChannel;
00052 static cvar_t *irc_logConsole;
00053 static cvar_t *irc_showIfNotInMenu;
00054
00055 static cvar_t *irc_send_buffer;
00056
00057 static qboolean irc_connected;
00058
00059 #define IRC_SEND_BUF_SIZE 512
00060 #define IRC_RECV_BUF_SIZE 1024
00061
00062 typedef struct irc_user_s {
00063 char key[MAX_VAR];
00064 struct irc_user_s *next;
00065 } irc_user_t;
00066
00067 typedef struct irc_channel_s {
00068 char name[MAX_VAR];
00069 char topic[256];
00070 int users;
00071 irc_user_t *user;
00072 } irc_channel_t;
00073
00074
00075 typedef enum irc_numeric_e {
00076
00077 RPL_WELCOME = 1,
00078 RPL_YOURHOST = 2,
00079 RPL_CREATED = 3,
00080 RPL_MYINFO = 4,
00081 RPL_ISUPPORT = 5,
00082 RPL_HELLO = 20,
00083 RPL_NONE = 300,
00084 RPL_USERHOST = 302,
00085 RPL_ISON = 303,
00086 RPL_AWAY = 301,
00087 RPL_UNAWAY = 305,
00088 RPL_NOWAWAY = 306,
00089 RPL_WHOISUSER = 311,
00090 RPL_WHOISSERVER = 312,
00091 RPL_WHOISOPERATOR = 313,
00092 RPL_WHOISIDLE = 317,
00093 RPL_ENDOFWHOIS = 318,
00094 RPL_WHOISCHANNELS = 319,
00095 RPL_WHOWASUSER = 314,
00096 RPL_ENDOFWHOWAS = 369,
00097 RPL_WHOISACCOUNT = 330,
00098
00099 RPL_LISTSTART = 321,
00100 RPL_LIST = 322,
00101 RPL_LISTEND = 323,
00102 RPL_CHANNELMODEIS = 324,
00103 RPL_NOTOPIC = 331,
00104 RPL_TOPIC = 332,
00105 RPL_TOPICWHOTIME = 333,
00106 RPL_INVITING = 341,
00107 RPL_SUMMONING = 342,
00108 RPL_VERSION = 351,
00109 RPL_WHOREPLY = 352,
00110 RPL_ENDOFWHO = 315,
00111 RPL_NAMREPLY = 353,
00112 RPL_ENDOFNAMES = 366,
00113 RPL_LINKS = 364,
00114 RPL_ENDOFLINKS = 365,
00115 RPL_BANLIST = 367,
00116 RPL_ENDOFBANLIST = 368,
00117 RPL_INFO = 371,
00118 RPL_ENDOFINFO = 374,
00119 RPL_MOTDSTART = 375,
00120 RPL_MOTD = 372,
00121 RPL_ENDOFMOTD = 376,
00122 RPL_YOUREOPER = 381,
00123 RPL_REHASHING = 382,
00124 RPL_TIME = 391,
00125 RPL_USERSSTART = 392,
00126 RPL_USERS = 393,
00127 RPL_ENDOFUSERS = 394,
00128 RPL_NOUSERS = 395,
00129 RPL_TRACELINK = 200,
00130 RPL_TRACECONNECTING = 201,
00131 RPL_TRACEHANDSHAKE = 202,
00132 RPL_TRACEUNKNOWN = 203,
00133 RPL_TRACEOPERATOR = 204,
00134 RPL_TRACEUSER = 205,
00135 RPL_TRACESERVER = 206,
00136 RPL_TRACENEWTYPE = 208,
00137 RPL_TRACELOG = 261,
00138 RPL_STATSLINKINFO = 211,
00139 RPL_STATSCOMMANDS = 212,
00140 RPL_STATSCLINE = 213,
00141 RPL_STATSNLINE = 214,
00142 RPL_STATSILINE = 215,
00143 RPL_STATSKLINE = 216,
00144 RPL_STATSYLINE = 218,
00145 RPL_ENDOFSTATS = 219,
00146 RPL_STATSLLINE = 241,
00147 RPL_STATSUPTIME = 242,
00148 RPL_STATSOLINE = 243,
00149 RPL_STATSHLINE = 244,
00150 RPL_UMODEIS = 221,
00151 RPL_LUSERCLIENT = 251,
00152 RPL_LUSEROP = 252,
00153 RPL_LUSERUNKNOWN = 253,
00154 RPL_LUSERCHANNELS = 254,
00155 RPL_LUSERME = 255,
00156 RPL_ADMINME = 256,
00157 RPL_ADMINLOC1 = 257,
00158 RPL_ADMINLOC2 = 258,
00159 RPL_ADMINEMAIL = 259,
00160 RPL_LOCALUSERS = 265,
00161 RPL_GLOBALUSERS = 266,
00162
00163
00164 ERR_NOSUCHNICK = 401,
00165 ERR_NOSUCHSERVER = 402,
00166 ERR_NOSUCHCHANNEL = 403,
00167 ERR_CANNOTSENDTOCHAN = 404,
00168 ERR_TOOMANYCHANNELS = 405,
00169 ERR_WASNOSUCHNICK = 406,
00170 ERR_TOOMANYTARGETS = 407,
00171 ERR_NOORIGIN = 409,
00172 ERR_NORECIPIENT = 411,
00173 ERR_NOTEXTTOSEND = 412,
00174 ERR_NOTOPLEVEL = 413,
00175 ERR_WILDTOPLEVEL = 414,
00176 ERR_UNKNOWNCOMMAND = 421,
00177 ERR_NOMOTD = 422,
00178 ERR_NOADMININFO = 423,
00179 ERR_FILEERROR = 424,
00180 ERR_NONICKNAMEGIVEN = 431,
00181 ERR_ERRONEUSNICKNAME = 432,
00182 ERR_NICKNAMEINUSE = 433,
00183 ERR_NICKCOLLISION = 436,
00184 ERR_BANNICKCHANGE = 437,
00185 ERR_NCHANGETOOFAST = 438,
00186 ERR_USERNOTINCHANNEL = 441,
00187 ERR_NOTONCHANNEL = 442,
00188 ERR_USERONCHANNEL = 443,
00189 ERR_NOLOGIN = 444,
00190 ERR_SUMMONDISABLED = 445,
00191 ERR_USERSDISABLED = 446,
00192 ERR_NOTREGISTERED = 451,
00193 ERR_NEEDMOREPARAMS = 461,
00194 ERR_ALREADYREGISTRED = 462,
00195 ERR_NOPERMFORHOST = 463,
00196 ERR_PASSWDMISMATCH = 464,
00197 ERR_YOUREBANNEDCREEP = 465,
00198 ERR_BADNAME = 468,
00199 ERR_KEYSET = 467,
00200 ERR_CHANNELISFULL = 471,
00201 ERR_UNKNOWNMODE = 472,
00202 ERR_INVITEONLYCHAN = 473,
00203 ERR_BANNEDFROMCHAN = 474,
00204 ERR_BADCHANNELKEY = 475,
00205 ERR_NOPRIVILEGES = 481,
00206 ERR_CHANOPRIVSNEEDED = 482,
00207 ERR_CANTKILLSERVER = 483,
00208 ERR_NOOPERHOST = 491,
00209 ERR_UMODEUNKNOWNFLAG = 501,
00210 ERR_USERSDONTMATCH = 502,
00211 ERR_GHOSTEDCLIENT = 503,
00212 ERR_LAST_ERR_MSG = 504,
00213 ERR_SILELISTFULL = 511,
00214 ERR_NOSUCHGLINE = 512,
00215
00216 ERR_BADPING = 513,
00217 ERR_TOOMANYDCC = 514,
00218 ERR_LISTSYNTAX = 521,
00219 ERR_WHOSYNTAX = 522,
00220 ERR_WHOLIMEXCEED = 523
00221 } irc_numeric_t;
00222
00223 typedef enum irc_command_type_e {
00224 IRC_COMMAND_NUMERIC,
00225 IRC_COMMAND_STRING
00226 } irc_command_type_t;
00227
00228 typedef enum irc_nick_prefix_e {
00229 IRC_NICK_PREFIX_NONE = ' ',
00230 IRC_NICK_PREFIX_OP = '@',
00231 IRC_NICK_PREFIX_VOICE = '+'
00232 } irc_nick_prefix_t;
00233
00234
00235 typedef struct irc_command_s {
00236 union {
00237
00238 irc_numeric_t numeric;
00239 const char * string;
00240 } id;
00241 irc_command_type_t type;
00242 } irc_command_t;
00243
00244
00245 typedef struct irc_server_msg_s {
00246 union {
00247 char string[IRC_SEND_BUF_SIZE];
00248 irc_numeric_t numeric;
00249 } id;
00250 irc_command_type_t type;
00251 char prefix[IRC_SEND_BUF_SIZE];
00252 char params[IRC_SEND_BUF_SIZE];
00253 char trailing[IRC_SEND_BUF_SIZE];
00254 } irc_server_msg_t;
00255
00256 static struct net_stream *irc_stream;
00257
00258 static const char IRC_QUIT_MSG[] = "ufoai.sf.net";
00259 static const char IRC_INVITE_FOR_A_GAME[] = "UFOAIINVITE;";
00260
00261 static irc_channel_t ircChan;
00262 static irc_channel_t *chan;
00263
00264 static char irc_buffer[4096];
00265
00266 static void Irc_Logic_RemoveChannelName(irc_channel_t *channel, const char *nick);
00267 static void Irc_Logic_AddChannelName(irc_channel_t *channel, irc_nick_prefix_t prefix, const char *nick);
00268 static void Irc_Client_Names_f(void);
00269 static qboolean Irc_Client_Join(const char *channel, const char *password);
00270 static void Irc_Logic_Disconnect(const char *reason);
00271
00272 static qboolean Irc_AppendToBuffer(const char* const msg, ...) __attribute__((format(__printf__, 1, 2)));
00273 static qboolean Irc_Proto_ParseServerMsg(const char *txt, size_t txt_len, irc_server_msg_t *msg);
00274 static qboolean Irc_Proto_Enqueue(const char *msg, size_t msg_len);
00275
00276 static qboolean Irc_Net_Connect(const char *host, const char *port);
00277 static qboolean Irc_Net_Disconnect(void);
00278 static void Irc_Net_Send(const char *msg, size_t msg_len);
00279
00280 static void Irc_Connect_f(void);
00281 static void Irc_Disconnect_f(void);
00282 static void Irc_Input_Deactivate_f(void);
00283
00284
00285
00286
00287
00288
00289
00290 static inline qboolean Irc_IsChannel (const char *target)
00291 {
00292 assert(target);
00293 return (target[0] == '#' || target[0] == '&');
00294 }
00295
00296 static void Irc_ParseName (const char *mask, char *nick, size_t size, irc_nick_prefix_t *prefix)
00297 {
00298 const char *emph;
00299 if (mask[0] == IRC_NICK_PREFIX_OP || mask[0] == IRC_NICK_PREFIX_VOICE) {
00300 *prefix = (irc_nick_prefix_t) *mask;
00301 ++mask;
00302 } else
00303 *prefix = IRC_NICK_PREFIX_NONE;
00304 emph = strchr(mask, '!');
00305 if (emph) {
00306 size_t length = emph - mask;
00307 if (length >= size - 1)
00308 length = size - 1;
00309
00310 memcpy(nick, mask, length);
00311 nick[length] = '\0';
00312 } else
00313
00314 Q_strncpyz(nick, mask, size);
00315 }
00316
00317
00318
00319
00320
00321
00322
00323 static cvar_t *irc_messageBucketSize;
00324 static cvar_t *irc_messageBucketBurst;
00325 static cvar_t *irc_characterBucketSize;
00326 static cvar_t *irc_characterBucketBurst;
00327 static cvar_t *irc_characterBucketRate;
00328
00329 typedef struct irc_bucket_message_s {
00330 char *msg;
00331 size_t msg_len;
00332 struct irc_bucket_message_s *next;
00333 } irc_bucket_message_t;
00334
00335 typedef struct irc_bucket_s {
00336 irc_bucket_message_t *first_msg;
00337 unsigned int message_size;
00338 unsigned int character_size;
00339 int last_refill;
00340 double character_token;
00341 } irc_bucket_t;
00342
00343 static irc_bucket_t irc_bucket;
00344
00348 static qboolean Irc_Proto_Connect (const char *host, const char *port)
00349 {
00350 const qboolean status = Irc_Net_Connect(host, port);
00351 if (!status) {
00352 irc_bucket.first_msg = NULL;
00353 irc_bucket.message_size = 0;
00354 irc_bucket.character_size = 0;
00355 irc_bucket.last_refill = CL_Milliseconds();
00356 irc_bucket.character_token = (double)irc_characterBucketBurst->value;
00357 }
00358 return status;
00359 }
00360
00364 static qboolean Irc_Proto_Disconnect (void)
00365 {
00366 const qboolean status = Irc_Net_Disconnect();
00367 if (!status) {
00368 irc_bucket_message_t *msg = irc_bucket.first_msg;
00369 irc_bucket_message_t *prev;
00370 while (msg) {
00371 prev = msg;
00372 msg = msg->next;
00373 Mem_Free(prev->msg);
00374 Mem_Free(prev);
00375 }
00376 irc_bucket.first_msg = NULL;
00377 irc_bucket.message_size = 0;
00378 irc_bucket.character_size = 0;
00379 }
00380 return status;
00381 }
00382
00386 static qboolean Irc_Proto_Quit (const char *quitmsg)
00387 {
00388 char msg[IRC_SEND_BUF_SIZE];
00389 const int msg_len = snprintf(msg, sizeof(msg) - 1, "QUIT %s\r\n", quitmsg);
00390 msg[sizeof(msg) - 1] = '\0';
00391 Irc_Net_Send(msg, msg_len);
00392 return qfalse;
00393 }
00394
00398 static qboolean Irc_Proto_Nick (const char *nick)
00399 {
00400 char msg[IRC_SEND_BUF_SIZE];
00401 const int msg_len = snprintf(msg, sizeof(msg) - 1, "NICK %s\r\n", nick);
00402 msg[sizeof(msg) - 1] = '\0';
00403 return Irc_Proto_Enqueue(msg, msg_len);
00404 }
00405
00409 static qboolean Irc_Proto_User (const char *user, qboolean invisible, const char *name)
00410 {
00411 char msg[IRC_SEND_BUF_SIZE];
00412 const int msg_len = snprintf(msg, sizeof(msg) - 1, "USER %s %c * :%s\r\n", user, invisible ? '8' : '0', name);
00413 msg[sizeof(msg) - 1] = '\0';
00414 return Irc_Proto_Enqueue(msg, msg_len);
00415 }
00416
00420 static qboolean Irc_Proto_Password (const char *password)
00421 {
00422 char msg[IRC_SEND_BUF_SIZE];
00423 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PASS %s\r\n", password);
00424 msg[sizeof(msg) - 1] = '\0';
00425 return Irc_Proto_Enqueue(msg, msg_len);
00426 }
00427
00431 static qboolean Irc_Proto_Join (const char *channel, const char *password)
00432 {
00433 char msg[IRC_SEND_BUF_SIZE];
00434 const int msg_len = password
00435 ? snprintf(msg, sizeof(msg) - 1, "JOIN %s %s\r\n", channel, password)
00436 : snprintf(msg, sizeof(msg) - 1, "JOIN %s\r\n", channel);
00437 msg[sizeof(msg) - 1] = '\0';
00438
00439
00440 if (chan) {
00441 Com_Printf("Already in a channel\n");
00442 return qfalse;
00443 }
00444
00445 chan = &ircChan;
00446 memset(chan, 0, sizeof(*chan));
00447 Q_strncpyz(chan->name, channel, sizeof(chan->name));
00448 return Irc_Proto_Enqueue(msg, msg_len);
00449 }
00450
00454 static qboolean Irc_Proto_Part (const char *channel)
00455 {
00456 char msg[IRC_SEND_BUF_SIZE];
00457 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PART %s\r\n", channel);
00458 msg[sizeof(msg) - 1] = '\0';
00459 return Irc_Proto_Enqueue(msg, msg_len);
00460 }
00461
00465 static qboolean Irc_Proto_Mode (const char *target, const char *modes, const char *params)
00466 {
00467 char msg[IRC_SEND_BUF_SIZE];
00468 const int msg_len = params
00469 ? snprintf(msg, sizeof(msg) - 1, "MODE %s %s %s\r\n", target, modes, params)
00470 : snprintf(msg, sizeof(msg) - 1, "MODE %s %s\r\n", target, modes);
00471 msg[sizeof(msg) - 1] = '\0';
00472 return Irc_Proto_Enqueue(msg, msg_len);
00473 }
00474
00478 static qboolean Irc_Proto_Topic (const char *channel, const char *topic)
00479 {
00480 char msg[IRC_SEND_BUF_SIZE];
00481 const int msg_len = topic
00482 ? snprintf(msg, sizeof(msg) - 1, "TOPIC %s :%s\r\n", channel, topic)
00483 : snprintf(msg, sizeof(msg) - 1, "TOPIC %s\r\n", channel);
00484 msg[sizeof(msg) - 1] = '\0';
00485 return Irc_Proto_Enqueue(msg, msg_len);
00486 }
00487
00493 static qboolean Irc_Proto_Msg (const char *target, const char *text)
00494 {
00495 if (*text == '/') {
00496 Com_DPrintf(DEBUG_CLIENT, "Don't send irc commands as PRIVMSG\n");
00497 Cbuf_AddText(va("%s\n", &text[1]));
00498 return qtrue;
00499 } else {
00500 char msg[IRC_SEND_BUF_SIZE];
00501 const int msg_len = snprintf(msg, sizeof(msg) - 1, "PRIVMSG %s :%s\r\n", target, text);
00502 msg[sizeof(msg) - 1] = '\0';
00503 return Irc_Proto_Enqueue(msg, msg_len);
00504 }
00505 }
00506
00510 static qboolean Irc_Proto_Notice (const char *target, const char *text)
00511 {
00512 char msg[IRC_SEND_BUF_SIZE];
00513 const int msg_len = snprintf(msg, sizeof(msg) - 1, "NOTICE %s :%s\r\n", target, text);
00514 msg[sizeof(msg) - 1] = '\0';
00515 return Irc_Proto_Enqueue(msg, msg_len);
00516 }
00517
00521 static void Irc_Proto_Pong (const char *nick, const char *server, const char *cookie)
00522 {
00523 char msg[IRC_SEND_BUF_SIZE];
00524 const int msg_len = cookie
00525 ? snprintf(msg, sizeof(msg) - 1, "PONG %s %s :%s\r\n", nick, server, cookie)
00526 : snprintf(msg, sizeof(msg) - 1, "PONG %s %s\r\n", nick, server);
00527 msg[sizeof(msg) - 1] = '\0';
00528 Irc_Net_Send(msg, msg_len);
00529 }
00530
00534 static qboolean Irc_Proto_Kick (const char *channel, const char *nick, const char *reason)
00535 {
00536 char msg[IRC_SEND_BUF_SIZE];
00537 const int msg_len = reason
00538 ? snprintf(msg, sizeof(msg) - 1, "KICK %s %s :%s\r\n", channel, nick, reason)
00539 : snprintf(msg, sizeof(msg) - 1, "KICK %s %s :%s\r\n", channel, nick, nick);
00540 msg[sizeof(msg) - 1] = '\0';
00541 return Irc_Proto_Enqueue(msg, msg_len);
00542 }
00543
00547 static qboolean Irc_Proto_Who (const char *nick)
00548 {
00549 char msg[IRC_SEND_BUF_SIZE];
00550 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHO %s\r\n", nick);
00551 msg[sizeof(msg) - 1] = '\0';
00552 return Irc_Proto_Enqueue(msg, msg_len);
00553 }
00554
00558 static qboolean Irc_Proto_Whois (const char *nick)
00559 {
00560 char msg[IRC_SEND_BUF_SIZE];
00561 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHOIS %s\r\n", nick);
00562 msg[sizeof(msg) - 1] = '\0';
00563 return Irc_Proto_Enqueue(msg, msg_len);
00564 }
00565
00569 static qboolean Irc_Proto_Whowas (const char *nick)
00570 {
00571 char msg[IRC_SEND_BUF_SIZE];
00572 const int msg_len = snprintf(msg, sizeof(msg) - 1, "WHOWAS %s\r\n", nick);
00573 msg[sizeof(msg) - 1] = '\0';
00574 return Irc_Proto_Enqueue(msg, msg_len);
00575 }
00576
00580 static qboolean Irc_Proto_PollServerMsg (irc_server_msg_t *msg, qboolean *msg_complete)
00581 {
00582 static char buf[IRC_RECV_BUF_SIZE];
00583 static char *last = buf;
00584 int recvd;
00585 *msg_complete = qfalse;
00586
00587 recvd = NET_StreamDequeue(irc_stream, last, sizeof(buf) - (last - buf) - 1);
00588 if (recvd >= 0) {
00589
00590 const char * const begin = buf;
00591 last += recvd;
00592 *last = '\0';
00593 if (last != begin) {
00594
00595 const char * const end = strstr(begin, "\r\n");
00596 if (end) {
00597
00598 const size_t cmd_len = end + 2 - begin;
00599 if (!Irc_Proto_ParseServerMsg(begin, cmd_len, msg)) {
00600
00601
00602 memmove(buf, end + 2, sizeof(buf) - cmd_len);
00603 last -= cmd_len;
00604 *msg_complete = qtrue;
00605 } else {
00606
00607 Com_Printf("Received invalid packet from server\n");
00608 return qtrue;
00609 }
00610 }
00611 } else
00612 *msg_complete = qfalse;
00613 return qfalse;
00614 }
00615 return qtrue;
00616 }
00617
00624 static qboolean Irc_AppendToBuffer (const char* const msg, ...)
00625 {
00626 char buf[IRC_RECV_BUF_SIZE];
00627 va_list ap;
00628 char appendString[2048];
00629
00630 va_start(ap, msg);
00631 Q_vsnprintf(appendString, sizeof(appendString), msg, ap);
00632 va_end(ap);
00633
00634 while (strlen(irc_buffer) + strlen(appendString) + 1 >= sizeof(irc_buffer)) {
00635 const char *n;
00636 if (!(n = strchr(irc_buffer, '\n'))) {
00637 irc_buffer[0] = '\0';
00638 break;
00639 }
00640 memmove(irc_buffer, n + 1, strlen(n));
00641 }
00642
00643 Com_sprintf(buf, sizeof(buf), "%s\n", appendString);
00644 Q_strcat(irc_buffer, buf, sizeof(irc_buffer));
00645 if (irc_logConsole->integer)
00646 Com_Printf("IRC: %s\n", appendString);
00647
00648 UI_RegisterText(TEXT_IRCCONTENT, irc_buffer);
00649 UI_TextScrollEnd("irc.irc_data");
00650
00651 if (irc_showIfNotInMenu->integer && strcmp(UI_GetActiveWindowName(), "irc")) {
00652 S_StartLocalSample("misc/talk", SND_VOLUME_DEFAULT);
00653 MP_AddChatMessage(appendString);
00654 return qtrue;
00655 }
00656 return qfalse;
00657 }
00658
00659 static void Irc_Client_CmdRplWhowasuser (const char *params, const char *trailing)
00660 {
00661 char buf[IRC_SEND_BUF_SIZE];
00662 const char *nick = "", *user = "", *host = "", *real_name = trailing;
00663 char *p;
00664 unsigned int i = 0;
00665
00666
00667 Q_strncpyz(buf, params, sizeof(buf));
00668 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00669 switch (i) {
00670 case 1:
00671 nick = p;
00672 break;
00673 case 2:
00674 user = p;
00675 break;
00676 case 3:
00677 host = p;
00678 break;
00679 }
00680 }
00681 Irc_AppendToBuffer("^B%s was %s@%s : %s", nick, user, host, real_name);
00682 }
00683
00684 static inline void Irc_Client_CmdTopic (const char *prefix, const char *trailing)
00685 {
00686 Cvar_ForceSet("irc_topic", trailing);
00687 }
00688
00689 static void Irc_Client_CmdRplTopic (const char *params, const char *trailing)
00690 {
00691 const char *channel = strchr(params, ' ');
00692 if (channel) {
00693 ++channel;
00694 Irc_Client_CmdTopic(params, trailing);
00695 }
00696 }
00697
00698 static void Irc_Client_CmdRplWhoisuser (const char *params, const char *trailing)
00699 {
00700 char buf[IRC_SEND_BUF_SIZE];
00701 const char *nick = "", *user = "", *host = "", *real_name = trailing;
00702 char *p;
00703 unsigned int i = 0;
00704
00705
00706 strcpy(buf, params);
00707 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00708 switch (i) {
00709 case 1:
00710 nick = p;
00711 break;
00712 case 2:
00713 user = p;
00714 break;
00715 case 3:
00716 host = p;
00717 break;
00718 }
00719 }
00720 Irc_AppendToBuffer("^B%s is %s@%s : %s", nick, user, host, real_name);
00721 }
00722
00723 static void Irc_Client_CmdRplWhoisserver (const char *params, const char *trailing)
00724 {
00725 char buf[IRC_SEND_BUF_SIZE];
00726 const char *nick = "", *server = "", *server_info = trailing;
00727 char *p;
00728 unsigned int i = 0;
00729
00730
00731 strcpy(buf, params);
00732 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00733 switch (i) {
00734 case 1:
00735 nick = p;
00736 break;
00737 case 2:
00738 server = p;
00739 break;
00740 }
00741 }
00742 Irc_AppendToBuffer("^B%s using %s : %s", nick, server, server_info);
00743 }
00744
00745 static void Irc_Client_CmdRplWhoisaccount (const char *params, const char *trailing)
00746 {
00747 char buf[IRC_SEND_BUF_SIZE];
00748 const char *nick = "", *account = "";
00749 char *p;
00750 unsigned int i = 0;
00751
00752
00753 strcpy(buf, params);
00754 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00755 switch (i) {
00756 case 1:
00757 nick = p;
00758 break;
00759 case 2:
00760 account = p;
00761 break;
00762 }
00763 }
00764 Irc_AppendToBuffer("^B%s %s %s", nick, trailing, account);
00765 }
00766
00767 static void Irc_Client_CmdRplWhoisidle (const char *params, const char *trailing)
00768 {
00769 char buf[IRC_SEND_BUF_SIZE];
00770 const char *nick = "", *idle = "";
00771 char *p;
00772 unsigned int i = 0;
00773
00774
00775 strcpy(buf, params);
00776 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00777 switch (i) {
00778 case 1:
00779 nick = p;
00780 break;
00781 case 2:
00782 idle = p;
00783 break;
00784 }
00785 }
00786 Irc_AppendToBuffer("^B%s is %s %s", nick, idle, trailing);
00787 }
00788
00789 static void Irc_Client_CmdRplWhoreply (const char *params, const char *trailing)
00790 {
00791 char buf[IRC_SEND_BUF_SIZE];
00792 const char *channel = "", *user = "", *host = "", *server = "", *nick = "", *hg = "";
00793 char *p;
00794 unsigned int i = 0;
00795
00796
00797 strcpy(buf, params);
00798 for (p = strtok(buf, " "); p; p = strtok(NULL, " "), ++i) {
00799 switch (i) {
00800 case 0:
00801 channel = p;
00802 break;
00803 case 1:
00804 user = p;
00805 break;
00806 case 2:
00807 host = p;
00808 break;
00809 case 3:
00810 server = p;
00811 break;
00812 case 4:
00813 nick = p;
00814 break;
00815 case 5:
00816 hg = p;
00817 break;
00818 }
00819 }
00820 Irc_AppendToBuffer("%s %s %s %s %s %s : %s", channel, user, host, server, nick, hg, trailing);
00821 }
00822
00823 static void Irc_Client_CmdMode (const char *prefix, const char *params, const char *trailing)
00824 {
00825 char nick[MAX_VAR];
00826 irc_nick_prefix_t p;
00827 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00828 Irc_AppendToBuffer("^B%s sets mode %s", nick, params);
00829 }
00830
00831 static void Irc_Client_CmdJoin (const char *prefix, const char *params, const char *trailing)
00832 {
00833 char nick[MAX_VAR];
00834 irc_nick_prefix_t p;
00835 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00836 Irc_AppendToBuffer("^BJoined: %s", nick);
00837 Irc_Logic_AddChannelName(chan, p, nick);
00838 }
00839
00840 static void Irc_Client_CmdPart (const char *prefix, const char *trailing)
00841 {
00842 char nick[MAX_VAR];
00843 irc_nick_prefix_t p;
00844 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00845 Irc_AppendToBuffer("^BLeft: %s (%s)", nick, prefix);
00846 Irc_Logic_RemoveChannelName(chan, nick);
00847 }
00848
00849 static void Irc_Client_CmdQuit (const char *prefix, const char *params, const char *trailing)
00850 {
00851 char nick[MAX_VAR];
00852 irc_nick_prefix_t p;
00853 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00854 Irc_AppendToBuffer("^BQuits: %s (%s)", nick, trailing);
00855 Irc_Logic_RemoveChannelName(chan, nick);
00856 }
00857
00858 static void Irc_Client_CmdKill (const char *prefix, const char *params, const char *trailing)
00859 {
00860 char nick[MAX_VAR];
00861 irc_nick_prefix_t p;
00862 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00863 Irc_AppendToBuffer("^BKilled: %s (%s)", nick, trailing);
00864 Irc_Logic_RemoveChannelName(chan, nick);
00865 }
00866
00867 static void Irc_Client_CmdKick (const char *prefix, const char *params, const char *trailing)
00868 {
00869 char buf[IRC_SEND_BUF_SIZE];
00870 char nick[MAX_VAR];
00871 irc_nick_prefix_t p;
00872 const char *channel, *victim;
00873 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00874 strcpy(buf, params);
00875 channel = strtok(buf, " ");
00876 victim = strtok(NULL, " ");
00877 if (!strcmp(victim, irc_nick->string)) {
00878
00879 Irc_AppendToBuffer("^BYou were kicked from %s by %s (%s)", channel, nick, trailing);
00880 } else {
00881
00882 Irc_AppendToBuffer("^B%s kicked %s (%s)", nick, victim, trailing);
00883 }
00884 Irc_Logic_RemoveChannelName(chan, nick);
00885 }
00886
00890 static void Irc_Client_CmdNick (const char *prefix, const char *params, const char *trailing)
00891 {
00892 char nick[MAX_VAR];
00893 irc_nick_prefix_t p;
00894
00895
00896 if (!chan)
00897 return;
00898
00899 Irc_ParseName(prefix, nick, sizeof(nick), &p);
00900 if (!strcmp(irc_nick->string, nick))
00901 Cvar_ForceSet("cl_name", trailing);
00902 Irc_AppendToBuffer("%s is now known as %s", nick, trailing);
00903 Irc_Logic_RemoveChannelName(chan, nick);
00904 Irc_Logic_AddChannelName(chan, p, trailing);
00905 }
00906
00907 #define IRC_CTCP_MARKER_CHR '\001'
00908 #define IRC_CTCP_MARKER_STR "\001"
00909
00913 static void Irc_Client_CmdPrivmsg (const char *prefix, const char *params, const char *trailing)
00914 {
00915 char nick[MAX_VAR];
00916 char * const emph = strchr(prefix, '!');
00917 char * ctcp = strchr(trailing, IRC_CTCP_MARKER_CHR);
00918 memset(nick, 0, sizeof(nick));
00919 if (emph)
00920 memcpy(nick, prefix, emph - prefix);
00921 else
00922 strcpy(nick, prefix);
00923
00924 if (ctcp) {
00925 if (!strcmp(trailing + 1, "VERSION" IRC_CTCP_MARKER_STR)) {
00926
00927 Irc_Proto_Msg(irc_defaultChannel->string, Cvar_GetString("version"));
00928
00929 Com_DPrintf(DEBUG_CLIENT, "Irc_Client_CmdPrivmsg: Response to version query\n");
00930 } else if (!strncmp(trailing + 1, "PING", 4)) {
00931
00932 char response[IRC_SEND_BUF_SIZE];
00933 strcpy(response, trailing);
00934 response[2] = 'O';
00935 Irc_Proto_Notice(nick, response);
00936 } else if (!strcmp(trailing + 1, "TIME" IRC_CTCP_MARKER_STR)) {
00937 const time_t t = time(NULL);
00938 char response[IRC_SEND_BUF_SIZE];
00939 const size_t response_len = sprintf(response, IRC_CTCP_MARKER_STR "TIME :%s" IRC_CTCP_MARKER_STR, ctime(&t));
00940 response[response_len - 1] = '\0';
00941 Irc_Proto_Notice(nick, response);
00942 } else {
00943 Com_Printf("Irc_Client_CmdPrivmsg: Unknown ctcp command: '%s'\n", trailing);
00944 }
00945 } else {
00946 if (!strncmp(trailing, IRC_INVITE_FOR_A_GAME, strlen(IRC_INVITE_FOR_A_GAME))) {
00947 char serverIPAndPort[128];
00948 char *port;
00949 Q_strncpyz(serverIPAndPort, trailing + strlen(IRC_INVITE_FOR_A_GAME), sizeof(serverIPAndPort));
00950
00951 port = strstr(serverIPAndPort, ";");
00952 if (port == NULL) {
00953 Com_DPrintf(DEBUG_CLIENT, "Invalid irc invite message received\n");
00954 return;
00955 }
00956
00957
00958 *port++ = '\0';
00959
00961 UI_ExecuteConfunc("multiplayer_invite_server_info %s %s", serverIPAndPort, port);
00962
00963 UI_PushWindow("multiplayer_invite", NULL);
00964 } else if (!Irc_AppendToBuffer("<%s> %s", nick, trailing)) {
00965
00966 if (params && strcmp(params, irc_defaultChannel->string)) {
00967 S_StartLocalSample("misc/lobbyprivmsg", SND_VOLUME_DEFAULT);
00968 MP_AddChatMessage(va("<%s> %s\n", nick, trailing));
00969 } else if (strstr(trailing, irc_nick->string)) {
00970 S_StartLocalSample("misc/lobbyprivmsg", SND_VOLUME_DEFAULT);
00971 MP_AddChatMessage(va("<%s> %s\n", nick, trailing));
00972 if (strcmp(UI_GetActiveWindowName(), "irc") && strcmp(UI_GetActiveWindowName(), mn_hud->string)) {
00973
00974 UI_PushWindow("chat_popup", NULL);
00975 }
00976 }
00977 }
00978
00979 if (UI_GetActiveWindow() && strcmp(UI_GetActiveWindowName(), "irc")) {
00980 Com_Printf(COLORED_GREEN "<%s@lobby> %s\n", nick, trailing);
00981 }
00982 }
00983 }
00984
00985 static void Irc_Client_CmdRplNamreply (const char *params, const char *trailing)
00986 {
00987 char *parseBuf, *pos;
00988 char *space;
00989 char nick[MAX_VAR];
00990 size_t len = strlen(trailing) + 1;
00991 irc_nick_prefix_t p;
00992
00993 if (!chan)
00994 return;
00995
00996 parseBuf = (char *)Mem_PoolAlloc(len * sizeof(char), cl_ircSysPool, 0);
00997 if (!parseBuf)
00998 return;
00999
01000 Q_strncpyz(parseBuf, trailing, len);
01001 pos = parseBuf;
01002
01003 do {
01004
01005 space = strstr(pos, " ");
01006 if (space)
01007 *space = '\0';
01008 Irc_ParseName(pos, nick, sizeof(nick), &p);
01009 Irc_Logic_AddChannelName(chan, p, nick);
01010 if (space)
01011 pos = space + 1;
01012 } while (space && *pos);
01013
01014 Mem_Free(parseBuf);
01015 }
01016
01020 static void Irc_Client_CmdRplEndofnames (const char *params, const char *trailing)
01021 {
01022 }
01023
01027 static qboolean Irc_Proto_ProcessServerMsg (const irc_server_msg_t *msg)
01028 {
01029 irc_command_t cmd;
01030 const char *p = NULL;
01031 cmd.type = msg->type;
01032
01035 switch (cmd.type) {
01036 case IRC_COMMAND_NUMERIC:
01037 cmd.id.numeric = msg->id.numeric;
01038 switch (cmd.id.numeric) {
01039 case RPL_HELLO:
01040 case RPL_WELCOME:
01041 case RPL_YOURHOST:
01042 case RPL_CREATED:
01043 case RPL_MYINFO:
01044 case RPL_MOTDSTART:
01045 case RPL_MOTD:
01046 case RPL_LOCALUSERS:
01047 case RPL_GLOBALUSERS:
01048 case RPL_ISUPPORT:
01049 case RPL_LUSEROP:
01050 case RPL_LUSERUNKNOWN:
01051 case RPL_LUSERCHANNELS:
01052 case RPL_LUSERCLIENT:
01053 case RPL_LUSERME:
01054 return qtrue;
01055
01056
01057 case RPL_ENDOFMOTD:
01058 {
01059 byte *fbuf;
01060 int size;
01061 size = FS_LoadFile("irc_motd.txt", &fbuf);
01062 if (size) {
01063 Irc_AppendToBuffer("%s", (char *)fbuf);
01064 FS_FreeFile(fbuf);
01065 }
01066 }
01067 return qtrue;
01068
01069 case RPL_NAMREPLY:
01070 Irc_Client_CmdRplNamreply(msg->params, msg->trailing);
01071 return qtrue;
01072 case RPL_ENDOFNAMES:
01073 Irc_Client_CmdRplEndofnames(msg->params, msg->trailing);
01074 return qtrue;
01075 case RPL_TOPIC:
01076 Irc_Client_CmdRplTopic(msg->params, msg->trailing);
01077 return qtrue;
01078 case RPL_NOTOPIC:
01079 return qtrue;
01080 case RPL_WHOISUSER:
01081 Irc_Client_CmdRplWhoisuser(msg->params, msg->trailing);
01082 return qtrue;
01083 case RPL_WHOISSERVER:
01084 Irc_Client_CmdRplWhoisserver(msg->params, msg->trailing);
01085 return qtrue;
01086 case RPL_WHOISIDLE:
01087 Irc_Client_CmdRplWhoisidle(msg->params, msg->trailing);
01088 return qtrue;
01089 case RPL_WHOISACCOUNT:
01090 Irc_Client_CmdRplWhoisaccount(msg->params, msg->trailing);
01091 return qtrue;
01092 case RPL_WHOREPLY:
01093 Irc_Client_CmdRplWhoreply(msg->params, msg->trailing);
01094 return qtrue;
01095 case RPL_WHOWASUSER:
01096 Irc_Client_CmdRplWhowasuser(msg->params, msg->trailing);
01097 return qtrue;
01098 case RPL_ENDOFWHO:
01099 case RPL_WHOISCHANNELS:
01100 case RPL_WHOISOPERATOR:
01101 case RPL_ENDOFWHOIS:
01102 case RPL_ENDOFWHOWAS:
01103 p = strchr(msg->params, ' ');
01104 if (p) {
01105 ++p;
01106 Irc_AppendToBuffer("%s %s", p, msg->trailing);
01107 }
01108 return qtrue;
01109
01110 case ERR_NICKNAMEINUSE:
01111 case ERR_NOSUCHNICK:
01112 case ERR_NONICKNAMEGIVEN:
01113 case ERR_ERRONEUSNICKNAME:
01114 case ERR_NICKCOLLISION:
01115 Irc_AppendToBuffer("%s : %s", msg->params, msg->trailing);
01116 UI_PushWindow("irc_changename", NULL);
01117 break;
01118
01119 case ERR_NOSUCHSERVER:
01120 case ERR_NOSUCHCHANNEL:
01121 case ERR_CANNOTSENDTOCHAN:
01122 case ERR_TOOMANYCHANNELS:
01123 case ERR_WASNOSUCHNICK:
01124 case ERR_TOOMANYTARGETS:
01125 case ERR_NOORIGIN:
01126 case ERR_NORECIPIENT:
01127 case ERR_NOTEXTTOSEND:
01128 case ERR_NOTOPLEVEL:
01129 case ERR_WILDTOPLEVEL:
01130 case ERR_UNKNOWNCOMMAND:
01131 case ERR_NOMOTD:
01132 case ERR_NOADMININFO:
01133 case ERR_FILEERROR:
01134 case ERR_BANNICKCHANGE:
01135 case ERR_NCHANGETOOFAST:
01136 case ERR_USERNOTINCHANNEL:
01137 case ERR_NOTONCHANNEL:
01138 case ERR_USERONCHANNEL:
01139 case ERR_NOLOGIN:
01140 case ERR_SUMMONDISABLED:
01141 case ERR_USERSDISABLED:
01142 case ERR_NOTREGISTERED:
01143 case ERR_NEEDMOREPARAMS:
01144 case ERR_ALREADYREGISTRED:
01145 case ERR_NOPERMFORHOST:
01146 case ERR_PASSWDMISMATCH:
01147 case ERR_YOUREBANNEDCREEP:
01148 case ERR_BADNAME:
01149 case ERR_KEYSET:
01150 case ERR_CHANNELISFULL:
01151 case ERR_UNKNOWNMODE:
01152 case ERR_INVITEONLYCHAN:
01153 case ERR_BANNEDFROMCHAN:
01154 case ERR_BADCHANNELKEY:
01155 case ERR_NOPRIVILEGES:
01156 case ERR_CHANOPRIVSNEEDED:
01157 case ERR_CANTKILLSERVER:
01158 case ERR_NOOPERHOST:
01159 case ERR_UMODEUNKNOWNFLAG:
01160 case ERR_USERSDONTMATCH:
01161 case ERR_GHOSTEDCLIENT:
01162 case ERR_LAST_ERR_MSG:
01163 case ERR_SILELISTFULL:
01164 case ERR_NOSUCHGLINE:
01165 case ERR_BADPING:
01166 case ERR_TOOMANYDCC:
01167 case ERR_LISTSYNTAX:
01168 case ERR_WHOSYNTAX:
01169 case ERR_WHOLIMEXCEED:
01170 Irc_AppendToBuffer("%s : %s", msg->params, msg->trailing);
01171 return qtrue;
01172 default:
01173 Com_DPrintf(DEBUG_CLIENT, "<%s> [%s] %s : %s\n", msg->prefix, cmd.id.string, msg->params, msg->trailing);
01174 return qtrue;
01175 }
01176 break;
01177 case IRC_COMMAND_STRING:
01178 cmd.id.string = msg->id.string;
01179 #ifdef DEBUG
01180 Com_DPrintf(DEBUG_CLIENT, "<%s> [%s] %s : %s\n", msg->prefix, cmd.id.string, msg->params, msg->trailing);
01181 #endif
01182 if (!strncmp(cmd.id.string, "NICK", 4))
01183 Irc_Client_CmdNick(msg->prefix, msg->params, msg->trailing);
01184 else if (!strncmp(cmd.id.string, "QUIT", 4))
01185 Irc_Client_CmdQuit(msg->prefix, msg->params, msg->trailing);
01186 else if (!strncmp(cmd.id.string, "NOTICE", 6)) {
01187 if (irc_logConsole->integer)
01188 Com_Printf("%s\n", msg->trailing);
01189 } else if (!strncmp(cmd.id.string, "PRIVMSG", 7))
01190 Irc_Client_CmdPrivmsg(msg->prefix, msg->params, msg->trailing);
01191 else if (!strncmp(cmd.id.string, "MODE", 4))
01192 Irc_Client_CmdMode(msg->prefix, msg->params, msg->trailing);
01193 else if (!strncmp(cmd.id.string, "JOIN", 4))
01194 Irc_Client_CmdJoin(msg->prefix, msg->params, msg->trailing);
01195 else if (!strncmp(cmd.id.string, "PART", 4))
01196 Irc_Client_CmdPart(msg->prefix, msg->trailing);
01197 else if (!strncmp(cmd.id.string, "TOPIC", 5))
01198 Irc_Client_CmdTopic(msg->prefix, msg->trailing);
01199 else if (!strncmp(cmd.id.string, "KILL", 4))
01200 Irc_Client_CmdKill(msg->prefix, msg->params, msg->trailing);
01201 else if (!strncmp(cmd.id.string, "KICK", 4))
01202 Irc_Client_CmdKick(msg->prefix, msg->params, msg->trailing);
01203 else if (!strncmp(cmd.id.string, "PING", 4))
01204 Irc_Proto_Pong(irc_nick->string, msg->params, msg->trailing[0] ? msg->trailing : NULL);
01205 else if (!strncmp(cmd.id.string, "ERROR", 5)) {
01206 Irc_Logic_Disconnect(msg->trailing);
01207 Q_strncpyz(popupText, msg->trailing, sizeof(popupText));
01208 UI_Popup(_("Error"), popupText);
01209 } else
01210 Irc_AppendToBuffer("%s", msg->trailing);
01211 break;
01212 }
01213 return qfalse;
01214 }
01215
01216 static qboolean Irc_Proto_ParseServerMsg (const char *txt, size_t txt_len, irc_server_msg_t *msg)
01217 {
01218 const char *c = txt;
01219 const char *end = txt + txt_len;
01220 msg->prefix[0] = '\0';
01221 msg->params[0] = '\0';
01222 msg->trailing[0] = '\0';
01223 if (c < end && *c == ':') {
01224
01225 char *prefix = msg->prefix;
01226 int i = 1;
01227 const size_t size = sizeof(msg->prefix);
01228 ++c;
01229 while (c < end && *c != '\r' && *c != ' ') {
01230 if (i++ >= size)
01231 return qtrue;
01232 *prefix++ = *c++;
01233 }
01234 *prefix = '\0';
01235 ++c;
01236 }
01237 if (c < end && *c != '\r') {
01238
01239 if (c < end && *c >= '0' && *c <= '9') {
01240
01241 char command[4];
01242 int i;
01243 for (i = 0; i < 3; ++i) {
01244 if (c < end && *c >= '0' && *c <= '9')
01245 command[i] = *c++;
01246 else
01247 return qtrue;
01248 }
01249 command[3] = '\0';
01250 msg->type = IRC_COMMAND_NUMERIC;
01251 msg->id.numeric = atoi(command);
01252 } else if (c < end && *c != '\r') {
01253
01254 int i = 1;
01255 const size_t size = sizeof(msg->id.string);
01256 char *command = msg->id.string;
01257 while (c < end && *c != '\r' && *c != ' ') {
01258 if (i++ >= size)
01259 return qtrue;
01260 *command++ = *c++;
01261 }
01262 *command = '\0';
01263 msg->type = IRC_COMMAND_STRING;
01264 } else
01265 return qtrue;
01266 if (c < end && *c == ' ') {
01267
01268 char *params = msg->params;
01269 int i = 1;
01270 const size_t size = sizeof(msg->params);
01271 ++c;
01272
01273
01274 while (c < end && *c != '\r' && *c != ':') {
01275
01276 while (c < end && *c != '\r' && *c != ' ') {
01277 if (i++ >= size)
01278 return qtrue;
01279 *params++ = *c++;
01280 }
01281
01282 if (c + 1 < end && *c == ' ' && *(c + 1) != ':') {
01283 if (i++ >= size)
01284 return qtrue;
01285 *params++ = ' ';
01286 }
01287 if (*c == ' ')
01288 ++c;
01289 }
01290 *params = '\0';
01291
01292 if (c < end && *c == ':') {
01293 char *trailing = msg->trailing;
01294 int i = 1;
01295 const size_t size = sizeof(msg->trailing);
01296 ++c;
01297 while (c < end && *c != '\r') {
01298 if (i++ >= size)
01299 return qtrue;
01300 *trailing++ = *c++;
01301 }
01302 *trailing = '\0';
01303 }
01304 }
01305 }
01306 return qfalse;
01307 }
01308
01313 static qboolean Irc_Proto_Enqueue (const char *msg, size_t msg_len)
01314 {
01315 irc_bucket_message_t *m;
01316 irc_bucket_message_t *n;
01317 const int messageBucketSize = irc_messageBucketSize->integer;
01318 const int characterBucketSize = irc_characterBucketSize->integer;
01319
01320 if (!irc_connected) {
01321 Com_Printf("Irc_Proto_Enqueue: not connected\n");
01322 return qtrue;
01323 }
01324
01325
01326 m = (irc_bucket_message_t*) Mem_Alloc(sizeof(*m));
01327 n = irc_bucket.first_msg;
01328 if (irc_bucket.message_size + 1 <= messageBucketSize && irc_bucket.character_size + msg_len <= characterBucketSize) {
01330 m->msg = (char*) Mem_Alloc(msg_len);
01331 memcpy(m->msg, msg, msg_len);
01332 m->msg_len = msg_len;
01333 m->next = NULL;
01334
01335 if (n) {
01336 while (n->next)
01337 n = n->next;
01338 n->next = m;
01339 } else {
01340 irc_bucket.first_msg = m;
01341 }
01342
01343 ++irc_bucket.message_size;
01344 irc_bucket.character_size += msg_len;
01345 return qfalse;
01346 } else {
01347 Com_Printf("Bucket(s) full. Could not enqueue message\n");
01348 return qtrue;
01349 }
01350 }
01351
01356 static void Irc_Proto_RefillBucket (void)
01357 {
01358
01359 const double characterBucketSize = irc_characterBucketSize->value;
01360 const double characterBucketRate = irc_characterBucketRate->value;
01361 const int ms = CL_Milliseconds();
01362 const int ms_delta = ms - irc_bucket.last_refill;
01363 const double char_delta = (ms_delta * characterBucketRate) / 1000;
01364 const double char_new = irc_bucket.character_token + char_delta;
01365
01366 irc_bucket.character_token = min(char_new, characterBucketSize);
01367
01368 irc_bucket.last_refill = ms;
01369 }
01370
01376 static void Irc_Proto_DrainBucket (void)
01377 {
01378 const double characterBucketBurst = irc_characterBucketBurst->value;
01379 irc_bucket_message_t *msg;
01380
01381
01382 for (msg = irc_bucket.first_msg; msg && msg->msg_len > characterBucketBurst; msg = irc_bucket.first_msg) {
01383 irc_bucket_message_t * const next = msg->next;
01384
01385 --irc_bucket.message_size;
01386 irc_bucket.character_size -= msg->msg_len;
01387
01388 Mem_Free(msg->msg);
01389
01390 irc_bucket.first_msg = next;
01391 }
01392
01393 for (msg = irc_bucket.first_msg; msg; msg = irc_bucket.first_msg) {
01394
01395 Irc_Net_Send(msg->msg, msg->msg_len);
01396 irc_bucket.character_token -= msg->msg_len;
01397
01398 irc_bucket.first_msg = msg->next;
01399
01400 --irc_bucket.message_size;
01401 irc_bucket.character_size -= msg->msg_len;
01402
01403 Mem_Free(msg->msg);
01404 Mem_Free(msg);
01405 }
01406 }
01407
01408
01409
01410
01411
01412
01413
01417 static void Irc_Logic_SendMessages (void)
01418 {
01419 Irc_Proto_RefillBucket();
01420 Irc_Proto_DrainBucket();
01421 }
01422
01429 static void Irc_Logic_ReadMessages (void)
01430 {
01431 qboolean msg_complete;
01432 do {
01433 irc_server_msg_t msg;
01434 if (!Irc_Proto_PollServerMsg(&msg, &msg_complete)) {
01435
01436 if (msg_complete)
01437 Irc_Proto_ProcessServerMsg(&msg);
01438 } else {
01439
01440 UI_Popup(_("Error"), _("Server closed connection"));
01441 Irc_Logic_Disconnect("Server closed connection");
01442 }
01443 } while (msg_complete);
01444 }
01445
01446 static void Irc_Logic_Connect (const char *server, const char *port)
01447 {
01448 if (!Irc_Proto_Connect(server, port)) {
01449
01450 const char * const pass = irc_password->string;
01451 const char * const user = irc_user->string;
01452 irc_connected = qtrue;
01453 if (pass[0] != '\0')
01454 Irc_Proto_Password(pass);
01455 Irc_Proto_Nick(irc_nick->string);
01456 Irc_Proto_User(user, qtrue, user);
01457 } else {
01458 Com_Printf("Could not connect to the irc server %s:%s\n", server, port);
01459 }
01460 }
01461
01462 static void Irc_Logic_Disconnect (const char *reason)
01463 {
01464 if (irc_connected) {
01465 Com_Printf("Irc_Disconnect: %s\n", reason);
01466 Irc_Proto_Quit(reason);
01467 Irc_Proto_Disconnect();
01468 irc_connected = qfalse;
01469 chan = NULL;
01470 Cvar_ForceSet("irc_defaultChannel", "");
01471 Cvar_ForceSet("irc_topic", "Connecting (please wait)...");
01472 UI_ResetData(TEXT_IRCUSERS);
01473 Irc_Input_Deactivate_f();
01474 } else
01475 Com_Printf("Irc_Disconnect: not connected\n");
01476 }
01477
01482 void Irc_Logic_Frame (void)
01483 {
01484 if (irc_connected) {
01485 if (irc_channel->modified) {
01487 Irc_Logic_Disconnect("Switched to another channel");
01488 Irc_Logic_Connect(irc_server->string, irc_port->string);
01489 if (irc_connected)
01490 Irc_Client_Join(irc_channel->string, NULL);
01491 }
01492
01493 if (irc_connected) {
01494 Irc_Logic_SendMessages();
01495 Irc_Logic_ReadMessages();
01496 }
01497 }
01498 irc_channel->modified = qfalse;
01499 }
01500
01501 static const char *Irc_Logic_GetChannelTopic (const irc_channel_t *channel)
01502 {
01503 assert(channel);
01504 return channel->topic;
01505 }
01506
01511 static void Irc_Logic_AddChannelName (irc_channel_t *channel, irc_nick_prefix_t prefix, const char *nick)
01512 {
01513 int i;
01514
01515 irc_user_t* user = channel->user;
01516 for (i = 0; user && i < channel->users; i++, user = user->next) {
01517 if (!strncmp(&(user->key[1]), nick, MAX_VAR - 1))
01518 return;
01519 }
01520 user = Mem_PoolAlloc(sizeof(*user), cl_ircSysPool, 0);
01521 user->next = channel->user;
01522 channel->user = user;
01523
01524 Com_sprintf(user->key, sizeof(user->key), "%c%s", prefix, nick);
01525 channel->users++;
01526 Com_DPrintf(DEBUG_CLIENT, "Add user '%s' to userlist at pos %i\n", user->key, channel->users);
01527 Irc_Client_Names_f();
01528 }
01529
01534 static void Irc_Logic_RemoveChannelName (irc_channel_t *channel, const char *nick)
01535 {
01536 int i;
01537
01538 irc_user_t* user = channel->user;
01539 irc_user_t* predecessor = NULL;
01540 for (i = 0; user && i < channel->users; i++, user = user->next) {
01541 if (!strncmp(&(user->key[1]), nick, sizeof(user->key))) {
01542
01543 if (!predecessor)
01544 channel->user = user->next;
01545
01546 else
01547 predecessor->next = user->next;
01548 Mem_Free(user);
01549 chan->users--;
01550 Irc_Client_Names_f();
01551 return;
01552 }
01553 predecessor = user;
01554 }
01555 }
01556
01557
01558
01559
01560
01561
01562
01567 static qboolean Irc_Net_Connect (const char *host, const char *port)
01568 {
01569 if (irc_stream)
01570 NET_StreamFree(irc_stream);
01571 irc_stream = NET_Connect(host, port);
01572 return irc_stream ? qfalse : qtrue;
01573 }
01574
01578 static qboolean Irc_Net_Disconnect (void)
01579 {
01580 NET_StreamFree(irc_stream);
01581 irc_stream = NULL;
01582 return qtrue;
01583 }
01584
01585 static void Irc_Net_Send (const char *msg, size_t msg_len)
01586 {
01587 assert(msg);
01588 NET_StreamEnqueue(irc_stream, msg, msg_len);
01589 }
01590
01591
01592
01593
01594
01595
01596
01597 static void Irc_Connect_f (void)
01598 {
01599 const int argc = Cmd_Argc();
01600 if (!irc_port || !irc_server)
01601 return;
01602 if (argc >= 2 && argc <= 4) {
01603 #if 0
01604 if (irc_connected)
01605 Irc_Logic_Disconnect("reconnect");
01606 #endif
01607 if (!irc_connected) {
01608
01609 if (argc >= 2)
01610 Cvar_Set("irc_server", Cmd_Argv(1));
01611 if (argc >= 3)
01612 Cvar_Set("irc_port", Cmd_Argv(2));
01613 Com_Printf("Connecting to %s:%s\n", irc_server->string, irc_port->string);
01614 Irc_Logic_Connect(irc_server->string, irc_port->string);
01615 if (irc_connected && argc >= 4)
01616 Irc_Client_Join(Cmd_Argv(3), NULL);
01617 } else
01618 Com_Printf("Already connected.\n");
01619
01620 } else if (irc_server->string[0] != '\0' && irc_port->integer) {
01621 if (!irc_connected)
01622 Cbuf_AddText(va("irc_connect %s %i %s\n", irc_server->string, irc_port->integer, irc_channel->string));
01623 else
01624 Com_Printf("Already connected.\n");
01625 } else
01626 Com_Printf("Usage: %s [<server>] [<port>] [<channel>]\n", Cmd_Argv(0));
01627 }
01628
01629 static void Irc_Disconnect_f (void)
01630 {
01631 Irc_Logic_Disconnect("normal exit");
01632 }
01633
01634 static qboolean Irc_Client_Join (const char *channel, const char *password)
01635 {
01636 if (!Irc_IsChannel(channel)) {
01637 Com_Printf("No valid channel name\n");
01638 return qfalse;
01639 }
01640
01641 if (!Irc_Proto_Join(channel, password)) {
01642 Cvar_ForceSet("irc_defaultChannel", channel);
01643 Com_Printf("Joined channel: '%s'\n", channel);
01644 return qtrue;
01645 } else {
01646 Com_Printf("Could not join channel: '%s'\n", channel);
01647 return qfalse;
01648 }
01649 }
01650
01651 static void Irc_Client_Join_f (void)
01652 {
01653 const int argc = Cmd_Argc();
01654 if (argc >= 2 && argc <= 3) {
01655 const char * const channel = Cmd_Argv(1);
01656
01657 const char * const channel_pass = (argc == 3) ? Cmd_Argv(2) : NULL;
01658 Irc_Client_Join(channel, channel_pass);
01659 } else
01660 Com_Printf("Usage: %s <channel> [<password>]\n", Cmd_Argv(0));
01661 }
01662
01663 static void Irc_Client_Part_f (void)
01664 {
01665 const int argc = Cmd_Argc();
01666 if (argc == 2) {
01667 const char * const channel = Cmd_Argv(1);
01668 Irc_Proto_Part(channel);
01669 } else
01670 Com_Printf("Usage: %s <channel>\n", Cmd_Argv(0));
01671 }
01672
01678 static void Irc_Client_Msg_f (void)
01679 {
01680 char cropped_msg[IRC_SEND_BUF_SIZE];
01681 const char *msg;
01682
01683 if (Cmd_Argc() >= 2)
01684 msg = Cmd_Args();
01685 else if (irc_send_buffer->string[0] != '\0')
01686 msg = irc_send_buffer->string;
01687 else
01688 return;
01689
01690 if (msg[0] != '\0') {
01691 if (irc_defaultChannel->string[0] != '\0') {
01692 if (msg[0] == '"') {
01693 const size_t msg_len = strlen(msg);
01694 memcpy(cropped_msg, msg + 1, msg_len - 2);
01695 cropped_msg[msg_len - 2] = '\0';
01696 msg = cropped_msg;
01697 }
01698 if (!Irc_Proto_Msg(irc_defaultChannel->string, msg)) {
01699
01700 Irc_AppendToBuffer("<%s> %s", irc_nick->string, msg);
01701 }
01702 Cvar_ForceSet("irc_send_buffer", "");
01703 } else
01704 Com_Printf("Join a channel first.\n");
01705 }
01706 }
01707
01708 static void Irc_Client_PrivMsg_f (void)
01709 {
01710 if (Cmd_Argc() >= 3) {
01711 char cropped_msg[IRC_SEND_BUF_SIZE];
01712 const char * const target = Cmd_Argv(1);
01713 const char *msg = Cmd_Args() + strlen(target) + 1;
01714 if (*msg == '"') {
01715 size_t msg_len = strlen(msg);
01716 memcpy(cropped_msg, msg + 1, msg_len - 2);
01717 cropped_msg[msg_len - 2] = '\0';
01718 msg = cropped_msg;
01719 }
01720 Irc_Proto_Msg(target, msg);
01721 } else
01722 Com_Printf("Usage: %s <target> {<msg>}\n", Cmd_Argv(0));
01723 }
01724
01725 static void Irc_Client_Mode_f (void)
01726 {
01727 const int argc = Cmd_Argc();
01728 if (argc >= 3) {
01729 const char * const target = Cmd_Argv(1);
01730 const char * const modes = Cmd_Argv(2);
01731 const char * const params = argc >= 4
01732 ? Cmd_Args() + strlen(target) + strlen(modes) + 2
01733 : NULL;
01734 Irc_Proto_Mode(target, modes, params);
01735 } else
01736 Com_Printf("Usage: %s <target> <modes> {<param>}\n", Cmd_Argv(0));
01737 }
01738
01739 static void Irc_Client_Topic_f (void)
01740 {
01741 const int argc = Cmd_Argc();
01742 if (argc >= 2) {
01743 const char * const channel = Cmd_Argv(1);
01744 if (chan) {
01745 if (argc >= 3) {
01746 char buf[1024];
01747 const char *in = Cmd_Args();
01748 char *out = buf;
01749 if (*in == '"')
01750 in += 2;
01751 in += strlen(channel) + 1;
01752 Q_strncpyz(out, in, sizeof(out));
01753 if (*out == '"') {
01754 size_t out_len;
01755 ++out;
01756 out_len = strlen(out);
01757 assert(out_len >= 1);
01758 out[out_len - 1] = '\0';
01759 }
01760 Irc_Proto_Topic(channel, out);
01761 } else
01762 Com_Printf("%s topic: \"%s\"\n", channel, Irc_Logic_GetChannelTopic(chan));
01763 } else
01764 Com_Printf("Not joined: %s\n", channel);
01765 } else
01766 Com_Printf("Usage: %s <channel> [<topic>]\n", Cmd_Argv(0));
01767 }
01768
01769 #define IRC_MAX_USERLIST 512
01770 static char irc_userListOrdered[IRC_MAX_USERLIST][MAX_VAR];
01771
01772 static void Irc_Client_Names_f (void)
01773 {
01774 irc_user_t* user;
01775 if (chan) {
01776 linkedList_t *irc_names_buffer = NULL;
01777 int i;
01778 user = chan->user;
01779 for (i = 0; i < chan->users; i++) {
01780 if (i >= IRC_MAX_USERLIST)
01781 break;
01782 Q_strncpyz(irc_userListOrdered[i], user->key, MAX_VAR);
01783 user = user->next;
01784 }
01785 if (i > 0) {
01786 qsort((void *)irc_userListOrdered, i, MAX_VAR, Q_StringSort);
01787 while (i--)
01788 LIST_AddString(&irc_names_buffer, irc_userListOrdered[i]);
01789 }
01790 UI_RegisterLinkedListText(TEXT_IRCUSERS, irc_names_buffer);
01791 } else
01792 Com_Printf("Not joined\n");
01793 }
01794
01795 static void Irc_Client_Kick_f (void)
01796 {
01797 const int argc = Cmd_Argc();
01798 if (argc >= 3) {
01799 const char *channel = Cmd_Argv(1);
01800 if (chan) {
01801 const char * const nick = Cmd_Argv(2);
01802 const char *reason;
01803 if (argc >= 4)
01804 reason = Cmd_Args() + strlen(nick) + strlen(channel) + 2;
01805 else
01806 reason = NULL;
01807 Irc_Proto_Kick(channel, nick, reason);
01808 } else
01809 Com_Printf("Not joined: %s.\n", channel);
01810 } else
01811 Com_Printf("Usage: %s <channel> <nick> [<reason>]\n", Cmd_Argv(0));
01812 }
01813
01817 static void Irc_Client_Invite_f (void)
01818 {
01819 const irc_user_t *user;
01820 char buf[128];
01821 const char *node;
01822
01823 if (!CL_OnBattlescape()) {
01824 Com_Printf("You must be connected to a running server to invite others\n");
01825 return;
01826 }
01827
01828 if (!chan) {
01829 UI_PushWindow("irc_popup", NULL);
01830 return;
01831 }
01832
01834 node = "127.0.0.1";
01835 Com_sprintf(buf, sizeof(buf), "%s%s;%s", IRC_INVITE_FOR_A_GAME, node, port->string);
01836 user = chan->user;
01837 while (user) {
01840
01841
01842 const char *name = &(user->key[1]);
01843 if (strcmp(name, irc_nick->string))
01844 Irc_Proto_Msg(name, buf);
01845 user = user->next;
01846 }
01847 }
01848
01849 static void Irc_Client_Who_f (void)
01850 {
01851 if (Cmd_Argc() == 2) {
01852 Irc_Proto_Who(Cmd_Argv(1));
01853 } else
01854 Com_Printf("Usage: %s <usermask>\n", Cmd_Argv(0));
01855 }
01856
01857 static void Irc_Client_Whois_f (void)
01858 {
01859 if (Cmd_Argc() == 2) {
01860 Irc_Proto_Whois(Cmd_Argv(1));
01861 } else
01862 Com_Printf("Usage: %s <nick>\n", Cmd_Argv(0));
01863 }
01864
01865 static void Irc_Client_Whowas_f (void)
01866 {
01867 if (Cmd_Argc() == 2) {
01868 Irc_Proto_Whowas(Cmd_Argv(1));
01869 } else
01870 Com_Printf("Usage: %s <nick>\n", Cmd_Argv(0));
01871 }
01872
01873
01874
01875
01876
01877
01878
01883 static void Irc_UserClick_f (void)
01884 {
01885 const char *name;
01886 int num, cnt;
01887
01888 if (Cmd_Argc() != 2)
01889 return;
01890
01891 if (!chan)
01892 return;
01893
01894 num = atoi(Cmd_Argv(1));
01895 if (num < 0 || num >= chan->users || num >= IRC_MAX_USERLIST)
01896 return;
01897
01898 cnt = min(chan->users, IRC_MAX_USERLIST);
01899 cnt -= num + 1;
01900
01901 name = irc_userListOrdered[cnt];
01902 Cvar_Set("irc_send_buffer", va("%s%s: ", irc_send_buffer->string, &name[1]));
01903 }
01904
01909 static void Irc_UserRightClick_f (void)
01910 {
01911 const char *name;
01912 int num, cnt;
01913
01914 if (Cmd_Argc() != 2)
01915 return;
01916
01917 if (!chan)
01918 return;
01919
01920 num = atoi(Cmd_Argv(1));
01921 if (num < 0 || num >= chan->users || num >= IRC_MAX_USERLIST)
01922 return;
01923
01924 cnt = min(chan->users, IRC_MAX_USERLIST);
01925 cnt -= num + 1;
01926
01927 name = irc_userListOrdered[cnt];
01928 Irc_Proto_Whois(&name[1]);
01929 }
01930
01934 static void Irc_Input_Activate_f (void)
01935 {
01936
01937 if (irc_connected && irc_defaultChannel->string[0] != '\0') {
01938 UI_RegisterText(TEXT_IRCCONTENT, irc_buffer);
01939 } else {
01940 Com_DPrintf(DEBUG_CLIENT, "Irc_Input_Activate: Warning - IRC not connected\n");
01941 UI_PopWindow(qfalse);
01942 UI_PushWindow("irc_popup", NULL);
01943
01944 return;
01945 }
01946 }
01947
01951 static void Irc_Input_Deactivate_f (void)
01952 {
01953 irc_send_buffer->modified = qfalse;
01954
01955 UI_ResetData(TEXT_IRCCONTENT);
01956 }
01957
01958
01959
01960
01961
01962
01963
01964 void Irc_Init (void)
01965 {
01966
01967 Cmd_AddCommand("irc_join", Irc_Client_Join_f, "Join an irc channel");
01968 Cmd_AddCommand("irc_connect", Irc_Connect_f, "Connect to the irc network");
01969 Cmd_AddCommand("irc_disconnect", Irc_Disconnect_f, "Disconnect from the irc network");
01970 Cmd_AddCommand("irc_part", Irc_Client_Part_f, NULL);
01971 Cmd_AddCommand("irc_privmsg", Irc_Client_PrivMsg_f, NULL);
01972 Cmd_AddCommand("irc_mode", Irc_Client_Mode_f, NULL);
01973 Cmd_AddCommand("irc_who", Irc_Client_Who_f, NULL);
01974 Cmd_AddCommand("irc_whois", Irc_Client_Whois_f, NULL);
01975 Cmd_AddCommand("irc_whowas", Irc_Client_Whowas_f, NULL);
01976 Cmd_AddCommand("irc_chanmsg", Irc_Client_Msg_f, NULL);
01977 Cmd_AddCommand("irc_topic", Irc_Client_Topic_f, NULL);
01978 Cmd_AddCommand("irc_names", Irc_Client_Names_f, NULL);
01979 Cmd_AddCommand("irc_kick", Irc_Client_Kick_f, NULL);
01980 Cmd_AddCommand("irc_invite", Irc_Client_Invite_f, "Invite other players for a game");
01981
01982 Cmd_AddCommand("irc_userlist_click", Irc_UserClick_f, "Menu function for clicking a user from the list");
01983 Cmd_AddCommand("irc_userlist_rclick", Irc_UserRightClick_f, "Menu function for clicking a user from the list");
01984
01985 Cmd_AddCommand("irc_activate", Irc_Input_Activate_f, "IRC init when entering the menu");
01986 Cmd_AddCommand("irc_deactivate", Irc_Input_Deactivate_f, "IRC deactivate when leaving the irc menu");
01987
01988
01989 irc_server = Cvar_Get("irc_server", "irc.freenode.org", CVAR_ARCHIVE, "IRC server to connect to");
01990 irc_channel = Cvar_Get("irc_channel", "#ufoai-gamer", CVAR_ARCHIVE, "IRC channel to join into");
01991 irc_channel->modified = qfalse;
01992 irc_port = Cvar_Get("irc_port", "6667", CVAR_ARCHIVE, "IRC port to connect to");
01993 irc_user = Cvar_Get("irc_user", "UFOAIPlayer", CVAR_ARCHIVE, NULL);
01994 irc_password = Cvar_Get("irc_password", "", CVAR_ARCHIVE, NULL);
01995 irc_topic = Cvar_Get("irc_topic", "Connecting (please wait)...", CVAR_NOSET, NULL);
01996 irc_defaultChannel = Cvar_Get("irc_defaultChannel", "", CVAR_NOSET, NULL);
01997 irc_logConsole = Cvar_Get("irc_logConsole", "0", CVAR_ARCHIVE, "Log all irc conversations to game console, too");
01998 irc_showIfNotInMenu = Cvar_Get("irc_showIfNotInMenu", "0", CVAR_ARCHIVE, "Show chat messages on top of the menu stack if we are not in the irc menu");
01999 irc_send_buffer = Cvar_Get("irc_send_buffer", "", 0, NULL);
02000 irc_nick = Cvar_Get("cl_name", "", 0, NULL);
02001
02002 irc_messageBucketSize = Cvar_Get("irc_messageBucketSize", "100", CVAR_ARCHIVE, "max 100 messages in bucket");
02003 irc_messageBucketBurst = Cvar_Get("irc_messageBucketBurst", "5", CVAR_ARCHIVE, "max burst size is 5 messages");
02004 irc_characterBucketSize = Cvar_Get("irc_characterBucketSize", "2500", CVAR_ARCHIVE, "max 2,500 characters in bucket");
02005 irc_characterBucketBurst = Cvar_Get("irc_characterBucketBurst", "250", CVAR_ARCHIVE, "max burst size is 250 characters");
02006 irc_characterBucketRate = Cvar_Get("irc_characterBucketRate", "10", CVAR_ARCHIVE, "per second (100 characters in 10 seconds)");
02007 }
02008
02009 void Irc_Shutdown (void)
02010 {
02011 if (irc_connected)
02012 Irc_Logic_Disconnect("shutdown");
02013 }