Changeset 5f40da7
- Timestamp:
- 2011-12-26T10:51:19Z (13 years ago)
- Branches:
- master
- Children:
- 199fea6
- Parents:
- 96f954d (diff), 644b808 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Files:
-
- 2 added
- 27 edited
Legend:
- Unmodified
- Added
- Removed
-
Makefile
r96f954d r5f40da7 27 27 subdirobjs = $(foreach dir,$(subdirs),$(dir)/$(dir).o) 28 28 29 all: $(OUTFILE) $(OTR_PI) $(SKYPE_PI) systemd 29 all: $(OUTFILE) $(OTR_PI) $(SKYPE_PI) doc systemd 30 ifdef SKYPE_PI 31 $(MAKE) -C protocols/skype doc 32 endif 33 34 doc: 30 35 $(MAKE) -C doc 31 ifdef SKYPE_PI32 $(MAKE) -C protocols/skype doc33 endif34 36 35 37 uninstall: uninstall-bin uninstall-doc … … 43 45 .PHONY: install install-bin install-etc install-doc install-plugins install-systemd \ 44 46 uninstall uninstall-bin uninstall-etc uninstall-doc \ 45 all clean distclean tar $(subdirs) 47 all clean distclean tar $(subdirs) doc 46 48 47 49 Makefile.settings: … … 189 191 @echo Hello World 190 192 193 # Check if we can load the helpfile. (This fails if some article is >1KB.) 194 # If print returns a NULL pointer, the file is unusable. 195 testhelp: doc 196 gdb --eval-command='b main' --eval-command='r' \ 197 --eval-command='print help_init(&global->helpfile, "doc/user-guide/help.txt")' \ 198 $(OUTFILE) < /dev/null 199 191 200 -include .depend/*.d 192 201 # DO NOT DELETE -
doc/user-guide/commands.xml
r96f954d r5f40da7 1126 1126 <description> 1127 1127 <para> 1128 This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory.1129 </para> 1130 1131 <para> 1132 With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will returna PIN code which you can give back to BitlBee to finish the process.1133 </para> 1134 1135 <para> 1136 The resulting access token will be saved permanently, so you have to do this only once. 1128 This enables OAuth authentication for an IM account; right now the Twitter (working for Twitter only) and Jabber (for Google Talk, Facebook and MSN Messenger) module support it. 1129 </para> 1130 1131 <para> 1132 With OAuth enabled, you shouldn't tell BitlBee your account password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with the service. If this succeeds, you will get a PIN code which you can give back to BitlBee to finish the process. 1133 </para> 1134 1135 <para> 1136 The resulting access token will be saved permanently, so you have to do this only once. If for any reason you want to/have to reauthenticate, you can use <emphasis>account set</emphasis> to reset the account password to something random. 1137 1137 </para> 1138 1138 </description> -
irc_im.c
r96f954d r5f40da7 442 442 { 443 443 irc_user_t *iu = data; 444 char *msg = g_string_free( iu->pastebuf, FALSE );444 char *msg; 445 445 GSList *l; 446 447 msg = g_string_free( iu->pastebuf, FALSE ); 448 iu->pastebuf = NULL; 449 iu->pastebuf_timer = 0; 446 450 447 451 for( l = irc_plugins; l; l = l->next ) … … 470 474 471 475 g_free( msg ); 472 iu->pastebuf = NULL;473 iu->pastebuf_timer = 0;474 476 475 477 return FALSE; -
lib/Makefile
r96f954d r5f40da7 13 13 14 14 # [SH] Program variables 15 objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o15 objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o 16 16 17 17 LFLAGS += -r -
lib/arc.c
r96f954d r5f40da7 200 200 { 201 201 *clear = g_strdup( "" ); 202 return 0;202 return -1; 203 203 } 204 204 -
lib/md5.c
r96f954d r5f40da7 24 24 #include <sys/types.h> 25 25 #include <string.h> /* for memcpy() */ 26 #include <stdio.h> 26 27 #include "md5.h" 27 28 … … 160 161 memcpy(digest, ctx->buf, 16); 161 162 memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ 163 } 164 165 void md5_finish_ascii(struct MD5Context *context, char *ascii) 166 { 167 md5_byte_t bin[16]; 168 int i; 169 170 md5_finish(context, bin); 171 for (i = 0; i < 16; i ++) 172 sprintf(ascii + i * 2, "%02x", bin[i]); 162 173 } 163 174 -
lib/md5.h
r96f954d r5f40da7 43 43 G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len); 44 44 G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]); 45 G_MODULE_EXPORT void md5_finish_ascii(struct MD5Context *context, char *ascii); 45 46 46 47 #endif -
lib/misc.c
r96f954d r5f40da7 729 729 return cmd; 730 730 } 731 732 char *get_rfc822_header( char *text, char *header, int len ) 733 { 734 int hlen = strlen( header ), i; 735 char *ret; 736 737 if( text == NULL ) 738 return NULL; 739 740 if( len == 0 ) 741 len = strlen( text ); 742 743 i = 0; 744 while( ( i + hlen ) < len ) 745 { 746 /* Maybe this is a bit over-commented, but I just hate this part... */ 747 if( g_strncasecmp( text + i, header, hlen ) == 0 ) 748 { 749 /* Skip to the (probable) end of the header */ 750 i += hlen; 751 752 /* Find the first non-[: \t] character */ 753 while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++; 754 755 /* Make sure we're still inside the string */ 756 if( i >= len ) return( NULL ); 757 758 /* Save the position */ 759 ret = text + i; 760 761 /* Search for the end of this line */ 762 while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++; 763 764 /* Make sure we're still inside the string */ 765 if( i >= len ) return( NULL ); 766 767 /* Copy the found data */ 768 return( g_strndup( ret, text + i - ret ) ); 769 } 770 771 /* This wasn't the header we were looking for, skip to the next line. */ 772 while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++; 773 while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++; 774 775 /* End of headers? */ 776 if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) || 777 ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 || 778 strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) ) 779 { 780 break; 781 } 782 } 783 784 return NULL; 785 } -
lib/misc.h
r96f954d r5f40da7 65 65 66 66 G_MODULE_EXPORT char *word_wrap( const char *msg, int line_len ); 67 68 67 G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); 69 70 68 G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); 71 72 69 G_MODULE_EXPORT char **split_command_parts( char *command ); 70 G_MODULE_EXPORT char *get_rfc822_header( char *text, char *header, int len ); 73 71 74 72 #endif -
lib/oauth.c
r96f954d r5f40da7 38 38 const char *params, struct oauth_info *oi ) 39 39 { 40 sha1_state_t sha1;41 40 uint8_t hash[sha1_hash_size]; 42 uint8_t key[HMAC_BLOCK_SIZE+1]; 41 GString *payload = g_string_new( "" ); 42 char *key; 43 43 char *s; 44 int i; 45 46 /* Create K. If our current key is >64 chars we have to hash it, 47 otherwise just pad. */ 48 memset( key, 0, HMAC_BLOCK_SIZE ); 49 i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 ); 50 if( i > HMAC_BLOCK_SIZE ) 51 { 52 sha1_init( &sha1 ); 53 sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); 54 sha1_append( &sha1, (uint8_t*) "&", 1 ); 55 if( oi->token_secret ) 56 sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) ); 57 sha1_finish( &sha1, key ); 58 } 59 else 60 { 61 g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", 62 oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" ); 63 } 64 65 /* Inner part: H(K XOR 0x36, text) */ 66 sha1_init( &sha1 ); 67 68 for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) 69 key[i] ^= 0x36; 70 sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); 71 72 /* OAuth: text = method&url¶ms, all http_encoded. */ 73 sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); 74 sha1_append( &sha1, (const uint8_t*) "&", 1 ); 44 45 key = g_strdup_printf( "%s&%s", oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" ); 46 47 g_string_append_printf( payload, "%s&", method ); 75 48 76 49 s = g_new0( char, strlen( url ) * 3 + 1 ); 77 50 strcpy( s, url ); 78 51 http_encode( s ); 79 sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); 80 sha1_append( &sha1, (const uint8_t*) "&", 1 ); 52 g_string_append_printf( payload, "%s&", s ); 81 53 g_free( s ); 82 54 … … 84 56 strcpy( s, params ); 85 57 http_encode( s ); 86 sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); 87 g_free( s ); 88 89 sha1_finish( &sha1, hash ); 90 91 /* Final result: H(K XOR 0x5C, inner stuff) */ 92 sha1_init( &sha1 ); 93 for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) 94 key[i] ^= 0x36 ^ 0x5c; 95 sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); 96 sha1_append( &sha1, hash, sha1_hash_size ); 97 sha1_finish( &sha1, hash ); 58 g_string_append( payload, s ); 59 g_free( s ); 60 61 sha1_hmac( key, 0, payload->str, 0, hash ); 62 63 g_free( key ); 64 g_string_free( payload, TRUE ); 98 65 99 66 /* base64_encode + HTTP escape it (both consumers … … 122 89 char *item; 123 90 91 if( !key || !value ) 92 return; 93 124 94 item = g_strdup_printf( "%s=%s", key, value ); 125 95 *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); … … 130 100 int key_len = strlen( key ); 131 101 GSList *l, *n; 102 103 if( params == NULL ) 104 return; 132 105 133 106 for( l = *params; l; l = n ) … … 155 128 GSList *l; 156 129 130 if( params == NULL ) 131 return NULL; 132 157 133 for( l = *params; l; l = l->next ) 158 134 { … … 165 141 } 166 142 167 staticvoid oauth_params_parse( GSList **params, char *in )143 void oauth_params_parse( GSList **params, char *in ) 168 144 { 169 145 char *amp, *eq, *s; … … 333 309 oauth_params_parse( ¶ms, req->reply_body ); 334 310 st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); 311 st->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); 335 312 oauth_params_free( ¶ms ); 336 313 } … … 362 339 oauth_params_parse( &st->params, req->reply_body ); 363 340 st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) ); 341 g_free( st->token_secret ); 364 342 st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) ); 365 343 } -
lib/oauth.h
r96f954d r5f40da7 92 92 93 93 /* For reading misc. data. */ 94 void oauth_params_add( GSList **params, const char *key, const char *value ); 95 void oauth_params_parse( GSList **params, char *in ); 96 void oauth_params_free( GSList **params ); 97 char *oauth_params_string( GSList *params ); 98 void oauth_params_set( GSList **params, const char *key, const char *value ); 94 99 const char *oauth_params_get( GSList **params, const char *key ); -
protocols/jabber/conference.c
r96f954d r5f40da7 211 211 struct xt_node *c; 212 212 char *type = xt_find_attr( node, "type" ); 213 struct jabber_data *jd = ic->proto_data; 213 214 struct jabber_chat *jc; 214 215 char *s; … … 252 253 if( bud == jc->me ) 253 254 { 254 bud->ext_jid = jabber_normalize( ic->acc->user);255 bud->ext_jid = g_strdup( jd->me ); 255 256 } 256 257 else -
protocols/jabber/iq.c
r96f954d r5f40da7 31 31 { 32 32 struct im_connection *ic = data; 33 struct jabber_data *jd = ic->proto_data; 33 34 struct xt_node *c, *reply = NULL; 34 35 char *type, *s; … … 170 171 was added to (or removed from) the buddy list. AFAIK they're 171 172 sent even if we added this buddy in our own session. */ 172 int bare_len = strlen( ic->acc->user);173 int bare_len = strlen( jd->me ); 173 174 174 175 if( ( s = xt_find_attr( node, "from" ) ) == NULL || 175 ( strncmp( s, ic->acc->user, bare_len ) == 0 &&176 ( strncmp( s, jd->me, bare_len ) == 0 && 176 177 ( s[bare_len] == 0 || s[bare_len] == '/' ) ) ) 177 178 { … … 343 344 { 344 345 c = xt_find_node( c->children, "jid" ); 345 if( c && c->text_len && ( s = strchr( c->text, '/' ) ) && 346 strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 ) 346 if( !c || !c->text ) 347 { 348 /* Server is crap, but this is no disaster. */ 349 } 350 else if( strncmp( jd->me, c->text, strlen( jd->me ) ) != 0 ) 351 { 352 s = strchr( c->text, '/' ); 353 if( s ) 354 *s = '\0'; 355 jabber_set_me( ic, c->text ); 356 imcb_log( ic, "Server claims your JID is `%s' instead of `%s'. " 357 "This mismatch may cause problems with groupchats " 358 "and possibly other things.", 359 c->text, ic->acc->user ); 360 if( s ) 361 *s = '/'; 362 } 363 else if( c && c->text_len && ( s = strchr( c->text, '/' ) ) && 364 strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 ) 347 365 imcb_log( ic, "Server changed session resource string to `%s'", s + 1 ); 348 366 } -
protocols/jabber/jabber.c
r96f954d r5f40da7 32 32 #include "bitlbee.h" 33 33 #include "jabber.h" 34 #include "oauth.h" 34 35 #include "md5.h" 35 36 … … 60 61 s = set_add( &acc->set, "activity_timeout", "600", set_eval_int, acc ); 61 62 63 s = set_add( &acc->set, "oauth", "false", set_eval_oauth, acc ); 64 62 65 g_snprintf( str, sizeof( str ), "%d", jabber_port_list[0] ); 63 66 s = set_add( &acc->set, "port", str, set_eval_int, acc ); … … 73 76 s = set_add( &acc->set, "resource_select", "activity", NULL, acc ); 74 77 78 s = set_add( &acc->set, "sasl", "true", set_eval_bool, acc ); 79 s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT; 80 75 81 s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); 76 82 s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK; … … 84 90 s = set_add( &acc->set, "tls_verify", "true", set_eval_bool, acc ); 85 91 s->flags |= ACC_SET_OFFLINE_ONLY; 86 87 s = set_add( &acc->set, "sasl", "true", set_eval_bool, acc );88 s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT;89 92 90 93 s = set_add( &acc->set, "user_agent", "BitlBee", NULL, acc ); … … 102 105 struct im_connection *ic = imcb_new( acc ); 103 106 struct jabber_data *jd = g_new0( struct jabber_data, 1 ); 104 struct ns_srv_reply **srvl = NULL, *srv = NULL; 105 char *connect_to, *s; 106 int i; 107 char *s; 107 108 108 109 /* For now this is needed in the _connected() handlers if using … … 114 115 ic->proto_data = jd; 115 116 116 jd->username = g_strdup( acc->user ); 117 jd->server = strchr( jd->username, '@' ); 117 jabber_set_me( ic, acc->user ); 118 118 119 119 jd->fd = jd->r_inpa = jd->w_inpa = -1; … … 125 125 return; 126 126 } 127 128 /* So don't think of free()ing jd->server.. :-) */129 *jd->server = 0;130 jd->server ++;131 127 132 128 if( ( s = strchr( jd->server, '/' ) ) ) … … 141 137 } 142 138 143 /* This code isn't really pretty. Backwards compatibility never is... */144 s = acc->server;145 while( s )146 {147 static int had_port = 0;148 149 if( strncmp( s, "ssl", 3 ) == 0 )150 {151 set_setstr( &acc->set, "ssl", "true" );152 153 /* Flush this part so that (if this was the first154 part of the server string) acc->server gets155 flushed. We don't want to have to do this another156 time. :-) */157 *s = 0;158 s ++;159 160 /* Only set this if the user didn't specify a custom161 port number already... */162 if( !had_port )163 set_setint( &acc->set, "port", 5223 );164 }165 else if( isdigit( *s ) )166 {167 int i;168 169 /* The first character is a digit. It could be an170 IP address though. Only accept this as a port#171 if there are only digits. */172 for( i = 0; isdigit( s[i] ); i ++ );173 174 /* If the first non-digit character is a colon or175 the end of the string, save the port number176 where it should be. */177 if( s[i] == ':' || s[i] == 0 )178 {179 sscanf( s, "%d", &i );180 set_setint( &acc->set, "port", i );181 182 /* See above. */183 *s = 0;184 s ++;185 }186 187 had_port = 1;188 }189 190 s = strchr( s, ':' );191 if( s )192 {193 *s = 0;194 s ++;195 }196 }197 198 139 jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free ); 199 140 jd->buddies = g_hash_table_new( g_str_hash, g_str_equal ); 141 142 if( set_getbool( &acc->set, "oauth" ) ) 143 { 144 GSList *p_in = NULL; 145 const char *tok; 146 147 jd->fd = jd->r_inpa = jd->w_inpa = -1; 148 149 if( strstr( jd->server, ".live.com" ) ) 150 jd->oauth2_service = &oauth2_service_mslive; 151 else if( strstr( jd->server, ".facebook.com" ) ) 152 jd->oauth2_service = &oauth2_service_facebook; 153 else 154 jd->oauth2_service = &oauth2_service_google; 155 156 oauth_params_parse( &p_in, ic->acc->pass ); 157 158 /* First see if we have a refresh token, in which case any 159 access token we *might* have has probably expired already 160 anyway. */ 161 if( ( tok = oauth_params_get( &p_in, "refresh_token" ) ) ) 162 { 163 sasl_oauth2_refresh( ic, tok ); 164 } 165 /* If we don't have a refresh token, let's hope the access 166 token is still usable. */ 167 else if( ( tok = oauth_params_get( &p_in, "access_token" ) ) ) 168 { 169 jd->oauth2_access_token = g_strdup( tok ); 170 jabber_connect( ic ); 171 } 172 /* If we don't have any, start the OAuth process now. Don't 173 even open an XMPP connection yet. */ 174 else 175 { 176 sasl_oauth2_init( ic ); 177 ic->flags |= OPT_SLOW_LOGIN; 178 } 179 180 oauth_params_free( &p_in ); 181 } 182 else 183 jabber_connect( ic ); 184 } 185 186 /* Separate this from jabber_login() so we can do OAuth first if necessary. 187 Putting this in io.c would probably be more correct. */ 188 void jabber_connect( struct im_connection *ic ) 189 { 190 account_t *acc = ic->acc; 191 struct jabber_data *jd = ic->proto_data; 192 int i; 193 char *connect_to; 194 struct ns_srv_reply **srvl = NULL, *srv = NULL; 200 195 201 196 /* Figure out the hostname to connect to. */ … … 322 317 xt_free( jd->xt ); 323 318 319 g_free( jd->oauth2_access_token ); 324 320 g_free( jd->away_message ); 325 321 g_free( jd->username ); 322 g_free( jd->me ); 326 323 g_free( jd ); 327 324 … … 339 336 if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) 340 337 return jabber_write( ic, message, strlen( message ) ); 338 339 if( g_strcasecmp( who, JABBER_OAUTH_HANDLE ) == 0 && 340 !( jd->flags & OPT_LOGGED_IN ) && jd->fd == -1 ) 341 { 342 if( sasl_oauth2_get_refresh_token( ic, message ) ) 343 { 344 return 1; 345 } 346 else 347 { 348 imcb_error( ic, "OAuth failure" ); 349 imc_logout( ic, TRUE ); 350 return 0; 351 } 352 } 341 353 342 354 if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) … … 491 503 static void jabber_chat_invite_( struct groupchat *c, char *who, char *msg ) 492 504 { 505 struct jabber_data *jd = c->ic->proto_data; 493 506 struct jabber_chat *jc = c->data; 494 507 gchar *msg_alt = NULL; 495 508 496 509 if( msg == NULL ) 497 msg_alt = g_strdup_printf( "%s invited you to %s", c->ic->acc->user, jc->name );510 msg_alt = g_strdup_printf( "%s invited you to %s", jd->me, jc->name ); 498 511 499 512 if( c && who ) -
protocols/jabber/jabber.h
r96f954d r5f40da7 47 47 JFLAG_XMLCONSOLE = 64, /* If the user added an xmlconsole buddy. */ 48 48 JFLAG_STARTTLS_DONE = 128, /* If a plaintext session was converted to TLS. */ 49 50 JFLAG_SASL_FB = 0x10000, /* Trying Facebook authentication. */ 49 51 } jabber_flags_t; 50 52 … … 92 94 char *username; /* USERNAME@server */ 93 95 char *server; /* username@SERVER -=> server/domain, not hostname */ 96 char *me; /* bare jid */ 97 98 const struct oauth2_service *oauth2_service; 99 char *oauth2_access_token; 94 100 95 101 /* After changing one of these two (or the priority setting), call … … 188 194 189 195 #define JABBER_XMLCONSOLE_HANDLE "xmlconsole" 196 #define JABBER_OAUTH_HANDLE "jabber_oauth" 190 197 191 198 /* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the … … 232 239 #define XMLNS_IBB "http://jabber.org/protocol/ibb" /* XEP-0047 */ 233 240 241 /* jabber.c */ 242 void jabber_connect( struct im_connection *ic ); 243 234 244 /* iq.c */ 235 245 xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); … … 300 310 struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns ); 301 311 void jabber_error_free( struct jabber_error *err ); 312 gboolean jabber_set_me( struct im_connection *ic, const char *me ); 302 313 303 314 extern const struct jabber_away_state jabber_away_state_list[]; … … 316 327 xt_status sasl_pkt_result( struct xt_node *node, gpointer data ); 317 328 gboolean sasl_supported( struct im_connection *ic ); 329 void sasl_oauth2_init( struct im_connection *ic ); 330 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg ); 331 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token ); 332 333 extern const struct oauth2_service oauth2_service_google; 334 extern const struct oauth2_service oauth2_service_facebook; 335 extern const struct oauth2_service oauth2_service_mslive; 318 336 319 337 /* conference.c */ -
protocols/jabber/jabber_util.c
r96f954d r5f40da7 761 761 g_free( err ); 762 762 } 763 764 gboolean jabber_set_me( struct im_connection *ic, const char *me ) 765 { 766 struct jabber_data *jd = ic->proto_data; 767 768 if( strchr( me, '@' ) == NULL ) 769 return FALSE; 770 771 g_free( jd->username ); 772 g_free( jd->me ); 773 774 jd->me = jabber_normalize( me ); 775 jd->server = strchr( jd->me, '@' ); 776 jd->username = g_strndup( jd->me, jd->server - jd->me ); 777 jd->server ++; 778 779 return TRUE; 780 } -
protocols/jabber/sasl.c
r96f954d r5f40da7 26 26 #include "jabber.h" 27 27 #include "base64.h" 28 #include "oauth2.h" 29 #include "oauth.h" 30 31 const struct oauth2_service oauth2_service_google = 32 { 33 "https://accounts.google.com/o/oauth2/auth", 34 "https://accounts.google.com/o/oauth2/token", 35 "urn:ietf:wg:oauth:2.0:oob", 36 "https://www.googleapis.com/auth/googletalk", 37 "783993391592.apps.googleusercontent.com", 38 "6C-Zgf7Tr7gEQTPlBhMUgo7R", 39 }; 40 const struct oauth2_service oauth2_service_facebook = 41 { 42 "https://www.facebook.com/dialog/oauth", 43 "https://graph.facebook.com/oauth/access_token", 44 "http://www.bitlbee.org/main.php/Facebook/oauth2.html", 45 "offline_access,xmpp_login", 46 "126828914005625", 47 "4b100f0f244d620bf3f15f8b217d4c32", 48 }; 49 const struct oauth2_service oauth2_service_mslive = 50 { 51 "https://oauth.live.com/authorize", 52 "https://oauth.live.com/token", 53 "http://www.bitlbee.org/main.php/Messenger/oauth2.html", 54 "wl.offline_access%20wl.messenger", 55 "000000004C06FCD1", 56 "IRKlBPzJJAWcY-TbZjiTEJu9tn7XCFaV", 57 }; 28 58 29 59 xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ) … … 33 63 struct xt_node *c, *reply; 34 64 char *s; 35 int sup_plain = 0, sup_digest = 0; 65 int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0, sup_ms = 0; 66 int want_oauth = FALSE; 67 GString *mechs; 36 68 37 69 if( !sasl_supported( ic ) ) … … 52 84 } 53 85 86 mechs = g_string_new( "" ); 54 87 c = node->children; 55 88 while( ( c = xt_find_node( c, "mechanism" ) ) ) … … 57 90 if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 ) 58 91 sup_plain = 1; 59 if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )92 else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 ) 60 93 sup_digest = 1; 94 else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 ) 95 sup_gtalk = 1; 96 else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 ) 97 sup_fb = 1; 98 else if( c->text && g_strcasecmp( c->text, "X-MESSENGER-OAUTH2" ) == 0 ) 99 sup_ms = 1; 100 101 if( c->text ) 102 g_string_append_printf( mechs, " %s", c->text ); 61 103 62 104 c = c->next; … … 65 107 if( !sup_plain && !sup_digest ) 66 108 { 67 imcb_error( ic, "No known SASL authentication schemes supported" ); 109 if( !sup_gtalk && !sup_fb && !sup_ms ) 110 imcb_error( ic, "This server requires OAuth " 111 "(supported schemes:%s)", mechs->str ); 112 else 113 imcb_error( ic, "BitlBee does not support any of the offered SASL " 114 "authentication schemes:%s", mechs->str ); 68 115 imc_logout( ic, FALSE ); 116 g_string_free( mechs, TRUE ); 69 117 return XT_ABORT; 70 118 } 119 g_string_free( mechs, TRUE ); 71 120 72 121 reply = xt_new_node( "auth", NULL, NULL ); 73 122 xt_add_attr( reply, "xmlns", XMLNS_SASL ); 74 75 if( sup_digest ) 123 want_oauth = set_getbool( &ic->acc->set, "oauth" ); 124 125 if( sup_gtalk && want_oauth ) 126 { 127 int len; 128 129 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation. 130 It's currently used by GTalk and vaguely documented on 131 http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */ 132 xt_add_attr( reply, "mechanism", "X-OAUTH2" ); 133 134 len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2; 135 s = g_malloc( len + 1 ); 136 s[0] = 0; 137 strcpy( s + 1, jd->username ); 138 strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token ); 139 reply->text = base64_encode( (unsigned char *)s, len ); 140 reply->text_len = strlen( reply->text ); 141 g_free( s ); 142 } 143 else if( sup_ms && want_oauth ) 144 { 145 xt_add_attr( reply, "mechanism", "X-MESSENGER-OAUTH2" ); 146 reply->text = g_strdup( jd->oauth2_access_token ); 147 reply->text_len = strlen( jd->oauth2_access_token ); 148 } 149 else if( sup_fb && want_oauth ) 150 { 151 xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" ); 152 jd->flags |= JFLAG_SASL_FB; 153 } 154 else if( want_oauth ) 155 { 156 imcb_error( ic, "OAuth requested, but not supported by server" ); 157 imc_logout( ic, FALSE ); 158 xt_free_node( reply ); 159 return XT_ABORT; 160 } 161 else if( sup_digest ) 76 162 { 77 163 xt_add_attr( reply, "mechanism", "DIGEST-MD5" ); … … 96 182 } 97 183 98 if( !jabber_write_packet( ic, reply ) )184 if( reply && !jabber_write_packet( ic, reply ) ) 99 185 { 100 186 xt_free_node( reply ); … … 197 283 struct im_connection *ic = data; 198 284 struct jabber_data *jd = ic->proto_data; 199 struct xt_node *reply = NULL;285 struct xt_node *reply_pkt = NULL; 200 286 char *nonce = NULL, *realm = NULL, *cnonce = NULL; 201 287 unsigned char cnonce_bin[30]; 202 288 char *digest_uri = NULL; 203 289 char *dec = NULL; 204 char *s = NULL ;290 char *s = NULL, *reply = NULL; 205 291 xt_status ret = XT_ABORT; 206 292 … … 210 296 dec = frombase64( node->text ); 211 297 212 if( !( s = sasl_get_part( dec, "rspauth" ) ) ) 298 if( jd->flags & JFLAG_SASL_FB ) 299 { 300 /* New-style Facebook OAauth2 support. Instead of sending a refresh 301 token, they just send an access token that should never expire. */ 302 GSList *p_in = NULL, *p_out = NULL; 303 char time[33]; 304 305 oauth_params_parse( &p_in, dec ); 306 oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) ); 307 oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) ); 308 oauth_params_free( &p_in ); 309 310 g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) ); 311 oauth_params_add( &p_out, "call_id", time ); 312 oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key ); 313 oauth_params_add( &p_out, "v", "1.0" ); 314 oauth_params_add( &p_out, "format", "XML" ); 315 oauth_params_add( &p_out, "access_token", jd->oauth2_access_token ); 316 317 reply = oauth_params_string( p_out ); 318 oauth_params_free( &p_out ); 319 } 320 else if( !( s = sasl_get_part( dec, "rspauth" ) ) ) 213 321 { 214 322 /* See RFC 2831 for for information. */ … … 271 379 272 380 /* Now build the SASL response string: */ 273 g_free( dec ); 274 dec = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 275 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 276 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" ); 277 s = tobase64( dec ); 381 reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 382 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 383 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" ); 278 384 } 279 385 else 280 386 { 281 387 /* We found rspauth, but don't really care... */ 282 g_free( s ); 283 s = NULL; 284 } 285 286 reply = xt_new_node( "response", s, NULL ); 287 xt_add_attr( reply, "xmlns", XMLNS_SASL ); 288 289 if( !jabber_write_packet( ic, reply ) ) 388 } 389 390 s = reply ? tobase64( reply ) : NULL; 391 reply_pkt = xt_new_node( "response", s, NULL ); 392 xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL ); 393 394 if( !jabber_write_packet( ic, reply_pkt ) ) 290 395 goto silent_error; 291 396 … … 301 406 g_free( cnonce ); 302 407 g_free( nonce ); 408 g_free( reply ); 303 409 g_free( realm ); 304 410 g_free( dec ); 305 411 g_free( s ); 306 xt_free_node( reply );412 xt_free_node( reply_pkt ); 307 413 308 414 return ret; … … 347 453 return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0; 348 454 } 455 456 void sasl_oauth2_init( struct im_connection *ic ) 457 { 458 struct jabber_data *jd = ic->proto_data; 459 char *msg, *url; 460 461 imcb_log( ic, "Starting OAuth authentication" ); 462 463 /* Temporary contact, just used to receive the OAuth response. */ 464 imcb_add_buddy( ic, JABBER_OAUTH_HANDLE, NULL ); 465 url = oauth2_url( jd->oauth2_service ); 466 msg = g_strdup_printf( "Open this URL in your browser to authenticate: %s", url ); 467 imcb_buddy_msg( ic, JABBER_OAUTH_HANDLE, msg, 0, 0 ); 468 imcb_buddy_msg( ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned " 469 "authorization token.", 0, 0 ); 470 471 g_free( msg ); 472 g_free( url ); 473 } 474 475 static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_condition cond ) 476 { 477 struct im_connection *ic = data; 478 if( g_slist_find( jabber_connections, ic ) ) 479 imcb_remove_buddy( ic, JABBER_OAUTH_HANDLE, NULL ); 480 return FALSE; 481 } 482 483 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ); 484 485 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg ) 486 { 487 struct jabber_data *jd = ic->proto_data; 488 char *code; 489 int ret; 490 491 imcb_log( ic, "Requesting OAuth access token" ); 492 493 /* Don't do it here because the caller may get confused if the contact 494 we're currently sending a message to is deleted. */ 495 b_timeout_add( 1, sasl_oauth2_remove_contact, ic ); 496 497 code = g_strdup( msg ); 498 g_strstrip( code ); 499 ret = oauth2_access_token( jd->oauth2_service, OAUTH2_AUTH_CODE, 500 code, sasl_oauth2_got_token, ic ); 501 502 g_free( code ); 503 return ret; 504 } 505 506 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token ) 507 { 508 struct jabber_data *jd = ic->proto_data; 509 510 return oauth2_access_token( jd->oauth2_service, OAUTH2_AUTH_REFRESH, 511 refresh_token, sasl_oauth2_got_token, ic ); 512 } 513 514 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ) 515 { 516 struct im_connection *ic = data; 517 struct jabber_data *jd; 518 GSList *auth = NULL; 519 520 if( g_slist_find( jabber_connections, ic ) == NULL ) 521 return; 522 523 jd = ic->proto_data; 524 525 if( access_token == NULL ) 526 { 527 imcb_error( ic, "OAuth failure (missing access token)" ); 528 imc_logout( ic, TRUE ); 529 return; 530 } 531 532 oauth_params_parse( &auth, ic->acc->pass ); 533 if( refresh_token ) 534 oauth_params_set( &auth, "refresh_token", refresh_token ); 535 if( access_token ) 536 oauth_params_set( &auth, "access_token", access_token ); 537 538 g_free( ic->acc->pass ); 539 ic->acc->pass = oauth_params_string( auth ); 540 oauth_params_free( &auth ); 541 542 g_free( jd->oauth2_access_token ); 543 jd->oauth2_access_token = g_strdup( access_token ); 544 545 jabber_connect( ic ); 546 } -
protocols/msn/msn.h
r96f954d r5f40da7 234 234 int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group ); 235 235 void msn_buddy_ask( bee_user_t *bu ); 236 char *msn_findheader( char *text, char *header, int len );237 236 char **msn_linesplit( char *line ); 238 237 int msn_handler( struct msn_handler_data *h ); -
protocols/msn/msn_util.c
r96f954d r5f40da7 226 226 bu->handle, bu->fullname ); 227 227 imcb_ask( bu->ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no ); 228 }229 230 char *msn_findheader( char *text, char *header, int len )231 {232 int hlen = strlen( header ), i;233 char *ret;234 235 if( len == 0 )236 len = strlen( text );237 238 i = 0;239 while( ( i + hlen ) < len )240 {241 /* Maybe this is a bit over-commented, but I just hate this part... */242 if( g_strncasecmp( text + i, header, hlen ) == 0 )243 {244 /* Skip to the (probable) end of the header */245 i += hlen;246 247 /* Find the first non-[: \t] character */248 while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++;249 250 /* Make sure we're still inside the string */251 if( i >= len ) return( NULL );252 253 /* Save the position */254 ret = text + i;255 256 /* Search for the end of this line */257 while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++;258 259 /* Make sure we're still inside the string */260 if( i >= len ) return( NULL );261 262 /* Copy the found data */263 return( g_strndup( ret, text + i - ret ) );264 }265 266 /* This wasn't the header we were looking for, skip to the next line. */267 while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++;268 while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;269 270 /* End of headers? */271 if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||272 ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||273 strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )274 {275 break;276 }277 }278 279 return( NULL );280 228 } 281 229 -
protocols/msn/ns.c
r96f954d r5f40da7 609 609 if( g_strcasecmp( cmd[1], "Hotmail" ) == 0 ) 610 610 { 611 char *ct = msn_findheader( msg, "Content-Type:", msglen );611 char *ct = get_rfc822_header( msg, "Content-Type:", msglen ); 612 612 613 613 if( !ct ) … … 622 622 return( 1 ); 623 623 624 mtype = msn_findheader( body, "Type:", blen );625 arg1 = msn_findheader( body, "Arg1:", blen );624 mtype = get_rfc822_header( body, "Type:", blen ); 625 arg1 = get_rfc822_header( body, "Arg1:", blen ); 626 626 627 627 if( mtype && strcmp( mtype, "1" ) == 0 ) … … 642 642 if( set_getbool( &ic->acc->set, "mail_notifications" ) ) 643 643 { 644 char *inbox = msn_findheader( body, "Inbox-Unread:", blen );645 char *folders = msn_findheader( body, "Folders-Unread:", blen );644 char *inbox = get_rfc822_header( body, "Inbox-Unread:", blen ); 645 char *folders = get_rfc822_header( body, "Folders-Unread:", blen ); 646 646 647 647 if( inbox && folders ) … … 656 656 if( set_getbool( &ic->acc->set, "mail_notifications" ) ) 657 657 { 658 char *from = msn_findheader( body, "From-Addr:", blen );659 char *fromname = msn_findheader( body, "From:", blen );658 char *from = get_rfc822_header( body, "From-Addr:", blen ); 659 char *fromname = get_rfc822_header( body, "From:", blen ); 660 660 661 661 if( from && fromname ) -
protocols/msn/sb.c
r96f954d r5f40da7 682 682 if( strcmp( cmd[0], "MSG" ) == 0 ) 683 683 { 684 char *ct = msn_findheader( msg, "Content-Type:", msglen );684 char *ct = get_rfc822_header( msg, "Content-Type:", msglen ); 685 685 686 686 if( !ct ) … … 711 711 else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 ) 712 712 { 713 char *command = msn_findheader( body, "Invitation-Command:", blen );714 char *cookie = msn_findheader( body, "Invitation-Cookie:", blen );713 char *command = get_rfc822_header( body, "Invitation-Command:", blen ); 714 char *cookie = get_rfc822_header( body, "Invitation-Cookie:", blen ); 715 715 unsigned int icookie; 716 716 … … 750 750 else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 ) 751 751 { 752 char *who = msn_findheader( msg, "TypingUser:", msglen );752 char *who = get_rfc822_header( msg, "TypingUser:", msglen ); 753 753 754 754 if( who ) -
protocols/nogaim.c
r96f954d r5f40da7 327 327 imcb_log( ic, "Signing off.." ); 328 328 329 /* TBH I don't remember anymore why I didn't just use ic->acc... */ 330 for( a = bee->accounts; a; a = a->next ) 331 if( a->ic == ic ) 332 break; 333 334 if( a && !allow_reconnect && !( ic->flags & OPT_LOGGED_IN ) && 335 set_getbool( &a->set, "oauth" ) ) 336 { 337 /* If this account supports OAuth, we're not logged in yet and 338 not allowed to retry, assume there were auth issues. Give a 339 helpful message on what might be necessary to fix this. */ 340 imcb_log( ic, "If you're having problems logging in, try re-requesting " 341 "an OAuth token: account %s set password \"\"", a->tag ); 342 } 343 329 344 for( l = bee->users; l; ) 330 345 { … … 347 362 348 363 query_del_by_conn( (irc_t*) ic->bee->ui_data, ic ); 349 350 for( a = bee->accounts; a; a = a->next )351 if( a->ic == ic )352 break;353 364 354 365 if( !a ) -
protocols/twitter/twitter.c
r96f954d r5f40da7 220 220 } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ 221 221 def_url = IDENTICA_API_URL; 222 def_oauth = " false";222 def_oauth = "true"; 223 223 } 224 224 … … 240 240 s->flags |= ACC_SET_OFFLINE_ONLY; 241 241 242 s = set_add(&acc->set, "oauth", def_oauth, set_eval_oauth, acc); 243 242 244 s = set_add(&acc->set, "show_ids", "false", set_eval_bool, acc); 243 245 s->flags |= ACC_SET_OFFLINE_ONLY; 244 246 245 247 s = set_add(&acc->set, "show_old_mentions", "true", set_eval_bool, acc); 246 247 s = set_add(&acc->set, "oauth", def_oauth, set_eval_bool, acc);248 248 } 249 249 -
root_commands.c
r96f954d r5f40da7 443 443 } 444 444 445 irc_rootmsg( irc, "Account successfully added with tag %s%s", 446 a->tag, cmd[4] ? "" : 447 ", now use /OPER to enter the password" ); 445 irc_rootmsg( irc, "Account successfully added with tag %s", a->tag ); 446 447 if( cmd[4] == NULL ) 448 { 449 set_t *oauth = set_find( &a->set, "oauth" ); 450 if( oauth && bool2int( set_value( oauth ) ) ) 451 { 452 *a->pass = '\0'; 453 irc_rootmsg( irc, "No need to enter a password for this " 454 "account since it's using OAuth" ); 455 } 456 else 457 { 458 irc_rootmsg( irc, "You can now use the /OPER command to " 459 "enter the password" ); 460 if( oauth ) 461 irc_rootmsg( irc, "Alternatively, enable OAuth if " 462 "the account supports it: account %s " 463 "set oauth on", a->tag ); 464 } 465 } 448 466 449 467 return; -
set.c
r96f954d r5f40da7 85 85 return NULL; 86 86 87 return s ->value ? s->value : s->def;87 return set_value( s ); 88 88 } 89 89 … … 250 250 } 251 251 252 /* 253 char *set_eval_ops( set_t *set, char *value ) 254 { 255 irc_t *irc = set->data; 256 257 if( g_strcasecmp( value, "user" ) == 0 ) 258 irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, 259 irc->channel, "+o-o", irc->nick, irc->mynick ); 260 else if( g_strcasecmp( value, "root" ) == 0 ) 261 irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, 262 irc->channel, "-o+o", irc->nick, irc->mynick ); 263 else if( g_strcasecmp( value, "both" ) == 0 ) 264 irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, 265 irc->channel, "+oo", irc->nick, irc->mynick ); 266 else if( g_strcasecmp( value, "none" ) == 0 ) 267 irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, 268 irc->channel, "-oo", irc->nick, irc->mynick ); 269 else 270 return SET_INVALID; 271 272 return value; 273 } 274 */ 252 char *set_eval_oauth( set_t *set, char *value ) 253 { 254 account_t *acc = set->data; 255 256 if( bool2int( value ) && strcmp( acc->pass, PASSWORD_PENDING ) == 0 ) 257 *acc->pass = '\0'; 258 259 return set_eval_bool( set, value ); 260 } -
set.h
r96f954d r5f40da7 77 77 } set_t; 78 78 79 #define set_value( set ) ((set)->value) ? ((set)->value) : ((set)->def) 80 79 81 /* Should be pretty clear. */ 80 82 set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ); … … 111 113 /* Some not very generic evaluators that really shouldn't be here... */ 112 114 char *set_eval_to_char( set_t *set, char *value ); 113 char *set_eval_o ps( set_t *set, char *value );115 char *set_eval_oauth( set_t *set, char *value ); 114 116 115 117 #endif /* __SET_H__ */ -
storage_xml.c
r96f954d r5f40da7 150 150 "Unknown protocol: %s", protocol ); 151 151 else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && 152 arc_decode( pass_cr, pass_len, &password, xd->given_pass ))152 arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 ) 153 153 { 154 154 xd->current_account = account_add( irc->b, prpl, handle, password );
Note: See TracChangeset
for help on using the changeset viewer.