| 4004 #undef PAGE_CTRL |
3963 #undef PAGE_CTRL |
| 4005 #undef USER_CTRL |
3964 #undef USER_CTRL |
| 4006 #undef PASSWD_CTRL |
3965 #undef PASSWD_CTRL |
| 4007 } |
3966 } |
| 4008 |
3967 |
| 4009 /** |
|
| 4010 * The arguments to this function are similar to printf. |
|
| 4011 */ |
|
| 4012 static void |
|
| 4013 purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...) |
|
| 4014 { |
|
| 4015 gchar *error_message; |
|
| 4016 va_list args; |
|
| 4017 |
|
| 4018 va_start(args, format); |
|
| 4019 error_message = g_strdup_vprintf(format, args); |
|
| 4020 va_end(args); |
|
| 4021 |
|
| 4022 gfud->callback(gfud, gfud->user_data, NULL, 0, error_message); |
|
| 4023 g_free(error_message); |
|
| 4024 purple_util_fetch_url_cancel(gfud); |
|
| 4025 } |
|
| 4026 |
|
| 4027 static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); |
|
| 4028 static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond); |
|
| 4029 static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data); |
|
| 4030 |
|
| 4031 static gboolean |
|
| 4032 parse_redirect(const char *data, size_t data_len, |
|
| 4033 PurpleUtilFetchUrlData *gfud) |
|
| 4034 { |
|
| 4035 gchar *s; |
|
| 4036 gchar *new_url, *temp_url, *end; |
|
| 4037 gboolean full; |
|
| 4038 int len; |
|
| 4039 |
|
| 4040 if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL) |
|
| 4041 /* We're not being redirected */ |
|
| 4042 return FALSE; |
|
| 4043 |
|
| 4044 s += strlen("Location: "); |
|
| 4045 end = strchr(s, '\r'); |
|
| 4046 |
|
| 4047 /* Just in case :) */ |
|
| 4048 if (end == NULL) |
|
| 4049 end = strchr(s, '\n'); |
|
| 4050 |
|
| 4051 if (end == NULL) |
|
| 4052 return FALSE; |
|
| 4053 |
|
| 4054 len = end - s; |
|
| 4055 |
|
| 4056 new_url = g_malloc(len + 1); |
|
| 4057 strncpy(new_url, s, len); |
|
| 4058 new_url[len] = '\0'; |
|
| 4059 |
|
| 4060 full = gfud->full; |
|
| 4061 |
|
| 4062 if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) |
|
| 4063 { |
|
| 4064 temp_url = new_url; |
|
| 4065 |
|
| 4066 new_url = g_strdup_printf("%s:%d%s", gfud->website.address, |
|
| 4067 gfud->website.port, temp_url); |
|
| 4068 |
|
| 4069 g_free(temp_url); |
|
| 4070 |
|
| 4071 full = FALSE; |
|
| 4072 } |
|
| 4073 |
|
| 4074 purple_debug_info("util", "Redirecting to %s\n", new_url); |
|
| 4075 |
|
| 4076 gfud->num_times_redirected++; |
|
| 4077 if (gfud->num_times_redirected >= 5) |
|
| 4078 { |
|
| 4079 purple_util_fetch_url_error(gfud, |
|
| 4080 _("Could not open %s: Redirected too many times"), |
|
| 4081 gfud->url); |
|
| 4082 return TRUE; |
|
| 4083 } |
|
| 4084 |
|
| 4085 /* |
|
| 4086 * Try again, with this new location. This code is somewhat |
|
| 4087 * ugly, but we need to reuse the gfud because whoever called |
|
| 4088 * us is holding a reference to it. |
|
| 4089 */ |
|
| 4090 g_free(gfud->url); |
|
| 4091 gfud->url = new_url; |
|
| 4092 gfud->full = full; |
|
| 4093 g_free(gfud->request); |
|
| 4094 gfud->request = NULL; |
|
| 4095 |
|
| 4096 if (gfud->is_ssl) { |
|
| 4097 gfud->is_ssl = FALSE; |
|
| 4098 purple_ssl_close(gfud->ssl_connection); |
|
| 4099 gfud->ssl_connection = NULL; |
|
| 4100 } else { |
|
| 4101 purple_input_remove(gfud->inpa); |
|
| 4102 gfud->inpa = 0; |
|
| 4103 close(gfud->fd); |
|
| 4104 gfud->fd = -1; |
|
| 4105 } |
|
| 4106 gfud->request_written = 0; |
|
| 4107 gfud->len = 0; |
|
| 4108 gfud->data_len = 0; |
|
| 4109 |
|
| 4110 g_free(gfud->website.user); |
|
| 4111 g_free(gfud->website.passwd); |
|
| 4112 g_free(gfud->website.address); |
|
| 4113 g_free(gfud->website.page); |
|
| 4114 purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, |
|
| 4115 &gfud->website.page, &gfud->website.user, &gfud->website.passwd); |
|
| 4116 |
|
| 4117 if (purple_strcasestr(new_url, "https://") != NULL) { |
|
| 4118 gfud->is_ssl = TRUE; |
|
| 4119 gfud->ssl_connection = purple_ssl_connect(gfud->account, |
|
| 4120 gfud->website.address, gfud->website.port, |
|
| 4121 ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); |
|
| 4122 } else { |
|
| 4123 gfud->connect_data = purple_proxy_connect(NULL, gfud->account, |
|
| 4124 gfud->website.address, gfud->website.port, |
|
| 4125 url_fetch_connect_cb, gfud); |
|
| 4126 } |
|
| 4127 |
|
| 4128 if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) |
|
| 4129 { |
|
| 4130 purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), |
|
| 4131 gfud->website.address); |
|
| 4132 } |
|
| 4133 |
|
| 4134 return TRUE; |
|
| 4135 } |
|
| 4136 |
|
| 4137 static const char * |
|
| 4138 find_header_content(const char *data, size_t data_len, const char *header, size_t header_len) |
|
| 4139 { |
|
| 4140 const char *p = NULL; |
|
| 4141 |
|
| 4142 if (header_len <= 0) |
|
| 4143 header_len = strlen(header); |
|
| 4144 |
|
| 4145 /* Note: data is _not_ nul-terminated. */ |
|
| 4146 if (data_len > header_len) { |
|
| 4147 if (header[0] == '\n') |
|
| 4148 p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL; |
|
| 4149 if (!p) |
|
| 4150 p = purple_strcasestr(data, header); |
|
| 4151 if (p) |
|
| 4152 p += header_len; |
|
| 4153 } |
|
| 4154 |
|
| 4155 /* If we can find the header at all, try to sscanf it. |
|
| 4156 * Response headers should end with at least \r\n, so sscanf is safe, |
|
| 4157 * if we make sure that there is indeed a \n in our header. |
|
| 4158 */ |
|
| 4159 if (p && g_strstr_len(p, data_len - (p - data), "\n")) { |
|
| 4160 return p; |
|
| 4161 } |
|
| 4162 |
|
| 4163 return NULL; |
|
| 4164 } |
|
| 4165 |
|
| 4166 static size_t |
|
| 4167 parse_content_len(const char *data, size_t data_len) |
|
| 4168 { |
|
| 4169 size_t content_len = 0; |
|
| 4170 const char *p = NULL; |
|
| 4171 |
|
| 4172 p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1); |
|
| 4173 if (p) { |
|
| 4174 sscanf(p, "%" G_GSIZE_FORMAT, &content_len); |
|
| 4175 purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len); |
|
| 4176 } |
|
| 4177 |
|
| 4178 return content_len; |
|
| 4179 } |
|
| 4180 |
|
| 4181 static gboolean |
|
| 4182 content_is_chunked(const char *data, size_t data_len) |
|
| 4183 { |
|
| 4184 const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1); |
|
| 4185 if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0) |
|
| 4186 return TRUE; |
|
| 4187 |
|
| 4188 return FALSE; |
|
| 4189 } |
|
| 4190 |
|
| 4191 /* Process in-place */ |
|
| 4192 static void |
|
| 4193 process_chunked_data(char *data, gsize *len) |
|
| 4194 { |
|
| 4195 gsize sz; |
|
| 4196 gsize newlen = 0; |
|
| 4197 char *p = data; |
|
| 4198 char *s = data; |
|
| 4199 |
|
| 4200 while (*s) { |
|
| 4201 /* Read the size of this chunk */ |
|
| 4202 if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1) |
|
| 4203 { |
|
| 4204 purple_debug_error("util", "Error processing chunked data: " |
|
| 4205 "Expected data length, found: %s\n", s); |
|
| 4206 break; |
|
| 4207 } |
|
| 4208 if (sz == 0) { |
|
| 4209 /* We've reached the last chunk */ |
|
| 4210 /* |
|
| 4211 * TODO: The spec allows "footers" to follow the last chunk. |
|
| 4212 * If there is more data after this line then we should |
|
| 4213 * treat it like a header. |
|
| 4214 */ |
|
| 4215 break; |
|
| 4216 } |
|
| 4217 |
|
| 4218 /* Advance to the start of the data */ |
|
| 4219 s = strstr(s, "\r\n"); |
|
| 4220 if (s == NULL) |
|
| 4221 break; |
|
| 4222 s += 2; |
|
| 4223 |
|
| 4224 if (s + sz > data + *len) { |
|
| 4225 purple_debug_error("util", "Error processing chunked data: " |
|
| 4226 "Chunk size %" G_GSIZE_FORMAT " bytes was longer " |
|
| 4227 "than the data remaining in the buffer (%" |
|
| 4228 G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s); |
|
| 4229 } |
|
| 4230 |
|
| 4231 /* Move all data overtop of the chunk length that we read in earlier */ |
|
| 4232 g_memmove(p, s, sz); |
|
| 4233 p += sz; |
|
| 4234 s += sz; |
|
| 4235 newlen += sz; |
|
| 4236 if (*s != '\r' && *(s + 1) != '\n') { |
|
| 4237 purple_debug_error("util", "Error processing chunked data: " |
|
| 4238 "Expected \\r\\n, found: %s\n", s); |
|
| 4239 break; |
|
| 4240 } |
|
| 4241 s += 2; |
|
| 4242 } |
|
| 4243 |
|
| 4244 /* NULL terminate the data */ |
|
| 4245 *p = 0; |
|
| 4246 |
|
| 4247 *len = newlen; |
|
| 4248 } |
|
| 4249 |
|
| 4250 static void |
|
| 4251 url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond) |
|
| 4252 { |
|
| 4253 PurpleUtilFetchUrlData *gfud = url_data; |
|
| 4254 int len; |
|
| 4255 char buf[4096]; |
|
| 4256 char *data_cursor; |
|
| 4257 gboolean got_eof = FALSE; |
|
| 4258 |
|
| 4259 /* |
|
| 4260 * Read data in a loop until we can't read any more! This is a |
|
| 4261 * little confusing because we read using a different function |
|
| 4262 * depending on whether the socket is ssl or cleartext. |
|
| 4263 */ |
|
| 4264 while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) || |
|
| 4265 (!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0)) |
|
| 4266 { |
|
| 4267 if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) { |
|
| 4268 purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"), |
|
| 4269 gfud->website.address, gfud->max_len); |
|
| 4270 return; |
|
| 4271 } |
|
| 4272 |
|
| 4273 /* If we've filled up our buffer, make it bigger */ |
|
| 4274 if((gfud->len + len) >= gfud->data_len) { |
|
| 4275 while((gfud->len + len) >= gfud->data_len) |
|
| 4276 gfud->data_len += sizeof(buf); |
|
| 4277 |
|
| 4278 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); |
|
| 4279 } |
|
| 4280 |
|
| 4281 data_cursor = gfud->webdata + gfud->len; |
|
| 4282 |
|
| 4283 gfud->len += len; |
|
| 4284 |
|
| 4285 memcpy(data_cursor, buf, len); |
|
| 4286 |
|
| 4287 gfud->webdata[gfud->len] = '\0'; |
|
| 4288 |
|
| 4289 if(!gfud->got_headers) { |
|
| 4290 char *end_of_headers; |
|
| 4291 |
|
| 4292 /* See if we've reached the end of the headers yet */ |
|
| 4293 end_of_headers = strstr(gfud->webdata, "\r\n\r\n"); |
|
| 4294 if (end_of_headers) { |
|
| 4295 char *new_data; |
|
| 4296 guint header_len = (end_of_headers + 4 - gfud->webdata); |
|
| 4297 size_t content_len; |
|
| 4298 |
|
| 4299 purple_debug_misc("util", "Response headers: '%.*s'\n", |
|
| 4300 header_len, gfud->webdata); |
|
| 4301 |
|
| 4302 /* See if we can find a redirect. */ |
|
| 4303 if(parse_redirect(gfud->webdata, header_len, gfud)) |
|
| 4304 return; |
|
| 4305 |
|
| 4306 gfud->got_headers = TRUE; |
|
| 4307 |
|
| 4308 /* No redirect. See if we can find a content length. */ |
|
| 4309 content_len = parse_content_len(gfud->webdata, header_len); |
|
| 4310 gfud->chunked = content_is_chunked(gfud->webdata, header_len); |
|
| 4311 |
|
| 4312 if (content_len == 0) { |
|
| 4313 /* We'll stick with an initial 8192 */ |
|
| 4314 content_len = 8192; |
|
| 4315 } else { |
|
| 4316 gfud->has_explicit_data_len = TRUE; |
|
| 4317 } |
|
| 4318 |
|
| 4319 |
|
| 4320 /* If we're returning the headers too, we don't need to clean them out */ |
|
| 4321 if (gfud->include_headers) { |
|
| 4322 gfud->data_len = content_len + header_len; |
|
| 4323 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); |
|
| 4324 } else { |
|
| 4325 size_t body_len = gfud->len - header_len; |
|
| 4326 |
|
| 4327 content_len = MAX(content_len, body_len); |
|
| 4328 |
|
| 4329 new_data = g_try_malloc(content_len); |
|
| 4330 if (new_data == NULL) { |
|
| 4331 purple_debug_error("util", |
|
| 4332 "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n", |
|
| 4333 content_len, g_strerror(errno)); |
|
| 4334 purple_util_fetch_url_error(gfud, |
|
| 4335 _("Unable to allocate enough memory to hold " |
|
| 4336 "the contents from %s. The web server may " |
|
| 4337 "be trying something malicious."), |
|
| 4338 gfud->website.address); |
|
| 4339 |
|
| 4340 return; |
|
| 4341 } |
|
| 4342 |
|
| 4343 /* We may have read part of the body when reading the headers, don't lose it */ |
|
| 4344 if (body_len > 0) { |
|
| 4345 memcpy(new_data, end_of_headers + 4, body_len); |
|
| 4346 } |
|
| 4347 |
|
| 4348 /* Out with the old... */ |
|
| 4349 g_free(gfud->webdata); |
|
| 4350 |
|
| 4351 /* In with the new. */ |
|
| 4352 gfud->len = body_len; |
|
| 4353 gfud->data_len = content_len; |
|
| 4354 gfud->webdata = new_data; |
|
| 4355 } |
|
| 4356 } |
|
| 4357 } |
|
| 4358 |
|
| 4359 if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) { |
|
| 4360 got_eof = TRUE; |
|
| 4361 break; |
|
| 4362 } |
|
| 4363 } |
|
| 4364 |
|
| 4365 if(len < 0) { |
|
| 4366 if(errno == EAGAIN) { |
|
| 4367 return; |
|
| 4368 } else { |
|
| 4369 purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"), |
|
| 4370 gfud->website.address, g_strerror(errno)); |
|
| 4371 return; |
|
| 4372 } |
|
| 4373 } |
|
| 4374 |
|
| 4375 if((len == 0) || got_eof) { |
|
| 4376 gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); |
|
| 4377 gfud->webdata[gfud->len] = '\0'; |
|
| 4378 |
|
| 4379 if (!gfud->include_headers && gfud->chunked) { |
|
| 4380 /* Process only if we don't want the headers. */ |
|
| 4381 process_chunked_data(gfud->webdata, &gfud->len); |
|
| 4382 } |
|
| 4383 |
|
| 4384 gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL); |
|
| 4385 purple_util_fetch_url_cancel(gfud); |
|
| 4386 } |
|
| 4387 } |
|
| 4388 |
|
| 4389 static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) |
|
| 4390 { |
|
| 4391 url_fetch_recv_cb(data, -1, cond); |
|
| 4392 } |
|
| 4393 |
|
| 4394 /** |
|
| 4395 * This function is called when the socket is available to be written |
|
| 4396 * to. |
|
| 4397 * |
|
| 4398 * @param source The file descriptor that can be written to. This can |
|
| 4399 * be an http connection or it can be the SSL connection of an |
|
| 4400 * https request. So be careful what you use it for! If it's |
|
| 4401 * an https request then use purple_ssl_write() instead of |
|
| 4402 * writing to it directly. |
|
| 4403 */ |
|
| 4404 static void |
|
| 4405 url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond) |
|
| 4406 { |
|
| 4407 PurpleUtilFetchUrlData *gfud; |
|
| 4408 int len, total_len; |
|
| 4409 |
|
| 4410 gfud = data; |
|
| 4411 |
|
| 4412 if (gfud->request == NULL) { |
|
| 4413 |
|
| 4414 PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account); |
|
| 4415 GString *request_str = g_string_new(NULL); |
|
| 4416 |
|
| 4417 g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n" |
|
| 4418 "Connection: close\r\n", |
|
| 4419 (gfud->full ? "" : "/"), |
|
| 4420 (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")), |
|
| 4421 (gfud->http11 ? "1.1" : "1.0")); |
|
| 4422 |
|
| 4423 if (gfud->user_agent) |
|
| 4424 g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent); |
|
| 4425 |
|
| 4426 /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 |
|
| 4427 * clients must know how to handle the "chunked" transfer encoding. |
|
| 4428 * Purple doesn't know how to handle "chunked", so should always send |
|
| 4429 * the Host header regardless, to get around some observed problems |
|
| 4430 */ |
|
| 4431 g_string_append_printf(request_str, "Accept: */*\r\n" |
|
| 4432 "Host: %s\r\n", |
|
| 4433 (gfud->website.address ? gfud->website.address : "")); |
|
| 4434 |
|
| 4435 if (purple_proxy_info_get_username(gpi) != NULL |
|
| 4436 && (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR |
|
| 4437 || purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) { |
|
| 4438 /* This chunk of code was copied from proxy.c http_start_connect_tunneling() |
|
| 4439 * This is really a temporary hack - we need a more complete proxy handling solution, |
|
| 4440 * so I didn't think it was worthwhile to refactor for reuse |
|
| 4441 */ |
|
| 4442 char *t1, *t2, *ntlm_type1; |
|
| 4443 char hostname[256]; |
|
| 4444 int ret; |
|
| 4445 |
|
| 4446 ret = gethostname(hostname, sizeof(hostname)); |
|
| 4447 hostname[sizeof(hostname) - 1] = '\0'; |
|
| 4448 if (ret < 0 || hostname[0] == '\0') { |
|
| 4449 purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?"); |
|
| 4450 strcpy(hostname, "localhost"); |
|
| 4451 } |
|
| 4452 |
|
| 4453 t1 = g_strdup_printf("%s:%s", |
|
| 4454 purple_proxy_info_get_username(gpi), |
|
| 4455 purple_proxy_info_get_password(gpi) ? |
|
| 4456 purple_proxy_info_get_password(gpi) : ""); |
|
| 4457 t2 = purple_base64_encode((const guchar *)t1, strlen(t1)); |
|
| 4458 g_free(t1); |
|
| 4459 |
|
| 4460 ntlm_type1 = purple_ntlm_gen_type1(hostname, ""); |
|
| 4461 |
|
| 4462 g_string_append_printf(request_str, |
|
| 4463 "Proxy-Authorization: Basic %s\r\n" |
|
| 4464 "Proxy-Authorization: NTLM %s\r\n" |
|
| 4465 "Proxy-Connection: Keep-Alive\r\n", |
|
| 4466 t2, ntlm_type1); |
|
| 4467 g_free(ntlm_type1); |
|
| 4468 g_free(t2); |
|
| 4469 } |
|
| 4470 |
|
| 4471 g_string_append(request_str, "\r\n"); |
|
| 4472 |
|
| 4473 gfud->request = g_string_free(request_str, FALSE); |
|
| 4474 } |
|
| 4475 |
|
| 4476 if(purple_debug_is_unsafe()) |
|
| 4477 purple_debug_misc("util", "Request: '%s'\n", gfud->request); |
|
| 4478 else |
|
| 4479 purple_debug_misc("util", "request constructed\n"); |
|
| 4480 |
|
| 4481 total_len = strlen(gfud->request); |
|
| 4482 |
|
| 4483 if (gfud->is_ssl) |
|
| 4484 len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written, |
|
| 4485 total_len - gfud->request_written); |
|
| 4486 else |
|
| 4487 len = write(gfud->fd, gfud->request + gfud->request_written, |
|
| 4488 total_len - gfud->request_written); |
|
| 4489 |
|
| 4490 if (len < 0 && errno == EAGAIN) |
|
| 4491 return; |
|
| 4492 else if (len < 0) { |
|
| 4493 purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"), |
|
| 4494 gfud->website.address, g_strerror(errno)); |
|
| 4495 return; |
|
| 4496 } |
|
| 4497 gfud->request_written += len; |
|
| 4498 |
|
| 4499 if (gfud->request_written < total_len) |
|
| 4500 return; |
|
| 4501 |
|
| 4502 /* We're done writing our request, now start reading the response */ |
|
| 4503 if (gfud->is_ssl) { |
|
| 4504 purple_input_remove(gfud->inpa); |
|
| 4505 gfud->inpa = 0; |
|
| 4506 purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud); |
|
| 4507 } else { |
|
| 4508 purple_input_remove(gfud->inpa); |
|
| 4509 gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb, |
|
| 4510 gfud); |
|
| 4511 } |
|
| 4512 } |
|
| 4513 |
|
| 4514 static void |
|
| 4515 url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message) |
|
| 4516 { |
|
| 4517 PurpleUtilFetchUrlData *gfud; |
|
| 4518 |
|
| 4519 gfud = url_data; |
|
| 4520 gfud->connect_data = NULL; |
|
| 4521 |
|
| 4522 if (source == -1) |
|
| 4523 { |
|
| 4524 purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), |
|
| 4525 (gfud->website.address ? gfud->website.address : ""), error_message); |
|
| 4526 return; |
|
| 4527 } |
|
| 4528 |
|
| 4529 gfud->fd = source; |
|
| 4530 |
|
| 4531 gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE, |
|
| 4532 url_fetch_send_cb, gfud); |
|
| 4533 url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE); |
|
| 4534 } |
|
| 4535 |
|
| 4536 static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) |
|
| 4537 { |
|
| 4538 PurpleUtilFetchUrlData *gfud; |
|
| 4539 |
|
| 4540 gfud = data; |
|
| 4541 |
|
| 4542 gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE, |
|
| 4543 url_fetch_send_cb, gfud); |
|
| 4544 url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE); |
|
| 4545 } |
|
| 4546 |
|
| 4547 static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data) |
|
| 4548 { |
|
| 4549 PurpleUtilFetchUrlData *gfud; |
|
| 4550 |
|
| 4551 gfud = data; |
|
| 4552 gfud->ssl_connection = NULL; |
|
| 4553 |
|
| 4554 purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), |
|
| 4555 (gfud->website.address ? gfud->website.address : ""), |
|
| 4556 purple_ssl_strerror(error)); |
|
| 4557 } |
|
| 4558 |
|
| 4559 PurpleUtilFetchUrlData * |
|
| 4560 purple_util_fetch_url_request(PurpleAccount *account, |
|
| 4561 const char *url, gboolean full, const char *user_agent, gboolean http11, |
|
| 4562 const char *request, gboolean include_headers, gssize max_len, |
|
| 4563 PurpleUtilFetchUrlCallback callback, void *user_data) |
|
| 4564 { |
|
| 4565 PurpleUtilFetchUrlData *gfud; |
|
| 4566 |
|
| 4567 g_return_val_if_fail(url != NULL, NULL); |
|
| 4568 g_return_val_if_fail(callback != NULL, NULL); |
|
| 4569 |
|
| 4570 if(purple_debug_is_unsafe()) |
|
| 4571 purple_debug_info("util", |
|
| 4572 "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n", |
|
| 4573 url, full, user_agent?user_agent:"(null)", http11); |
|
| 4574 else |
|
| 4575 purple_debug_info("util", "requesting to fetch a URL\n"); |
|
| 4576 |
|
| 4577 gfud = g_new0(PurpleUtilFetchUrlData, 1); |
|
| 4578 |
|
| 4579 gfud->callback = callback; |
|
| 4580 gfud->user_data = user_data; |
|
| 4581 gfud->url = g_strdup(url); |
|
| 4582 gfud->user_agent = g_strdup(user_agent); |
|
| 4583 gfud->http11 = http11; |
|
| 4584 gfud->full = full; |
|
| 4585 gfud->request = g_strdup(request); |
|
| 4586 gfud->include_headers = include_headers; |
|
| 4587 gfud->fd = -1; |
|
| 4588 gfud->max_len = max_len; |
|
| 4589 gfud->account = account; |
|
| 4590 |
|
| 4591 purple_url_parse(url, &gfud->website.address, &gfud->website.port, |
|
| 4592 &gfud->website.page, &gfud->website.user, &gfud->website.passwd); |
|
| 4593 |
|
| 4594 if (purple_strcasestr(url, "https://") != NULL) { |
|
| 4595 if (!purple_ssl_is_supported()) { |
|
| 4596 purple_util_fetch_url_error(gfud, |
|
| 4597 _("Unable to connect to %s: %s"), |
|
| 4598 gfud->website.address, |
|
| 4599 _("Server requires TLS/SSL, but no TLS/SSL support was found.")); |
|
| 4600 return NULL; |
|
| 4601 } |
|
| 4602 |
|
| 4603 gfud->is_ssl = TRUE; |
|
| 4604 gfud->ssl_connection = purple_ssl_connect(account, |
|
| 4605 gfud->website.address, gfud->website.port, |
|
| 4606 ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); |
|
| 4607 } else { |
|
| 4608 gfud->connect_data = purple_proxy_connect(NULL, account, |
|
| 4609 gfud->website.address, gfud->website.port, |
|
| 4610 url_fetch_connect_cb, gfud); |
|
| 4611 } |
|
| 4612 |
|
| 4613 if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) |
|
| 4614 { |
|
| 4615 purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), |
|
| 4616 gfud->website.address); |
|
| 4617 return NULL; |
|
| 4618 } |
|
| 4619 |
|
| 4620 return gfud; |
|
| 4621 } |
|
| 4622 |
|
| 4623 void |
|
| 4624 purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud) |
|
| 4625 { |
|
| 4626 if (gfud->ssl_connection != NULL) |
|
| 4627 purple_ssl_close(gfud->ssl_connection); |
|
| 4628 |
|
| 4629 if (gfud->connect_data != NULL) |
|
| 4630 purple_proxy_connect_cancel(gfud->connect_data); |
|
| 4631 |
|
| 4632 if (gfud->inpa > 0) |
|
| 4633 purple_input_remove(gfud->inpa); |
|
| 4634 |
|
| 4635 if (gfud->fd >= 0) |
|
| 4636 close(gfud->fd); |
|
| 4637 |
|
| 4638 g_free(gfud->website.user); |
|
| 4639 g_free(gfud->website.passwd); |
|
| 4640 g_free(gfud->website.address); |
|
| 4641 g_free(gfud->website.page); |
|
| 4642 g_free(gfud->url); |
|
| 4643 g_free(gfud->user_agent); |
|
| 4644 g_free(gfud->request); |
|
| 4645 g_free(gfud->webdata); |
|
| 4646 |
|
| 4647 g_free(gfud); |
|
| 4648 } |
|
| 4649 |
3968 |
| 4650 const char * |
3969 const char * |
| 4651 purple_url_decode(const char *str) |
3970 purple_url_decode(const char *str) |
| 4652 { |
3971 { |
| 4653 static char buf[BUF_LEN]; |
3972 static char buf[BUF_LEN]; |