You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
503 lines
16 KiB
503 lines
16 KiB
#include<libwapcaplet/libwapcaplet.h>
|
|
#include<stdlib.h>
|
|
|
|
#include "utils/url.h"
|
|
#include "utils/nsurl.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/corestrings.h"
|
|
#include "utils/nsurl.h"
|
|
#include "utils/log.h"
|
|
|
|
#include "content/fetch.h"
|
|
#include "content/fetchers.h"
|
|
#include "content/fetchers/httplib_kolibri.h"
|
|
#include "frontends/kolibrios/kolibri_http.h"
|
|
|
|
#include <assert.h>
|
|
|
|
struct httpfetcher {
|
|
struct http_msg *handle;
|
|
struct fetch *owner;
|
|
nsurl *url;
|
|
bool headercbdone;
|
|
unsigned int datalen_cb_done;
|
|
|
|
struct httpfetcher *next;
|
|
};
|
|
|
|
static struct httpfetcher *head = NULL;
|
|
|
|
static void add_to_poll(struct httpfetcher *newfetcher) {
|
|
|
|
NSLOG(fetch, DEBUG, "(head:0x%x) newfetcher 0x%x, newfetcher->handle 0x%x", head, newfetcher, newfetcher->handle);
|
|
|
|
struct httpfetcher *t = head;
|
|
while(t) {
|
|
NSLOG(fetch, DEBUG, "ll node = 0x%x, ->next 0x%x", t, t->next);
|
|
t=t->next;
|
|
}
|
|
|
|
if(head == NULL) {
|
|
head = newfetcher;
|
|
assert(head->next == NULL);
|
|
NSLOG(fetch, DEBUG, "(head:0x%x) ADDED newfetcher 0x%x, newfetcher->handle 0x%x", head, head, head->handle);
|
|
}
|
|
else {
|
|
t = head;
|
|
|
|
while(t->next != NULL) {
|
|
NSLOG(fetch, DEBUG, "-- Looping t 0x%x, t->handle 0x%x", t->next, t->next->handle);
|
|
t = t->next;
|
|
}
|
|
t->next = newfetcher;
|
|
NSLOG(fetch, DEBUG, "(head:0x%x) ADDED newfetcher 0x%x, newfetcher->handle 0x%x, nn 0x%x", head, t->next, t->next->handle, t->next->next);
|
|
}
|
|
|
|
}
|
|
|
|
static struct httpfetcher* remove_from_poll(struct httpfetcher *removee) {
|
|
struct httpfetcher *t = head, *p = head;
|
|
NSLOG(fetch, DEBUG, "(head=0x%x), remove: 0x%x , removee->handle: 0x%x", head, removee, removee->handle);
|
|
|
|
while(t) {
|
|
NSLOG(fetch, DEBUG, "BEFORE REMOVE ll node = 0x%x, next 0x%x", t, t->next);
|
|
t=t->next;
|
|
}
|
|
|
|
t = head;
|
|
while(t) {
|
|
if (t == removee) {
|
|
if(t == head) {
|
|
p = t->next;
|
|
head = p;
|
|
|
|
return head;
|
|
break;
|
|
}
|
|
else {
|
|
p->next = t->next;
|
|
return t->next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p = t;
|
|
t = t->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
static bool init_fetcher(lwc_string *scheme) {
|
|
bool supported_scheme;
|
|
assert(lwc_string_isequal(scheme, corestring_lwc_http, &supported_scheme) == lwc_error_ok);
|
|
|
|
NSLOG(fetch, INFO, "Initializing http library!");
|
|
debug_board_printf("---- [NETSURF] Trying to initialize http library.\n");
|
|
|
|
if(kolibri_http_init() == 0) {
|
|
NSLOG(fetch, INFO, "[INFO] Loaded http.obj library successfully.\n");
|
|
debug_board_printf("---- [NETSURF] Successfully initialized http library.\n");
|
|
}
|
|
else {
|
|
NSLOG(fetch, ERROR, "Could not load http.obj library.\n");
|
|
debug_board_printf("---- [NETSURF] Could not initialize http library. Exiting.\n");
|
|
return false;
|
|
}
|
|
|
|
return supported_scheme;
|
|
}
|
|
|
|
static bool supported_url_check(const struct nsurl *url) {
|
|
bool supported;
|
|
lwc_string *url_scheme = nsurl_get_component(url, NSURL_SCHEME);
|
|
assert(lwc_string_isequal(url_scheme, corestring_lwc_http, &supported) == lwc_error_ok);
|
|
lwc_string_unref(url_scheme);
|
|
return supported;
|
|
}
|
|
|
|
static void *setup_fetch(struct fetch *parent_fetch, struct nsurl *url,
|
|
bool only_2xx, bool downgrade_tls, const char *post_urlenc,
|
|
const struct fetch_multipart_data *post_multipart,
|
|
const char **headers) {
|
|
|
|
struct http_msg* request = NULL;
|
|
|
|
NSLOG(fetch, DEBUG, "[SETUP] Our target URL: %s", nsurl_access(url));
|
|
NSLOG(fetch, DEBUG, "[SETUP] POST urlencoded data: %s", post_urlenc);
|
|
int i = 0;
|
|
for(i = 0; headers[i] != NULL; i++)
|
|
NSLOG(fetch, DEBUG, "[SETUP] -- Headers : %s", headers[i]);
|
|
|
|
|
|
if(post_multipart != NULL) {
|
|
struct fetch_multipart_data *printer = post_multipart;
|
|
while(printer != NULL) {
|
|
NSLOG(fetch, DEBUG, "Multipart POST : (%s = %s)\n", printer->name, printer->value);
|
|
/* debug_board_printf("Multipart POST : (%s = %s)\n", printer->name, printer->value); */
|
|
printer = printer->next;
|
|
}
|
|
char *boundary = "--------Netsurf------------KolibriOS----Multipart----";
|
|
int lenb = strlen(boundary);
|
|
char *contenttype = "multipart/form-data; boundary=--------Netsurf------------KolibriOS----Multipart----";
|
|
|
|
/* Intermediate boundaries have 2 additional dashes at the beginning */
|
|
char *iboundary = "----------Netsurf------------KolibriOS----Multipart----";
|
|
char *content = NULL;
|
|
int contentlen = 0;
|
|
|
|
printer = post_multipart;
|
|
while(printer != NULL) {
|
|
int leninc = strlen(iboundary) + 2 + strlen("Content-Disposition: form-data; name=") + 1 + strlen(printer->name) + 1 + 2 + 2 + strlen(printer->value) + 2;
|
|
contentlen+=leninc;
|
|
printer = printer -> next;
|
|
}
|
|
|
|
/* Space for last boundary (which has 2 empty boundary dashes) and CRLF */
|
|
contentlen += strlen(iboundary) + 2 + 2;
|
|
|
|
/* Space for terminating NULL */
|
|
contentlen += 1;
|
|
|
|
content = realloc(content, contentlen);
|
|
|
|
char *tmp = content;
|
|
/* debug_board_printf("Before Loop tmp = %u\n", tmp); */
|
|
|
|
printer = post_multipart;
|
|
while(printer != NULL) {
|
|
int i = 0;
|
|
strcpy(tmp, iboundary);
|
|
tmp+=strlen(iboundary);
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
*tmp++ = '\r';
|
|
*tmp++ = '\n';
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
strcpy(tmp, "Content-Disposition: form-data; name=\"");
|
|
tmp+= strlen("Content-Disposition: form-data; name=\"");
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
strcpy(tmp, printer->name);
|
|
tmp += strlen(printer->name);
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
*tmp++ = '"';
|
|
*tmp++ = '\r';
|
|
*tmp++ = '\n';
|
|
*tmp++ = '\r';
|
|
*tmp++ = '\n';
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
strcpy(tmp, printer->value);
|
|
tmp += strlen(printer->value);
|
|
|
|
/* debug_board_printf("tmp = %u\n", tmp); */
|
|
|
|
*tmp++ = '\r';
|
|
*tmp++ = '\n';
|
|
|
|
/* debug_board_printf("END OF LOOP tmp = %u\n", tmp); */
|
|
|
|
printer = printer->next;
|
|
}
|
|
|
|
/* debug_board_printf("AFTER LOOP tmp = %u, content=%u\n", tmp, content); */
|
|
|
|
strcpy(tmp, iboundary);
|
|
tmp+=strlen(iboundary);
|
|
|
|
strcpy(tmp, "--");
|
|
tmp+=2;
|
|
|
|
*tmp++ = '\r';
|
|
*tmp++ = '\n';
|
|
|
|
*tmp='\0';
|
|
|
|
/* debug_board_printf("TERMINATING NULL tmp = %u\n", tmp); */
|
|
|
|
NSLOG(fetch, DEBUG, "Multipart request content length : %d", contentlen);
|
|
NSLOG(fetch, DEBUG, "Multipart request content: %s", content);
|
|
|
|
request = http_post_asm(nsurl_access(url), NULL, 1<<8, *headers, contenttype, contentlen - 1);
|
|
|
|
if(request != NULL) {
|
|
int datasent = 0;
|
|
/* debug_board_printf("--- Sending data : %s with length %u\n", content, contentlen); */
|
|
/* LOG("--- Sending data : with length %u\n", contentlen); */
|
|
datasent = http_send_asm(request, content, contentlen - 1);
|
|
/* debug_board_printf("--- Sent %d bytes of data.\n", datasent); */
|
|
NSLOG(fetch, DEBUG, "--- Sent %d bytes of multipart post data.\n", datasent);
|
|
}
|
|
}
|
|
else if(post_urlenc) {
|
|
NSLOG(fetch, DEBUG, "http_post: %s", nsurl_access(url));
|
|
request = http_post_asm(nsurl_access(url), NULL, 0, *headers, "application/x-www-form-urlencoded", strlen(post_urlenc));
|
|
if(request != NULL) {
|
|
int datasent = 0;
|
|
/* Send all the data here itself. Move this later to polling maybe. */
|
|
/* debug_board_printf("--- Sending data : %s with length %u\n", post_urlenc, strlen(post_urlenc)); */
|
|
datasent = http_send_asm(request, post_urlenc, strlen(post_urlenc));
|
|
NSLOG(fetch, DEBUG, "--- Sent %d bytes of urlencoded data.\n", datasent);
|
|
}
|
|
}
|
|
else {
|
|
/* Do a GET */
|
|
NSLOG(fetch, DEBUG, "http_get: %s", nsurl_access(url));
|
|
request = http_get_asm(nsurl_access(url), NULL, 0, *headers);
|
|
}
|
|
|
|
if(request == NULL) {
|
|
NSLOG(fetch, ERROR, "Failed to allocate http buffer. Could be multiple reasons for failure (like DNS resolution)", request);
|
|
return NULL;
|
|
}
|
|
|
|
/* This assert is tricky beacause the http library may fail when it fails to resolve the hostname.
|
|
There is no sane way to understand that the hostname is wrong or needs to be fixed.
|
|
assert leads to a crash for no reason. Disabling it, and the code should handle this from now.
|
|
Returning NuLL from here should just force a BadURL to the user.
|
|
*/
|
|
|
|
struct httpfetcher *newfetcher = (struct httpfetcher *) malloc(sizeof(struct httpfetcher));
|
|
assert(newfetcher);
|
|
|
|
newfetcher->next = NULL;
|
|
newfetcher->handle = request;
|
|
newfetcher->url = url;
|
|
newfetcher->headercbdone = false;
|
|
newfetcher->owner = parent_fetch;
|
|
newfetcher->datalen_cb_done = 0;
|
|
|
|
return newfetcher;
|
|
}
|
|
|
|
static bool start_fetch(void *httpf) {
|
|
assert(((struct httpfetcher *)httpf)->owner != NULL);
|
|
|
|
|
|
NSLOG(fetch, DEBUG, "httpf: 0x%x", httpf);
|
|
|
|
add_to_poll((struct httpfetcher *) httpf);
|
|
struct httpfetcher *wrapper = httpf;
|
|
|
|
assert(((struct httpfetcher *)httpf)->owner != NULL);
|
|
|
|
NSLOG(fetch, DEBUG, "END OF add_to_poll: httpf: 0x%x, httpf->handle 0x%x, httpf->next 0x%x", wrapper, wrapper->handle, wrapper->next);
|
|
return true;
|
|
}
|
|
|
|
static bool abort_fetch(void *httpf) {
|
|
NSLOG(fetch, DEBUG, "aborting httpf 0x%x, httpf->handle 0x%x, httpf->next 0x%x", ((struct httpfetcher *)httpf), ((struct httpfetcher *)httpf)->handle, ((struct httpfetcher *)httpf)->next);
|
|
|
|
remove_from_poll(httpf);
|
|
|
|
fetch_remove_from_queues(((struct httpfetcher *)httpf)->owner);
|
|
|
|
fetch_free(((struct httpfetcher *)httpf)->owner);
|
|
return true;
|
|
}
|
|
|
|
static void free_fetch(void *httpf) {
|
|
NSLOG(fetch, DEBUG, "free_fetch httpf 0x%x", ((struct httpfetcher *)httpf));
|
|
|
|
http_disconnect_asm((((struct httpfetcher *)httpf)->handle));
|
|
|
|
http_free_asm((((struct httpfetcher *)httpf)->handle));
|
|
free((struct httpfetcher *)httpf);
|
|
}
|
|
|
|
static void poll_fetch(lwc_string *scheme) {
|
|
bool supported_scheme;
|
|
assert(lwc_string_isequal(scheme, corestring_lwc_http, &supported_scheme) == lwc_error_ok);
|
|
assert(supported_scheme);
|
|
|
|
struct httpfetcher *t = head;
|
|
NSLOG(fetch, DEBUG, "poller head = 0x%x", t);
|
|
|
|
while(t != NULL) {
|
|
NSLOG(fetch, DEBUG, "-- Polling for t 0x%x, http_msg 0x%x, fetch 0x%x [ hcbdone = %s ]", t, t->handle, t->owner, t->headercbdone == true ? "true" : "false");
|
|
NSLOG(fetch, DEBUG, "--- http_msg struct at : %x", t->handle);
|
|
NSLOG(fetch, DEBUG, "--- Header starts at : %x", &(t->handle->http_header));
|
|
NSLOG(fetch, DEBUG, "--- Header Length: %d", t->handle->header_length);
|
|
NSLOG(fetch, DEBUG, "--- Content starts at : %x", &(t->handle->content_ptr));
|
|
NSLOG(fetch, DEBUG, "--- Content Length (received / total): %d / %d", t->handle->content_received, t->handle->content_length);
|
|
NSLOG(fetch, DEBUG, "--- ^ was for url : %s", nsurl_access(t->url));
|
|
|
|
int ret = http_receive_asm(t->handle);
|
|
|
|
if(t->handle->flags & HTTP_ERRORS) {
|
|
fetch_msg msg;
|
|
msg.type = FETCH_ERROR;
|
|
NSLOG(fetch, DEBUG, "---- http_msg -> flags = 0x%x", t->handle->flags);
|
|
msg.data.header_or_data.buf = (const uint8_t *) "HTTPLIB ERROR";
|
|
msg.data.header_or_data.len = strlen("HTTPLIB ERROR");
|
|
struct httpfetcher *t2 = remove_from_poll(t);
|
|
fetch_send_callback(&msg, t->owner);
|
|
t->headercbdone = true;
|
|
|
|
t = t2;
|
|
continue;
|
|
}
|
|
|
|
if(t->headercbdone == false) {
|
|
if (t->handle->flags & HTTP_GOT_HEADER) {
|
|
NSLOG(fetch, DEBUG, "---- Received all HTTP Headers.");
|
|
NSLOG(fetch, DEBUG, "---- response status code = %d", t->handle->status);
|
|
fetch_set_http_code(t->owner, t->handle->status);
|
|
|
|
if(t->handle->status >= 200 && t->handle->status < 300) {
|
|
fetch_msg msg;
|
|
int plen = 0;
|
|
char *ptr = &(t->handle->http_header);
|
|
|
|
while(plen < t->handle->header_length) {
|
|
int j = plen;
|
|
|
|
for(;;j++) {
|
|
if(*(ptr + j) == '\r')
|
|
break;
|
|
}
|
|
|
|
msg.type = FETCH_HEADER;
|
|
msg.data.header_or_data.buf = (const uint8_t *)(ptr + plen);
|
|
msg.data.header_or_data.len = j - plen;
|
|
|
|
char *xx = (char *) malloc(j - plen + 1);
|
|
strncpy(xx, ptr + plen, j-plen);
|
|
xx[j-plen] = '\0';
|
|
NSLOG(fetch, DEBUG, "Headerline: %s", xx);
|
|
fetch_send_callback(&msg, t->owner);
|
|
free(xx);
|
|
|
|
plen = j + 2;
|
|
}
|
|
|
|
t->headercbdone = true;
|
|
}
|
|
else if(t->handle->status >= 300 && t->handle->status < 400) {
|
|
if(t->handle->status == 304) {
|
|
fetch_msg msg;
|
|
msg.type = FETCH_NOTMODIFIED;
|
|
struct httpfetcher *t2 = remove_from_poll(t);
|
|
fetch_send_callback(&msg, t->owner);
|
|
t = t2;
|
|
continue;
|
|
}
|
|
else {
|
|
fetch_msg msg;
|
|
int lenloc = 0;
|
|
char *tmp = http_find_header_field_asm(t->handle, "location");
|
|
while(!isspace(*(tmp + lenloc))) lenloc++;
|
|
|
|
msg.type = FETCH_REDIRECT;
|
|
char *newlocation = malloc(lenloc + 1);
|
|
strncpy(newlocation, tmp, lenloc);
|
|
newlocation[lenloc]='\0';
|
|
msg.data.redirect = newlocation;
|
|
|
|
NSLOG(fetch, DEBUG, "---- [3xx] : Redirect to %s", msg.data.redirect);
|
|
struct httpfetcher *t2 = remove_from_poll(t);
|
|
fetch_send_callback(&msg, t->owner);
|
|
t->headercbdone = true;
|
|
t = t2;
|
|
NSLOG(fetch, DEBUG, "---- DID [3xx] : Redirect to %s", msg.data.redirect);
|
|
/* t = t->next; */
|
|
/* t = head; */
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
fetch_msg msg;
|
|
msg.type = FETCH_ERROR;
|
|
NSLOG(fetch, ERROR, " ---- Unhandled HTTP Code : %d", t->handle->status);
|
|
fetch_send_callback(&msg, t->owner);
|
|
t->headercbdone = true;
|
|
fetch_remove_from_queues(t->owner);
|
|
struct httpfetcher *t2 = remove_from_poll(t);
|
|
fetch_free(t->owner);
|
|
t = t2;
|
|
/* t = t->next; */
|
|
/* t = head; */
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
NSLOG(fetch, DEBUG, "---- Headers not received yet.");
|
|
}
|
|
}
|
|
else if(ret == -1) {
|
|
/* If data was received send it to netsurf core with FETCH_DATA */
|
|
NSLOG(fetch, DEBUG, "Calledback vs received : %u vs %u!", t->datalen_cb_done, t->handle->content_received);
|
|
|
|
if(t->handle->content_received > t->datalen_cb_done) {
|
|
fetch_msg msg;
|
|
NSLOG(fetch, DEBUG, "Doing a data callback\n");
|
|
msg.type = FETCH_DATA;
|
|
msg.data.header_or_data.buf = (const uint8_t *) (t->handle->content_ptr + t->datalen_cb_done);
|
|
msg.data.header_or_data.len = t->handle->content_received - t->datalen_cb_done;
|
|
fetch_send_callback(&msg, t->owner);
|
|
t->datalen_cb_done = t->handle->content_received;
|
|
}
|
|
}
|
|
else if(ret == 0) {
|
|
if(t->handle->content_received > t->datalen_cb_done) {
|
|
/* Callback any remaining data before finishing off */
|
|
fetch_msg msg;
|
|
msg.type = FETCH_DATA;
|
|
msg.data.header_or_data.buf = (const uint8_t *) (t->handle->content_ptr + t->datalen_cb_done);
|
|
msg.data.header_or_data.len = t->handle->content_received - t->datalen_cb_done;
|
|
fetch_send_callback(&msg, t->owner);
|
|
t->datalen_cb_done = t->handle->content_received;
|
|
}
|
|
|
|
fetch_msg msg;
|
|
|
|
msg.type = FETCH_FINISHED;
|
|
msg.data.header_or_data.buf = NULL;
|
|
msg.data.header_or_data.len = 0;
|
|
fetch_send_callback(&msg, t->owner);
|
|
NSLOG(fetch, DEBUG, "---- FETCH_FINISHED for fetch 0x%x", t->owner);
|
|
|
|
fetch_remove_from_queues(t->owner);
|
|
/* t = head; */
|
|
struct httpfetcher *t2 = remove_from_poll(t);
|
|
fetch_free(t->owner);
|
|
t = t2;
|
|
/* t = next; */
|
|
continue;
|
|
}
|
|
|
|
NSLOG(fetch, DEBUG, "Main loop: t going from 0x%x to 0x%x", t->owner, t->next != NULL ? t->next->owner : NULL);
|
|
t = t->next;
|
|
}
|
|
}
|
|
|
|
static void finalize_fetcher(lwc_string *scheme) {
|
|
bool supported_scheme;
|
|
assert(lwc_string_isequal(scheme, corestring_lwc_http, &supported_scheme) == lwc_error_ok);
|
|
lwc_string_unref(scheme);
|
|
}
|
|
|
|
static struct fetcher_operation_table fetcher_ops = {
|
|
.initialise = init_fetcher,
|
|
.acceptable = supported_url_check,
|
|
.setup = setup_fetch,
|
|
.start = start_fetch,
|
|
.abort = abort_fetch,
|
|
.free = free_fetch,
|
|
.poll = poll_fetch,
|
|
.finalise = finalize_fetcher
|
|
};
|
|
|
|
nserror fetch_httplib_kolibri_register(void) {
|
|
lwc_string *scheme = lwc_string_ref(corestring_lwc_http);
|
|
return fetcher_add(scheme, &fetcher_ops);
|
|
}
|