Mercurial > pidgin
comparison src/util.c @ 7094:2343c3aa1dec
[gaim-migrate @ 7659]
grab_url() and parse_url() are gone, replaced with gaim_url_fetch() and
gaim_url_parse(). They were also moved to util.[ch].
committer: Tailor Script <tailor@pidgin.im>
| author | Christian Hammond <chipx86@chipx86.com> |
|---|---|
| date | Wed, 01 Oct 2003 03:01:25 +0000 |
| parents | acd2a66e59ed |
| children | c8bf2da398e3 |
comparison
equal
deleted
inserted
replaced
| 7093:3650612c7daa | 7094:2343c3aa1dec |
|---|---|
| 1 /* | 1 /* |
| 2 * gaim | 2 * @file util.h Utility Functions |
| 3 * @ingroup core | |
| 3 * | 4 * |
| 4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> | 5 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
| 6 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> | |
| 5 * | 7 * |
| 6 * This program is free software; you can redistribute it and/or modify | 8 * This program is free software; you can redistribute it and/or modify |
| 7 * it under the terms of the GNU General Public License as published by | 9 * it under the terms of the GNU General Public License as published by |
| 8 * the Free Software Foundation; either version 2 of the License, or | 10 * the Free Software Foundation; either version 2 of the License, or |
| 9 * (at your option) any later version. | 11 * (at your option) any later version. |
| 14 * GNU General Public License for more details. | 16 * GNU General Public License for more details. |
| 15 * | 17 * |
| 16 * You should have received a copy of the GNU General Public License | 18 * You should have received a copy of the GNU General Public License |
| 17 * along with this program; if not, write to the Free Software | 19 * along with this program; if not, write to the Free Software |
| 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 * | |
| 20 */ | 21 */ |
| 21 | |
| 22 #include "internal.h" | 22 #include "internal.h" |
| 23 | 23 |
| 24 #include "conversation.h" | 24 #include "conversation.h" |
| 25 #include "debug.h" | 25 #include "debug.h" |
| 26 #include "prpl.h" | 26 #include "prpl.h" |
| 27 #include "prefs.h" | 27 #include "prefs.h" |
| 28 #include "util.h" | |
| 29 | |
| 30 typedef struct | |
| 31 { | |
| 32 void (*callback)(void *, const char *, size_t); | |
| 33 void *user_data; | |
| 34 | |
| 35 struct | |
| 36 { | |
| 37 char *address; | |
| 38 int port; | |
| 39 char *page; | |
| 40 | |
| 41 } website; | |
| 42 | |
| 43 char *url; | |
| 44 gboolean full; | |
| 45 char *user_agent; | |
| 46 gboolean http11; | |
| 47 | |
| 48 int inpa; | |
| 49 | |
| 50 gboolean sentreq; | |
| 51 gboolean newline; | |
| 52 gboolean startsaving; | |
| 53 char *webdata; | |
| 54 unsigned long len; | |
| 55 unsigned long data_len; | |
| 56 | |
| 57 } GaimFetchUrlData; | |
| 58 | |
| 28 | 59 |
| 29 static char home_dir[MAXPATHLEN]; | 60 static char home_dir[MAXPATHLEN]; |
| 30 | 61 |
| 31 char *full_date() | 62 char *full_date() |
| 32 { | 63 { |
| 994 | 1025 |
| 995 return g_strdup_printf("%.2f %s", size_mag, size_str[size_index]); | 1026 return g_strdup_printf("%.2f %s", size_mag, size_str[size_index]); |
| 996 } | 1027 } |
| 997 } | 1028 } |
| 998 | 1029 |
| 999 gboolean gaim_markup_find_tag(const char *needle, const char *haystack, const char **start, const char **end, GData **attributes) { | 1030 gboolean |
| 1031 gaim_markup_find_tag(const char *needle, const char *haystack, | |
| 1032 const char **start, const char **end, GData **attributes) | |
| 1033 { | |
| 1000 GData *attribs; | 1034 GData *attribs; |
| 1001 const char *cur = haystack; | 1035 const char *cur = haystack; |
| 1002 char *name = NULL; | 1036 char *name = NULL; |
| 1003 gboolean found = FALSE; | 1037 gboolean found = FALSE; |
| 1004 gboolean in_tag = FALSE; | 1038 gboolean in_tag = FALSE; |
| 1005 gboolean in_attr = FALSE; | 1039 gboolean in_attr = FALSE; |
| 1006 char *in_quotes = NULL; | 1040 const char *in_quotes = NULL; |
| 1007 size_t needlelen = strlen(needle); | 1041 size_t needlelen = strlen(needle); |
| 1008 | 1042 |
| 1009 g_datalist_init(&attribs); | 1043 g_datalist_init(&attribs); |
| 1010 | 1044 |
| 1011 while (*cur && !found) { | 1045 while (*cur && !found) { |
| 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 } |
