WIP adding devices.json and auth
This commit is contained in:
parent
77cecdbe2a
commit
4f0a7aedfd
|
@ -13,5 +13,5 @@ LONGINT_IMPL = MPZ
|
|||
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
|
||||
|
||||
CIRCUITPY_ESP_FLASH_MODE=dio
|
||||
CIRCUITPY_ESP_FLASH_FREQ=80m
|
||||
CIRCUITPY_ESP_FLASH_FREQ=40m
|
||||
CIRCUITPY_ESP_FLASH_SIZE=16MB
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
STATIC bool inited = false;
|
||||
|
||||
void mdns_server_construct(mdns_server_obj_t *self) {
|
||||
void mdns_server_construct(mdns_server_obj_t *self, bool workflow) {
|
||||
if (inited) {
|
||||
return;
|
||||
}
|
||||
|
@ -46,19 +46,21 @@ void mdns_server_construct(mdns_server_obj_t *self) {
|
|||
snprintf(self->default_hostname, sizeof(self->default_hostname), "cpy-%02x%02x%02x", mac[3], mac[4], mac[5]);
|
||||
common_hal_mdns_server_set_hostname(self, self->default_hostname);
|
||||
|
||||
// Set a delegated entry to ourselves. This allows us to respond to "circuitpython.local"
|
||||
// queries as well.
|
||||
// TODO: Allow for disabling this with `supervisor.disable_web_workflow()`.
|
||||
mdns_ip_addr_t our_ip;
|
||||
esp_netif_get_ip_info(common_hal_wifi_radio_obj.netif, &common_hal_wifi_radio_obj.ip_info);
|
||||
our_ip.next = NULL;
|
||||
our_ip.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
our_ip.addr.u_addr.ip4 = common_hal_wifi_radio_obj.ip_info.ip;
|
||||
our_ip.addr.u_addr.ip6.addr[1] = 0;
|
||||
our_ip.addr.u_addr.ip6.addr[2] = 0;
|
||||
our_ip.addr.u_addr.ip6.addr[3] = 0;
|
||||
our_ip.addr.u_addr.ip6.zone = 0;
|
||||
mdns_delegate_hostname_add("circuitpython", &our_ip);
|
||||
if (workflow) {
|
||||
// Set a delegated entry to ourselves. This allows us to respond to "circuitpython.local"
|
||||
// queries as well.
|
||||
// TODO: Allow for disabling this with `supervisor.disable_web_workflow()`.
|
||||
mdns_ip_addr_t our_ip;
|
||||
esp_netif_get_ip_info(common_hal_wifi_radio_obj.netif, &common_hal_wifi_radio_obj.ip_info);
|
||||
our_ip.next = NULL;
|
||||
our_ip.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
our_ip.addr.u_addr.ip4 = common_hal_wifi_radio_obj.ip_info.ip;
|
||||
our_ip.addr.u_addr.ip6.addr[1] = 0;
|
||||
our_ip.addr.u_addr.ip6.addr[2] = 0;
|
||||
our_ip.addr.u_addr.ip6.addr[3] = 0;
|
||||
our_ip.addr.u_addr.ip6.zone = 0;
|
||||
mdns_delegate_hostname_add("circuitpython", &our_ip);
|
||||
}
|
||||
}
|
||||
|
||||
void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface) {
|
||||
|
@ -69,7 +71,7 @@ void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_
|
|||
if (inited) {
|
||||
mp_raise_RuntimeError(translate("mDNS already initialized"));
|
||||
}
|
||||
mdns_server_construct(self);
|
||||
mdns_server_construct(self, false);
|
||||
}
|
||||
|
||||
void common_hal_mdns_server_deinit(mdns_server_obj_t *self) {
|
||||
|
@ -104,6 +106,48 @@ void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const cha
|
|||
self->instance_name = instance_name;
|
||||
}
|
||||
|
||||
size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol,
|
||||
mp_float_t timeout, mdns_remoteservice_obj_t *out, size_t out_len) {
|
||||
mdns_search_once_t *search = mdns_query_async_new(NULL, service_type, protocol, MDNS_TYPE_PTR, timeout * 1000, 255, NULL);
|
||||
if (search == NULL) {
|
||||
return 0;
|
||||
}
|
||||
mdns_result_t *results;
|
||||
while (!mdns_query_async_get_results(search, 1, &results)) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
mdns_query_async_delete(search);
|
||||
// Count how many results we got.
|
||||
// TODO: Remove this loop when moving off 4.4. Newer APIs will give us num_results
|
||||
// back directly.
|
||||
mdns_result_t *next = results;
|
||||
uint8_t num_results = 0;
|
||||
while (next != NULL) {
|
||||
num_results++;
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
next = results;
|
||||
// Don't error if we're out of memory. Instead, truncate the tuple.
|
||||
uint8_t added = 0;
|
||||
while (next != NULL && added < out_len) {
|
||||
mdns_remoteservice_obj_t *service = &out[added];
|
||||
|
||||
service->result = next;
|
||||
service->base.type = &mdns_remoteservice_type;
|
||||
next = next->next;
|
||||
// Break the linked list so we free each result separately.
|
||||
service->result->next = NULL;
|
||||
added++;
|
||||
}
|
||||
if (added < out_len) {
|
||||
// Free the remaining results from the IDF because we don't have
|
||||
// enough space in Python.
|
||||
mdns_query_results_free(next);
|
||||
}
|
||||
return num_results;
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_float_t timeout) {
|
||||
mdns_search_once_t *search = mdns_query_async_new(NULL, service_type, protocol, MDNS_TYPE_PTR, timeout * 1000, 255, NULL);
|
||||
if (search == NULL) {
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
|
||||
#define MAC_ADDRESS_LENGTH 6
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "radio";
|
||||
|
||||
static void set_mode_station(wifi_radio_obj_t *self, bool state) {
|
||||
wifi_mode_t next_mode;
|
||||
if (state) {
|
||||
|
@ -236,11 +240,12 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
|
|||
if (!common_hal_wifi_radio_get_enabled(self)) {
|
||||
mp_raise_RuntimeError(translate("wifi is not enabled"));
|
||||
}
|
||||
ESP_LOGI(TAG, "connect");
|
||||
wifi_config_t *config = &self->sta_config;
|
||||
|
||||
size_t timeout_ms = timeout * 1000;
|
||||
uint32_t start_time = common_hal_time_monotonic_ms();
|
||||
uint32_t end_time = start_time + timeout_ms;
|
||||
// size_t timeout_ms = timeout * 1000;
|
||||
// uint32_t start_time = common_hal_time_monotonic_ms();
|
||||
// uint32_t end_time = start_time + timeout_ms;
|
||||
|
||||
EventBits_t bits;
|
||||
// can't block since both bits are false after wifi_init
|
||||
|
@ -304,7 +309,9 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
|
|||
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
|
||||
self->starting_retries = 5;
|
||||
self->retries_left = 5;
|
||||
ESP_LOGI(TAG, "wifi connect");
|
||||
esp_wifi_connect();
|
||||
ESP_LOGI(TAG, "wifi connect done");
|
||||
|
||||
do {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
|
@ -314,10 +321,11 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
|
|||
pdTRUE,
|
||||
0);
|
||||
// Don't retry anymore if we're over our time budget.
|
||||
if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) {
|
||||
self->retries_left = 0;
|
||||
}
|
||||
// if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) {
|
||||
// self->retries_left = 0;
|
||||
// }
|
||||
} while ((bits & (WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT)) == 0 && !mp_hal_is_interrupted());
|
||||
ESP_LOGI(TAG, "connect done");
|
||||
if ((bits & WIFI_DISCONNECTED_BIT) != 0) {
|
||||
if (self->last_disconnect_reason == WIFI_REASON_AUTH_FAIL) {
|
||||
return WIFI_RADIO_ERROR_AUTH_FAIL;
|
||||
|
|
|
@ -44,7 +44,7 @@ wifi_radio_obj_t common_hal_wifi_radio_obj;
|
|||
|
||||
#include "supervisor/workflow.h"
|
||||
|
||||
static const char *TAG = "wifi";
|
||||
static const char *TAG = "CP wifi";
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data) {
|
||||
|
|
|
@ -297,7 +297,7 @@ CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
|||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048
|
||||
CONFIG_ESP_INT_WDT=y
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=1000
|
||||
CONFIG_ESP_INT_WDT_CHECK_CPU1=y
|
||||
# CONFIG_ESP_TASK_WDT is not set
|
||||
# CONFIG_ESP_PANIC_HANDLER_IRAM is not set
|
||||
|
@ -805,9 +805,9 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
|
|||
CONFIG_LOG_BOOTLOADER_LEVEL=3
|
||||
# CONFIG_APP_ROLLBACK_ENABLE is not set
|
||||
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
|
||||
# CONFIG_FLASHMODE_QIO is not set
|
||||
CONFIG_FLASHMODE_QIO=y
|
||||
# CONFIG_FLASHMODE_QOUT is not set
|
||||
CONFIG_FLASHMODE_DIO=y
|
||||
# CONFIG_FLASHMODE_DIO is not set
|
||||
# CONFIG_FLASHMODE_DOUT is not set
|
||||
# CONFIG_MONITOR_BAUD_9600B is not set
|
||||
# CONFIG_MONITOR_BAUD_57600B is not set
|
||||
|
|
|
@ -136,7 +136,7 @@ MP_PROPERTY_GETSET(mdns_server_instance_name_obj,
|
|||
//| :param float/int timeout: Time to wait for responses"""
|
||||
//| ...
|
||||
//|
|
||||
STATIC mp_obj_t mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
STATIC mp_obj_t _mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
mdns_server_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
check_for_deinit(self);
|
||||
|
||||
|
@ -156,7 +156,7 @@ STATIC mp_obj_t mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_
|
|||
|
||||
return common_hal_mdns_server_find(self, service_type, protocol, timeout);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, mdns_server_find);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, _mdns_server_find);
|
||||
|
||||
//| def advertise_service(self, *, service_type: str, protocol: str, port: int) -> None:
|
||||
//| """Respond to queries for the given service with the given port.
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "common-hal/mdns/Server.h"
|
||||
|
||||
#include "shared-bindings/mdns/RemoteService.h"
|
||||
|
||||
extern const mp_obj_type_t mdns_server_type;
|
||||
|
||||
void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface);
|
||||
|
@ -43,4 +45,6 @@ mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *servic
|
|||
void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port);
|
||||
|
||||
// For internal use.
|
||||
void mdns_server_construct(mdns_server_obj_t *self);
|
||||
void mdns_server_construct(mdns_server_obj_t *self, bool workflow);
|
||||
size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol,
|
||||
mp_float_t timeout, mdns_remoteservice_obj_t *out, size_t out_len);
|
||||
|
|
|
@ -28,13 +28,14 @@
|
|||
|
||||
#include "extmod/vfs.h"
|
||||
#include "extmod/vfs_fat.h"
|
||||
|
||||
#include "genhdr/mpversion.h"
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#include "shared-bindings/wifi/Radio.h"
|
||||
#include "supervisor/shared/translate/translate.h"
|
||||
#include "supervisor/shared/web_workflow/web_workflow.h"
|
||||
|
||||
#include "shared-bindings/mdns/RemoteService.h"
|
||||
#include "shared-bindings/mdns/Server.h"
|
||||
#include "shared-bindings/socketpool/__init__.h"
|
||||
#include "shared-bindings/socketpool/Socket.h"
|
||||
|
@ -71,6 +72,7 @@ typedef struct {
|
|||
size_t offset;
|
||||
bool redirect;
|
||||
bool done;
|
||||
bool authenticated;
|
||||
} _request;
|
||||
|
||||
static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE;
|
||||
|
@ -83,6 +85,72 @@ static socketpool_socket_obj_t active;
|
|||
|
||||
static _request active_request;
|
||||
|
||||
static char _api_password[64];
|
||||
|
||||
// in_len is the number of bytes to encode. out_len is the number of bytes we
|
||||
// have to do it.
|
||||
static bool _base64_in_place(char *buf, size_t in_len, size_t out_len) {
|
||||
size_t triples = (((in_len - 1) / 3) + 1);
|
||||
size_t encoded_len = triples * 4;
|
||||
if (encoded_len + 1 > out_len) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "triples %d", triples);
|
||||
|
||||
// First pass, we convert input buffer to numeric base 64 values
|
||||
char *in = buf + (triples - 1) * 3;
|
||||
char *out = buf + (triples - 1) * 4;
|
||||
int r = in_len % 3;
|
||||
int partial = 0;
|
||||
if (r != 0) {
|
||||
out[0] = (in[0] & 0xFC) >> 2;
|
||||
out[1] = (in[0] & 0x03) << 4;
|
||||
if (r == 2) {
|
||||
out[1] |= (in[1] & 0xF0) >> 4;
|
||||
out[2] = (in[1] & 0x0F) << 2;
|
||||
} else {
|
||||
out[2] = 64;
|
||||
}
|
||||
out[3] = 64;
|
||||
in -= 3;
|
||||
out -= 4;
|
||||
partial = 1;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "no partial");
|
||||
}
|
||||
buf[encoded_len] = '\0';
|
||||
for (size_t i = 0; i < triples - partial; i++) {
|
||||
ESP_LOGI(TAG, "in %d %d %d", in[0], in[1], in[2]);
|
||||
out[0] = (in[0] & 0xFC) >> 2;
|
||||
out[1] = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
|
||||
out[2] = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6;
|
||||
out[3] = in[2] & 0x3F;
|
||||
ESP_LOGI(TAG, "out %d %d %d %d", out[0], out[1], out[2], out[3]);
|
||||
in -= 3;
|
||||
out -= 4;
|
||||
}
|
||||
|
||||
// Second pass, we convert number base 64 values to actual base64 ascii encoding
|
||||
out = buf;
|
||||
for (mp_uint_t j = 0; j < encoded_len; j++) {
|
||||
if (*out < 26) {
|
||||
*out += 'A';
|
||||
} else if (*out < 52) {
|
||||
*out += 'a' - 26;
|
||||
} else if (*out < 62) {
|
||||
*out += '0' - 52;
|
||||
} else if (*out == 62) {
|
||||
*out = '+';
|
||||
} else if (*out == 63) {
|
||||
*out = '/';
|
||||
} else {
|
||||
*out = '=';
|
||||
}
|
||||
out++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void supervisor_web_workflow_status(void) {
|
||||
serial_write_compressed(translate("Wi-Fi: "));
|
||||
|
@ -145,7 +213,8 @@ void supervisor_start_web_workflow(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
mdns_server_construct(&mdns);
|
||||
mdns_server_construct(&mdns, true);
|
||||
common_hal_mdns_server_set_instance_name(&mdns, MICROPY_HW_BOARD_NAME);
|
||||
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", 80);
|
||||
|
||||
pool.base.type = &socketpool_socketpool_type;
|
||||
|
@ -160,6 +229,16 @@ void supervisor_start_web_workflow(void) {
|
|||
common_hal_socketpool_socket_bind(&listening, ip, strlen(ip), 80);
|
||||
common_hal_socketpool_socket_listen(&listening, 1);
|
||||
|
||||
|
||||
mp_int_t api_password_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, sizeof(_api_password) - 2);
|
||||
if (api_password_len > 0) {
|
||||
_api_password[0] = ':';
|
||||
_api_password[api_password_len + 1] = '\0';
|
||||
ESP_LOGW(TAG, "password before: %s", _api_password);
|
||||
_base64_in_place(_api_password, api_password_len + 1, sizeof(_api_password));
|
||||
ESP_LOGW(TAG, "password encoded: %s", _api_password);
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "listening on socket %d", listening.num);
|
||||
|
||||
active.base.type = &socketpool_socket_type;
|
||||
|
@ -234,6 +313,37 @@ static bool _endswith(const char *str, const char *suffix) {
|
|||
return strcmp(str + (strlen(str) - strlen(suffix)), suffix) == 0;
|
||||
}
|
||||
|
||||
static void _reply_missing(socketpool_socket_obj_t *socket) {
|
||||
const char *response = "HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"CircuitPython\"\r\n\r\n";
|
||||
_send_str(socket, response);
|
||||
}
|
||||
|
||||
static void _reply_forbidden(socketpool_socket_obj_t *socket) {
|
||||
const char *response = "HTTP/1.1 403 Forbidden\r\n"
|
||||
"Content-Length: 0\r\n\r\n";
|
||||
_send_str(socket, response);
|
||||
}
|
||||
|
||||
static void _reply_unauthorized(socketpool_socket_obj_t *socket) {
|
||||
const char *response = "HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"CircuitPython\"\r\n\r\n";
|
||||
_send_str(socket, response);
|
||||
}
|
||||
|
||||
static void _reply_redirect(socketpool_socket_obj_t *socket, const char *path) {
|
||||
const char *redirect_response = "HTTP/1.1 301 Moved Permanently\r\nConnection: close\r\nContent-Length: 0\r\nLocation: http://";
|
||||
int nodelay = 1;
|
||||
lwip_setsockopt(socket->num, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
|
||||
_send_str(socket, redirect_response);
|
||||
_send_str(socket, common_hal_mdns_server_get_instance_name(&mdns));
|
||||
_send_str(socket, ".local");
|
||||
_send_str(socket, path);
|
||||
_send_str(socket, "\r\n\r\n");
|
||||
}
|
||||
|
||||
static void _process_request(socketpool_socket_obj_t *socket, _request *request) {
|
||||
bool more = true;
|
||||
bool error = false;
|
||||
|
@ -348,129 +458,188 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
|
|||
return;
|
||||
}
|
||||
if (request->redirect) {
|
||||
const char *redirect_response = "HTTP/1.1 301 Moved Permanently\r\nConnection: close\r\nContent-Length: 0\r\nLocation: http://";
|
||||
int nodelay = 1;
|
||||
int err = lwip_setsockopt(socket->num, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
|
||||
int sent = socketpool_socket_send(socket, (const uint8_t *)redirect_response, strlen(redirect_response));
|
||||
const char *instance_name = common_hal_mdns_server_get_instance_name(&mdns);
|
||||
sent += socketpool_socket_send(socket, (const uint8_t *)instance_name, strlen(instance_name));
|
||||
const char *local = ".local";
|
||||
sent += socketpool_socket_send(socket, (const uint8_t *)local, strlen(local));
|
||||
sent += socketpool_socket_send(socket, (const uint8_t *)request->path, strlen(request->path));
|
||||
const char *two_lines = "\r\n\r\n";
|
||||
sent += socketpool_socket_send(socket, (const uint8_t *)two_lines, strlen(two_lines));
|
||||
ESP_LOGW(TAG, "sent %d %d", sent, err);
|
||||
_reply_redirect(socket, request->path);
|
||||
} else if (memcmp(request->path, "/fs/", 4) == 0) {
|
||||
ESP_LOGW(TAG, "filesystem %s %s", request->method, request->path + 3);
|
||||
const char *path = request->path + 3;
|
||||
size_t pathlen = strlen(path);
|
||||
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
|
||||
// Trailing / is a directory.
|
||||
if (path[pathlen - 1] == '/') {
|
||||
if (strcmp(request->method, "GET") == 0) {
|
||||
FF_DIR dir;
|
||||
FRESULT res = f_opendir(fs, &dir, path);
|
||||
if (res != FR_OK) {
|
||||
// TODO: Send 404
|
||||
}
|
||||
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
|
||||
"Content-Type: text/html\r\n\r\n";
|
||||
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
||||
_send_chunk(socket, "<!DOCTYPE html><html><head><title>");
|
||||
_send_chunk(socket, path);
|
||||
_send_chunk(socket, "</title><meta charset=\"UTF-8\"></head><body><h1>");
|
||||
_send_chunk(socket, path);
|
||||
_send_chunk(socket, "</h1><pre>");
|
||||
if (strlen(path) > 1) {
|
||||
_send_chunk(socket, "🗀\t<a href=\"..\">..</a><br/>");
|
||||
}
|
||||
|
||||
FILINFO file_info;
|
||||
char *fn = file_info.fname;
|
||||
res = f_readdir(&dir, &file_info);
|
||||
while (res == FR_OK && fn[0] != 0) {
|
||||
// uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
|
||||
// (file_info.fdate >> 5) & 0xf,
|
||||
// file_info.fdate & 0x1f,
|
||||
// file_info.ftime >> 11,
|
||||
// (file_info.ftime >> 5) & 0x1f,
|
||||
// (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
|
||||
// entry->truncated_time = truncated_time;
|
||||
// if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
// entry->flags = 1; // Directory
|
||||
// entry->file_size = 0;
|
||||
// } else {
|
||||
// entry->flags = 0;
|
||||
// entry->file_size = file_info.fsize;
|
||||
// }
|
||||
// _send_chunk(socket, "<li>");
|
||||
if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
_send_chunk(socket, "🗀\t");
|
||||
} else if (_endswith(path, ".txt") || _endswith(path, ".py")) {
|
||||
_send_chunk(socket, "🖹\t");
|
||||
} else {
|
||||
_send_chunk(socket, "⭳\t");
|
||||
if (!request->authenticated) {
|
||||
if (_api_password[0] != '\0') {
|
||||
_reply_unauthorized(socket);
|
||||
} else {
|
||||
_reply_forbidden(socket);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "filesystem %s %s", request->method, request->path + 3);
|
||||
const char *path = request->path + 3;
|
||||
size_t pathlen = strlen(path);
|
||||
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
|
||||
// Trailing / is a directory.
|
||||
if (path[pathlen - 1] == '/') {
|
||||
if (strcmp(request->method, "GET") == 0) {
|
||||
FF_DIR dir;
|
||||
FRESULT res = f_opendir(fs, &dir, path);
|
||||
if (res != FR_OK) {
|
||||
_reply_missing(socket);
|
||||
}
|
||||
_send_chunk(socket, "<a href=\"");
|
||||
_send_chunk(socket, request->path);
|
||||
_send_chunk(socket, file_info.fname);
|
||||
if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
_send_chunk(socket, "/");
|
||||
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
|
||||
"Content-Type: text/html\r\n\r\n";
|
||||
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
||||
_send_chunk(socket, "<!DOCTYPE html><html><head><title>");
|
||||
_send_chunk(socket, path);
|
||||
_send_chunk(socket, "</title><meta charset=\"UTF-8\"></head><body><h1>");
|
||||
_send_chunk(socket, path);
|
||||
_send_chunk(socket, "</h1><pre>");
|
||||
if (strlen(path) > 1) {
|
||||
_send_chunk(socket, "🗀\t<a href=\"..\">..</a><br/>");
|
||||
}
|
||||
_send_chunk(socket, "\">");
|
||||
|
||||
_send_chunk(socket, file_info.fname);
|
||||
_send_chunk(socket, "</a><a>✏️</a><a>🗑️</a><br/>");
|
||||
FILINFO file_info;
|
||||
char *fn = file_info.fname;
|
||||
res = f_readdir(&dir, &file_info);
|
||||
}
|
||||
_send_chunk(socket, "</pre>Upload:<input type=\"file\" multiple></body></html>");
|
||||
_send_chunk(socket, "");
|
||||
f_closedir(&dir);
|
||||
}
|
||||
|
||||
} else { // Dealing with a file.
|
||||
if (strcmp(request->method, "GET") == 0) {
|
||||
FIL active_file;
|
||||
FRESULT result = f_open(fs, &active_file, path, FA_READ);
|
||||
|
||||
if (result != FR_OK) {
|
||||
// TODO: 404
|
||||
}
|
||||
uint32_t total_length = f_size(&active_file);
|
||||
char encoded_len[10];
|
||||
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
|
||||
|
||||
_send_str(socket, "HTTP/1.1 200 OK\r\nContent-Length: ");
|
||||
_send_str(socket, encoded_len);
|
||||
_send_str(socket, "\r\n");
|
||||
if (_endswith(path, ".txt") || _endswith(path, ".py")) {
|
||||
_send_str(socket, "Content-Type: text/plain\r\n");
|
||||
} else {
|
||||
_send_str(socket, "Content-Type: application/octet-stream\r\n");
|
||||
}
|
||||
_send_str(socket, "\r\n");
|
||||
|
||||
uint32_t total_read = 0;
|
||||
while (total_read < total_length) {
|
||||
uint8_t data_buffer[64];
|
||||
size_t quantity_read;
|
||||
f_read(&active_file, data_buffer, 64, &quantity_read);
|
||||
total_read += quantity_read;
|
||||
uint32_t send_offset = 0;
|
||||
while (send_offset < quantity_read) {
|
||||
int sent = socketpool_socket_send(socket, data_buffer + send_offset, quantity_read - send_offset);
|
||||
if (sent < 0) {
|
||||
ESP_LOGE(TAG, "file send error %d", sent);
|
||||
break;
|
||||
while (res == FR_OK && fn[0] != 0) {
|
||||
// uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
|
||||
// (file_info.fdate >> 5) & 0xf,
|
||||
// file_info.fdate & 0x1f,
|
||||
// file_info.ftime >> 11,
|
||||
// (file_info.ftime >> 5) & 0x1f,
|
||||
// (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
|
||||
// entry->truncated_time = truncated_time;
|
||||
// if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
// entry->flags = 1; // Directory
|
||||
// entry->file_size = 0;
|
||||
// } else {
|
||||
// entry->flags = 0;
|
||||
// entry->file_size = file_info.fsize;
|
||||
// }
|
||||
// _send_chunk(socket, "<li>");
|
||||
if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
_send_chunk(socket, "🗀\t");
|
||||
} else if (_endswith(file_info.fname, ".txt") ||
|
||||
_endswith(file_info.fname, ".py")) {
|
||||
_send_chunk(socket, "🖹\t");
|
||||
} else {
|
||||
_send_chunk(socket, "⬇\t");
|
||||
}
|
||||
send_offset += sent;
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "file return done");
|
||||
_send_chunk(socket, "<a href=\"");
|
||||
_send_chunk(socket, request->path);
|
||||
_send_chunk(socket, file_info.fname);
|
||||
if ((file_info.fattrib & AM_DIR) != 0) {
|
||||
_send_chunk(socket, "/");
|
||||
}
|
||||
_send_chunk(socket, "\">");
|
||||
|
||||
f_close(&active_file);
|
||||
_send_chunk(socket, file_info.fname);
|
||||
_send_chunk(socket, "</a><a>✏️</a><a>🗑️</a><br/>");
|
||||
res = f_readdir(&dir, &file_info);
|
||||
}
|
||||
_send_chunk(socket, "</pre>Upload:<input type=\"file\" multiple></body></html>");
|
||||
_send_chunk(socket, "");
|
||||
f_closedir(&dir);
|
||||
}
|
||||
|
||||
} else { // Dealing with a file.
|
||||
if (strcmp(request->method, "GET") == 0) {
|
||||
FIL active_file;
|
||||
FRESULT result = f_open(fs, &active_file, path, FA_READ);
|
||||
|
||||
if (result != FR_OK) {
|
||||
// TODO: 404
|
||||
}
|
||||
uint32_t total_length = f_size(&active_file);
|
||||
char encoded_len[10];
|
||||
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
|
||||
|
||||
_send_str(socket, "HTTP/1.1 200 OK\r\nContent-Length: ");
|
||||
_send_str(socket, encoded_len);
|
||||
_send_str(socket, "\r\n");
|
||||
if (_endswith(path, ".txt") || _endswith(path, ".py")) {
|
||||
_send_str(socket, "Content-Type: text/plain\r\n");
|
||||
} else {
|
||||
_send_str(socket, "Content-Type: application/octet-stream\r\n");
|
||||
}
|
||||
_send_str(socket, "\r\n");
|
||||
|
||||
uint32_t total_read = 0;
|
||||
while (total_read < total_length) {
|
||||
uint8_t data_buffer[64];
|
||||
size_t quantity_read;
|
||||
f_read(&active_file, data_buffer, 64, &quantity_read);
|
||||
total_read += quantity_read;
|
||||
uint32_t send_offset = 0;
|
||||
while (send_offset < quantity_read) {
|
||||
int sent = socketpool_socket_send(socket, data_buffer + send_offset, quantity_read - send_offset);
|
||||
if (sent < 0) {
|
||||
ESP_LOGE(TAG, "file send error %d", sent);
|
||||
break;
|
||||
}
|
||||
send_offset += sent;
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "file return done");
|
||||
|
||||
f_close(&active_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (memcmp(request->path, "/cp/", 4) == 0) {
|
||||
ESP_LOGW(TAG, "circuitpython %s %s", request->method, request->path + 3);
|
||||
const char *path = request->path + 3;
|
||||
if (strcmp(request->method, "GET") != 0) {
|
||||
// Return error if not a GET
|
||||
}
|
||||
if (strcmp(path, "/devices.json") == 0) {
|
||||
mdns_remoteservice_obj_t found_devices[32];
|
||||
size_t total_results = mdns_server_find(&mdns, "_circuitpython", "_tcp", 1, found_devices, MP_ARRAY_SIZE(found_devices));
|
||||
size_t count = MIN(total_results, MP_ARRAY_SIZE(found_devices));
|
||||
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n";
|
||||
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
||||
_send_chunk(socket, "{\"total\": ");
|
||||
char total_encoded[4];
|
||||
snprintf(total_encoded, sizeof(total_encoded), "%d", total_results);
|
||||
_send_chunk(socket, total_encoded);
|
||||
_send_chunk(socket, ", \"devices\": [");
|
||||
ESP_LOGW(TAG, "%d total devices", total_results);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
_send_chunk(socket, ",");
|
||||
}
|
||||
_send_chunk(socket, "{\"hostname\": \"");
|
||||
_send_chunk(socket, common_hal_mdns_remoteservice_get_hostname(&found_devices[i]));
|
||||
_send_chunk(socket, "\", \"instance_name\": \"");
|
||||
_send_chunk(socket, common_hal_mdns_remoteservice_get_instance_name(&found_devices[i]));
|
||||
_send_chunk(socket, "\", \"port\": ");
|
||||
char port_encoded[4];
|
||||
int port = common_hal_mdns_remoteservice_get_port(&found_devices[i]);
|
||||
snprintf(port_encoded, sizeof(port_encoded), "%d", port);
|
||||
_send_chunk(socket, port_encoded);
|
||||
_send_chunk(socket, "}");
|
||||
common_hal_mdns_remoteservice_deinit(&found_devices[i]);
|
||||
}
|
||||
_send_chunk(socket, "]}");
|
||||
// Empty chunk signals the end of the response.
|
||||
_send_chunk(socket, "");
|
||||
} else if (strcmp(path, "/version.json") == 0) {
|
||||
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n";
|
||||
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
||||
_send_chunk(socket, "{\"web_api_version\": 1, \"version\": \"");
|
||||
_send_chunk(socket, MICROPY_GIT_TAG);
|
||||
_send_chunk(socket, "\", \"build_date\": \"");
|
||||
_send_chunk(socket, MICROPY_BUILD_DATE);
|
||||
_send_chunk(socket, "\", \"board_name\": \"");
|
||||
_send_chunk(socket, MICROPY_HW_BOARD_NAME);
|
||||
_send_chunk(socket, "\", \"mcu_name\": \"");
|
||||
_send_chunk(socket, MICROPY_HW_MCU_NAME);
|
||||
_send_chunk(socket, "\", \"board_id\": \"");
|
||||
_send_chunk(socket, CIRCUITPY_BOARD_ID);
|
||||
_send_chunk(socket, "\", \"creator_id\": ");
|
||||
char encoded_id[8];
|
||||
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATOR_ID);
|
||||
_send_chunk(socket, encoded_id);
|
||||
_send_chunk(socket, ", \"creation_id\": ");
|
||||
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATION_ID);
|
||||
_send_chunk(socket, encoded_id);
|
||||
_send_chunk(socket, "}");
|
||||
// Empty chunk signals the end of the response.
|
||||
_send_chunk(socket, "");
|
||||
}
|
||||
} else {
|
||||
const char *ok_response = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/plain\r\n\r\nHello World";
|
||||
int sent = socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
||||
|
|
Loading…
Reference in New Issue