| 3216 } |
3216 } |
| 3217 |
3217 |
| 3218 return content_len; |
3218 return content_len; |
| 3219 } |
3219 } |
| 3220 |
3220 |
| |
3221 |
| 3221 static void |
3222 static void |
| 3222 url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond) |
3223 url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond) |
| 3223 { |
3224 { |
| 3224 GaimFetchUrlData *gfud = url_data; |
3225 GaimFetchUrlData *gfud = url_data; |
| 3225 int len; |
3226 int len; |
| 3226 char buf[4096]; |
3227 char buf[4096]; |
| 3227 char *data_cursor; |
3228 char *data_cursor; |
| 3228 gboolean got_eof = FALSE; |
3229 gboolean got_eof = FALSE; |
| 3229 |
3230 |
| 3230 if (sock == -1) |
3231 while((len = read(sock, buf, sizeof(buf))) > 0) { |
| 3231 { |
|
| 3232 gfud->callback(gfud->user_data, NULL, 0); |
|
| 3233 |
|
| 3234 destroy_fetch_url_data(gfud); |
|
| 3235 |
|
| 3236 return; |
|
| 3237 } |
|
| 3238 |
|
| 3239 if (!gfud->sentreq) |
|
| 3240 { |
|
| 3241 char *send; |
|
| 3242 char buf[1024]; |
|
| 3243 |
|
| 3244 if (gfud->request) { |
|
| 3245 send = gfud->request; |
|
| 3246 } else { |
|
| 3247 if (gfud->user_agent) { |
|
| 3248 /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 |
|
| 3249 * clients must know how to handle the "chunked" transfer encoding. |
|
| 3250 * Gaim doesn't know how to handle "chunked", so should always send |
|
| 3251 * the Host header regardless, to get around some observed problems |
|
| 3252 */ |
|
| 3253 g_snprintf(buf, sizeof(buf), |
|
| 3254 "GET %s%s HTTP/%s\r\n" |
|
| 3255 "Connection: close\r\n" |
|
| 3256 "User-Agent: %s\r\n" |
|
| 3257 "Host: %s\r\n\r\n", |
|
| 3258 (gfud->full ? "" : "/"), |
|
| 3259 (gfud->full ? gfud->url : gfud->website.page), |
|
| 3260 (gfud->http11 ? "1.1" : "1.0"), |
|
| 3261 gfud->user_agent, gfud->website.address); |
|
| 3262 } else { |
|
| 3263 g_snprintf(buf, sizeof(buf), |
|
| 3264 "GET %s%s HTTP/%s\r\n" |
|
| 3265 "Connection: close\r\n" |
|
| 3266 "Host: %s\r\n\r\n", |
|
| 3267 (gfud->full ? "" : "/"), |
|
| 3268 (gfud->full ? gfud->url : gfud->website.page), |
|
| 3269 (gfud->http11 ? "1.1" : "1.0"), |
|
| 3270 gfud->website.address); |
|
| 3271 } |
|
| 3272 send = buf; |
|
| 3273 } |
|
| 3274 |
|
| 3275 gaim_debug_misc("gaim_url_fetch", "Request: %s\n", send); |
|
| 3276 |
|
| 3277 write(sock, send, strlen(send)); |
|
| 3278 fcntl(sock, F_SETFL, O_NONBLOCK); |
|
| 3279 gfud->sentreq = TRUE; |
|
| 3280 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, |
|
| 3281 url_fetched_cb, url_data); |
|
| 3282 gfud->data_len = 4096; |
|
| 3283 gfud->webdata = g_malloc(gfud->data_len); |
|
| 3284 |
|
| 3285 return; |
|
| 3286 } |
|
| 3287 |
|
| 3288 while ((len = read(sock, buf, sizeof(buf))) > 0) |
|
| 3289 { |
|
| 3290 /* If we've filled up our butfer, make it bigger */ |
3232 /* If we've filled up our butfer, make it bigger */ |
| 3291 if ((gfud->len + len) >= gfud->data_len) |
3233 if((gfud->len + len) >= gfud->data_len) { |
| 3292 { |
3234 while((gfud->len + len) >= gfud->data_len) |
| 3293 gfud->data_len += MAX(((gfud->data_len) / 2), sizeof(buf)); |
3235 gfud->data_len += sizeof(buf); |
| 3294 |
3236 |
| 3295 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); |
3237 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); |
| 3296 } |
3238 } |
| 3297 |
3239 |
| 3298 data_cursor = gfud->webdata + gfud->len; |
3240 data_cursor = gfud->webdata + gfud->len; |
| 3301 |
3243 |
| 3302 memcpy(data_cursor, buf, len); |
3244 memcpy(data_cursor, buf, len); |
| 3303 |
3245 |
| 3304 gfud->webdata[gfud->len] = '\0'; |
3246 gfud->webdata[gfud->len] = '\0'; |
| 3305 |
3247 |
| 3306 if (!gfud->startsaving) |
3248 if(!gfud->got_headers) { |
| 3307 { |
|
| 3308 char *tmp; |
3249 char *tmp; |
| 3309 |
3250 |
| 3310 /** See if we've reached the end of the headers yet */ |
3251 /** See if we've reached the end of the headers yet */ |
| 3311 if ((tmp = strstr(gfud->webdata, "\r\n\r\n"))) { |
3252 if((tmp = strstr(gfud->webdata, "\r\n\r\n"))) { |
| 3312 char * new_data; |
3253 char * new_data; |
| 3313 guint header_len = (tmp + 4 - gfud->webdata); |
3254 guint header_len = (tmp + 4 - gfud->webdata); |
| 3314 size_t content_len, body_len = 0; |
3255 size_t content_len, body_len = 0; |
| 3315 |
3256 |
| 3316 gaim_debug_misc("gaim_url_fetch", "Response headers: '%.*s'\n", |
3257 gaim_debug_misc("gaim_url_fetch", "Response headers: '%.*s'\n", |
| 3318 |
3259 |
| 3319 /* See if we can find a redirect. */ |
3260 /* See if we can find a redirect. */ |
| 3320 if (parse_redirect(gfud->webdata, header_len, sock, gfud)) |
3261 if (parse_redirect(gfud->webdata, header_len, sock, gfud)) |
| 3321 return; |
3262 return; |
| 3322 |
3263 |
| 3323 gfud->startsaving = TRUE; |
3264 gfud->got_headers = TRUE; |
| 3324 |
3265 |
| 3325 /* No redirect. See if we can find a content length. */ |
3266 /* No redirect. See if we can find a content length. */ |
| 3326 content_len = parse_content_len(gfud->webdata, header_len); |
3267 content_len = parse_content_len(gfud->webdata, header_len); |
| 3327 |
3268 |
| 3328 if (content_len == 0) |
3269 if (content_len == 0) |
| 3410 close(sock); |
3350 close(sock); |
| 3411 gfud->callback(gfud->user_data, gfud->webdata, gfud->len); |
3351 gfud->callback(gfud->user_data, gfud->webdata, gfud->len); |
| 3412 |
3352 |
| 3413 destroy_fetch_url_data(gfud); |
3353 destroy_fetch_url_data(gfud); |
| 3414 } |
3354 } |
| |
3355 } |
| |
3356 |
| |
3357 static void |
| |
3358 url_fetch_connect_cb(gpointer url_data, gint sock, GaimInputCondition cond) { |
| |
3359 GaimFetchUrlData *gfud = url_data; |
| |
3360 int len, total_len; |
| |
3361 |
| |
3362 if(sock == -1) { |
| |
3363 gfud->callback(gfud->user_data, NULL, 0); |
| |
3364 destroy_fetch_url_data(gfud); |
| |
3365 return; |
| |
3366 } |
| |
3367 |
| |
3368 if (!gfud->request) { |
| |
3369 if (gfud->user_agent) { |
| |
3370 /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 |
| |
3371 * clients must know how to handle the "chunked" transfer encoding. |
| |
3372 * Gaim doesn't know how to handle "chunked", so should always send |
| |
3373 * the Host header regardless, to get around some observed problems |
| |
3374 */ |
| |
3375 gfud->request = g_strdup_printf( |
| |
3376 "GET %s%s HTTP/%s\r\n" |
| |
3377 "Connection: close\r\n" |
| |
3378 "User-Agent: %s\r\n" |
| |
3379 "Host: %s\r\n\r\n", |
| |
3380 (gfud->full ? "" : "/"), |
| |
3381 (gfud->full ? gfud->url : gfud->website.page), |
| |
3382 (gfud->http11 ? "1.1" : "1.0"), |
| |
3383 gfud->user_agent, gfud->website.address); |
| |
3384 } else { |
| |
3385 gfud->request = g_strdup_printf( |
| |
3386 "GET %s%s HTTP/%s\r\n" |
| |
3387 "Connection: close\r\n" |
| |
3388 "Host: %s\r\n\r\n", |
| |
3389 (gfud->full ? "" : "/"), |
| |
3390 (gfud->full ? gfud->url : gfud->website.page), |
| |
3391 (gfud->http11 ? "1.1" : "1.0"), |
| |
3392 gfud->website.address); |
| |
3393 } |
| |
3394 } |
| |
3395 |
| |
3396 gaim_debug_misc("gaim_url_fetch", "Request: '%s'\n", gfud->request); |
| |
3397 |
| |
3398 if(!gfud->inpa) |
| |
3399 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_WRITE, |
| |
3400 url_fetch_connect_cb, gfud); |
| |
3401 |
| |
3402 total_len = strlen(gfud->request); |
| |
3403 |
| |
3404 len = write(sock, gfud->request + gfud->request_written, |
| |
3405 total_len - gfud->request_written); |
| |
3406 |
| |
3407 if(len < 0 && errno == EAGAIN) |
| |
3408 return; |
| |
3409 else if(len < 0) { |
| |
3410 gaim_input_remove(gfud->inpa); |
| |
3411 close(sock); |
| |
3412 gfud->callback(gfud->user_data, NULL, 0); |
| |
3413 destroy_fetch_url_data(gfud); |
| |
3414 return; |
| |
3415 } |
| |
3416 gfud->request_written += len; |
| |
3417 |
| |
3418 if(gfud->request_written != total_len) |
| |
3419 return; |
| |
3420 |
| |
3421 gaim_input_remove(gfud->inpa); |
| |
3422 |
| |
3423 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, url_fetched_cb, |
| |
3424 gfud); |
| 3415 } |
3425 } |
| 3416 |
3426 |
| 3417 void |
3427 void |
| 3418 gaim_url_fetch_request(const char *url, gboolean full, |
3428 gaim_url_fetch_request(const char *url, gboolean full, |
| 3419 const char *user_agent, gboolean http11, |
3429 const char *user_agent, gboolean http11, |
| 3442 |
3452 |
| 3443 gaim_url_parse(url, &gfud->website.address, &gfud->website.port, |
3453 gaim_url_parse(url, &gfud->website.address, &gfud->website.port, |
| 3444 &gfud->website.page, &gfud->website.user, &gfud->website.passwd); |
3454 &gfud->website.page, &gfud->website.user, &gfud->website.passwd); |
| 3445 |
3455 |
| 3446 if (gaim_proxy_connect(NULL, gfud->website.address, |
3456 if (gaim_proxy_connect(NULL, gfud->website.address, |
| 3447 gfud->website.port, url_fetched_cb, |
3457 gfud->website.port, url_fetch_connect_cb, gfud) != 0) { |
| 3448 gfud) != 0) |
|
| 3449 { |
|
| 3450 destroy_fetch_url_data(gfud); |
3458 destroy_fetch_url_data(gfud); |
| 3451 |
3459 |
| 3452 cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0); |
3460 cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0); |
| 3453 } |
3461 } |
| 3454 } |
3462 } |
| 3755 out = g_string_new(""); |
3763 out = g_string_new(""); |
| 3756 |
3764 |
| 3757 while( (b = strstr(buf, "&#")) ) { |
3765 while( (b = strstr(buf, "&#")) ) { |
| 3758 gunichar wc; |
3766 gunichar wc; |
| 3759 int base = 0; |
3767 int base = 0; |
| 3760 |
3768 |
| 3761 /* append everything leading up to the &# */ |
3769 /* append everything leading up to the &# */ |
| 3762 g_string_append_len(out, buf, b-buf); |
3770 g_string_append_len(out, buf, b-buf); |
| 3763 |
3771 |
| 3764 b += 2; /* skip past the &# */ |
3772 b += 2; /* skip past the &# */ |
| 3765 |
3773 |
| 3766 /* strtoul will handle 0x prefix as hex, but not x */ |
3774 /* strtoul will handle 0x prefix as hex, but not x */ |
| 3767 if(*b == 'x' || *b == 'X') |
3775 if(*b == 'x' || *b == 'X') |
| 3768 base = 16; |
3776 base = 16; |
| 3769 |
3777 |
| 3770 /* advances buf to the end of the ncr segment */ |
3778 /* advances buf to the end of the ncr segment */ |