| 1131 *attributes = NULL; |
1165 *attributes = NULL; |
| 1132 } |
1166 } |
| 1133 |
1167 |
| 1134 return found; |
1168 return found; |
| 1135 } |
1169 } |
| |
1170 |
| |
1171 gboolean |
| |
1172 gaim_url_parse(const char *url, char **ret_host, int *ret_port, |
| |
1173 char **ret_path) |
| |
1174 { |
| |
1175 char scan_info[255]; |
| |
1176 char port_str[5]; |
| |
1177 int f; |
| |
1178 const char *turl; |
| |
1179 char host[256], path[256]; |
| |
1180 int port = 0; |
| |
1181 /* hyphen at end includes it in control set */ |
| |
1182 static char addr_ctrl[] = "A-Za-z0-9.-"; |
| |
1183 static char port_ctrl[] = "0-9"; |
| |
1184 static char page_ctrl[] = "A-Za-z0-9.~_/:*!@&%%?=+^-"; |
| |
1185 |
| |
1186 g_return_val_if_fail(url != NULL, FALSE); |
| |
1187 |
| |
1188 if ((turl = strstr(url, "http://")) != NULL || |
| |
1189 (turl = strstr(url, "HTTP://")) != NULL) |
| |
1190 { |
| |
1191 turl += 7; |
| |
1192 url = turl; |
| |
1193 } |
| |
1194 |
| |
1195 g_snprintf(scan_info, sizeof(scan_info), |
| |
1196 "%%[%s]:%%[%s]/%%[%s]", addr_ctrl, port_ctrl, page_ctrl); |
| |
1197 |
| |
1198 f = sscanf(url, scan_info, host, port_str, path); |
| |
1199 |
| |
1200 if (f == 1) |
| |
1201 { |
| |
1202 g_snprintf(scan_info, sizeof(scan_info), |
| |
1203 "%%[%s]/%%[%s]", |
| |
1204 addr_ctrl, page_ctrl); |
| |
1205 f = sscanf(url, scan_info, host, path); |
| |
1206 g_snprintf(port_str, sizeof(port_str), "80"); |
| |
1207 } |
| |
1208 |
| |
1209 if (f == 1) |
| |
1210 *path = '\0'; |
| |
1211 |
| |
1212 sscanf(port_str, "%d", &port); |
| |
1213 |
| |
1214 if (ret_host != NULL) *ret_host = g_strdup(host); |
| |
1215 if (ret_port != NULL) *ret_port = port; |
| |
1216 if (ret_path != NULL) *ret_path = g_strdup(path); |
| |
1217 |
| |
1218 return TRUE; |
| |
1219 } |
| |
1220 |
| |
1221 static void |
| |
1222 destroy_fetch_url_data(GaimFetchUrlData *gfud) |
| |
1223 { |
| |
1224 if (gfud->webdata != NULL) g_free(gfud->webdata); |
| |
1225 if (gfud->url != NULL) g_free(gfud->url); |
| |
1226 if (gfud->user_agent != NULL) g_free(gfud->user_agent); |
| |
1227 if (gfud->website.address != NULL) g_free(gfud->website.address); |
| |
1228 if (gfud->website.page != NULL) g_free(gfud->website.page); |
| |
1229 |
| |
1230 g_free(gfud); |
| |
1231 } |
| |
1232 |
| |
1233 static gboolean |
| |
1234 parse_redirect(const char *data, size_t data_len, gint sock, |
| |
1235 GaimFetchUrlData *gfud) |
| |
1236 { |
| |
1237 gchar *s; |
| |
1238 |
| |
1239 if ((s = g_strstr_len(data, data_len, "Location: ")) != NULL) |
| |
1240 { |
| |
1241 gchar *new_url, *temp_url, *end; |
| |
1242 gboolean full; |
| |
1243 int len; |
| |
1244 |
| |
1245 s += strlen("Location: "); |
| |
1246 end = strchr(s, '\r'); |
| |
1247 |
| |
1248 /* Just in case :) */ |
| |
1249 if (end == NULL) |
| |
1250 end = strchr(s, '\n'); |
| |
1251 |
| |
1252 len = end - s; |
| |
1253 |
| |
1254 new_url = g_malloc(len + 1); |
| |
1255 strncpy(new_url, s, len); |
| |
1256 new_url[len] = '\0'; |
| |
1257 |
| |
1258 full = gfud->full; |
| |
1259 |
| |
1260 if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) |
| |
1261 { |
| |
1262 temp_url = new_url; |
| |
1263 |
| |
1264 new_url = g_strdup_printf("%s:%d%s", gfud->website.address, |
| |
1265 gfud->website.port, temp_url); |
| |
1266 |
| |
1267 g_free(temp_url); |
| |
1268 |
| |
1269 full = FALSE; |
| |
1270 } |
| |
1271 |
| |
1272 /* Close the existing stuff. */ |
| |
1273 gaim_input_remove(gfud->inpa); |
| |
1274 close(sock); |
| |
1275 |
| |
1276 gaim_debug_info("gaim_url_fetch", "Redirecting to %s\n", new_url); |
| |
1277 |
| |
1278 /* Try again, with this new location. */ |
| |
1279 gaim_url_fetch(new_url, full, gfud->user_agent, gfud->http11, |
| |
1280 gfud->callback, gfud->user_data); |
| |
1281 |
| |
1282 /* Free up. */ |
| |
1283 g_free(new_url); |
| |
1284 destroy_fetch_url_data(gfud); |
| |
1285 |
| |
1286 return TRUE; |
| |
1287 } |
| |
1288 |
| |
1289 return FALSE; |
| |
1290 } |
| |
1291 |
| |
1292 static size_t |
| |
1293 parse_content_len(const char *data, size_t data_len) |
| |
1294 { |
| |
1295 size_t content_len = 0; |
| |
1296 |
| |
1297 sscanf(data, "Content-Length: %d", &content_len); |
| |
1298 |
| |
1299 return content_len; |
| |
1300 } |
| |
1301 |
| |
1302 static void |
| |
1303 url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond) |
| |
1304 { |
| |
1305 GaimFetchUrlData *gfud = url_data; |
| |
1306 char data; |
| |
1307 |
| |
1308 if (sock == -1) |
| |
1309 { |
| |
1310 gfud->callback(gfud->user_data, NULL, 0); |
| |
1311 |
| |
1312 destroy_fetch_url_data(gfud); |
| |
1313 |
| |
1314 return; |
| |
1315 } |
| |
1316 |
| |
1317 if (!gfud->sentreq) |
| |
1318 { |
| |
1319 char buf[1024]; |
| |
1320 |
| |
1321 if (gfud->user_agent) |
| |
1322 { |
| |
1323 if (gfud->http11) |
| |
1324 { |
| |
1325 g_snprintf(buf, sizeof(buf), |
| |
1326 "GET %s%s HTTP/1.1\r\n" |
| |
1327 "User-Agent: \"%s\"\r\n" |
| |
1328 "Host: %s\r\n\r\n", |
| |
1329 (gfud->full ? "" : "/"), |
| |
1330 (gfud->full ? gfud->url : gfud->website.page), |
| |
1331 gfud->user_agent, gfud->website.address); |
| |
1332 } |
| |
1333 else |
| |
1334 { |
| |
1335 g_snprintf(buf, sizeof(buf), |
| |
1336 "GET %s%s HTTP/1.0\r\n" |
| |
1337 "User-Agent: \"%s\"\r\n\r\n", |
| |
1338 (gfud->full ? "" : "/"), |
| |
1339 (gfud->full ? gfud->url : gfud->website.page), |
| |
1340 gfud->user_agent); |
| |
1341 } |
| |
1342 } |
| |
1343 else |
| |
1344 { |
| |
1345 if (gfud->http11) |
| |
1346 { |
| |
1347 g_snprintf(buf, sizeof(buf), |
| |
1348 "GET %s%s HTTP/1.1\r\n" |
| |
1349 "Host: %s\r\n\r\n", |
| |
1350 (gfud->full ? "" : "/"), |
| |
1351 (gfud->full ? gfud->url : gfud->website.page), |
| |
1352 gfud->website.address); |
| |
1353 } |
| |
1354 else |
| |
1355 { |
| |
1356 g_snprintf(buf, sizeof(buf), |
| |
1357 "GET %s%s HTTP/1.0\r\n\r\n", |
| |
1358 (gfud->full ? "" : "/"), |
| |
1359 (gfud->full ? gfud->url : gfud->website.page)); |
| |
1360 } |
| |
1361 } |
| |
1362 |
| |
1363 gaim_debug_misc("gaim_url_fetch", "Request: %s\n", buf); |
| |
1364 |
| |
1365 write(sock, buf, strlen(buf)); |
| |
1366 fcntl(sock, F_SETFL, O_NONBLOCK); |
| |
1367 gfud->sentreq = TRUE; |
| |
1368 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, |
| |
1369 url_fetched_cb, url_data); |
| |
1370 gfud->data_len = 4096; |
| |
1371 gfud->webdata = g_malloc(gfud->data_len); |
| |
1372 |
| |
1373 return; |
| |
1374 } |
| |
1375 |
| |
1376 if (read(sock, &data, 1) > 0 || errno == EWOULDBLOCK) |
| |
1377 { |
| |
1378 if (errno == EWOULDBLOCK) |
| |
1379 { |
| |
1380 errno = 0; |
| |
1381 |
| |
1382 return; |
| |
1383 } |
| |
1384 |
| |
1385 gfud->len++; |
| |
1386 |
| |
1387 if (gfud->len == gfud->data_len + 1) |
| |
1388 { |
| |
1389 gfud->data_len += (gfud->data_len) / 2; |
| |
1390 |
| |
1391 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); |
| |
1392 } |
| |
1393 |
| |
1394 gfud->webdata[gfud->len - 1] = data; |
| |
1395 |
| |
1396 if (!gfud->startsaving) |
| |
1397 { |
| |
1398 if (data == '\r') |
| |
1399 return; |
| |
1400 |
| |
1401 if (data == '\n') |
| |
1402 { |
| |
1403 if (gfud->newline) |
| |
1404 { |
| |
1405 size_t content_len; |
| |
1406 gfud->startsaving = TRUE; |
| |
1407 |
| |
1408 /* See if we can find a redirect. */ |
| |
1409 if (parse_redirect(gfud->webdata, gfud->len, sock, gfud)) |
| |
1410 return; |
| |
1411 |
| |
1412 /* No redirect. See if we can find a content length. */ |
| |
1413 content_len = parse_content_len(gfud->webdata, gfud->len); |
| |
1414 |
| |
1415 if (content_len == 0) |
| |
1416 { |
| |
1417 /* We'll stick with an initial 8192 */ |
| |
1418 content_len = 8192; |
| |
1419 } |
| |
1420 |
| |
1421 /* Out with the old... */ |
| |
1422 gfud->len = 0; |
| |
1423 g_free(gfud->webdata); |
| |
1424 gfud->webdata = NULL; |
| |
1425 |
| |
1426 /* In with the new. */ |
| |
1427 gfud->data_len = content_len; |
| |
1428 gfud->webdata = g_malloc(gfud->data_len); |
| |
1429 } |
| |
1430 else |
| |
1431 gfud->newline = TRUE; |
| |
1432 |
| |
1433 return; |
| |
1434 } |
| |
1435 |
| |
1436 gfud->newline = FALSE; |
| |
1437 } |
| |
1438 } |
| |
1439 else if (errno != ETIMEDOUT) |
| |
1440 { |
| |
1441 gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); |
| |
1442 gfud->webdata[gfud->len] = 0; |
| |
1443 |
| |
1444 gaim_debug_misc("gaim_url_fetch", "Received: '%s'\n", gfud->webdata); |
| |
1445 |
| |
1446 gaim_input_remove(gfud->inpa); |
| |
1447 close(sock); |
| |
1448 gfud->callback(gfud->user_data, gfud->webdata, gfud->len); |
| |
1449 |
| |
1450 if (gfud->webdata) |
| |
1451 g_free(gfud->webdata); |
| |
1452 |
| |
1453 destroy_fetch_url_data(gfud); |
| |
1454 } |
| |
1455 else |
| |
1456 { |
| |
1457 gaim_input_remove(gfud->inpa); |
| |
1458 close(sock); |
| |
1459 |
| |
1460 gfud->callback(gfud->user_data, NULL, 0); |
| |
1461 |
| |
1462 destroy_fetch_url_data(gfud); |
| |
1463 } |
| |
1464 } |
| |
1465 |
| |
1466 void |
| |
1467 gaim_url_fetch(const char *url, gboolean full, |
| |
1468 const char *user_agent, gboolean http11, |
| |
1469 void (*cb)(gpointer, const char *, size_t), |
| |
1470 void *user_data) |
| |
1471 { |
| |
1472 int sock; |
| |
1473 GaimFetchUrlData *gfud; |
| |
1474 |
| |
1475 g_return_if_fail(url != NULL); |
| |
1476 g_return_if_fail(cb != NULL); |
| |
1477 |
| |
1478 gfud = g_new0(GaimFetchUrlData, 1); |
| |
1479 |
| |
1480 gfud->callback = cb; |
| |
1481 gfud->user_data = user_data; |
| |
1482 gfud->url = g_strdup(url); |
| |
1483 gfud->user_agent = (user_agent != NULL ? g_strdup(user_agent) : NULL); |
| |
1484 gfud->http11 = http11; |
| |
1485 gfud->full = full; |
| |
1486 |
| |
1487 gaim_url_parse(url, &gfud->website.address, &gfud->website.port, |
| |
1488 &gfud->website.page); |
| |
1489 |
| |
1490 if ((sock = gaim_proxy_connect(NULL, gfud->website.address, |
| |
1491 gfud->website.port, url_fetched_cb, |
| |
1492 gfud)) < 0) |
| |
1493 { |
| |
1494 destroy_fetch_url_data(gfud); |
| |
1495 |
| |
1496 cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0); |
| |
1497 } |
| |
1498 } |