From 53b731190e20be39bd93f676404bbd944fce0dae Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Thu, 25 Mar 2021 00:23:20 -0700 Subject: [PATCH 1/3] allow the hostname to be configured through setup.ini or gcode --- ESPWebDAV.ino | 18 ++++++++++-------- config.cpp | 32 +++++++++++++++++++++++++++----- config.h | 10 +++++++--- gcode.cpp | 31 ++++++++++++++++++++++--------- gcode.h | 1 + ini/SETUP.INI | 3 ++- network.cpp | 4 ++-- network.h | 1 - 8 files changed, 71 insertions(+), 29 deletions(-) diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index 885de86..0468aa5 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -25,19 +25,21 @@ void setup() { if(config.load() == 1) { // Connected before if(!network.start()) { SERIAL_ECHOLN("Connect fail, please check your INI file or set the wifi config and connect again"); - SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); - SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); - SERIAL_ECHOLN("- M52: Start to connect the wifi"); - SERIAL_ECHOLN("- M53: Check the connection status"); + SERIAL_ECHOLN("- M50: Set WiFi SSID"); + SERIAL_ECHOLN("- M51: Set WiFi Password"); + SERIAL_ECHOLN("- M52: Connect"); + SERIAL_ECHOLN("- M53: Connection Status"); + SERIAL_ECHOLN("- M54: Set Hostname"); } } else { SERIAL_ECHOLN("Welcome to FYSETC: www.fysetc.com"); SERIAL_ECHOLN("Please set the wifi config first"); - SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); - SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); - SERIAL_ECHOLN("- M52: Start to connect the wifi"); - SERIAL_ECHOLN("- M53: Check the connection status"); + SERIAL_ECHOLN("- M50: Set WiFi SSID"); + SERIAL_ECHOLN("- M51: Set WiFi Password"); + SERIAL_ECHOLN("- M52: Connect"); + SERIAL_ECHOLN("- M53: Connection Status"); + SERIAL_ECHOLN("- M54: Set Hostname"); } } diff --git a/config.cpp b/config.cpp index 24abee8..4b37a6d 100644 --- a/config.cpp +++ b/config.cpp @@ -66,11 +66,23 @@ int Config::loadSD() { goto FAIL; } } + else if(sKEY == "HOSTNAME") { + SERIAL_ECHOLN("INI file : HOSTNAME found"); + if(sValue.length()>0) { + memset(data._hostname,'\0',HOSTNAME_LEN); + sValue.toCharArray(data._hostname,HOSTNAME_LEN); + step++; + } + else { + rst = -7; + goto FAIL; + } + } else continue; // Bad line } - if(step != 2) { // We miss ssid or password + if(step != 3) { // We miss ssid or password //memset(data,) // TODO: do we need to empty the data? - SERIAL_ECHOLN("Please check your SSDI or PASSWORD in ini file"); + SERIAL_ECHOLN("Please check your SSID, PASSWORD, and HOSTNAME in ini file"); rst = -6; goto FAIL; } @@ -123,14 +135,24 @@ void Config::password(char* password) { strncpy(data.psw,password,WIFI_PASSWD_LEN); } -void Config::save(const char*ssid,const char*password) { - if(ssid ==NULL || password==NULL) +char* Config::hostname() { + return data._hostname; +} + +void Config::hostname(char* hostname) { + if (hostname==NULL) return; + strncpy(data._hostname, hostname, HOSTNAME_LEN); +} + +void Config::save(const char*ssid,const char*password, const char* hostname) { + if(ssid ==NULL || password==NULL || hostname==NULL) return; EEPROM.begin(EEPROM_SIZE); data.flag = 1; strncpy(data.ssid, ssid, WIFI_SSID_LEN); strncpy(data.psw, password, WIFI_PASSWD_LEN); + strncpy(data._hostname, hostname, HOSTNAME_LEN); uint8_t *p = (uint8_t*)(&data); for (int i = 0; i < sizeof(data); i++) { @@ -140,7 +162,7 @@ void Config::save(const char*ssid,const char*password) { } void Config::save() { - if(data.ssid == NULL || data.psw == NULL) + if(data.ssid == NULL || data.psw == NULL || data._hostname==NULL) return; EEPROM.begin(EEPROM_SIZE); diff --git a/config.h b/config.h index 0101ad7..71ad5bd 100644 --- a/config.h +++ b/config.h @@ -6,14 +6,16 @@ #define WIFI_SSID_LEN 32 #define WIFI_PASSWD_LEN 64 +#define HOSTNAME_LEN 32 #define EEPROM_SIZE 512 typedef struct config_type { unsigned char flag; // Was saved before? - char ssid[32]; - char psw[64]; + char ssid[WIFI_SSID_LEN]; + char psw[WIFI_PASSWD_LEN]; + char _hostname[HOSTNAME_LEN]; }CONFIG_TYPE; class Config { @@ -24,7 +26,9 @@ class Config { void ssid(char* ssid); char* password(); void password(char* password); - void save(const char*ssid,const char*password); + char* hostname(); + void hostname(char* hostname); + void save(const char*ssid,const char*password, const char* hostname); void save(); int save_ip(const char *ip); diff --git a/gcode.cpp b/gcode.cpp index 77faffb..583e8f7 100644 --- a/gcode.cpp +++ b/gcode.cpp @@ -122,10 +122,11 @@ void Gcode::gcode_M51() { void Gcode::gcode_M52() { if(!network.start()) { SERIAL_ECHOLN("Connect fail, please check your INI file or set the wifi config and connect again"); - SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); - SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); - SERIAL_ECHOLN("- M52: Start to connect the wifi"); - SERIAL_ECHOLN("- M53: Check the connection status"); + SERIAL_ECHOLN("- M50: Set WiFi SSID"); + SERIAL_ECHOLN("- M51: Set WiFi Password"); + SERIAL_ECHOLN("- M52: Connect"); + SERIAL_ECHOLN("- M53: Connection Status"); + SERIAL_ECHOLN("- M54: Set Hostname"); } } @@ -135,10 +136,11 @@ void Gcode::gcode_M52() { void Gcode::gcode_M53() { if(WiFi.status() != WL_CONNECTED) { SERIAL_ECHOLN("Wifi not connected"); - SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); - SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); - SERIAL_ECHOLN("- M52: Start to connect the wifi"); - SERIAL_ECHOLN("- M53: Check the connection status"); + SERIAL_ECHOLN("- M50: Set WiFi SSID"); + SERIAL_ECHOLN("- M51: Set WiFi Password"); + SERIAL_ECHOLN("- M52: Connect"); + SERIAL_ECHOLN("- M53: Connection Status"); + SERIAL_ECHOLN("- M54: Set Hostname"); } else { SERIAL_ECHOLN(""); @@ -146,10 +148,20 @@ void Gcode::gcode_M53() { SERIAL_ECHO("IP address: "); SERIAL_ECHOLN(WiFi.localIP()); SERIAL_ECHO("RSSI: "); SERIAL_ECHOLN(WiFi.RSSI()); SERIAL_ECHO("Mode: "); SERIAL_ECHOLN(WiFi.getPhyMode()); - SERIAL_ECHO("Asscess to SD at the Run prompt : \\\\"); SERIAL_ECHO(WiFi.localIP());SERIAL_ECHOLN("\\DavWWWRoot"); + SERIAL_ECHO("Access to SD at the Run prompt : \\\\"); SERIAL_ECHO(config.hostname());SERIAL_ECHOLN("\\DavWWWRoot"); } } +/** + * M54: Set the hostname + */ +void Gcode::gcode_M54() { + for (char *fn = parser.string_arg; *fn; ++fn); + config.hostname(parser.string_arg); + SERIAL_ECHO("hostname: "); + SERIAL_ECHOLN(config.hostname()); +} + /** * Process the parsed command and dispatch it to its handler */ @@ -169,6 +181,7 @@ void Gcode::process_parsed_command() { case 51: gcode_M51(); break; case 52: gcode_M52(); break; case 53: gcode_M53(); break; + case 54: gcode_M54(); break; default: parser.unknown_command_error(); } break; diff --git a/gcode.h b/gcode.h index dcf04a6..e16736a 100644 --- a/gcode.h +++ b/gcode.h @@ -21,6 +21,7 @@ class Gcode { void gcode_M51(); void gcode_M52(); void gcode_M53(); + void gcode_M54(); void process_parsed_command(); void process_next_command(); diff --git a/ini/SETUP.INI b/ini/SETUP.INI index 3a48cd8..a3e06b6 100644 --- a/ini/SETUP.INI +++ b/ini/SETUP.INI @@ -1,2 +1,3 @@ SSID=xxxx -PASSWORD=xxxx \ No newline at end of file +PASSWORD=xxxx +HOSTNAME=xxxx diff --git a/network.cpp b/network.cpp index 6db1083..444f9c6 100644 --- a/network.cpp +++ b/network.cpp @@ -19,7 +19,7 @@ bool Network::start() { wifiConnecting = true; // Set hostname first - WiFi.hostname(HOSTNAME); + WiFi.hostname(config.hostname()); // Reduce startup surge current WiFi.setAutoConnect(false); WiFi.mode(WIFI_STA); @@ -46,7 +46,7 @@ bool Network::start() { SERIAL_ECHO("IP address: "); SERIAL_ECHOLN(WiFi.localIP()); SERIAL_ECHO("RSSI: "); SERIAL_ECHOLN(WiFi.RSSI()); SERIAL_ECHO("Mode: "); SERIAL_ECHOLN(WiFi.getPhyMode()); - SERIAL_ECHO("Asscess to SD at the Run prompt : \\\\"); SERIAL_ECHO(WiFi.localIP());SERIAL_ECHOLN("\\DavWWWRoot"); + SERIAL_ECHO("Access to SD at the Run prompt : \\\\"); SERIAL_ECHO(config.hostname());SERIAL_ECHOLN("\\DavWWWRoot"); wifiConnected = true; diff --git a/network.h b/network.h index 1757897..cf1081c 100644 --- a/network.h +++ b/network.h @@ -1,7 +1,6 @@ #ifndef _NETWORK_H_ #define _NETWORK_H_ -#define HOSTNAME "FYSETC" #define SERVER_PORT 80 #define WIFI_CONNECT_TIMEOUT 30000UL From cc84e20f06668a55ce963ba81fba3f928bb26ea1 Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Thu, 25 Mar 2021 00:42:48 -0700 Subject: [PATCH 2/3] make note of the required SDFat version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3029db..a34421b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ GCode can be directly uploaded from the slicer (Cura) to this remote drive, ther ## Dependencies: 1. [ESP8266 Arduino Core version 2.4](https://github.com/esp8266/Arduino) -2. [SdFat library](https://github.com/greiman/SdFat) +2. [SdFat library version 1.0.16](https://github.com/greiman/SdFat) ## Use: From 2cf8389efda2e636e27c79103bbf037634a281ee Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Thu, 25 Mar 2021 21:09:55 -0700 Subject: [PATCH 3/3] enable mounting with davfs2 (https://savannah.nongnu.org/projects/davfs2) OPTIONS / was returning a "DAV: 2" header. This causes davfs2 to reject the attempt: /sbin/mount.davfs: mounting failed; the server does not support WebDAV Setting ignore_dav_header 1 in /etc/davfs2/davfs2.conf allows the mount to continue, but this shouldn't be necessary. Advertising Class 1 capability in addition to Class 2 fixes this problem, while not affecting compatibility with other clients (was still able to mount the device from Windows 10, for instance). --- ESPWebDAV.cpp | 1138 ++++++++++++++++++++++++------------------------- 1 file changed, 569 insertions(+), 569 deletions(-) diff --git a/ESPWebDAV.cpp b/ESPWebDAV.cpp index 63666d9..b5fb052 100644 --- a/ESPWebDAV.cpp +++ b/ESPWebDAV.cpp @@ -1,569 +1,569 @@ -// WebDAV server using ESP8266 and SD card filesystem -// Targeting Windows 7 Explorer WebDav - -#include -#include -#include -#include -#include -#include "ESPWebDAV.h" - -// define cal constants -const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - - -// ------------------------ -bool ESPWebDAV::init(int chipSelectPin, SPISettings spiSettings, int serverPort) { -// ------------------------ - // start the wifi server - server = new WiFiServer(serverPort); - server->begin(); - - // initialize the SD card - return sd.begin(chipSelectPin, spiSettings); -} - -// ------------------------ -bool ESPWebDAV::initSD(int chipSelectPin, SPISettings spiSettings) { - // initialize the SD card - return sd.begin(chipSelectPin, spiSettings); -} - -// ------------------------ -bool ESPWebDAV::startServer() { -// ------------------------ - // start the wifi server - server->begin(); -} - -// ------------------------ -void ESPWebDAV::handleNotFound() { -// ------------------------ - String message = "Not found\n"; - message += "URI: "; - message += uri; - message += " Method: "; - message += method; - message += "\n"; - - sendHeader("Allow", "OPTIONS,MKCOL,POST,PUT"); - send("404 Not Found", "text/plain", message); - DBG_PRINTLN("404 Not Found"); -} - - - -// ------------------------ -void ESPWebDAV::handleReject(String rejectMessage) { -// ------------------------ - DBG_PRINT("Rejecting request: "); DBG_PRINTLN(rejectMessage); - - // handle options - if(method.equals("OPTIONS")) - return handleOptions(RESOURCE_NONE); - - // handle properties - if(method.equals("PROPFIND")) { - sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE"); - setContentLength(CONTENT_LENGTH_UNKNOWN); - send("207 Multi-Status", "application/xml;charset=utf-8", ""); - sendContent(F("/HTTP/1.1 200 OKFri, 30 Nov 1979 00:00:00 GMT\"3333333333333333333333333333333333333333\"")); - - if(depthHeader.equals("1")) { - sendContent(F("/")); - sendContent(rejectMessage); - sendContent(F("HTTP/1.1 200 OKFri, 01 Apr 2016 16:07:40 GMT\"2222222222222222222222222222222222222222\"0application/octet-stream")); - } - - sendContent(F("")); - return; - } - else - // if reached here, means its a 404 - handleNotFound(); -} - - - - -// set http_proxy=http://localhost:36036 -// curl -v -X PROPFIND -H "Depth: 1" http://Rigidbot/Old/PipeClip.gcode -// Test PUT a file: curl -v -T c.txt -H "Expect:" http://Rigidbot/c.txt -// C:\Users\gsbal>curl -v -X LOCK http://Rigidbot/EMA_CPP_TRCC_Tutorial/Consumer.cpp -d "CARBON2\gsbal" -// ------------------------ -void ESPWebDAV::handleRequest(String blank) { -// ------------------------ - ResourceType resource = RESOURCE_NONE; - - // does uri refer to a file or directory or a null? - FatFile tFile; - if(tFile.open(sd.vwd(), uri.c_str(), O_READ)) { - resource = tFile.isDir() ? RESOURCE_DIR : RESOURCE_FILE; - tFile.close(); - } - - DBG_PRINT("\r\nm: "); DBG_PRINT(method); - DBG_PRINT(" r: "); DBG_PRINT(resource); - DBG_PRINT(" u: "); DBG_PRINTLN(uri); - - // add header that gets sent everytime - sendHeader("DAV", "2"); - - // handle properties - if(method.equals("PROPFIND")) - return handleProp(resource); - - if(method.equals("GET")) - return handleGet(resource, true); - - if(method.equals("HEAD")) - return handleGet(resource, false); - - // handle options - if(method.equals("OPTIONS")) - return handleOptions(resource); - - // handle file create/uploads - if(method.equals("PUT")) - return handlePut(resource); - - // handle file locks - if(method.equals("LOCK")) - return handleLock(resource); - - if(method.equals("UNLOCK")) - return handleUnlock(resource); - - if(method.equals("PROPPATCH")) - return handlePropPatch(resource); - - // directory creation - if(method.equals("MKCOL")) - return handleDirectoryCreate(resource); - - // move a file or directory - if(method.equals("MOVE")) - return handleMove(resource); - - // delete a file or directory - if(method.equals("DELETE")) - return handleDelete(resource); - - // if reached here, means its a 404 - handleNotFound(); -} - - - -// ------------------------ -void ESPWebDAV::handleOptions(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing OPTION"); - sendHeader("Allow", "PROPFIND,GET,DELETE,PUT,COPY,MOVE"); - send("200 OK", NULL, ""); -} - - - -// ------------------------ -void ESPWebDAV::handleLock(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing LOCK"); - - // does URI refer to an existing resource - if(resource == RESOURCE_NONE) - return handleNotFound(); - - sendHeader("Allow", "PROPPATCH,PROPFIND,OPTIONS,DELETE,UNLOCK,COPY,LOCK,MOVE,HEAD,POST,PUT,GET"); - sendHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); - - size_t contentLen = contentLengthHeader.toInt(); - uint8_t buf[1024]; - size_t numRead = readBytesWithTimeout(buf, sizeof(buf), contentLen); - - if(numRead == 0) - return handleNotFound(); - - buf[contentLen] = 0; - String inXML = String((char*) buf); - int startIdx = inXML.indexOf(""); - int endIdx = inXML.indexOf(""); - if(startIdx < 0 || endIdx < 0) - return handleNotFound(); - - String lockUser = inXML.substring(startIdx + 8, endIdx); - String resp1 = F("urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); - String resp2 = F("infinity"); - String resp3 = F("Second-3600"); - - send("200 OK", "application/xml;charset=utf-8", resp1 + uri + resp2 + lockUser + resp3); -} - - - -// ------------------------ -void ESPWebDAV::handleUnlock(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing UNLOCK"); - sendHeader("Allow", "PROPPATCH,PROPFIND,OPTIONS,DELETE,UNLOCK,COPY,LOCK,MOVE,HEAD,POST,PUT,GET"); - sendHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); - send("204 No Content", NULL, ""); -} - - - -// ------------------------ -void ESPWebDAV::handlePropPatch(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("PROPPATCH forwarding to PROPFIND"); - handleProp(resource); -} - - - -// ------------------------ -void ESPWebDAV::handleProp(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing PROPFIND"); - // check depth header - DepthType depth = DEPTH_NONE; - if(depthHeader.equals("1")) - depth = DEPTH_CHILD; - else if(depthHeader.equals("infinity")) - depth = DEPTH_ALL; - - DBG_PRINT("Depth: "); DBG_PRINTLN(depth); - - // does URI refer to an existing resource - if(resource == RESOURCE_NONE) - return handleNotFound(); - - if(resource == RESOURCE_FILE) - sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); - else - sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE"); - - setContentLength(CONTENT_LENGTH_UNKNOWN); - send("207 Multi-Status", "application/xml;charset=utf-8", ""); - sendContent(F("")); - sendContent(F("")); - - // open this resource - SdFile baseFile; - baseFile.open(uri.c_str(), O_READ); - sendPropResponse(false, &baseFile); - - if((resource == RESOURCE_DIR) && (depth == DEPTH_CHILD)) { - // append children information to message - SdFile childFile; - while(childFile.openNext(&baseFile, O_READ)) { - yield(); - sendPropResponse(true, &childFile); - childFile.close(); - } - } - - baseFile.close(); - sendContent(F("")); -} - - - -// ------------------------ -void ESPWebDAV::sendPropResponse(boolean recursing, FatFile *curFile) { -// ------------------------ - char buf[255]; - curFile->getName(buf, sizeof(buf)); - -// String fullResPath = "http://" + hostHeader + uri; - String fullResPath = uri; - - if(recursing) - if(fullResPath.endsWith("/")) - fullResPath += String(buf); - else - fullResPath += "/" + String(buf); - - // get file modified time - dir_t dir; - curFile->dirEntry(&dir); - - // convert to required format - tm tmStr; - tmStr.tm_hour = FAT_HOUR(dir.lastWriteTime); - tmStr.tm_min = FAT_MINUTE(dir.lastWriteTime); - tmStr.tm_sec = FAT_SECOND(dir.lastWriteTime); - tmStr.tm_year = FAT_YEAR(dir.lastWriteDate) - 1900; - tmStr.tm_mon = FAT_MONTH(dir.lastWriteDate) - 1; - tmStr.tm_mday = FAT_DAY(dir.lastWriteDate); - time_t t2t = mktime(&tmStr); - tm *gTm = gmtime(&t2t); - - // Tue, 13 Oct 2015 17:07:35 GMT - sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT", wdays[gTm->tm_wday], gTm->tm_mday, months[gTm->tm_mon], gTm->tm_year + 1900, gTm->tm_hour, gTm->tm_min, gTm->tm_sec); - String fileTimeStamp = String(buf); - - - // send the XML information about thyself to client - sendContent(F("")); - // append full file path - sendContent(fullResPath); - sendContent(F("HTTP/1.1 200 OK")); - // append modified date - sendContent(fileTimeStamp); - sendContent(F("")); - // append unique tag generated from full path - sendContent("\"" + sha1(fullResPath + fileTimeStamp) + "\""); - sendContent(F("")); - - if(curFile->isDir()) - sendContent(F("")); - else { - sendContent(F("")); - // append the file size - sendContent(String(curFile->fileSize())); - sendContent(F("")); - // append correct file mime type - sendContent(getMimeType(fullResPath)); - sendContent(F("")); - } - sendContent(F("")); -} - - - - -// ------------------------ -void ESPWebDAV::handleGet(ResourceType resource, bool isGet) { -// ------------------------ - DBG_PRINTLN("Processing GET"); - - // does URI refer to an existing file resource - if(resource != RESOURCE_FILE) - return handleNotFound(); - - SdFile rFile; - long tStart = millis(); - uint8_t buf[1460]; - rFile.open(uri.c_str(), O_READ); - - sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); - size_t fileSize = rFile.fileSize(); - setContentLength(fileSize); - String contentType = getMimeType(uri); - if(uri.endsWith(".gz") && contentType != "application/x-gzip" && contentType != "application/octet-stream") - sendHeader("Content-Encoding", "gzip"); - - send("200 OK", contentType.c_str(), ""); - - if(isGet) { - // disable Nagle if buffer size > TCP MTU of 1460 - // client.setNoDelay(1); - - // send the file - while(rFile.available()) { - // SD read speed ~ 17sec for 4.5MB file - int numRead = rFile.read(buf, sizeof(buf)); - client.write(buf, numRead); - } - } - - rFile.close(); - DBG_PRINT("File "); DBG_PRINT(fileSize); DBG_PRINT(" bytes sent in: "); DBG_PRINT((millis() - tStart)/1000); DBG_PRINTLN(" sec"); -} - - - - -// ------------------------ -void ESPWebDAV::handlePut(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing Put"); - - // does URI refer to a directory - if(resource == RESOURCE_DIR) - return handleNotFound(); - - SdFile nFile; - sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); - - // if file does not exist, create it - if(resource == RESOURCE_NONE) { - if(!nFile.open(uri.c_str(), O_CREAT | O_WRITE)) - return handleWriteError("Unable to create a new file", &nFile); - } - - // file is created/open for writing at this point - DBG_PRINT(uri); DBG_PRINTLN(" - ready for data"); - // did server send any data in put - size_t contentLen = contentLengthHeader.toInt(); - - if(contentLen != 0) { - // buffer size is critical *don't change* - const size_t WRITE_BLOCK_CONST = 512; - uint8_t buf[WRITE_BLOCK_CONST]; - long tStart = millis(); - size_t numRemaining = contentLen; - - // high speed raw write implementation - // close any previous file - nFile.close(); - // delete old file - sd.remove(uri.c_str()); - - // create a contiguous file - size_t contBlocks = (contentLen/WRITE_BLOCK_CONST + 1); - uint32_t bgnBlock, endBlock; - - if (!nFile.createContiguous(sd.vwd(), uri.c_str(), contBlocks * WRITE_BLOCK_CONST)) - return handleWriteError("File create contiguous sections failed", &nFile); - - // get the location of the file's blocks - if (!nFile.contiguousRange(&bgnBlock, &endBlock)) - return handleWriteError("Unable to get contiguous range", &nFile); - - if (!sd.card()->writeStart(bgnBlock, contBlocks)) - return handleWriteError("Unable to start writing contiguous range", &nFile); - - // read data from stream and write to the file - while(numRemaining > 0) { - size_t numToRead = (numRemaining > WRITE_BLOCK_CONST) ? WRITE_BLOCK_CONST : numRemaining; - size_t numRead = readBytesWithTimeout(buf, sizeof(buf), numToRead); - if(numRead == 0) - break; - - // store whole buffer into file regardless of numRead - if (!sd.card()->writeData(buf)) - return handleWriteError("Write data failed", &nFile); - - // reduce the number outstanding - numRemaining -= numRead; - } - - // stop writing operation - if (!sd.card()->writeStop()) - return handleWriteError("Unable to stop writing contiguous range", &nFile); - - // detect timeout condition - if(numRemaining) - return handleWriteError("Timed out waiting for data", &nFile); - - // truncate the file to right length - if(!nFile.truncate(contentLen)) - return handleWriteError("Unable to truncate the file", &nFile); - - DBG_PRINT("File "); DBG_PRINT(contentLen - numRemaining); DBG_PRINT(" bytes stored in: "); DBG_PRINT((millis() - tStart)/1000); DBG_PRINTLN(" sec"); - } - - if(resource == RESOURCE_NONE) - send("201 Created", NULL, ""); - else - send("200 OK", NULL, ""); - - nFile.close(); -} - - - - -// ------------------------ -void ESPWebDAV::handleWriteError(String message, FatFile *wFile) { -// ------------------------ - // close this file - wFile->close(); - // delete the wrile being written - sd.remove(uri.c_str()); - // send error - send("500 Internal Server Error", "text/plain", message); - DBG_PRINTLN(message); -} - - -// ------------------------ -void ESPWebDAV::handleDirectoryCreate(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing MKCOL"); - - // does URI refer to anything - if(resource != RESOURCE_NONE) - return handleNotFound(); - - // create directory - if (!sd.mkdir(uri.c_str(), true)) { - // send error - send("500 Internal Server Error", "text/plain", "Unable to create directory"); - DBG_PRINTLN("Unable to create directory"); - return; - } - - DBG_PRINT(uri); DBG_PRINTLN(" directory created"); - sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); - send("201 Created", NULL, ""); -} - - - -// ------------------------ -void ESPWebDAV::handleMove(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing MOVE"); - - // does URI refer to anything - if(resource == RESOURCE_NONE) - return handleNotFound(); - - if(destinationHeader.length() == 0) - return handleNotFound(); - - String dest = urlToUri(destinationHeader); - - DBG_PRINT("Move destination: "); DBG_PRINTLN(dest); - - // move file or directory - if ( !sd.rename(uri.c_str(), dest.c_str()) ) { - // send error - send("500 Internal Server Error", "text/plain", "Unable to move"); - DBG_PRINTLN("Unable to move file/directory"); - return; - } - - DBG_PRINTLN("Move successful"); - sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); - send("201 Created", NULL, ""); -} - - - - -// ------------------------ -void ESPWebDAV::handleDelete(ResourceType resource) { -// ------------------------ - DBG_PRINTLN("Processing DELETE"); - - // does URI refer to anything - if(resource == RESOURCE_NONE) - return handleNotFound(); - - bool retVal; - - if(resource == RESOURCE_FILE) - // delete a file - retVal = sd.remove(uri.c_str()); - else - // delete a directory - retVal = sd.rmdir(uri.c_str()); - - if(!retVal) { - // send error - send("500 Internal Server Error", "text/plain", "Unable to delete"); - DBG_PRINTLN("Unable to delete file/directory"); - return; - } - - DBG_PRINTLN("Delete successful"); - sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); - send("200 OK", NULL, ""); -} - -ESPWebDAV dav; +// WebDAV server using ESP8266 and SD card filesystem +// Targeting Windows 7 Explorer WebDav + +#include +#include +#include +#include +#include +#include "ESPWebDAV.h" + +// define cal constants +const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +// ------------------------ +bool ESPWebDAV::init(int chipSelectPin, SPISettings spiSettings, int serverPort) { +// ------------------------ + // start the wifi server + server = new WiFiServer(serverPort); + server->begin(); + + // initialize the SD card + return sd.begin(chipSelectPin, spiSettings); +} + +// ------------------------ +bool ESPWebDAV::initSD(int chipSelectPin, SPISettings spiSettings) { + // initialize the SD card + return sd.begin(chipSelectPin, spiSettings); +} + +// ------------------------ +bool ESPWebDAV::startServer() { +// ------------------------ + // start the wifi server + server->begin(); +} + +// ------------------------ +void ESPWebDAV::handleNotFound() { +// ------------------------ + String message = "Not found\n"; + message += "URI: "; + message += uri; + message += " Method: "; + message += method; + message += "\n"; + + sendHeader("Allow", "OPTIONS,MKCOL,POST,PUT"); + send("404 Not Found", "text/plain", message); + DBG_PRINTLN("404 Not Found"); +} + + + +// ------------------------ +void ESPWebDAV::handleReject(String rejectMessage) { +// ------------------------ + DBG_PRINT("Rejecting request: "); DBG_PRINTLN(rejectMessage); + + // handle options + if(method.equals("OPTIONS")) + return handleOptions(RESOURCE_NONE); + + // handle properties + if(method.equals("PROPFIND")) { + sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE"); + setContentLength(CONTENT_LENGTH_UNKNOWN); + send("207 Multi-Status", "application/xml;charset=utf-8", ""); + sendContent(F("/HTTP/1.1 200 OKFri, 30 Nov 1979 00:00:00 GMT\"3333333333333333333333333333333333333333\"")); + + if(depthHeader.equals("1")) { + sendContent(F("/")); + sendContent(rejectMessage); + sendContent(F("HTTP/1.1 200 OKFri, 01 Apr 2016 16:07:40 GMT\"2222222222222222222222222222222222222222\"0application/octet-stream")); + } + + sendContent(F("")); + return; + } + else + // if reached here, means its a 404 + handleNotFound(); +} + + + + +// set http_proxy=http://localhost:36036 +// curl -v -X PROPFIND -H "Depth: 1" http://Rigidbot/Old/PipeClip.gcode +// Test PUT a file: curl -v -T c.txt -H "Expect:" http://Rigidbot/c.txt +// C:\Users\gsbal>curl -v -X LOCK http://Rigidbot/EMA_CPP_TRCC_Tutorial/Consumer.cpp -d "CARBON2\gsbal" +// ------------------------ +void ESPWebDAV::handleRequest(String blank) { +// ------------------------ + ResourceType resource = RESOURCE_NONE; + + // does uri refer to a file or directory or a null? + FatFile tFile; + if(tFile.open(sd.vwd(), uri.c_str(), O_READ)) { + resource = tFile.isDir() ? RESOURCE_DIR : RESOURCE_FILE; + tFile.close(); + } + + DBG_PRINT("\r\nm: "); DBG_PRINT(method); + DBG_PRINT(" r: "); DBG_PRINT(resource); + DBG_PRINT(" u: "); DBG_PRINTLN(uri); + + // add header that gets sent everytime + sendHeader("DAV", "1, 2"); + + // handle properties + if(method.equals("PROPFIND")) + return handleProp(resource); + + if(method.equals("GET")) + return handleGet(resource, true); + + if(method.equals("HEAD")) + return handleGet(resource, false); + + // handle options + if(method.equals("OPTIONS")) + return handleOptions(resource); + + // handle file create/uploads + if(method.equals("PUT")) + return handlePut(resource); + + // handle file locks + if(method.equals("LOCK")) + return handleLock(resource); + + if(method.equals("UNLOCK")) + return handleUnlock(resource); + + if(method.equals("PROPPATCH")) + return handlePropPatch(resource); + + // directory creation + if(method.equals("MKCOL")) + return handleDirectoryCreate(resource); + + // move a file or directory + if(method.equals("MOVE")) + return handleMove(resource); + + // delete a file or directory + if(method.equals("DELETE")) + return handleDelete(resource); + + // if reached here, means its a 404 + handleNotFound(); +} + + + +// ------------------------ +void ESPWebDAV::handleOptions(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing OPTION"); + sendHeader("Allow", "PROPFIND,GET,DELETE,PUT,COPY,MOVE"); + send("200 OK", NULL, ""); +} + + + +// ------------------------ +void ESPWebDAV::handleLock(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing LOCK"); + + // does URI refer to an existing resource + if(resource == RESOURCE_NONE) + return handleNotFound(); + + sendHeader("Allow", "PROPPATCH,PROPFIND,OPTIONS,DELETE,UNLOCK,COPY,LOCK,MOVE,HEAD,POST,PUT,GET"); + sendHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); + + size_t contentLen = contentLengthHeader.toInt(); + uint8_t buf[1024]; + size_t numRead = readBytesWithTimeout(buf, sizeof(buf), contentLen); + + if(numRead == 0) + return handleNotFound(); + + buf[contentLen] = 0; + String inXML = String((char*) buf); + int startIdx = inXML.indexOf(""); + int endIdx = inXML.indexOf(""); + if(startIdx < 0 || endIdx < 0) + return handleNotFound(); + + String lockUser = inXML.substring(startIdx + 8, endIdx); + String resp1 = F("urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); + String resp2 = F("infinity"); + String resp3 = F("Second-3600"); + + send("200 OK", "application/xml;charset=utf-8", resp1 + uri + resp2 + lockUser + resp3); +} + + + +// ------------------------ +void ESPWebDAV::handleUnlock(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing UNLOCK"); + sendHeader("Allow", "PROPPATCH,PROPFIND,OPTIONS,DELETE,UNLOCK,COPY,LOCK,MOVE,HEAD,POST,PUT,GET"); + sendHeader("Lock-Token", "urn:uuid:26e57cb3-834d-191a-00de-000042bdecf9"); + send("204 No Content", NULL, ""); +} + + + +// ------------------------ +void ESPWebDAV::handlePropPatch(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("PROPPATCH forwarding to PROPFIND"); + handleProp(resource); +} + + + +// ------------------------ +void ESPWebDAV::handleProp(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing PROPFIND"); + // check depth header + DepthType depth = DEPTH_NONE; + if(depthHeader.equals("1")) + depth = DEPTH_CHILD; + else if(depthHeader.equals("infinity")) + depth = DEPTH_ALL; + + DBG_PRINT("Depth: "); DBG_PRINTLN(depth); + + // does URI refer to an existing resource + if(resource == RESOURCE_NONE) + return handleNotFound(); + + if(resource == RESOURCE_FILE) + sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); + else + sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE"); + + setContentLength(CONTENT_LENGTH_UNKNOWN); + send("207 Multi-Status", "application/xml;charset=utf-8", ""); + sendContent(F("")); + sendContent(F("")); + + // open this resource + SdFile baseFile; + baseFile.open(uri.c_str(), O_READ); + sendPropResponse(false, &baseFile); + + if((resource == RESOURCE_DIR) && (depth == DEPTH_CHILD)) { + // append children information to message + SdFile childFile; + while(childFile.openNext(&baseFile, O_READ)) { + yield(); + sendPropResponse(true, &childFile); + childFile.close(); + } + } + + baseFile.close(); + sendContent(F("")); +} + + + +// ------------------------ +void ESPWebDAV::sendPropResponse(boolean recursing, FatFile *curFile) { +// ------------------------ + char buf[255]; + curFile->getName(buf, sizeof(buf)); + +// String fullResPath = "http://" + hostHeader + uri; + String fullResPath = uri; + + if(recursing) + if(fullResPath.endsWith("/")) + fullResPath += String(buf); + else + fullResPath += "/" + String(buf); + + // get file modified time + dir_t dir; + curFile->dirEntry(&dir); + + // convert to required format + tm tmStr; + tmStr.tm_hour = FAT_HOUR(dir.lastWriteTime); + tmStr.tm_min = FAT_MINUTE(dir.lastWriteTime); + tmStr.tm_sec = FAT_SECOND(dir.lastWriteTime); + tmStr.tm_year = FAT_YEAR(dir.lastWriteDate) - 1900; + tmStr.tm_mon = FAT_MONTH(dir.lastWriteDate) - 1; + tmStr.tm_mday = FAT_DAY(dir.lastWriteDate); + time_t t2t = mktime(&tmStr); + tm *gTm = gmtime(&t2t); + + // Tue, 13 Oct 2015 17:07:35 GMT + sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT", wdays[gTm->tm_wday], gTm->tm_mday, months[gTm->tm_mon], gTm->tm_year + 1900, gTm->tm_hour, gTm->tm_min, gTm->tm_sec); + String fileTimeStamp = String(buf); + + + // send the XML information about thyself to client + sendContent(F("")); + // append full file path + sendContent(fullResPath); + sendContent(F("HTTP/1.1 200 OK")); + // append modified date + sendContent(fileTimeStamp); + sendContent(F("")); + // append unique tag generated from full path + sendContent("\"" + sha1(fullResPath + fileTimeStamp) + "\""); + sendContent(F("")); + + if(curFile->isDir()) + sendContent(F("")); + else { + sendContent(F("")); + // append the file size + sendContent(String(curFile->fileSize())); + sendContent(F("")); + // append correct file mime type + sendContent(getMimeType(fullResPath)); + sendContent(F("")); + } + sendContent(F("")); +} + + + + +// ------------------------ +void ESPWebDAV::handleGet(ResourceType resource, bool isGet) { +// ------------------------ + DBG_PRINTLN("Processing GET"); + + // does URI refer to an existing file resource + if(resource != RESOURCE_FILE) + return handleNotFound(); + + SdFile rFile; + long tStart = millis(); + uint8_t buf[1460]; + rFile.open(uri.c_str(), O_READ); + + sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); + size_t fileSize = rFile.fileSize(); + setContentLength(fileSize); + String contentType = getMimeType(uri); + if(uri.endsWith(".gz") && contentType != "application/x-gzip" && contentType != "application/octet-stream") + sendHeader("Content-Encoding", "gzip"); + + send("200 OK", contentType.c_str(), ""); + + if(isGet) { + // disable Nagle if buffer size > TCP MTU of 1460 + // client.setNoDelay(1); + + // send the file + while(rFile.available()) { + // SD read speed ~ 17sec for 4.5MB file + int numRead = rFile.read(buf, sizeof(buf)); + client.write(buf, numRead); + } + } + + rFile.close(); + DBG_PRINT("File "); DBG_PRINT(fileSize); DBG_PRINT(" bytes sent in: "); DBG_PRINT((millis() - tStart)/1000); DBG_PRINTLN(" sec"); +} + + + + +// ------------------------ +void ESPWebDAV::handlePut(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing Put"); + + // does URI refer to a directory + if(resource == RESOURCE_DIR) + return handleNotFound(); + + SdFile nFile; + sendHeader("Allow", "PROPFIND,OPTIONS,DELETE,COPY,MOVE,HEAD,POST,PUT,GET"); + + // if file does not exist, create it + if(resource == RESOURCE_NONE) { + if(!nFile.open(uri.c_str(), O_CREAT | O_WRITE)) + return handleWriteError("Unable to create a new file", &nFile); + } + + // file is created/open for writing at this point + DBG_PRINT(uri); DBG_PRINTLN(" - ready for data"); + // did server send any data in put + size_t contentLen = contentLengthHeader.toInt(); + + if(contentLen != 0) { + // buffer size is critical *don't change* + const size_t WRITE_BLOCK_CONST = 512; + uint8_t buf[WRITE_BLOCK_CONST]; + long tStart = millis(); + size_t numRemaining = contentLen; + + // high speed raw write implementation + // close any previous file + nFile.close(); + // delete old file + sd.remove(uri.c_str()); + + // create a contiguous file + size_t contBlocks = (contentLen/WRITE_BLOCK_CONST + 1); + uint32_t bgnBlock, endBlock; + + if (!nFile.createContiguous(sd.vwd(), uri.c_str(), contBlocks * WRITE_BLOCK_CONST)) + return handleWriteError("File create contiguous sections failed", &nFile); + + // get the location of the file's blocks + if (!nFile.contiguousRange(&bgnBlock, &endBlock)) + return handleWriteError("Unable to get contiguous range", &nFile); + + if (!sd.card()->writeStart(bgnBlock, contBlocks)) + return handleWriteError("Unable to start writing contiguous range", &nFile); + + // read data from stream and write to the file + while(numRemaining > 0) { + size_t numToRead = (numRemaining > WRITE_BLOCK_CONST) ? WRITE_BLOCK_CONST : numRemaining; + size_t numRead = readBytesWithTimeout(buf, sizeof(buf), numToRead); + if(numRead == 0) + break; + + // store whole buffer into file regardless of numRead + if (!sd.card()->writeData(buf)) + return handleWriteError("Write data failed", &nFile); + + // reduce the number outstanding + numRemaining -= numRead; + } + + // stop writing operation + if (!sd.card()->writeStop()) + return handleWriteError("Unable to stop writing contiguous range", &nFile); + + // detect timeout condition + if(numRemaining) + return handleWriteError("Timed out waiting for data", &nFile); + + // truncate the file to right length + if(!nFile.truncate(contentLen)) + return handleWriteError("Unable to truncate the file", &nFile); + + DBG_PRINT("File "); DBG_PRINT(contentLen - numRemaining); DBG_PRINT(" bytes stored in: "); DBG_PRINT((millis() - tStart)/1000); DBG_PRINTLN(" sec"); + } + + if(resource == RESOURCE_NONE) + send("201 Created", NULL, ""); + else + send("200 OK", NULL, ""); + + nFile.close(); +} + + + + +// ------------------------ +void ESPWebDAV::handleWriteError(String message, FatFile *wFile) { +// ------------------------ + // close this file + wFile->close(); + // delete the wrile being written + sd.remove(uri.c_str()); + // send error + send("500 Internal Server Error", "text/plain", message); + DBG_PRINTLN(message); +} + + +// ------------------------ +void ESPWebDAV::handleDirectoryCreate(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing MKCOL"); + + // does URI refer to anything + if(resource != RESOURCE_NONE) + return handleNotFound(); + + // create directory + if (!sd.mkdir(uri.c_str(), true)) { + // send error + send("500 Internal Server Error", "text/plain", "Unable to create directory"); + DBG_PRINTLN("Unable to create directory"); + return; + } + + DBG_PRINT(uri); DBG_PRINTLN(" directory created"); + sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); + send("201 Created", NULL, ""); +} + + + +// ------------------------ +void ESPWebDAV::handleMove(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing MOVE"); + + // does URI refer to anything + if(resource == RESOURCE_NONE) + return handleNotFound(); + + if(destinationHeader.length() == 0) + return handleNotFound(); + + String dest = urlToUri(destinationHeader); + + DBG_PRINT("Move destination: "); DBG_PRINTLN(dest); + + // move file or directory + if ( !sd.rename(uri.c_str(), dest.c_str()) ) { + // send error + send("500 Internal Server Error", "text/plain", "Unable to move"); + DBG_PRINTLN("Unable to move file/directory"); + return; + } + + DBG_PRINTLN("Move successful"); + sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); + send("201 Created", NULL, ""); +} + + + + +// ------------------------ +void ESPWebDAV::handleDelete(ResourceType resource) { +// ------------------------ + DBG_PRINTLN("Processing DELETE"); + + // does URI refer to anything + if(resource == RESOURCE_NONE) + return handleNotFound(); + + bool retVal; + + if(resource == RESOURCE_FILE) + // delete a file + retVal = sd.remove(uri.c_str()); + else + // delete a directory + retVal = sd.rmdir(uri.c_str()); + + if(!retVal) { + // send error + send("500 Internal Server Error", "text/plain", "Unable to delete"); + DBG_PRINTLN("Unable to delete file/directory"); + return; + } + + DBG_PRINTLN("Delete successful"); + sendHeader("Allow", "OPTIONS,MKCOL,LOCK,POST,PUT"); + send("200 OK", NULL, ""); +} + +ESPWebDAV dav;