Changeset 5ebff60 for protocols/jabber/sasl.c
- Timestamp:
- 2015-02-20T22:50:54Z (9 years ago)
- Branches:
- master
- Children:
- 0b9daac, 3d45471, 7733b8c
- Parents:
- af359b4
- git-author:
- Indent <please@…> (19-02-15 05:47:20)
- git-committer:
- dequis <dx@…> (20-02-15 22:50:54)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/jabber/sasl.c
raf359b4 r5ebff60 48 48 }; 49 49 50 xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data)50 xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) 51 51 { 52 52 struct im_connection *ic = data; … … 57 57 int want_oauth = FALSE; 58 58 GString *mechs; 59 60 if( !sasl_supported( ic ) ) 61 { 59 60 if (!sasl_supported(ic)) { 62 61 /* Should abort this now, since we should already be doing 63 62 IQ authentication. Strange things happen when you try 64 63 to do both... */ 65 imcb_log( ic, "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!" ); 64 imcb_log(ic, 65 "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!"); 66 66 return XT_HANDLED; 67 67 } 68 69 s = xt_find_attr( node, "xmlns" ); 70 if( !s || strcmp( s, XMLNS_SASL ) != 0 ) 71 { 72 imcb_log( ic, "Stream error while authenticating" ); 73 imc_logout( ic, FALSE ); 68 69 s = xt_find_attr(node, "xmlns"); 70 if (!s || strcmp(s, XMLNS_SASL) != 0) { 71 imcb_log(ic, "Stream error while authenticating"); 72 imc_logout(ic, FALSE); 74 73 return XT_ABORT; 75 74 } 76 77 want_oauth = set_getbool( &ic->acc->set, "oauth");78 79 mechs = g_string_new( "");75 76 want_oauth = set_getbool(&ic->acc->set, "oauth"); 77 78 mechs = g_string_new(""); 80 79 c = node->children; 81 while( ( c = xt_find_node( c, "mechanism" ) ) ) 82 { 83 if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 ) 80 while ((c = xt_find_node(c, "mechanism"))) { 81 if (c->text && g_strcasecmp(c->text, "PLAIN") == 0) { 84 82 sup_plain = 1; 85 else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )83 } else if (c->text && g_strcasecmp(c->text, "DIGEST-MD5") == 0) { 86 84 sup_digest = 1; 87 else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 )85 } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) { 88 86 sup_gtalk = 1; 89 else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 )87 } else if (c->text && g_strcasecmp(c->text, "X-FACEBOOK-PLATFORM") == 0) { 90 88 sup_fb = 1; 91 92 if( c->text ) 93 g_string_append_printf( mechs, " %s", c->text ); 94 89 } 90 91 if (c->text) { 92 g_string_append_printf(mechs, " %s", c->text); 93 } 94 95 95 c = c->next; 96 96 } 97 98 if ( !want_oauth && !sup_plain && !sup_digest )99 {100 if( !sup_gtalk && !sup_fb )101 imcb_error( ic, "This server requires OAuth "102 "(supported schemes:%s)", mechs->str );103 else104 imcb_error( ic, "BitlBee does not support any of the offered SASL "105 "authentication schemes:%s", mechs->str );106 imc_logout( ic, FALSE);107 g_string_free( mechs, TRUE);97 98 if (!want_oauth && !sup_plain && !sup_digest) { 99 if (!sup_gtalk && !sup_fb) { 100 imcb_error(ic, "This server requires OAuth " 101 "(supported schemes:%s)", mechs->str); 102 } else { 103 imcb_error(ic, "BitlBee does not support any of the offered SASL " 104 "authentication schemes:%s", mechs->str); 105 } 106 imc_logout(ic, FALSE); 107 g_string_free(mechs, TRUE); 108 108 return XT_ABORT; 109 109 } 110 g_string_free( mechs, TRUE ); 111 112 reply = xt_new_node( "auth", NULL, NULL ); 113 xt_add_attr( reply, "xmlns", XMLNS_SASL ); 114 115 if( sup_gtalk && want_oauth ) 116 { 110 g_string_free(mechs, TRUE); 111 112 reply = xt_new_node("auth", NULL, NULL); 113 xt_add_attr(reply, "xmlns", XMLNS_SASL); 114 115 if (sup_gtalk && want_oauth) { 117 116 int len; 118 117 119 118 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation. 120 119 It's currently used by GTalk and vaguely documented on 121 120 http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */ 122 xt_add_attr( reply, "mechanism", "X-OAUTH2");123 124 len = strlen( jd->username ) + strlen( jd->oauth2_access_token) + 2;125 s = g_malloc( len + 1);121 xt_add_attr(reply, "mechanism", "X-OAUTH2"); 122 123 len = strlen(jd->username) + strlen(jd->oauth2_access_token) + 2; 124 s = g_malloc(len + 1); 126 125 s[0] = 0; 127 strcpy( s + 1, jd->username ); 128 strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token ); 129 reply->text = base64_encode( (unsigned char *)s, len ); 130 reply->text_len = strlen( reply->text ); 131 g_free( s ); 132 } 133 else if( sup_fb && want_oauth ) 134 { 135 xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" ); 126 strcpy(s + 1, jd->username); 127 strcpy(s + 2 + strlen(jd->username), jd->oauth2_access_token); 128 reply->text = base64_encode((unsigned char *) s, len); 129 reply->text_len = strlen(reply->text); 130 g_free(s); 131 } else if (sup_fb && want_oauth) { 132 xt_add_attr(reply, "mechanism", "X-FACEBOOK-PLATFORM"); 136 133 jd->flags |= JFLAG_SASL_FB; 137 } 138 else if( want_oauth ) 139 { 140 imcb_error( ic, "OAuth requested, but not supported by server" ); 141 imc_logout( ic, FALSE ); 142 xt_free_node( reply ); 134 } else if (want_oauth) { 135 imcb_error(ic, "OAuth requested, but not supported by server"); 136 imc_logout(ic, FALSE); 137 xt_free_node(reply); 143 138 return XT_ABORT; 144 } 145 else if( sup_digest ) 146 { 147 xt_add_attr( reply, "mechanism", "DIGEST-MD5" ); 148 139 } else if (sup_digest) { 140 xt_add_attr(reply, "mechanism", "DIGEST-MD5"); 141 149 142 /* The rest will be done later, when we receive a <challenge/>. */ 150 } 151 else if( sup_plain ) 152 { 143 } else if (sup_plain) { 153 144 int len; 154 155 xt_add_attr( reply, "mechanism", "PLAIN");156 145 146 xt_add_attr(reply, "mechanism", "PLAIN"); 147 157 148 /* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */ 158 len = strlen( jd->username ) + strlen( ic->acc->pass) + 2;159 s = g_malloc( len + 1);149 len = strlen(jd->username) + strlen(ic->acc->pass) + 2; 150 s = g_malloc(len + 1); 160 151 s[0] = 0; 161 strcpy( s + 1, jd->username ); 162 strcpy( s + 2 + strlen( jd->username ), ic->acc->pass ); 163 reply->text = base64_encode( (unsigned char *)s, len ); 164 reply->text_len = strlen( reply->text ); 165 g_free( s ); 166 } 167 168 if( reply && !jabber_write_packet( ic, reply ) ) 169 { 170 xt_free_node( reply ); 152 strcpy(s + 1, jd->username); 153 strcpy(s + 2 + strlen(jd->username), ic->acc->pass); 154 reply->text = base64_encode((unsigned char *) s, len); 155 reply->text_len = strlen(reply->text); 156 g_free(s); 157 } 158 159 if (reply && !jabber_write_packet(ic, reply)) { 160 xt_free_node(reply); 171 161 return XT_ABORT; 172 162 } 173 xt_free_node( reply);174 163 xt_free_node(reply); 164 175 165 /* To prevent classic authentication from happening. */ 176 166 jd->flags |= JFLAG_STREAM_STARTED; 177 167 178 168 return XT_HANDLED; 179 169 } … … 181 171 /* Non-static function, but not mentioned in jabber.h because it's for internal 182 172 use, just that the unittest should be able to reach it... */ 183 char *sasl_get_part( char *data, char *field)173 char *sasl_get_part(char *data, char *field) 184 174 { 185 175 int i, len; 186 187 len = strlen( field ); 188 189 while( g_ascii_isspace( *data ) || *data == ',' ) 190 data ++; 191 192 if( g_strncasecmp( data, field, len ) == 0 && data[len] == '=' ) 193 { 194 i = strlen( field ) + 1; 195 } 196 else 197 { 198 for( i = 0; data[i]; i ++ ) 199 { 176 177 len = strlen(field); 178 179 while (g_ascii_isspace(*data) || *data == ',') { 180 data++; 181 } 182 183 if (g_strncasecmp(data, field, len) == 0 && data[len] == '=') { 184 i = strlen(field) + 1; 185 } else { 186 for (i = 0; data[i]; i++) { 200 187 /* If we have a ", skip until it's closed again. */ 201 if ( data[i] == '"' )202 {203 i ++;204 while( data[i] != '"' || data[i-1] == '\\' )205 i ++;188 if (data[i] == '"') { 189 i++; 190 while (data[i] != '"' || data[i - 1] == '\\') { 191 i++; 192 } 206 193 } 207 194 208 195 /* If we got a comma, we got a new field. Check it, 209 196 find the next key after it. */ 210 if( data[i] == ',' ) 211 { 212 while( g_ascii_isspace( data[i] ) || data[i] == ',' ) 213 i ++; 214 215 if( g_strncasecmp( data + i, field, len ) == 0 && 216 data[i+len] == '=' ) 217 { 197 if (data[i] == ',') { 198 while (g_ascii_isspace(data[i]) || data[i] == ',') { 199 i++; 200 } 201 202 if (g_strncasecmp(data + i, field, len) == 0 && 203 data[i + len] == '=') { 218 204 i += len + 1; 219 205 break; … … 222 208 } 223 209 } 224 225 if( data[i] == '"' ) 226 { 210 211 if (data[i] == '"') { 227 212 int j; 228 213 char *ret; 229 230 i 214 215 i++; 231 216 len = 0; 232 while( data[i+len] != '"' || data[i+len-1] == '\\' ) 233 len ++; 234 235 ret = g_strndup( data + i, len ); 236 for( i = j = 0; ret[i]; i ++ ) 237 { 238 if( ret[i] == '\\' ) 239 { 217 while (data[i + len] != '"' || data[i + len - 1] == '\\') { 218 len++; 219 } 220 221 ret = g_strndup(data + i, len); 222 for (i = j = 0; ret[i]; i++) { 223 if (ret[i] == '\\') { 240 224 ret[j++] = ret[++i]; 241 } 242 else 243 { 225 } else { 244 226 ret[j++] = ret[i]; 245 227 } 246 228 } 247 229 ret[j] = 0; 248 230 249 231 return ret; 250 } 251 else if( data[i] ) 252 { 232 } else if (data[i]) { 253 233 len = 0; 254 while( data[i+len] && data[i+len] != ',' ) 255 len ++; 256 257 return g_strndup( data + i, len ); 258 } 259 else 260 { 234 while (data[i + len] && data[i + len] != ',') { 235 len++; 236 } 237 238 return g_strndup(data + i, len); 239 } else { 261 240 return NULL; 262 241 } 263 242 } 264 243 265 xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data)244 xt_status sasl_pkt_challenge(struct xt_node *node, gpointer data) 266 245 { 267 246 struct im_connection *ic = data; … … 274 253 char *s = NULL, *reply = NULL; 275 254 xt_status ret = XT_ABORT; 276 277 if ( node->text_len == 0 )255 256 if (node->text_len == 0) { 278 257 goto error; 279 280 dec = frombase64( node->text ); 281 282 if( jd->flags & JFLAG_SASL_FB ) 283 {258 } 259 260 dec = frombase64(node->text); 261 262 if (jd->flags & JFLAG_SASL_FB) { 284 263 /* New-style Facebook OAauth2 support. Instead of sending a refresh 285 264 token, they just send an access token that should never expire. */ 286 265 GSList *p_in = NULL, *p_out = NULL; 287 266 char time[33]; 288 289 oauth_params_parse( &p_in, dec ); 290 oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) ); 291 oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) ); 292 oauth_params_free( &p_in ); 293 294 g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) ); 295 oauth_params_add( &p_out, "call_id", time ); 296 oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key ); 297 oauth_params_add( &p_out, "v", "1.0" ); 298 oauth_params_add( &p_out, "format", "XML" ); 299 oauth_params_add( &p_out, "access_token", jd->oauth2_access_token ); 300 301 reply = oauth_params_string( p_out ); 302 oauth_params_free( &p_out ); 303 } 304 else if( !( s = sasl_get_part( dec, "rspauth" ) ) ) 305 { 267 268 oauth_params_parse(&p_in, dec); 269 oauth_params_add(&p_out, "nonce", oauth_params_get(&p_in, "nonce")); 270 oauth_params_add(&p_out, "method", oauth_params_get(&p_in, "method")); 271 oauth_params_free(&p_in); 272 273 g_snprintf(time, sizeof(time), "%lld", (long long) (gettime() * 1000)); 274 oauth_params_add(&p_out, "call_id", time); 275 oauth_params_add(&p_out, "api_key", oauth2_service_facebook.consumer_key); 276 oauth_params_add(&p_out, "v", "1.0"); 277 oauth_params_add(&p_out, "format", "XML"); 278 oauth_params_add(&p_out, "access_token", jd->oauth2_access_token); 279 280 reply = oauth_params_string(p_out); 281 oauth_params_free(&p_out); 282 } else if (!(s = sasl_get_part(dec, "rspauth"))) { 306 283 /* See RFC 2831 for for information. */ 307 284 md5_state_t A1, A2, H; … … 309 286 char A1h[33], A2h[33], Hh[33]; 310 287 int i; 311 312 nonce = sasl_get_part( dec, "nonce");313 realm = sasl_get_part( dec, "realm");314 315 if ( !nonce )288 289 nonce = sasl_get_part(dec, "nonce"); 290 realm = sasl_get_part(dec, "realm"); 291 292 if (!nonce) { 316 293 goto error; 317 294 } 295 318 296 /* Jabber.Org considers the realm part optional and doesn't 319 297 specify one. Oh well, actually they're right, but still, 320 298 don't know if this is right... */ 321 if( !realm ) 322 realm = g_strdup( jd->server ); 323 324 random_bytes( cnonce_bin, sizeof( cnonce_bin ) ); 325 cnonce = base64_encode( cnonce_bin, sizeof( cnonce_bin ) ); 326 digest_uri = g_strdup_printf( "%s/%s", "xmpp", jd->server ); 327 299 if (!realm) { 300 realm = g_strdup(jd->server); 301 } 302 303 random_bytes(cnonce_bin, sizeof(cnonce_bin)); 304 cnonce = base64_encode(cnonce_bin, sizeof(cnonce_bin)); 305 digest_uri = g_strdup_printf("%s/%s", "xmpp", jd->server); 306 328 307 /* Generate the MD5 hash of username:realm:password, 329 308 I decided to call it H. */ 330 md5_init( &H);331 s = g_strdup_printf( "%s:%s:%s", jd->username, realm, ic->acc->pass);332 md5_append( &H, (unsigned char *) s, strlen( s ));333 g_free( s);334 md5_finish( &H, Hr);335 309 md5_init(&H); 310 s = g_strdup_printf("%s:%s:%s", jd->username, realm, ic->acc->pass); 311 md5_append(&H, (unsigned char *) s, strlen(s)); 312 g_free(s); 313 md5_finish(&H, Hr); 314 336 315 /* Now generate the hex. MD5 hash of H:nonce:cnonce, called A1. */ 337 md5_init( &A1 ); 338 s = g_strdup_printf( ":%s:%s", nonce, cnonce ); 339 md5_append( &A1, Hr, 16 ); 340 md5_append( &A1, (unsigned char *) s, strlen( s ) ); 341 g_free( s ); 342 md5_finish( &A1, A1r ); 343 for( i = 0; i < 16; i ++ ) 344 sprintf( A1h + i * 2, "%02x", A1r[i] ); 345 316 md5_init(&A1); 317 s = g_strdup_printf(":%s:%s", nonce, cnonce); 318 md5_append(&A1, Hr, 16); 319 md5_append(&A1, (unsigned char *) s, strlen(s)); 320 g_free(s); 321 md5_finish(&A1, A1r); 322 for (i = 0; i < 16; i++) { 323 sprintf(A1h + i * 2, "%02x", A1r[i]); 324 } 325 346 326 /* A2... */ 347 md5_init( &A2 ); 348 s = g_strdup_printf( "%s:%s", "AUTHENTICATE", digest_uri ); 349 md5_append( &A2, (unsigned char *) s, strlen( s ) ); 350 g_free( s ); 351 md5_finish( &A2, A2r ); 352 for( i = 0; i < 16; i ++ ) 353 sprintf( A2h + i * 2, "%02x", A2r[i] ); 354 327 md5_init(&A2); 328 s = g_strdup_printf("%s:%s", "AUTHENTICATE", digest_uri); 329 md5_append(&A2, (unsigned char *) s, strlen(s)); 330 g_free(s); 331 md5_finish(&A2, A2r); 332 for (i = 0; i < 16; i++) { 333 sprintf(A2h + i * 2, "%02x", A2r[i]); 334 } 335 355 336 /* Final result: A1:nonce:00000001:cnonce:auth:A2. Let's reuse H for it. */ 356 md5_init( &H ); 357 s = g_strdup_printf( "%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h ); 358 md5_append( &H, (unsigned char *) s, strlen( s ) ); 359 g_free( s ); 360 md5_finish( &H, Hr ); 361 for( i = 0; i < 16; i ++ ) 362 sprintf( Hh + i * 2, "%02x", Hr[i] ); 363 337 md5_init(&H); 338 s = g_strdup_printf("%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h); 339 md5_append(&H, (unsigned char *) s, strlen(s)); 340 g_free(s); 341 md5_finish(&H, Hr); 342 for (i = 0; i < 16; i++) { 343 sprintf(Hh + i * 2, "%02x", Hr[i]); 344 } 345 364 346 /* Now build the SASL response string: */ 365 reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 366 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 367 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" ); 368 } 369 else 370 { 347 reply = g_strdup_printf("username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 348 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 349 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8"); 350 } else { 371 351 /* We found rspauth, but don't really care... */ 372 g_free( s);373 } 374 375 s = reply ? tobase64( reply) : NULL;376 reply_pkt = xt_new_node( "response", s, NULL);377 xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL);378 379 if ( !jabber_write_packet( ic, reply_pkt ) )352 g_free(s); 353 } 354 355 s = reply ? tobase64(reply) : NULL; 356 reply_pkt = xt_new_node("response", s, NULL); 357 xt_add_attr(reply_pkt, "xmlns", XMLNS_SASL); 358 359 if (!jabber_write_packet(ic, reply_pkt)) { 380 360 goto silent_error; 381 361 } 362 382 363 ret = XT_HANDLED; 383 364 goto silent_error; 384 365 385 366 error: 386 imcb_error( ic, "Incorrect SASL challenge received");387 imc_logout( ic, FALSE);367 imcb_error(ic, "Incorrect SASL challenge received"); 368 imc_logout(ic, FALSE); 388 369 389 370 silent_error: 390 g_free( digest_uri);391 g_free( cnonce);392 g_free( nonce);393 g_free( reply);394 g_free( realm);395 g_free( dec);396 g_free( s);397 xt_free_node( reply_pkt);398 371 g_free(digest_uri); 372 g_free(cnonce); 373 g_free(nonce); 374 g_free(reply); 375 g_free(realm); 376 g_free(dec); 377 g_free(s); 378 xt_free_node(reply_pkt); 379 399 380 return ret; 400 381 } 401 382 402 xt_status sasl_pkt_result( struct xt_node *node, gpointer data)383 xt_status sasl_pkt_result(struct xt_node *node, gpointer data) 403 384 { 404 385 struct im_connection *ic = data; 405 386 struct jabber_data *jd = ic->proto_data; 406 387 char *s; 407 408 s = xt_find_attr( node, "xmlns" ); 409 if( !s || strcmp( s, XMLNS_SASL ) != 0 ) 410 { 411 imcb_log( ic, "Stream error while authenticating" ); 412 imc_logout( ic, FALSE ); 388 389 s = xt_find_attr(node, "xmlns"); 390 if (!s || strcmp(s, XMLNS_SASL) != 0) { 391 imcb_log(ic, "Stream error while authenticating"); 392 imc_logout(ic, FALSE); 413 393 return XT_ABORT; 414 394 } 415 416 if( strcmp( node->name, "success" ) == 0 ) 417 { 418 imcb_log( ic, "Authentication finished" ); 395 396 if (strcmp(node->name, "success") == 0) { 397 imcb_log(ic, "Authentication finished"); 419 398 jd->flags |= JFLAG_AUTHENTICATED | JFLAG_STREAM_RESTART; 420 } 421 else if( strcmp( node->name, "failure" ) == 0 ) 422 { 423 imcb_error( ic, "Authentication failure" ); 424 imc_logout( ic, FALSE ); 399 } else if (strcmp(node->name, "failure") == 0) { 400 imcb_error(ic, "Authentication failure"); 401 imc_logout(ic, FALSE); 425 402 return XT_ABORT; 426 403 } 427 404 428 405 return XT_HANDLED; 429 406 } … … 432 409 It's done by checking if the <stream:stream> from the server has a 433 410 version attribute. I don't know if this is the right way though... */ 434 gboolean sasl_supported( struct im_connection *ic)435 { 436 struct jabber_data *jd = ic->proto_data; 437 438 return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" )) != 0;439 } 440 441 void sasl_oauth2_init( struct im_connection *ic)411 gboolean sasl_supported(struct im_connection *ic) 412 { 413 struct jabber_data *jd = ic->proto_data; 414 415 return (jd->xt && jd->xt->root && xt_find_attr(jd->xt->root, "version")) != 0; 416 } 417 418 void sasl_oauth2_init(struct im_connection *ic) 442 419 { 443 420 struct jabber_data *jd = ic->proto_data; 444 421 char *msg, *url; 445 446 imcb_log( ic, "Starting OAuth authentication");447 422 423 imcb_log(ic, "Starting OAuth authentication"); 424 448 425 /* Temporary contact, just used to receive the OAuth response. */ 449 imcb_add_buddy( ic, JABBER_OAUTH_HANDLE, NULL);450 url = oauth2_url( jd->oauth2_service);451 msg = g_strdup_printf( "Open this URL in your browser to authenticate: %s", url);452 imcb_buddy_msg( ic, JABBER_OAUTH_HANDLE, msg, 0, 0);453 imcb_buddy_msg( 454 "authorization token.", 0, 0);455 456 g_free( msg);457 g_free( url);458 } 459 460 static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_condition cond)426 imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL); 427 url = oauth2_url(jd->oauth2_service); 428 msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url); 429 imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0); 430 imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned " 431 "authorization token.", 0, 0); 432 433 g_free(msg); 434 g_free(url); 435 } 436 437 static gboolean sasl_oauth2_remove_contact(gpointer data, gint fd, b_input_condition cond) 461 438 { 462 439 struct im_connection *ic = data; 463 if( g_slist_find( jabber_connections, ic ) ) 464 imcb_remove_buddy( ic, JABBER_OAUTH_HANDLE, NULL ); 440 441 if (g_slist_find(jabber_connections, ic)) { 442 imcb_remove_buddy(ic, JABBER_OAUTH_HANDLE, NULL); 443 } 465 444 return FALSE; 466 445 } 467 446 468 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error ); 469 470 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg ) 447 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, 448 const char *error); 449 450 int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg) 471 451 { 472 452 struct jabber_data *jd = ic->proto_data; 473 453 char *code; 474 454 int ret; 475 476 imcb_log( ic, "Requesting OAuth access token");477 455 456 imcb_log(ic, "Requesting OAuth access token"); 457 478 458 /* Don't do it here because the caller may get confused if the contact 479 459 we're currently sending a message to is deleted. */ 480 b_timeout_add( 1, sasl_oauth2_remove_contact, ic);481 482 code = g_strdup( msg);483 g_strstrip( code);484 ret = oauth2_access_token( 485 code, sasl_oauth2_got_token, ic);486 487 g_free( code);460 b_timeout_add(1, sasl_oauth2_remove_contact, ic); 461 462 code = g_strdup(msg); 463 g_strstrip(code); 464 ret = oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_CODE, 465 code, sasl_oauth2_got_token, ic); 466 467 g_free(code); 488 468 return ret; 489 469 } 490 470 491 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token)492 { 493 struct jabber_data *jd = ic->proto_data; 494 495 return oauth2_access_token( 496 refresh_token, sasl_oauth2_got_token, ic);497 } 498 499 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error)471 int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token) 472 { 473 struct jabber_data *jd = ic->proto_data; 474 475 return oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_REFRESH, 476 refresh_token, sasl_oauth2_got_token, ic); 477 } 478 479 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error) 500 480 { 501 481 struct im_connection *ic = data; 502 482 struct jabber_data *jd; 503 483 GSList *auth = NULL; 504 505 if ( g_slist_find( jabber_connections, ic ) == NULL )484 485 if (g_slist_find(jabber_connections, ic) == NULL) { 506 486 return; 507 487 } 488 508 489 jd = ic->proto_data; 509 510 if( access_token == NULL ) 511 { 512 imcb_error( ic, "OAuth failure (%s)", error ); 513 imc_logout( ic, TRUE ); 490 491 if (access_token == NULL) { 492 imcb_error(ic, "OAuth failure (%s)", error); 493 imc_logout(ic, TRUE); 514 494 return; 515 495 } 516 517 oauth_params_parse( &auth, ic->acc->pass ); 518 if( refresh_token ) 519 oauth_params_set( &auth, "refresh_token", refresh_token ); 520 if( access_token ) 521 oauth_params_set( &auth, "access_token", access_token ); 522 523 g_free( ic->acc->pass ); 524 ic->acc->pass = oauth_params_string( auth ); 525 oauth_params_free( &auth ); 526 527 g_free( jd->oauth2_access_token ); 528 jd->oauth2_access_token = g_strdup( access_token ); 529 530 jabber_connect( ic ); 531 } 496 497 oauth_params_parse(&auth, ic->acc->pass); 498 if (refresh_token) { 499 oauth_params_set(&auth, "refresh_token", refresh_token); 500 } 501 if (access_token) { 502 oauth_params_set(&auth, "access_token", access_token); 503 } 504 505 g_free(ic->acc->pass); 506 ic->acc->pass = oauth_params_string(auth); 507 oauth_params_free(&auth); 508 509 g_free(jd->oauth2_access_token); 510 jd->oauth2_access_token = g_strdup(access_token); 511 512 jabber_connect(ic); 513 }
Note: See TracChangeset
for help on using the changeset viewer.