#include #include #include #include #include #include #include "support.h" #include "global.h" #include "server.h" #include "log.h" #include "handler.h" #include "chat.h" #include "connection.h" #include "string_list.h" #include "irc_handler.h" #include "irc.h" #include "utils.h" #include "transfer.h" /* missing * SERVICE * OPER * QUIT * SQUIT * .... */ // first entry must be the irc_error() function! static HANDLER_ENTRY Protocol_IRC[] = { {CIRC_ERROR, irc_error}, // ERROR {CIRC_NOTICE, irc_notice}, // NOTICE {CIRC_JOIN, irc_join}, // JOIN {CIRC_PING, irc_ping}, // PING {CIRC_PRIVMSG, irc_private_message}, // PRIVMSG {CIRC_NICK, irc_nick_changed}, // NICK {CIRC_QUIT, irc_quit}, // QUIT {CIRC_PART, irc_part}, // PART {CIRC_TOPIC_SET, irc_topic_set}, // TOPIC {CIRC_MODE, irc_mode}, // MODE {CIRC_PONG, irc_pong}, // PONG {CIRC_KICK, irc_kick}, // KICK {CIRC_WELCOME, irc_login_ack}, // 1 {CIRC_YOURHOST, irc_message}, // 2 {CIRC_CREATED, irc_message}, // 3 {CIRC_MYINFO, irc_message}, // 4 {CIRC_INFO1, irc_message}, // 8 {CIRC_UMODEIS, irc_user_mode}, // 221 {CIRC_LUSERCLIENT, irc_message}, // 251 {CIRC_LUSEROP, irc_message}, // 252 {CIRC_LUSERUNKNOWN, irc_message}, // 253 {CIRC_LUSERCHANNELS, irc_message}, // 254 {CIRC_LUSERME, irc_message}, // 255 {CIRC_ADMINME, irc_message}, // 256 {CIRC_ADMINLOC1, irc_message}, // 257 {CIRC_ADMINLOC2, irc_message}, // 258 {CIRC_ADMINEMAIL, irc_message}, // 259 {CIRC_TRYAGAIN, irc_error}, // 263 {CIRC_INFO2, irc_message}, // 265 {CIRC_INFO3, irc_message}, // 266 {CIRC_ENDOFWHO, NULL}, // 315 {CIRC_LISTSTART, NULL}, // 321 {CIRC_CHLIST, irc_list}, // 322 {CIRC_LISTEND, irc_list_end}, // 323 {CIRC_CHANNELMODEIS, irc_chmode_is}, // 324 {CIRC_INFO4, NULL}, // 329 {CIRC_NOTOPIC, irc_topic}, // 331 {CIRC_TOPIC, irc_topic}, // 332 {CIRC_TOPIC_SET_BY, irc_topic_set_by}, // 333 {CIRC_VERSION, irc_message}, // 351 {CIRC_WHOREPLY, irc_whoreply}, // 352 {CIRC_NAMREPLY, irc_user_list}, // 353 {CIRC_ENDOFNAMES, NULL}, // 366 {CIRC_BANLIST, irc_banlist}, // 367 {CIRC_ENDOFBANLIST, NULL}, // 368 {CIRC_MOTD, irc_motd}, // 372 {CIRC_MOTD_START, irc_motd}, // 375 {CIRC_MOTD_END, irc_motd}, // 376 // ERROR MESSAGES /* {CIRC_NOSUCHNICK, irc_error}, // 401 {CIRC_NOSUCHCHANNEL, irc_error}, // 403 {CIRC_TOOMANYCHANNELS, irc_error}, // 405 {CIRC_TOOMANYTARGETS, irc_error}, // 407 {CIRC_UNKNOWNCOMMAND, irc_error}, // 421 {CIRC_NONICKNAMEGIVEN, irc_error}, // 431 {CIRC_ERRONEUSNICKNAME, irc_error}, // 432 {CIRC_NICKNAMEINUSE, irc_error}, // 433 {CIRC_NICKCOLLISION, irc_error}, // 436 {CIRC_UNAVAILRESOURCE, irc_error}, // 437 {CIRC_USERNOTINCHANNEL, irc_error}, // 441 {CIRC_NOTONCHANNEL, irc_error}, // 442 {CIRC_NEEDMOREPARAMS, irc_error}, // 461 {CIRC_ALREADYREGISTERED,irc_error}, // 462 {CIRC_BANNED, irc_error}, // 465 {CIRC_KEYSET, irc_error}, // 467 {CIRC_CHANNELISFULL, irc_error}, // 471 {CIRC_UNKNOWNMODE, irc_error}, // 472 {CIRC_INVITEONLYCHAN, irc_error}, // 473 {CIRC_BANNEDFROMCHAN, irc_error}, // 474 {CIRC_BADCHANNELKEY, irc_error}, // 475 {CIRC_BADCHANMASK, irc_error}, // 476 {CIRC_NOCHANMODES, irc_error}, // 477 {CIRC_NOPRIVILEGES, irc_error}, // 481 {CIRC_CHANOPRIVSNEEDED, irc_error}, // 482 {CIRC_CANTKILLSERVER, irc_error}, // 483 {CIRC_RESTRICTED, irc_error}, // 484 {CIRC_UMODEUNKNOWNFLAG, irc_error}, // 501 {CIRC_USERSDONTMATCH, irc_error}, // 502 */ }; static char* sender_nick = NULL; static char* sender_user = NULL; static char* target = NULL; static char* iarg[15] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; static int nargs = 0; #define STRIP_TARGET(message) \ if (message) {\ target = arg((message), 0); \ (message) = arg(NULL, 1); \ } else target = NULL; #define STRIP_COLON(message) \ if (message && *message == ':') message++;\ if (message && *message == 0) message = NULL; #define STRIP_SENDER(data) {\ char* pos;\ if (*data == ':') {\ pos = arg(data, 0);\ if (pos) {\ sender_nick = pos+1;\ data = arg(NULL, 1);\ pos = strchr(sender_nick, '!');\ if (!pos) {\ sender_user = sender_nick;\ } else {\ *pos = 0;\ sender_user = pos+1;\ }\ } else {\ sender_nick = NULL;\ }\ } else {\ sender_nick = NULL;\ }\ } static int get_iargs(char* data) { char* temp; nargs = 0; if (!data) return nargs; while (nargs < 15) { iarg[nargs] = data; nargs++; if (iarg[nargs-1][0] == ':') { iarg[nargs-1] = iarg[nargs-1] + 1; return nargs; } temp = strchr(data, ' '); if (!temp) return nargs; *temp = 0; data = temp+1; } return nargs; } static int is_channel(const char *data) { switch (*data) { case '#': case '+': case '!': case '&': return 1; } return 0; } HANDLER_ENTRY* search_handler_irc(int command) { int i1; int Protocol_Size; Protocol_Size = sizeof(Protocol_IRC) / sizeof(HANDLER_ENTRY); if (command >= 400 && command < 600) return &(Protocol_IRC[0]); // error handler! for (i1 = 0; i1 < Protocol_Size; i1++) { if (Protocol_IRC[i1].code == command) return &(Protocol_IRC[i1]); } return NULL; } HANDLER(irc_error) { STRIP_SENDER(data); STRIP_TARGET(data); STRIP_COLON(data); if (!data) return; server_message(net, "%s", data); l_log(net, "Messages", LOG_MESSAGE, "%s\n", data); if (net->active_server->status == SERVER_CONNECTING) server_disconnect(net->active_server, data, 1); } HANDLER(irc_notice) { STRIP_SENDER(data); if (get_iargs(data) != 2) return; if (!sender_nick || strchr(sender_nick, '.')) { // we asumme its the server here. server_message(net, "%s", iarg[1]); } else if (is_channel(iarg[0])) { // notice to channel public_notice(net, iarg[0], sender_nick, iarg[1]); } else { // private notice, target should be you. private_notice(net, sender_nick, iarg[1]); } } HANDLER(irc_login_ack) { STRIP_SENDER(data); if (get_iargs(data) != 2) return; network_success(net); server_message(net, "%s", iarg[1]); } HANDLER(irc_motd) { server_t *server; STRIP_SENDER(data); if (get_iargs(data) != 2) return; server = net->active_server; motd_message(net, iarg[1]); } HANDLER(irc_join) { chat_page_t* page; STRIP_SENDER(data); if (get_iargs(data) != 1) return; if (!strcmp(sender_nick, net->user.username)) { page = chat_page_search(net, iarg[0], P_PUBLIC, 3); if (!page) { // we havent requested the join! command_send(net, CMD_PART, iarg[0], "I haven't requested to listen to that channel!"); return; } channel_join_ack(page); } else { page = chat_page_search(net, iarg[0], P_PUBLIC, 1); if (!page) return; user_joined(page, sender_nick, -1, -1); } } HANDLER(irc_user_list) { char *channel; char *user; int flags = 0; chat_page_t* page; STRIP_SENDER(data); STRIP_TARGET(data); channel = arg(data, 0); // this is the channel type channel = arg(NULL, 0); if (!channel) return; page = chat_page_search(net, channel, P_PUBLIC, 1); if (!page) return; while ((user = arg(NULL, 0)) != NULL) { if (*user == ':') user++; // thats the first in list if (*user == '@') { user++; if (!strcasecmp(net->user.username, user)) { chat_page_set_op(page, TRUE); } flags = CU_OP; } else if (*user == '+') { user++; if (!strcasecmp(net->user.username, user)) { chat_page_set_op(page, TRUE); } flags = CU_VOICE; } else flags = 0; user_online(page, user, 0, 0, flags); } } HANDLER(irc_ping) { if (data) command_send(net, CMD_PONG, data); } HANDLER(irc_topic) { chat_page_t *page; STRIP_SENDER(data); if (get_iargs(data) < 2) return; page = chat_page_search(net, iarg[1], P_PUBLIC, 1); if (!page) return; channel_set_topic(page, NULL, nargs<3?NULL:iarg[2], 2); } HANDLER(irc_topic_set_by) { chat_page_t* page; time_t was_set; STRIP_SENDER(data); if (get_iargs(data) != 4) return; was_set = strtoul(iarg[3], NULL, 10); page = chat_page_search(net, iarg[1], P_PUBLIC, 1); if (!page) return; chat_print_time_stamp(page, M_PUBLIC); chat_print_prefix(page, 0); chat_print_text(page, M_PUBLIC, "text", "("); chat_print_channel(page, M_PUBLIC, page->name, page->net); chat_print_text(page, M_PUBLIC, "text", ") "); chat_print_colored(page, M_PUBLIC, "error", "Topic was set by "); chat_print_nick(page, M_PUBLIC, iarg[2], net); chat_print_colored(page, M_PUBLIC, "error", " at "); chat_print_colored(page, M_PUBLIC, "text", ctime(&was_set)); } HANDLER(irc_message) { STRIP_SENDER(data); STRIP_TARGET(data); STRIP_COLON(data); server_message(net, "%s", data); l_log(net, "Messages", LOG_MESSAGE, "%s\n", data); } static HANDLER(irc_ctcp) { char* message; if (!strncasecmp(iarg[1], "VERSION", 7)) { command_send(net, CMD_NOTICE, sender_nick, "\001VERSION "VERSION_STRING"\001"); return; } else if (!strncasecmp(data, "ACTION", 6)) { message = arg(iarg[1], 0); message = arg(NULL, 1); if (is_channel(iarg[0])) public_emote(net, iarg[0], sender_nick, message); else private_emote(net, sender_nick, message); /* } else if (!strncasecmp(data, "DCC", 3)) { message = arg(data, 0); message = arg(NULL, 1); irc_dcc(net, sender_nick, message); */ } else { printf("*** irc_ctcp: not implemented [%s]\n", iarg[1]); } } HANDLER(irc_private_message) { STRIP_SENDER(data); if (get_iargs(data) != 2) return; if (!sender_nick) return; if (iarg[1][0] == '\001' && iarg[1][strlen(iarg[1]) - 1] == '\001') { iarg[1][strlen(iarg[1]) - 1] = 0; // ctcp irc_ctcp(net, iarg[1]+1); } else if (is_channel(iarg[0])) { public_message(net, iarg[0], sender_nick, iarg[1]); } else { private_message(net, sender_nick, iarg[1]); } } HANDLER(irc_nick_changed) { chat_page_t* page; GList* dlist; int row; channel_user_t* cu; STRIP_SENDER(data); STRIP_COLON(data); if (!data) return; for (dlist = global.chat_pages; dlist; dlist = dlist->next) { page = dlist->data; if (net && page->net != net) continue; if (page->type == P_PUBLIC) { if (!page->online) continue; if ((cu = search_user_in_list(GTK_CLIST(page->online), sender_nick, &row)) == NULL) continue; g_free(cu->user); cu->user = g_strdup(data); update_channel_user(page, cu); } else if (page->type == P_PRIVATE && !strcmp(page->name, sender_nick)) { g_free(page->name); g_free(page->vname); page->name = g_strdup(data); page->vname = g_strdup(data); chat_page_update_network(page, net); } else { continue; } chat_print_time_stamp(page, M_PUBLIC); chat_print_prefix(page, 0); chat_print_nick(page, M_PUBLIC, sender_nick, net); chat_print_text(page, M_PUBLIC, "error", " changed nickname to "); chat_print_nick(page, M_PUBLIC, data, net); chat_print_text(page, M_PUBLIC, "text", "\n"); } } HANDLER(irc_quit) { GList *dlist; chat_page_t *page; GtkWidget *temp; channel_user_t* cu; int row; STRIP_SENDER(data); STRIP_COLON(data); data = arg(data, 1); if (!sender_nick) return; for (dlist = global.chat_pages; dlist; dlist = dlist->next) { page = dlist->data; if (page->type != P_PUBLIC) continue; if (page->net != net) continue; temp = page->online; if (!temp) continue; cu = search_user_in_list(GTK_CLIST(temp), sender_nick, &row); if (cu) user_parted(net, page, sender_nick, data?data:"Client exited", -1, -1, 1, NULL); } } HANDLER(irc_part) { chat_page_t *page; STRIP_SENDER(data); STRIP_TARGET(data); STRIP_COLON(data); page = chat_page_search(net, target, P_PUBLIC, 1); if (!page) return; if (!strcmp(sender_nick, net->user.username)) { destroy_channel(page); } else { user_parted(net, page, sender_nick, data, -1, -1, 0, NULL); } } HANDLER(irc_topic_set) { chat_page_t* page; STRIP_SENDER(data); STRIP_TARGET(data); STRIP_COLON(data); if (!target) return; page = chat_page_search(net, target, P_PUBLIC, 1); if (!page) return; channel_set_topic(page, sender_nick, data, 2); } HANDLER(irc_user_mode) { chat_page_t *page; (void)net; STRIP_SENDER(data); STRIP_TARGET(data); if (!target) return; if (!data) return; page = chat_page_get_printable(); if (!page) return; chat_print_time_stamp(page, M_PUBLIC); chat_print_prefix(page, 0); chat_print_text(page, M_PUBLIC, "error", "Your usermode: "); chat_print_text(page, M_PUBLIC, "text", data); chat_print_text(page, M_PUBLIC, "text", "\n"); } static char* chmode2int_irc(chat_page_t* page, int change) { int plus = 1; static char lop_mode[1024] = ""; char* lop_mode_pos = lop_mode; int lop_plus; char* pos; int i2; int arg_pos = 1; while (arg_pos < nargs) { pos = iarg[arg_pos]; plus = 1; lop_plus = -1; while (*pos) { switch (*pos) { case '-': plus = 0; break; case '+': plus = 1; break; case 'o': arg_pos++; if (arg_pos < nargs) user_flagged(page, sender_nick, iarg[arg_pos], plus, CU_OP, NULL); break; case '0': arg_pos++; if (arg_pos < nargs) user_flagged(page, sender_nick, iarg[arg_pos], plus, CU_CREATOR, NULL); break; case 'v': arg_pos++; if (arg_pos < nargs) user_flagged(page, sender_nick, iarg[arg_pos], plus, CU_VOICE, NULL); break; case 'b': arg_pos++; if (arg_pos < nargs) user_flagged(page, sender_nick, iarg[arg_pos], plus, CU_BANNED, NULL); break; case 'l': arg_pos++; if (arg_pos < nargs) channel_set_limit(page, change?sender_nick:NULL, atoi(iarg[arg_pos]), 0); break; case 'k': arg_pos++; // FIXME: support channel keys break; default: for (i2 = 0; i2 < CMODE_NUMBER; i2++) { if (((1< [%d ms]", target, timestamp_difference(stamp)); global.userstamp = g_list_remove(global.userstamp, stamp); g_free(stamp->user); g_free(stamp); } } HANDLER(irc_whoreply) { char* nick; char* channel; char* host; char* user; char* server; chat_page_t* page; STRIP_SENDER(data); STRIP_TARGET(data); if (!data) return; channel = arg(data, 0); user = arg(NULL, 0); host = arg(NULL, 0); server = arg(NULL, 0); nick = arg(NULL, 0); if (!nick) return; page = chat_page_get_printable(); chat_print_time_stamp(page, M_PUBLIC); chat_print_prefix(page, 0); chat_print_text(page, M_PUBLIC, "user", "<"); chat_print_nick(page, M_PUBLIC, nick, net); chat_print_text(page, M_PUBLIC, "user", "> "); chat_print_text(page, M_PUBLIC, "error", "("); chat_print_text(page, M_PUBLIC, "text", user); chat_print_text(page, M_PUBLIC, "error", "@"); chat_print_text(page, M_PUBLIC, "text", host); chat_print_text(page, M_PUBLIC, "error", ") "); chat_print_text(page, M_PUBLIC, "error", "is on "); chat_print_text(page, M_PUBLIC, "text", server); chat_print_text(page, M_PUBLIC, "user", " ["); chat_print_network(page, M_PUBLIC, net, 0, 1); chat_print_text(page, M_PUBLIC, "user", "]\n"); } HANDLER(irc_list) { STRIP_SENDER(data); if (get_iargs(data) != 4) return; channel_entry_new(net, iarg[1], iarg[2], iarg[3], NULL, NULL); } HANDLER(irc_list_end) { chat_page_t* page; (void)data; page = chat_page_search(net, "Channels", P_CHANNEL, 3); if (page) channel_list_end(page); } HANDLER(irc_banlist) { time_t l1; char* who; char* when; char* channel; char* nick; GtkWidget *temp; (void)net; if (!global.ban_win) return; temp = lookup_widget(global.ban_win, "clist3"); STRIP_SENDER(data); STRIP_TARGET(data); channel = arg(data, 0); nick = arg(NULL, 0); who = arg(NULL, 0); when = arg(NULL, 0); data = strchr(nick, '!'); if (data) *data = 0; if (!when) return; strcpy(tstr[0], nick); strcpy(tstr[1], who); strcpy(tstr[3], ""); l1 = strtoul(when, NULL, 10); sprintf(tstr[2], "%s", ctime(&l1)); tstr[2][strlen(tstr[2])-1] = 0; gtk_clist_append(GTK_CLIST(temp), list); } HANDLER(irc_kick) { chat_page_t *page; char* who; char* reason; STRIP_SENDER(data); STRIP_TARGET(data); who = arg(data, 0); reason = arg(NULL, 1); STRIP_COLON(reason); if (!who) return; page = chat_page_search(net, target, P_PUBLIC, 1); if (!page) return; user_parted(net, page, who, reason, -1, -1, 2, sender_nick); if (!strcmp(who, net->user.username)) { destroy_channel(page); } } /* HANDLER(irc_channel_mode) { char* from; char* channel; char* mode; char* param; int set = 1; char* pos; chat_page_t* page; channel_user_t* cu; int row; int chmode1 = 0; int chmode2 = 0; from = arg(data, 0); channel = arg(NULL, 0); mode = arg(NULL, 1); param = strchr(mode, ' '); mode = arg(mode, 0); page = chat_page_search(net, channel, P_PUBLIC, 1); if (!page) return; if (*mode == ':') mode++; chat_print_time_stamp(page, M_PUBLIC); chat_print_prefix(page, 0); chat_print_nick(page, M_PUBLIC, from, net); chat_print_text(page, M_PUBLIC, "error", " has changed channel mode to "); chat_print_text(page, M_PUBLIC, "text", mode); if (param) { chat_print_text(page, M_PUBLIC, "text", " "); chat_print_text(page, M_PUBLIC, "text", param+1); } chat_print_text(page, M_PUBLIC, "user", "\n"); pos = mode; while (*pos) { switch (*pos) { case '-': set = 0; break; case '+': set = 1; break; case 'O': case 'o': param = arg(NULL, 0); if (!param) break; if (!strcasecmp(net->user.username, param)) { if (set) chat_page_set_op(page, TRUE); else chat_page_set_op(page, FALSE); } cu = search_user_in_list(GTK_CLIST(page->online), param, &row); if (cu) { if (!set) cu->speed = 0; else cu->speed = 10; update_channel_user(page, cu); } break; case 'v': param = arg(NULL, 0); if (!param) break; cu = search_user_in_list(GTK_CLIST(page->online), param, &row); if (cu) { if (!set) cu->speed = 0; else cu->speed = 5; update_channel_user(page, cu); } break; case 't': case 'p': case 'i': case 'm': if (set) chmode1 |= irc_channelmode2int(*pos); else chmode2 |= irc_channelmode2int(*pos); break; case 'a': case 'n': case 'q': case 'r': case 's': printf("** channel flag %c not supported\n", *pos); break; case 'k': case 'b': case 'e': case 'I': param = arg(NULL, 0); break; case 'l': param = arg(NULL, 0); if (!param) break; channel_set_limit(page, atoi(param), 0); break; default: printf("** channel flag %c not recognized\n", *pos); break; } pos++; } if (chmode1 > 0) channel_set_mode(page, chmode1, 1, 0); if (chmode2 > 0) channel_set_mode(page, chmode2, 0, 0); } */ /* HANDLER(irc_ctcp) { char* command; char* channel; char* user; char* message; char* buffer; user = arg(data, 0); channel = arg(NULL, 0); command = arg(NULL, 0); if (!command) return; if (!strcasecmp(command, "VERSION")) { if (INTERN_MICRO_VERSION) buffer = g_strdup_printf ("%s \001VERSION Lopster "VERSION".%d\001", user, INTERN_MICRO_VERSION); else buffer = g_strdup_printf ("%s \001VERSION Lopster "VERSION"\001", user); // send_command(CMD_NOTICE, buffer, net); g_free(buffer); } else if (!strcasecmp(command, "ACTION")) { message = arg(NULL, 1); if (!message) message = ""; if (*message == ':') message++; buffer = g_strdup_printf("%s %s \"%s\"", channel, user, message); std_emote(net, buffer); g_free(buffer); } else { printf("*** irc_ctcp: not implemented %s\n", command); } } */