static void Irc_Rcon_Flush_f(int redirected, const char *msg, const void *extra) { if (redirected == 1) { // cut into lines size_t len = strlen(msg); char * const outputbuf = (char*) Irc_MemAlloc(len + 1); char *line; memcpy(outputbuf, msg, len); outputbuf[len] = '\0'; for (line = strtok(outputbuf, "\n"); line; line = strtok(NULL, "\n")) { // perform color code translation char * const colored_line = (char*) Irc_MemAlloc(strlen(line) * 2); char *c = colored_line; char chunk[101]; Irc_ColorFilter(line, IRC_COLOR_WSW_TO_IRC, colored_line); // cut line into neat little chunks so the IRC server accepts them len = strlen(c); while (len) { size_t to_copy = min(sizeof(chunk) - 1, len); memcpy(chunk, c, to_copy); chunk[to_copy] = '\0'; Irc_Proto_Msg(rcon_flush_to, chunk); c += to_copy; len -= to_copy; } Irc_MemFree(colored_line); } Irc_MemFree(outputbuf); } }
void Irc_Proto_RemoveListener(irc_command_t cmd, irc_listener_f listener) { if (!immutable_listeners) { // remove now irc_listener_node_t *n, *prev = NULL; switch (cmd.type) { case IRC_COMMAND_NUMERIC: // numeric command, remove from numeric_listeners for (n = numeric_listeners[cmd.numeric]; n; n = n->next) { if (n->listener == listener) { // match, remove if (prev) // not list head, cut prev->next = n->next; else // list head numeric_listeners[cmd.numeric] = n->next; Irc_MemFree(n); break; } prev = n; } break; case IRC_COMMAND_STRING: // string command, remove from string_listeners IRC_IMPORT.Trie_Find(string_listeners, cmd.string, TRIE_EXACT_MATCH, (void**) &n); for (; n; n = n->next) { if (n->listener == listener) { // match, remove if (prev) { // not list head, cut prev->next = n->next; } else { // list head if (n->next) { // has linked nodes, replace head with next node IRC_IMPORT.Trie_Replace(string_listeners, cmd.string, n->next, (void**) &prev); } else { // empty list, remove cmd.string from trie IRC_IMPORT.Trie_Remove(string_listeners, cmd.string, (void**) &prev); } } Irc_MemFree(n); break; } prev = n; } break; } } else { // prepend to removed_listeners for later removal irc_removed_listener_node_t * const n = (irc_removed_listener_node_t*) Irc_MemAlloc(sizeof(irc_removed_listener_node_t)); n->cmd = cmd; n->listener = listener; n->next = removed_listeners; removed_listeners = n; } }
void Irc_Proto_CallListeners(irc_command_t cmd, const char *prefix, const char *params, const char *trailing) { irc_listener_node_t *n; switch (cmd.type) { case IRC_COMMAND_NUMERIC: // numeric command, search in numeric_listeners n = numeric_listeners[cmd.numeric]; break; case IRC_COMMAND_STRING: // string command, search in string_listeners IRC_IMPORT.Trie_Find(string_listeners, cmd.string, TRIE_EXACT_MATCH, (void**) &n); break; default: n = NULL; } if (!n) // no specific listeners found, call generic listeners n = generic_listeners; // call all listeners in list immutable_listeners = true; for (; n; n = n->next) n->listener(cmd, prefix, params, trailing); immutable_listeners = false; // perform pending concurrent removals if (removed_listeners) { irc_removed_listener_node_t *rprev = NULL, *rn = removed_listeners; do { Irc_Proto_RemoveListener(rn->cmd, rn->listener); rprev = rn; rn = rn->next; Irc_MemFree(rprev); } while (rn); removed_listeners = NULL; } }
void Irc_Rcon_Connected_f(void *connected) { qboolean * const c = (qboolean*) connected; if (!irc_rcon) irc_rcon = IRC_IMPORT.Cvar_Get("irc_rcon", "0", CVAR_ARCHIVE); if (!irc_rconTimeout) irc_rconTimeout = IRC_IMPORT.Cvar_Get("irc_rconTimeout", "300", CVAR_ARCHIVE); if (*c) { irc_command_t cmd; cmd.type = IRC_COMMAND_STRING; cmd.string = "PRIVMSG"; Irc_Proto_AddListener(cmd, Irc_Rcon_CmdPrivmsg_f); cmd.string = "QUIT"; Irc_Proto_AddListener(cmd, Irc_Rcon_CmdQuit_f); assert(!irc_rcon_users); IRC_IMPORT.Trie_Create(TRIE_CASE_SENSITIVE, &irc_rcon_users); assert(irc_rcon_users); } else { unsigned int i; trie_dump_t *dump; irc_command_t cmd; cmd.type = IRC_COMMAND_STRING; cmd.string = "PRIVMSG"; Irc_Proto_RemoveListener(cmd, Irc_Rcon_CmdPrivmsg_f); cmd.string = "QUIT"; Irc_Proto_RemoveListener(cmd, Irc_Rcon_CmdQuit_f); assert(irc_rcon_users); IRC_IMPORT.Trie_Dump(irc_rcon_users, "", TRIE_DUMP_VALUES, &dump); for (i = 0; i < dump->size; ++i) Irc_MemFree(dump->key_value_vector[i].value); IRC_IMPORT.Trie_FreeDump(dump); IRC_IMPORT.Trie_Destroy(irc_rcon_users); irc_rcon_users = NULL; } }
bool Irc_Proto_Disconnect(void) { const bool status = Irc_Net_Disconnect(irc_sock); if (!status) { irc_bucket_message_t *msg = irc_bucket.first_msg; irc_bucket_message_t *prev; while (msg) { prev = msg; msg = msg->next; Irc_MemFree(prev->msg); Irc_MemFree(prev); } irc_bucket.first_msg = NULL; irc_bucket.message_size = 0; irc_bucket.character_size = 0; } return status; }
static void Irc_Rcon_CmdQuit_f(irc_command_t cmd, const char *prefix, const char *params, const char *trailing) { assert(irc_rcon); if (Cvar_GetIntegerValue(irc_rcon)) { irc_rcon_user_t *rcon_user; if (IRC_IMPORT.Trie_Remove(irc_rcon_users, prefix, (void**) &rcon_user) == TRIE_OK) Irc_MemFree(rcon_user); } }
static void Irc_Proto_FreeListenerList(irc_listener_node_t *n) { if (n) { irc_listener_node_t *prev = NULL; do { prev = n; n = n->next; Irc_MemFree(prev); } while (n); } }
static bool Irc_Proto_DrainBucket(void) { const double characterBucketBurst = Cvar_GetFloatValue(irc_characterBucketBurst); bool status = false; irc_bucket_message_t *msg; // remove messages whose size exceed our burst size (we can not send them) for ( msg = irc_bucket.first_msg; msg && msg->msg_len > characterBucketBurst; msg = irc_bucket.first_msg ) { irc_bucket_message_t * const next = msg->next; // update bucket sizes --irc_bucket.message_size; irc_bucket.character_size -= msg->msg_len; // free message Irc_MemFree(msg->msg); // dequeue message irc_bucket.first_msg = next; } // send burst of remaining messages for ( msg = irc_bucket.first_msg; msg && !status && irc_bucket.message_token >= 1.0 && msg->msg_len <= irc_bucket.character_token; msg = irc_bucket.first_msg ) { // send message status = Irc_Net_Send(irc_sock, msg->msg, msg->msg_len); --irc_bucket.message_token; irc_bucket.character_token -= msg->msg_len; // dequeue message irc_bucket.first_msg = msg->next; // update bucket sizes --irc_bucket.message_size; irc_bucket.character_size -= msg->msg_len; // free message Irc_MemFree(msg->msg); Irc_MemFree(msg); } return status; }
void Irc_Proto_RemoveGenericListener(irc_listener_f listener) { irc_listener_node_t *prev = NULL, *n = generic_listeners; while (n) { if (n->listener == listener) { if (prev) prev->next = n->next; else generic_listeners = n->next; Irc_MemFree(n); break; } prev = n; n = n->next; } }
static void Irc_Rcon_ProcessMsg(const char *user, const char *msg) { static char nick[IRC_SEND_BUF_SIZE]; irc_nick_prefix_t prefix; char *buf = (char*) Irc_MemAlloc((int) strlen(msg) + 1); const char *word; Irc_ParseName(user, nick, &prefix); strcpy(buf, msg); word = strtok(buf, " "); if (word && !strcasecmp(word, IRC_RCON_PREFIX)) { // it really is an RCON message, not a normal PRIVMSG unsigned int millis = IRC_IMPORT.Milliseconds(); irc_rcon_user_t *rcon_user; if (IRC_IMPORT.Trie_Find(irc_rcon_users, user, TRIE_EXACT_MATCH, (void**) &rcon_user) == TRIE_OK) { // user is already authorized const unsigned int timeout = Cvar_GetIntegerValue(irc_rconTimeout); if (!timeout || ((millis - rcon_user->millis) / 1000) < timeout) { // no timeout, reset user timestamp irc_rcon_user_t *rcon_user_old; rcon_user->millis = millis; IRC_IMPORT.Trie_Replace(irc_rcon_users, user, (void*) rcon_user, (void**) &rcon_user_old); assert(rcon_user == rcon_user_old); word = strtok(NULL, " "); if (word) { if (!strcasecmp(word, IRC_RCON_LOGOUT)) { // user wants to log off Irc_Proto_Msg(nick, "Logged out. You may login again via " IRC_RCON_PREFIX " " IRC_RCON_LOGIN " <rcon_password>."); IRC_IMPORT.Trie_Remove(irc_rcon_users, user, (void**) &rcon_user); Irc_MemFree(rcon_user); } else { // redirect console and execute char cmd_buf[IRC_SEND_BUF_SIZE + 2]; char rcon_buf[16384]; // make it big, we don't trust console redirect char *c = cmd_buf; size_t word_len = strlen(word); memset(rcon_buf, 0, sizeof(rcon_buf)); memcpy(c, word, word_len); c += word_len; for (word = strtok(NULL, " "); word; word = strtok(NULL, " ")) { *c++ = ' '; word_len = strlen(word); memcpy(c, word, word_len); c += word_len; } *c = '\0'; rcon_flush_to = nick; IRC_IMPORT.Com_BeginRedirect(1, rcon_buf, sizeof(rcon_buf) - 1, Irc_Rcon_Flush_f, NULL); IRC_IMPORT.Cmd_ExecuteString(cmd_buf); IRC_IMPORT.Com_EndRedirect(); } } } else { // timeout, inform user Irc_Proto_Msg(nick, "Timed out. Please login via " IRC_RCON_PREFIX " " IRC_RCON_LOGIN " <rcon_password>."); IRC_IMPORT.Trie_Remove(irc_rcon_users, user, (void**) &rcon_user); Irc_MemFree(rcon_user); } } else { // user not authorized, check for IRC_RCON_LOGIN command word = strtok(NULL, " "); if (word && !strcasecmp(word, IRC_RCON_LOGIN)) { const cvar_t * const rcon_password = IRC_IMPORT.Cvar_Get("rcon_password", "", CVAR_ARCHIVE); word = strtok(NULL, " "); if (word && !strcmp(word, Cvar_GetStringValue(rcon_password))) { // password correct, authorize Irc_Proto_Msg(nick, "Logged in. You may now issue commands via " IRC_RCON_PREFIX " <command> {<arg>}. Log out via " IRC_RCON_PREFIX " " IRC_RCON_LOGOUT "."); rcon_user = (irc_rcon_user_t*) Irc_MemAlloc(sizeof(irc_rcon_user_t)); rcon_user->millis = millis; IRC_IMPORT.Trie_Insert(irc_rcon_users, user, (void*) rcon_user); } } } } Irc_MemFree(buf); }