/********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * * Copyright 2002-2012 Wilmer van der Gaast and others * \********************************************************************/ /* IRC commands */ /* 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 with the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define BITLBEE_CORE #include "bitlbee.h" #include "help.h" #include "ipc.h" static void irc_cmd_pass( irc_t *irc, char **cmd ) { if( irc->status & USTATUS_LOGGED_IN ) { char *send_cmd[] = { "identify", cmd[1], NULL }; /* We're already logged in, this client seems to send the PASS command last. (Possibly it won't send it at all if it turns out we don't require it, which will break this feature.) Try to identify using the given password. */ root_command( irc, send_cmd ); return; } /* Handling in pre-logged-in state, first see if this server is password-protected: */ else if( global.conf->auth_pass && ( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ? md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 : strcmp( cmd[1], global.conf->auth_pass ) == 0 ) ) { irc->status |= USTATUS_AUTHORIZED; irc_check_login( irc ); } else if( global.conf->auth_pass ) { irc_send_num( irc, 464, ":Incorrect password" ); } else { /* Remember the password and try to identify after USER/NICK. */ irc_setpass( irc, cmd[1] ); irc_check_login( irc ); } } static void irc_cmd_user( irc_t *irc, char **cmd ) { irc->user->user = g_strdup( cmd[1] ); irc->user->fullname = g_strdup( cmd[4] ); irc_check_login( irc ); } static void irc_cmd_nick( irc_t *irc, char **cmd ) { irc_user_t *iu; if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user ) { irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] ); } else if( !nick_ok( NULL, cmd[1] ) ) { /* [SH] Invalid characters. */ irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] ); } else if( irc->status & USTATUS_LOGGED_IN ) { /* WATCH OUT: iu from the first if reused here to check if the new nickname is the same (other than case, possibly). If it is, no need to reset identify-status. */ if( ( irc->status & USTATUS_IDENTIFIED ) && iu != irc->user ) { irc_setpass( irc, NULL ); irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set( irc, "-R", 1 ); irc_rootmsg( irc, "Changing nicks resets your identify status. " "Re-identify or register a new account if you want " "your configuration to be saved. See \x02help " "nick_changes\x02." ); } if( strcmp( cmd[1], irc->user->nick ) != 0 ) irc_user_set_nick( irc->user, cmd[1] ); } else { g_free( irc->user->nick ); irc->user->nick = g_strdup( cmd[1] ); irc_check_login( irc ); } } static void irc_cmd_quit( irc_t *irc, char **cmd ) { if( cmd[1] && *cmd[1] ) irc_abort( irc, 0, "Quit: %s", cmd[1] ); else irc_abort( irc, 0, "Leaving..." ); } static void irc_cmd_ping( irc_t *irc, char **cmd ) { irc_write( irc, ":%s PONG %s :%s", irc->root->host, irc->root->host, cmd[1]?cmd[1]:irc->root->host ); } static void irc_cmd_pong( irc_t *irc, char **cmd ) { /* We could check the value we get back from the user, but in fact we don't care, we're just happy s/he's still alive. */ irc->last_pong = gettime(); irc->pinging = 0; } static void irc_cmd_join( irc_t *irc, char **cmd ) { char *comma, *s = cmd[1]; while( s ) { irc_channel_t *ic; if( ( comma = strchr( s, ',' ) ) ) *comma = '\0'; if( ( ic = irc_channel_by_name( irc, s ) ) == NULL && ( ic = irc_channel_new( irc, s ) ) ) { if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 ) { /* Autoconfiguration is for control channels only ATM. */ } else if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) { set_setstr( &ic->set, "group", ic->name +
#ifndef __OSCAR_INFO_H__
#define __OSCAR_INFO_H__

#define AIM_CB_FAM_LOC 0x0002

/*
 * SNAC Family: Location Services.
 */ 
#define AIM_CB_LOC_ERROR 0x0001
#define AIM_CB_LOC_REQRIGHTS 0x0002
#define AIM_CB_LOC_RIGHTSINFO 0x0003
#define AIM_CB_LOC_SETUSERINFO 0x0004
#define AIM_CB_LOC_REQUSERINFO 0x0005
#define AIM_CB_LOC_USERINFO 0x0006
#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
#define AIM_CB_LOC_WATCHERNOT 0x0008
#define AIM_CB_LOC_DEFAULT 0xffff

#define AIM_CAPS_BUDDYICON      0x00000001
#define AIM_CAPS_VOICE          0x00000002
#define AIM_CAPS_IMIMAGE        0x00000004
#define AIM_CAPS_CHAT           0x00000008
#define AIM_CAPS_GETFILE        0x00000010
#define AIM_CAPS_SENDFILE       0x00000020
#define AIM_CAPS_GAMES          0x00000040
#define AIM_CAPS_SAVESTOCKS     0x00000080
#define AIM_CAPS_SENDBUDDYLIST  0x00000100
#define AIM_CAPS_GAMES2         0x00000200
#define AIM_CAPS_ICQ            0x00000400
#define AIM_CAPS_APINFO         0x00000800
#define AIM_CAPS_ICQRTF	        0x00001000
#define AIM_CAPS_EMPTY	        0x00002000
#define AIM_CAPS_ICQSERVERRELAY 0x00004000
#define AIM_CAPS_ICQUNKNOWN     0x00008000
#define AIM_CAPS_TRILLIANCRYPT  0x00010000
#define AIM_CAPS_UTF8           0x00020000
#define AIM_CAPS_INTEROP        0x00040000
#define AIM_CAPS_ICHAT          0x00080000
#define AIM_CAPS_EXTCHAN2       0x00100000
#define AIM_CAPS_LAST           0x00200000

int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn);

#endif /* __OSCAR_INFO_H__ */
e here", cmd[2] ); else if( ic->f->invite( ic, iu ) ) irc_send_num( irc, 341, "%s %s", iu->nick, ic->name ); } static void irc_cmd_userhost( irc_t *irc, char **cmd ) { int i; /* [TV] Usable USERHOST-implementation according to RFC1459. Without this, mIRC shows an error while connecting, and the used way of rejecting breaks standards. */ for( i = 1; cmd[i]; i ++ ) { irc_user_t *iu = irc_user_by_name( irc, cmd[i] ); if( iu ) irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick, irc_user_get_away( iu ) ? '-' : '+', iu->user, iu->host ); } } static void irc_cmd_ison( irc_t *irc, char **cmd ) { char buff[IRC_MAX_LINE]; int lenleft, i; buff[0] = '\0'; /* [SH] Leave room for : and \0 */ lenleft = IRC_MAX_LINE - 2; for( i = 1; cmd[i]; i ++ ) { char *this, *next; this = cmd[i]; while( *this ) { irc_user_t *iu; if( ( next = strchr( this, ' ' ) ) ) *next = 0; if( ( iu = irc_user_by_name( irc, this ) ) && iu->bu && iu->bu->flags & BEE_USER_ONLINE ) { lenleft -= strlen( iu->nick ) + 1; if( lenleft < 0 ) break; strcat( buff, iu->nick ); strcat( buff, " " ); } if( next ) { *next = ' '; this = next + 1; } else { break; } } /* *sigh* */ if( lenleft < 0 ) break; } if( strlen( buff ) > 0 ) buff[strlen(buff)-1] = '\0'; irc_send_num( irc, 303, ":%s", buff ); } static void irc_cmd_watch( irc_t *irc, char **cmd ) { int i; /* Obviously we could also mark a user structure as being watched, but what if the WATCH command is sent right after connecting? The user won't exist yet then... */ for( i = 1; cmd[i]; i ++ ) { char *nick; irc_user_t *iu; if( !cmd[i][0] || !cmd[i][1] ) break; nick = g_strdup( cmd[i] + 1 ); nick_lc( irc, nick ); iu = irc_user_by_name( irc, nick ); if( cmd[i][0] == '+' ) { if( !g_hash_table_lookup( irc->watches, nick ) ) g_hash_table_insert( irc->watches, nick, nick ); if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE ) irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "is online" ); else irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" ); } else if( cmd[i][0] == '-' ) { gpointer okey, ovalue; if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) ) { g_hash_table_remove( irc->watches, okey ); g_free( okey ); irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); } } } } static void irc_cmd_topic( irc_t *irc, char **cmd ) { irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] ); const char *new = cmd[2]; if( ic == NULL ) { irc_send_num( irc, 403, "%s :No such channel", cmd[1] ); } else if( new ) { if( ic->f->topic == NULL ) irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name ); else if( ic->f->topic( ic, new ) ) irc_send_topic( ic, TRUE ); } else { irc_send_topic( ic, FALSE ); } } static void irc_cmd_away( irc_t *irc, char **cmd ) { if( cmd[1] && *cmd[1] ) { char away[strlen(cmd[1])+1]; int i, j; /* Copy away string, but skip control chars. Mainly because Jabber really doesn't like them. */ for( i = j = 0; cmd[1][i]; i ++ ) if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' ) j ++; away[j] = '\0'; irc_send_num( irc, 306, ":You're now away: %s", away ); set_setstr( &irc->b->set, "away", away ); } else { irc_send_num( irc, 305, ":Welcome back" ); set_setstr( &irc->b->set, "away", NULL ); } } static void irc_cmd_list( irc_t *irc, char **cmd ) { GSList *l; for( l = irc->channels; l; l = l->next ) { irc_channel_t *ic = l->data; irc_send_num( irc, 322, "%s %d :%s", ic->name, g_slist_length( ic->users ), ic->topic ? : "" ); } irc_send_num( irc, 323, ":%s", "End of /LIST" ); } static void irc_cmd_version( irc_t *irc, char **cmd ) { irc_send_num( irc, 351, "%s-%s. %s :%s/%s ", PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU ); } static void irc_cmd_completions( irc_t *irc, char **cmd ) { help_t *h; set_t *s; int i; irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" ); for( i = 0; root_commands[i].command; i ++ ) irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command ); for( h = global.help; h; h = h->next ) irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title ); for( s = irc->b->set; s; s = s->next ) irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key ); irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" ); } static void irc_cmd_rehash( irc_t *irc, char **cmd ) { if( global.conf->runmode == RUNMODE_INETD ) ipc_master_cmd_rehash( NULL, NULL ); else ipc_to_master( cmd ); irc_send_num( irc, 382, "%s :Rehashing", global.conf_file ); } static const command_t irc_commands[] = { { "pass", 1, irc_cmd_pass, 0 }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, { "quit", 0, irc_cmd_quit, 0 }, { "ping", 0, irc_cmd_ping, 0 }, { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN }, { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN }, { "names", 1, irc_cmd_names, IRC_CMD_LOGGED_IN }, { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN }, { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN }, { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN }, { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN }, { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN }, { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN }, { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN }, { "notice", 1, irc_cmd_notice, IRC_CMD_LOGGED_IN }, { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN }, { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN }, { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN }, { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN }, { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN }, { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN }, { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN }, { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN }, { "list", 0, irc_cmd_list, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wall", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { NULL } }; void irc_exec( irc_t *irc, char *cmd[] ) { int i, n_arg; if( !cmd[0] ) return; for( i = 0; irc_commands[i].command; i++ ) if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 ) { /* There should be no typo in the next line: */ for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --; if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN ) { irc_send_num( irc, 462, ":Only allowed before logging in" ); } else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) ) { irc_send_num( irc, 451, ":Register first" ); } else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) ) { irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" ); } else if( n_arg < irc_commands[i].required_parameters ) { irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] ); } else if( irc_commands[i].flags & IRC_CMD_TO_MASTER ) { /* IPC doesn't make sense in inetd mode, but the function will catch that. */ ipc_to_master( cmd ); } else { irc_commands[i].execute( irc, cmd ); } return; } if( irc->status & USTATUS_LOGGED_IN ) irc_send_num( irc, 421, "%s :Unknown command", cmd[0] ); }