/* Utilities */ //Pretty print for easy debug void prettyPrintRequest(AsyncWebServerRequest * request) { if (request->method() == HTTP_GET) Serial.printf("GET"); else if (request->method() == HTTP_POST) Serial.printf("POST"); else if (request->method() == HTTP_DELETE) Serial.printf("DELETE"); else if (request->method() == HTTP_PUT) Serial.printf("PUT"); else if (request->method() == HTTP_PATCH) Serial.printf("PATCH"); else if (request->method() == HTTP_HEAD) Serial.printf("HEAD"); else if (request->method() == HTTP_OPTIONS) Serial.printf("OPTIONS"); else Serial.printf("UNKNOWN"); Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); if (request->contentLength()) { Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); } int headers = request->headers(); int i; for (i = 0; i < headers; i++) { AsyncWebHeader* h = request->getHeader(i); Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); } int params = request->params(); for (i = 0; i < params; i++) { AsyncWebParameter* p = request->getParam(i); if (p->isFile()) { Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); } else if (p->isPost()) { Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); } else { Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); } } } //Check if a path is dir bool IsDir(const String& path) { File file = SD.open(path); if (file && !file.isDirectory()) { file.close(); return false; } file.close(); return true; } //Generate a random 32 bit hex String GeneratedRandomHex() { String hexString = ""; for (int i = 0; i < 8; i++) { byte randomValue = random(256); // Generate a random byte (0 to 255) hexString += String(randomValue, HEX); // Convert the random byte to its hexadecimal representation } return hexString; } //Get the current unix timestamp unsigned long getTime() { unsigned long now = timeClient.getEpochTime(); return now; } //Convert Unix timestamp to UTC string String getUTCTimeString(time_t unixTimestamp) { char utcString[30]; const char* weekdayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; const char* monthNames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; tm* timeInfo = gmtime(&unixTimestamp); sprintf(utcString, "%s, %02d %s %04d %02d:%02d:%02d GMT", weekdayNames[timeInfo->tm_wday], timeInfo->tm_mday, monthNames[timeInfo->tm_mon], timeInfo->tm_year + 1900, timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec); return String(utcString); } //Get a certain cookie value by its key String GetCookieValueByKey(AsyncWebServerRequest *request, const String& key) { //Check if the Cookie header exists if (!request->hasHeader("Cookie")) { return ""; } //Load the cookie value as string from header String targetValue = ""; AsyncWebHeader* h = request->getHeader("Cookie"); String cookieHeader = String(h->value().c_str()); // Find the start and end positions of target cookie key int startIndex = cookieHeader.indexOf(key + "="); if (startIndex != -1) { startIndex += 9; // Length of "web-auth=" int endIndex = cookieHeader.indexOf(';', startIndex); if (endIndex == -1) { endIndex = cookieHeader.length(); } // Extract the value of the "web-auth" cookie targetValue = cookieHeader.substring(startIndex, endIndex); } return targetValue; } //Get the filename from filepath String basename(const String& filePath) { int lastSlashIndex = filePath.lastIndexOf('/'); // If no slash is found, return the original path if (lastSlashIndex == -1) { return filePath; } // Return the substring after the last slash return filePath.substring(lastSlashIndex + 1); } bool recursiveDirRemove(const String& path) { Serial.println(path); File directory = SD.open(path); if (!directory) { Serial.println("Error opening directory!"); return false; } // Delete all the files in the directory directory.rewindDirectory(); while (true) { File entry = directory.openNextFile(); if (!entry) { // No more files break; } String filename = String(entry.name()); if (entry.isDirectory()) { // Recursively delete the subdirectory recursiveDirRemove(path + filename); } else { // Delete the file entry.close(); Serial.println("Removing " + path + filename); SD.remove(path + filename); } } // Close the directory directory.close(); // Delete the directory itself if (!SD.rmdir(path)) { Serial.println("Error deleting directory!"); return false; } return true; } /* uint32_t totalSize = 0; uint16_t fileCount = 0; uint16_t folderCount = 0; // Call the recursive function to analyze the directory and its contents analyzeDirectory(directoryPath, totalSize, fileCount, folderCount); */ void analyzeDirectory(const String& path, uint32_t& totalSize, uint16_t& fileCount, uint16_t& folderCount) { File directory = SD.open(path); if (!directory) { Serial.println("Error opening directory!"); return; } // Analyze all files and subdirectories in the directory directory.rewindDirectory(); while (true) { File entry = directory.openNextFile(); if (!entry) { // No more files break; } if (entry.isDirectory()) { // Recursively analyze the subdirectory folderCount++; analyzeDirectory(entry.name(), totalSize, fileCount, folderCount); } else { // Update the counters and add file size to total size fileCount++; totalSize += entry.size(); } entry.close(); } // Close the directory directory.close(); } void scanSDCardForKeyword(const String& directoryPath, const String& keyword, int *matchCounter, AsyncResponseStream *response) { File directory = SD.open(directoryPath); if (!directory) { Serial.println("Error opening directory " + directoryPath); return; } // Scan all files and subdirectories in the directory directory.rewindDirectory(); while (true) { File entry = directory.openNextFile(); if (!entry) { // No more files break; } if (entry.isDirectory()) { // Recursively scan the subdirectory scanSDCardForKeyword((directoryPath + entry.name() + "/"), keyword, matchCounter, response); } else { // Check if the filename contains the keyword String filename = basename(entry.name()); if (filename.indexOf(keyword) != -1) { if ((*matchCounter) > 0){ //Append a comma before appending next matching file response->print(","); } //When writing response, trim off the /www root folder name from directoryPath response->print("\"" + directoryPath.substring(4) + entry.name() + "\""); (*matchCounter)++; } } entry.close(); } // Close the directory directory.close(); }