static xt_status jabber_carbons_message(struct xt_node *node, gpointer data) { struct im_connection *ic = data; struct xt_node *wrap, *fwd, *msg; gboolean carbons_sent; if ((wrap = xt_find_node(node->children, "received"))) { carbons_sent = FALSE; } else if ((wrap = xt_find_node(node->children, "sent"))) { carbons_sent = TRUE; } if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) { return XT_NEXT; } if (!(fwd = xt_find_node(wrap->children, "forwarded")) || (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) || !(msg = xt_find_node(fwd->children, "message"))) { imcb_log(ic, "Error: Invalid carbons message received"); return XT_ABORT; } return jabber_pkt_message_normal(msg, data, carbons_sent); }
static void handle_settings(struct xt_node *node, set_t **head) { struct xt_node *c; struct set *s; for (c = node->children; (c = xt_find_node(c, "setting")); c = c->next) { char *name = xt_find_attr(c, "name"); char *locked = xt_find_attr(c, "locked"); 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); if (locked && !g_strcasecmp(locked, "true")) { s = set_find(head, name); if (s) { s->flags |= SET_LOCKED; } } } }
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; }
xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *c; struct jabber_buddy *bud; char *feature, *xmlns, *from; if( !( from = xt_find_attr( node, "from" ) ) || !( c = xt_find_node( node->children, "query" ) ) || !( xmlns = xt_find_attr( c, "xmlns" ) ) || !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; } if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { /* Who cares about the unknown... */ imcb_log( ic, "Couldn't find buddy: %s", from ); return XT_HANDLED; } c = c->children; while( ( c = xt_find_node( c, "feature" ) ) ) { feature = xt_find_attr( c, "var" ); if( feature ) bud->features = g_slist_append( bud->features, g_strdup( feature ) ); c = c->next; } return XT_HANDLED; }
xt_status sasl_pkt_result( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; char *s; s = xt_find_attr( node, "xmlns" ); if( !s || strcmp( s, XMLNS_SASL ) != 0 ) { imcb_log( ic, "Stream error while authenticating" ); imc_logout( ic, FALSE ); return XT_ABORT; } if( strcmp( node->name, "success" ) == 0 ) { imcb_log( ic, "Authentication finished" ); jd->flags |= JFLAG_AUTHENTICATED | JFLAG_STREAM_RESTART; } else if( strcmp( node->name, "failure" ) == 0 ) { imcb_error( ic, "Authentication failure" ); imc_logout( ic, FALSE ); return XT_ABORT; } return XT_HANDLED; }
static xt_status jabber_iq_version_response( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *query; GString *rets; char *s; char *ret[2] = {}; bee_user_t *bu; struct jabber_buddy *bud = NULL; if( ( s = xt_find_attr( node, "from" ) ) && ( bud = jabber_buddy_by_jid( ic, s, 0 ) ) && ( query = xt_find_node( node->children, "query" ) ) && ( bu = bee_user_by_handle( ic->bee, ic, bud->bare_jid ) ) ) { rets = g_string_new( "Resource " ); g_string_append( rets, bud->resource ); } else return XT_HANDLED; for( query = query->children; query; query = query->next ) if( query->text_len > 0 ) g_string_append_printf( rets, " %s: %s,", query->name, query->text ); g_string_truncate( rets, rets->len - 1 ); ret[0] = rets->str; imcb_buddy_action_response( bu, "VERSION", ret, NULL ); g_string_free( rets, TRUE ); return XT_HANDLED; }
static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct jabber_data *jd = ic->proto_data; char *type; if( !( type = xt_find_attr( node, "type" ) ) ) { imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" ); imc_logout( ic, FALSE ); return XT_HANDLED; } if( strcmp( type, "error" ) == 0 ) { imcb_error( ic, "Authentication failure" ); imc_logout( ic, FALSE ); return XT_ABORT; } else if( strcmp( type, "result" ) == 0 ) { /* This happens when we just successfully authenticated the old (non-SASL) way. */ jd->flags |= JFLAG_AUTHENTICATED; if( !jabber_get_roster( ic ) ) return XT_ABORT; if( !jabber_iq_disco_server( ic ) ) return XT_ABORT; } return XT_HANDLED; }
static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; char *xmlns; xmlns = xt_find_attr( node, "xmlns" ); /* Just ignore it when it doesn't seem to be TLS-related (is that at all possible??). */ if( !xmlns || strcmp( xmlns, XMLNS_TLS ) != 0 ) return XT_HANDLED; /* We don't want event handlers to touch our TLS session while it's still initializing! */ b_event_remove( jd->r_inpa ); if( jd->tx_len > 0 ) { /* Actually the write queue should be empty here, but just to be sure... */ b_event_remove( jd->w_inpa ); g_free( jd->txq ); jd->txq = NULL; jd->tx_len = 0; } jd->w_inpa = jd->r_inpa = 0; imcb_log( ic, "Converting stream to TLS" ); jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic ); return XT_HANDLED; }
static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct jabber_data *jd = ic->proto_data; struct xt_node *reply, *query; xt_status st; char *s; if( !( query = xt_find_node( node->children, "query" ) ) ) { imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" ); imc_logout( ic, FALSE ); return XT_HANDLED; } /* Time to authenticate ourselves! */ reply = xt_new_node( "query", NULL, NULL ); xt_add_attr( reply, "xmlns", XMLNS_AUTH ); xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) ); xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) ); if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) ) { /* We can do digest authentication, it seems, and of course we prefer that. */ sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; int i; sha1_init( &sha ); sha1_append( &sha, (unsigned char*) s, strlen( s ) ); sha1_append( &sha, (unsigned char*) ic->acc->pass, strlen( ic->acc->pass ) ); sha1_finish( &sha, hash ); for( i = 0; i < 20; i ++ ) sprintf( hash_hex + i * 2, "%02x", hash[i] ); xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) ); } else if( xt_find_node( query->children, "password" ) ) { /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */ xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) ); } else { xt_free_node( reply ); imcb_error( ic, "Can't find suitable authentication method" ); imc_logout( ic, FALSE ); return XT_ABORT; } reply = jabber_make_packet( "iq", "set", NULL, reply ); jabber_cache_add( ic, reply, jabber_finish_iq_auth ); st = jabber_write_packet( ic, reply ); return st ? XT_HANDLED : XT_ABORT; }
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; 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 ); 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 ); } 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 xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *query, *c; int initial = ( orig != NULL ); if( !( query = xt_find_node( node->children, "query" ) ) ) { imcb_log( ic, "Warning: Received NULL roster packet" ); return XT_HANDLED; } c = query->children; while( ( c = xt_find_node( c, "item" ) ) ) { struct xt_node *group = xt_find_node( c->children, "group" ); char *jid = xt_find_attr( c, "jid" ); char *name = xt_find_attr( c, "name" ); char *sub = xt_find_attr( c, "subscription" ); if( jid && sub ) { if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) ) { imcb_add_buddy( ic, jid, ( group && group->text_len ) ? group->text : NULL ); if( name ) imcb_rename_buddy( ic, jid, name ); } else if( strcmp( sub, "remove" ) == 0 ) { jabber_buddy_remove_bare( ic, jid ); imcb_remove_buddy( ic, jid, NULL ); } } c = c->next; } if( initial ) imcb_connected( ic ); return XT_HANDLED; }
struct xt_node *xt_find_node_by_attr(struct xt_node *xt, const char *tag, const char *key, const char *value) { struct xt_node *c; char *s; for (c = xt; (c = xt_find_node(c, tag)); c = c->next) { if ((s = xt_find_attr(c, key)) && strcmp(s, value) == 0) { return c; } } return NULL; }
static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { char *s, *jid = NULL; struct xt_node *c; if( ( c = xt_find_node( orig->children, "query" ) ) && ( c = xt_find_node( c->children, "item" ) ) && ( jid = xt_find_attr( c, "jid" ) ) && ( s = xt_find_attr( node, "type" ) ) && strcmp( s, "result" ) == 0 ) { if( bee_user_by_handle( ic->bee, ic, jid ) == NULL ) imcb_add_buddy( ic, jid, NULL ); } else { imcb_log( ic, "Error while adding `%s' to your contact list.", jid ? jid : "(unknown handle)" ); } return XT_HANDLED; }
static xt_status jabber_iq_disco_server_response( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct jabber_data *jd = ic->proto_data; struct xt_node *id; if( ( id = xt_find_path( node, "query/identity" ) ) ) { char *cat, *type, *name; if( !( cat = xt_find_attr( id, "category" ) ) || !( type = xt_find_attr( id, "type" ) ) || !( name = xt_find_attr( id, "name" ) ) ) return XT_HANDLED; if( strcmp( cat, "server" ) == 0 && strcmp( type, "im" ) == 0 && strstr( name, "Google" ) != NULL ) jd->flags |= JFLAG_GTALK; } return XT_HANDLED; }
xt_status jabber_pkt_message(struct xt_node *node, gpointer data) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; char *from = xt_find_attr(node, "from"); if (jabber_compare_jid(jd->me, from)) { /* Probably a Carbons message */ xt_status st = jabber_carbons_message(node, data); if (st == XT_HANDLED || st == XT_ABORT) { return st; } } return jabber_pkt_message_normal(node, data, FALSE); }
static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) { struct jabber_error *err; struct jabber_buddy *bud; char *room; room = xt_find_attr(orig, "to"); bud = jabber_buddy_by_jid(ic, room, 0); err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR); if (err) { imcb_error(ic, "Error joining groupchat %s: %s%s%s", room, err->code, err->text ? ": " : "", err->text ? err->text : ""); jabber_error_free(err); } if (bud) { jabber_chat_free(jabber_chat_by_jid(ic, bud->bare_jid)); } return XT_HANDLED; }
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 ); } }
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; struct xt_node *c, *reply = NULL; char *type, *s; int st, pack = 1; type = xt_find_attr( node, "type" ); if( !type ) { imcb_error( ic, "Received IQ packet without type." ); imc_logout( ic, TRUE ); return XT_ABORT; } if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 ) { return jabber_cache_handle_packet( ic, node ); } else if( strcmp( type, "get" ) == 0 ) { if( !( ( c = xt_find_node( node->children, "query" ) ) || ( c = xt_find_node( node->children, "ping" ) ) || ( c = xt_find_node( node->children, "time" ) ) ) || !( s = xt_find_attr( c, "xmlns" ) ) ) { /* Sigh. Who decided to suddenly invent new elements instead of just sticking with <query/>? */ return XT_HANDLED; } reply = xt_new_node( "query", NULL, NULL ); xt_add_attr( reply, "xmlns", s ); /* Of course this is a very essential query to support. ;-) */ if( strcmp( s, XMLNS_VERSION ) == 0 ) { xt_add_child( reply, xt_new_node( "name", set_getstr( &ic->acc->set, "user_agent" ), NULL ) ); xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) ); xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) ); } else if( strcmp( s, XMLNS_TIME_OLD ) == 0 ) { time_t time_ep; char buf[1024]; buf[sizeof(buf)-1] = 0; time_ep = time( NULL ); strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "utc", buf, NULL ) ); strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "tz", buf, NULL ) ); } else if( strcmp( s, XMLNS_TIME ) == 0 ) { time_t time_ep; char buf[1024]; buf[sizeof(buf)-1] = 0; time_ep = time( NULL ); xt_free_node( reply ); reply = xt_new_node( "time", NULL, NULL ); xt_add_attr( reply, "xmlns", XMLNS_TIME ); strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%SZ", gmtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "utc", buf, NULL ) ); strftime( buf, sizeof( buf ) - 1, "%z", localtime( &time_ep ) ); if( strlen( buf ) >= 5 ) { buf[6] = '\0'; buf[5] = buf[4]; buf[4] = buf[3]; buf[3] = ':'; } xt_add_child( reply, xt_new_node( "tzo", buf, NULL ) ); } else if( strcmp( s, XMLNS_PING ) == 0 ) { xt_free_node( reply ); reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL ); if( ( s = xt_find_attr( node, "id" ) ) ) xt_add_attr( reply, "id", s ); pack = 0; } else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 ) { const char *features[] = { XMLNS_DISCO_INFO, XMLNS_VERSION, XMLNS_TIME_OLD, XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, XMLNS_PING, XMLNS_SI, XMLNS_BYTESTREAMS, XMLNS_FILETRANSFER, NULL }; const char **f; c = xt_new_node( "identity", NULL, NULL ); xt_add_attr( c, "category", "client" ); xt_add_attr( c, "type", "pc" ); xt_add_attr( c, "name", set_getstr( &ic->acc->set, "user_agent" ) ); xt_add_child( reply, c ); for( f = features; *f; f ++ ) { c = xt_new_node( "feature", NULL, NULL ); xt_add_attr( c, "var", *f ); xt_add_child( reply, c ); } } else { xt_free_node( reply ); reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } else if( strcmp( type, "set" ) == 0 ) { if( ( c = xt_find_node( node->children, "si" ) ) && ( s = xt_find_attr( c, "xmlns" ) ) && ( strcmp( s, XMLNS_SI ) == 0 ) ) { return jabber_si_handle_request( ic, node, c ); } else if( !( c = xt_find_node( node->children, "query" ) ) || !( s = xt_find_attr( c, "xmlns" ) ) ) { return XT_HANDLED; } else if( strcmp( s, XMLNS_ROSTER ) == 0 ) { /* This is a roster push. XMPP servers send this when someone was added to (or removed from) the buddy list. AFAIK they're sent even if we added this buddy in our own session. */ int bare_len = strlen( jd->me ); if( ( s = xt_find_attr( node, "from" ) ) == NULL || ( strncmp( s, jd->me, bare_len ) == 0 && ( s[bare_len] == 0 || s[bare_len] == '/' ) ) ) { jabber_parse_roster( ic, node, NULL ); /* Should we generate a reply here? Don't think it's very important... */ } else { imcb_log( ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)" ); xt_free_node( reply ); reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL ); pack = 0; } } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) { /* Bytestream Request (stage 2 of file transfer) */ return jabber_bs_recv_request( ic, node, c ); } else { xt_free_node( reply ); reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } /* If we recognized the xmlns and managed to generate a reply, finish and send it. */ if( reply ) { /* Normally we still have to pack it into an iq-result packet, but for errors, for example, we don't. */ if( pack ) { reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply ); if( ( s = xt_find_attr( node, "id" ) ) ) xt_add_attr( reply, "id", s ); } st = jabber_write_packet( ic, reply ); xt_free_node( reply ); if( !st ) return XT_ABORT; } return XT_HANDLED; }
static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action ) { struct xml_parsedata xd[1]; char *fn, buf[2048]; int fd, st; struct xt_parser *xp = NULL; struct xt_node *node; storage_status_t ret = STORAGE_OTHER_ERROR; xd->irc = irc; strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH ); xd->given_nick[MAX_NICK_LENGTH] = '\0'; nick_lc( NULL, xd->given_nick ); xd->given_pass = (char*) password; fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL ); if( ( fd = open( fn, O_RDONLY ) ) < 0 ) { ret = STORAGE_NO_SUCH_USER; goto error; } xp = xt_new( handlers, xd ); while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) { st = xt_feed( xp, buf, st ); if( st != 1 ) break; } close( fd ); if( st != 0 ) goto error; node = xp->root; if( node == NULL || node->next != NULL || strcmp( node->name, "user" ) != 0 ) goto error; { char *nick = xt_find_attr( node, "nick" ); char *pass = xt_find_attr( node, "password" ); if( !nick || !pass ) { goto error; } else if( ( st = md5_verify_password( xd->given_pass, pass ) ) != 0 ) { ret = STORAGE_INVALID_PASSWORD; goto error; } } if( action == XML_PASS_CHECK_ONLY ) { ret = STORAGE_OK; goto error; } /* DO NOT call xt_handle() before verifying the password! */ if( xt_handle( xp, NULL, 1 ) == XT_HANDLED ) ret = STORAGE_OK; handle_settings( node, &xd->irc->b->set ); error: xt_free( xp ); g_free( fn ); return ret; }
/* This one is needed to judge if we'll do authentication using IQ or SASL. It's done by checking if the <stream:stream> from the server has a version attribute. I don't know if this is the right way though... */ gboolean sasl_supported( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0; }
xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; struct xt_node *c, *reply; char *s; int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0; int want_oauth = FALSE; GString *mechs; if( !sasl_supported( ic ) ) { /* Should abort this now, since we should already be doing IQ authentication. Strange things happen when you try to do both... */ imcb_log( ic, "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!" ); return XT_HANDLED; } s = xt_find_attr( node, "xmlns" ); if( !s || strcmp( s, XMLNS_SASL ) != 0 ) { imcb_log( ic, "Stream error while authenticating" ); imc_logout( ic, FALSE ); return XT_ABORT; } want_oauth = set_getbool( &ic->acc->set, "oauth" ); mechs = g_string_new( "" ); c = node->children; while( ( c = xt_find_node( c, "mechanism" ) ) ) { if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 ) sup_plain = 1; else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 ) sup_digest = 1; else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 ) sup_gtalk = 1; else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 ) sup_fb = 1; if( c->text ) g_string_append_printf( mechs, " %s", c->text ); c = c->next; } if( !want_oauth && !sup_plain && !sup_digest ) { if( !sup_gtalk && !sup_fb ) imcb_error( ic, "This server requires OAuth " "(supported schemes:%s)", mechs->str ); else imcb_error( ic, "BitlBee does not support any of the offered SASL " "authentication schemes:%s", mechs->str ); imc_logout( ic, FALSE ); g_string_free( mechs, TRUE ); return XT_ABORT; } g_string_free( mechs, TRUE ); reply = xt_new_node( "auth", NULL, NULL ); xt_add_attr( reply, "xmlns", XMLNS_SASL ); if( sup_gtalk && want_oauth ) { int len; /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation. It's currently used by GTalk and vaguely documented on http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */ xt_add_attr( reply, "mechanism", "X-OAUTH2" ); len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2; s = g_malloc( len + 1 ); s[0] = 0; strcpy( s + 1, jd->username ); strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token ); reply->text = base64_encode( (unsigned char *)s, len ); reply->text_len = strlen( reply->text ); g_free( s ); } else if( sup_fb && want_oauth ) { xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" ); jd->flags |= JFLAG_SASL_FB; } else if( want_oauth ) { imcb_error( ic, "OAuth requested, but not supported by server" ); imc_logout( ic, FALSE ); xt_free_node( reply ); return XT_ABORT; } else if( sup_digest ) { xt_add_attr( reply, "mechanism", "DIGEST-MD5" ); /* The rest will be done later, when we receive a <challenge/>. */ } else if( sup_plain ) { int len; xt_add_attr( reply, "mechanism", "PLAIN" ); /* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */ len = strlen( jd->username ) + strlen( ic->acc->pass ) + 2; s = g_malloc( len + 1 ); s[0] = 0; strcpy( s + 1, jd->username ); strcpy( s + 2 + strlen( jd->username ), ic->acc->pass ); reply->text = base64_encode( (unsigned char *)s, len ); reply->text_len = strlen( reply->text ); g_free( s ); } if( reply && !jabber_write_packet( ic, reply ) ) { xt_free_node( reply ); return XT_ABORT; } xt_free_node( reply ); /* To prevent classic authentication from happening. */ jd->flags |= JFLAG_STREAM_STARTED; return XT_HANDLED; }
xt_status jabber_pkt_message( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; char *from = xt_find_attr( node, "from" ); char *type = xt_find_attr( node, "type" ); char *id = xt_find_attr( node, "id" ); struct xt_node *body = xt_find_node( node->children, "body" ), *c; struct xt_node *request = xt_find_node( node->children, "request" ); struct jabber_buddy *bud = NULL; char *s, *room = NULL, *reason = NULL; if( !from ) return XT_HANDLED; /* Consider this packet corrupted. */ if( request && id ) { /* Send a message receipt (XEP-0184), looking like this: * <message * from='[email protected]/throne' * id='bi29sg183b4v' * to='[email protected]/westminster'> * <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/> * </message> */ struct xt_node *received, *receipt; received = xt_new_node( "received", NULL, NULL ); xt_add_attr( received, "xmlns", XMLNS_RECEIPTS ); xt_add_attr( received, "id", id ); receipt = jabber_make_packet( "message", NULL, from, received ); jabber_write_packet( ic, receipt ); xt_free_node( receipt ); } bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ); if( type && strcmp( type, "error" ) == 0 ) { /* Handle type=error packet. */ } else if( type && from && strcmp( type, "groupchat" ) == 0 ) { jabber_chat_pkt_message( ic, bud, node ); } else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */ { GString *fullmsg = g_string_new( "" ); for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next ) { char *ns = xt_find_attr( c, "xmlns" ); struct xt_node *inv; if( ns && strcmp( ns, XMLNS_MUC_USER ) == 0 && ( inv = xt_find_node( c->children, "invite" ) ) ) { /* This is an invitation. Set some vars which will be passed to imcb_chat_invite() below. */ room = from; if( ( from = xt_find_attr( inv, "from" ) ) == NULL ) from = room; if( ( inv = xt_find_node( inv->children, "reason" ) ) && inv->text_len > 0 ) reason = inv->text; } } if( ( s = strchr( from, '/' ) ) ) { if( bud ) { bud->last_msg = time( NULL ); from = bud->ext_jid ? bud->ext_jid : bud->bare_jid; } else *s = 0; /* We need to generate a bare JID now. */ } if( type && strcmp( type, "headline" ) == 0 ) { if( ( c = xt_find_node( node->children, "subject" ) ) && c->text_len > 0 ) g_string_append_printf( fullmsg, "Headline: %s\n", c->text ); /* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */ for( c = node->children; c; c = c->next ) { struct xt_node *url; if( ( url = xt_find_node( c->children, "url" ) ) && url->text_len > 0 ) g_string_append_printf( fullmsg, "URL: %s\n", url->text ); } } else if( ( c = xt_find_node( node->children, "subject" ) ) && c->text_len > 0 && ( !bud || !( bud->flags & JBFLAG_HIDE_SUBJECT ) ) ) { g_string_append_printf( fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text ); if( bud ) bud->flags |= JBFLAG_HIDE_SUBJECT; } else if( bud && !c ) { /* Yeah, possibly we're hiding changes to this field now. But nobody uses this for anything useful anyway, except GMail when people reply to an e-mail via chat, repeating the same subject all the time. I don't want to have to remember full subject strings for everyone. */ bud->flags &= ~JBFLAG_HIDE_SUBJECT; } if( body && body->text_len > 0 ) /* Could be just a typing notification. */ fullmsg = g_string_append( fullmsg, body->text ); if( fullmsg->len > 0 ) imcb_buddy_msg( ic, from, fullmsg->str, 0, jabber_get_timestamp( node ) ); if( room ) imcb_chat_invite( ic, room, from, reason ); g_string_free( fullmsg, TRUE ); /* Handling of incoming typing notifications. */ if( bud == NULL ) { /* Can't handle these for unknown buddies. */ } else if( xt_find_node( node->children, "composing" ) ) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing( ic, from, OPT_TYPING ); } /* No need to send a "stopped typing" signal when there's a message. */ else if( xt_find_node( node->children, "active" ) && ( body == NULL ) ) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing( ic, from, 0 ); } else if( xt_find_node( node->children, "paused" ) ) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing( ic, from, OPT_THINKING ); } if( s ) *s = '/'; /* And convert it back to a full JID. */ } return XT_HANDLED; }
xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ) { struct im_connection *ic = data; char *from = xt_find_attr( node, "from" ); char *type = xt_find_attr( node, "type" ); /* NULL should mean the person is online. */ struct xt_node *c, *cap; struct jabber_buddy *bud, *send_presence = NULL; int is_chat = 0; char *s; if( !from ) return XT_HANDLED; if( ( s = strchr( from, '/' ) ) ) { *s = 0; if( jabber_chat_by_jid( ic, from ) ) is_chat = 1; *s = '/'; } if( type == NULL ) { if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) ) { if( set_getbool( &ic->irc->set, "debug" ) ) imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from ); return XT_HANDLED; } g_free( bud->away_message ); if( ( c = xt_find_node( node->children, "status" ) ) && c->text_len > 0 ) bud->away_message = g_strdup( c->text ); else bud->away_message = NULL; if( ( c = xt_find_node( node->children, "show" ) ) && c->text_len > 0 ) { bud->away_state = (void*) jabber_away_state_by_code( c->text ); } else { bud->away_state = NULL; /* Let's only set last_act if there's *no* away state, since it could be some auto-away thingy. */ bud->last_act = time( NULL ); } if( ( c = xt_find_node( node->children, "priority" ) ) && c->text_len > 0 ) bud->priority = atoi( c->text ); else bud->priority = 0; if( bud && ( cap = xt_find_node( node->children, "c" ) ) && ( s = xt_find_attr( cap, "xmlns" ) ) && strcmp( s, XMLNS_CAPS ) == 0 ) { /* This <presence> stanza includes an XEP-0115 capabilities part. Not too interesting, but we can see if it has an ext= attribute. */ s = xt_find_attr( cap, "ext" ); if( s && ( strstr( s, "cstates" ) || strstr( s, "chatstate" ) ) ) bud->flags |= JBFLAG_DOES_XEP85; /* This field can contain more information like xhtml support, but we don't support that ourselves. Officially the ext= tag was deprecated, but enough clients do send it. (I'm aware that this is not the right way to use this field.) See for an explanation of ext=: http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/ } if( is_chat ) jabber_chat_pkt_presence( ic, bud, node ); else send_presence = jabber_buddy_by_jid( ic, bud->bare_jid, 0 ); } else if( strcmp( type, "unavailable" ) == 0 ) { if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { if( set_getbool( &ic->irc->set, "debug" ) ) imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from ); return XT_HANDLED; } /* Handle this before we delete the JID. */ if( is_chat ) { jabber_chat_pkt_presence( ic, bud, node ); } if( strchr( from, '/' ) == NULL ) /* Sometimes servers send a type="unavailable" from a bare JID, which should mean that suddenly all resources for this JID disappeared. */ jabber_buddy_remove_bare( ic, from ); else jabber_buddy_remove( ic, from ); if( is_chat ) { /* Nothing else to do for now? */ } else if( ( s = strchr( from, '/' ) ) ) { *s = 0; /* If another resource is still available, send its presence information. */ if( ( send_presence = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { /* Otherwise, count him/her as offline now. */ imcb_buddy_status( ic, from, 0, NULL, NULL ); } *s = '/'; } else { imcb_buddy_status( ic, from, 0, NULL, NULL ); } } else if( strcmp( type, "subscribe" ) == 0 ) { jabber_buddy_ask( ic, from ); } else if( strcmp( type, "subscribed" ) == 0 ) { /* Not sure about this one, actually... */ imcb_log( ic, "%s just accepted your authorization request", from ); } else if( strcmp( type, "unsubscribe" ) == 0 || strcmp( type, "unsubscribed" ) == 0 ) { /* Do nothing here. Plenty of control freaks or over-curious souls get excited when they can see who still has them in their buddy list and who finally removed them. Somehow I got the impression that those are the people who get removed from many buddy lists for "some" reason... If you're one of those people, this is your chance to write your first line of code in C... */ } else if( strcmp( type, "error" ) == 0 ) { return jabber_cache_handle_packet( ic, node ); /* struct jabber_error *err; if( ( c = xt_find_node( node->children, "error" ) ) ) { err = jabber_error_parse( c, XMLNS_STANZA_ERROR ); imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name, err->code, err->text ? ": " : "", err->text ? err->text : "" ); jabber_error_free( err ); } */ } if( send_presence ) { int is_away = 0; if( send_presence->away_state && !( *send_presence->away_state->code == 0 || strcmp( send_presence->away_state->code, "chat" ) == 0 ) ) is_away = OPT_AWAY; imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away, ( is_away && send_presence->away_state ) ? send_presence->away_state->full_name : NULL, send_presence->away_message ); } return XT_HANDLED; }
/* * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info */ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *c; struct jabber_data *jd = ic->proto_data; char *xmlns, *from; if( !( c = xt_find_node( node->children, "query" ) ) || !( from = xt_find_attr( node, "from" ) ) || !( xmlns = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; } jd->have_streamhosts++; if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 ) { char *itemjid; /* answer from server */ c = c->children; while( ( c = xt_find_node( c, "item" ) ) ) { itemjid = xt_find_attr( c, "jid" ); if( itemjid ) jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); c = c->next; } } else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; /* answer from potential proxy */ c = c->children; while( ( c = xt_find_node( c, "identity" ) ) ) { category = xt_find_attr( c, "category" ); type = xt_find_attr( c, "type" ); if( type && ( strcmp( type, "bytestreams" ) == 0 ) && category && ( strcmp( category, "proxy" ) == 0 ) ) jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS ); c = c->next; } } else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 ) { char *host, *jid, *port_s; int port; /* answer from proxy */ if( ( c = xt_find_node( c->children, "streamhost" ) ) && ( host = xt_find_attr( c, "host" ) ) && ( port_s = xt_find_attr( c, "port" ) ) && ( sscanf( port_s, "%d", &port ) == 1 ) && ( jid = xt_find_attr( c, "jid" ) ) ) { jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); sh->jid = g_strdup( jid ); sh->host = g_strdup( host ); g_snprintf( sh->port, sizeof( sh->port ), "%u", port ); imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); jd->streamhosts = g_slist_append( jd->streamhosts, sh ); } } if( jd->have_streamhosts == 0 ) jd->have_streamhosts++; return XT_HANDLED; }
/* * First function that gets called when a file transfer request comes in. * A lot to parse. * * We choose a stream type from the options given by the initiator. * Then we wait for imcb to call the accept or cancel callbacks. */ int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) { struct xt_node *c, *d, *reply; char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; struct jabber_buddy *bud; int requestok = FALSE; char *name, *cmp; size_t size; struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; file_transfer_t *ft; /* All this means we expect something like this: ( I think ) * <iq from=... to=... id=...> * <si id=id xmlns=si profile=ft> * <file xmlns=ft/> * <feature xmlns=feature> * <x xmlns=xdata type=submit> * <field var=stream-method> * */ if (!(ini_jid = xt_find_attr(node, "from")) || !(tgt_jid = xt_find_attr(node, "to")) || !(iq_id = xt_find_attr(node, "id")) || !(sid = xt_find_attr(sinode, "id")) || !(cmp = xt_find_attr(sinode, "profile")) || !(0 == strcmp(cmp, XMLNS_FILETRANSFER)) || !(d = xt_find_node(sinode->children, "file")) || !(cmp = xt_find_attr(d, "xmlns")) || !(0 == strcmp(cmp, XMLNS_FILETRANSFER)) || !(name = xt_find_attr(d, "name")) || !(size_s = xt_find_attr(d, "size")) || !(1 == sscanf(size_s, "%zd", &size)) || !(d = xt_find_node(sinode->children, "feature")) || !(cmp = xt_find_attr(d, "xmlns")) || !(0 == strcmp(cmp, XMLNS_FEATURE)) || !(d = xt_find_node(d->children, "x")) || !(cmp = xt_find_attr(d, "xmlns")) || !(0 == strcmp(cmp, XMLNS_XDATA)) || !(cmp = xt_find_attr(d, "type")) || !(0 == strcmp(cmp, "form")) || !(d = xt_find_node(d->children, "field")) || !(cmp = xt_find_attr(d, "var")) || !(0 == strcmp(cmp, "stream-method"))) { imcb_log(ic, "WARNING: Received incomplete Stream Initiation request"); } else { /* Check if we support one of the options */ c = d->children; while ((c = xt_find_node(c, "option"))) { if ((d = xt_find_node(c->children, "value")) && (d->text != NULL) && (strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) { requestok = TRUE; break; } else { c = c->next; } } if (!requestok) { imcb_log(ic, "WARNING: Unsupported file transfer request from %s", ini_jid); } } if (requestok) { /* Figure out who the transfer should come from... */ ext_jid = ini_jid; if ((s = strchr(ini_jid, '/'))) { if ((bud = jabber_buddy_by_jid(ic, ini_jid, GET_BUDDY_EXACT))) { bud->last_msg = time(NULL); ext_jid = bud->ext_jid ? : bud->bare_jid; } else { *s = 0; /* We need to generate a bare JID now. */ } }
static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_action action) { struct xml_parsedata xd[1]; char *fn, buf[2048]; int fd, st; struct xt_parser *xp = NULL; struct xt_node *node; storage_status_t ret = STORAGE_OTHER_ERROR; xd->irc = irc; strncpy(xd->given_nick, my_nick, MAX_NICK_LENGTH); xd->given_nick[MAX_NICK_LENGTH] = '\0'; nick_lc(NULL, xd->given_nick); xd->given_pass = (char *) password; fn = g_strconcat(global.conf->configdir, xd->given_nick, ".xml", NULL); if ((fd = open(fn, O_RDONLY)) < 0) { if (errno == ENOENT) { ret = STORAGE_NO_SUCH_USER; } else { irc_rootmsg(irc, "Error loading user config: %s", g_strerror(errno)); } goto error; } xp = xt_new(handlers, xd); while ((st = read(fd, buf, sizeof(buf))) > 0) { st = xt_feed(xp, buf, st); if (st != 1) { break; } } close(fd); if (st != 0) { goto error; } node = xp->root; if (node == NULL || node->next != NULL || strcmp(node->name, "user") != 0) { goto error; } if (action == XML_PASS_CHECK) { char *nick = xt_find_attr(node, "nick"); char *pass = xt_find_attr(node, "password"); char *backend = xt_find_attr(node, "auth_backend"); if (!nick || !(pass || backend)) { goto error; } if (backend) { g_free(xd->irc->auth_backend); xd->irc->auth_backend = g_strdup(backend); ret = STORAGE_CHECK_BACKEND; } else if ((st = md5_verify_password(xd->given_pass, pass)) != 0) { ret = STORAGE_INVALID_PASSWORD; } else { ret = STORAGE_OK; } goto error; } if (xt_handle(xp, NULL, 1) == XT_HANDLED) { ret = STORAGE_OK; } handle_settings(node, &xd->irc->b->set); error: xt_free(xp); g_free(fn); return ret; }
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 xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */ GString *reply; char *s; if( ( s = xt_find_attr( node, "type" ) ) == NULL || strcmp( s, "result" ) != 0 || ( vc = xt_find_node( node->children, "vCard" ) ) == NULL ) { s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */ imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" ); return XT_HANDLED; } s = xt_find_attr( orig, "to" ); reply = g_string_new( "vCard information for " ); reply = g_string_append( reply, s ? s : "(NULL)" ); reply = g_string_append( reply, ":\n" ); /* I hate this format, I really do... */ if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len ) g_string_append_printf( reply, "Name: %s\n", c->text ); if( ( c = xt_find_node( vc->children, "N" ) ) && c->children ) { reply = g_string_append( reply, "Full name:" ); if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len ) g_string_append_printf( reply, " %s", sc->text ); if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len ) g_string_append_printf( reply, " %s", sc->text ); if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len ) g_string_append_printf( reply, " %s", sc->text ); if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len ) g_string_append_printf( reply, " %s", sc->text ); if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len ) g_string_append_printf( reply, " %s", sc->text ); reply = g_string_append_c( reply, '\n' ); } if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len ) g_string_append_printf( reply, "Nickname: %s\n", c->text ); if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len ) g_string_append_printf( reply, "Date of birth: %s\n", c->text ); /* Slightly alternative use of for... ;-) */ for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next ) { if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 ) continue; if( xt_find_node( c->children, "HOME" ) ) s = "Home"; else if( xt_find_node( c->children, "WORK" ) ) s = "Work"; else s = "Misc."; g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text ); } if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len ) g_string_append_printf( reply, "Homepage: %s\n", c->text ); /* Slightly alternative use of for... ;-) */ for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next ) { if( xt_find_node( c->children, "HOME" ) ) s = "Home"; else if( xt_find_node( c->children, "WORK" ) ) s = "Work"; else s = "Misc."; g_string_append_printf( reply, "%s address: ", s ); if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len ) g_string_append_printf( reply, "%s ", sc->text ); if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len ) g_string_append_printf( reply, "%s, ", sc->text ); if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len ) g_string_append_printf( reply, "%s, ", sc->text ); if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len ) g_string_append_printf( reply, "%s, ", sc->text ); if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len ) g_string_append_printf( reply, "%s, ", sc->text ); if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len ) g_string_append_printf( reply, "%s", sc->text ); if( reply->str[reply->len-2] == ',' ) reply = g_string_truncate( reply, reply->len-2 ); reply = g_string_append_c( reply, '\n' ); } for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next ) { if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 ) continue; if( xt_find_node( c->children, "HOME" ) ) s = "Home"; else if( xt_find_node( c->children, "WORK" ) ) s = "Work"; else s = "Misc."; g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text ); } if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len ) g_string_append_printf( reply, "Other information:\n%s", c->text ); /* *sigh* */ imcb_log( ic, "%s", reply->str ); g_string_free( reply, TRUE ); return XT_HANDLED; }
static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent) { struct im_connection *ic = data; struct jabber_data *jd = ic->proto_data; char *from = xt_find_attr(node, carbons_sent ? "to" : "from"); char *type = xt_find_attr(node, "type"); char *id = xt_find_attr(node, "id"); struct xt_node *body = xt_find_node(node->children, "body"), *c; struct xt_node *request = xt_find_node(node->children, "request"); struct jabber_buddy *bud = NULL; char *s, *room = NULL, *reason = NULL; if (!from) { return XT_HANDLED; /* Consider this packet corrupted. */ } /* try to detect hipchat's own version of self-messages */ if (jd->flags & JFLAG_HIPCHAT) { struct xt_node *c; if ((c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY)) && (s = xt_find_attr(c, "from_jid")) && jabber_compare_jid(s, jd->me)) { carbons_sent = TRUE; } } if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) { /* Send a message receipt (XEP-0184), looking like this: * <message from='...' id='...' to='...'> * <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/> * </message> * * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors. */ struct xt_node *received, *receipt; received = xt_new_node("received", NULL, NULL); xt_add_attr(received, "xmlns", XMLNS_RECEIPTS); xt_add_attr(received, "id", id); receipt = jabber_make_packet("message", NULL, from, received); jabber_write_packet(ic, receipt); xt_free_node(receipt); } bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT); if (type && strcmp(type, "error") == 0) { /* Handle type=error packet. */ } else if (type && from && strcmp(type, "groupchat") == 0) { jabber_chat_pkt_message(ic, bud, node); } else { /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */ GString *fullmsg = g_string_new(""); for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) { char *ns = xt_find_attr(c, "xmlns"); struct xt_node *inv; if (ns && strcmp(ns, XMLNS_MUC_USER) == 0 && (inv = xt_find_node(c->children, "invite"))) { /* This is an invitation. Set some vars which will be passed to imcb_chat_invite() below. */ room = from; if ((from = xt_find_attr(inv, "from")) == NULL) { from = room; } if ((inv = xt_find_node(inv->children, "reason")) && inv->text_len > 0) { reason = inv->text; } } } if ((s = strchr(from, '/'))) { if (bud) { bud->last_msg = time(NULL); from = bud->ext_jid ? bud->ext_jid : bud->bare_jid; } else { *s = 0; /* We need to generate a bare JID now. */ } } if (type && strcmp(type, "headline") == 0) { if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0) { g_string_append_printf(fullmsg, "Headline: %s\n", c->text); } /* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */ for (c = node->children; c; c = c->next) { struct xt_node *url; if ((url = xt_find_node(c->children, "url")) && url->text_len > 0) { g_string_append_printf(fullmsg, "URL: %s\n", url->text); } } } else if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0 && (!bud || !(bud->flags & JBFLAG_HIDE_SUBJECT))) { g_string_append_printf(fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text); if (bud) { bud->flags |= JBFLAG_HIDE_SUBJECT; } } else if (bud && !c) { /* Yeah, possibly we're hiding changes to this field now. But nobody uses this for anything useful anyway, except GMail when people reply to an e-mail via chat, repeating the same subject all the time. I don't want to have to remember full subject strings for everyone. */ bud->flags &= ~JBFLAG_HIDE_SUBJECT; } if (body && body->text_len > 0) { /* Could be just a typing notification. */ fullmsg = g_string_append(fullmsg, body->text); } if (fullmsg->len > 0) { imcb_buddy_msg(ic, from, fullmsg->str, carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node)); } if (room) { imcb_chat_invite(ic, room, from, reason); } g_string_free(fullmsg, TRUE); /* Handling of incoming typing notifications. */ if (bud == NULL || carbons_sent) { /* Can't handle these for unknown buddies. And ignore them if it's just carbons */ } else if (xt_find_node(node->children, "composing")) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing(ic, from, OPT_TYPING); } else if (xt_find_node(node->children, "active")) { bud->flags |= JBFLAG_DOES_XEP85; /* No need to send a "stopped typing" signal when there's a message. */ if (body == NULL) { imcb_buddy_typing(ic, from, 0); } } else if (xt_find_node(node->children, "paused")) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing(ic, from, OPT_THINKING); } if (s) { *s = '/'; /* And convert it back to a full JID. */ } } return XT_HANDLED; }
/* Not really the same syntax as the normal pkt_ functions, but this isn't called by the xmltree parser directly and this way I can add some extra parameters so we won't have to repeat too many things done by the caller already. */ void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node) { struct groupchat *chat; struct xt_node *c; char *type = xt_find_attr(node, "type"); struct jabber_data *jd = ic->proto_data; struct jabber_chat *jc; char *s; if ((chat = jabber_chat_by_jid(ic, bud->bare_jid)) == NULL) { /* How could this happen?? We could do kill( self, 11 ) now or just wait for the OS to do it. :-) */ return; } jc = chat->data; if (type == NULL && !(bud->flags & JBFLAG_IS_CHATROOM)) { bud->flags |= JBFLAG_IS_CHATROOM; /* If this one wasn't set yet, this buddy just joined the chat. Slightly hackish way of finding out eh? ;-) */ /* This is pretty messy... Here it sets ext_jid to the real JID of the participant. Works for non-anonymized channels. Might break if someone joins a chat twice, though. */ for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) { if ((s = xt_find_attr(c, "xmlns")) && (strcmp(s, XMLNS_MUC_USER) == 0)) { struct xt_node *item; item = xt_find_node(c->children, "item"); if ((s = xt_find_attr(item, "jid"))) { /* Yay, found what we need. :-) */ bud->ext_jid = jabber_normalize(s); break; } } } /* Make up some other handle, if necessary. */ if (bud->ext_jid == NULL) { if (bud == jc->me) { bud->ext_jid = g_strdup(jd->me); } else { int i; /* Don't want the nick to be at the end, so let's think of some slightly different notation to use for anonymous groupchat participants in BitlBee. */ bud->ext_jid = g_strdup_printf("%s=%s", bud->resource, bud->bare_jid); /* And strip any unwanted characters. */ for (i = 0; bud->resource[i]; i++) { if (bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@') { bud->ext_jid[i] = '_'; } } /* Some program-specific restrictions. */ imcb_clean_handle(ic, bud->ext_jid); } bud->flags |= JBFLAG_IS_ANONYMOUS; } if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) { /* If JIDs are anonymized, add them to the local list for the duration of this chat. */ imcb_add_buddy(ic, bud->ext_jid, NULL); imcb_buddy_nick_hint(ic, bud->ext_jid, bud->resource); } if (bud == jc->me && jc->invite != NULL) { char *msg = g_strdup_printf("Please join me in room %s", jc->name); jabber_chat_invite(chat, jc->invite, msg); g_free(jc->invite); g_free(msg); jc->invite = NULL; } s = strchr(bud->ext_jid, '/'); if (s) { *s = 0; /* Should NEVER be NULL, but who knows... */ } imcb_chat_add_buddy(chat, bud->ext_jid); if (s) { *s = '/'; } } else if (type) { /* type can only be NULL or "unavailable" in this function */ if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) { char *reason = NULL; char *status = NULL; char *status_text = NULL; if ((c = xt_find_node_by_attr(node->children, "x", "xmlns", XMLNS_MUC_USER))) { struct xt_node *c2 = c->children; while ((c2 = xt_find_node(c2, "status"))) { char *code = xt_find_attr(c2, "code"); if (g_strcmp0(code, "301") == 0) { status = "Banned"; break; } else if (g_strcmp0(code, "303") == 0) { /* This could be handled in a cleverer way, * but let's just show a literal part/join for now */ status = "Changing nicks"; break; } else if (g_strcmp0(code, "307") == 0) { status = "Kicked"; break; } c2 = c2->next; } /* Sometimes the status message is in presence/x/item/reason */ if ((c2 = xt_find_path(c, "item/reason")) && c2->text && c2->text_len) { status_text = c2->text; } } /* Sometimes the status message is right inside <presence> */ if ((c = xt_find_node(node->children, "status")) && c->text && c->text_len) { status_text = c->text; } if (status_text && status) { reason = g_strdup_printf("%s: %s", status, status_text); } else { reason = g_strdup(status_text ? : status); } s = strchr(bud->ext_jid, '/'); if (s) { *s = 0; } imcb_chat_remove_buddy(chat, bud->ext_jid, reason); if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) { imcb_remove_buddy(ic, bud->ext_jid, reason); } if (s) { *s = '/'; } g_free(reason); } if (bud == jc->me) { jabber_chat_free(chat); } }