Changes in / [ab19567:41a94dd]
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
lib/http_client.c
rab19567 r41a94dd 2 2 * BitlBee -- An IRC to other IM-networks gateway * 3 3 * * 4 * Copyright 2002-201 3Wilmer van der Gaast and others *4 * Copyright 2002-2012 Wilmer van der Gaast and others * 5 5 \********************************************************************/ 6 6 … … 69 69 req->request_length = strlen( request ); 70 70 req->redir_ttl = 3; 71 req->content_length = -1;72 71 73 72 if( getenv( "BITLBEE_DEBUG" ) ) … … 97 96 request = g_strdup_printf( "GET %s HTTP/1.0\r\n" 98 97 "Host: %s\r\n" 98 "Connection: close\r\n" 99 99 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" 100 100 "\r\n", url->file, url->host ); … … 193 193 } 194 194 195 typedef enum {196 CR_OK,197 CR_EOF,198 CR_ERROR,199 CR_ABORT,200 } http_ret_t;201 202 195 static gboolean http_handle_headers( struct http_request *req ); 203 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len );204 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len );205 196 206 197 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) … … 208 199 struct http_request *req = data; 209 200 char buffer[4096]; 201 char *s; 202 size_t content_length; 210 203 int st; 211 204 … … 251 244 } 252 245 253 if( st > 0 ) 254 { 255 http_ret_t c; 256 257 if( req->flags & HTTPC_CHUNKED ) 258 c = http_process_chunked_data( req, buffer, st ); 259 else 260 c = http_process_data( req, buffer, st ); 261 262 if( c == CR_EOF ) 263 goto eof; 264 else if( c == CR_ERROR || c == CR_ABORT ) 265 return FALSE; 266 } 267 268 if( req->content_length != -1 && 269 req->body_size >= req->content_length ) 270 goto eof; 246 if( st > 0 && !req->sbuf ) 247 { 248 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); 249 memcpy( req->reply_headers + req->bytes_read, buffer, st ); 250 req->bytes_read += st; 251 252 st = 0; 253 } 254 255 if( st >= 0 && ( req->flags & HTTPC_STREAMING ) ) 256 { 257 if( !req->reply_body && 258 ( strstr( req->reply_headers, "\r\n\r\n" ) || 259 strstr( req->reply_headers, "\n\n" ) ) ) 260 { 261 size_t hlen; 262 263 /* We've now received all headers, so process them once 264 before we start feeding back data. */ 265 if( !http_handle_headers( req ) ) 266 return FALSE; 267 268 hlen = req->reply_body - req->reply_headers; 269 270 req->sblen = req->bytes_read - hlen; 271 req->sbuf = g_memdup( req->reply_body, req->sblen + 1 ); 272 req->reply_headers = g_realloc( req->reply_headers, hlen + 1 ); 273 274 req->reply_body = req->sbuf; 275 } 276 277 if( st > 0 ) 278 { 279 int pos = req->reply_body - req->sbuf; 280 req->sbuf = g_realloc( req->sbuf, req->sblen + st + 1 ); 281 memcpy( req->sbuf + req->sblen, buffer, st ); 282 req->bytes_read += st; 283 req->sblen += st; 284 req->sbuf[req->sblen] = '\0'; 285 req->reply_body = req->sbuf + pos; 286 req->body_size = req->sblen - pos; 287 } 288 289 if( req->reply_body ) 290 req->func( req ); 291 } 271 292 272 293 if( ssl_pending( req->ssl ) ) … … 290 311 goto cleanup; 291 312 } 313 314 if( !( req->flags & HTTPC_STREAMING ) ) 315 { 316 /* Returns FALSE if we were redirected, in which case we should abort 317 and not run any callback yet. */ 318 if( !http_handle_headers( req ) ) 319 return FALSE; 320 } 292 321 293 322 cleanup: … … 297 326 closesocket( req->fd ); 298 327 299 if( req->body_size < req->content_length ) 300 { 301 req->status_code = -1; 302 g_free( req->status_string ); 303 req->status_string = g_strdup( "Response truncated" ); 304 } 328 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 329 sscanf( s, "%zd", &content_length ) == 1 ) 330 { 331 if( content_length < req->body_size ) 332 { 333 req->status_code = -1; 334 g_free( req->status_string ); 335 req->status_string = g_strdup( "Response truncated" ); 336 } 337 } 338 g_free( s ); 305 339 306 340 if( getenv( "BITLBEE_DEBUG" ) && req ) … … 313 347 } 314 348 315 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len )316 {317 char *chunk, *eos, *s;318 319 if( len < 0 )320 return TRUE;321 322 if( len > 0 )323 {324 req->cbuf = g_realloc( req->cbuf, req->cblen + len + 1 );325 memcpy( req->cbuf + req->cblen, buffer, len );326 req->cblen += len;327 req->cbuf[req->cblen] = '\0';328 }329 330 /* Turns out writing a proper chunked-encoding state machine is not331 that simple. :-( I've tested this one feeding it byte by byte so332 I hope it's solid now. */333 chunk = req->cbuf;334 eos = req->cbuf + req->cblen;335 while( TRUE )336 {337 int clen = 0;338 339 /* Might be a \r\n from the last chunk. */340 s = chunk;341 while( isspace( *s ) )342 s ++;343 /* Chunk length. Might be incomplete. */344 if( s < eos && sscanf( s, "%x", &clen ) != 1 )345 return CR_ERROR;346 while( isxdigit( *s ) )347 s ++;348 349 /* If we read anything here, it *must* be \r\n. */350 if( strncmp( s, "\r\n", MIN( 2, eos - s ) ) != 0 )351 return CR_ERROR;352 s += 2;353 354 if( s >= eos )355 break;356 357 /* 0-length chunk means end of response. */358 if( clen == 0 )359 return CR_EOF;360 361 /* Wait for the whole chunk to arrive. */362 if( s + clen > eos )363 break;364 if( http_process_data( req, s, clen ) != CR_OK )365 return CR_ABORT;366 367 chunk = s + clen;368 }369 370 if( chunk != req->cbuf )371 {372 req->cblen = eos - chunk;373 s = g_memdup( chunk, req->cblen + 1 );374 g_free( req->cbuf );375 req->cbuf = s;376 }377 378 return CR_OK;379 }380 381 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len )382 {383 if( len <= 0 )384 return CR_OK;385 386 if( !req->reply_body )387 {388 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + len + 1 );389 memcpy( req->reply_headers + req->bytes_read, buffer, len );390 req->bytes_read += len;391 req->reply_headers[req->bytes_read] = '\0';392 393 if( strstr( req->reply_headers, "\r\n\r\n" ) ||394 strstr( req->reply_headers, "\n\n" ) )395 {396 /* We've now received all headers. Look for something397 interesting. */398 if( !http_handle_headers( req ) )399 return CR_ABORT;400 401 /* Start parsing the body as chunked if required. */402 if( req->flags & HTTPC_CHUNKED )403 return http_process_chunked_data( req, NULL, 0 );404 }405 }406 else407 {408 int pos = req->reply_body - req->sbuf;409 req->sbuf = g_realloc( req->sbuf, req->sblen + len + 1 );410 memcpy( req->sbuf + req->sblen, buffer, len );411 req->bytes_read += len;412 req->sblen += len;413 req->sbuf[req->sblen] = '\0';414 req->reply_body = req->sbuf + pos;415 req->body_size = req->sblen - pos;416 }417 418 if( ( req->flags & HTTPC_STREAMING ) && req->reply_body )419 req->func( req );420 421 return CR_OK;422 }423 424 349 /* Splits headers and body. Checks result code, in case of 300s it'll handle 425 350 redirects. If this returns FALSE, don't call any callbacks! */ 426 351 static gboolean http_handle_headers( struct http_request *req ) 427 352 { 428 char *end1, *end2 , *s;353 char *end1, *end2; 429 354 int evil_server = 0; 430 355 … … 452 377 } 453 378 454 *end1 = '\0';379 *end1 = 0; 455 380 456 381 if( getenv( "BITLBEE_DEBUG" ) ) … … 462 387 req->reply_body = end1 + 2; 463 388 464 /* Separately allocated space for headers and body. */ 465 req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body; 466 req->sbuf = req->reply_body = g_memdup( req->reply_body, req->body_size + 1 ); 467 req->reply_headers = g_realloc( req->reply_headers, end1 - req->reply_headers + 1 ); 389 req->body_size = req->reply_headers + req->bytes_read - req->reply_body; 468 390 469 391 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) … … 530 452 don't need this yet anyway, I won't implement it. */ 531 453 532 req->status_string = g_strdup( "Can't handle re lative redirects" );454 req->status_string = g_strdup( "Can't handle recursive redirects" ); 533 455 534 456 return TRUE; … … 538 460 /* A whole URL */ 539 461 url_t *url; 540 char *s , *version, *headers;462 char *s; 541 463 const char *new_method; 542 464 … … 566 488 return TRUE; 567 489 } 568 headers = s;569 490 570 491 /* More or less HTTP/1.0 compliant, from my reading of RFC 2616. … … 586 507 new_method = "POST"; 587 508 588 if( ( version = strstr( req->request, " HTTP/" ) ) &&589 ( s = strstr( version, "\r\n" ) ) )590 {591 version ++;592 version = g_strndup( version, s - version );593 }594 else595 version = g_strdup( "HTTP/1.0" );596 597 509 /* Okay, this isn't fun! We have to rebuild the request... :-( */ 598 new_request = g_strdup_printf( "%s %s %s\r\nHost: %s%s", 599 new_method, url->file, version, 600 url->host, headers ); 510 new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s", 511 new_method, url->file, url->host, s ); 601 512 602 513 new_host = g_strdup( url->host ); … … 610 521 611 522 g_free( url ); 612 g_free( version );613 523 } 614 524 … … 647 557 g_free( req->request ); 648 558 g_free( req->reply_headers ); 649 g_free( req->sbuf );650 559 req->request = new_request; 651 560 req->request_length = strlen( new_request ); 652 561 req->bytes_read = req->bytes_written = req->inpa = 0; 653 562 req->reply_headers = req->reply_body = NULL; 654 req->sbuf = req->cbuf = NULL;655 req->sblen = req->cblen = 0;656 563 657 564 return FALSE; 658 }659 660 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&661 sscanf( s, "%d", &req->content_length ) != 1 )662 req->content_length = -1;663 g_free( s );664 665 if( ( s = get_rfc822_header( req->reply_headers, "Transfer-Encoding", 0 ) ) )666 {667 if( strcasestr( s, "chunked" ) )668 {669 req->flags |= HTTPC_CHUNKED;670 req->cbuf = req->sbuf;671 req->cblen = req->sblen;672 673 req->reply_body = req->sbuf = g_strdup( "" );674 req->body_size = req->sblen = 0;675 }676 g_free( s );677 565 } 678 566 … … 719 607 g_free( req->status_string ); 720 608 g_free( req->sbuf ); 721 g_free( req->cbuf );722 609 g_free( req ); 723 610 } -
lib/http_client.h
rab19567 r41a94dd 42 42 HTTPC_STREAMING = 1, 43 43 HTTPC_EOF = 2, 44 HTTPC_CHUNKED = 4,45 44 46 45 /* Let's reserve 0x1000000+ for lib users. */ … … 78 77 int bytes_written; 79 78 int bytes_read; 80 int content_length; /* "Content-Length:" header or -1 */81 79 82 80 /* Used in streaming mode. Caller should read from reply_body. */ 83 81 char *sbuf; 84 82 size_t sblen; 85 86 /* Chunked encoding only. Raw chunked stream is decoded from here. */87 char *cbuf;88 size_t cblen;89 83 }; 90 84 -
lib/oauth.c
rab19567 r41a94dd 262 262 "Content-Type: application/x-www-form-urlencoded\r\n" 263 263 "Content-Length: %zd\r\n" 264 "Connection: close\r\n" 264 265 "\r\n" 265 266 "%s", url_p.file, url_p.host, strlen( post ), post ); -
lib/oauth2.c
rab19567 r41a94dd 96 96 "Content-Type: application/x-www-form-urlencoded\r\n" 97 97 "Content-Length: %zd\r\n" 98 "Connection: close\r\n" 98 99 "\r\n" 99 100 "%s", url_p.file, url_p.host, strlen( args_s ), args_s ); -
lib/proxy.c
rab19567 r41a94dd 158 158 } 159 159 160 event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port _, fd);160 event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); 161 161 162 162 if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) { -
protocols/twitter/twitter.c
rab19567 r41a94dd 289 289 char *def_url; 290 290 char *def_tul; 291 char *def_mentions;292 291 293 292 if (strcmp(acc->prpl->name, "twitter") == 0) { 294 293 def_url = TWITTER_API_URL; 295 294 def_tul = "20"; 296 def_mentions = "true";297 295 } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ 298 296 def_url = IDENTICA_API_URL; 299 297 def_tul = "0"; 300 def_mentions = "false";301 298 } 302 299 … … 311 308 s->flags |= ACC_SET_OFFLINE_ONLY; 312 309 313 s = set_add(&acc->set, "fetch_mentions", def_mentions, set_eval_bool, acc);310 s = set_add(&acc->set, "fetch_mentions", "true", set_eval_bool, acc); 314 311 315 312 s = set_add(&acc->set, "message_length", "140", set_eval_int, acc); -
protocols/twitter/twitter_http.c
rab19567 r41a94dd 78 78 79 79 // Make the request. 80 g_string_printf(request, "%s %s%s%s%s HTTP/1. 1\r\n"80 g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n" 81 81 "Host: %s\r\n" 82 82 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
Note: See TracChangeset
for help on using the changeset viewer.