From a6b59a0b467a0772581153dd573000f379d0f667 Mon Sep 17 00:00:00 2001 From: Jaime Idolpx Date: Sat, 19 Oct 2024 00:44:24 -0400 Subject: [PATCH] [iec] sync and troubleshooting protocol --- data/BUILD_IEC/ml.0.spr | Bin 66 -> 0 bytes data/BUILD_IEC/mlb.prg | Bin 11201 -> 0 bytes lib/FileSystem/fnFsTNFSvfs.cpp | 4 +- lib/bus/iec/iec.cpp | 21 ++-- lib/bus/iec/ieee-488.h | 4 + lib/bus/iec/protocol/_protocol.cpp | 18 +-- lib/bus/iec/protocol/_protocol.h | 1 + lib/bus/iec/protocol/cpbstandardserial.cpp | 107 ++++++++--------- lib/device/iec/drive.cpp | 9 +- lib/device/iec/drive.h | 19 +-- lib/device/iec/network.cpp | 18 +-- lib/http/webdav/request.cpp | 64 +++++------ lib/http/webdav/request.h | 13 +++ lib/http/webdav/response.cpp | 5 +- lib/http/webdav/response.h | 6 +- lib/http/webdav/webdav_server.cpp | 128 ++++++++++++++++----- lib/meatloaf/disk/d64.h | 2 +- lib/meatloaf/network/http.cpp | 1 + lib/utils/string_utils.cpp | 38 ++++++ lib/utils/string_utils.h | 3 + 20 files changed, 293 insertions(+), 168 deletions(-) delete mode 100644 data/BUILD_IEC/ml.0.spr delete mode 100644 data/BUILD_IEC/mlb.prg diff --git a/data/BUILD_IEC/ml.0.spr b/data/BUILD_IEC/ml.0.spr deleted file mode 100644 index ebb37a0410009cc168a17f1e080cbb1fb9d3c2e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66 zcmZQDU;u(p2F6eaAYu&-03y-Q5YhNQqV<17OKU_+--y=F5si-#4ZkBA`iC{N0HOw{ L7^sUugrOe*D9jO> diff --git a/data/BUILD_IEC/mlb.prg b/data/BUILD_IEC/mlb.prg deleted file mode 100644 index b05aa3d395bf668b7614419da3f760b60b552bb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11201 zcmeHJYitx%6uvXN)7ken+cs@U)hmi@tNWPQ(pM0=(1kQ?w{=@UVlW%92E!v&8Xtd@ zYT5+DKvIbQVR0!x$e1h<-A~N(JIRrub_V_=xz-fQqYRJ zLheBgTCbpM6!f$PU8SJUE9j^OZBWoR74&xv+Nhu>6qK^g&bdiJPb=sn8njtKKT^=A zHRvEI!RM{g$|K+%m%TW6hh;BHOnAsT)(OMA*0E;@m57EdH%dt221^nk;ek*R@sks+ zcHVq2VIEl;|`q3m}GW7B! zD#=VOPfo)7u_XE}Gxc~9UCB(XNTREm@h6g#S0^VB`BAQ!&eTD*1Rk}CIdDWuc!&s< z1bWmEp_GW{gD>OuzKbC**IZ@ir7N6fhJp6fhJp6fhJp z6fhJp6fhJp6fhJp6fhJp6fhJp6fhK6iUOAh2aX*&GBA|7IDF*&ql3dK9s6PQC-TeS z(POC#!-E5}xSIO&(9pqm4h|fWptHWHn=<3>QV)l{?JlOgcKc;t2Sb%R+*Aqfa(l`t z2L}6CwG@YRBa7wf_bs0D%f%sxtn;`l%NV=?PF#hDkEWu-Obb`2$tRy69WycH5nl$F79+>*-ELC)g2Phv{nE z#!zO5)x`wX!zq52aTU#xwU%2dXurwr448mY#kc~_!Y%UA@)}{w!E{=*wys%&yX1w0 zUDbFKQ;k=zrWR@RB8@VeRHG!1qO~CzbAf4t|B)w^gL);ZQQ%P&Eyn>VjQL?{JVvU? z6zZ{s3iImCe#)Kh+V?IzcK-ZS9&a0>l--R+&R7QWseZ1i)D{$PWj zYep}`+qXno(Egrq&*ttH@CLji-nq3q5-<7>^djfm)w`!}7##;%TYGFH zM$Y%T;*sud9FA?nnH~ZC?0`Q%=ilG=3R(n_Uo0`CC2~RN z053imEOedQQ3s#XNC(f??}$R7?yfijhgY1C>`)X6BIZHdZ)44d0)_&H0)_&H0{@c& z2T BUS_IDLE ); @@ -499,12 +500,12 @@ void systemBus::read_command() // Sometimes ATN isn't released immediately. Wait for ATN to be // released before trying to process the command. // Long ATN delay (>1.5ms) seems to occur more frequently with VIC-20. - pull ( PIN_IEC_SRQ ); + //pull ( PIN_IEC_SRQ ); protocol->timeoutWait ( PIN_IEC_ATN, RELEASED, TIMEOUT_DEFAULT, false ); // Delay after ATN is RELEASED //protocol->wait( TIMING_Ttk, false ); - release ( PIN_IEC_SRQ ); + //release ( PIN_IEC_SRQ ); } @@ -835,14 +836,14 @@ void IRAM_ATTR systemBus::deviceListen() void IRAM_ATTR systemBus::deviceTalk() { // Now do bus turnaround - //pull(PIN_IEC_SRQ); + pull(PIN_IEC_SRQ); if (!turnAround()) { Debug_printv("error flags[%d]", flags); state = BUS_ERROR; return; } - //release(PIN_IEC_SRQ); + release(PIN_IEC_SRQ); // We have recieved a CMD and we should talk now: state = BUS_PROCESS; @@ -871,7 +872,7 @@ bool IRAM_ATTR systemBus::turnAround() */ // Wait for CLK to be released - pull ( PIN_IEC_SRQ ); + //pull ( PIN_IEC_SRQ ); if (protocol->timeoutWait(PIN_IEC_CLK_IN, RELEASED, TIMEOUT_Ttlta) == TIMEOUT_Ttlta) { Debug_printv("Wait until the computer releases the CLK line\r\n"); @@ -881,7 +882,7 @@ bool IRAM_ATTR systemBus::turnAround() } release ( PIN_IEC_DATA_OUT ); pull ( PIN_IEC_CLK_OUT ); - release ( PIN_IEC_SRQ ); + //release ( PIN_IEC_SRQ ); // 80us minimum delay after TURNAROUND // *** IMPORTANT! diff --git a/lib/bus/iec/ieee-488.h b/lib/bus/iec/ieee-488.h index e69de29bb..ed381079d 100644 --- a/lib/bus/iec/ieee-488.h +++ b/lib/bus/iec/ieee-488.h @@ -0,0 +1,4 @@ +// +// https://www.pagetable.com/?p=1023 +// https://ia601902.us.archive.org/9/items/PET_and_the_IEEE488_Bus_1980_McGraw-Hill/PET_and_the_IEEE488_Bus_1980_McGraw-Hill.pdf +// diff --git a/lib/bus/iec/protocol/_protocol.cpp b/lib/bus/iec/protocol/_protocol.cpp index 9280b4d4a..a5eb2e3ae 100644 --- a/lib/bus/iec/protocol/_protocol.cpp +++ b/lib/bus/iec/protocol/_protocol.cpp @@ -24,7 +24,8 @@ IECProtocol::IECProtocol() { esp_timer_create_args_t args = { .callback = onTimer, .arg = this, - .name = nullptr + .dispatch_method = ESP_TIMER_ISR, + .name = "onTimer" }; esp_timer_create(&args, &timer_handle); }; @@ -40,13 +41,14 @@ IECProtocol::~IECProtocol() { void IECProtocol::timer_start(uint64_t timeout_us) { timer_timedout = false; - esp_timer_stop(timer_handle); + timer_started = esp_timer_get_time(); esp_timer_start_once(timer_handle, timeout_us); //IEC.pull( PIN_IEC_SRQ ); } void IECProtocol::timer_stop() { esp_timer_stop(timer_handle); + timer_elapsed = esp_timer_get_time() - timer_started; //IEC.release( PIN_IEC_SRQ ); } @@ -93,7 +95,6 @@ int16_t IRAM_ATTR IECProtocol::timeoutWait(uint8_t pin, bool target_status, size uint64_t start = 0; uint64_t current = 0; uint64_t elapsed = 0; - bool atn_status = false; #ifndef IEC_SPLIT_LINES IEC.release ( pin ); @@ -113,9 +114,9 @@ int16_t IRAM_ATTR IECProtocol::timeoutWait(uint8_t pin, bool target_status, size IEC.release ( PIN_IEC_ATN ); #endif - // // Sample ATN and set flag to indicate COMMAND or DATA mode - // atn_status = IEC.status ( PIN_IEC_ATN ); - // if ( atn_status ) IEC.flags |= ATN_PULLED; + // Sample ATN and set flag to indicate COMMAND or DATA mode + if( IEC.status ( PIN_IEC_ATN ) ) + IEC.flags |= ATN_PULLED; } //IEC.pull ( PIN_IEC_SRQ ); @@ -134,9 +135,10 @@ int16_t IRAM_ATTR IECProtocol::timeoutWait(uint8_t pin, bool target_status, size return wait_us; } - if ( watch_atn && (IEC.flags & ATN_PULLED) ) + if ( watch_atn ) { - return -1; + if ( IEC.flags & ATN_PULLED ) + return -1; } if ( IEC.state < BUS_ACTIVE || elapsed > FOREVER ) diff --git a/lib/bus/iec/protocol/_protocol.h b/lib/bus/iec/protocol/_protocol.h index 8eb1bb2a6..e581aafaf 100644 --- a/lib/bus/iec/protocol/_protocol.h +++ b/lib/bus/iec/protocol/_protocol.h @@ -25,6 +25,7 @@ namespace Protocol }; bool timer_timedout = false; + uint64_t timer_started = 0; uint64_t timer_elapsed = 0; diff --git a/lib/bus/iec/protocol/cpbstandardserial.cpp b/lib/bus/iec/protocol/cpbstandardserial.cpp index 3a8fb3874..b006b125f 100644 --- a/lib/bus/iec/protocol/cpbstandardserial.cpp +++ b/lib/bus/iec/protocol/cpbstandardserial.cpp @@ -74,7 +74,7 @@ CPBStandardSerial::CPBStandardSerial() .callback = onSendBits, .arg = this, .dispatch_method = ESP_TIMER_ISR, - .name = "Send" + .name = "onSendBits" }; esp_timer_create(&args, &timer_send_h); //Debug_printv("send_timer_create"); @@ -119,6 +119,7 @@ uint8_t CPBStandardSerial::receiveByte() // to accept data. What happens next is variable. // Wait for all other devices to release the data line + //IEC.release( PIN_IEC_DATA_IN ); if ( timeoutWait ( PIN_IEC_DATA_IN, RELEASED, FOREVER, false ) == TIMED_OUT ) { Debug_printv ( "Wait for all other devices to release the data line" ); @@ -131,10 +132,8 @@ uint8_t CPBStandardSerial::receiveByte() // will do nothing. The listener should be watching, and if 200 microseconds pass // without the Clock line going to true, it has a special task to perform: note EOI. - //IEC.pull ( PIN_IEC_SRQ ); + IEC.pull ( PIN_IEC_SRQ ); if ( timeoutWait ( PIN_IEC_CLK_IN, PULLED, TIMING_Tye, false ) == TIMING_Tye ) - // timer_start( TIMING_Tye ); - // while ( IEC.status ( PIN_IEC_CLK_IN ) != PULLED ) { // INTERMISSION: EOI // If the Ready for Data signal isn't acknowledged by the talker within 200 microseconds, the @@ -152,30 +151,23 @@ uint8_t CPBStandardSerial::receiveByte() //IEC.pull ( PIN_IEC_SRQ ); - //if ( timer_timedout ) - { - timer_timedout = false; - IEC.flags |= EOI_RECVD; - - // Acknowledge by pull down data more than 60us - //wait ( TIMING_Th ); - IEC.pull ( PIN_IEC_DATA_OUT ); - wait ( TIMING_Tei ); - IEC.release ( PIN_IEC_DATA_OUT ); + timer_timedout = false; + IEC.flags |= EOI_RECVD; - // Wait for clock line to be pulled - timeoutWait ( PIN_IEC_CLK_IN, PULLED, TIMING_Tye, false ); - } + // Acknowledge by pull down data more than 60us + //wait ( TIMING_Th ); + IEC.pull ( PIN_IEC_DATA_OUT ); + wait ( TIMING_Tei ); + IEC.release ( PIN_IEC_DATA_OUT ); - //usleep( 2 ); - //IEC.release ( PIN_IEC_SRQ ); - //usleep( 2 ); + // Wait for clock line to be pulled + //timeoutWait ( PIN_IEC_CLK_IN, PULLED, TIMING_Tye, false ); } - //IEC.release ( PIN_IEC_SRQ ); + IEC.release ( PIN_IEC_SRQ ); - // Sample ATN and set flag to indicate COMMAND or DATA mode - if ( IEC.status ( PIN_IEC_ATN ) ) - IEC.flags |= ATN_PULLED; + // // Sample ATN and set flag to indicate COMMAND or DATA mode + // if ( IEC.status ( PIN_IEC_ATN ) ) + // IEC.flags |= ATN_PULLED; // STEP 3: RECEIVING THE BITS //IEC.pull ( PIN_IEC_SRQ ); @@ -229,18 +221,23 @@ uint8_t CPBStandardSerial::receiveBits () { IEC.bit = 0; IEC.byte = 0; - timer_start( TIMEOUT_DEFAULT ); + timer_start( TIMEOUT_DEFAULT ); while ( IEC.bit < 7 ) { if ( timer_timedout ) { + Debug_printv ( "Timeout bit[%d]", IEC.bit ); IEC.flags |= ERROR; return 0; } - usleep( 2 ); + IEC.pull( PIN_IEC_SRQ ); + usleep( 1 ); + IEC.release( PIN_IEC_SRQ ); + usleep( 1 ); } + timer_stop(); // If there is a 218us delay before bit 7, the controller uses JiffyDOS timer_start( TIMING_PROTOCOL_DETECT ); @@ -270,8 +267,12 @@ uint8_t CPBStandardSerial::receiveBits () } } - usleep( 2 ); + IEC.pull( PIN_IEC_SRQ ); + usleep( 1 ); + IEC.release( PIN_IEC_SRQ ); + usleep( 1 ); } + timer_stop(); // Wait for CLK to be pulled after last bit if ( timeoutWait ( PIN_IEC_CLK_IN, PULLED, FOREVER, false ) == TIMED_OUT ) @@ -396,7 +397,7 @@ bool CPBStandardSerial::sendByte(uint8_t data, bool eoi) // line to false. Suppose there is more than one listener. The Data line will go false // only when all listeners have RELEASED it - in other words, when all listeners are ready // to accept data. - IEC.pull ( PIN_IEC_SRQ ); + //IEC.pull ( PIN_IEC_SRQ ); if ( timeoutWait ( PIN_IEC_DATA_IN, RELEASED, FOREVER ) == TIMED_OUT ) { if ( !(IEC.flags & ATN_PULLED) ) @@ -407,7 +408,7 @@ bool CPBStandardSerial::sendByte(uint8_t data, bool eoi) return false; // return error because of ATN or timeout } - IEC.release ( PIN_IEC_SRQ ); + //IEC.release ( PIN_IEC_SRQ ); // What happens next is variable. Either the talker will pull the // Clock line back to true in less than 200 microseconds - usually within 60 microseconds - or it @@ -471,33 +472,33 @@ bool CPBStandardSerial::sendByte(uint8_t data, bool eoi) // Wait for listener to accept data //IEC.pull ( PIN_IEC_SRQ ); - // if ( timeoutWait ( PIN_IEC_DATA_IN, PULLED, TIMEOUT_Tf ) == TIMEOUT_Tf ) + if ( timeoutWait ( PIN_IEC_DATA_IN, PULLED, TIMEOUT_Tf ) == TIMEOUT_Tf ) + { + // RECIEVER TIMEOUT + // If no receiver pulls DATA within 1000 µs at the end of the transmission of a byte (after step 28), a receiver timeout is raised. + Debug_printv ( "Wait for listener to acknowledge byte received (pull data) [%02X]", data ); + Debug_printv ( "RECEIVER TIMEOUT" ); + IEC.flags |= ERROR; + //IEC.release ( PIN_IEC_SRQ ); + return false; // return error because timeout + } + //IEC.release ( PIN_IEC_SRQ ); + //IEC.pull ( PIN_IEC_SRQ ); + // timer_start( TIMEOUT_Tf ); + // while ( IEC.status ( PIN_IEC_DATA_IN ) != PULLED ) // { - // // RECIEVER TIMEOUT - // // If no receiver pulls DATA within 1000 µs at the end of the transmission of a byte (after step 28), a receiver timeout is raised. - // Debug_printv ( "Wait for listener to acknowledge byte received (pull data) [%02x]", data ); - // Debug_printv ( "RECEIVER TIMEOUT" ); - // IEC.flags |= ERROR; - // IEC.release ( PIN_IEC_SRQ ); - // return false; // return error because timeout + // if ( timer_timedout ) + // { + // // RECIEVER TIMEOUT + // // If no receiver pulls DATA within 1000 µs at the end of the transmission of a byte (after step 28), a receiver timeout is raised. + // Debug_printv ( "Wait for listener to acknowledge byte received (pull data) [%02X]", data ); + // Debug_printv ( "RECEIVER TIMEOUT" ); + // IEC.flags |= ERROR; + // //IEC.release ( PIN_IEC_SRQ ); + // return false; // return error because timeout + // } // } //IEC.release ( PIN_IEC_SRQ ); - IEC.pull ( PIN_IEC_SRQ ); - timer_start( TIMEOUT_Tf ); - while ( IEC.status ( PIN_IEC_DATA_IN ) != PULLED ) - { - if ( timer_timedout ) - { - // RECIEVER TIMEOUT - // If no receiver pulls DATA within 1000 µs at the end of the transmission of a byte (after step 28), a receiver timeout is raised. - Debug_printv ( "Wait for listener to acknowledge byte received (pull data) [%02x]", data ); - Debug_printv ( "RECEIVER TIMEOUT" ); - IEC.flags |= ERROR; - IEC.release ( PIN_IEC_SRQ ); - return false; // return error because timeout - } - } - IEC.release ( PIN_IEC_SRQ ); // STEP 5: START OVER // We're finished, and back where we started. The talker is holding the Clock line true, diff --git a/lib/device/iec/drive.cpp b/lib/device/iec/drive.cpp index 7ead06c1a..1e1903f31 100644 --- a/lib/device/iec/drive.cpp +++ b/lib/device/iec/drive.cpp @@ -355,15 +355,8 @@ void iecDrive::iec_command() { Debug_printv("command[%s]", payload.c_str()); - // if (mstr::startsWith(payload, "cd")) - // set_prefix(); - // else if (pt[0] == "pwd") - // get_prefix(); - // else if (pt[0] == "id") - // set_device_id(); - // Drive level commands - // CBM DOS 2.5 + // CBM DOS 2.6 switch ( payload[0] ) { case 'B': diff --git a/lib/device/iec/drive.h b/lib/device/iec/drive.h index a03abe54b..49257057f 100644 --- a/lib/device/iec/drive.h +++ b/lib/device/iec/drive.h @@ -37,14 +37,9 @@ class iecDrive : public virtualDevice std::unique_ptr _base; // Always points to current directory/image std::string _last_file; // Always points to last loaded file - // Named Channel functions - //std::shared_ptr currentStream; - bool registerStream (uint8_t channel); - std::shared_ptr retrieveStream ( uint8_t channel ); - bool closeStream ( uint8_t channel, bool close_all = false ); - uint16_t retrieveLastByte ( uint8_t channel ); - void storeLastByte( uint8_t channel, char last); - void flushLastByte( uint8_t channel ); + // RAM/ROM +// std::streambuf ram; + std::unique_ptr rom; // ROM File for current drive model if available // Directory uint16_t sendHeader(std::string header, std::string id); @@ -58,6 +53,14 @@ class iecDrive : public virtualDevice bool saveFile(); void sendFileNotFound(); + // Named Channel functions + bool registerStream (uint8_t channel); + std::shared_ptr retrieveStream ( uint8_t channel ); + bool closeStream ( uint8_t channel, bool close_all = false ); + uint16_t retrieveLastByte ( uint8_t channel ); + void storeLastByte( uint8_t channel, char last); + void flushLastByte( uint8_t channel ); + struct _error_response { unsigned char errnum = 73; diff --git a/lib/device/iec/network.cpp b/lib/device/iec/network.cpp index 9f889837f..d743476ab 100644 --- a/lib/device/iec/network.cpp +++ b/lib/device/iec/network.cpp @@ -109,7 +109,7 @@ void iecNetwork::iec_open() [](unsigned char c) { return std::toupper(c); }); // Instantiate protocol based on the scheme - Debug_printv("Creating protocol for chema %s\r\n", channel_data.urlParser->scheme.c_str()); + Debug_printv("Creating protocol for schema %s\r\n", channel_data.urlParser->scheme.c_str()); channel_data.protocol = std::move(NetworkProtocolFactory::createProtocol(channel_data.urlParser->scheme, channel_data)); if (!channel_data.protocol) { @@ -311,7 +311,7 @@ void iecNetwork::iec_reopen_channel_listen() int channelId = commanddata.channel; auto& channel_data = network_data_map[channelId]; - Debug_printv("channel[%2X]", channelId); + Debug_printv("channel[%02X]", channelId); if (!channel_data.protocol) { @@ -355,7 +355,7 @@ void iecNetwork::iec_reopen_channel_talk() bool set_eoi = false; NetworkStatus ns; - Debug_printv("channel[%2X]", channelId); + Debug_printv("channel[%02X]", channelId); // If protocol isn't connected, then return not connected. if (!channel_data.protocol) @@ -733,7 +733,7 @@ void iecNetwork::iec_command() return; } - Debug_printf("pt[0]=='%s'\n", pt[0].c_str()); + Debug_printv("pt[0]=='%s'\n", pt[0].c_str()); if (pt[0] == "cd") set_prefix(); else if (pt[0] == "chmode") @@ -789,13 +789,13 @@ void iecNetwork::iec_command() return; } - Debug_printv("pt[0][0]=[%2X] pt[1]=[%d] aux1[%d] aux2[%d]", pt[0][0], channel, cmdFrame.aux1, cmdFrame.aux2); - - if (channel_data.protocol->special_inquiry(pt[0][0]) == 0x00) + uint8_t m = channel_data.protocol->special_inquiry(pt[0][0]); + Debug_printv("pt[0][0]=[%2X] pt[1]=[%d] size[%d] m[%d]", pt[0][0], channel, pt.size(), m); + if (m == 0x00) perform_special_00(); - else if (channel_data.protocol->special_inquiry(pt[0][0]) == 0x40) + else if (m == 0x40) perform_special_40(); - else if (channel_data.protocol->special_inquiry(pt[0][0]) == 0x80) + else if (m == 0x80) perform_special_80(); } } diff --git a/lib/http/webdav/request.cpp b/lib/http/webdav/request.cpp index de36626c4..20044b51a 100644 --- a/lib/http/webdav/request.cpp +++ b/lib/http/webdav/request.cpp @@ -1,45 +1,45 @@ -#include -#include - #include "request.h" +#include +#include + using namespace WebDav; bool Request::parseRequest() { - std::string s; - - s = getHeader("Overwrite"); - if (!s.empty()) { - if (s == "F") - overwrite = false; - else if (s != "T") - return false; - } - - s = getHeader("Depth"); - if (!s.empty()) { - if (s == "0") - depth = DEPTH_0; - else if (s == "1") - depth = DEPTH_1; - else if (s != "infinity") - return false; - } - - return true; + std::string s; + + s = getHeader("Overwrite"); + if (!s.empty()) { + if (s == "F") + overwrite = false; + else if (s != "T") + return false; + } + + s = getHeader("Depth"); + if (!s.empty()) { + if (s == "0") + depth = DEPTH_0; + else if (s == "1") + depth = DEPTH_1; + else if (s != "infinity") + return false; + } + + return true; } std::string Request::getDestination() { - std::string destination = getHeader("Destination"); - std::string host = getHeader("Host"); + std::string destination = getHeader("Destination"); + std::string host = getHeader("Host"); - if (destination.empty() || host.empty()) - return ""; + if (destination.empty() || host.empty()) + return ""; - size_t pos = destination.find(host); - if (pos == std::string::npos) - return ""; + size_t pos = destination.find(host); + if (pos == std::string::npos) + return ""; - return destination.substr(pos + host.length()); + return destination.substr(pos + host.length()); } diff --git a/lib/http/webdav/request.h b/lib/http/webdav/request.h index 395523aa0..844b71e1e 100644 --- a/lib/http/webdav/request.h +++ b/lib/http/webdav/request.h @@ -3,8 +3,11 @@ #include #include +#include "string_utils.h" #include "../../include/debug.h" +#define HTTPD_100 "100 Continue" + namespace WebDav { @@ -24,6 +27,10 @@ namespace WebDav path = httpd_req->uri; depth = DEPTH_INFINITY; overwrite = true; + + // Drop trailing slash from path + if (mstr::endsWith(path, "/")) + mstr::drop(path, 1); } bool parseRequest(); @@ -47,6 +54,12 @@ namespace WebDav return s; } + void sendContinue() + { + std::string c = "HTTP/1.1 " HTTPD_100 "\r\n\r\n"; + httpd_send(req, c.c_str(), c.length()); + } + size_t getContentLength() { if (!req) diff --git a/lib/http/webdav/response.cpp b/lib/http/webdav/response.cpp index d6b8baa55..830767dca 100644 --- a/lib/http/webdav/response.cpp +++ b/lib/http/webdav/response.cpp @@ -7,9 +7,10 @@ using namespace WebDav; void Response::setDavHeaders() { - setHeader("DAV", "1"); + setHeader("DAV", "1,2"); setHeader("Allow", "COPY,DELETE,GET,HEAD,LOCK,MKCOL,MOVE,OPTIONS,PROPFIND,PROPPATCH,PUT,UNLOCK"); - setHeader("Connection", "close"); + setHeader("Keep-Alive", "timeout=5, max=100"); + setHeader("Connection", "Keep-Alive"); } void Response::setHeader(std::string header, std::string value) { diff --git a/lib/http/webdav/response.h b/lib/http/webdav/response.h index 1dc86d8e3..c235d44c2 100644 --- a/lib/http/webdav/response.h +++ b/lib/http/webdav/response.h @@ -42,7 +42,6 @@ namespace WebDav public: Response(httpd_req_t *httpd_req) { req = httpd_req; - setDavHeaders(); } ~Response() {} @@ -108,6 +107,7 @@ namespace WebDav //Debug_printv("status[%s]", status); httpd_resp_set_status(req, status); + setDavHeaders(); } void setContentType(const char *ct) @@ -141,9 +141,7 @@ namespace WebDav void closeBody() { - //Debug_printv("chunked[%d]", chunked); - //if (!chunked) - httpd_resp_send(req, "", 0); + httpd_resp_send(req, "", 0); } private: diff --git a/lib/http/webdav/webdav_server.cpp b/lib/http/webdav/webdav_server.cpp index 9a26d733d..fd8aa1049 100644 --- a/lib/http/webdav/webdav_server.cpp +++ b/lib/http/webdav/webdav_server.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include "file-utils.h" #include "string_utils.h" @@ -21,19 +23,29 @@ Server::Server(std::string rootURI, std::string rootPath) : rootURI(rootURI), ro std::string Server::uriToPath(std::string uri) { + if ( rootURI == rootPath ) + return mstr::urlDecode(uri); + if (uri.find(rootURI) != 0) return rootPath; std::string path = rootPath + uri.substr(rootURI.length()); - while (path.substr(path.length() - 1, 1) == "/") - path = path.substr(0, path.length() - 1); mstr::replaceAll(path, "//", "/"); //Debug_printv("uri[%s] path[%s]", uri.c_str(), path.c_str()); + if ( path.length() > 1 ) + { + while (path.substr(path.length() - 1, 1) == "/") + path = path.substr(0, path.length() - 1); + } + //Debug_printv("uri[%s] path[%s]", uri.c_str(), path.c_str()); return mstr::urlDecode(path); } std::string Server::pathToURI(std::string path) { + if ( rootURI == rootPath ) + return mstr::urlEncode(path);; + if (path.find(rootPath) != 0) return ""; @@ -63,7 +75,7 @@ void Server::sendMultiStatusResponse(Response &resp, MultiStatusResponse &msr) { std::ostringstream s; - s << "\r\n"; + s << "\r\n"; xmlElement(s, "D:href", msr.href.c_str()); s << "\r\n"; xmlElement(s, "D:status", msr.status.c_str()); @@ -72,7 +84,7 @@ void Server::sendMultiStatusResponse(Response &resp, MultiStatusResponse &msr) for (const auto &p : msr.props) xmlElement(s, p.first.c_str(), p.second.c_str()); - xmlElement(s, "esp:resourcetype", msr.isCollection ? "" : ""); + xmlElement(s, "D:resourcetype", msr.isCollection ? "" : ""); s << "\r\n"; s << "\r\n"; @@ -85,11 +97,15 @@ void Server::sendMultiStatusResponse(Response &resp, MultiStatusResponse &msr) int Server::sendPropResponse(Response &resp, std::string path, int recurse) { + mstr::replaceAll(path, "//", "/"); std::string uri = pathToURI(path); //Debug_printv("uri[%s] path[%s] recurse[%d]", uri.c_str(), path.c_str(), recurse); - bool exists = (path == rootPath) || - (access(path.c_str(), R_OK) == 0); + // bool exists = (path == rootPath) || + // (access(path.c_str(), R_OK) == 0); + struct stat sb; + int i = stat(path.c_str(), &sb); + bool exists (i == 0); MultiStatusResponse r; @@ -99,21 +115,18 @@ int Server::sendPropResponse(Response &resp, std::string path, int recurse) { r.status = "HTTP/1.1 200 OK"; - struct stat sb; - int ret = stat(path.c_str(), &sb); - if (ret < 0) - return -errno; + r.props["D:creationdate"] = formatTime(sb.st_ctime); + r.props["D:getlastmodified"] = formatTime(sb.st_mtime); + //r.props["D:displayname"] = mstr::urlEncode(basename(path.c_str())); - r.props["esp:creationdate"] = formatTime(sb.st_ctime); - r.props["esp:getlastmodified"] = formatTime(sb.st_mtime); - r.props["esp:displayname"] = mstr::urlEncode(basename(path.c_str())); + std::string s = path + std::to_string(sb.st_mtime); + r.props["D:getetag"] = mstr::sha1(s); r.isCollection = ((sb.st_mode & S_IFMT) == S_IFDIR); if ( !r.isCollection ) { - r.props["esp:getcontentlength"] = std::to_string(sb.st_size); - r.props["esp:getcontenttype"] = HTTPD_TYPE_OCTET; - r.props["esp:getetag"] = std::to_string(sb.st_ino); + r.props["D:getcontentlength"] = std::to_string(sb.st_size); + r.props["D:getcontenttype"] = HTTPD_TYPE_OCTET; } //Debug_printv("Found!"); } @@ -141,9 +154,17 @@ int Server::sendPropResponse(Response &resp, std::string path, int recurse) std::string rpath = path + "/" + de->d_name; sendPropResponse(resp, rpath, recurse - 1); } - closedir(dir); } + + // If we are at root and SD card is mounted send entry + if (path == "/") + { + i = stat("/sd", &sb); + if (i == 0) + sendPropResponse(resp, "/sd", recurse - 1); + } + } return 0; @@ -158,7 +179,7 @@ int Server::doCopy(Request &req, Response &resp) std::string source = uriToPath(req.getPath()); std::string destination = uriToPath(req.getDestination()); - //Debug_printv("req[%s] source[%s]", req.getPath().c_str(), source.c_str()); + Debug_printv("req[%s] source[%s]", req.getPath().c_str(), source.c_str()); if (source == destination) return 403; @@ -233,10 +254,11 @@ int Server::doGet(Request &req, Response &resp) if (!f) return 404; + std::string s = path + std::to_string(sb.st_mtime); + resp.setHeader("Content-Length", sb.st_size); - resp.setHeader("ETag", sb.st_ino); + resp.setHeader("ETag", mstr::sha1(s)); resp.setHeader("Last-Modified", formatTime(sb.st_mtime)); - resp.setHeader("Connection","close"); ret = 0; @@ -277,8 +299,10 @@ int Server::doHead(Request &req, Response &resp) if (ret < 0) return 404; + std::string s = path + std::to_string(sb.st_mtime); + resp.setHeader("Content-Length", sb.st_size); - resp.setHeader("ETag", sb.st_ino); + resp.setHeader("ETag", mstr::sha1(s)); resp.setHeader("Last-Modified", formatTime(sb.st_mtime)); return 200; @@ -286,7 +310,38 @@ int Server::doHead(Request &req, Response &resp) int Server::doLock(Request &req, Response &resp) { - return 501; + Debug_printv("req[%s]", req.getPath().c_str()); + + resp.setStatus(200); + resp.setContentType("application/xml;charset=utf-8"); + resp.setHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); + resp.flushHeaders(); + + resp.sendChunk("\r\n"); + + std::ostringstream s; + + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "0\r\n"; + s << "http://meatloaf.cc\r\n"; + s << "Second-600\r\n"; + s << "\r\n"; + s << "opaquelocktoken:89f0e324-e19d-4efd-bafe-e383829cf5a8\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << ""; + + //Debug_printv("[%s]", s.str().c_str()); + + resp.sendChunk(s.str().c_str()); + resp.closeChunk(); + + return 200; } int Server::doMkcol(Request &req, Response &resp) @@ -378,9 +433,12 @@ int Server::doPropfind(Request &req, Response &resp) //Debug_printv("req[%s] path[%s]", req.getPath().c_str(), path.c_str()); - bool exists = (path == rootPath) || - (access(path.c_str(), R_OK) == 0); - + // bool exists = (path == rootPath) || + // (access(path.c_str(), R_OK) == 0); + struct stat sb; + int i = stat(path.c_str(), &sb); + bool exists (i == 0); + if (!exists) return 404; @@ -394,7 +452,6 @@ int Server::doPropfind(Request &req, Response &resp) resp.sendChunk("\r\n"); resp.sendChunk("\r\n"); - sendPropResponse(resp, path, recurse); resp.sendChunk("\r\n"); resp.closeChunk(); @@ -423,11 +480,21 @@ int Server::doPut(Request &req, Response &resp) //Debug_printv("req[%s] path[%s]", req.getPath().c_str(), path.c_str()); - bool exists = access(path.c_str(), R_OK) == 0; + struct stat sb; + int i = stat(path.c_str(), &sb); + bool exists (i == 0); + FILE *f = fopen(path.c_str(), "w"); if (!f) return 404; + // // Do we need to continue to get the data? + // if (req.getHeader("Expect").contains("100-continue") ) + // { + // Debug_printv("continue"); + // req.sendContinue(); + // } + int remaining = req.getContentLength(); const int chunkSize = 8192; @@ -454,9 +521,6 @@ int Server::doPut(Request &req, Response &resp) free(chunk); fclose(f); - resp.closeChunk(); - - resp.setHeader("Connection","close"); if (ret < 0) return 500; @@ -469,5 +533,7 @@ int Server::doPut(Request &req, Response &resp) int Server::doUnlock(Request &req, Response &resp) { - return 501; + Debug_printv("req[%s]", req.getPath().c_str()); + resp.setHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); + return 204; } diff --git a/lib/meatloaf/disk/d64.h b/lib/meatloaf/disk/d64.h index e9e7bb69d..67efde44e 100644 --- a/lib/meatloaf/disk/d64.h +++ b/lib/meatloaf/disk/d64.h @@ -1,6 +1,6 @@ // .D64, .D41 - 1541 disk image format // -// https://vice-emu.sourceforge.io/vice_17.html#SEC345 +// https://vice-emu.sourceforge.io/vice_16.html#SEC408 // https://ist.uwaterloo.ca/~schepers/formats/D64.TXT // https://ist.uwaterloo.ca/~schepers/formats/GEOS.TXT // https://www.lemon64.com/forum/viewtopic.php?t=70024&start=0 (File formats = Why is D64 not called D40/D41) diff --git a/lib/meatloaf/network/http.cpp b/lib/meatloaf/network/http.cpp index 089ccaa52..69bff1241 100644 --- a/lib/meatloaf/network/http.cpp +++ b/lib/meatloaf/network/http.cpp @@ -394,6 +394,7 @@ int MeatHttpClient::openAndFetchHeaders(esp_http_client_method_t meth, int resum mstr::replaceAll(url, " ", "%20"); esp_http_client_config_t config = { .url = url.c_str(), + .auth_type = HTTP_AUTH_TYPE_BASIC, .user_agent = USER_AGENT, .method = meth, .timeout_ms = 10000, diff --git a/lib/utils/string_utils.cpp b/lib/utils/string_utils.cpp index d24eca456..b62774b84 100644 --- a/lib/utils/string_utils.cpp +++ b/lib/utils/string_utils.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include //#include "../../include/petscii.h" #include "../../include/debug.h" @@ -67,6 +69,31 @@ namespace mstr { return ch == '\xA0' || std::isspace(ch); } + // is OSX/Windows junk system file + bool isJunk(std::string &s) + { + std::vector names = { + // OSX + "/._", + "/.DS_Store", + "/.fseventsd", + "/.Spotlight-V", + "/.TemporaryItems", + "/.Trashes", + "/.VolumeIcon.icns", + + // Windows + "/Desktop.ini", + "/Thumbs.ini" + }; + + for (auto it = begin (names); it != end (names); ++it) { + if (s.contains(it->c_str())) + return true; + } + + return false; + } std::string drop(std::string str, size_t count) { @@ -491,6 +518,17 @@ namespace mstr { return result; } + std::string sha1(const std::string &s) + { + unsigned char hash[21] = { 0x00 }; + mbedtls_sha1((const unsigned char *)s.c_str(), s.length(), hash); + // unsigned char output[64]; + // size_t outlen; + // mbedtls_base64_encode(output, 64, &outlen, hash, 20); + std::string o(reinterpret_cast< char const* >(hash)); + return toHex(o); + } + std::string urlDecode(const std::string& s) { return urlDecode(s, true); diff --git a/lib/utils/string_utils.h b/lib/utils/string_utils.h index 57f7b5087..bb7aa802d 100644 --- a/lib/utils/string_utils.h +++ b/lib/utils/string_utils.h @@ -55,6 +55,8 @@ namespace mstr { void urlDecode(char *s, size_t size, bool alter_pluses); void urlDecode(char *s, size_t size); + std::string sha1(const std::string &s); + // void toASCII(std::string &s); // void toPETSCII(std::string &s); std::string toUTF8(const std::string &petsciiInput); @@ -65,6 +67,7 @@ namespace mstr { bool isText(std::string &s); bool isNumeric(std::string &s); bool isA0Space(int ch); + bool isJunk(std::string &s); void A02Space(std::string &s); std::string format(const char *format, ...);