static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); struct msn_buddy_data *bd = bu ? bu->data : NULL; struct msn_switchboard *sb; #ifdef DEBUG if( strcmp( who, "raw" ) == 0 ) { msn_ns_write( ic, -1, "%s\r\n", message ); } else #endif if( bd && bd->flags & MSN_BUDDY_FED ) { msn_ns_sendmessage( ic, bu, message ); } else if( ( sb = msn_sb_by_handle( ic, who ) ) ) { return( msn_sb_sendmessage( sb, message ) ); } else { struct msn_message *m; /* Create a message. We have to arrange a usable switchboard, and send the message later. */ m = g_new0( struct msn_message, 1 ); m->who = g_strdup( who ); m->text = g_strdup( message ); return msn_sb_write_msg( ic, m ); } return( 0 ); }
int msn_ns_set_display_name( struct im_connection *ic, const char *value ) { struct msn_data *md = ic->proto_data; char fn[strlen(value)*3+1]; strcpy( fn, value ); http_encode( fn ); /* Note: We don't actually know if the server accepted the new name, and won't give proper feedback yet if it doesn't. */ return msn_ns_write( ic, -1, "PRP %d MFN %s\r\n", ++md->trId, fn ); }
static void msn_set_away( struct im_connection *ic, char *state, char *message ) { char *uux; struct msn_data *md = ic->proto_data; if( state == NULL ) md->away_state = msn_away_state_list; else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL ) md->away_state = msn_away_state_list + 1; if( !msn_ns_write( ic, -1, "CHG %d %s %d:%02d\r\n", ++md->trId, md->away_state->code, MSN_CAP1, MSN_CAP2 ) ) return; uux = g_markup_printf_escaped( "<EndpointData><Capabilities>%d:%02d" "</Capabilities></EndpointData>", MSN_CAP1, MSN_CAP2 ); msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); g_free( uux ); uux = g_markup_printf_escaped( "<PrivateEndpointData><EpName>%s</EpName>" "<Idle>%s</Idle><ClientType>%d</ClientType>" "<State>%s</State></PrivateEndpointData>", md->uuid, strcmp( md->away_state->code, "IDL" ) ? "false" : "true", 1, /* ? */ md->away_state->code ); msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); g_free( uux ); uux = g_markup_printf_escaped( "<Data><DDP></DDP><PSM>%s</PSM>" "<CurrentMedia></CurrentMedia>" "<MachineGuid>%s</MachineGuid></Data>", message ? message : "", md->uuid ); msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); g_free( uux ); }
static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) { struct msn_handler_data *handler = data; struct im_connection *ic = handler->data; struct msn_data *md; if( !g_slist_find( msn_connections, ic ) ) return FALSE; md = ic->proto_data; if( source == -1 ) { imcb_error( ic, "Could not connect to server" ); imc_logout( ic, TRUE ); return FALSE; } g_free( handler->rxq ); handler->rxlen = 0; handler->rxq = g_new0( char, 1 ); if( md->uuid == NULL ) { struct utsname name; sha1_state_t sha[1]; /* UUID == SHA1("BitlBee" + my hostname + MSN username) */ sha1_init( sha ); sha1_append( sha, (void*) "BitlBee", 7 ); if( uname( &name ) == 0 ) { sha1_append( sha, (void*) name.nodename, strlen( name.nodename ) ); } sha1_append( sha, (void*) ic->acc->user, strlen( ic->acc->user ) ); md->uuid = sha1_random_uuid( sha ); memcpy( md->uuid, "b171be3e", 8 ); /* :-P */ } if( msn_ns_write( ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ) ) { handler->inpa = b_input_add( handler->fd, B_EV_IO_READ, msn_ns_callback, handler ); imcb_log( ic, "Connected to server, waiting for reply" ); } return FALSE; }
int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group ) { struct msn_data *md = ic->proto_data; char groupid[8]; bee_user_t *bu; struct msn_buddy_data *bd; char *adl; *groupid = '\0'; #if 0 if( group ) { int i; for( i = 0; i < md->groupcount; i ++ ) if( g_strcasecmp( md->grouplist[i], group ) == 0 ) { g_snprintf( groupid, sizeof( groupid ), " %d", i ); break; } } #endif if( !( bu = bee_user_by_handle( ic->bee, ic, who ) ) || !( bd = bu->data ) || !( bd->flags & list ) ) return 1; bd->flags &= ~list; if( list == MSN_BUDDY_FL ) msn_soap_ab_contact_del( ic, bu ); else msn_soap_memlist_edit( ic, who, FALSE, list ); if( ( adl = adlrml_entry( who, list ) ) ) { int st = msn_ns_write( ic, -1, "RML %d %zd\r\n%s", ++md->trId, strlen( adl ), adl ); g_free( adl ); return st; } return 1; }
int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) { struct msn_data *md = ic->proto_data; struct msn_switchboard *sb; /* FIXME: *CHECK* the reliability of using spare sb's! */ if( ( sb = msn_sb_spare( ic ) ) ) { debug( "Trying to use a spare switchboard to message %s", m->who ); sb->who = g_strdup( m->who ); if( msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, m->who ) ) { /* He/She should join the switchboard soon, let's queue the message. */ sb->msgq = g_slist_append( sb->msgq, m ); return( 1 ); } } debug( "Creating a new switchboard to message %s", m->who ); /* If we reach this line, there was no spare switchboard, so let's make one. */ if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) ) { g_free( m->who ); g_free( m->text ); g_free( m ); return( 0 ); } /* And queue the message to md. We'll pick it up when the switchboard comes up. */ md->msgq = g_slist_append( md->msgq, m ); /* FIXME: If the switchboard creation fails, the message will not be sent. */ return( 1 ); }
int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname, const char *group ) { struct msn_data *md = ic->proto_data; char groupid[8]; bee_user_t *bu; struct msn_buddy_data *bd; char *adl; *groupid = '\0'; #if 0 if( group ) { int i; for( i = 0; i < md->groupcount; i ++ ) if( g_strcasecmp( md->grouplist[i], group ) == 0 ) { g_snprintf( groupid, sizeof( groupid ), " %d", i ); break; } if( *groupid == '\0' ) { /* Have to create this group, it doesn't exist yet. */ struct msn_groupadd *ga; GSList *l; for( l = md->grpq; l; l = l->next ) { ga = l->data; if( g_strcasecmp( ga->group, group ) == 0 ) break; } ga = g_new0( struct msn_groupadd, 1 ); ga->who = g_strdup( who ); ga->group = g_strdup( group ); md->grpq = g_slist_prepend( md->grpq, ga ); if( l == NULL ) { char groupname[strlen(group)+1]; strcpy( groupname, group ); http_encode( groupname ); g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); return msn_write( ic, buf, strlen( buf ) ); } else { /* This can happen if the user's doing lots of adds to a new group at once; we're still waiting for the server to confirm group creation. */ return 1; } } } #endif if( !( ( bu = bee_user_by_handle( ic->bee, ic, who ) ) || ( bu = bee_user_new( ic->bee, ic, who, 0 ) ) ) || !( bd = bu->data ) || bd->flags & list ) return 1; bd->flags |= list; if( list == MSN_BUDDY_FL ) msn_soap_ab_contact_add( ic, bu ); else msn_soap_memlist_edit( ic, who, TRUE, list ); if( ( adl = adlrml_entry( who, list ) ) ) { int st = msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", ++md->trId, strlen( adl ), adl ); g_free( adl ); return st; } return 1; }
static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts ) { struct im_connection *ic = handler->data; struct msn_data *md = ic->proto_data; if( num_parts == 0 ) { /* Hrrm... Empty command...? Ignore? */ return( 1 ); } if( strcmp( cmd[0], "VER" ) == 0 ) { if( cmd[2] && strncmp( cmd[2], MSNP_VER, 5 ) != 0 ) { imcb_error( ic, "Unsupported protocol" ); imc_logout( ic, FALSE ); return( 0 ); } return( msn_ns_write( ic, handler->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", ++md->trId, ic->acc->user ) ); } else if( strcmp( cmd[0], "CVR" ) == 0 ) { /* We don't give a damn about the information we just received */ return msn_ns_write( ic, handler->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user ); } else if( strcmp( cmd[0], "XFR" ) == 0 ) { char *server; int port; if( num_parts >= 6 && strcmp( cmd[2], "NS" ) == 0 ) { b_event_remove( handler->inpa ); handler->inpa = -1; server = strchr( cmd[3], ':' ); if( !server ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } *server = 0; port = atoi( server + 1 ); server = cmd[3]; imcb_log( ic, "Transferring to other server" ); return msn_ns_connect( ic, handler, server, port ); } else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 ) { struct msn_switchboard *sb; server = strchr( cmd[3], ':' ); if( !server ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } *server = 0; port = atoi( server + 1 ); server = cmd[3]; if( strcmp( cmd[4], "CKI" ) != 0 ) { imcb_error( ic, "Unknown authentication method for switchboard" ); imc_logout( ic, TRUE ); return( 0 ); } debug( "Connecting to a new switchboard with key %s", cmd[5] ); if( ( sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW ) ) == NULL ) { /* Although this isn't strictly fatal for the NS connection, it's definitely something serious (we ran out of file descriptors?). */ imcb_error( ic, "Could not create new switchboard" ); imc_logout( ic, TRUE ); return( 0 ); } if( md->msgq ) { struct msn_message *m = md->msgq->data; GSList *l; sb->who = g_strdup( m->who ); /* Move all the messages to the first user in the message queue to the switchboard message queue. */ l = md->msgq; while( l ) { m = l->data; l = l->next; if( strcmp( m->who, sb->who ) == 0 ) { sb->msgq = g_slist_append( sb->msgq, m ); md->msgq = g_slist_remove( md->msgq, m ); } } } } else { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } } else if( strcmp( cmd[0], "USR" ) == 0 ) { if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) { g_free( md->pp_policy ); md->pp_policy = g_strdup( cmd[4] ); msn_soap_passport_sso_request( ic, cmd[5] ); } else if( strcmp( cmd[2], "OK" ) == 0 ) { /* If the number after the handle is 0, the e-mail address is unverified, which means we can't change the display name. */ if( cmd[4][0] == '0' ) md->flags |= MSN_EMAIL_UNVERIFIED; imcb_log( ic, "Authenticated, getting buddy list" ); msn_soap_memlist_request( ic ); } else { imcb_error( ic, "Unknown authentication type" ); imc_logout( ic, FALSE ); return( 0 ); } } else if( strcmp( cmd[0], "MSG" ) == 0 ) { if( num_parts < 4 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } handler->msglen = atoi( cmd[3] ); if( handler->msglen <= 0 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } } else if( strcmp( cmd[0], "BLP" ) == 0 ) { msn_ns_send_adl_start( ic ); return msn_ns_finish_login( ic ); } else if( strcmp( cmd[0], "ADL" ) == 0 ) { if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 ) { msn_ns_send_adl( ic ); return msn_ns_finish_login( ic ); } else if( num_parts >= 3 ) { handler->msglen = atoi( cmd[2] ); } } else if( strcmp( cmd[0], "PRP" ) == 0 ) { imcb_connected( ic ); } else if( strcmp( cmd[0], "CHL" ) == 0 ) { char *resp; int st; if( num_parts < 3 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } resp = msn_p11_challenge( cmd[2] ); st = msn_ns_write( ic, -1, "QRY %d %s %zd\r\n%s", ++md->trId, MSNP11_PROD_ID, strlen( resp ), resp ); g_free( resp ); return st; } else if( strcmp( cmd[0], "ILN" ) == 0 || strcmp( cmd[0], "NLN" ) == 0 ) { const struct msn_away_state *st; const char *handle; int cap = 0; if( num_parts < 6 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } /* ILN and NLN are more or less the same, except ILN has a trId at the start, and NLN has a capability field at the end. Does ILN still exist BTW? */ if( cmd[0][1] == 'I' ) cmd ++; else cap = atoi( cmd[4] ); handle = msn_normalize_handle( cmd[2] ); if( strcmp( handle, ic->acc->user ) == 0 ) return 1; /* That's me! */ http_decode( cmd[3] ); imcb_rename_buddy( ic, handle, cmd[3] ); st = msn_away_state_by_code( cmd[1] ); if( !st ) { /* FIXME: Warn/Bomb about unknown away state? */ st = msn_away_state_list + 1; } imcb_buddy_status( ic, handle, OPT_LOGGED_IN | ( st != msn_away_state_list ? OPT_AWAY : 0 ) | ( cap & 1 ? OPT_MOBILE : 0 ), st->name, NULL ); msn_sb_stop_keepalives( msn_sb_by_handle( ic, handle ) ); } else if( strcmp( cmd[0], "FLN" ) == 0 ) { const char *handle; if( cmd[1] == NULL ) return 1; handle = msn_normalize_handle( cmd[1] ); imcb_buddy_status( ic, handle, 0, NULL, NULL ); msn_sb_start_keepalives( msn_sb_by_handle( ic, handle ), TRUE ); } else if( strcmp( cmd[0], "RNG" ) == 0 ) { struct msn_switchboard *sb; char *server; int session, port; if( num_parts < 7 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } session = atoi( cmd[1] ); server = strchr( cmd[2], ':' ); if( !server ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } *server = 0; port = atoi( server + 1 ); server = cmd[2]; if( strcmp( cmd[3], "CKI" ) != 0 ) { imcb_error( ic, "Unknown authentication method for switchboard" ); imc_logout( ic, TRUE ); return( 0 ); } debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] ); if( ( sb = msn_sb_create( ic, server, port, cmd[4], session ) ) == NULL ) { /* Although this isn't strictly fatal for the NS connection, it's definitely something serious (we ran out of file descriptors?). */ imcb_error( ic, "Could not create new switchboard" ); imc_logout( ic, TRUE ); return( 0 ); } else { sb->who = g_strdup( msn_normalize_handle( cmd[5] ) ); } } else if( strcmp( cmd[0], "OUT" ) == 0 ) { int allow_reconnect = TRUE; if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 ) { imcb_error( ic, "Someone else logged in with your account" ); allow_reconnect = FALSE; } else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 ) { imcb_error( ic, "Terminating session because of server shutdown" ); } else { imcb_error( ic, "Session terminated by remote server (%s)", cmd[1] ? cmd[1] : "reason unknown)" ); } imc_logout( ic, allow_reconnect ); return( 0 ); } else if( strcmp( cmd[0], "IPG" ) == 0 ) { imcb_error( ic, "Received IPG command, we don't handle them yet." ); handler->msglen = atoi( cmd[1] ); if( handler->msglen <= 0 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } } #if 0 else if( strcmp( cmd[0], "ADG" ) == 0 ) { char *group = g_strdup( cmd[3] ); int groupnum, i; GSList *l, *next; http_decode( group ); if( sscanf( cmd[4], "%d", &groupnum ) == 1 ) { if( groupnum >= md->groupcount ) { md->grouplist = g_renew( char *, md->grouplist, groupnum + 1 ); for( i = md->groupcount; i <= groupnum; i ++ ) md->grouplist[i] = NULL; md->groupcount = groupnum + 1; } g_free( md->grouplist[groupnum] ); md->grouplist[groupnum] = group; }
static void msn_keepalive( struct im_connection *ic ) { msn_ns_write( ic, -1, "PNG\r\n" ); }