1
0

webserv.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. // Webserver
  2. // uses multiple sockets, except for socket 7 which is reserved by netHID
  3. #define word char
  4. #include "lib/math.c"
  5. #include "lib/stdlib.c"
  6. #include "lib/sys.c"
  7. #include "lib/fs.c"
  8. #include "lib/wiz5500.c"
  9. #define TMPMEM_LOCATION 0x440000
  10. #define FILE_BUFFER_LOCATION 0x430000
  11. #define FILE_BUFFER_SIZE 8192 // buffer size for reading files from USB storage
  12. char* fileBuffer = (char *) FILE_BUFFER_LOCATION; //fileBuffer[FILE_BUFFER_SIZE];
  13. char WIZrbuf[WIZNET_MAX_RBUF];
  14. //-------------------
  15. //LIST DIR CUSTOM FUNCTIONS
  16. //-------------------
  17. // Parses and writes name.extension and filesize on one line
  18. void wiz_parseFATdata(word datalen, char* fatBuffer, char* b, word* bufLen)
  19. {
  20. if (datalen != 32)
  21. {
  22. BDOS_PrintConsole("Unexpected FAT table length\n");
  23. return;
  24. }
  25. // start HTML link tag
  26. word catLen = strcpy(b + *bufLen, "<tr><td style=\"padding:0 15px 0 15px;\"><a href=\"");
  27. (*bufLen) += catLen;
  28. // parse filename
  29. word printLen = FS_parseFATstring(fatBuffer, 8, b, bufLen);
  30. // add '.' and parse extension
  31. if (fatBuffer[8] != ' ' || fatBuffer[9] != ' ' || fatBuffer[10] != ' ')
  32. {
  33. b[*bufLen] = '.';
  34. (*bufLen)++;
  35. printLen += FS_parseFATstring(fatBuffer+8, 3, b, bufLen) + 1;
  36. }
  37. // filesize
  38. word fileSize = 0;
  39. fileSize += fatBuffer[28];
  40. fileSize += (fatBuffer[29] << 8);
  41. fileSize += (fatBuffer[30] << 16);
  42. fileSize += (fatBuffer[31] << 24);
  43. // add slash if folder (filesize 0)
  44. if (fileSize == 0)
  45. {
  46. catLen = strcpy(b + *bufLen, "/");
  47. (*bufLen) += catLen;
  48. }
  49. // end the starting HTML link tag
  50. catLen = strcpy(b + *bufLen, "\">");
  51. (*bufLen) += catLen;
  52. // do again for the link text
  53. // parse filename
  54. printLen = FS_parseFATstring(fatBuffer, 8, b, bufLen);
  55. // add '.' and parse extension
  56. if (fatBuffer[8] != ' ' || fatBuffer[9] != ' ' || fatBuffer[10] != ' ')
  57. {
  58. b[*bufLen] = '.';
  59. (*bufLen)++;
  60. printLen += FS_parseFATstring(fatBuffer+8, 3, b, bufLen) + 1;
  61. }
  62. // add slash if folder (filesize 0)
  63. if (fileSize == 0)
  64. {
  65. catLen = strcpy(b + *bufLen, "/");
  66. (*bufLen) += catLen;
  67. }
  68. // end hyperlink
  69. catLen = strcpy(b + *bufLen, "</a></td><td style=\"padding:0 15px 0 15px;\">");
  70. (*bufLen) += catLen;
  71. // filesize to integer string
  72. char buffer[10];
  73. itoa(fileSize, &buffer[0]);
  74. // write to buffer
  75. word i = 0;
  76. while (buffer[i] != 0)
  77. {
  78. b[*bufLen] = buffer[i];
  79. (*bufLen)++;
  80. i++;
  81. }
  82. catLen = strcpy(b + *bufLen, "</td></tr>\n");
  83. (*bufLen) += catLen;
  84. }
  85. // Reads FAT data for single entry
  86. // FAT data is parsed by FS_parseFatData()
  87. void wiz_readFATdata(char* b, word* bufLen)
  88. {
  89. FS_spiBeginTransfer();
  90. FS_spiTransfer(FS_CMD_RD_USB_DATA0);
  91. word datalen = FS_spiTransfer(0x0);
  92. char fatbuf[32];
  93. word i;
  94. for (i = 0; i < datalen; i++)
  95. {
  96. fatbuf[i] = FS_spiTransfer(0x00);
  97. }
  98. wiz_parseFATdata(datalen, fatbuf, b, bufLen);
  99. FS_spiEndTransfer();
  100. }
  101. // Lists directory of full path f
  102. // f needs to start with / and not end with /
  103. // Returns ANSW_USB_INT_SUCCESS if successful
  104. // Writes parsed result to address b
  105. // Result is terminated with a \0
  106. word wiz_listDir(char* f, char* b)
  107. {
  108. word bufLen = 0;
  109. word* pBuflen = &bufLen;
  110. word retval = FS_sendFullPath(f);
  111. // Return on failure
  112. if (retval != FS_ANSW_USB_INT_SUCCESS)
  113. return retval;
  114. retval = FS_open();
  115. // Return on failure
  116. if (retval != FS_ANSW_USB_INT_SUCCESS && retval != FS_ANSW_ERR_OPEN_DIR)
  117. return retval;
  118. FS_sendSinglePath("*");
  119. retval = FS_open();
  120. // Return on failure
  121. if (retval != FS_ANSW_USB_INT_DISK_READ)
  122. return retval;
  123. // Init length of output buffer
  124. *pBuflen = 0;
  125. while (retval == FS_ANSW_USB_INT_DISK_READ)
  126. {
  127. wiz_readFATdata(b, pBuflen);
  128. FS_spiBeginTransfer();
  129. FS_spiTransfer(FS_CMD_FILE_ENUM_GO);
  130. FS_spiEndTransfer();
  131. retval = FS_WaitGetStatus();
  132. }
  133. // Terminate buffer
  134. b[*pBuflen] = 0;
  135. (*pBuflen)++;
  136. return FS_ANSW_USB_INT_SUCCESS;
  137. }
  138. word PercentageDone(word remaining, word full)
  139. {
  140. word x = remaining * 100;
  141. return 100 - MATH_divU(x, full);
  142. }
  143. //-------------------
  144. //W5500 CONNECTION HANDLING FUNCTIONS
  145. //-------------------
  146. // Writes response from (successfully) opened USB file
  147. word wizWriteResponseFromUSB(word s, word fileSize)
  148. {
  149. // file size is already checked on being > 0
  150. if (FS_setCursor(0) != FS_ANSW_USB_INT_SUCCESS)
  151. BDOS_PrintConsole("cursor error\n");
  152. word bytesSent = 0;
  153. char buffer[10];
  154. char dbuf[10]; // percentage done for progress indication
  155. dbuf[0] = 0; // terminate
  156. // loop until all bytes are sent
  157. while (bytesSent != fileSize)
  158. {
  159. word partToSend = fileSize - bytesSent;
  160. // send in parts of FILE_BUFFER_SIZE
  161. if (partToSend > FILE_BUFFER_SIZE)
  162. partToSend = FILE_BUFFER_SIZE;
  163. // read from usb to buffer
  164. if (FS_readFile(fileBuffer, partToSend, 0) != FS_ANSW_USB_INT_SUCCESS)
  165. BDOS_PrintConsole("read error\n");
  166. if (!wizWriteDataFromMemory(s, fileBuffer, partToSend))
  167. {
  168. //uprintln("wizTranser error");
  169. return 0;
  170. }
  171. // Update the amount of bytes sent
  172. bytesSent += partToSend;
  173. //BDOS_PrintConsole(".");
  174. // indicate progress
  175. // remove previous percentage
  176. word i = strlen(dbuf);
  177. while (i > 0)
  178. {
  179. BDOS_PrintcConsole(0x8); // backspace
  180. i--;
  181. }
  182. if (strlen(dbuf) != 0)
  183. {
  184. BDOS_PrintcConsole(0x8); // backspace
  185. }
  186. itoa(PercentageDone(fileSize - bytesSent, fileSize), dbuf);
  187. BDOS_PrintConsole(dbuf);
  188. BDOS_PrintcConsole('%');
  189. }
  190. // remove previous percentage
  191. word i = strlen(dbuf);
  192. while (i > 0)
  193. {
  194. BDOS_PrintcConsole(0x8); // backspace
  195. i--;
  196. }
  197. if (strlen(dbuf) != 0)
  198. {
  199. BDOS_PrintcConsole(0x8); // backspace
  200. }
  201. FS_close();
  202. return 1;
  203. }
  204. void wizSend404Response(word s)
  205. {
  206. char* retTxt = "<!DOCTYPE html><html><head><title>ERROR404</title></head><body>ERROR 404: This is not the page you are looking for</body></html>";
  207. wizWriteDataFromMemory(s, retTxt, strlen(retTxt));
  208. }
  209. // Gets requested file path from request header
  210. // Returns size of requested path (should be 1 or higher because '/')
  211. // Fills pbuf with path
  212. // Assumes a request header of appropiate size
  213. word wizGetFilePath(char* rbuf, char* pbuf)
  214. {
  215. word foundPath = 0;
  216. word foundSpace = 0;
  217. word cursor = 0;
  218. word pathIndex = 0;
  219. while (foundPath == 0)
  220. {
  221. // until we found the first space after GET (or POST)
  222. if (foundSpace == 0 && rbuf[cursor] == 32)
  223. {
  224. foundSpace = 1;
  225. }
  226. else
  227. {
  228. if (foundSpace == 1)
  229. {
  230. // until we found the second space (after the file path)
  231. if (rbuf[cursor] == 32)
  232. {
  233. // exit the loop, we are done
  234. foundPath = 1;
  235. }
  236. else
  237. {
  238. // copy the character
  239. pbuf[pathIndex] = rbuf[cursor];
  240. //uprintc(rbuf[cursor]);
  241. pathIndex++;
  242. }
  243. }
  244. }
  245. // go to next character
  246. cursor++;
  247. }
  248. pbuf[pathIndex] = 0; // terminate string
  249. // return the path length
  250. return (pathIndex + 1);
  251. }
  252. void wizDirectoryListing(word s, char* path)
  253. {
  254. // write start of html page
  255. wizWriteDataFromMemory(s, "<!DOCTYPE html><html><body><h2>", 31);
  256. word pathLen = 0;
  257. while (path[pathLen] != 0)
  258. pathLen++;
  259. wizWriteDataFromMemory(s, path, pathLen-1);
  260. wizWriteDataFromMemory(s, "</h2><table><tr><th>Name</th><th>Size</th></tr>", 47);
  261. char *b = (char *) TMPMEM_LOCATION;
  262. if (wiz_listDir(path, b) == FS_ANSW_USB_INT_SUCCESS)
  263. {
  264. word listSize = 0;
  265. while (b[listSize] != 0)
  266. listSize++;
  267. if (listSize == 0)
  268. listSize = 1;
  269. wizWriteDataFromMemory(s, b, listSize-1);
  270. }
  271. // write end of html page
  272. wizWriteDataFromMemory(s, "</table></body></html>", 22);
  273. }
  274. void wizServeFile(word s, char* path)
  275. {
  276. BDOS_PrintConsole(path);
  277. BDOS_PrintConsole(": ");
  278. // Redirect "/" to "/INDEX.HTM"
  279. if (path[0] == 47 && path[1] == 0)
  280. {
  281. // send an actual redirect to the browser
  282. char* response = "HTTP/1.1 301 Moved Permanently\nLocation: /INDEX.HTM\n";
  283. BDOS_PrintConsole("Redirect to /INDEX.HTM\n");
  284. wizWriteDataFromMemory(s, response, strlen(response));
  285. // Disconnect after sending the redirect
  286. wizCmd(s, WIZNET_CR_DISCON);
  287. return;
  288. }
  289. word error = 0;
  290. word listDir = 0;
  291. if (FS_sendFullPath(&path[0]) != FS_ANSW_USB_INT_SUCCESS) // automatically upercases the path
  292. error = 404;
  293. if (!error)
  294. {
  295. word openStatus = FS_open();
  296. if (openStatus == FS_ANSW_ERR_OPEN_DIR)
  297. listDir = 1;
  298. else if (openStatus != FS_ANSW_USB_INT_SUCCESS)
  299. error = 404;
  300. }
  301. word fileSize = 0;
  302. if (!error && !listDir)
  303. fileSize = FS_getFileSize();
  304. if (!error && !listDir)
  305. {
  306. if (fileSize == 0)
  307. error = 404; // handle empty files as if they do not exist
  308. }
  309. // send error response on error
  310. if (error)
  311. {
  312. BDOS_PrintConsole("404 ");
  313. // currently puts all errors under 404
  314. // write header
  315. char* header = "HTTP/1.1 404 Not Found\nServer: FPGC/1.0\nContent-Type: text/html\n\n";
  316. wizWriteDataFromMemory(s, header, strlen(header));
  317. FS_sendFullPath("/404.HTM");
  318. if (FS_open() != FS_ANSW_USB_INT_SUCCESS)
  319. {
  320. // if the custom 404 does not exist, return own error code
  321. BDOS_PrintConsole("no 404.HTM\n");
  322. wizSend404Response(s);
  323. }
  324. else
  325. {
  326. // send custom 404
  327. fileSize = FS_getFileSize();
  328. // write the response from USB
  329. BDOS_PrintConsole("/404.HTM ");
  330. wizWriteResponseFromUSB(s, fileSize);
  331. BDOS_PrintConsole("Done\n");
  332. }
  333. }
  334. else
  335. {
  336. if (listDir)
  337. {
  338. // check if last character is a /
  339. // if not, redirect to the path with / after it
  340. word i = 0;
  341. while (path[i] != 0)
  342. i++;
  343. if (path[i-1] != '/')
  344. {
  345. //BDOS_PrintConsole(path);
  346. //BDOS_PrintConsole("\n");
  347. path[i] = '/';
  348. path[i+1] = 0;
  349. char* response = "HTTP/1.1 301 Moved Permanently\nLocation: ";
  350. BDOS_PrintConsole("Redirect to ");
  351. BDOS_PrintConsole(path);
  352. BDOS_PrintConsole("\n");
  353. wizWriteDataFromMemory(s, response, strlen(response));
  354. wizWriteDataFromMemory(s, path, i+1);
  355. wizWriteDataFromMemory(s, "\n", 1);
  356. }
  357. else
  358. {
  359. BDOS_PrintConsole("200 ");
  360. // write header
  361. // currently omitting content type
  362. char* header = "HTTP/1.1 200 OK\nServer: FPGC/1.0\n\n";
  363. wizWriteDataFromMemory(s, header, strlen(header));
  364. wizDirectoryListing(s, path);
  365. BDOS_PrintConsole("Done\n");
  366. }
  367. }
  368. else
  369. {
  370. if (fileSize + 1 != 0) // really make sure no directory is being read
  371. {
  372. BDOS_PrintConsole("200 ");
  373. // write header
  374. // currently omitting content type
  375. // includes content length so the client knows how large a download is
  376. char header[128];
  377. header[0] = 0;
  378. strcat(header, "HTTP/1.1 200 OK\nServer: FPGC/1.0\nContent-Length: ");
  379. char fileSizeStr[24];
  380. itoa(fileSize, fileSizeStr);
  381. strcat(header, fileSizeStr);
  382. strcat(header, "\n\n");
  383. wizWriteDataFromMemory(s, header, strlen(header));
  384. // write the response from USB
  385. wizWriteResponseFromUSB(s, fileSize);
  386. BDOS_PrintConsole("Done\n");
  387. }
  388. }
  389. }
  390. // Disconnect after sending a response
  391. wizCmd(s, WIZNET_CR_DISCON);
  392. }
  393. // Handle session for socket s
  394. void wizHandleSession(word s)
  395. {
  396. // Size of received data
  397. word rsize;
  398. rsize = wizGetSockReg16(s, WIZNET_SnRX_RSR);
  399. if (rsize == 0)
  400. {
  401. wizCmd(s, WIZNET_CR_DISCON);
  402. return;
  403. }
  404. char* rbuf = WIZrbuf;
  405. wizReadRecvData(s, rbuf, rsize);
  406. // read rbuf for requested page
  407. // parse from {GET /INFO.HTM HTTP/1.1}
  408. char pbuf[128]; // buffer for path name
  409. word pbufSize = wizGetFilePath(&rbuf[1], pbuf);
  410. wizServeFile(s, pbuf);
  411. // Free received data when not read
  412. // Not used, since we currently read the request
  413. //wizFlush(s, rsize);
  414. }
  415. int main()
  416. {
  417. BDOS_PrintConsole("Starting Web Server\n");
  418. // Assumes that the USB drive is already properly initialized by BDOS
  419. word ip_addr[4] = {192, 168, 0, 213};
  420. word gateway_addr[4] = {192, 168, 0, 1};
  421. word mac_addr[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x24, 0x64};
  422. word sub_mask[4] = {255, 255, 255, 0};
  423. wiz_Init(ip_addr, gateway_addr, mac_addr, sub_mask);
  424. // Open all sockets in TCP Server mode at port 80
  425. wizInitSocketTCP(0, 80);
  426. wizInitSocketTCP(1, 80);
  427. wizInitSocketTCP(2, 80);
  428. wizInitSocketTCP(3, 80);
  429. wizInitSocketTCP(4, 80);
  430. wizInitSocketTCP(5, 80);
  431. wizInitSocketTCP(6, 80);
  432. // Socket s status
  433. word sxStatus;
  434. while(1)
  435. {
  436. if (HID_FifoAvailable())
  437. {
  438. HID_FifoRead(); // remove it from the buffer
  439. return 'q';
  440. }
  441. // handle all sockets (socket 7 is reserved by netHID)
  442. word s;
  443. for (s = 0; s < 7; s++)
  444. {
  445. // Get status for socket s
  446. sxStatus = wizGetSockReg8(s, WIZNET_SnSR);
  447. if (sxStatus == WIZNET_SOCK_CLOSED)
  448. {
  449. // Open the socket when closed
  450. // Set socket s in TCP Server mode at port 80
  451. wizInitSocketTCP(s, 80);
  452. }
  453. else if (sxStatus == WIZNET_SOCK_ESTABLISHED)
  454. {
  455. // Handle session when a connection is established
  456. // Also reinitialize socket
  457. wizHandleSession(s);
  458. // Set socket s in TCP Server mode at port 80
  459. wizInitSocketTCP(s, 80);
  460. }
  461. else if (sxStatus == WIZNET_SOCK_LISTEN || sxStatus == WIZNET_SOCK_SYNSENT || sxStatus == WIZNET_SOCK_SYNRECV)
  462. {
  463. // Do nothing in these cases
  464. }
  465. else
  466. {
  467. // In other cases, reset the socket
  468. // Set socket s in TCP Server mode at port 80
  469. wizInitSocketTCP(s, 80);
  470. }
  471. }
  472. // Delay a few milliseconds
  473. // Should (could) eventually be replaced by an interrupt checker
  474. delay(10);
  475. }
  476. return 'q';
  477. }
  478. void interrupt()
  479. {
  480. // Handle all interrupts
  481. word i = getIntID();
  482. switch(i)
  483. {
  484. case INTID_TIMER1:
  485. timer1Value = 1; // Notify ending of timer1
  486. break;
  487. }
  488. }