server_file.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  1. /* HTTP File Server Example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include "defines.h"
  8. #include "server_file.h"
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <string>
  12. #include <sys/param.h>
  13. #include <sys/unistd.h>
  14. #include <sys/stat.h>
  15. #include <iostream>
  16. #include <sys/types.h>
  17. #include <dirent.h>
  18. #include "esp_err.h"
  19. #include "esp_log.h"
  20. #include "esp_vfs.h"
  21. #include <esp_spiffs.h>
  22. #include "esp_http_server.h"
  23. #include "ClassLogFile.h"
  24. #include "MainFlowControl.h"
  25. #include "server_help.h"
  26. #include "md5.h"
  27. #include "interface_mqtt.h"
  28. #include "server_GPIO.h"
  29. #include "Helper.h"
  30. #include "miniz.h"
  31. #include "basic_auth.h"
  32. static const char *TAG = "OTA FILE";
  33. using namespace std;
  34. string TEMP_SUFFIX = "_tmp";
  35. static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file);
  36. static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file);
  37. struct file_server_data
  38. {
  39. /* Base path of file storage */
  40. char base_path[ESP_VFS_PATH_MAX + 1];
  41. /* Scratch buffer for temporary storage during file transfer */
  42. char scratch[SERVER_FILER_SCRATCH_BUFSIZE];
  43. };
  44. esp_err_t get_numbers_file_handler(httpd_req_t *req)
  45. {
  46. std::string ret = flowctrl.getNumbersName();
  47. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  48. httpd_resp_set_type(req, "text/plain");
  49. httpd_resp_sendstr_chunk(req, ret.c_str());
  50. httpd_resp_sendstr_chunk(req, NULL);
  51. return ESP_OK;
  52. }
  53. esp_err_t get_data_file_handler(httpd_req_t *req)
  54. {
  55. struct dirent *entry;
  56. std::string _filename, _fileext;
  57. size_t pos = 0;
  58. const char verz_name[] = "/sdcard/log/data";
  59. ESP_LOGD(TAG, "Suche data files in /sdcard/log/data");
  60. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  61. httpd_resp_set_type(req, "text/plain");
  62. DIR *pDir = opendir(verz_name);
  63. while ((entry = readdir(pDir)) != NULL)
  64. {
  65. _filename = std::string(entry->d_name);
  66. ESP_LOGD(TAG, "File: %s", _filename.c_str());
  67. // ignore all files with starting dot (hidden files)
  68. if (_filename.rfind(".", 0) == 0)
  69. {
  70. continue;
  71. }
  72. _fileext = _filename;
  73. pos = _fileext.find_last_of(".");
  74. if (pos != std::string::npos)
  75. {
  76. _fileext = _fileext.erase(0, pos + 1);
  77. }
  78. ESP_LOGD(TAG, " Extension: %s", _fileext.c_str());
  79. if (_fileext == "csv")
  80. {
  81. _filename = _filename + "\t";
  82. httpd_resp_sendstr_chunk(req, _filename.c_str());
  83. }
  84. }
  85. closedir(pDir);
  86. httpd_resp_sendstr_chunk(req, NULL);
  87. return ESP_OK;
  88. }
  89. esp_err_t get_tflite_file_handler(httpd_req_t *req)
  90. {
  91. struct dirent *entry;
  92. std::string _filename, _fileext;
  93. size_t pos = 0;
  94. const char verz_name[] = "/sdcard/config";
  95. ESP_LOGD(TAG, "Suche TFLITE in /sdcard/config/");
  96. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  97. httpd_resp_set_type(req, "text/plain");
  98. DIR *pDir = opendir(verz_name);
  99. while ((entry = readdir(pDir)) != NULL)
  100. {
  101. _filename = std::string(entry->d_name);
  102. ESP_LOGD(TAG, "File: %s", _filename.c_str());
  103. // ignore all files with starting dot (hidden files)
  104. if (_filename.rfind(".", 0) == 0)
  105. {
  106. continue;
  107. }
  108. _fileext = _filename;
  109. pos = _fileext.find_last_of(".");
  110. if (pos != std::string::npos)
  111. {
  112. _fileext = _fileext.erase(0, pos + 1);
  113. }
  114. ESP_LOGD(TAG, " Extension: %s", _fileext.c_str());
  115. if ((_fileext == "tfl") || (_fileext == "tflite"))
  116. {
  117. _filename = "/config/" + _filename + "\t";
  118. httpd_resp_sendstr_chunk(req, _filename.c_str());
  119. }
  120. }
  121. closedir(pDir);
  122. httpd_resp_sendstr_chunk(req, NULL);
  123. return ESP_OK;
  124. }
  125. /* Send HTTP response with a run-time generated html consisting of
  126. * a list of all files and folders under the requested path.
  127. * In case of SPIFFS this returns empty list when path is any
  128. * string other than '/', since SPIFFS doesn't support directories */
  129. static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const char *uripath, bool readonly)
  130. {
  131. char entrypath[FILE_PATH_MAX];
  132. char dirpath_corrected[FILE_PATH_MAX];
  133. strcpy(dirpath_corrected, dirpath);
  134. file_server_data *server_data = (file_server_data *)req->user_ctx;
  135. if ((strlen(dirpath_corrected) - 1) > strlen(server_data->base_path))
  136. {
  137. // if dirpath is not mountpoint, the last "\" needs to be removed
  138. dirpath_corrected[strlen(dirpath_corrected) - 1] = '\0';
  139. }
  140. DIR *pdir = opendir(dirpath_corrected);
  141. const size_t dirpath_len = strlen(dirpath);
  142. ESP_LOGD(TAG, "Dirpath: <%s>, Pathlength: %d", dirpath, dirpath_len);
  143. // Retrieve the base path of file storage to construct the full path
  144. strlcpy(entrypath, dirpath, sizeof(entrypath));
  145. ESP_LOGD(TAG, "entrypath: <%s>", entrypath);
  146. if (!pdir)
  147. {
  148. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + std::string(dirpath) + "!");
  149. // Respond with 404 Not Found
  150. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
  151. return ESP_FAIL;
  152. }
  153. char entrysize[16];
  154. const char *entrytype;
  155. struct dirent *entry;
  156. struct stat entry_stat;
  157. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  158. // Send HTML file header
  159. httpd_resp_sendstr_chunk(req, "<!DOCTYPE html><html lang=\"en\" xml:lang=\"en\"><head>");
  160. httpd_resp_sendstr_chunk(req, "<link type=\"image/png\" href=\"/close.png\" rel=\"icon\">");
  161. httpd_resp_sendstr_chunk(req, "<link href=\"/file_server.css\" rel=\"stylesheet\">");
  162. httpd_resp_sendstr_chunk(req, "<link href=\"/firework.css\" rel=\"stylesheet\">");
  163. httpd_resp_sendstr_chunk(req, "<script type=\"text/javascript\" src=\"/jquery-3.6.0.min.js\"></script>");
  164. httpd_resp_sendstr_chunk(req, "<script type=\"text/javascript\" src=\"/firework.js\"></script></head>");
  165. httpd_resp_sendstr_chunk(req, "<body>");
  166. httpd_resp_sendstr_chunk(req, "<table class=\"fixed\" border=\"0\" width=100% style=\"font-family: arial\">");
  167. httpd_resp_sendstr_chunk(req, "<tr><td style=\"vertical-align: top;width: 300px;\"><h2>Fileserver</h2></td>"
  168. "<td rowspan=\"2\"><table border=\"0\" style=\"width:100%\"><tr><td style=\"width:80px\">"
  169. "<label for=\"newfile\">Source</label></td><td colspan=\"2\">"
  170. "<input id=\"newfile\" type=\"file\" onchange=\"setpath()\" style=\"width:100%;\"></td></tr>"
  171. "<tr><td><label for=\"filepath\">Destination</label></td><td>"
  172. "<input id=\"filepath\" type=\"text\" style=\"width:94%;\"></td><td>"
  173. "<button id=\"upload\" type=\"button\" class=\"button\" onclick=\"upload()\">Upload</button></td></tr>"
  174. "</table></td></tr><tr></tr><tr><td colspan=\"2\">"
  175. "<button style=\"font-size:16px; padding: 5px 10px\" id=\"dirup\" type=\"button\" onclick=\"dirup()\""
  176. "disabled>&#129145; Directory up</button><span style=\"padding-left:15px\" id=\"currentpath\">"
  177. "</span></td></tr>");
  178. httpd_resp_sendstr_chunk(req, "</table>");
  179. httpd_resp_sendstr_chunk(req, "<script type=\"text/javascript\" src=\"/file_server.js\"></script>");
  180. httpd_resp_sendstr_chunk(req, "<script type=\"text/javascript\">initFileServer();</script>");
  181. std::string temp_string = std::string(dirpath);
  182. temp_string = temp_string.substr(8, temp_string.length() - 8);
  183. temp_string = "/delete/" + temp_string + "?task=deldircontent";
  184. // Send file-list table definition and column labels
  185. httpd_resp_sendstr_chunk(req, "<table id=\"files_table\">"
  186. "<col width=\"800px\"><col width=\"300px\"><col width=\"300px\"><col width=\"100px\">"
  187. "<thead><tr><th>Name</th><th>Type</th><th>Size</th>");
  188. if (!readonly)
  189. {
  190. httpd_resp_sendstr_chunk(req, "<th><form method=\"post\" action=\"");
  191. httpd_resp_sendstr_chunk(req, temp_string.c_str());
  192. httpd_resp_sendstr_chunk(req, "\"><button type=\"submit\">DELETE ALL!</button></form></th></tr>");
  193. }
  194. httpd_resp_sendstr_chunk(req, "</thead><tbody>\n");
  195. // Iterate over all files / folders and fetch their names and sizes
  196. while ((entry = readdir(pdir)) != NULL)
  197. {
  198. // wlan.ini soll nicht angezeigt werden!
  199. if ((strcmp("wlan.ini", entry->d_name) != 0) || (strcmp("network.ini", entry->d_name) != 0))
  200. {
  201. entrytype = (entry->d_type == DT_DIR ? "directory" : "file");
  202. strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
  203. ESP_LOGD(TAG, "Entrypath: %s", entrypath);
  204. if (stat(entrypath, &entry_stat) == -1)
  205. {
  206. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat " + std::string(entrytype) + ": " + std::string(entry->d_name));
  207. continue;
  208. }
  209. if (entry->d_type == DT_DIR)
  210. {
  211. strcpy(entrysize, "-\0");
  212. }
  213. else
  214. {
  215. if (entry_stat.st_size >= 1024)
  216. {
  217. sprintf(entrysize, "%ld KiB", entry_stat.st_size / 1024); // kBytes
  218. }
  219. else
  220. {
  221. sprintf(entrysize, "%ld B", entry_stat.st_size); // Bytes
  222. }
  223. }
  224. ESP_LOGD(TAG, "Found %s: %s (%s bytes)", entrytype, entry->d_name, entrysize);
  225. // Send chunk of HTML file containing table entries with file name and size
  226. httpd_resp_sendstr_chunk(req, "<tr><td><a href=\"");
  227. httpd_resp_sendstr_chunk(req, "/fileserver");
  228. httpd_resp_sendstr_chunk(req, uripath);
  229. httpd_resp_sendstr_chunk(req, entry->d_name);
  230. if (entry->d_type == DT_DIR)
  231. {
  232. httpd_resp_sendstr_chunk(req, "/");
  233. }
  234. httpd_resp_sendstr_chunk(req, "\">");
  235. httpd_resp_sendstr_chunk(req, entry->d_name);
  236. httpd_resp_sendstr_chunk(req, "</a></td><td>");
  237. httpd_resp_sendstr_chunk(req, entrytype);
  238. httpd_resp_sendstr_chunk(req, "</td><td>");
  239. httpd_resp_sendstr_chunk(req, entrysize);
  240. if (!readonly)
  241. {
  242. httpd_resp_sendstr_chunk(req, "</td><td>");
  243. httpd_resp_sendstr_chunk(req, "<form method=\"post\" action=\"/delete");
  244. httpd_resp_sendstr_chunk(req, uripath);
  245. httpd_resp_sendstr_chunk(req, entry->d_name);
  246. httpd_resp_sendstr_chunk(req, "\"><button type=\"submit\">Delete</button></form>");
  247. }
  248. httpd_resp_sendstr_chunk(req, "</td></tr>\n");
  249. }
  250. }
  251. closedir(pdir);
  252. // Finish the file list table
  253. httpd_resp_sendstr_chunk(req, "</tbody></table>");
  254. // Send remaining chunk of HTML file to complete it
  255. httpd_resp_sendstr_chunk(req, "</body></html>");
  256. // Send empty chunk to signal HTTP response completion
  257. httpd_resp_sendstr_chunk(req, NULL);
  258. return ESP_OK;
  259. }
  260. static esp_err_t logfileact_get_full_handler(httpd_req_t *req)
  261. {
  262. return send_logfile(req, true);
  263. }
  264. static esp_err_t logfileact_get_last_part_handler(httpd_req_t *req)
  265. {
  266. return send_logfile(req, false);
  267. }
  268. static esp_err_t datafileact_get_full_handler(httpd_req_t *req)
  269. {
  270. return send_datafile(req, true);
  271. }
  272. static esp_err_t datafileact_get_last_part_handler(httpd_req_t *req)
  273. {
  274. return send_datafile(req, false);
  275. }
  276. static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
  277. {
  278. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "data_get_last_part_handler");
  279. ESP_LOGD(TAG, "uri: %s", req->uri);
  280. std::string currentfilename = LogFile.GetCurrentFileNameData();
  281. ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, currentfilename.c_str(), currentfilename.c_str());
  282. FILE *pFile = fopen(currentfilename.c_str(), "r");
  283. if (!pFile)
  284. {
  285. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
  286. /* Respond with 404 Error */
  287. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
  288. return ESP_FAIL;
  289. }
  290. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  291. set_content_type_from_file(req, currentfilename.c_str());
  292. if (!send_full_file)
  293. {
  294. // Send only last part of file
  295. ESP_LOGD(TAG, "Sending last %d bytes of the actual datafile!", LOGFILE_LAST_PART_BYTES);
  296. /* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
  297. if (fseek(pFile, 0, SEEK_END))
  298. {
  299. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
  300. return ESP_FAIL;
  301. }
  302. else
  303. {
  304. long pos = ftell(pFile); // Number of bytes in the file
  305. ESP_LOGI(TAG, "File contains %ld bytes", pos);
  306. if (fseek(pFile, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET))
  307. {
  308. // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
  309. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
  310. return ESP_FAIL;
  311. }
  312. }
  313. /* Find end of line */
  314. while (1)
  315. {
  316. if (fgetc(pFile) == '\n')
  317. {
  318. break;
  319. }
  320. }
  321. }
  322. /* Retrieve the pointer to scratch buffer for temporary storage */
  323. char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
  324. size_t chunksize;
  325. do
  326. {
  327. /* Read file in chunks into the scratch buffer */
  328. chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, pFile);
  329. /* Send the buffer contents as HTTP response chunk */
  330. if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
  331. {
  332. fclose(pFile);
  333. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
  334. /* Abort sending file */
  335. httpd_resp_sendstr_chunk(req, NULL);
  336. /* Respond with 500 Internal Server Error */
  337. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
  338. return ESP_FAIL;
  339. }
  340. /* Keep looping till the whole file is sent */
  341. } while (chunksize != 0);
  342. /* Close file after sending complete */
  343. fclose(pFile);
  344. ESP_LOGI(TAG, "File sending complete");
  345. /* Respond with an empty chunk to signal HTTP response completion */
  346. httpd_resp_send_chunk(req, NULL, 0);
  347. return ESP_OK;
  348. }
  349. static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
  350. {
  351. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "log_get_last_part_handler");
  352. ESP_LOGI(TAG, "uri: %s", req->uri);
  353. const char *filename = "";
  354. std::string currentfilename = LogFile.GetCurrentFileName();
  355. ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, filename, currentfilename.c_str());
  356. // Since the log file is still could open for writing, we need to close it first
  357. LogFile.CloseLogFileAppendHandle();
  358. FILE *pFile = fopen(currentfilename.c_str(), "r");
  359. if (!pFile)
  360. {
  361. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
  362. /* Respond with 404 Error */
  363. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
  364. return ESP_FAIL;
  365. }
  366. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  367. set_content_type_from_file(req, filename);
  368. if (!send_full_file)
  369. {
  370. // Send only last part of file
  371. ESP_LOGD(TAG, "Sending last %d bytes of the actual logfile!", LOGFILE_LAST_PART_BYTES);
  372. /* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
  373. if (fseek(pFile, 0, SEEK_END))
  374. {
  375. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
  376. return ESP_FAIL;
  377. }
  378. else
  379. {
  380. long pos = ftell(pFile); // Number of bytes in the file
  381. ESP_LOGI(TAG, "File contains %ld bytes", pos);
  382. if (fseek(pFile, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET))
  383. {
  384. // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
  385. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
  386. return ESP_FAIL;
  387. }
  388. }
  389. /* Find end of line */
  390. while (1)
  391. {
  392. if (fgetc(pFile) == '\n')
  393. {
  394. break;
  395. }
  396. }
  397. }
  398. /* Retrieve the pointer to scratch buffer for temporary storage */
  399. char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
  400. size_t chunksize;
  401. do
  402. {
  403. /* Read file in chunks into the scratch buffer */
  404. chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, pFile);
  405. /* Send the buffer contents as HTTP response chunk */
  406. if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
  407. {
  408. fclose(pFile);
  409. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
  410. /* Abort sending file */
  411. httpd_resp_sendstr_chunk(req, NULL);
  412. /* Respond with 500 Internal Server Error */
  413. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
  414. return ESP_FAIL;
  415. }
  416. /* Keep looping till the whole file is sent */
  417. } while (chunksize != 0);
  418. /* Close file after sending complete */
  419. fclose(pFile);
  420. ESP_LOGD(TAG, "File sending complete");
  421. /* Respond with an empty chunk to signal HTTP response completion */
  422. httpd_resp_send_chunk(req, NULL, 0);
  423. return ESP_OK;
  424. }
  425. /* Handler to download a file kept on the server */
  426. static esp_err_t download_get_handler(httpd_req_t *req)
  427. {
  428. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "download_get_handler");
  429. char filepath[FILE_PATH_MAX];
  430. struct stat file_stat;
  431. ESP_LOGD(TAG, "uri: %s", req->uri);
  432. const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri + sizeof("/fileserver") - 1, sizeof(filepath));
  433. ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath);
  434. if (!filename)
  435. {
  436. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Filename is too long");
  437. /* Respond with 414 Error */
  438. httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
  439. return ESP_FAIL;
  440. }
  441. /* If name has trailing '/', respond with directory contents */
  442. if (filename[strlen(filename) - 1] == '/')
  443. {
  444. bool readonly = false;
  445. size_t buf_len = httpd_req_get_url_query_len(req) + 1;
  446. if (buf_len > 1)
  447. {
  448. char buf[buf_len];
  449. if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK)
  450. {
  451. ESP_LOGI(TAG, "Found URL query => %s", buf);
  452. char param[32];
  453. /* Get value of expected key from query string */
  454. if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK)
  455. {
  456. ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
  457. readonly = (strcmp(param, "true") == 0);
  458. }
  459. }
  460. }
  461. ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath);
  462. return http_resp_dir_html(req, filepath, filename, readonly);
  463. }
  464. std::string testwlan = to_upper(std::string(filename));
  465. if ((stat(filepath, &file_stat) == -1) || (testwlan.compare("/WLAN.INI") == 0) || (testwlan.compare("/NETWORK.INI") == 0))
  466. {
  467. // wlan.ini soll nicht angezeigt werden!
  468. /* If file not present on SPIFFS check if URI
  469. * corresponds to one of the hardcoded paths */
  470. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat file: " + std::string(filepath) + "!");
  471. /* Respond with 404 Not Found */
  472. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
  473. return ESP_FAIL;
  474. }
  475. FILE *pFile = fopen(filepath, "r");
  476. if (!pFile)
  477. {
  478. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) + "!");
  479. /* Respond with 404 Error */
  480. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
  481. return ESP_FAIL;
  482. }
  483. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  484. ESP_LOGD(TAG, "Sending file: %s (%ld bytes)...", filename, file_stat.st_size);
  485. set_content_type_from_file(req, filename);
  486. /* Retrieve the pointer to scratch buffer for temporary storage */
  487. char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
  488. size_t chunksize;
  489. do
  490. {
  491. /* Read file in chunks into the scratch buffer */
  492. chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, pFile);
  493. /* Send buffer contents as HTTP chunk. If empty this functions as a
  494. * last-chunk message, signaling end-of-response, to the HTTP client.
  495. * See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */
  496. if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
  497. {
  498. fclose(pFile);
  499. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
  500. /* Abort sending file */
  501. httpd_resp_sendstr_chunk(req, NULL);
  502. /* Respond with 500 Internal Server Error */
  503. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file!");
  504. return ESP_FAIL;
  505. }
  506. /* Keep looping till the whole file is sent */
  507. } while (chunksize != 0);
  508. /* Close file after sending complete */
  509. fclose(pFile);
  510. ESP_LOGD(TAG, "File successfully sent");
  511. return ESP_OK;
  512. }
  513. /* Handler to upload a file onto the server */
  514. static esp_err_t upload_post_handler(httpd_req_t *req)
  515. {
  516. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "upload_post_handler");
  517. char filepath[FILE_PATH_MAX];
  518. ESP_LOGI(TAG, "uri: %s", req->uri);
  519. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  520. /* Skip leading "/upload" from URI to get filename */
  521. /* Note sizeof() counts NULL termination hence the -1 */
  522. const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri + sizeof("/upload") - 1, sizeof(filepath));
  523. if (!filename)
  524. {
  525. /* Respond with 413 Error */
  526. httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
  527. return ESP_FAIL;
  528. }
  529. /* Filename cannot have a trailing '/' */
  530. if (filename[strlen(filename) - 1] == '/')
  531. {
  532. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
  533. /* Respond with 400 Bad Request */
  534. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
  535. return ESP_FAIL;
  536. }
  537. struct stat file_stat;
  538. if (stat(filepath, &file_stat) == 0)
  539. {
  540. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File already exists: " + string(filepath));
  541. /* Respond with 400 Bad Request */
  542. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
  543. return ESP_FAIL;
  544. }
  545. /* File cannot be larger than a limit */
  546. if (req->content_len > MAX_FILE_SIZE)
  547. {
  548. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File too large: " + to_string(req->content_len) + " bytes");
  549. /* Respond with 400 Bad Request */
  550. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File size must be less than " MAX_FILE_SIZE_STR "!");
  551. /* Return failure to close underlying connection else the
  552. * incoming file content will keep the socket busy */
  553. return ESP_FAIL;
  554. }
  555. FILE *pFile = fopen(filepath, "w");
  556. if (!pFile)
  557. {
  558. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create file: " + string(filepath));
  559. /* Respond with 500 Internal Server Error */
  560. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
  561. return ESP_FAIL;
  562. }
  563. ESP_LOGI(TAG, "Receiving file: %s...", filename);
  564. /* Retrieve the pointer to scratch buffer for temporary storage */
  565. char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
  566. int received;
  567. /* Content length of the request gives
  568. * the size of the file being uploaded */
  569. int remaining = req->content_len;
  570. while (remaining > 0)
  571. {
  572. ESP_LOGI(TAG, "Remaining size: %d", remaining);
  573. /* Receive the file part by part into a buffer */
  574. if ((received = httpd_req_recv(req, buf, MIN(remaining, SERVER_FILER_SCRATCH_BUFSIZE))) <= 0)
  575. {
  576. if (received == HTTPD_SOCK_ERR_TIMEOUT)
  577. {
  578. /* Retry if timeout occurred */
  579. continue;
  580. }
  581. /* In case of unrecoverable error,
  582. * close and delete the unfinished file*/
  583. fclose(pFile);
  584. unlink(filepath);
  585. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File reception failed!");
  586. /* Respond with 500 Internal Server Error */
  587. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
  588. return ESP_FAIL;
  589. }
  590. /* Write buffer content to file on storage */
  591. if (received && (received != fwrite(buf, 1, received, pFile)))
  592. {
  593. /* Couldn't write everything to file!
  594. * Storage may be full? */
  595. fclose(pFile);
  596. unlink(filepath);
  597. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File write failed!");
  598. /* Respond with 500 Internal Server Error */
  599. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
  600. return ESP_FAIL;
  601. }
  602. /* Keep track of remaining size of
  603. * the file left to be uploaded */
  604. remaining -= received;
  605. }
  606. /* Close file upon upload completion */
  607. fclose(pFile);
  608. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File saved: " + string(filename));
  609. ESP_LOGI(TAG, "File reception completed");
  610. string s = req->uri;
  611. if (is_in_string(s, "?md5"))
  612. {
  613. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Calculate and return MD5 sum...");
  614. pFile = fopen(filepath, "r");
  615. if (!pFile)
  616. {
  617. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to open file for reading: " + string(filepath));
  618. /* Respond with 500 Internal Server Error */
  619. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file for reading");
  620. return ESP_FAIL;
  621. }
  622. uint8_t result[16];
  623. string md5hex = "";
  624. string response = "{\"md5\":";
  625. char hex[3];
  626. md5File(pFile, result);
  627. fclose(pFile);
  628. for (int i = 0; i < sizeof(result); i++)
  629. {
  630. snprintf(hex, sizeof(hex), "%02x", result[i]);
  631. md5hex.append(hex);
  632. }
  633. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "MD5 of " + string(filepath) + ": " + md5hex);
  634. response.append("\"" + md5hex + "\"");
  635. response.append("}");
  636. httpd_resp_sendstr(req, response.c_str());
  637. }
  638. else
  639. {
  640. // Return file server page
  641. std::string directory = std::string(filepath);
  642. size_t temp_size = directory.find("/");
  643. size_t found = temp_size;
  644. while (temp_size != std::string::npos)
  645. {
  646. temp_size = directory.find("/", found + 1);
  647. if (temp_size != std::string::npos)
  648. {
  649. found = temp_size;
  650. }
  651. }
  652. int start_filename = strlen(((struct file_server_data *)req->user_ctx)->base_path);
  653. ESP_LOGD(TAG, "Directory: %s, start_filename: %d, found: %d", directory.c_str(), start_filename, found);
  654. directory = directory.substr(start_filename, found - start_filename + 1);
  655. directory = "/fileserver" + directory;
  656. /* Redirect onto root to see the updated file list */
  657. if (strcmp(filename, "/config/config.ini") == 0 ||
  658. strcmp(filename, "/config/ref0.jpg") == 0 ||
  659. strcmp(filename, "/config/ref0_org.jpg") == 0 ||
  660. strcmp(filename, "/config/ref1.jpg") == 0 ||
  661. strcmp(filename, "/config/ref1_org.jpg") == 0 ||
  662. strcmp(filename, "/config/reference.jpg") == 0 ||
  663. strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
  664. strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
  665. strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
  666. strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
  667. strcmp(filename, "/img_tmp/reference.jpg") == 0)
  668. {
  669. httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
  670. }
  671. else
  672. {
  673. httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
  674. }
  675. httpd_resp_set_hdr(req, "Location", directory.c_str());
  676. httpd_resp_sendstr(req, "File uploaded successfully");
  677. }
  678. return ESP_OK;
  679. }
  680. /* Handler to delete a file from the server */
  681. static esp_err_t delete_post_handler(httpd_req_t *req)
  682. {
  683. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "delete_post_handler");
  684. char filepath[FILE_PATH_MAX];
  685. struct stat file_stat;
  686. //////////////////////////////////////////////////////////////
  687. char _query[200];
  688. char _valuechar[30];
  689. std::string _task;
  690. std::string directory;
  691. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  692. if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK)
  693. {
  694. ESP_LOGD(TAG, "Query: %s", _query);
  695. if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
  696. {
  697. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "task is found: " + string(_valuechar));
  698. _task = std::string(_valuechar);
  699. }
  700. }
  701. if (_task.compare("deldircontent") == 0)
  702. {
  703. /* Skip leading "/delete" from URI to get filename */
  704. /* Note sizeof() counts NULL termination hence the -1 */
  705. const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri + sizeof("/delete") - 1, sizeof(filepath));
  706. if (!filename)
  707. {
  708. /* Respond with 414 Error */
  709. httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
  710. return ESP_FAIL;
  711. }
  712. std::string temp_filename = std::string(filename);
  713. temp_filename = temp_filename.substr(0, temp_filename.length() - 1);
  714. directory = "/fileserver" + temp_filename + "/";
  715. temp_filename = "/sdcard" + temp_filename;
  716. ESP_LOGD(TAG, "Directory to delete: %s", temp_filename.c_str());
  717. delete_all_in_directory(temp_filename);
  718. ESP_LOGD(TAG, "Location after delete directory content: %s", directory.c_str());
  719. }
  720. else
  721. {
  722. /* Skip leading "/delete" from URI to get filename */
  723. /* Note sizeof() counts NULL termination hence the -1 */
  724. const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri + sizeof("/delete") - 1, sizeof(filepath));
  725. if (!filename)
  726. {
  727. /* Respond with 500 Internal Server Error */
  728. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
  729. return ESP_FAIL;
  730. }
  731. /* Filename cannot have a trailing '/' */
  732. if (filename[strlen(filename) - 1] == '/')
  733. {
  734. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
  735. /* Respond with 400 Bad Request */
  736. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
  737. return ESP_FAIL;
  738. }
  739. if ((strcmp(filename, "wlan.ini") == 0) || (strcmp(filename, "network.ini") == 0))
  740. {
  741. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to delete protected file : " + string(filename));
  742. httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Not allowed to delete wlan.ini");
  743. return ESP_FAIL;
  744. }
  745. if (stat(filepath, &file_stat) == -1)
  746. {
  747. // File does not exist
  748. /* This is ok, we would delete it anyway */
  749. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File does not exist: " + string(filename));
  750. }
  751. /* Delete file */
  752. unlink(filepath);
  753. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File deleted: " + string(filename));
  754. ESP_LOGI(TAG, "File deletion completed");
  755. directory = std::string(filepath);
  756. size_t temp_size = directory.find("/");
  757. size_t found = temp_size;
  758. while (temp_size != std::string::npos)
  759. {
  760. temp_size = directory.find("/", found + 1);
  761. if (temp_size != std::string::npos)
  762. {
  763. found = temp_size;
  764. }
  765. }
  766. int start_filename = strlen(((struct file_server_data *)req->user_ctx)->base_path);
  767. ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_filename, found);
  768. directory = directory.substr(start_filename, found - start_filename + 1);
  769. directory = "/fileserver" + directory;
  770. ESP_LOGD(TAG, "Directory danach 4: %s", directory.c_str());
  771. //////////////////////////////////////////////////////////////
  772. /* Redirect onto root to see the updated file list */
  773. if (strcmp(filename, "/config/config.ini") == 0 ||
  774. strcmp(filename, "/config/ref0.jpg") == 0 ||
  775. strcmp(filename, "/config/ref0_org.jpg") == 0 ||
  776. strcmp(filename, "/config/ref1.jpg") == 0 ||
  777. strcmp(filename, "/config/ref1_org.jpg") == 0 ||
  778. strcmp(filename, "/config/reference.jpg") == 0 ||
  779. strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
  780. strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
  781. strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
  782. strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
  783. strcmp(filename, "/img_tmp/reference.jpg") == 0)
  784. {
  785. httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
  786. }
  787. else
  788. {
  789. httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
  790. }
  791. }
  792. httpd_resp_set_hdr(req, "Location", directory.c_str());
  793. httpd_resp_sendstr(req, "File successfully deleted");
  794. return ESP_OK;
  795. }
  796. void delete_all_in_directory(std::string _directory)
  797. {
  798. DIR *pDir = opendir(_directory.c_str());
  799. if (!pDir)
  800. {
  801. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + _directory);
  802. return;
  803. }
  804. struct dirent *entry;
  805. std::string filename;
  806. /* Iterate over all files / folders and fetch their names and sizes */
  807. while ((entry = readdir(pDir)) != NULL)
  808. {
  809. if (!(entry->d_type == DT_DIR))
  810. {
  811. if ((strcmp("wlan.ini", entry->d_name) != 0) || (strcmp("network.ini", entry->d_name) != 0))
  812. {
  813. // auf wlan.ini soll nicht zugegriffen werden !!!
  814. filename = _directory + "/" + std::string(entry->d_name);
  815. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + filename);
  816. /* Delete file */
  817. unlink(filename.c_str());
  818. }
  819. };
  820. }
  821. closedir(pDir);
  822. }
  823. std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::string _html_final, std::string _target_bin, std::string _main, bool _initial_setup)
  824. {
  825. ESP_LOGD(TAG, "minizip.c version: %s", MZ_VERSION);
  826. ESP_LOGD(TAG, "Zipfile: %s", _in_zip_file.c_str());
  827. std::string ret = "";
  828. // Now try to open the archive.
  829. mz_zip_archive zip_archive;
  830. memset(&zip_archive, 0, sizeof(zip_archive));
  831. mz_bool status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
  832. if (!status)
  833. {
  834. ESP_LOGD(TAG, "mzip_zip_reader_init_file() failed!");
  835. return ret;
  836. }
  837. // Get and print information about each file in the archive.
  838. int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
  839. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files to be extracted: " + to_string(numberoffiles));
  840. int sort_iter = 0;
  841. memset(&zip_archive, 0, sizeof(zip_archive));
  842. status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
  843. if (!status)
  844. {
  845. ESP_LOGD(TAG, "mzip_zip_reader_init_file() failed!");
  846. return ret;
  847. }
  848. void *pZip;
  849. size_t uncomp_size;
  850. char archive_filename[64];
  851. std::string directory = "";
  852. std::string temp_string = "";
  853. for (int i = 0; i < numberoffiles; i++)
  854. {
  855. mz_zip_archive_file_stat file_stat;
  856. mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
  857. sprintf(archive_filename, file_stat.m_filename);
  858. if (!file_stat.m_is_directory)
  859. {
  860. // Try to extract all the files to the heap.
  861. pZip = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
  862. if (!pZip)
  863. {
  864. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_extract_file_to_heap() failed on file " + string(archive_filename));
  865. mz_zip_reader_end(&zip_archive);
  866. return ret;
  867. }
  868. // Save to File.
  869. temp_string = std::string(archive_filename);
  870. ESP_LOGD(TAG, "Rohfilename: %s", temp_string.c_str());
  871. if (to_upper(temp_string) == "FIRMWARE.BIN")
  872. {
  873. temp_string = _target_bin + temp_string;
  874. ret = temp_string;
  875. }
  876. else
  877. {
  878. std::string _dir = get_directory(temp_string);
  879. if ((_dir == "config-initial") && !_initial_setup)
  880. {
  881. continue;
  882. }
  883. else
  884. {
  885. _dir = "config";
  886. std::string _s1 = "config-initial";
  887. find_replace(temp_string, _s1, _dir);
  888. }
  889. if (_dir.length() > 0)
  890. {
  891. temp_string = _main + temp_string;
  892. }
  893. else
  894. {
  895. temp_string = _html_tmp + temp_string;
  896. }
  897. }
  898. // files in the html folder shall be redirected to the temporary html folder
  899. if (temp_string.find(_html_final) == 0)
  900. {
  901. find_replace(temp_string, _html_final, _html_tmp);
  902. }
  903. string temp_filename = temp_string + TEMP_SUFFIX;
  904. ESP_LOGI(TAG, "File to extract: %s, Temp. Filename: %s", temp_string.c_str(), temp_filename.c_str());
  905. std::string folder = temp_filename.substr(0, temp_filename.find_last_of('/'));
  906. make_dir(folder);
  907. // extrahieren in zwischendatei
  908. delete_file(temp_filename);
  909. FILE *fpTargetFile = fopen(temp_filename.c_str(), "wb");
  910. uint writtenbytes = fwrite(pZip, 1, (uint)uncomp_size, fpTargetFile);
  911. fclose(fpTargetFile);
  912. bool isokay = true;
  913. if (writtenbytes == (uint)uncomp_size)
  914. {
  915. isokay = true;
  916. }
  917. else
  918. {
  919. isokay = false;
  920. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in writting extracted file (function fwrite) extracted file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
  921. }
  922. delete_file(temp_string);
  923. if (!isokay)
  924. {
  925. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in fwrite \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
  926. }
  927. isokay = isokay && rename_file(temp_filename, temp_string);
  928. if (!isokay)
  929. {
  930. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in Rename \"" + temp_filename + "\" to \"" + temp_string);
  931. }
  932. if (isokay)
  933. {
  934. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully extracted file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
  935. }
  936. else
  937. {
  938. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in extracting file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
  939. ret = "ERROR";
  940. }
  941. mz_free(pZip);
  942. }
  943. }
  944. // Close the archive, freeing any resources it was using
  945. mz_zip_reader_end(&zip_archive);
  946. ESP_LOGD(TAG, "Success.");
  947. return ret;
  948. }
  949. void unzip(std::string _in_zip_file, std::string _target_directory)
  950. {
  951. ESP_LOGD(TAG, "minizip.c version: %s", MZ_VERSION);
  952. ESP_LOGD(TAG, "Zipfile: %s", _in_zip_file.c_str());
  953. ESP_LOGD(TAG, "Target Dir: %s", _target_directory.c_str());
  954. // Now try to open the archive.
  955. mz_zip_archive zip_archive;
  956. memset(&zip_archive, 0, sizeof(zip_archive));
  957. mz_bool status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
  958. if (!status)
  959. {
  960. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_init_file() failed!");
  961. return;
  962. }
  963. // Get and print information about each file in the archive.
  964. int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
  965. void *pZip;
  966. size_t uncomp_size;
  967. char archive_filename[64];
  968. std::string temp_string;
  969. for (int sort_iter = 0; sort_iter < 2; sort_iter++)
  970. {
  971. memset(&zip_archive, 0, sizeof(zip_archive));
  972. status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
  973. if (!status)
  974. {
  975. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_init_file() failed!");
  976. return;
  977. }
  978. for (int i = 0; i < numberoffiles; i++)
  979. {
  980. mz_zip_archive_file_stat file_stat;
  981. mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
  982. sprintf(archive_filename, file_stat.m_filename);
  983. // Try to extract all the files to the heap.
  984. pZip = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
  985. if (!pZip)
  986. {
  987. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_extract_file_to_heap() failed!");
  988. mz_zip_reader_end(&zip_archive);
  989. return;
  990. }
  991. // Save to File.
  992. temp_string = std::string(archive_filename);
  993. temp_string = _target_directory + temp_string;
  994. ESP_LOGD(TAG, "File to extract: %s", temp_string.c_str());
  995. FILE *fpTargetFile = fopen(temp_string.c_str(), "wb");
  996. fwrite(pZip, 1, (uint)uncomp_size, fpTargetFile);
  997. fclose(fpTargetFile);
  998. ESP_LOGD(TAG, "Successfully extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);
  999. // We're done.
  1000. mz_free(pZip);
  1001. }
  1002. // Close the archive, freeing any resources it was using
  1003. mz_zip_reader_end(&zip_archive);
  1004. }
  1005. ESP_LOGD(TAG, "Success.");
  1006. }
  1007. void file_server_register_uri(httpd_handle_t server, const char *base_path)
  1008. {
  1009. static struct file_server_data *server_data = NULL;
  1010. /* Validate file storage base path */
  1011. if (!base_path)
  1012. {
  1013. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server base_path not set");
  1014. }
  1015. if (server_data)
  1016. {
  1017. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server already started");
  1018. }
  1019. /* Allocate memory for server data */
  1020. server_data = (file_server_data *)calloc(1, sizeof(struct file_server_data));
  1021. if (!server_data)
  1022. {
  1023. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate memory for server data");
  1024. }
  1025. strlcpy(server_data->base_path, base_path, sizeof(server_data->base_path));
  1026. /* URI handler for getting uploaded files */
  1027. httpd_uri_t file_download = {
  1028. .uri = "/fileserver*",
  1029. .method = HTTP_GET,
  1030. .handler = APPLY_BASIC_AUTH_FILTER(download_get_handler),
  1031. .user_ctx = server_data,
  1032. };
  1033. httpd_register_uri_handler(server, &file_download);
  1034. httpd_uri_t file_datafileact = {
  1035. .uri = "/datafileact",
  1036. .method = HTTP_GET,
  1037. .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_full_handler),
  1038. .user_ctx = server_data,
  1039. };
  1040. httpd_register_uri_handler(server, &file_datafileact);
  1041. httpd_uri_t file_datafile_last_part_handle = {
  1042. .uri = "/data",
  1043. .method = HTTP_GET,
  1044. .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_last_part_handler),
  1045. .user_ctx = server_data,
  1046. };
  1047. httpd_register_uri_handler(server, &file_datafile_last_part_handle);
  1048. httpd_uri_t file_logfileact = {
  1049. .uri = "/logfileact",
  1050. .method = HTTP_GET,
  1051. .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_full_handler),
  1052. .user_ctx = server_data,
  1053. };
  1054. httpd_register_uri_handler(server, &file_logfileact);
  1055. httpd_uri_t file_logfile_last_part_handle = {
  1056. .uri = "/log",
  1057. .method = HTTP_GET,
  1058. .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_last_part_handler),
  1059. .user_ctx = server_data,
  1060. };
  1061. httpd_register_uri_handler(server, &file_logfile_last_part_handle);
  1062. /* URI handler for uploading files to server */
  1063. httpd_uri_t file_upload = {
  1064. .uri = "/upload/*",
  1065. .method = HTTP_POST,
  1066. .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handler),
  1067. .user_ctx = server_data,
  1068. };
  1069. httpd_register_uri_handler(server, &file_upload);
  1070. /* URI handler for deleting files from server */
  1071. httpd_uri_t file_delete = {
  1072. .uri = "/delete/*",
  1073. .method = HTTP_POST,
  1074. .handler = APPLY_BASIC_AUTH_FILTER(delete_post_handler),
  1075. .user_ctx = server_data,
  1076. };
  1077. httpd_register_uri_handler(server, &file_delete);
  1078. }