#include #include #include #include #include #include #include #include #include #include "buffer.h" #include "http-parse.h" #define EXPECT(test) \ do { \ token = read_token (&source, &b); \ if (!(test)) \ goto error; \ } while (0) int http_get (const char *host, int port, object path, object query) { object content_type = Cnil; object location = Cnil; static object result[3]; int sock; struct sockaddr_in addr; struct hostent *host_entry; struct buffer b; struct http_source source; unsigned char body[128]; int fd = -1; int token; int status; result[0] = Cnil; result[1] = Cnil; result[2] = Cnil; source.fp = NULL; buffer_init (&b, 128); sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) return ((int) result); host_entry = gethostbyname (host); if (host_entry == NULL) { fputs ("Host not found\n", stderr); goto error; } memcpy (&addr.sin_addr.s_addr, host_entry->h_addr_list[0], host_entry->h_length); addr.sin_family = AF_INET; addr.sin_port = htons (port); if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) { perror ("connect"); goto error; } buffer_addstr (&b, "GET "); buffer_add (&b, path->st.st_self, path->st.st_fillp); if (query != Cnil) { buffer_addch (&b, '?'); buffer_add (&b, query->st.st_self, query->st.st_fillp); } buffer_addstr (&b, " HTTP/1.0\r\nHost: "); buffer_addstr (&b, host); buffer_addstr (&b, "\r\n\r\n"); if (write (sock, b.data, b.index) != b.index) { perror ("write"); goto error; } source.fp = fdopen (sock, "r"); if (source.fp == NULL) { perror ("fdopen"); goto error; } source.string = NULL; source.length = -1; source.index = 0; EXPECT (token == WORD); if (strncmp (b.data, "HTTP/", 5) != 0) { fputs ("Bad response: expected HTTP/x.x\n", stderr); goto error; } EXPECT (token == WORD); status = atoi (b.data); if ((status < 200) || (status >= 400)) goto error; result[0] = make_fixnum (status); http_read_line (&source, &b); while (!maybe_read_crlf (&source)) { EXPECT (token == WORD); if (strcasecmp (b.data, "content-type") == 0) { EXPECT (token == COLON); EXPECT (token == WORD); content_type = make_simple_string (b.data); http_read_line (&source, &b); } else if (strcasecmp (b.data, "location") == 0) { EXPECT (token == COLON); if (http_read_line (&source, &b) && (b.index > 0)) location = make_simple_string (b.data); else { fputs ("Blank Location header\n", stderr); goto error; } } else http_read_line (&source, &b); } if ((status < 300) && (content_type != Cnil)) { int n; int len = 0; buffer_reset (&b); buffer_addstr (&b, "/tmp/HTTP-GET-XXXXXX"); fd = mkstemp (b.data); if (fd == -1) { perror ("mkstemp"); goto error; } while ((n = fread (body, 1, sizeof (body), source.fp)) > 0) { if (len >= MAX_CONTENT_LENGTH) { fputs ("Exceeded MAX_CONTENT_LENGTH\n", stderr); goto error; } if (write (fd, body, n) == n) len += n; else { perror ("write"); goto error; } } if (len > 0) { result[1] = make_simple_string (b.data); result[2] = content_type; } } else if ((status >= 300) && (location != Cnil)) result[1] = location; else result[0] = Cnil; cleanup: if (fd > 0) close (fd); if (source.fp) fclose (source.fp); else close (sock); free (b.data); return ((int) result); error: result[0] = Cnil; result[1] = Cnil; result[2] = Cnil; goto cleanup; } object retrieve_object_from_array (int a, int i) { object x = ((object *) a)[i]; ((object *) a)[i] = Cnil; return x; }