Changeset 5ebff60 for lib/http_client.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
-
lib/http_client.c
raf359b4 r5ebff60 1 1 /********************************************************************\ 2 2 * BitlBee -- An IRC to other IM-networks gateway * 3 3 * * … … 32 32 33 33 34 static gboolean http_connected( gpointer data, int source, b_input_condition cond ); 35 static gboolean http_ssl_connected( gpointer data, int returncode, void *source, b_input_condition cond ); 36 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ); 37 static void http_free( struct http_request *req ); 38 39 40 struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ) 34 static gboolean http_connected(gpointer data, int source, b_input_condition cond); 35 static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond); 36 static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond); 37 static void http_free(struct http_request *req); 38 39 40 struct http_request *http_dorequest(char *host, int port, int ssl, char *request, http_input_function func, 41 gpointer data) 41 42 { 42 43 struct http_request *req; 43 44 int error = 0; 44 45 req = g_new0( struct http_request, 1 ); 46 47 if( ssl ) 48 { 49 req->ssl = ssl_connect( host, port, TRUE, http_ssl_connected, req ); 50 if( req->ssl == NULL ) 45 46 req = g_new0(struct http_request, 1); 47 48 if (ssl) { 49 req->ssl = ssl_connect(host, port, TRUE, http_ssl_connected, req); 50 if (req->ssl == NULL) { 51 51 error = 1; 52 } 53 else 54 { 55 req->fd = proxy_connect( host, port, http_connected, req ); 56 if( req->fd < 0 ) 52 } 53 } else { 54 req->fd = proxy_connect(host, port, http_connected, req); 55 if (req->fd < 0) { 57 56 error = 1; 58 }59 60 if( error ) 61 {62 http_free( req);57 } 58 } 59 60 if (error) { 61 http_free(req); 63 62 return NULL; 64 63 } 65 64 66 65 req->func = func; 67 66 req->data = data; 68 req->request = g_strdup( request);69 req->request_length = strlen( request);67 req->request = g_strdup(request); 68 req->request_length = strlen(request); 70 69 req->redir_ttl = 3; 71 70 req->content_length = -1; 72 73 if( getenv( "BITLBEE_DEBUG" ) ) 74 printf( "About to send HTTP request:\n%s\n", req->request ); 75 71 72 if (getenv("BITLBEE_DEBUG")) { 73 printf("About to send HTTP request:\n%s\n", req->request); 74 } 75 76 76 return req; 77 77 } 78 78 79 struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data)80 { 81 url_t *url = g_new0( url_t, 1);79 struct http_request *http_dorequest_url(char *url_string, http_input_function func, gpointer data) 80 { 81 url_t *url = g_new0(url_t, 1); 82 82 char *request; 83 83 void *ret; 84 85 if( !url_set( url, url_string ) ) 86 { 87 g_free( url ); 84 85 if (!url_set(url, url_string)) { 86 g_free(url); 88 87 return NULL; 89 88 } 90 91 if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS ) 92 { 93 g_free( url ); 89 90 if (url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS) { 91 g_free(url); 94 92 return NULL; 95 93 } 96 97 request = g_strdup_printf( 98 99 100 "\r\n", url->file, url->host);101 102 ret = http_dorequest( 103 url->proto == PROTO_HTTPS, request, func, data);104 105 g_free( url);106 g_free( request);94 95 request = g_strdup_printf("GET %s HTTP/1.0\r\n" 96 "Host: %s\r\n" 97 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" 98 "\r\n", url->file, url->host); 99 100 ret = http_dorequest(url->host, url->port, 101 url->proto == PROTO_HTTPS, request, func, data); 102 103 g_free(url); 104 g_free(request); 107 105 return ret; 108 106 } 109 107 110 /* This one is actually pretty simple... Might get more calls if we can't write 108 /* This one is actually pretty simple... Might get more calls if we can't write 111 109 the whole request at once. */ 112 static gboolean http_connected( gpointer data, int source, b_input_condition cond)110 static gboolean http_connected(gpointer data, int source, b_input_condition cond) 113 111 { 114 112 struct http_request *req = data; 115 113 int st; 116 117 if ( source < 0 )114 115 if (source < 0) { 118 116 goto error; 119 120 if( req->inpa > 0 ) 121 b_event_remove( req->inpa ); 122 123 sock_make_nonblocking( req->fd ); 124 125 if( req->ssl ) 126 { 127 st = ssl_write( req->ssl, req->request + req->bytes_written, 128 req->request_length - req->bytes_written ); 129 if( st < 0 ) 130 { 131 if( ssl_errno != SSL_AGAIN ) 132 { 133 ssl_disconnect( req->ssl ); 117 } 118 119 if (req->inpa > 0) { 120 b_event_remove(req->inpa); 121 } 122 123 sock_make_nonblocking(req->fd); 124 125 if (req->ssl) { 126 st = ssl_write(req->ssl, req->request + req->bytes_written, 127 req->request_length - req->bytes_written); 128 if (st < 0) { 129 if (ssl_errno != SSL_AGAIN) { 130 ssl_disconnect(req->ssl); 134 131 goto error; 135 132 } 136 133 } 137 } 138 else 139 { 140 st = write( source, req->request + req->bytes_written, 141 req->request_length - req->bytes_written ); 142 if( st < 0 ) 143 { 144 if( !sockerr_again() ) 145 { 146 closesocket( req->fd ); 134 } else { 135 st = write(source, req->request + req->bytes_written, 136 req->request_length - req->bytes_written); 137 if (st < 0) { 138 if (!sockerr_again()) { 139 closesocket(req->fd); 147 140 goto error; 148 141 } 149 142 } 150 143 } 151 152 if ( st > 0 )144 145 if (st > 0) { 153 146 req->bytes_written += st; 154 155 if( req->bytes_written < req->request_length ) 156 req->inpa = b_input_add( source, 157 req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE, 158 http_connected, req ); 159 else 160 req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req ); 161 147 } 148 149 if (req->bytes_written < req->request_length) { 150 req->inpa = b_input_add(source, 151 req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_WRITE, 152 http_connected, req); 153 } else { 154 req->inpa = b_input_add(source, B_EV_IO_READ, http_incoming_data, req); 155 } 156 162 157 return FALSE; 163 158 164 159 error: 165 if( req->status_string == NULL ) 166 req->status_string = g_strdup( "Error while writing HTTP request" ); 167 168 req->func( req ); 169 http_free( req ); 160 if (req->status_string == NULL) { 161 req->status_string = g_strdup("Error while writing HTTP request"); 162 } 163 164 req->func(req); 165 http_free(req); 170 166 return FALSE; 171 167 } 172 168 173 static gboolean http_ssl_connected( gpointer data, int returncode, void *source, b_input_condition cond)169 static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond) 174 170 { 175 171 struct http_request *req = data; 176 177 if( source == NULL ) 178 { 179 if( returncode != 0 ) 180 { 181 char *err = ssl_verify_strerror( returncode ); 172 173 if (source == NULL) { 174 if (returncode != 0) { 175 char *err = ssl_verify_strerror(returncode); 182 176 req->status_string = g_strdup_printf( 183 184 returncode, err ? err : "Unknown");185 g_free( err);186 } 187 return http_connected( data, -1, cond);188 } 189 190 req->fd = ssl_getfd( source);191 192 return http_connected( data, req->fd, cond);177 "Certificate verification problem 0x%x: %s", 178 returncode, err ? err : "Unknown"); 179 g_free(err); 180 } 181 return http_connected(data, -1, cond); 182 } 183 184 req->fd = ssl_getfd(source); 185 186 return http_connected(data, req->fd, cond); 193 187 } 194 188 … … 200 194 } http_ret_t; 201 195 202 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 206 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond)196 static gboolean http_handle_headers(struct http_request *req); 197 static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len); 198 static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len); 199 200 static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond) 207 201 { 208 202 struct http_request *req = data; 209 203 char buffer[4096]; 210 204 int st; 211 212 if( req->inpa > 0 ) 213 { 214 b_event_remove( req->inpa ); 205 206 if (req->inpa > 0) { 207 b_event_remove(req->inpa); 215 208 req->inpa = 0; 216 209 } 217 218 if( req->ssl ) 219 { 220 st = ssl_read( req->ssl, buffer, sizeof( buffer ) ); 221 if( st < 0 ) 222 { 223 if( ssl_errno != SSL_AGAIN ) 224 { 210 211 if (req->ssl) { 212 st = ssl_read(req->ssl, buffer, sizeof(buffer)); 213 if (st < 0) { 214 if (ssl_errno != SSL_AGAIN) { 225 215 /* goto cleanup; */ 226 216 227 217 /* YAY! We have to deal with crappy Microsoft 228 218 servers that LOVE to send invalid TLS 229 219 packets that abort connections! \o/ */ 230 220 231 221 goto eof; 232 222 } 233 } 234 else if( st == 0 ) 235 { 223 } else if (st == 0) { 236 224 goto eof; 237 225 } 238 } 239 else 240 { 241 st = read( req->fd, buffer, sizeof( buffer ) ); 242 if( st < 0 ) 243 { 244 if( !sockerr_again() ) 245 { 246 req->status_string = g_strdup( strerror( errno ) ); 226 } else { 227 st = read(req->fd, buffer, sizeof(buffer)); 228 if (st < 0) { 229 if (!sockerr_again()) { 230 req->status_string = g_strdup(strerror(errno)); 247 231 goto cleanup; 248 232 } 249 } 250 else if( st == 0 ) 251 { 233 } else if (st == 0) { 252 234 goto eof; 253 235 } 254 236 } 255 256 if( st > 0 ) 257 { 237 238 if (st > 0) { 258 239 http_ret_t c; 259 260 if( req->flags & HTTPC_CHUNKED ) 261 c = http_process_chunked_data( req, buffer, st ); 262 else 263 c = http_process_data( req, buffer, st ); 264 265 if( c == CR_EOF ) 240 241 if (req->flags & HTTPC_CHUNKED) { 242 c = http_process_chunked_data(req, buffer, st); 243 } else { 244 c = http_process_data(req, buffer, st); 245 } 246 247 if (c == CR_EOF) { 266 248 goto eof; 267 else if( c == CR_ERROR || c == CR_ABORT )249 } else if (c == CR_ERROR || c == CR_ABORT) { 268 250 return FALSE; 269 } 270 271 if( req->content_length != -1 && 272 req->body_size >= req->content_length ) 251 } 252 } 253 254 if (req->content_length != -1 && 255 req->body_size >= req->content_length) { 273 256 goto eof; 274 275 if( ssl_pending( req->ssl ) ) 276 return http_incoming_data( data, source, cond ); 277 257 } 258 259 if (ssl_pending(req->ssl)) { 260 return http_incoming_data(data, source, cond); 261 } 262 278 263 /* There will be more! */ 279 req->inpa = b_input_add( 280 req->ssl ? ssl_getdirection( req->ssl) : B_EV_IO_READ,281 http_incoming_data, req);282 264 req->inpa = b_input_add(req->fd, 265 req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_READ, 266 http_incoming_data, req); 267 283 268 return FALSE; 284 269 285 270 eof: 286 271 req->flags |= HTTPC_EOF; 287 272 288 273 /* Maybe if the webserver is overloaded, or when there's bad SSL 289 274 support... */ 290 if( req->bytes_read == 0 ) 291 { 292 req->status_string = g_strdup( "Empty HTTP reply" ); 275 if (req->bytes_read == 0) { 276 req->status_string = g_strdup("Empty HTTP reply"); 293 277 goto cleanup; 294 278 } … … 298 282 req->inpa = 0; 299 283 300 if ( req->ssl )301 ssl_disconnect( req->ssl);302 else303 closesocket( req->fd);304 305 if( req->body_size < req->content_length ) 306 {284 if (req->ssl) { 285 ssl_disconnect(req->ssl); 286 } else { 287 closesocket(req->fd); 288 } 289 290 if (req->body_size < req->content_length) { 307 291 req->status_code = -1; 308 g_free( req->status_string ); 309 req->status_string = g_strdup( "Response truncated" ); 310 } 311 312 if( getenv( "BITLBEE_DEBUG" ) && req ) 313 printf( "Finishing HTTP request with status: %s\n", 314 req->status_string ? req->status_string : "NULL" ); 315 316 req->func( req ); 317 http_free( req ); 292 g_free(req->status_string); 293 req->status_string = g_strdup("Response truncated"); 294 } 295 296 if (getenv("BITLBEE_DEBUG") && req) { 297 printf("Finishing HTTP request with status: %s\n", 298 req->status_string ? req->status_string : "NULL"); 299 } 300 301 req->func(req); 302 http_free(req); 318 303 return FALSE; 319 304 } 320 305 321 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len)306 static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len) 322 307 { 323 308 char *chunk, *eos, *s; 324 325 if ( len < 0 )309 310 if (len < 0) { 326 311 return TRUE; 327 328 if( len > 0 ) 329 {330 req->cbuf = g_realloc( req->cbuf, req->cblen + len + 1);331 memcpy( req->cbuf + req->cblen, buffer, len);312 } 313 314 if (len > 0) { 315 req->cbuf = g_realloc(req->cbuf, req->cblen + len + 1); 316 memcpy(req->cbuf + req->cblen, buffer, len); 332 317 req->cblen += len; 333 318 req->cbuf[req->cblen] = '\0'; 334 319 } 335 320 336 321 /* Turns out writing a proper chunked-encoding state machine is not 337 322 that simple. :-( I've tested this one feeding it byte by byte so … … 339 324 chunk = req->cbuf; 340 325 eos = req->cbuf + req->cblen; 341 while( TRUE ) 342 { 326 while (TRUE) { 343 327 int clen = 0; 344 328 345 329 /* Might be a \r\n from the last chunk. */ 346 330 s = chunk; 347 while( g_ascii_isspace( *s ) ) 348 s ++; 331 while (g_ascii_isspace(*s)) { 332 s++; 333 } 349 334 /* Chunk length. Might be incomplete. */ 350 if ( s < eos && sscanf( s, "%x", &clen ) != 1 )335 if (s < eos && sscanf(s, "%x", &clen) != 1) { 351 336 return CR_ERROR; 352 while( g_ascii_isxdigit( *s ) ) 353 s ++; 354 337 } 338 while (g_ascii_isxdigit(*s)) { 339 s++; 340 } 341 355 342 /* If we read anything here, it *must* be \r\n. */ 356 if ( strncmp( s, "\r\n", MIN( 2, eos - s ) ) != 0 )343 if (strncmp(s, "\r\n", MIN(2, eos - s)) != 0) { 357 344 return CR_ERROR; 345 } 358 346 s += 2; 359 360 if ( s >= eos )347 348 if (s >= eos) { 361 349 break; 362 363 /* 0-length chunk means end of response. */ 364 if( clen == 0 ) 350 } 351 352 /* 0-length chunk means end of response. */ 353 if (clen == 0) { 365 354 return CR_EOF; 366 355 } 356 367 357 /* Wait for the whole chunk to arrive. */ 368 if ( s + clen > eos )358 if (s + clen > eos) { 369 359 break; 370 if( http_process_data( req, s, clen ) != CR_OK ) 360 } 361 if (http_process_data(req, s, clen) != CR_OK) { 371 362 return CR_ABORT; 372 363 } 364 373 365 chunk = s + clen; 374 366 } 375 376 if( chunk != req->cbuf ) 377 { 367 368 if (chunk != req->cbuf) { 378 369 req->cblen = eos - chunk; 379 s = g_memdup( chunk, req->cblen + 1);380 g_free( req->cbuf);370 s = g_memdup(chunk, req->cblen + 1); 371 g_free(req->cbuf); 381 372 req->cbuf = s; 382 373 } 383 374 384 375 return CR_OK; 385 376 } 386 377 387 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len)388 { 389 if ( len <= 0 )378 static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len) 379 { 380 if (len <= 0) { 390 381 return CR_OK; 391 392 if( !req->reply_body ) 393 {394 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + len + 1);395 memcpy( req->reply_headers + req->bytes_read, buffer, len);382 } 383 384 if (!req->reply_body) { 385 req->reply_headers = g_realloc(req->reply_headers, req->bytes_read + len + 1); 386 memcpy(req->reply_headers + req->bytes_read, buffer, len); 396 387 req->bytes_read += len; 397 388 req->reply_headers[req->bytes_read] = '\0'; 398 399 if( strstr( req->reply_headers, "\r\n\r\n" ) || 400 strstr( req->reply_headers, "\n\n" ) ) 401 { 389 390 if (strstr(req->reply_headers, "\r\n\r\n") || 391 strstr(req->reply_headers, "\n\n")) { 402 392 /* We've now received all headers. Look for something 403 393 interesting. */ 404 if ( !http_handle_headers( req ) )394 if (!http_handle_headers(req)) { 405 395 return CR_ABORT; 406 396 } 397 407 398 /* Start parsing the body as chunked if required. */ 408 if( req->flags & HTTPC_CHUNKED ) 409 return http_process_chunked_data( req, NULL, 0 ); 410 } 411 } 412 else 413 { 399 if (req->flags & HTTPC_CHUNKED) { 400 return http_process_chunked_data(req, NULL, 0); 401 } 402 } 403 } else { 414 404 int pos = req->reply_body - req->sbuf; 415 req->sbuf = g_realloc( req->sbuf, req->sblen + len + 1);416 memcpy( req->sbuf + req->sblen, buffer, len);405 req->sbuf = g_realloc(req->sbuf, req->sblen + len + 1); 406 memcpy(req->sbuf + req->sblen, buffer, len); 417 407 req->bytes_read += len; 418 408 req->sblen += len; … … 421 411 req->body_size = req->sblen - pos; 422 412 } 423 424 if( ( req->flags & HTTPC_STREAMING ) && req->reply_body ) 425 req->func( req ); 426 413 414 if ((req->flags & HTTPC_STREAMING) && req->reply_body) { 415 req->func(req); 416 } 417 427 418 return CR_OK; 428 419 } … … 430 421 /* Splits headers and body. Checks result code, in case of 300s it'll handle 431 422 redirects. If this returns FALSE, don't call any callbacks! */ 432 static gboolean http_handle_headers( struct http_request *req)423 static gboolean http_handle_headers(struct http_request *req) 433 424 { 434 425 char *end1, *end2, *s; 435 426 int evil_server = 0; 436 427 437 428 /* Zero termination is very convenient. */ 438 429 req->reply_headers[req->bytes_read] = '\0'; 439 430 440 431 /* Find the separation between headers and body, and keep stupid 441 432 webservers in mind. */ 442 end1 = strstr( req->reply_headers, "\r\n\r\n" ); 443 end2 = strstr( req->reply_headers, "\n\n" ); 444 445 if( end2 && end2 < end1 ) 446 { 433 end1 = strstr(req->reply_headers, "\r\n\r\n"); 434 end2 = strstr(req->reply_headers, "\n\n"); 435 436 if (end2 && end2 < end1) { 447 437 end1 = end2 + 1; 448 438 evil_server = 1; 449 } 450 else if( end1 ) 451 { 439 } else if (end1) { 452 440 end1 += 2; 453 } 454 else 455 { 456 req->status_string = g_strdup( "Malformed HTTP reply" ); 441 } else { 442 req->status_string = g_strdup("Malformed HTTP reply"); 457 443 return TRUE; 458 444 } 459 445 460 446 *end1 = '\0'; 461 462 if( getenv( "BITLBEE_DEBUG" ) ) 463 printf( "HTTP response headers:\n%s\n", req->reply_headers ); 464 465 if( evil_server ) 447 448 if (getenv("BITLBEE_DEBUG")) { 449 printf("HTTP response headers:\n%s\n", req->reply_headers); 450 } 451 452 if (evil_server) { 466 453 req->reply_body = end1 + 1; 467 else454 } else { 468 455 req->reply_body = end1 + 2; 469 456 } 457 470 458 /* Separately allocated space for headers and body. */ 471 459 req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body; 472 req->sbuf = req->reply_body = g_memdup( req->reply_body, req->body_size + 1 ); 473 req->reply_headers = g_realloc( req->reply_headers, end1 - req->reply_headers + 1 ); 474 475 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) 476 { 477 if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 ) 478 { 479 req->status_string = g_strdup( "Can't parse status code" ); 460 req->sbuf = req->reply_body = g_memdup(req->reply_body, req->body_size + 1); 461 req->reply_headers = g_realloc(req->reply_headers, end1 - req->reply_headers + 1); 462 463 if ((end1 = strchr(req->reply_headers, ' ')) != NULL) { 464 if (sscanf(end1 + 1, "%hd", &req->status_code) != 1) { 465 req->status_string = g_strdup("Can't parse status code"); 480 466 req->status_code = -1; 481 } 482 else 483 { 467 } else { 484 468 char *eol; 485 486 if( evil_server ) 487 eol = strchr( end1, '\n' ); 488 else 489 eol = strchr( end1, '\r' ); 490 491 req->status_string = g_strndup( end1 + 1, eol - end1 - 1 ); 492 469 470 if (evil_server) { 471 eol = strchr(end1, '\n'); 472 } else { 473 eol = strchr(end1, '\r'); 474 } 475 476 req->status_string = g_strndup(end1 + 1, eol - end1 - 1); 477 493 478 /* Just to be sure... */ 494 if ( ( eol = strchr( req->status_string, '\r' ) ) )479 if ((eol = strchr(req->status_string, '\r'))) { 495 480 *eol = 0; 496 if( ( eol = strchr( req->status_string, '\n' ) ) ) 481 } 482 if ((eol = strchr(req->status_string, '\n'))) { 497 483 *eol = 0; 498 } 499 } 500 else 501 { 502 req->status_string = g_strdup( "Can't locate status code" ); 484 } 485 } 486 } else { 487 req->status_string = g_strdup("Can't locate status code"); 503 488 req->status_code = -1; 504 489 } 505 506 if( ( ( req->status_code >= 301 && req->status_code <= 303 ) || 507 req->status_code == 307 ) && req->redir_ttl-- > 0 ) 508 { 490 491 if (((req->status_code >= 301 && req->status_code <= 303) || 492 req->status_code == 307) && req->redir_ttl-- > 0) { 509 493 char *loc, *new_request, *new_host; 510 494 int error = 0, new_port, new_proto; 511 495 512 496 /* We might fill it again, so let's not leak any memory. */ 513 g_free( req->status_string);497 g_free(req->status_string); 514 498 req->status_string = NULL; 515 516 loc = strstr( req->reply_headers, "\nLocation: " ); 517 if( loc == NULL ) /* We can't handle this redirect... */ 518 { 519 req->status_string = g_strdup( "Can't locate Location: header" ); 499 500 loc = strstr(req->reply_headers, "\nLocation: "); 501 if (loc == NULL) { /* We can't handle this redirect... */ 502 req->status_string = g_strdup("Can't locate Location: header"); 520 503 return TRUE; 521 504 } 522 505 523 506 loc += 11; 524 while( *loc == ' ' ) 525 loc ++; 526 507 while (*loc == ' ') { 508 loc++; 509 } 510 527 511 /* TODO/FIXME: Possibly have to handle relative redirections, 528 512 and rewrite Host: headers. Not necessary for now, it's 529 513 enough for passport authentication like this. */ 530 531 if( *loc == '/' ) 532 { 514 515 if (*loc == '/') { 533 516 /* Just a different pathname... */ 534 517 535 518 /* Since we don't cache the servername, and since we 536 519 don't need this yet anyway, I won't implement it. */ 537 538 req->status_string = g_strdup( "Can't handle relative redirects");539 520 521 req->status_string = g_strdup("Can't handle relative redirects"); 522 540 523 return TRUE; 541 } 542 else 543 { 524 } else { 544 525 /* A whole URL */ 545 526 url_t *url; 546 527 char *s, *version, *headers; 547 528 const char *new_method; 548 549 s = strstr( loc, "\r\n");550 if ( s == NULL )529 530 s = strstr(loc, "\r\n"); 531 if (s == NULL) { 551 532 return TRUE; 552 553 url = g_new0( url_t, 1 ); 533 } 534 535 url = g_new0(url_t, 1); 554 536 *s = 0; 555 556 if( !url_set( url, loc ) ) 557 { 558 req->status_string = g_strdup( "Malformed redirect URL" ); 559 g_free( url ); 537 538 if (!url_set(url, loc)) { 539 req->status_string = g_strdup("Malformed redirect URL"); 540 g_free(url); 560 541 return TRUE; 561 542 } 562 543 563 544 /* Find all headers and, if necessary, the POST request contents. 564 545 Skip the old Host: header though. This crappy code here means 565 546 anything using this http_client MUST put the Host: header at 566 547 the top. */ 567 if( !( ( s = strstr( req->request, "\r\nHost: " ) ) && 568 ( s = strstr( s + strlen( "\r\nHost: " ), "\r\n" ) ) ) ) 569 { 570 req->status_string = g_strdup( "Error while rebuilding request string" ); 571 g_free( url ); 548 if (!((s = strstr(req->request, "\r\nHost: ")) && 549 (s = strstr(s + strlen("\r\nHost: "), "\r\n")))) { 550 req->status_string = g_strdup("Error while rebuilding request string"); 551 g_free(url); 572 552 return TRUE; 573 553 } 574 554 headers = s; 575 555 576 556 /* More or less HTTP/1.0 compliant, from my reading of RFC 2616. 577 557 Always perform a GET request unless we received a 301. 303 was 578 558 meant for this but it's HTTP/1.1-only and we're specifically 579 559 speaking HTTP/1.0. ... 580 560 581 561 Well except someone at identi.ca's didn't bother reading any 582 562 RFCs and just return HTTP/1.1-specific status codes to HTTP/1.0 583 563 requests. Fuckers. So here we are, handle 301..303,307. */ 584 if ( strncmp( req->request, "GET", 3 ) == 0 )564 if (strncmp(req->request, "GET", 3) == 0) { 585 565 /* GETs never become POSTs. */ 586 566 new_method = "GET"; 587 else if( req->status_code == 302 || req->status_code == 303 )567 } else if (req->status_code == 302 || req->status_code == 303) { 588 568 /* 302 de-facto becomes GET, 303 as specified by RFC 2616#10.3.3 */ 589 569 new_method = "GET"; 590 else570 } else { 591 571 /* 301 de-facto should stay POST, 307 specifally RFC 2616#10.3.8 */ 592 572 new_method = "POST"; 593 594 if( ( version = strstr( req->request, " HTTP/" ) ) && 595 ( s = strstr( version, "\r\n" ) ) )596 {597 version 598 version = g_strndup( version, s - version);599 } 600 else601 version = g_strdup( "HTTP/1.0" );602 573 } 574 575 if ((version = strstr(req->request, " HTTP/")) && 576 (s = strstr(version, "\r\n"))) { 577 version++; 578 version = g_strndup(version, s - version); 579 } else { 580 version = g_strdup("HTTP/1.0"); 581 } 582 603 583 /* Okay, this isn't fun! We have to rebuild the request... :-( */ 604 new_request = g_strdup_printf( 605 606 url->host, headers);607 608 new_host = g_strdup( url->host);584 new_request = g_strdup_printf("%s %s %s\r\nHost: %s%s", 585 new_method, url->file, version, 586 url->host, headers); 587 588 new_host = g_strdup(url->host); 609 589 new_port = url->port; 610 590 new_proto = url->proto; 611 591 612 592 /* If we went from POST to GET, truncate the request content. */ 613 if (new_request[0] != req->request[0] && new_request[0] == 'G' &&614 ( s = strstr( new_request, "\r\n\r\n" ) ) )593 if (new_request[0] != req->request[0] && new_request[0] == 'G' && 594 (s = strstr(new_request, "\r\n\r\n"))) { 615 595 s[4] = '\0'; 616 617 g_free( url ); 618 g_free( version ); 619 } 620 621 if( req->ssl ) 622 ssl_disconnect( req->ssl ); 623 else 624 closesocket( req->fd ); 625 596 } 597 598 g_free(url); 599 g_free(version); 600 } 601 602 if (req->ssl) { 603 ssl_disconnect(req->ssl); 604 } else { 605 closesocket(req->fd); 606 } 607 626 608 req->fd = -1; 627 609 req->ssl = NULL; 628 629 if ( getenv( "BITLBEE_DEBUG" ) )630 printf( "New headers for redirected HTTP request:\n%s\n", new_request);631 632 if( new_proto == PROTO_HTTPS ) 633 {634 req->ssl = ssl_connect( new_host, new_port, TRUE, http_ssl_connected, req);635 if ( req->ssl == NULL )610 611 if (getenv("BITLBEE_DEBUG")) { 612 printf("New headers for redirected HTTP request:\n%s\n", new_request); 613 } 614 615 if (new_proto == PROTO_HTTPS) { 616 req->ssl = ssl_connect(new_host, new_port, TRUE, http_ssl_connected, req); 617 if (req->ssl == NULL) { 636 618 error = 1; 637 } 638 else 639 { 640 req->fd = proxy_connect( new_host, new_port, http_connected, req ); 641 if( req->fd < 0 ) 619 } 620 } else { 621 req->fd = proxy_connect(new_host, new_port, http_connected, req); 622 if (req->fd < 0) { 642 623 error = 1; 643 }644 g_free( new_host );645 646 if( error ) 647 {648 req->status_string = g_strdup( "Connection problem during redirect");649 g_free( new_request);624 } 625 } 626 g_free(new_host); 627 628 if (error) { 629 req->status_string = g_strdup("Connection problem during redirect"); 630 g_free(new_request); 650 631 return TRUE; 651 632 } 652 653 g_free( req->request);654 g_free( req->reply_headers);655 g_free( req->sbuf);633 634 g_free(req->request); 635 g_free(req->reply_headers); 636 g_free(req->sbuf); 656 637 req->request = new_request; 657 req->request_length = strlen( new_request);638 req->request_length = strlen(new_request); 658 639 req->bytes_read = req->bytes_written = req->inpa = 0; 659 640 req->reply_headers = req->reply_body = NULL; 660 641 req->sbuf = req->cbuf = NULL; 661 642 req->sblen = req->cblen = 0; 662 643 663 644 return FALSE; 664 645 } 665 646 666 if ( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 )) &&667 sscanf( s, "%d", &req->content_length ) != 1 )647 if ((s = get_rfc822_header(req->reply_headers, "Content-Length", 0)) && 648 sscanf(s, "%d", &req->content_length) != 1) { 668 649 req->content_length = -1; 669 g_free( s ); 670 671 if( ( s = get_rfc822_header( req->reply_headers, "Transfer-Encoding", 0 ) ) ) 672 { 673 if( strcasestr( s, "chunked" ) ) 674 { 650 } 651 g_free(s); 652 653 if ((s = get_rfc822_header(req->reply_headers, "Transfer-Encoding", 0))) { 654 if (strcasestr(s, "chunked")) { 675 655 req->flags |= HTTPC_CHUNKED; 676 656 req->cbuf = req->sbuf; 677 657 req->cblen = req->sblen; 678 679 req->reply_body = req->sbuf = g_strdup( "");658 659 req->reply_body = req->sbuf = g_strdup(""); 680 660 req->body_size = req->sblen = 0; 681 661 } 682 g_free( s);683 } 684 662 g_free(s); 663 } 664 685 665 return TRUE; 686 666 } 687 667 688 void http_flush_bytes( struct http_request *req, size_t len)689 { 690 if ( len <= 0 || len > req->body_size || !( req->flags & HTTPC_STREAMING ) )668 void http_flush_bytes(struct http_request *req, size_t len) 669 { 670 if (len <= 0 || len > req->body_size || !(req->flags & HTTPC_STREAMING)) { 691 671 return; 692 672 } 673 693 674 req->reply_body += len; 694 675 req->body_size -= len; 695 696 if( req->reply_body - req->sbuf >= 512 ) 697 { 698 char *new = g_memdup( req->reply_body, req->body_size + 1 ); 699 g_free( req->sbuf ); 676 677 if (req->reply_body - req->sbuf >= 512) { 678 char *new = g_memdup(req->reply_body, req->body_size + 1); 679 g_free(req->sbuf); 700 680 req->reply_body = req->sbuf = new; 701 681 req->sblen = req->body_size; … … 703 683 } 704 684 705 void http_close( struct http_request *req)706 { 707 if ( !req )685 void http_close(struct http_request *req) 686 { 687 if (!req) { 708 688 return; 709 710 if( req->inpa > 0 ) 711 b_event_remove( req->inpa ); 712 713 if( req->ssl ) 714 ssl_disconnect( req->ssl ); 715 else 716 closesocket( req->fd ); 717 718 http_free( req ); 719 } 720 721 static void http_free( struct http_request *req ) 722 { 723 g_free( req->request ); 724 g_free( req->reply_headers ); 725 g_free( req->status_string ); 726 g_free( req->sbuf ); 727 g_free( req->cbuf ); 728 g_free( req ); 729 } 689 } 690 691 if (req->inpa > 0) { 692 b_event_remove(req->inpa); 693 } 694 695 if (req->ssl) { 696 ssl_disconnect(req->ssl); 697 } else { 698 closesocket(req->fd); 699 } 700 701 http_free(req); 702 } 703 704 static void http_free(struct http_request *req) 705 { 706 g_free(req->request); 707 g_free(req->reply_headers); 708 g_free(req->status_string); 709 g_free(req->sbuf); 710 g_free(req->cbuf); 711 g_free(req); 712 }
Note: See TracChangeset
for help on using the changeset viewer.