Skip to content

Commit 34c5cd4

Browse files
committed
Added support for editing settings.
Added an lwIP patch.
1 parent ca7583d commit 34c5cd4

17 files changed

+471
-28
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.visualgdb
22
.vs
33
build
4+
*.user
5+
CMakeLists.txt.old

PicoHTTPServer/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ set(CMAKE_CXX_STANDARD 17)
1616

1717
function(add_resource_folder target name path)
1818
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}.fs ${CMAKE_CURRENT_BINARY_DIR}/__rerun_${name}.fs
19-
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/SimpleFSBuilder/SimpleFSBuilder
19+
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../tools/SimpleFSBuilder/SimpleFSBuilder
2020
ARGS ${path} ${CMAKE_CURRENT_BINARY_DIR}/${name}.fs
2121
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
2222
COMMENT "Generating ${name}.fs")

PicoHTTPServer/PicoHTTPServer.sln

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ VisualStudioVersion = 17.3.32811.315
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{803FD0C6-D64E-4E16-9DC3-1DAEC859A3D2}") = "PicoHTTPServer", "PicoHTTPServer.vgdbproj", "{ACC40D02-2301-4CA2-816D-2DC4079047DF}"
77
EndProject
8-
Project("{803FD0C6-D64E-4E16-9DC3-1DAEC859A3D2}") = "SimpleFSBuilder", "tools\SimpleFSBuilder\SimpleFSBuilder.vgdbcmake", "{74B44ADD-7CE6-4F55-929C-FE4C5CEFB6A6}"
8+
Project("{803FD0C6-D64E-4E16-9DC3-1DAEC859A3D2}") = "SimpleFSBuilder", "..\tools\SimpleFSBuilder\SimpleFSBuilder.vgdbcmake", "{74B44ADD-7CE6-4F55-929C-FE4C5CEFB6A6}"
99
EndProject
1010
Global
1111
GlobalSection(SolutionConfigurationPlatforms) = preSolution

PicoHTTPServer/PicoHTTPServer.vgdbproj

+6-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@
7575
<string>com.sysprogs.embedded.semihosting_and_profiler</string>
7676
</IDs>
7777
<Config>
78-
<Entries />
78+
<Entries>
79+
<KeyValue>
80+
<Key>com.sysprogs.efp.profiling.debugger_check</Key>
81+
<Value>SYSPROGS_PROFILER_DEBUGGER_CHECK_MODE=1</Value>
82+
</KeyValue>
83+
</Entries>
7984
</Config>
8085
</ReferencedFrameworks>
8186
</Extension>

PicoHTTPServer/httpserver.c

+63-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ struct _http_connection
2828
http_server_instance server;
2929
int socket;
3030
size_t buffered_size;
31+
struct
32+
{
33+
int buffer_used, buffer_pos;
34+
int remaining_input_len;
35+
int offset_from_main_buffer;
36+
} post;
37+
3138
char buffer[1];
3239
};
3340

@@ -52,7 +59,7 @@ static int recv_line(int socket, char *buffer, int buffer_size)
5259

5360
//Read next line using the buffer (multiple lines can be buffered at once).
5461
//If the line was too long to fit into the buffer, returned length will be negative, but the next line will still get found correctly.
55-
static char *recv_next_line_buffered(int socket, char *buffer, int buffer_size, int *buffer_used, int *offset, int *len)
62+
static char *recv_next_line_buffered(int socket, char *buffer, int buffer_size, int *buffer_used, int *offset, int *len, int *recv_limit)
5663
{
5764
int skipped_len = 0;
5865
if (*offset > *buffer_used)
@@ -91,10 +98,20 @@ static char *recv_next_line_buffered(int socket, char *buffer, int buffer_size,
9198
*offset = 0;
9299
}
93100

94-
int done = recv(socket, buffer + *buffer_used, buffer_size - *buffer_used, 0);
101+
int buffer_avail = buffer_size - *buffer_used;
102+
if (recv_limit)
103+
buffer_avail = MIN(buffer_avail, *recv_limit);
104+
105+
if (buffer_avail <= 0)
106+
return NULL;
107+
108+
int done = recv(socket, buffer + *buffer_used, buffer_avail, 0);
95109
if (done <= 0)
96110
return NULL;
97111

112+
if (recv_limit)
113+
*recv_limit -= done;
114+
98115
*buffer_used += done;
99116
}
100117
}
@@ -164,6 +181,8 @@ static void parse_and_handle_http_request(http_connection ctx)
164181
int header_buf_size = 0, header_buf_pos = 0, header_buf_used = 0;
165182
char host[32];
166183
host[0] = 0;
184+
enum http_request_type reqtype = HTTP_GET;
185+
ctx->post.remaining_input_len = ctx->post.buffer_used = ctx->post.buffer_pos = 0;
167186

168187
if (len)
169188
{
@@ -172,6 +191,9 @@ static void parse_and_handle_http_request(http_connection ctx)
172191
if (p1)
173192
p2 = strchr(++p1, ' ');
174193

194+
if (!strncasecmp(ctx->buffer, "POST ", 5))
195+
reqtype = HTTP_POST;
196+
175197
if (p2)
176198
p3 = strstr(p2, "\r\n");
177199

@@ -195,7 +217,7 @@ static void parse_and_handle_http_request(http_connection ctx)
195217

196218
for (;;)
197219
{
198-
char *line = recv_next_line_buffered(ctx->socket, header_buf, header_buf_size, &header_buf_used, &header_buf_pos, &len);
220+
char *line = recv_next_line_buffered(ctx->socket, header_buf, header_buf_size, &header_buf_used, &header_buf_pos, &len, NULL);
199221
if (!line)
200222
{
201223
debug_printf("HTTP: unexpected end of headers");
@@ -210,6 +232,18 @@ static void parse_and_handle_http_request(http_connection ctx)
210232
memcpy(host, line + 6, len - 6);
211233
host[len - 6] = 0;
212234
}
235+
else if (!strncasecmp(line, "Content-length: ", 16))
236+
{
237+
ctx->post.remaining_input_len = atoi(line + 16);
238+
}
239+
}
240+
241+
if (reqtype == HTTP_POST && ctx->post.remaining_input_len)
242+
{
243+
ctx->post.buffer_pos = header_buf_pos;
244+
ctx->post.buffer_used = header_buf_used;
245+
ctx->post.remaining_input_len -= (header_buf_used - header_buf_pos);
246+
ctx->post.offset_from_main_buffer = header_buf - ctx->buffer;
213247
}
214248

215249
debug_printf("HTTP: %s%s\n", host, path);
@@ -248,7 +282,7 @@ static void parse_and_handle_http_request(http_connection ctx)
248282
while (path[off] == '/')
249283
off++;
250284

251-
if (zone->handler(ctx, path + off, zone->context))
285+
if (zone->handler(ctx, reqtype, path + off, zone->context))
252286
return;
253287
}
254288
}
@@ -412,3 +446,28 @@ void http_server_end_write_reply(http_write_handle handle, const char *footer)
412446

413447
conn->buffered_size = 0;
414448
}
449+
450+
char *http_server_read_post_line(http_connection conn)
451+
{
452+
if (conn->post.remaining_input_len <= 0 && conn->post.buffer_pos >= conn->post.buffer_used)
453+
return NULL;
454+
455+
int len = 0;
456+
char *result = recv_next_line_buffered(conn->socket,
457+
conn->buffer + conn->post.offset_from_main_buffer,
458+
conn->server->buffer_size - conn->post.offset_from_main_buffer,
459+
&conn->post.buffer_used,
460+
&conn->post.buffer_pos,
461+
&len,
462+
&conn->post.remaining_input_len);
463+
464+
if (!result)
465+
return NULL;
466+
467+
if (len < 0)
468+
return NULL; //Too long line got truncated
469+
470+
result[len] = 0;
471+
472+
return result;
473+
}

PicoHTTPServer/httpserver.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
typedef struct _http_server_instance *http_server_instance;
44
typedef struct _http_connection *http_connection, *http_write_handle;
55

6-
typedef bool(*http_request_handler)(http_connection conn, char *path, void *context);
6+
enum http_request_type
7+
{
8+
HTTP_GET = 0,
9+
HTTP_POST = 1,
10+
};
11+
12+
typedef bool(*http_request_handler)(http_connection conn, enum http_request_type type, char *path, void *context);
713

814
typedef struct http_zone
915
{
@@ -19,6 +25,10 @@ http_server_instance http_server_create(const char *main_host, const char *main_
1925
void http_server_add_zone(http_server_instance server, http_zone *instance, const char *prefix, http_request_handler handler, void *context);
2026
void http_server_send_reply(http_connection conn, const char *code, const char *contentType, const char *content, int size);
2127

28+
/* Reads a single line from the POST request using the internal connection buffer. Returns NULL when the entire request has been read. */
29+
char *http_server_read_post_line(http_connection conn);
30+
31+
2232
http_write_handle http_server_begin_write_reply(http_connection conn, const char *code, const char *contentType);
2333
void http_server_write_reply(http_write_handle handle, const char *format, ...);
2434
void http_server_end_write_reply(http_write_handle handle, const char *footer);

PicoHTTPServer/main.c

+142-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <stdarg.h>
22
#include <pico/cyw43_arch.h>
33
#include <pico/stdlib.h>
4+
#include <hardware/watchdog.h>
45

56
#include <lwip/ip4_addr.h>
67
#include <lwip/netif.h>
@@ -13,7 +14,7 @@
1314
#include "dns/dnsserver.h"
1415
#include "server_settings.h"
1516
#include "httpserver.h"
16-
#include "tools/SimpleFSBuilder/SimpleFS.h"
17+
#include "../tools/SimpleFSBuilder/SimpleFS.h"
1718

1819
#define TEST_TASK_PRIORITY (tskIDLE_PRIORITY + 2UL)
1920

@@ -35,7 +36,7 @@ bool simplefs_init(struct SimpleFSContext *ctx, void *data)
3536
return true;
3637
}
3738

38-
static bool do_retrieve_file(http_connection conn, char *path, void *context)
39+
static bool do_retrieve_file(http_connection conn, enum http_request_type type, char *path, void *context)
3940
{
4041
for (int i = 0; i < s_SimpleFS.header->EntryCount; i++)
4142
{
@@ -53,7 +54,94 @@ static bool do_retrieve_file(http_connection conn, char *path, void *context)
5354
return false;
5455
}
5556

56-
static bool do_handle_api_call(http_connection conn, char *path, void *context)
57+
static char *parse_server_settings(http_connection conn, pico_server_settings *settings)
58+
{
59+
bool has_password = false, use_domain = false, use_second_ip = false;
60+
bool bad_password = false, bad_domain = false;
61+
62+
for (;;)
63+
{
64+
char *line = http_server_read_post_line(conn);
65+
if (!line)
66+
break;
67+
68+
char *p = strchr(line, '=');
69+
if (!p)
70+
continue;
71+
*p++ = 0;
72+
if (!strcasecmp(line, "has_password"))
73+
has_password = !strcasecmp(p, "true") || p[0] == '1';
74+
else if (!strcasecmp(line, "use_domain"))
75+
use_domain = !strcasecmp(p, "true") || p[0] == '1';
76+
else if (!strcasecmp(line, "use_second_ip"))
77+
use_second_ip = !strcasecmp(p, "true") || p[0] == '1';
78+
else if (!strcasecmp(line, "ssid"))
79+
{
80+
if (strlen(p) >= sizeof(settings->network_name))
81+
return "SSID too long";
82+
if (!p[0])
83+
return "missing SSID";
84+
strcpy(settings->network_name, p);
85+
}
86+
else if (!strcasecmp(line, "password"))
87+
{
88+
if (strlen(p) >= sizeof(settings->network_password))
89+
bad_password = true;
90+
else
91+
strcpy(settings->network_password, p);
92+
}
93+
else if (!strcasecmp(line, "hostname"))
94+
{
95+
if (strlen(p) >= sizeof(settings->hostname))
96+
return "hostname too long";
97+
if (!p[0])
98+
return "missing hostname";
99+
strcpy(settings->hostname, p);
100+
}
101+
else if (!strcasecmp(line, "domain"))
102+
{
103+
if (strlen(p) >= sizeof(settings->domain_name))
104+
bad_domain = true;
105+
else
106+
strcpy(settings->domain_name, p);
107+
}
108+
else if (!strcasecmp(line, "ipaddr"))
109+
{
110+
settings->ip_address = ipaddr_addr(p);
111+
if (!settings->ip_address || settings->ip_address == -1)
112+
return "invalid IP address";
113+
}
114+
else if (!strcasecmp(line, "netmask"))
115+
{
116+
settings->network_mask = ipaddr_addr(p);
117+
if (!settings->network_mask || settings->network_mask == -1)
118+
return "invalid network mask";
119+
}
120+
else if (!strcasecmp(line, "ipaddr2"))
121+
{
122+
settings->secondary_address = ipaddr_addr(p);
123+
}
124+
}
125+
126+
if (!has_password)
127+
memset(settings->network_password, 0, sizeof(settings->network_password));
128+
else if (bad_password)
129+
return "password too long";
130+
131+
if (!use_domain)
132+
memset(settings->domain_name, 0, sizeof(settings->domain_name));
133+
else if (bad_domain)
134+
return "domain too long";
135+
136+
if (!use_second_ip)
137+
settings->secondary_address = 0;
138+
else if (!settings->secondary_address || settings->secondary_address == -1)
139+
return "invalid secondary IP address";
140+
141+
return NULL;
142+
}
143+
144+
static bool do_handle_api_call(http_connection conn, enum http_request_type type, char *path, void *context)
57145
{
58146
static int s_InitializedMask = 0;
59147

@@ -116,13 +204,63 @@ static bool do_handle_api_call(http_connection conn, char *path, void *context)
116204
return true;
117205
}
118206
}
207+
else if (!strcmp(path, "settings"))
208+
{
209+
if (type == HTTP_POST)
210+
{
211+
static pico_server_settings settings;
212+
settings = *get_pico_server_settings();
213+
214+
char *err = parse_server_settings(conn, &settings);
215+
if (err)
216+
{
217+
http_server_send_reply(conn, "200 OK", "text/plain", err, -1);
218+
return true;
219+
}
220+
221+
write_pico_server_settings(&settings);
222+
http_server_send_reply(conn, "200 OK", "text/plain", "OK", -1);
223+
watchdog_reboot(0, SRAM_END, 500);
224+
return true;
225+
}
226+
else
227+
{
228+
const pico_server_settings *settings = get_pico_server_settings();
229+
http_write_handle reply = http_server_begin_write_reply(conn, "200 OK", "text/json");
230+
http_server_write_reply(reply, "{\"ssid\": \"%s\"", settings->network_name);
231+
http_server_write_reply(reply, ",\"has_password\": %d, \"password\" : \"%s\"", settings->network_password[0] != 0, settings->network_password);
232+
http_server_write_reply(reply, ",\"hostname\" : \"%s\"", settings->hostname);
233+
http_server_write_reply(reply, ",\"use_domain\": %d, \"domain\" : \"%s\"", settings->domain_name[0] != 0, settings->domain_name);
234+
http_server_write_reply(reply, ",\"ipaddr\" : \"%d.%d.%d.%d\"", (settings->ip_address >> 0) & 0xFF, (settings->ip_address >> 8) & 0xFF, (settings->ip_address >> 16) & 0xFF, (settings->ip_address >> 24) & 0xFF);
235+
http_server_write_reply(reply, ",\"netmask\" : \"%d.%d.%d.%d\"", (settings->network_mask >> 0) & 0xFF, (settings->network_mask >> 8) & 0xFF, (settings->network_mask >> 16) & 0xFF, (settings->network_mask >> 24) & 0xFF);
236+
http_server_write_reply(reply, ",\"use_second_ip\": %d", settings->secondary_address != 0);
237+
http_server_write_reply(reply, ",\"ipaddr2\" : \"%d.%d.%d.%d\"", (settings->secondary_address >> 0) & 0xFF, (settings->secondary_address >> 8) & 0xFF, (settings->secondary_address >> 16) & 0xFF, (settings->secondary_address >> 24) & 0xFF);
238+
239+
http_server_end_write_reply(reply, "}");
240+
return true;
241+
}
242+
}
119243

120244
return false;
121245
}
122246

123247

248+
static void set_secondary_ip_address(int address)
249+
{
250+
/************************************ !!! WARNING !!! ************************************
251+
* If you get an 'undefined reference to ip4_secondary_ip_address' error here, *
252+
* you need to patch your lwIP using the lwip_patch/lwip.patch file from this repository.*
253+
* This ensures that this device can pretend to be a router redirecting requests to *
254+
* external IPs to its login page, so the OS can automatically navigate there. *
255+
*****************************************************************************************/
256+
257+
extern int ip4_secondary_ip_address;
258+
ip4_secondary_ip_address = address;
259+
}
260+
124261
static void main_task(__unused void *params)
125262
{
263+
126264
if (cyw43_arch_init())
127265
{
128266
printf("failed to initialise\n");
@@ -149,6 +287,7 @@ static void main_task(__unused void *params)
149287
static dhcp_server_t dhcp_server;
150288
dhcp_server_init(&dhcp_server, &netif->ip_addr, &netif->netmask, settings->domain_name);
151289
dns_server_init(netif->ip_addr.addr, settings->secondary_address, settings->hostname, settings->domain_name);
290+
set_secondary_ip_address(settings->secondary_address);
152291
http_server_instance server = http_server_create(settings->hostname, settings->domain_name, 4, 4096);
153292
static http_zone zone1, zone2;
154293
http_server_add_zone(server, &zone1, "", do_retrieve_file, NULL);

0 commit comments

Comments
 (0)