/* * commands.cpp * * (C) 2001-2002 Murat Deligonul * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "autoconf.h" #include #include #include #include #include #include #include #include "commands.h" #include "server.h" #include "dynbuff.h" #include "general.h" #include "conn.h" #include "messages.h" #include "ircaddr.h" #include "debug.h" /******************************************************* * Stupid hacks to work around GCC 2.95.x compiler bugs ******************************************************* * It chokes if it encounters to pointer-to-member function inside * a struct, so if we detect buggy GCC, we just store function * pointers in array, and replace relevant struct element with an * array index */ #ifdef H #undef H #endif #ifdef GCC_COMPILER_BUG #warning Buggy GCC 2.95.x detected -- trying to work around ... #define H(x,y) (x) const cmd_handler_t handlers[] = { &conn::do_registration_cmd, /* 0 */ &conn::do_login_cmd, &conn::do_conn_cmd, &conn::do_misc_cmd, &conn::do_ident_cmd, &conn::do_motd_cmd, /* 5 */ &conn::do_help_cmd, &conn::do_vhost_cmd, &conn::do_vhosts_cmd, &conn::do_detach_cmd, &conn::do_reattach_cmd, /* 10 */ &conn::do_disconnect_cmd, &conn::do_log_cmd, &conn::do_sessions_cmd, &conn::do_traffic_cmd, &conn::do_set_cmd, /* 15 */ &conn::do_echo_cmd, &conn::do_save_cmd, &conn::do_allowed_cmd, &conn::do_debug_cmd, &conn::do_version_cmd, /* 20 */ &conn::do_about_cmd, &conn::do_ezb_cmd, &conn::do_quit_cmd, &conn::do_privmsg_cmd, &conn::do_status_cmd, /* 25 */ &conn::do_rehash_cmd, &conn::do_write_cmd, &conn::do_hash_cmd, &conn::do_adminmisc_cmd, &conn::do_whois_cmd, /* 30 */ &conn::do_trace_cmd, &conn::do_reload_cmd, #ifdef __DEBUG__ &conn::do_dccsend_cmd, /* 33 */ #else 0, /* 33 */ #endif 0, 0, /* 35 -- reserved for future use ???*/ 0, 0, 0, 0, &conn::do_privmsg_incoming_cmd, /* 40 */ &conn::do_nick_incoming_cmd, &conn::do_mode_incoming_cmd, &conn::do_join_incoming_cmd, &conn::do_part_incoming_cmd, &conn::do_kick_incoming_cmd, /* 45 */ &conn::do_topic_incoming_cmd, &conn::do_notice_incoming_cmd, &conn::do_quit_incoming_cmd, &conn::do_servinfo_cmd, &conn::do_pong_incoming_cmd, /* 50 */ &conn::do_ping_incoming_cmd, &conn::do_error_incoming_cmd }; #else #define H(x,y) (y) #endif /* * The NDMs are No Direct Match (when Connected). * 'ezb' command is directly matched when connected. * the other commands are not. * * PPE = hack to make sure Ping/pong/error don't match * on initial command scan */ const struct cmd command_table[] = { /* str int-id req_flag bad_flag handler help */ /* -------------------------------------------------------------------------------------------------------------------------------------------*/ { "USER", CMD_USER, 0, NDM | USERED | CONNECTING, H(0, &conn::do_registration_cmd), __HELP_ENTRY(user)}, { "NICK", CMD_NICK, 0, NDM | CONNECTING, H(0, &conn::do_registration_cmd), __HELP_ENTRY(nick)}, { "PASS", CMD_PASS, 0, NDM | PWED | CONNECTING, H(1, &conn::do_login_cmd), __HELP_ENTRY(pass)}, { "LOGIN", CMD_LOGIN, 0, NDM | PWED | CONNECTING, H(1, &conn::do_login_cmd), __HELP_ENTRY(login)}, { "CONN", CMD_CONN, REGISTERED, BOUNCED | CONNECTING, H(2, &conn::do_conn_cmd), __HELP_ENTRY(conn)}, { "CANCEL" , CMD_CANCEL, REGISTERED | CONNECTING, 0, H(3, &conn::do_misc_cmd), __HELP_ENTRY(cancel)}, { "IDENT", CMD_IDENT, REGISTERED, NDM | CONNECTING, H(4, &conn::do_ident_cmd), __HELP_ENTRY(ident)}, { "MOTD", CMD_MOTD, REGISTERED, NDM | CONNECTING, H(5, &conn::do_motd_cmd), __HELP_ENTRY(motd)}, { "HELP", CMD_HELP, REGISTERED, NDM | CONNECTING, H(6, &conn::do_help_cmd), __HELP_ENTRY(help)}, { "INTERFACE", CMD_INTERFACE, REGISTERED, BOUNCED | CONNECTING, H(7, &conn::do_vhost_cmd), __HELP_ENTRY(vhost)}, { "VHOST", CMD_INTERFACE, REGISTERED, BOUNCED | CONNECTING, H(7, &conn::do_vhost_cmd), __HELP_ENTRY(vhost)}, { "VHOSTS", CMD_VHOSTS, REGISTERED, NDM | CONNECTING, H(8, &conn::do_vhosts_cmd), __HELP_ENTRY(vhosts)}, { "DETACH", CMD_DETACH, REGISTERED, NDM | DETACHED | CONNECTING,H(9, &conn::do_detach_cmd), __HELP_ENTRY(detach)}, { "REATTACH", CMD_REATTACH, REGISTERED, NDM | CONNECTING, H(10, &conn::do_reattach_cmd), __HELP_ENTRY(reattach)}, { "ATTACH", CMD_REATTACH, REGISTERED, NDM | CONNECTING, H(10, &conn::do_reattach_cmd), __HELP_ENTRY(reattach)}, { "DISCONNECT", CMD_DISCONNECT, REGISTERED | BOUNCED, NDM | CONNECTING, H(11, &conn::do_disconnect_cmd), __HELP_ENTRY(disconnect)}, { "LOG", CMD_LOG, REGISTERED, NDM | CONNECTING, H(12, &conn::do_log_cmd), __HELP_ENTRY(log)}, { "SESSIONS", CMD_SESSIONS, REGISTERED, CONNECTING, H(13, &conn::do_sessions_cmd), __HELP_ENTRY(sessions)}, { "TRAFFIC", CMD_TRAFFIC, REGISTERED, NDM | CONNECTING, H(14, &conn::do_traffic_cmd), __HELP_ENTRY(traffic)}, { "SET", CMD_SET, REGISTERED, NDM | CONNECTING, H(15, &conn::do_set_cmd), __HELP_ENTRY(set)}, { "ECHO", CMD_ECHO, REGISTERED, NDM, H(16, &conn::do_echo_cmd), __HELP_ENTRY(echo)}, { "SAVE", CMD_SAVE, REGISTERED, NDM | CONNECTING, H(17, &conn::do_save_cmd), __HELP_ENTRY(save)}, { "ALLOWED", CMD_ALLOWED, REGISTERED, NDM | CONNECTING, H(18, &conn::do_allowed_cmd), __HELP_ENTRY(allowed)}, { "DEBUG", CMD_DEBUG, REGISTERED, NDM, H(19, &conn::do_debug_cmd), __HELP_ENTRY(debug)}, { "VERSION", CMD_VERSION, REGISTERED, NDM | CONNECTING, H(20, &conn::do_version_cmd), __HELP_ENTRY(version)}, { "ABOUT", CMD_ABOUT, REGISTERED, NDM | CONNECTING, H(21, &conn::do_about_cmd), __HELP_ENTRY(about)}, { "EZBOUNCE", CMD_EZBOUNCE, REGISTERED, CONNECTING, H(22, &conn::do_ezb_cmd), __HELP_ENTRY(ezbounce)}, { "EZB", CMD_EZBOUNCE, REGISTERED, CONNECTING, H(22, &conn::do_ezb_cmd), __HELP_ENTRY(ezbounce)}, { "QUIT", CMD_QUIT, REGISTERED | BOUNCED, CONNECTING, H(23, &conn::do_quit_cmd), __HELP_ENTRY(quit)}, { "PRIVMSG", CMD_PRIVMSG, REGISTERED | BOUNCED, CONNECTING, H(24, &conn::do_privmsg_cmd), NULL}, { "STATUS", CMD_STATUS, REGISTERED | ADMIN, NDM | CONNECTING, H(25, &conn::do_status_cmd), __HELP_ENTRY(status)}, { "REHASH", CMD_REHASH, REGISTERED | ADMIN, NDM | CONNECTING, H(26, &conn::do_rehash_cmd), __HELP_ENTRY(rehash)}, { "WRITE", CMD_WRITE, REGISTERED | ADMIN, NDM | CONNECTING, H(27, &conn::do_write_cmd), __HELP_ENTRY(write)}, { "HASH", CMD_HASH, REGISTERED | ADMIN, NDM | CONNECTING, H(28, &conn::do_hash_cmd), __HELP_ENTRY(hash)}, { "KILL", CMD_KILL, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(kill)}, { "DIE" , CMD_DIE, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(die)}, { "DIENOW", CMD_DIENOW, REGISTERED | ADMIN, NDM | CONNECTING, H(29, &conn::do_adminmisc_cmd), __HELP_ENTRY(dienow)}, { "WHOIS", CMD_WHOIS, REGISTERED | ADMIN, NDM | CONNECTING, H(30, &conn::do_whois_cmd), __HELP_ENTRY(whois)}, { "TRACE", CMD_TRACE, REGISTERED | ADMIN, NDM | CONNECTING, H(31, &conn::do_trace_cmd), __HELP_ENTRY(trace)}, { "RELOAD", CMD_RELOAD, REGISTERED | ADMIN, NDM | CONNECTING, H(32, &conn::do_reload_cmd), __HELP_ENTRY(reload)}, #ifdef __DEBUG__ { "CHATSEND", CMD_CHATSEND, REGISTERED | ADMIN, NDM | CONNECTING, H(33, &conn::do_dccsend_cmd), NULL}, { "DCCSEND", CMD_DCCSEND, REGISTERED | ADMIN, NDM | CONNECTING, H(33, &conn::do_dccsend_cmd), NULL}, { "ZOMBIFY", CMD_ZOMBIFY, REGISTERED, NDM, H(34, &conn::do_misc_cmd), NULL}, #endif }; /* table for incoming traps */ const struct cmd incoming_command_table[] = { {"PRIVMSG", INCOMING_PRIVMSG, 0, 0, H(40, &conn::do_privmsg_incoming_cmd)}, {"NICK", INCOMING_NICK, 0, 0, H(41, &conn::do_nick_incoming_cmd)}, {"MODE", INCOMING_MODE, DETACHED, 0, H(42, &conn::do_mode_incoming_cmd)}, {"JOIN", INCOMING_JOIN, 0, 0, H(43, &conn::do_join_incoming_cmd)}, {"PART", INCOMING_PART, 0, 0, H(44, &conn::do_part_incoming_cmd)}, {"KICK", INCOMING_KICK, 0, 0, H(45, &conn::do_kick_incoming_cmd)}, {"TOPIC", INCOMING_TOPIC, DETACHED, 0, H(46, &conn::do_topic_incoming_cmd)}, {"NOTICE", INCOMING_NOTICE, DETACHED, 0, H(47, &conn::do_notice_incoming_cmd)}, {"QUIT", INCOMING_QUIT, DETACHED, 0, H(48, &conn::do_quit_incoming_cmd)}, {"003", INCOMING_003, 0, GOTSERVINFO, H(49, &conn::do_servinfo_cmd)}, {"004", INCOMING_004, 0, GOTSERVINFO, H(49, &conn::do_servinfo_cmd)}, {"005", INCOMING_005, 0, GOTSERVINFO3, H(49, &conn::do_servinfo_cmd)}, {"PONG", INCOMING_PONG, 0, 0, H(50, &conn::do_pong_incoming_cmd)}, {"PING", INCOMING_PING, PPE, 0, H(51, &conn::do_ping_incoming_cmd)}, {"ERROR", INCOMING_ERROR, DETACHED | PPE, 0, H(52, &conn::do_error_incoming_cmd)}, }; int conn::init_command_hash(void) { int size = sizeof(command_table) / sizeof(struct cmd); int i; for (i = 0; i < size; ++i) cmdhash.insert(&command_table[i]); size = sizeof(incoming_command_table) / sizeof(struct cmd); for (i = 0; i < size; ++i) incoming_hash.insert(&incoming_command_table[i]); return 1; } int htbl::insert(const struct cmd * c) { int idx = hash(c->msg) % buckets; list_add(&table[idx], (void *) c); return 1; } /* note: str must be capitalized */ const struct cmd * htbl::lookup(const char * str, int flags) { int idx = hash(str) % (buckets); struct hashlist * l = &table[idx]; struct node * n; const struct cmd * c; lookups ++; for (n = l->head; n; n = n->next) { c = (const struct cmd *) n->data; /* No required flags for this command or we meet all required flags */ if (((flags & c->req_flags) == c->req_flags) || (!c->req_flags)) if (!strcmp(c->msg, str)) /* Check that we don't have any of the bad flags */ if (hits++, !c->bad_flags || !((flags & c->bad_flags) & c->bad_flags)) /* Found it */ return c; } return 0; } /* * Show some info on 'c' */ void conn::show_whois(conn * c) const { char timebuff[10]; char flagbuff[10]; c->mkstat(flagbuff); duration(ircproxy_time() - c->connect_time, 0, timebuff, sizeof(timebuff)); cprintf("---> Connection ID %d\r\n",c->id); cprintf(" Online: %s\r\n",timebuff); cprintf(" From: %s\r\n", inet_ntoa(c->client_saddr.sin_addr)); cprintf(" IRC Nick: %s\r\n", c->uinfo.irc->nick); if (c->server) cprintf(" Connected to: %s:%d (running %s)\r\n", inet_ntoa(c->serv_saddr.sin_addr), c->uinfo.port, c->uinfo.serverversion); cprintf(" Flags: %s (0x%X)\r\n", flagbuff, c->stat); cprintf("<--- [end of info]\r\n"); } /* Command functions below. * Type: int conn::do_XXX_cmd(int type, int argc, char * argv[]) * .. or CMDFUNC(XXX) * * Return: * 0 - Unhandled, relay to IRC server * 1 - Handled properly * -1 - Handled, but destroy object immediately */ /* int */ CMDFUNC(registration) /* (int type, int argc, char ** argv) */ { switch (type) { case CMD_USER: /* Check for non-blank non-whitespace valid arguments */ if (!argc) return 1; uinfo.usercmd = my_strdup(argv[0]); setf(USERED); if (checkf(USERED) && checkf(NICKED)) goto success; return 1; case CMD_NICK: { if (!argc) return 1; if (strlen(argv[1]) > NICKNAME_LENGTH) argv[1][NICKNAME_LENGTH] = 0; /* First time */ delete[] uinfo.irc->nick; uinfo.irc->nick = my_strdup(argv[1]); /* If we all these set, then merely exit with updated nickname information */ if (checkf(USERED) && checkf(NICKED) && checkf(PWED)) { client->printf(":%s!unknown@%s NICK :%s\r\n", uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr), argv[1]); printlog("NICK CHANGE: %s --> %s\n", addr(), argv[1]); return 1; } setf(NICKED); if (checkf(USERED) && checkf(NICKED)) goto success; return 1; } } return 1; success: /* Got password? */ if (!checkf(PWED)) cprintf("[awaiting login/pass command]\r\n"); /* Yes, we have password */ else if (checkf(PWED) && checkf(NICKED) && checkf(USERED)) on_client_connect(0); return 1; } CMDFUNC(login) { char login[50], pw[50]; userdef * u = 0; int as_ptr = 3; if (!argc) return 1; safe_strcpy(login, argv[1], sizeof(login)); if (type == CMD_PASS) { if (gettok(login, pw, sizeof(pw), ':',2)) { *strchr(login, ':') = 0; as_ptr = 2; } else { strcpy(pw, login); strcpy(login, "default"); as_ptr = 2; } } else { if (argc < 2) return 1; safe_strcpy(pw, argv[2], sizeof(pw)); } u = userdef::find(users, login); if (!u) { cprintf("Invalid username: `%s'\r\n", login); return 1; } char * host = inet_ntoa(client_saddr.sin_addr); unsigned short port =ntohs(client_saddr.sin_port); switch (u->add(this, &rulesets, pw, host, port) ) { case 1: /* ->add() gave us rulesets, now check them all */ char reason[100]; switch (ruleset::list_is_allowed_from(&rulesets, host, port, reason, sizeof(reason))) { case 0: u->remove(this); goto no_authorization; case -1: cprintf(msg_banned, reason, reason); printlog("Connection DENIED: (banned) from %s on port %d\n", host, port); u->remove(this); on_client_disconnect(0); return -1; } /* Ready to go */ user = u; config = new user_options(&user->cfg); config->clear(OPT_PASSWORD); /* for admins, just wipe out the list. they can connect wherever the * hell they want, and don't need to bother with limits */ if (config->checkf(OPT_USER_IS_ADMIN)) rulesets.clear(); else ruleset::list_register_from(&rulesets, host, port); setf(PWED | FROM_RULESETS_REGISTERED); /* Auto server: */ if (argv[as_ptr]) config->set(OPT_AUTOSERVER, argv[as_ptr]); if (checkf(USERED) && checkf(NICKED)) on_client_connect(0); return 1; case 0: cprintf("LOGIN: Incorrect password for `%s'\r\n", login); printlog("Incorrect password from %s\n", addr()); if (++failed_passwords >= pcfg.max_failed_passwords && pcfg.max_failed_passwords) { cprintf(msg_too_many_failures); printlog("... disconnected client for giving too many incorrect passwords!\n"); on_client_disconnect(0); return -1; } break; case -1: no_authorization: on_client_disconnect(0); cprintf("No authorization\r\n"); printlog("DENIED: Connection from %s: No authorization\n", addr()); return -1; } return 0; } /* the /conn command */ CMDFUNC(conn) { char port[6] = "", * pass = 0; int tmp,i=1,inssl=0; u_short p; if (!argc) { cprintf(msg_not_enuff_args, "CONN"); return 1; } if (strcasecmp("-ssl",argv[i]) == 0) { if (argc<2) { cprintf(msg_not_enuff_args, "CONN"); return 1; } inssl=1; i++; } if (gettok(argv[i], port, sizeof(port), ':', 2)) *strchr(argv[i],':') = 0; p = (strcmp(port, "") == 0) ? 6667: (u_short) atoi(port); pass = argv[i+1]; /* Let's see if we CAN connect to this place */ if (!can_connect(argv[i], p)) { printlog("Connection attempt DENIED: %s to %s:%d\n", addr(), server, p); return 1; } /* we can now try to connect.. */ uinfo.server = my_strdup(argv[i]); printlog("Connection attempt: %s to %s:%d\n", addr(), argv[i], p); if ((tmp = do_connect(argv[i], p,pass,inssl)) == 1) { in_addr in = { config->get(PREF_VHOST) }; cprintf("[\002connecting to\002]: %s:%d\r\n", argv[i], p); cprintf("[\002vhost\002]: %s\r\n", inet_ntoa(in)); cprintf("Use `/quote cancel' to bail out\r\n"); } else { const char *err; if (tmp == -1) err = "unknown host"; else err = strerror(errno); cprintf(msg_conn_failed, argv[i], err); printlog("Connection attempt FAILED: %s to %s:%d (failed prematurely): %s\n", addr(), argv[i], p, err); } return 1; } /* ezb help system */ CMDFUNC(help) { if (!argc) { cprintf_multiline(__HELP_ENTRY(command_list)); return 1; } ToUpper(argv[1]); const struct cmd * c = cmdhash.lookup(argv[1], 0); if (!c || !c->help) cprintf("HELP: no help found for command %s\n", argv[1]); else cprintf_multiline(c->help); return 1; } /* VHOST & INTERFACE commands */ CMDFUNC(vhost) { if (!config->checkf(OPT_ENABLE_VHOST_COMMAND)) { cprintf("This command has been disabled here.\r\n"); return 1; } if (!argc) { /* * No arguments given: set vhost to default * interface */ config->set(PREF_VHOST, user->cfg.get(PREF_VHOST)); cprintf("VHOST: Set vhost to default interface\r\n"); return 1; } /* * Arguments given: check if the vhost exists in the table * and then set it if so * (ignore whats on the table for admin) */ if (!checkf(ADMIN)) { list_iterator vi(vhosts); while (vi.has_next()) { char * r = vi.next(); if (!strcasecmp(r, "all") || !strcasecmp(r, argv[1])) goto found; } if (user->vhosts) { list_iterator vi2(user->vhosts); while (vi2.has_next()) { char * r = vi2.next(); if (!strcasecmp(r, "all") || !strcasecmp(r, argv[1])) goto found; } } cprintf("Host is on not on my vhost list.\r\n"); return 1; } found: /* Resolve interface and fill it into client->iface */ struct in_addr in; switch (fill_in_addr(argv[1], &in)) { case 0: cprintf(msg_interface_failed, strerror(errno)); break; case 1: config->set(PREF_VHOST, in.s_addr); cprintf(msg_interface_set, argv[1], inet_ntoa(in)); break; case -1: cprintf(msg_interface_failed, "unknown host\n"); default: break; } return 1; } CMDFUNC(vhosts) { if (!config->checkf(OPT_ENABLE_VHOST_COMMAND)) { cprintf("This command has been disabled here.\r\n"); return 1; } cprintf("Available virtual hosts:\r\n"); /* BLAH! duplicated code */ list_iterator vi(vhosts); int n = 0; while (vi.has_next()) cprintf("%d) %s=\r\n", ++n, vi.next()); if (user->vhosts) { list_iterator vi2(user->vhosts); while (vi2.has_next()) cprintf("%d) %s\r\n", ++n, vi2.next()); } cprintf("End of list.\r\n"); return 1; } CMDFUNC(motd) { if (pcfg.motdfile) show_motd(); else cprintf("There is no MOTD.\r\n"); return 1; } CMDFUNC(disconnect) { on_server_disconnect(); cprintf("Ok, broke your connection to the irc server.\r\n"); printlog("DISCONNECT: %s from IRC server by his request.\n", addr()); return 1; } /* * Set a fake ident. Fake idents require MDIDENTD to be running */ CMDFUNC(ident) { if (!config->checkf(OPT_ENABLE_FAKE_IDENTS)) { cprintf("Sorry, fake idents have been disabled here.\r\n"); return 1; } if (argc) { config->set(PREF_FAKE_IDENT, argv[1]); cprintf("Ok, set your fake ident to %s\r\n", argv[1]); } else cprintf(msg_not_enuff_args, "IDENT"); return 1; } CMDFUNC(quit) { if (config->decide(PREF_AUTO_DETACH)) { do_auto_detach(); return 1; } return 0; } /* other commands */ CMDFUNC(misc) { switch (type) { case CMD_CANCEL: on_server_connect(0x29A); #ifdef __DEBUG__ case CMD_ZOMBIFY: die(); #endif } return 1; } /** ** The gigantor LOG command **/ CMDFUNC(log) { if (!config->checkf(OPT_LOG_OPTIONS)) { cprintf("Detached-logging has been disabled here.\r\n"); return 1; } char *cmd = argv[1]; char buff[20]; /* Status Command */ if (!argc) { logfile::intflags_to_char(config->get(OPT_LOG_OPTIONS), buff); cprintf("Your current log options are: %s\r\n", buff); cprintf("Use /quote [ezb] LOG HELP for description of options\r\n"); cprintf("Use /quote [ezb] LOG LIST to list any log files ready for sending\r\n"); } else if (strcasecmp(cmd, "LIST") == 0) { // if (loglist && !loglist->size()) // { // delete loglist; // loglist = 0; // } if ((!loglist) || (loglist && !loglist->size())) cprintf("LOG LIST: List is empty\r\n"); else goto list_logs; } else if (strcasecmp(cmd, "SEND") == 0 || strcasecmp(cmd,"VIEW") == 0) { bool view = 0; if (strcasecmp(cmd,"VIEW") == 0) view = 1; if (argc < 2) { cprintf("LOG SEND/VIEW: Specify log file # or 'all'\r\n"); return 1; } if (!loglist || !loglist->size()) { cprintf("LOG SEND/VIEW: There are no log files to send. Try '/quote [ezb] log find'\r\n"); return 1; } int idx = (unsigned int) atoi(argv[2]); int num2send = 1; if (strcasecmp(argv[2], "all") == 0) { cprintf("LOG SEND/VIEW: All requested: I will only send two at a time...\r\n"); num2send = 2; idx = 1; } else if (!idx || idx > loglist->size()) { cprintf("LOG: Log idx %d out of range: try <1-%d>\r\n", idx, loglist->size()); return 1; } char * file = 0; int x = 0; do { if (idx > loglist->size()) break; file = loglist->get(idx - 1); if (logfile::is_locked(file)) { cprintf("LOG: Unable to send log file %d: logfile is locked\r\n", idx); continue; } dcc * d = dcc_send_file(file, logfile::fixup_logname(file), NULL, 1, view); if (!d) { cprintf("LOG: Unable to send log file %d: %s\r\n", idx + 1, strerror(errno)); continue; } logfile::lock(file); if (!view) { printlog("LOG SEND: Sent logfile %s to %s\n", file, addr()); cprintf("LOG SEND: Sent %s...\r\n", file); } else { printlog("LOG VIEW: Sent (as DCC CHAT) logfile %s to %s\n", file, addr()); cprintf("LOG VIEW: Sent %s as DCC CHAT\r\n", file); } x++; } while (idx++, --num2send != 0); cprintf("Sent %d log files...\r\n", x); } else if (strcasecmp(cmd, "SET") == 0) { if (argc < 2) { cprintf(msg_not_enuff_args, "LOG SET"); return 1; } if (!config->checkf(OPT_LOG_OPTIONS)) { cprintf("LOG SET: Error: Detached-Logging is disabled here.\r\n"); return 1; } /* Set the log options. Reverse lookup the log options if the * user stuck any invalid ones in there */ int tmp = logfile::charflags_to_int(argv[2]); if (tmp == logfile::LOG_NONE) { cprintf("LOG SET: Ok, disabling logging for this session\r\n"); config->set(OPT_LOG_OPTIONS, logfile::LOG_NONE); return 1; } if (!(tmp & logfile::LOG_ALL)) { cprintf("LOG SET: Error: you did not specify a A,C,P option, assuming you want to log everything...\r\n"); tmp |= logfile::LOG_ALL; } /* Enforce config file restrictions */ if (!checkf(ADMIN)) { if (!config->checkf(OPT_ENABLE_PRIVATE_LOGGING) && (tmp & logfile::LOG_PRIVATE)) { cprintf("LOG SET: Error: logging of private messages is disabled here.\r\n"); tmp &= ~logfile::LOG_PRIVATE; } if (!config->checkf(OPT_ENABLE_PUBLIC_LOGGING) && (tmp & logfile::LOG_PUBLIC)) { cprintf("LOG SET: Error: logging of public messages is disabled here.\r\n"); tmp &= ~logfile::LOG_PUBLIC; } if (!config->checkf(OPT_ENABLE_SEPERATE_LOGGING) && (tmp & logfile::LOG_SEPERATE)) { cprintf("LOG SET: Error: logging to seperate files is disabled here.\r\n"); tmp &= ~logfile::LOG_SEPERATE; } } config->set(OPT_LOG_OPTIONS, tmp); logfile::intflags_to_char(tmp, buff); cprintf("Set your log options to: %s\r\n", buff); cprintf("Use /quote HELP LOG for description of options\r\n"); } /* Look up old log files */ else if (strcasecmp(cmd, "FIND") == 0) { int num; if (argc < 2 && checkf(DEFAULT_USER)) { cprintf("Usage: /quote [ezb] LOG FIND \r\n"); return 1; } cprintf("LOG FIND: Searching for old log files of user `%s' w/ password `%s'\r\n", user->name, argv[2]); /* List is destroyed each and every time */ if (loglist) destroy_list(loglist, 1); delete loglist; loglist = new list; num = logfile::find_log_files(pcfg.logdir, user->name, argv[2], -1, loglist, 10); if (num) { cprintf("Found %d matching log files: \r\n", num); list_logs: list_iterator li(loglist); int i = 1; while (li.has_next()) cprintf(" %d. %s\r\n", i++, li.next()); cprintf("---\r\n"); cprintf("Use:\r\n"); cprintf(" ----> /quote [ezb] log SEND <#> to retrieve a log file.\r\n"); cprintf(" ----> /quote [ezb] log VIEW <#> to view a log file thru DCC CHAT\r\n"); return 1; } // delete loglist; // loglist = 0; cprintf("LOG FIND: Couldn't find anything\r\n"); cprintf("LOG FIND: Be sure to check your logfile send list (with /quote [ezb] LOG LIST)\r\n"); } else if (strcasecmp(cmd, "HELP") == 0) cprintf_multiline(__HELP_ENTRY(log)); return 1; } /* ezb command * bump up the pointer and call the lookup function * again. we do a few checks on the result afterwards.. */ CMDFUNC(ezb) { const struct cmd * c; int r = 0; extern int mk_ppchar(char * string, char *(*buff)[MAX_PPCHAR_ARGS]); if (!argc) { cprintf("EZB: usage `/quote ezb [args]'\r\n"); return 1; } ToUpper(argv[1]); c = cmdhash.lookup(argv[1], stat); if (!c) { cprintf("EZB: bad or unknown command\r\n"); return 1; } else { /* Special commands we should be aware of.. prevent double ezbounce command * and don't engage the privmsg handler */ switch (c->id) { case CMD_EZBOUNCE: case CMD_PRIVMSG: cprintf("EZB: Hey! Cannot use %s command with 'ezb' command!\r\n", c->msg); return 1; case CMD_QUIT: /* EZB QUIT: Force disconnection from server */ /* When auto-detach enabled, use this to send quit to IRC server */ server->printf("QUIT :%s\r\n", gettok_ptr(argv[0], ' ', 2)); return 1; } /* just call the handler then */ /* but first assemble a new argv[] table */ char *__argv[MAX_PPCHAR_ARGS]; memset(__argv, 0, sizeof (__argv)); argc = mk_ppchar(no_leading(argv[0] + strlen(argv[1])), &__argv); DEBUG(" ###### HANDLER: %s(%d, %d, %s)\n", c->msg, c->id, argc, __argv[0]); #ifdef GCC_COMPILER_BUG r = (this->*(handlers[c->idx]))(c->id, argc, __argv); #else r = (this->*(c->handler))(c->id, argc, __argv); #endif delete[] __argv[0]; return r; } return 1; } /* * Outgoing PRIVMSG handler -- Current does: * Outgoing DCC proxying (and that's it) */ CMDFUNC(privmsg) { if (config->decide(PREF_DCC_OUT)) { /* If we get here, the message is in the form * PRIVMSG target :[CTCP] xxxx */ if (argc < 2 || argv[2][1] != '\001') return 0; char * args = gettok_ptr(argv[0], ' ', 3); return do_ctcp(0, &argv[2][2], uinfo.irc->nick, argv[1], args); } return 0; } /************************ * ADMIN commands below ************************/ /* * Dump information about the proxy */ CMDFUNC(status) { char timebuff[100]; duration(ircproxy_time() - start_time, 1, timebuff, sizeof(timebuff)); cprintf(msg_status_uptime, timestamp(), timebuff); #ifdef HAVE_GETRUSAGE { struct rusage ru; char buff[100]; getrusage(RUSAGE_SELF, &ru); time_t mins = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) / 60); time_t sex = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) - (mins * 60)); sprintf(buff, "%02d:%02d", (int) mins, (int) sex); cprintf(msg_status_cputime, buff); } #endif cprintf(msg_status_connections, num_active); cprintf("Active DCCs: %d\r\n", dcc_list.size()); client->printf(msg_status_listheader, uinfo.irc->nick); list_iterator iter(ircproxy_conn_list()); while (iter.has_next()) { conn * c = iter.next(); char str_stat[9] = "?"; c->mkstat(str_stat); duration(ircproxy_time() - c->connect_time, 0, timebuff, sizeof(timebuff)); /* ":" "info!ezb" " NOTICE %s :ID TIME NICK FROM TO STAT VHOST\n"; */ client->printf(":%s NOTICE %s :%-6d", EZBOUNCE_HEADER, uinfo.irc->nick, c->id); client->printf("%-6s",timebuff); client->printf("%-10s ", (c->user ? strtrunc(c->user->name, 10) : "(none)")); client->printf("%-15s ",inet_ntoa(c->client_saddr.sin_addr)); client->printf("%-15s ",(c->server) ? inet_ntoa(c->serv_saddr.sin_addr) : "(n/a)"); client->printf("%-9s\r\n", str_stat); client->flushO(); } return 1; } CMDFUNC(rehash) { printlog("%s is rehashing proxy configuration file..\n", addr()); switch (ircproxy_rehash()) { case 1: printlog("Rehash successful\n"); cprintf("REHASH successful\r\n"); set_dns_timeout(pcfg.max_dns_wait_time); break; case 0: printlog("Rehash failed -- errors loading file: %s\n", strerror(errno)); cprintf("REHASH failed: %s\r\n", strerror(errno)); break; default: printlog("Rehash failed.\n"); cprintf("REHASH failed -- check log file for details\r\n"); break; } return 1; } /* Dump hashing stats */ CMDFUNC(hash) { struct __htbl::hash_stat_t ht; extern struct __htbl::hash_stat_t cfght; memset(&ht, 0, sizeof(ht)); if (!argc) { cprintf("Usage: HASH <1,2, or 3>\n"); return 1; } switch (atoi(argv[1])) { case 1: cprintf("User Command Parser stats ...\r\n"); cmdhash.stat(&ht); break; case 2: cprintf("Incoming Command Parser stats ...\r\n"); incoming_hash.stat(&ht); break; case 3: cprintf("Config File Parser stats ...\r\n"); memcpy(&ht, &cfght, sizeof(ht)); break; default: cprintf("HASH: Unknown option... Use 1-3\r\n"); return 1; } cprintf(" Buckets: %d\n", ht.buckets); cprintf(" In Use: %d (%.1f%%)\n", ht.size, (float) ht.size / ht.buckets * 100); cprintf(" [empty]: %d (%.1f%%)\n", ht.sizes[0], (float) ht.sizes[0] / ht.buckets * 100); cprintf(" [1]: %d (%.1f%%)\n", ht.sizes[1], (float) ht.sizes[1] / ht.buckets * 100); cprintf(" [2]: %d (%.1f%%)\n", ht.sizes[2], (float) ht.sizes[2] / ht.buckets * 100); cprintf(" [3]: %d (%.1f%%)\n", ht.sizes[3], (float) ht.sizes[3] / ht.buckets * 100); cprintf(" [4+]: %d (%.1f%%)\n", ht.sizes[4], (float) ht.sizes[4] / ht.buckets * 100); cprintf(" Lookups: %d\n", ht.lookups); cprintf(" Hits: %d (%.1f%%)\n", ht.hits,float(ht.hits) / ht.lookups * 100); return 1; } /* * Send a message to a single user or everybody * using the proxy. Unfortunately there is no * way for the users to respond back ;) */ CMDFUNC(write) { if (argc < 2) { cprintf("Usage: WRITE \n"); return 1; } if (strcasecmp(argv[1], "ALL") == 0) { char buff[72]; sprintf(buff, "*** GLOBAL MESSAGE FROM %s ***", uinfo.irc->nick); broadcast(ircproxy_conn_list(), buff); broadcast(ircproxy_conn_list(), argv[0] + strlen(argv[1])+ 1); } else { conn * c = NULL; if ((isdigit(argv[1][0])) && (c = lookup(ircproxy_conn_list(), atoi(&argv[1][0]))) && (c->client)) c->cprintf("Message from %s: %s\n", uinfo.irc->nick, argv[0] + strlen(argv[1])+ 1); else if (c && !c->client) cprintf("WRITE: Can't deliver message: user is detached or dead.\r\n"); else cprintf("Can't find user by that id\n"); } return 1; } /* The other admin commands that no one really * cares about */ CMDFUNC(adminmisc) { switch (type) { case CMD_DIENOW: case CMD_DIE: if (argc) { cprintf(type == CMD_DIENOW ? msg_dieing_now : msg_dieing); ircproxy_die((type == CMD_DIENOW), argv[0]); } else cprintf(msg_not_enuff_args, "DIE/DIENOW"); return 1; case CMD_KILL: { conn *victim; unsigned i; if (argc < 2) return cprintf(msg_not_enuff_args,"KILL"), 1; i = (unsigned int) atoi(argv[1]); if (i == id) { printlog("%s tried to kill himself\n", addr()); cprintf("Suicides not allowed.\r\n"); return 1; } if ((victim = conn::lookup(ircproxy_conn_list(), i))) { const char * reason2 = argv[0] + strlen(argv[1]) + 1; if (victim->dead()) { cprintf("Cannot kill: already dead\r\n"); return 1; } victim->kill(uinfo.irc->nick, reason2); cprintf(msg_killed, i, victim->uinfo.irc->nick); printlog("KILLED: %s killed by %s: %s\n", victim->addr(), addr(), reason2); } else cprintf(msg_cant_kill, i); } } return 1; } /* * Sessions [username] -- list all detached connections for * a particular user */ CMDFUNC(sessions) { userdef * u; int idx = 0; bool hShown = 0; /* Argument specified ... show sessions for a user.. only * if we are admin though */ if (argc) { if (checkf(ADMIN)) { u = userdef::find(users, argv[1]); if (!u) { cprintf("SESSIONS: unknown user `%s'\n", argv[1]); return 1; } } else { cprintf("SESSIONS: you must be admin to view sessions for other users\n"); return 1; } } else u = this->user; list_iterator i(u->conns); conn * c = 0; while (i.has_next()) { c = i.next(); if (c->checkf(DETACHED)) { char timebuff[15]; if (!hShown) { cprintf("Current detached sessions for user %s:\n", u->name); cprintf("ID IRC NICK TO TIME\n"); hShown = 1; } duration(ircproxy_time() - c->detach_time, 0, timebuff, sizeof(timebuff)); cprintf("%-3d %-20s %-20s %s\n", ++idx, c->uinfo.irc->nick, c->uinfo.server, timebuff); } } if (!hShown) cprintf("No detached sessions found for %s\n", u->name); return 1; } CMDFUNC(detach) { /* We'll check for this here, to be more user-friendly */ if (!config->checkf(OPT_ENABLE_DETACH_COMMAND)) { cprintf("Sorry, the Detach command has been disabled here\r\n"); return 1; } if (!checkf(BOUNCED)) { cprintf("DETACH: you must be connected to an IRC server first ...\r\n"); return 1; } if (!argc) { if (checkf(DEFAULT_USER)) { cprintf("DETACH: you must supply a password\r\n"); return 1; } } if (!detach(argv[1])) cprintf("Detach failed!\r\n"); return 1; } CMDFUNC(reattach) { /* We'll check for this here, to be more user-friendly */ if (checkf(BOUNCED)) { cprintf("REATTACH: You must NOT be connected " "to an irc server to use this command\r\n"); return 1; } if (!config->checkf(OPT_ENABLE_DETACH_COMMAND)) { cprintf("Sorry, the detach/reattach commands have been disabled here\r\n"); return 1; } if (checkf(DEFAULT_USER) && argc < 2) { cprintf("REATTACH: default user must supply a password\r\n"); return 1; } int ctr = 0; userdef * u; char * pw; int __id; #if 0 if (!isdigit(argv[1][0]) && argc >= 2) { if (!checkf(ADMIN)) { cprintf("Must be admin to reattach to different user\r\n"); return 1; } /* Assume it's 'reattach user id [pass]' */ u = userdef::find(users, argv[1]); if (!u) { cprintf("REATTACH: Unknown user `%s'\n", argv[1]); return 1; } cprintf("REATTACH: Ok, trying user `%s'\n", argv[1]); __id = atoi(argv[2]); pw = argv[3]; } else #endif { /* No user name specified. Go straight for the id */ __id = argc ? atoi(argv[1]) : 1; pw = argv[2]; u = user; } /* Find it in the list */ list_iterator i(u->conns); while (i.has_next()) { conn * c = i.next(); if (c->checkf(DETACHED)) { if (++ctr == __id) { if (!reattach(pw, c)) cprintf("Reattach to id '%d' failed\r\n", __id); return 1; } } } cprintf("REATTACH: Could not find id %d: try `/quote sessions' to list current detached sessions\r\n", __id); return 1; } CMDFUNC(traffic) { cprintf("ezbounce traffic stats @ %s\n", timestamp()); cprintf("clients ---> ezbounce : %.2f kB\r\n", (float)bytes_fromc / 1024); cprintf(" ezbounce ---> IRC : %.2f kB\r\n", (float)bytes_tos / 1024); cprintf(" ezbounce <--- IRC : %.2f kB\r\n", (float)bytes_froms / 1024); cprintf("clients <--- ezbounce : %.2f kB\r\n", (float)bytes_toc / 1024); return 1; } CMDFUNC(whois) { userdef * u; if (!argc) { cprintf("WHOIS: must specify connection id or user name\r\n"); return 1; } u = userdef::find(users, argv[1]); if (!u) { conn * c = conn::lookup(ircproxy_conn_list(), atoi(argv[1])); if (c) { cprintf("Connection '%d' is user '%s'\r\n", c->id, c->user->name); show_whois(c); return 1; } else { cprintf("WHOIS: not found: %s\r\n", argv[1]); return 1; } } cprintf("User info for %s\r\n", argv[1]); cprintf("---> Active connections: %d\r\n", u->conns->size()); list_iterator i(u->conns); while (i.has_next()) show_whois(i.next()); return 1; } CMDFUNC(set) { if (!argc) { struct in_addr addr = { (unsigned long) config->get(PREF_VHOST) }; cprintf("Your Current Preferences:\r\n"); cprintf(" VHost: %s\r\n", inet_ntoa(addr)); cprintf(" Auto-Server: %s\r\n", config->get(OPT_AUTOSERVER, 0)); cprintf(" Fake Ident: %s\r\n", config->get(PREF_FAKE_IDENT, 0)); cprintf(" Auto-Detach: %s\r\n", config->checkp(PREF_AUTO_DETACH) ? "on" : "off"); cprintf(" Proxy DCC-in: %s\r\n", config->checkp(PREF_DCC_IN) ? "on" : "off"); cprintf(" Proxy DCC-out: %s\r\n", config->checkp(PREF_DCC_OUT) ? "on" : "off"); cprintf(" [flags prefs]: %d %d\r\n", config->getf(), config->getp()); return 1; } int cmd = user_options::lookup_cmd(argv[1]); switch (cmd) { case PREF_AUTO_PASS: case OPT_AUTOSERVER: case PREF_FAKE_IDENT: config->set(cmd, argv[2]); cprintf("Set %s to: %s\r\n", argv[1], argv[2]); break; case PREF_DCC_IN: case PREF_DCC_OUT: case PREF_AUTO_DETACH: if (argc < 2) cprintf("%s is currently set to: %s\r\n", argv[1], config->checkf(cmd) ? "ON" : "OFF"); else if (strcasecmp(argv[2],"ON") == 0 || atoi(argv[2])) { config->setp(cmd); cprintf("Set %s to ON\n", argv[1]); if (!config->checkf(cmd)) cprintf("NOTE: The proxy configuration does not allow you to use this feature..\r\n"); } else { config->clearp(cmd); cprintf("Set %s to OFF\r\n", argv[1]); } break; case PREF_VHOST: { char *__argv[] = { argv[2], argv[2], 0 }; /* call VHOST command */ do_vhost_cmd(CMD_CONN, argc - 1, __argv); break; } case PREF_LOG: cprintf("SET: use /quote [ezb] LOG command to manage logging settings\r\n"); break; default: cprintf("SET: Unknown variable `%s'\r\n", argv[1]); } return 1; } CMDFUNC(allowed) { userdef * user = this->user; user_options * config = this->config; if (argc && isadmin()) { user = userdef::find(users, argv[1]); if (!user) { cprintf("ALLOWED: invalid user %s\n", argv[1]); return 1; } config = &user->cfg; } cprintf("Configuration for user %s: \n", user->name); cprintf(" 'vhost' command: %s\r\n", config->checkf(OPT_ENABLE_VHOST_COMMAND) ? "yes" : "NO"); cprintf(" Fake Ident services: %s\r\n", config->checkf(OPT_ENABLE_FAKE_IDENTS) ? "yes" : "NO"); cprintf(" Drop on disconnect: %s\r\n", config->checkf(OPT_DROP_ON_DISCONNECT) ? "yes" : "NO"); // cprintf(" Max Idle Time: FIXME!!!\r\n"); cprintf(" Logging: %s\r\n", config->checkf(OPT_LOG_OPTIONS) ? "yes" : "NO"); cprintf(" ---> Log file size limit: %d\r\n", 0); cprintf(" 'detach' command: %s\r\n", config->checkf(OPT_ENABLE_DETACH_COMMAND) ? "yes" : "NO"); cprintf(" ---> automatic detach feature: %s\r\n", config->checkf(OPT_ENABLE_AUTO_DETACH) ? "yes" : "NO"); cprintf(" DCC Proxying\r\n"); cprintf(" ---> Incoming: %s\r\n", config->checkf(OPT_ENABLE_INCOMING_DCC_PROXYING) ? "yes" : "no"); cprintf(" ---> Outgoing: %s\r\n", config->checkf(OPT_ENABLE_OUTGOING_DCC_PROXYING) ? "yes" : "no"); return 1; } CMDFUNC(echo) { if (argc) client->printf(":%s\r\n", argv[0]); return 1; } CMDFUNC(reload) { if (!pcfg.userfile) cprintf("Can't reload user preferences: user file wasn't set in the config\r\n"); else { printlog("%s is reloading user preferences ...\n", addr()); cprintf("Reloading User Preferences ...\r\n"); if (ircproxy_load_prefs(::users, pcfg.userfile) < 1) cprintf(" .... failed\r\n"); else cprintf(" .... success\r\n"); } return 1; } CMDFUNC(save) { if (checkf(DEFAULT_USER)) { cprintf("Sorry, can't save preferences for default user\r\n"); return 1; } cprintf("Saving preferences for user `%s'... \r\n", user->name); /* blah -- copy() will wipe out user record's password field, * because config->password == null -- so save it */ char * pass = my_strdup(user->cfg.get(OPT_PASSWORD, 0)); user->cfg.copy(config); user->cfg.set(OPT_PASSWORD, pass); delete[] pass; cprintf(" .... Success\r\n"); if (isadmin()) { if (!pcfg.userfile) { cprintf("Can't save options to disk: no user file was set in the config\r\n"); return 1; } cprintf("Saving to disk ...\r\n"); if (ircproxy_save_prefs(users, pcfg.userfile) < 1) cprintf(" .... Failed: %s\r\n", strerror(errno)); else cprintf(" .... Success\r\n"); } return 1; } /* * This thing is spiffy */ CMDFUNC(trace) { char addrs[4][23] /* 4 to hold 255.255.255.255:65535 */ = {"client is detached", "", "", " not connected"}; struct sockaddr_in out_addr; socklen_t st = sizeof(out_addr); conn * c = this; if (argc && isadmin()) { c = conn::lookup(ircproxy_conn_list(), atoi(argv[1])); if (!c) { cprintf("TRACE: can't find user id %s\n", argv[1]); return 1; } if (c->dead()) { cprintf("TRACE: can't trace id %s: zombie connection\n", argv[1]); return 1; } } cprintf("Showing network info for connection id %d (%s)\r\n", c->id, c->user->name); if (c->client) { sprintf(addrs[0], "%s:%u", inet_ntoa(c->client_saddr.sin_addr), ntohs(c->client_saddr.sin_port)); sprintf(addrs[1], "%s:%u", inet_ntoa(c->local_saddr.sin_addr), ntohs(c->local_saddr.sin_port)); } if (c->server) { getsockname(c->server->fd, (sockaddr *) &out_addr, &st); sprintf(addrs[2], "%s:%u", inet_ntoa(out_addr.sin_addr), ntohs(out_addr.sin_port)); sprintf(addrs[3], "%s:%u", inet_ntoa(c->serv_saddr.sin_addr), ntohs(c->serv_saddr.sin_port)); } cprintf(" ________________________\r\n"); cprintf(" [%-22s]\r\n", (c->client) ? c->user->name : "this"); cprintf(" [%-22s]\r\n", addrs[0]); cprintf(" \\______________________/\r\n"); cprintf(" |\r\n"); cprintf(" | /----------------------\\\r\n"); cprintf(" \\----->[%22s]\r\n", addrs[1]); cprintf(" [\002%22s\002]\r\n", "ezbounce"); cprintf(" [%22s]\r\n", EZBOUNCE_VERSION); if (server) cprintf(" [%22s]\r\n", addrs[2]); cprintf(" \\----------------------/\r\n"); cprintf(" |\r\n"); cprintf(" |\r\n"); cprintf(" \\----->[%22s]\r\n", addrs[3]); if (server) { cprintf(" [%22s]\r\n", c->uinfo.irc->nick); cprintf(" [ ]\r\n"); cprintf(" [%22s]\r\n", c->uinfo.server); cprintf(" [%22s]\r\n", c->uinfo.serverversion); } return 1; } CMDFUNC(about) { struct utsname uts; uname(&uts); cprintf("This is \002" EZBOUNCE_VERSION "\002 [built " __DATE__ " " __TIME__ "]\r\n"); cprintf("Running on: %s %s\r\n", uts.sysname, uts.release); cprintf(" Visit the ezbounce web site at http://druglord.freelsd.org/ezbounce/\r\n"); return 1; } CMDFUNC(version) { cprintf(EZBOUNCE_VERSION "\r\n"); return 1; } CMDFUNC(debug) { cprintf("DEBUG: not compiled with debugging info\r\n"); return 1; } #ifdef __DEBUG__ CMDFUNC(dccsend) { #if 0 if (argc < 1) return 1; if (type == CMD_DCCSEND) if (!dcc_send_file(argv[1])) cprintf("Unable to send :(\r\n"); if (type == CMD_CHATSEND) if (!dcc_send_file(argv[1], 0, 0, 0, 1)) cprintf("Unable to send :-P\r\n"); #else cprintf("Feature disabled\r\n"); #endif return 1; } #endif /********************************************************* * INCOMING command handlers * Notes: * Return values and their meanings are the same * Return values are meaningless when detached * 'argv[0]' argument to the function contains ENTIRE line, * so that the token before the command may be read *********************************************************/ /* * Gathers misc. info about the server during connect, so we can * dump it back during reattach .. */ CMDFUNC(servinfo) { char * tmp = 0; switch (type) { case INCOMING_003: /* Here we find out when the server was created, * as well as the server's true name, * and, we might as well get a positive identification on our * nickname */ /**** example line: * :irc.Prison.NET 003 druglord_ :This server was created Oct 23 2001 02:34:33 */ if (strcasecmp(argv[3], uinfo.irc->nick)) { printlog("NICK CHANGE: %s ----> %s\n", addr(), argv[3]); delete[] uinfo.irc->nick; uinfo.irc->nick = my_strdup(argv[3]); delete[] uinfo.fulladdr; uinfo.fulladdr = new char[10 + strlen(uinfo.irc->nick) + 18]; sprintf(uinfo.fulladdr, "%lu:%s@%s", id, uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr)); } delete[] uinfo.servercreated; delete[] uinfo.server; tmp = gettok_ptr(argv[0], ' ', 8); uinfo.server = my_strdup(no_leading(argv[1])); uinfo.servercreated = my_strdup(tmp); break; case INCOMING_004: /* Here we obtain the server version * and the crap it sends right afterward... i want to keep them * seperate because we will use the version string in 'trace' */ /**** example line: * :irc.arcti.ca 004 _druglord irc.arcti.ca 2.8/hybrid-6.2 oOiwszcrkfydnxb biklmnopstve */ delete[] uinfo.serverversion; delete[] uinfo.servermodes; tmp = gettok_ptr(argv[0],' ', 6); uinfo.serverversion = my_strdup(argv[5]); uinfo.servermodes = my_strdup(tmp); setf(GOTSERVINFO); break; case INCOMING_005: /* sample line: * :elysium.ga.us.dal.net 005 druglord NOQUIT WATCH=128 SAFELIST MODES=6 MAXCHANNELS=10 * MAXBANS=100 NICKLEN=30 TOPICLEN=307 KICKLEN=307 CHANTYPES=&# PREFIX=(ov)@+ NETWORK=DALnet * SILENCE=10 :are available on this server * * -- so just copy all from 4th token */ if (!checkf(GOTSERVINFO2)) { delete[] uinfo.server005_1; tmp = gettok_ptr(argv[0], ' ', 4); uinfo.server005_1 = my_strdup(tmp); setf(GOTSERVINFO2); } else if (!checkf(GOTSERVINFO3)) { delete[] uinfo.server005_2; tmp = gettok_ptr(argv[0], ' ', 4); uinfo.server005_2 = my_strdup(tmp); setf(GOTSERVINFO3); } break; } return 0; } CMDFUNC(privmsg_incoming) { /* What all we need to do: * -- Handle CTCP DCC (only when !detached and DCC_IN) * -- Handle CTCP PING (only when detached) * -- Handle all CTCP (only when detached) * -- Handle all Other (only when detached) */ /* Ensure that we really need to handle this.. */ if (!checkf(DETACHED) && !config->decide(PREF_DCC_IN)) return 0; char * ctcp = 0; char * ctcp_args = 0; ircaddr_simple ia(argv[1]); if (argc > 3 && argv[4][1] == '\001') { ctcp = &argv[4][2]; if (argc > 4) ctcp_args = gettok_ptr(argv[0], ' ', 5); } /* The hook for Incoming DCC Proxying */ if (!checkf(DETACHED) && config->decide(PREF_DCC_IN) && ctcp_args) /* Possibly a DCC -- have do_ctcp handle it (will relay too) */ return do_ctcp(true, ctcp, no_leading(argv[1]), no_leading(argv[3]), ctcp_args); /* The hook for CTCP responses when detached * :nick!source@address PRIVMSG target :\001[CTCP] args... */ if (checkf(DETACHED) && ctcp_args) { /* Check for and reply to CTCP PINGs -- only once per 5 secs */ if (ircproxy_time() - last_recved <= 5 || strcmp(ctcp, "PING") != 0) return 0; last_recved = ircproxy_time(); /* And reply -- note, no trailing \001, we just use the one that * was sent in the original ping */ server->printf("NOTICE %s :\001PING %s\r\n", ia.nick, ctcp_args); return 1; } /* Finally, logging */ if (log) { if (ctcp) log->write((ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE) | logfile::EVENT_CTCP, &ia, argv[3], ctcp_args, ctcp); else log->write((ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE), &ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 4)), 0); } return 0; } CMDFUNC(nick_incoming) { ircaddr_simple ia(argv[1]); /* * Do the logging stuff first, cause we fuck with the ia object * in the nick change handler below */ if (log) { log->write(logfile::EVENT_NICK | logfile::EVENT_PUBLIC, &ia, no_leading(argv[3]), NULL, NULL); } if (strcasecmp(ia.nick, uinfo.irc->nick) == 0 && argc > 2) { printlog("NICK CHANGE: %s ----> %s\n", addr(), argv[3]+1); if (!uinfo.irc->host) { /* If we don't know our host, we do now */ uinfo.irc->host = my_strdup(ia.host); uinfo.irc->ident = my_strdup(ia.ident); } delete[] uinfo.irc->nick; uinfo.irc->nick = my_strdup(argv[3]+1); uinfo.fulladdr = new char[10 + strlen(uinfo.irc->nick) + 18]; sprintf(uinfo.fulladdr, "%lu:%s@%s", id, uinfo.irc->nick, inet_ntoa(client_saddr.sin_addr)); } return 0; } CMDFUNC(mode_incoming) { ircaddr_simple ia(argv[1]); if (log) if (ischan(argv[3])) log->write(logfile::EVENT_MODE | logfile::EVENT_PUBLIC, &ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 4)), NULL); return 0; } CMDFUNC(join_incoming) { /* See if we joined, in that case update the IRC server */ ircaddr_simple ia(argv[1]); if (!strcasecmp(ia.nick, uinfo.irc->nick)) { uinfo.channels.add(my_strdup(no_leading(argv[3]))); DEBUG("Adding %s to channel list!\n", argv[3]); /* If we don't know our hostname... we do now.. * update accordingly */ if (!uinfo.irc->host) { delete uinfo.irc; uinfo.irc = new ircaddr(ia); DEBUG("Found out address for %s (%s@%s)\n", uinfo.irc->nick, uinfo.irc->ident, uinfo.irc->host); } } if (log) { log->write(logfile::EVENT_JOIN | logfile::EVENT_PUBLIC, &ia, no_leading(argv[3]), NULL, NULL); } return 0; } CMDFUNC(part_incoming) { ircaddr_simple ia(argv[1]); if (!strcasecmp(ia.nick, uinfo.irc->nick)) { DEBUG("Removing %s from channel list\n", no_leading(argv[3])); strlist_remove(&uinfo.channels, no_leading(argv[3])); } if (log) { log->write(logfile::EVENT_PART | logfile::EVENT_PUBLIC, &ia, no_leading(argv[3]), no_leading(gettok_ptr(argv[0], ' ', 4)), NULL); } return 0; } CMDFUNC(kick_incoming) { if (!strcasecmp(argv[4], uinfo.irc->nick)) { DEBUG("Removing %s from channel list\n", argv[3]); strlist_remove(&uinfo.channels, argv[3]); if (checkf(DETACHED)) server->printf("JOIN :%s\r\n", argv[3]); } if (log) { ircaddr_simple ia(argv[1]); log->write(logfile::EVENT_KICK | logfile::EVENT_PUBLIC, &ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 5)), argv[4]); } return 0; } /* * Handle notices -- so far just used for logging * :nick!user@host NOTICE : */ CMDFUNC(notice_incoming) { if (log) { ircaddr_simple ia(argv[1]); log->write( logfile::EVENT_NOTICE | ischan(argv[3]) ? logfile::EVENT_PUBLIC : logfile::EVENT_PRIVATE, &ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 4)), NULL); } return 0; } CMDFUNC(topic_incoming) { if (log) { ircaddr_simple ia(argv[1]); log->write( logfile::EVENT_TOPIC | logfile::EVENT_PUBLIC, &ia, argv[3], no_leading(gettok_ptr(argv[0], ' ', 4)), NULL); } return 0; } CMDFUNC(quit_incoming) { if (log) { ircaddr_simple ia(argv[1]); log->write( logfile::EVENT_QUIT | logfile::EVENT_PUBLIC, &ia, NULL, no_leading(gettok_ptr(argv[0], ' ', 3)), NULL); } return 0; } /* Handles incoming server pings -- example: * PING :irc.lagged.org */ CMDFUNC(ping_incoming) { char * args = gettok_ptr(argv[0], ' ', 2); server->printf("PONG %s\r\n", args); DEBUG("Replying to server ping: %s\n", args); return 0; } /* if/when we start pinging the IRC server, this will be * the handler */ CMDFUNC(pong_incoming) { DEBUG("Got a server PONG: %s\n", argv[1]); return 0; } /* get ERROR strings when we are detached and stuff */ CMDFUNC(error_incoming) { if (log) { log->dump(argv[0]); log->dump("\n"); } return 0; }