// Wget // Tool to do a HTTP GET request #define word char #include "LIB/MATH.C" #include "LIB/STDLIB.C" #include "LIB/SYS.C" #include "LIB/FS.C" #include "LIB/WIZ5500.C" #define HEAP_LOCATION 0x500000 // Which socket to use #define SOCKET_CLIENT 0 #define CONNECT_MAX_RETRIES 1000 // Initializes w5500 in client TCP mode, with a static ip (last digit as arg) // Port is also given as arg void initClient(word ip, word port) { word ip_addr[4] = {192, 168, 0, 213}; word gateway_addr[4] = {192, 168, 0, 1}; word mac_addr[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x24, 0x64}; word sub_mask[4] = {255, 255, 255, 0}; wiz_Init(ip_addr, gateway_addr, mac_addr, sub_mask); // Open socket in TCP Client mode at port wizInitSocketTCPClient(SOCKET_CLIENT, port); } // Connect to target at given ip and port // Return 1 on success, 0 on failure word connectToTarget(char* ipTarget, word portTarget) { wizWrite(WIZNET_SnDIPR, WIZNET_WRITE_SnR + (SOCKET_CLIENT << 5), ipTarget, 4); wizSetSockReg16(SOCKET_CLIENT, WIZNET_SnDPORT, portTarget); wizCmd(SOCKET_CLIENT, WIZNET_CR_CONNECT); word sxStatus = wizGetSockReg8(SOCKET_CLIENT, WIZNET_SnSR); word retries = 0; while (sxStatus != WIZNET_SOCK_ESTABLISHED && retries < CONNECT_MAX_RETRIES) { //BDOS_PrintHexConsole(sxStatus); if (sxStatus == WIZNET_SOCK_CLOSED) { return 0; } sxStatus = wizGetSockReg8(SOCKET_CLIENT, WIZNET_SnSR); if (sxStatus != WIZNET_SOCK_ESTABLISHED) { retries++; delay(1); } } return 1; } // Send request (headers should use \r\n according to the HTTP specs) void sendRequest() { char* rTxt = (char*) HEAP_LOCATION; // we use heap for the request text word rLen = 0; word catLen = 0; catLen = strcpy(rTxt + rLen, "GET /"); rLen += catLen; char targetURL[64]; BDOS_GetArgN(2, targetURL); catLen = strcpy(rTxt + rLen, targetURL); rLen += catLen; catLen = strcpy(rTxt + rLen, " HTTP/1.1\r\nUser-Agent: WGET(FPGC)\r\nAccept: */*\r\nHost: "); rLen += catLen; char ipstr[24]; BDOS_GetArgN(1, ipstr); catLen = strcpy(rTxt + rLen, ipstr); rLen += catLen; catLen = strcpy(rTxt + rLen, "\r\n\r\n"); rLen += catLen; wizWriteDataFromMemory(SOCKET_CLIENT, rTxt, rLen); BDOS_PrintConsole("Request sent\n"); } // Init output file for download // Returns 1 on success, else 0 word initOutputFile(char* path, char* fname, char* rbuf, word rsize) { // sanity check current path if (FS_sendFullPath(path) == FS_ANSW_USB_INT_SUCCESS) { word retval = FS_open(); // check that we can open the path if (retval == FS_ANSW_USB_INT_SUCCESS || retval == FS_ANSW_ERR_OPEN_DIR) { // check length of filename if (strlen(fname) <= 12) { // uppercase filename strToUpper(fname); // send filename FS_sendSinglePath(fname); // create the file if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS) { // open the path again FS_sendFullPath(path); FS_open(); // send filename again FS_sendSinglePath(fname); // open the newly created file if (FS_open() == FS_ANSW_USB_INT_SUCCESS) { // set cursor to start if (FS_setCursor(0) == FS_ANSW_USB_INT_SUCCESS) { if (FS_writeFile(rbuf, rsize) != FS_ANSW_USB_INT_SUCCESS) { FS_close(); BDOS_PrintConsole("Error while writing to file\n"); return 0; } } else { FS_close(); BDOS_PrintConsole("Cursor error\n"); return 0; } } } else { BDOS_PrintConsole("Error while creating output file\n"); return 0; } } else { BDOS_PrintConsole("Filename too long\n"); return 0; } } else { BDOS_PrintConsole("Could not open path\n"); return 0; } } else { BDOS_PrintConsole("Error sending path\n"); return 0; } return 1; } // Parses HTTP header, returns location of the first data byte word getDataStart(char* rbuf, word rsize) { word dataStart = 0; word i = 0; while (i < rsize - 4 && dataStart == 0) { //BDOS_PrintcConsole(rbuf[i]); if (rbuf[i] == '\r' && rbuf[i+1] == '\n' && rbuf[i+2] == '\r' && rbuf[i+3] == '\n') { dataStart = i+4; } else if (rbuf[i] == '\n' && rbuf[i+1] == '\n') { dataStart = i+2; } i++; } return dataStart; } // Parses HTTP header, returns status code word getStatusCode(char* rbuf, word rsize) { word i = 0; // look for first space, then 3 digits while (i < rsize - 4) { if (rbuf[i] == ' ') { char statusStr[4]; statusStr[0] = rbuf[i+1]; statusStr[1] = rbuf[i+2]; statusStr[2] = rbuf[i+3]; statusStr[3] = 0; return strToInt(statusStr); } i++; } return 0; } // Parses HTTP header, returns content length header field word getContentLength(char* rbuf, word rsize) { word i = 0; word strBufIndex = 0; // look for Content-Length field while (i < rsize - 4) { if (memcmp(&rbuf[i], "Content-Length: ", 16)) { i = i + 16; // when found, add all following characters to buffer until newline char contentLengthStrBuf[32]; contentLengthStrBuf[0] = 0; while (rbuf[i] != '\r' && rbuf[i] != '\n') { contentLengthStrBuf[strBufIndex] = rbuf[i]; i++; strBufIndex++; } contentLengthStrBuf[strBufIndex] = 0; // terminate buffer // finally, convert to int and return return strToInt(contentLengthStrBuf); } i++; } return 0; } word getPercentageDone(word remaining, word full) { word x = remaining * 100; return 100 - MATH_divU(x, full); } // Receive response while connected // Write to file and terminal void receiveResponse(char* path, char* fname) { word firstResponse = 1; word contentLengthOrig[1]; // its an array now, because of a compiler bug in DeprCcompiler (ShivyC) contentLengthOrig[0] = 0; word contentLength = 0; word currentProgress = 0; BDOS_PrintConsole("Downloading response (any key to stop)\n"); while (wizGetSockReg8(SOCKET_CLIENT, WIZNET_SnSR) == WIZNET_SOCK_ESTABLISHED) { word rsize = wizGetSockReg16(SOCKET_CLIENT, WIZNET_SnRX_RSR); if (rsize != 0) { char* rbuf = (char*) HEAP_LOCATION; // we use heap for the receive data buffer wizReadRecvData(SOCKET_CLIENT, rbuf, rsize); if (firstResponse) { word dataStart = getDataStart(rbuf, rsize); contentLength = getContentLength(rbuf, rsize); contentLengthOrig[0] = contentLength; BDOS_PrintConsole("Status code: "); BDOS_PrintDecConsole(getStatusCode(rbuf, rsize)); BDOS_PrintConsole("\n"); if (!initOutputFile(path, fname, &rbuf[dataStart], rsize - dataStart)) return; contentLength -= (rsize - dataStart); firstResponse = 0; // all data downloaded if (contentLength == 0) break; } else { if (FS_writeFile(rbuf, rsize) != FS_ANSW_USB_INT_SUCCESS) { FS_close(); BDOS_PrintConsole("Error while writing to file\n"); return; } contentLength -= rsize; if (getPercentageDone(contentLength, contentLengthOrig[0]) >= currentProgress) { BDOS_PrintDecConsole(currentProgress); BDOS_PrintConsole("%\n"); currentProgress += 10; } // all data downloaded if (contentLength == 0) break; } } // Allow manual cancel if (HID_FifoAvailable()) { HID_FifoRead(); // remove it from the buffer break; } } wizCmd(SOCKET_CLIENT, WIZNET_CR_DISCON); BDOS_PrintConsole("Output written to "); BDOS_PrintConsole(path); BDOS_PrintConsole("/"); BDOS_PrintConsole(fname); BDOS_PrintConsole("\n"); FS_close(); } // convert string to IP by looking at the dots void strToIP(char* str, char* IP) { word i = 0; word dotsFound = 0; word digitCounter = 0; char tmp[16]; while (str[i] != 0) { if (str[i] == '.') { tmp[digitCounter] = 0; IP[dotsFound] = strToInt(tmp); dotsFound += 1; digitCounter = 0; } else { tmp[digitCounter] = str[i]; digitCounter++; } i++; } tmp[digitCounter] = 0; IP[dotsFound] = strToInt(tmp); } int main() { // Init BDOS_PrintConsole("WGET (IP path output [port])\n"); initClient(213, 1024); // TODO: DNS lookup // IP char ipstr[24]; BDOS_GetArgN(1, ipstr); char ipTarget[4]; strToIP(ipstr, ipTarget); // Port word portTarget = 0; char portstr[16]; BDOS_GetArgN(4, portstr); if (portstr[0] != 0) portTarget = strToInt(portstr); if (portTarget == 0) portTarget = 80; // Connect to target if (!connectToTarget(ipTarget, portTarget)) { BDOS_PrintConsole("Could not connect to host\n"); return 'q'; } else BDOS_PrintConsole("Connected to host\n"); // Send request sendRequest(); // Receive response char* path = BDOS_GetPath(); // If no filename is given, use OUT.TXT as default char fname[16]; BDOS_GetArgN(3, fname); if (fname[0] == 0) { strcpy(fname, "OUT.TXT"); } receiveResponse(path, fname); return 'q'; } void interrupt() { // handle all interrupts word i = getIntID(); switch(i) { case INTID_TIMER1: timer1Value = 1; // notify ending of timer1 break; case INTID_TIMER2: break; case INTID_UART0: break; case INTID_GPU: break; case INTID_TIMER3: break; case INTID_PS2: break; case INTID_UART1: break; case INTID_UART2: break; } }