account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) { account_t *a; set_t *s; if( irc->accounts ) { for( a = irc->accounts; a->next; a = a->next ); a = a->next = g_new0( account_t, 1 ); } else { irc->accounts = a = g_new0 ( account_t, 1 ); } a->prpl = prpl; a->user = g_strdup( user ); a->pass = g_strdup( pass ); a->auto_connect = 1; a->irc = irc; s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); s->flags |= ACC_SET_NOSAVE; s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); s = set_add( &a->set, "password", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; s = set_add( &a->set, "username", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; set_setstr( &a->set, "username", user ); a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); /* This function adds some more settings (and might want to do more things that have to be done now, although I can't think of anything. */ if( prpl->init ) prpl->init( a ); return( a ); }
static void handle_settings( struct xt_node *node, set_t **head ) { struct xt_node *c; for( c = node->children; ( c = xt_find_node( c, "setting" ) ); c = c->next ) { char *name = xt_find_attr( c, "name" ); if( !name ) continue; if( strcmp( node->name, "account" ) == 0 ) { set_t *s = set_find( head, name ); if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ) continue; /* U can't touch this! */ } set_setstr( head, name, c->text ); } }
static void cmd_rename(irc_t *irc, char **cmd) { irc_user_t *iu, *old; gboolean del = g_strcasecmp(cmd[1], "-del") == 0; iu = irc_user_by_name(irc, cmd[del ? 2 : 1]); if (iu == NULL) { irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]); } else if (del) { if (iu->bu) { bee_irc_user_nick_reset(iu); } irc_rootmsg(irc, "Nickname reset to `%s'", iu->nick); } else if (iu == irc->user) { irc_rootmsg(irc, "Use /nick to change your own nickname"); } else if (!nick_ok(irc, cmd[2])) { irc_rootmsg(irc, "Nick `%s' is invalid", cmd[2]); } else if ((old = irc_user_by_name(irc, cmd[2])) && old != iu) { irc_rootmsg(irc, "Nick `%s' already exists", cmd[2]); } else { if (!irc_user_set_nick(iu, cmd[2])) { irc_rootmsg(irc, "Error while changing nick"); return; } if (iu == irc->root) { /* If we're called internally (user did "set root_nick"), let's not go O(INF). :-) */ if (strcmp(cmd[0], "set_rename") != 0) { set_setstr(&irc->b->set, "root_nick", cmd[2]); } } else if (iu->bu) { nick_set(iu->bu, cmd[2]); } irc_rootmsg(irc, "Nick successfully changed"); } }
static void irc_cmd_oper_hack( irc_t *irc, char **cmd ) { char *password = g_strjoinv( " ", cmd + 2 ); /* /OPER can now also be used to enter IM/identify passwords without echoing. It's a hack but the extra password security is worth it. */ if( irc->status & OPER_HACK_ACCOUNT_ADD ) { account_t *a; for( a = irc->b->accounts; a; a = a->next ) if( strcmp( a->pass, PASSWORD_PENDING ) == 0 ) { set_setstr( &a->set, "password", password ); irc_usermsg( irc, "Password added to IM account " "%s(%s)", a->prpl->name, a->user ); /* The IRC client may expect this. 491 suggests the OPER password was wrong, so the client won't expect a +o. It may however repeat the password prompt. We'll see. */ irc_send_num( irc, 491, ":Password added to IM account " "%s(%s)", a->prpl->name, a->user ); } } else if( irc->status & OPER_HACK_IDENTIFY ) { char *send_cmd[] = { "identify", password, NULL }; irc_send_num( irc, 491, ":Trying to identify" ); root_command( irc, send_cmd ); } else if( irc->status & OPER_HACK_REGISTER ) { char *send_cmd[] = { "register", password, NULL }; irc_send_num( irc, 491, ":Trying to identify" ); root_command( irc, send_cmd ); } irc->status &= ~OPER_HACK_ANY; g_free( password ); }
static xt_status handle_channel( struct xt_node *node, gpointer data ) { struct xml_parsedata *xd = data; irc_channel_t *ic; char *name, *type; name = xt_find_attr( node, "name" ); type = xt_find_attr( node, "type" ); if( !name || !type ) return XT_ABORT; /* The channel may exist already, for example if it's &bitlbee. Also, it's possible that the user just reconnected and the IRC client already rejoined all channels it was in. They should still get the right settings. */ if( ( ic = irc_channel_by_name( xd->irc, name ) ) || ( ic = irc_channel_new( xd->irc, name ) ) ) set_setstr( &ic->set, "type", type ); handle_settings( node, &ic->set ); return XT_HANDLED; }
static struct groupchat *jabber_chat_join_(struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets) { struct jabber_data *jd = ic->proto_data; char *final_nick; /* Ignore the passed nick parameter if we have our own default */ if (!(final_nick = set_getstr(sets, "nick")) && !(final_nick = set_getstr(&ic->acc->set, "display_name"))) { /* Well, whatever, actually use the provided default, then */ final_nick = (char *) nick; } if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) { char *guessed_name = hipchat_guess_channel_name(ic, room); if (guessed_name) { set_setstr(sets, "room", guessed_name); g_free(guessed_name); /* call this same function again with the fixed name */ return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets); } } if (strchr(room, '@') == NULL) { imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?", room, room, jd->server); } else if (jabber_chat_by_jid(ic, room)) { imcb_error(ic, "Already present in chat `%s'", room); } else { /* jabber_chat_join without the underscore is the conference.c one */ return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password")); } return NULL; }
static void cmd_account(irc_t *irc, char **cmd) { account_t *a; int len; if (global.conf->authmode == AUTHMODE_REGISTERED && !(irc->status & USTATUS_IDENTIFIED)) { irc_rootmsg(irc, "This server only accepts registered users"); return; } len = strlen(cmd[1]); if (len >= 1 && g_strncasecmp(cmd[1], "add", len) == 0) { struct prpl *prpl; MIN_ARGS(3); if (!global.conf->allow_account_add) { irc_rootmsg(irc, "This server does not allow adding new accounts"); return; } if (cmd[4] == NULL) { for (a = irc->b->accounts; a; a = a->next) { if (strcmp(a->pass, PASSWORD_PENDING) == 0) { irc_rootmsg(irc, "Enter password for account %s " "first (use /OPER)", a->tag); return; } } irc->status |= OPER_HACK_ACCOUNT_PASSWORD; } prpl = find_protocol(cmd[2]); if (prpl == NULL) { if (is_protocol_disabled(cmd[2])) { irc_rootmsg(irc, "Protocol disabled in global config"); } else { irc_rootmsg(irc, "Unknown protocol"); } return; } for (a = irc->b->accounts; a; a = a->next) { if (a->prpl == prpl && prpl->handle_cmp(a->user, cmd[3]) == 0) { irc_rootmsg(irc, "Warning: You already have an account with " "protocol `%s' and username `%s'. Are you accidentally " "trying to add it twice?", prpl->name, cmd[3]); } } a = account_add(irc->b, prpl, cmd[3], cmd[4] ? cmd[4] : PASSWORD_PENDING); if (cmd[5]) { irc_rootmsg(irc, "Warning: Passing a servername/other flags to `account add' " "is now deprecated. Use `account set' instead."); set_setstr(&a->set, "server", cmd[5]); } irc_rootmsg(irc, "Account successfully added with tag %s", a->tag); if (cmd[4] == NULL) { set_t *oauth = set_find(&a->set, "oauth"); if (oauth && bool2int(set_value(oauth))) { *a->pass = '******'; irc_rootmsg(irc, "No need to enter a password for this " "account since it's using OAuth"); } else { irc_rootmsg(irc, "You can now use the /OPER command to " "enter the password"); if (oauth) { irc_rootmsg(irc, "Alternatively, enable OAuth if " "the account supports it: account %s " "set oauth on", a->tag); } } } return; } else if (len >= 1 && g_strncasecmp(cmd[1], "list", len) == 0) { int i = 0; if (strchr(irc->umode, 'b')) { irc_rootmsg(irc, "Account list:"); } for (a = irc->b->accounts; a; a = a->next) { char *con; if (a->ic && (a->ic->flags & OPT_LOGGED_IN)) { con = " (connected)"; } else if (a->ic) { con = " (connecting)"; } else if (a->reconnect) { con = " (awaiting reconnect)"; } else { con = ""; } irc_rootmsg(irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con); i++; } irc_rootmsg(irc, "End of account list"); return; } else if (cmd[2]) { /* Try the following two only if cmd[2] == NULL */ } else if (len >= 2 && g_strncasecmp(cmd[1], "on", len) == 0) { if (irc->b->accounts) { irc_rootmsg(irc, "Trying to get all accounts connected..."); for (a = irc->b->accounts; a; a = a->next) { if (!a->ic && a->auto_connect) { if (strcmp(a->pass, PASSWORD_PENDING) == 0) { irc_rootmsg(irc, "Enter password for account %s " "first (use /OPER)", a->tag); } else { account_on(irc->b, a); } } } } else { irc_rootmsg(irc, "No accounts known. Use `account add' to add one."); } return; } else if (len >= 2 && g_strncasecmp(cmd[1], "off", len) == 0) { irc_rootmsg(irc, "Deactivating all active (re)connections..."); for (a = irc->b->accounts; a; a = a->next) { if (a->ic) { account_off(irc->b, a); } else if (a->reconnect) { cancel_auto_reconnect(a); } } return; } MIN_ARGS(2); len = strlen(cmd[2]); /* At least right now, don't accept on/off/set/del as account IDs even if they're a proper match, since people not familiar with the new syntax yet may get a confusing/nasty surprise. */ if (g_strcasecmp(cmd[1], "on") == 0 || g_strcasecmp(cmd[1], "off") == 0 || g_strcasecmp(cmd[1], "set") == 0 || g_strcasecmp(cmd[1], "del") == 0 || (a = account_get(irc->b, cmd[1])) == NULL) { irc_rootmsg(irc, "Could not find account `%s'.", cmd[1]); return; } if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) { if (a->flags & ACC_FLAG_LOCKED) { irc_rootmsg(irc, "Account is locked, can't delete"); } else if (a->ic) { irc_rootmsg(irc, "Account is still logged in, can't delete"); } else { account_del(irc->b, a); irc_rootmsg(irc, "Account deleted"); } } else if (len >= 2 && g_strncasecmp(cmd[2], "on", len) == 0) { if (a->ic) { irc_rootmsg(irc, "Account already online"); } else if (strcmp(a->pass, PASSWORD_PENDING) == 0) { irc_rootmsg(irc, "Enter password for account %s " "first (use /OPER)", a->tag); } else { account_on(irc->b, a); } } else if (len >= 2 && g_strncasecmp(cmd[2], "off", len) == 0) { if (a->ic) { account_off(irc->b, a); } else if (a->reconnect) { cancel_auto_reconnect(a); irc_rootmsg(irc, "Reconnect cancelled"); } else { irc_rootmsg(irc, "Account already offline"); } } else if (len >= 1 && g_strncasecmp(cmd[2], "set", len) == 0) { cmd_set_real(irc, cmd + 2, &a->set, cmd_account_set_checkflags); } else { irc_rootmsg(irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2]); } }
static void cmd_chat(irc_t *irc, char **cmd) { account_t *acc; if (g_strcasecmp(cmd[1], "add") == 0) { char *channel, *s; struct irc_channel *ic; MIN_ARGS(3); if (!(acc = account_get(irc->b, cmd[2]))) { irc_rootmsg(irc, "Invalid account"); return; } else if (!acc->prpl->chat_join) { irc_rootmsg(irc, "Named chatrooms not supported on that account."); return; } if (cmd[4] == NULL) { channel = g_strdup(cmd[3]); if ((s = strchr(channel, '@'))) { *s = 0; } } else { channel = g_strdup(cmd[4]); } if (strchr(CTYPES, channel[0]) == NULL) { s = g_strdup_printf("#%s", channel); g_free(channel); channel = s; irc_channel_name_strip(channel); } if ((ic = irc_channel_new(irc, channel)) && set_setstr(&ic->set, "type", "chat") && set_setstr(&ic->set, "chat_type", "room") && set_setstr(&ic->set, "account", cmd[2]) && set_setstr(&ic->set, "room", cmd[3])) { irc_rootmsg(irc, "Chatroom successfully added."); } else { if (ic) { irc_channel_free(ic); } irc_rootmsg(irc, "Could not add chatroom."); } g_free(channel); } else if (g_strcasecmp(cmd[1], "with") == 0) { irc_user_t *iu; MIN_ARGS(2); if ((iu = irc_user_by_name(irc, cmd[2])) && iu->bu && iu->bu->ic->acc->prpl->chat_with) { if (!iu->bu->ic->acc->prpl->chat_with(iu->bu->ic, iu->bu->handle)) { irc_rootmsg(irc, "(Possible) failure while trying to open " "a groupchat with %s.", iu->nick); } } else { irc_rootmsg(irc, "Can't open a groupchat with %s.", cmd[2]); } } else if (g_strcasecmp(cmd[1], "list") == 0 || g_strcasecmp(cmd[1], "set") == 0 || g_strcasecmp(cmd[1], "del") == 0) { irc_rootmsg(irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command."); cmd_channel(irc, cmd); } else { irc_rootmsg(irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1]); } }
static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) { struct xml_parsedata *xd = data; irc_t *irc = xd->irc; if( xd->unknown_tag > 0 ) { xd->unknown_tag ++; } else if( g_strcasecmp( element_name, "user" ) == 0 ) { char *nick = xml_attr( attr_names, attr_values, "nick" ); char *pass = xml_attr( attr_names, attr_values, "password" ); int st; if( !nick || !pass ) { g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); } else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 ) { xd->pass_st = XML_PASS_WRONG; g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error while decoding password attribute" ); } else if( st == 0 ) { if( xd->pass_st != XML_PASS_CHECK_ONLY ) xd->pass_st = XML_PASS_OK; } else { xd->pass_st = XML_PASS_WRONG; g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Password mismatch" ); } } else if( xd->pass_st < XML_PASS_OK ) { /* Let's not parse anything else if we only have to check the password. */ } else if( g_strcasecmp( element_name, "account" ) == 0 ) { char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; char *pass_b64 = NULL; unsigned char *pass_cr = NULL; int pass_len; struct prpl *prpl = NULL; handle = xml_attr( attr_names, attr_values, "handle" ); pass_b64 = xml_attr( attr_names, attr_values, "password" ); server = xml_attr( attr_names, attr_values, "server" ); autoconnect = xml_attr( attr_names, attr_values, "autoconnect" ); tag = xml_attr( attr_names, attr_values, "tag" ); protocol = xml_attr( attr_names, attr_values, "protocol" ); if( protocol ) prpl = find_protocol( protocol ); if( !handle || !pass_b64 || !protocol ) g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); else if( !prpl ) g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unknown protocol: %s", protocol ); else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 ) { xd->current_account = account_add( irc->b, prpl, handle, password ); if( server ) set_setstr( &xd->current_account->set, "server", server ); if( autoconnect ) set_setstr( &xd->current_account->set, "auto_connect", autoconnect ); if( tag ) set_setstr( &xd->current_account->set, "tag", tag ); } else { /* Actually the _decode functions don't even return error codes, but maybe they will later... */ g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error while decrypting account password" ); } g_free( pass_cr ); g_free( password ); } else if( g_strcasecmp( element_name, "setting" ) == 0 ) { char *setting; if( xd->current_setting ) { g_free( xd->current_setting ); xd->current_setting = NULL; } if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) { if( xd->current_channel != NULL ) xd->current_set_head = &xd->current_channel->set; else if( xd->current_account != NULL ) xd->current_set_head = &xd->current_account->set; else xd->current_set_head = &xd->irc->b->set; xd->current_setting = g_strdup( setting ); } else g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); } else if( g_strcasecmp( element_name, "buddy" ) == 0 ) { char *handle, *nick; handle = xml_attr( attr_names, attr_values, "handle" ); nick = xml_attr( attr_names, attr_values, "nick" ); if( xd->current_account && handle && nick ) { nick_set_raw( xd->current_account, handle, nick ); } else { g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); } } else if( g_strcasecmp( element_name, "channel" ) == 0 ) { char *name, *type; name = xml_attr( attr_names, attr_values, "name" ); type = xml_attr( attr_names, attr_values, "type" ); if( !name || !type ) { g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); return; } /* The channel may exist already, for example if it's &bitlbee. Also, it's possible that the user just reconnected and the IRC client already rejoined all channels it was in. They should still get the right settings. */ if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) || ( xd->current_channel = irc_channel_new( irc, name ) ) ) set_setstr(&xd->current_channel->set, "type", type ); } /* Backward compatibility: Keep this around for a while for people switching from BitlBee 1.2.4+. */ else if( g_strcasecmp( element_name, "chat" ) == 0 ) { char *handle, *channel; handle = xml_attr( attr_names, attr_values, "handle" ); channel = xml_attr( attr_names, attr_values, "channel" ); if( xd->current_account && handle && channel ) { irc_channel_t *ic; if( ( ic = irc_channel_new( irc, channel ) ) && set_setstr( &ic->set, "type", "chat" ) && set_setstr( &ic->set, "chat_type", "room" ) && set_setstr( &ic->set, "account", xd->current_account->tag ) && set_setstr( &ic->set, "room", handle ) ) { /* Try to pick up some settings where possible. */ xd->current_channel = ic; } else if( ic ) irc_channel_free( ic ); } else { g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing attributes for %s element", element_name ); } } else { xd->unknown_tag ++; irc_rootmsg( irc, "Warning: Unknown XML tag found in configuration file (%s). " "This may happen when downgrading BitlBee versions. " "This tag will be skipped and the information will be lost " "once you save your settings.", element_name ); /* g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Unkown element: %s", element_name ); */ } }
static xt_status handle_account( struct xt_node *node, gpointer data ) { struct xml_parsedata *xd = data; char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; char *pass_b64 = NULL; unsigned char *pass_cr = NULL; int pass_len, local = 0; struct prpl *prpl = NULL; account_t *acc; struct xt_node *c; handle = xt_find_attr( node, "handle" ); pass_b64 = xt_find_attr( node, "password" ); server = xt_find_attr( node, "server" ); autoconnect = xt_find_attr( node, "autoconnect" ); tag = xt_find_attr( node, "tag" ); protocol = xt_find_attr( node, "protocol" ); if( protocol ) { prpl = find_protocol( protocol ); local = protocol_account_islocal( protocol ); } if( !handle || !pass_b64 || !protocol || !prpl ) return XT_ABORT; else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 ) { acc = account_add( xd->irc->b, prpl, handle, password ); if( server ) set_setstr( &acc->set, "server", server ); if( autoconnect ) set_setstr( &acc->set, "auto_connect", autoconnect ); if( tag ) set_setstr( &acc->set, "tag", tag ); if( local ) acc->flags |= ACC_FLAG_LOCAL; } else return XT_ABORT; g_free( pass_cr ); g_free( password ); handle_settings( node, &acc->set ); for( c = node->children; ( c = xt_find_node( c, "buddy" ) ); c = c->next ) { char *handle, *nick; handle = xt_find_attr( c, "handle" ); nick = xt_find_attr( c, "nick" ); if( handle && nick ) nick_set_raw( acc, handle, nick ); else return XT_ABORT; } return XT_HANDLED; }
static void jabber_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); struct jabber_data *jd = g_new0( struct jabber_data, 1 ); char *s; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events on dead connections. */ jabber_connections = g_slist_prepend( jabber_connections, ic ); jd->ic = ic; ic->proto_data = jd; jabber_set_me( ic, acc->user ); jd->fd = jd->r_inpa = jd->w_inpa = -1; if( jd->server == NULL ) { imcb_error( ic, "Incomplete account name (format it like <*****@*****.**>)" ); imc_logout( ic, FALSE ); return; } if( ( s = strchr( jd->server, '/' ) ) ) { *s = 0; set_setstr( &acc->set, "resource", s + 1 ); /* Also remove the /resource from the original variable so we won't have to do this again every time. */ s = strchr( acc->user, '/' ); *s = 0; } jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free ); jd->buddies = g_hash_table_new( g_str_hash, g_str_equal ); if( set_getbool( &acc->set, "oauth" ) ) { GSList *p_in = NULL; const char *tok; jd->fd = jd->r_inpa = jd->w_inpa = -1; if( strstr( jd->server, ".live.com" ) ) jd->oauth2_service = &oauth2_service_mslive; else if( strstr( jd->server, ".facebook.com" ) ) jd->oauth2_service = &oauth2_service_facebook; else jd->oauth2_service = &oauth2_service_google; oauth_params_parse( &p_in, ic->acc->pass ); /* First see if we have a refresh token, in which case any access token we *might* have has probably expired already anyway. */ if( ( tok = oauth_params_get( &p_in, "refresh_token" ) ) ) { sasl_oauth2_refresh( ic, tok ); } /* If we don't have a refresh token, let's hope the access token is still usable. */ else if( ( tok = oauth_params_get( &p_in, "access_token" ) ) ) { jd->oauth2_access_token = g_strdup( tok ); jabber_connect( ic ); } /* If we don't have any, start the OAuth process now. Don't even open an XMPP connection yet. */ else { sasl_oauth2_init( ic ); ic->flags |= OPT_SLOW_LOGIN; } oauth_params_free( &p_in ); } else jabber_connect( ic ); }
static xt_status handle_account(struct xt_node *node, gpointer data) { struct xml_parsedata *xd = data; char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag, *locked; char *pass_b64 = NULL; unsigned char *pass_cr = NULL; int pass_len, local = 0; struct prpl *prpl = NULL; account_t *acc; struct xt_node *c; handle = xt_find_attr(node, "handle"); pass_b64 = xt_find_attr(node, "password"); server = xt_find_attr(node, "server"); autoconnect = xt_find_attr(node, "autoconnect"); tag = xt_find_attr(node, "tag"); locked = xt_find_attr(node, "locked"); protocol = xt_find_attr(node, "protocol"); if (protocol) { prpl = find_protocol(protocol); if (!prpl) { irc_rootmsg(xd->irc, "Error loading user config: Protocol not found: `%s'", protocol); return XT_ABORT; } local = protocol_account_islocal(protocol); } if (!handle || !pass_b64 || !protocol || !prpl) { return XT_ABORT; } pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr); if (xd->irc->auth_backend) { password = g_strdup((char *)pass_cr); } else { pass_len = arc_decode(pass_cr, pass_len, &password, xd->given_pass); if (pass_len < 0) { g_free(pass_cr); g_free(password); return XT_ABORT; } } acc = account_add(xd->irc->b, prpl, handle, password); if (server) { set_setstr(&acc->set, "server", server); } if (autoconnect) { set_setstr(&acc->set, "auto_connect", autoconnect); } if (tag) { set_setstr(&acc->set, "tag", tag); } if (local) { acc->flags |= ACC_FLAG_LOCAL; } if (locked && !g_strcasecmp(locked, "true")) { acc->flags |= ACC_FLAG_LOCKED; } g_free(pass_cr); g_free(password); handle_settings(node, &acc->set); for (c = node->children; (c = xt_find_node(c, "buddy")); c = c->next) { char *handle, *nick; handle = xt_find_attr(c, "handle"); nick = xt_find_attr(c, "nick"); if (handle && nick) { nick_set_raw(acc, handle, nick); } else { return XT_ABORT; } } return XT_HANDLED; }
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 + 1 ); set_setstr( &ic->set, "fill_by", "group" ); } else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) ) { set_setstr( &ic->set, "fill_by", "protocol" ); } else if( set_setstr( &ic->set, "account", ic->name + 1 ) ) { set_setstr( &ic->set, "fill_by", "account" ); } else { /* The set commands above will run this already, but if we didn't hit any, we have to fill the channel with the default population. */ bee_irc_channel_update( ic->irc, ic, NULL ); } } else if( ic == NULL ) { irc_send_num( irc, 479, "%s :Invalid channel name", s ); goto next; } if( ic->flags & IRC_CHANNEL_JOINED ) /* Dude, you're already there... RFC doesn't have any reply for that though? */ goto next; if( ic->f->join && !ic->f->join( ic ) ) /* The story is: FALSE either means the handler showed an error message, or is doing some work before the join should be confirmed. (In the latter case, the caller should take care of that confirmation.) TRUE means all's good, let the user join the channel right away. */ goto next; irc_channel_add_user( ic, irc->user ); next: if( comma ) { s = comma + 1; *comma = ','; } else break; } }
static void jabber_init(account_t *acc) { set_t *s; char str[16]; s = set_add(&acc->set, "activity_timeout", "600", set_eval_int, acc); s = set_add(&acc->set, "display_name", NULL, NULL, acc); g_snprintf(str, sizeof(str), "%d", jabber_port_list[0]); s = set_add(&acc->set, "port", str, set_eval_int, acc); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add(&acc->set, "priority", "0", set_eval_priority, acc); s = set_add(&acc->set, "proxy", "<local>;<auto>", NULL, acc); s = set_add(&acc->set, "resource", "BitlBeeInnoExperimental", NULL, acc); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add(&acc->set, "resource_select", "activity", NULL, acc); s = set_add(&acc->set, "sasl", "true", set_eval_bool, acc); s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT; s = set_add(&acc->set, "server", NULL, set_eval_account, acc); s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK; if (strcmp(acc->prpl->name, "hipchat") == 0) { set_setstr(&acc->set, "server", "chat.hipchat.com"); } else { set_add(&acc->set, "oauth", "false", set_eval_oauth, acc); /* this reuses set_eval_oauth, which clears the password */ set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc); } s = set_add(&acc->set, "ssl", "false", set_eval_bool, acc); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add(&acc->set, "tls", "true", set_eval_tls, acc); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add(&acc->set, "tls_verify", "true", set_eval_bool, acc); s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add(&acc->set, "user_agent", "BitlBee", NULL, acc); s = set_add(&acc->set, "xmlconsole", "false", set_eval_bool, acc); s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc); s->flags |= ACC_SET_OFFLINE_ONLY; /* changing this is rarely needed so keeping it secret */ s = set_add(&acc->set, "mail_notifications_limit", "5", set_eval_int, acc); s->flags |= SET_HIDDEN_DEFAULT; s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc); s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK; acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE | ACC_FLAG_HANDLE_DOMAINS; }
account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass ) { account_t *a; set_t *s; char tag[strlen(prpl->name)+10]; if( bee->accounts ) { for( a = bee->accounts; a->next; a = a->next ); a = a->next = g_new0( account_t, 1 ); } else { bee->accounts = a = g_new0 ( account_t, 1 ); } a->prpl = prpl; a->user = g_strdup( user ); a->pass = g_strdup( pass ); a->auto_connect = 1; a->bee = bee; s = set_add( &a->set, "auto_connect", "true", set_eval_account, a ); s->flags |= SET_NOSAVE; s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); s = set_add( &a->set, "nick_format", NULL, NULL, a ); s->flags |= SET_NULL_OK; s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a ); s->flags |= SET_NOSAVE; /* Just for bw compatibility! */ s = set_add( &a->set, "password", NULL, set_eval_account, a ); s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD; s = set_add( &a->set, "tag", NULL, set_eval_account, a ); s->flags |= SET_NOSAVE; s = set_add( &a->set, "username", NULL, set_eval_account, a ); s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY; set_setstr( &a->set, "username", user ); /* Hardcode some more clever tag guesses. */ strcpy( tag, prpl->name ); if( strcmp( prpl->name, "oscar" ) == 0 ) { if( isdigit( a->user[0] ) ) strcpy( tag, "icq" ); else strcpy( tag, "aim" ); } else if( strcmp( prpl->name, "jabber" ) == 0 ) { if( strstr( a->user, "@gmail.com" ) || strstr( a->user, "@googlemail.com" ) ) strcpy( tag, "gtalk" ); else if( strstr( a->user, "@chat.facebook.com" ) ) strcpy( tag, "fb" ); } if( account_by_tag( bee, tag ) ) { char *numpos = tag + strlen( tag ); int i; for( i = 2; i < 10000; i ++ ) { sprintf( numpos, "%d", i ); if( !account_by_tag( bee, tag ) ) break; } } set_setstr( &a->set, "tag", tag ); a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free ); /* This function adds some more settings (and might want to do more things that have to be done now, although I can't think of anything. */ if( prpl->init ) prpl->init( a ); s = set_add( &a->set, "away", NULL, set_eval_account, a ); s->flags |= SET_NULL_OK; if( a->flags & ACC_FLAG_STATUS_MESSAGE ) { s = set_add( &a->set, "status", NULL, set_eval_account, a ); s->flags |= SET_NULL_OK; } return a; }
static void check_buddy_add(int l) { struct jabber_buddy *budw1, *budw2, *budw3, *budn, *bud; budw1 = jabber_buddy_add( ic, "[email protected]/BitlBee" ); budw1->last_msg = time( NULL ) - 100; budw2 = jabber_buddy_add( ic, "[email protected]/Telepathy" ); budw2->priority = 2; budw2->last_msg = time( NULL ); budw3 = jabber_buddy_add( ic, "[email protected]/bitlbee" ); budw3->last_msg = time( NULL ) - 200; budw3->priority = 4; /* TODO(wilmer): Shouldn't this just return budw3? */ fail_if( jabber_buddy_add( ic, "[email protected]/Telepathy" ) != NULL ); budn = jabber_buddy_add( ic, "*****@*****.**" ); /* Shouldn't be allowed if there's already a bare JID. */ fail_if( jabber_buddy_add( ic, "[email protected]/Illegal" ) ); /* Case sensitivity: Case only matters after the / */ fail_if( jabber_buddy_by_jid( ic, "[email protected]/BitlBee", 0 ) == jabber_buddy_by_jid( ic, "[email protected]/bitlbee", 0 ) ); fail_if( jabber_buddy_by_jid( ic, "[email protected]/telepathy", 0 ) ); fail_unless( jabber_buddy_by_jid( ic, "[email protected]/BitlBee", 0 ) == budw1 ); fail_unless( jabber_buddy_by_jid( ic, "[email protected]/BitlBee", GET_BUDDY_EXACT ) == budw1 ); fail_unless( jabber_buddy_by_jid( ic, "[email protected]/BitlBee", GET_BUDDY_CREAT ) == budw1 ); fail_if( jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_EXACT ) ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) == budw3 ); /* Check O_FIRST and see if it's indeed the first item from the list. */ fail_unless( ( bud = jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_FIRST ) ) == budw1 ); fail_unless( bud->next == budw2 && bud->next->next == budw3 && bud->next->next->next == NULL ); /* Change the resource_select setting, now we should get a different resource. */ set_setstr( &ic->acc->set, "resource_select", "activity" ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) == budw2 ); /* Some testing of bare JID handling (which is horrible). */ fail_if( jabber_buddy_by_jid( ic, "[email protected]/Illegal", 0 ) ); fail_if( jabber_buddy_by_jid( ic, "[email protected]/Illegal", GET_BUDDY_CREAT ) ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) == budn ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_EXACT ) == budn ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_CREAT ) == budn ); /* More case sensitivity testing, and see if remove works properly. */ fail_if( jabber_buddy_remove( ic, "[email protected]/telepathy" ) ); fail_if( jabber_buddy_by_jid( ic, "[email protected]/telepathy", GET_BUDDY_CREAT ) == budw2 ); fail_unless( jabber_buddy_remove( ic, "[email protected]/Telepathy" ) ); fail_unless( jabber_buddy_remove( ic, "[email protected]/telepathy" ) ); /* Test activity_timeout and GET_BUDDY_BARE_OK. */ fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_BARE_OK ) == budw1 ); budw1->last_msg -= 50; fail_unless( ( bud = jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_BARE_OK ) ) != NULL ); fail_unless( strcmp( bud->full_jid, "*****@*****.**" ) == 0 ); fail_if( jabber_buddy_remove( ic, "*****@*****.**" ) ); fail_unless( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) == budw1 ); fail_if( jabber_buddy_remove( ic, "*****@*****.**" ) ); fail_unless( jabber_buddy_remove( ic, "[email protected]/bitlbee" ) ); fail_unless( jabber_buddy_remove( ic, "[email protected]/BitlBee" ) ); fail_if( jabber_buddy_by_jid( ic, "*****@*****.**", GET_BUDDY_BARE_OK ) ); /* Check if remove_bare() indeed gets rid of all. */ /* disable this one for now. fail_unless( jabber_buddy_remove_bare( ic, "*****@*****.**" ) ); fail_if( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) ); */ fail_if( jabber_buddy_remove( ic, "[email protected]/Illegal" ) ); fail_unless( jabber_buddy_remove( ic, "*****@*****.**" ) ); fail_if( jabber_buddy_by_jid( ic, "*****@*****.**", 0 ) ); /* Fixing a bug in this branch that caused information to get lost when removing the first full JID from a list. */ jabber_buddy_add( ic, "[email protected]/A" ); jabber_buddy_add( ic, "[email protected]/B" ); jabber_buddy_add( ic, "[email protected]/C" ); fail_unless( jabber_buddy_remove( ic, "[email protected]/A" ) ); fail_unless( jabber_buddy_remove( ic, "[email protected]/B" ) ); fail_unless( jabber_buddy_remove( ic, "[email protected]/C" ) ); }