Merge remote-tracking branch 'adafruit/main' into upload_folder
This commit is contained in:
commit
3aa5f60cb9
|
@ -202,6 +202,24 @@ Example:
|
||||||
curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### Move
|
||||||
|
Moves the directory at the given path to ``X-Destination``. Also known as rename.
|
||||||
|
|
||||||
|
The custom `X-Destination` header stores the destination path of the directory.
|
||||||
|
|
||||||
|
* `201 Created` - Directory renamed
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
|
* `404 Not Found` - Source directory not found or destination path is missing
|
||||||
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
|
* `412 Precondition Failed` - The destination path is already in use
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -v -u :passw0rd -X MOVE -H "X-Destination: /fs/lib/hello2/" -L --location-trusted http://circuitpython.local/fs/lib/hello/
|
||||||
|
```
|
||||||
|
|
||||||
##### DELETE
|
##### DELETE
|
||||||
Deletes the directory and all of its contents.
|
Deletes the directory and all of its contents.
|
||||||
|
|
||||||
|
@ -214,7 +232,7 @@ Deletes the directory and all of its contents.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello2/world/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,6 +288,25 @@ curl -v -u :passw0rd -L --location-trusted http://circuitpython.local/fs/lib/hel
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
##### Move
|
||||||
|
Moves the file at the given path to the ``X-Destination``. Also known as rename.
|
||||||
|
|
||||||
|
The custom `X-Destination` header stores the destination path of the file.
|
||||||
|
|
||||||
|
* `201 Created` - File renamed
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
|
* `404 Not Found` - Source file not found or destination path is missing
|
||||||
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
|
* `412 Precondition Failed` - The destination path is already in use
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -v -u :passw0rd -X MOVE -H "X-Destination: /fs/lib/hello/world2.txt" -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
##### DELETE
|
##### DELETE
|
||||||
Deletes the file.
|
Deletes the file.
|
||||||
|
|
||||||
|
@ -283,7 +320,7 @@ Deletes the file.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world2.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### `/cp/`
|
### `/cp/`
|
||||||
|
|
|
@ -30,22 +30,9 @@
|
||||||
#include "peripherals/touch.h"
|
#include "peripherals/touch.h"
|
||||||
#include "shared-bindings/microcontroller/Pin.h"
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
|
||||||
static uint16_t get_raw_reading(touchio_touchin_obj_t *self) {
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
|
||||||
uint16_t touch_value;
|
|
||||||
#else
|
|
||||||
uint32_t touch_value;
|
|
||||||
#endif;
|
|
||||||
touch_pad_read_raw_data(self->pin->touch_channel, &touch_value);
|
|
||||||
if (touch_value > UINT16_MAX) {
|
|
||||||
return UINT16_MAX;
|
|
||||||
}
|
|
||||||
return (uint16_t)touch_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void common_hal_touchio_touchin_construct(touchio_touchin_obj_t *self,
|
void common_hal_touchio_touchin_construct(touchio_touchin_obj_t *self,
|
||||||
const mcu_pin_obj_t *pin) {
|
const mcu_pin_obj_t *pin) {
|
||||||
if (pin->touch_channel == TOUCH_PAD_MAX) {
|
if (pin->touch_channel == NO_TOUCH_CHANNEL) {
|
||||||
raise_ValueError_invalid_pin();
|
raise_ValueError_invalid_pin();
|
||||||
}
|
}
|
||||||
claim_pin(pin);
|
claim_pin(pin);
|
||||||
|
@ -53,15 +40,12 @@ void common_hal_touchio_touchin_construct(touchio_touchin_obj_t *self,
|
||||||
// initialize touchpad
|
// initialize touchpad
|
||||||
peripherals_touch_init(pin->touch_channel);
|
peripherals_touch_init(pin->touch_channel);
|
||||||
|
|
||||||
// wait for touch data to reset
|
|
||||||
mp_hal_delay_ms(10);
|
|
||||||
|
|
||||||
// Set a "touched" threshold not too far above the initial value.
|
// Set a "touched" threshold not too far above the initial value.
|
||||||
// For simple finger touch, the values may vary as much as a factor of two,
|
// For simple finger touch, the values may vary as much as a factor of two,
|
||||||
// but for touches using fruit or other objects, the difference is much less.
|
// but for touches using fruit or other objects, the difference is much less.
|
||||||
|
|
||||||
self->pin = pin;
|
self->pin = pin;
|
||||||
self->threshold = get_raw_reading(self) + 100;
|
self->threshold = common_hal_touchio_touchin_get_raw_value(self) + 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_touchio_touchin_deinited(touchio_touchin_obj_t *self) {
|
bool common_hal_touchio_touchin_deinited(touchio_touchin_obj_t *self) {
|
||||||
|
@ -77,11 +61,11 @@ void common_hal_touchio_touchin_deinit(touchio_touchin_obj_t *self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_touchio_touchin_get_value(touchio_touchin_obj_t *self) {
|
bool common_hal_touchio_touchin_get_value(touchio_touchin_obj_t *self) {
|
||||||
return get_raw_reading(self) > self->threshold;
|
return common_hal_touchio_touchin_get_raw_value(self) > self->threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t common_hal_touchio_touchin_get_raw_value(touchio_touchin_obj_t *self) {
|
uint16_t common_hal_touchio_touchin_get_raw_value(touchio_touchin_obj_t *self) {
|
||||||
return get_raw_reading(self);
|
return peripherals_touch_read(self->pin->touch_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t common_hal_touchio_touchin_get_threshold(touchio_touchin_obj_t *self) {
|
uint16_t common_hal_touchio_touchin_get_threshold(touchio_touchin_obj_t *self) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ void peripherals_touch_never_reset(const bool enable) {
|
||||||
void peripherals_touch_init(const touch_pad_t touchpad) {
|
void peripherals_touch_init(const touch_pad_t touchpad) {
|
||||||
if (!touch_inited) {
|
if (!touch_inited) {
|
||||||
touch_pad_init();
|
touch_pad_init();
|
||||||
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
|
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_SW);
|
||||||
}
|
}
|
||||||
// touch_pad_config() must be done before touch_pad_fsm_start() the first time.
|
// touch_pad_config() must be done before touch_pad_fsm_start() the first time.
|
||||||
// Otherwise the calibration is wrong and we get maximum raw values if there is
|
// Otherwise the calibration is wrong and we get maximum raw values if there is
|
||||||
|
@ -52,13 +52,27 @@ void peripherals_touch_init(const touch_pad_t touchpad) {
|
||||||
touch_pad_config(touchpad, 0);
|
touch_pad_config(touchpad, 0);
|
||||||
#else
|
#else
|
||||||
touch_pad_config(touchpad);
|
touch_pad_config(touchpad);
|
||||||
#endif
|
|
||||||
if (!touch_inited) {
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
|
||||||
touch_pad_sw_start();
|
|
||||||
#else
|
|
||||||
touch_pad_fsm_start();
|
touch_pad_fsm_start();
|
||||||
#endif
|
#endif
|
||||||
touch_inited = true;
|
touch_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t peripherals_touch_read(touch_pad_t touchpad) {
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
|
uint16_t touch_value;
|
||||||
|
touch_pad_read(touchpad, &touch_value);
|
||||||
|
// ESP32 touch_pad_read() returns a lower value when a pin is touched instead of a higher value.
|
||||||
|
// Flip the values around to be consistent with TouchIn assumptions.
|
||||||
|
return UINT16_MAX - touch_value;
|
||||||
|
#else
|
||||||
|
uint32_t touch_value;
|
||||||
|
touch_pad_sw_start();
|
||||||
|
while (!touch_pad_meas_is_done()) {
|
||||||
|
}
|
||||||
|
touch_pad_read_raw_data(touchpad, &touch_value);
|
||||||
|
if (touch_value > UINT16_MAX) {
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
return (uint16_t)touch_value;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "driver/touch_pad.h"
|
#include "driver/touch_pad.h"
|
||||||
|
|
||||||
|
extern uint16_t peripherals_touch_read(touch_pad_t touchpad);
|
||||||
extern void peripherals_touch_reset(void);
|
extern void peripherals_touch_reset(void);
|
||||||
extern void peripherals_touch_never_reset(const bool enable);
|
extern void peripherals_touch_never_reset(const bool enable);
|
||||||
extern void peripherals_touch_init(const touch_pad_t touchpad);
|
extern void peripherals_touch_init(const touch_pad_t touchpad);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Online Code Editor</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="module" src="https://code.circuitpython.org/assets/js/device.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -10,9 +10,9 @@
|
||||||
<body>
|
<body>
|
||||||
<h1><a href="/"><img src="/favicon.ico"/></a> <span id="path"></span></h1>
|
<h1><a href="/"><img src="/favicon.ico"/></a> <span id="path"></span></h1>
|
||||||
<div id="usbwarning" style="display: none;">ℹ️ USB is using the storage. Only allowing reads. See <a href="https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage">the CircuitPython Essentials: Storage guide</a> for details.</div>
|
<div id="usbwarning" style="display: none;">ℹ️ USB is using the storage. Only allowing reads. See <a href="https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage">the CircuitPython Essentials: Storage guide</a> for details.</div>
|
||||||
<template id="row"><tr><td></td><td></td><td><a></a></td><td></td><td><button class="delete">🗑️</button></td><td><a class="edit_link" href="">Edit</a></td></tr></template>
|
<template id="row"><tr><td></td><td></td><td><a class="path"></a></td><td class="modtime"></td><td><button class="rename">✏️ Rename</button></td><td><button class="delete">🗑️ Delete</button></td><td><a class="edit_link" href=""><button>📝 Edit</button></a></td></tr></template>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th></th></tr></thead>
|
<thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th colspan="3"></th></tr></thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
@ -6,21 +6,22 @@ var url_base = window.location;
|
||||||
var current_path;
|
var current_path;
|
||||||
var editable = undefined;
|
var editable = undefined;
|
||||||
|
|
||||||
|
function compareValues(a, b) {
|
||||||
|
if (a.directory == b.directory && a.name.toLowerCase() === b.name.toLowerCase()) {
|
||||||
|
return 0;
|
||||||
|
} else if (a.directory != b.directory) {
|
||||||
|
return a.directory < b.directory ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function set_upload_enabled(enabled) {
|
function set_upload_enabled(enabled) {
|
||||||
files.disabled = !enabled;
|
files.disabled = !enabled;
|
||||||
dirs.disabled = !enabled;
|
dirs.disabled = !enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh_list() {
|
async function refresh_list() {
|
||||||
|
|
||||||
function compareValues(a, b) {
|
|
||||||
if (a.directory == b.directory && a.name.toLowerCase() === b.name.toLowerCase()) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return a.directory.toString().substring(3,4)+a.name.toLowerCase() < b.directory.toString().substring(3,4)+b.name.toLowerCase() ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
current_path = window.location.hash.substr(1);
|
current_path = window.location.hash.substr(1);
|
||||||
if (current_path == "") {
|
if (current_path == "") {
|
||||||
current_path = "/";
|
current_path = "/";
|
||||||
|
@ -58,7 +59,7 @@ async function refresh_list() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.location.path != "/fs/") {
|
if (current_path != "/") {
|
||||||
var clone = template.content.cloneNode(true);
|
var clone = template.content.cloneNode(true);
|
||||||
var td = clone.querySelectorAll("td");
|
var td = clone.querySelectorAll("td");
|
||||||
td[0].textContent = "📁";
|
td[0].textContent = "📁";
|
||||||
|
@ -68,6 +69,8 @@ async function refresh_list() {
|
||||||
path.textContent = "..";
|
path.textContent = "..";
|
||||||
// Remove the delete button
|
// Remove the delete button
|
||||||
td[4].replaceChildren();
|
td[4].replaceChildren();
|
||||||
|
td[5].replaceChildren();
|
||||||
|
td[6].replaceChildren();
|
||||||
new_children.push(clone);
|
new_children.push(clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,31 +91,46 @@ async function refresh_list() {
|
||||||
file_path = api_url;
|
file_path = api_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var text_file = false;
|
||||||
if (f.directory) {
|
if (f.directory) {
|
||||||
icon = "📁";
|
icon = "📁";
|
||||||
} else if(f.name.endsWith(".txt") ||
|
} else if(f.name.endsWith(".txt") ||
|
||||||
|
f.name.endsWith(".env") ||
|
||||||
f.name.endsWith(".py") ||
|
f.name.endsWith(".py") ||
|
||||||
f.name.endsWith(".js") ||
|
f.name.endsWith(".js") ||
|
||||||
f.name.endsWith(".json")) {
|
f.name.endsWith(".json")) {
|
||||||
icon = "📄";
|
icon = "📄";
|
||||||
|
text_file = true;
|
||||||
} else if (f.name.endsWith(".html")) {
|
} else if (f.name.endsWith(".html")) {
|
||||||
icon = "🌐";
|
icon = "🌐";
|
||||||
|
text_file = true;
|
||||||
}
|
}
|
||||||
td[0].textContent = icon;
|
td[0].textContent = icon;
|
||||||
td[1].textContent = f.file_size;
|
td[1].textContent = f.file_size;
|
||||||
var path = clone.querySelector("a");
|
var path = clone.querySelector("a.path");
|
||||||
path.href = file_path;
|
path.href = file_path;
|
||||||
path.textContent = f.name;
|
path.textContent = f.name;
|
||||||
td[3].textContent = (new Date(f.modified_ns / 1000000)).toLocaleString();
|
let modtime = clone.querySelector("td.modtime");
|
||||||
|
modtime.textContent = (new Date(f.modified_ns / 1000000)).toLocaleString();
|
||||||
var delete_button = clone.querySelector("button.delete");
|
var delete_button = clone.querySelector("button.delete");
|
||||||
delete_button.value = api_url;
|
delete_button.value = api_url;
|
||||||
delete_button.disabled = !editable;
|
delete_button.disabled = !editable;
|
||||||
delete_button.onclick = del;
|
delete_button.onclick = del;
|
||||||
|
|
||||||
if (editable && !f.directory) {
|
|
||||||
edit_url = new URL(edit_url, url_base);
|
var rename_button = clone.querySelector("button.rename");
|
||||||
|
rename_button.value = api_url;
|
||||||
|
rename_button.disabled = !editable;
|
||||||
|
rename_button.onclick = rename;
|
||||||
|
|
||||||
let edit_link = clone.querySelector(".edit_link");
|
let edit_link = clone.querySelector(".edit_link");
|
||||||
|
if (text_file && editable && !f.directory) {
|
||||||
|
edit_url = new URL(edit_url, url_base);
|
||||||
edit_link.href = edit_url
|
edit_link.href = edit_url
|
||||||
|
} else if (f.directory) {
|
||||||
|
edit_link.style = "display: none;";
|
||||||
|
} else {
|
||||||
|
edit_link.querySelector("button").disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_children.push(clone);
|
new_children.push(clone);
|
||||||
|
@ -214,6 +232,26 @@ async function del(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function rename(e) {
|
||||||
|
let fn = new URL(e.target.value);
|
||||||
|
var new_fn = prompt("Rename to ", fn.pathname.substr(3));
|
||||||
|
if (new_fn === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let new_uri = new URL("/fs" + new_fn, fn);
|
||||||
|
const response = await fetch(e.target.value,
|
||||||
|
{
|
||||||
|
method: "MOVE",
|
||||||
|
headers: {
|
||||||
|
'X-Destination': new_uri.pathname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (response.ok) {
|
||||||
|
refresh_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
find_devices();
|
find_devices();
|
||||||
|
|
||||||
let mkdir_button = document.getElementById("mkdir");
|
let mkdir_button = document.getElementById("mkdir");
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Code Edit</title>
|
<title>Offline Code Edit</title>
|
||||||
<link rel="stylesheet" href="http://circuitpython.org/assets/css/webworkflow-8.css">
|
<link rel="stylesheet" href="http://circuitpython.org/assets/css/webworkflow-8.css">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -17,3 +17,7 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:disabled {
|
||||||
|
filter: saturate(0%);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
var url_base = window.location;
|
var url_base = window.location;
|
||||||
var current_path;
|
var current_path;
|
||||||
|
|
||||||
var mdns_works = window.location.hostname.endsWith(".local");
|
var mdns_works = url_base.hostname.endsWith(".local");
|
||||||
|
|
||||||
async function find_devices() {
|
async function find_devices() {
|
||||||
var version_response = await fetch("/cp/version.json");
|
var version_response = await fetch("/cp/version.json");
|
||||||
|
|
|
@ -79,8 +79,9 @@ typedef struct {
|
||||||
enum request_state state;
|
enum request_state state;
|
||||||
char method[8];
|
char method[8];
|
||||||
char path[256];
|
char path[256];
|
||||||
|
char destination[256];
|
||||||
char header_key[64];
|
char header_key[64];
|
||||||
char header_value[64];
|
char header_value[256];
|
||||||
// We store the origin so we can reply back with it.
|
// We store the origin so we can reply back with it.
|
||||||
char origin[64];
|
char origin[64];
|
||||||
size_t content_length;
|
size_t content_length;
|
||||||
|
@ -554,6 +555,15 @@ static void _reply_conflict(socketpool_socket_obj_t *socket, _request *request)
|
||||||
_send_str(socket, "\r\nUSB storage active.");
|
_send_str(socket, "\r\nUSB storage active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _reply_precondition_failed(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
|
_send_strs(socket,
|
||||||
|
"HTTP/1.1 412 Precondition Failed\r\n",
|
||||||
|
"Content-Length: 0\r\n", NULL);
|
||||||
|
_cors_header(socket, request);
|
||||||
|
_send_str(socket, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
static void _reply_payload_too_large(socketpool_socket_obj_t *socket, _request *request) {
|
static void _reply_payload_too_large(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
_send_strs(socket,
|
_send_strs(socket,
|
||||||
"HTTP/1.1 413 Payload Too Large\r\n",
|
"HTTP/1.1 413 Payload Too Large\r\n",
|
||||||
|
@ -893,6 +903,7 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
|
||||||
|
|
||||||
#define STATIC_FILE(filename) extern uint32_t filename##_length; extern uint8_t filename[]; extern const char *filename##_content_type;
|
#define STATIC_FILE(filename) extern uint32_t filename##_length; extern uint8_t filename[]; extern const char *filename##_content_type;
|
||||||
|
|
||||||
|
STATIC_FILE(code_html);
|
||||||
STATIC_FILE(directory_html);
|
STATIC_FILE(directory_html);
|
||||||
STATIC_FILE(directory_js);
|
STATIC_FILE(directory_js);
|
||||||
STATIC_FILE(welcome_html);
|
STATIC_FILE(welcome_html);
|
||||||
|
@ -953,6 +964,28 @@ static uint8_t _hex2nibble(char h) {
|
||||||
return h - 'a' + 0xa;
|
return h - 'a' + 0xa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode percent encoding in place. Only do this once on a string!
|
||||||
|
static void _decode_percents(char *str) {
|
||||||
|
size_t o = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t startlen = strlen(str);
|
||||||
|
while (i < startlen) {
|
||||||
|
if (str[i] == '%') {
|
||||||
|
str[o] = _hex2nibble(str[i + 1]) << 4 | _hex2nibble(str[i + 2]);
|
||||||
|
i += 3;
|
||||||
|
} else {
|
||||||
|
if (i != o) {
|
||||||
|
str[o] = str[i];
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
o += 1;
|
||||||
|
}
|
||||||
|
if (o < i) {
|
||||||
|
str[o] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
if (request->redirect) {
|
if (request->redirect) {
|
||||||
_reply_redirect(socket, request, request->path);
|
_reply_redirect(socket, request, request->path);
|
||||||
|
@ -973,23 +1006,8 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
// Decode any percent encoded bytes so that we're left with UTF-8.
|
// Decode any percent encoded bytes so that we're left with UTF-8.
|
||||||
// We only do this on /fs/ paths and after redirect so that any
|
// We only do this on /fs/ paths and after redirect so that any
|
||||||
// path echoing we do stays encoded.
|
// path echoing we do stays encoded.
|
||||||
size_t o = 0;
|
_decode_percents(request->path);
|
||||||
size_t i = 0;
|
|
||||||
while (i < strlen(request->path)) {
|
|
||||||
if (request->path[i] == '%') {
|
|
||||||
request->path[o] = _hex2nibble(request->path[i + 1]) << 4 | _hex2nibble(request->path[i + 2]);
|
|
||||||
i += 3;
|
|
||||||
} else {
|
|
||||||
if (i != o) {
|
|
||||||
request->path[o] = request->path[i];
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
o += 1;
|
|
||||||
}
|
|
||||||
if (o < i) {
|
|
||||||
request->path[o] = '\0';
|
|
||||||
}
|
|
||||||
char *path = request->path + 3;
|
char *path = request->path + 3;
|
||||||
size_t pathlen = strlen(path);
|
size_t pathlen = strlen(path);
|
||||||
FATFS *fs = filesystem_circuitpy();
|
FATFS *fs = filesystem_circuitpy();
|
||||||
|
@ -1033,6 +1051,34 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
_reply_no_content(socket, request);
|
_reply_no_content(socket, request);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (strcasecmp(request->method, "MOVE") == 0) {
|
||||||
|
if (_usb_active()) {
|
||||||
|
_reply_conflict(socket, request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_percents(request->destination);
|
||||||
|
char *destination = request->destination + 3;
|
||||||
|
size_t destinationlen = strlen(destination);
|
||||||
|
if (destination[destinationlen - 1] == '/' && destinationlen > 1) {
|
||||||
|
destination[destinationlen - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
FRESULT result = f_rename(fs, path, destination);
|
||||||
|
#if CIRCUITPY_USB_MSC
|
||||||
|
usb_msc_unlock();
|
||||||
|
#endif
|
||||||
|
if (result == FR_EXIST) { // File exists and won't be overwritten.
|
||||||
|
_reply_precondition_failed(socket, request);
|
||||||
|
} else if (result == FR_NO_PATH || result == FR_NO_FILE) { // Missing higher directories or target file.
|
||||||
|
_reply_missing(socket, request);
|
||||||
|
} else if (result != FR_OK) {
|
||||||
|
ESP_LOGE(TAG, "move error %d %s", result, path);
|
||||||
|
_reply_server_error(socket, request);
|
||||||
|
} else {
|
||||||
|
_reply_created(socket, request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else if (directory) {
|
} else if (directory) {
|
||||||
if (strcasecmp(request->method, "GET") == 0) {
|
if (strcasecmp(request->method, "GET") == 0) {
|
||||||
FF_DIR dir;
|
FF_DIR dir;
|
||||||
|
@ -1110,6 +1156,8 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
} else {
|
} else {
|
||||||
_REPLY_STATIC(socket, request, edit_html);
|
_REPLY_STATIC(socket, request, edit_html);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(request->path, "/code/") == 0) {
|
||||||
|
_REPLY_STATIC(socket, request, code_html);
|
||||||
} else if (strncmp(request->path, "/cp/", 4) == 0) {
|
} else if (strncmp(request->path, "/cp/", 4) == 0) {
|
||||||
const char *path = request->path + 3;
|
const char *path = request->path + 3;
|
||||||
if (strcasecmp(request->method, "OPTIONS") == 0) {
|
if (strcasecmp(request->method, "OPTIONS") == 0) {
|
||||||
|
@ -1285,6 +1333,8 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
|
||||||
} else if (strcasecmp(request->header_key, "Sec-WebSocket-Key") == 0 &&
|
} else if (strcasecmp(request->header_key, "Sec-WebSocket-Key") == 0 &&
|
||||||
strlen(request->header_value) == 24) {
|
strlen(request->header_value) == 24) {
|
||||||
strcpy(request->websocket_key, request->header_value);
|
strcpy(request->websocket_key, request->header_value);
|
||||||
|
} else if (strcasecmp(request->header_key, "X-Destination") == 0) {
|
||||||
|
strcpy(request->destination, request->header_value);
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Header %s %s", request->header_key, request->header_value);
|
ESP_LOGI(TAG, "Header %s %s", request->header_key, request->header_value);
|
||||||
} else if (request->offset > sizeof(request->header_value) - 1) {
|
} else if (request->offset > sizeof(request->header_value) - 1) {
|
||||||
|
|
Loading…
Reference in New Issue