Jake B. / Mbed 2 deprecated MakerBotServer

Dependencies:   IAP NTPClient RTC mbed-rtos mbed Socket lwip-sys lwip BurstSPI

Fork of LPC1768_Mini-DK by Frank Vannieuwkerke

Committer:
jakeb
Date:
Fri Aug 23 21:45:08 2013 +0000
Revision:
15:688b3e3958fd
Initial commit of software v0.2;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jakeb 15:688b3e3958fd 1 // Copyright (c) 2013, jake (at) allaboutjake (dot) com
jakeb 15:688b3e3958fd 2 // All rights reserved.
jakeb 15:688b3e3958fd 3 //
jakeb 15:688b3e3958fd 4 // Redistribution and use in source and binary forms, with or without
jakeb 15:688b3e3958fd 5 // modification, are permitted provided that the following conditions are met:
jakeb 15:688b3e3958fd 6 // * Redistributions of source code must retain the above copyright
jakeb 15:688b3e3958fd 7 // notice, this list of conditions and the following disclaimer.
jakeb 15:688b3e3958fd 8 // * Redistributions in binary form must reproduce the above copyright
jakeb 15:688b3e3958fd 9 // notice, this list of conditions and the following disclaimer in the
jakeb 15:688b3e3958fd 10 // documentation and/or other materials provided with the distribution.
jakeb 15:688b3e3958fd 11 // * The name of the author and/or copyright holder nor the
jakeb 15:688b3e3958fd 12 // names of its contributors may be used to endorse or promote products
jakeb 15:688b3e3958fd 13 // derived from this software without specific prior written permission.
jakeb 15:688b3e3958fd 14 //
jakeb 15:688b3e3958fd 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
jakeb 15:688b3e3958fd 16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
jakeb 15:688b3e3958fd 17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
jakeb 15:688b3e3958fd 18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, AUTHOR, OR ANY CONTRIBUTORS
jakeb 15:688b3e3958fd 19 // BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
jakeb 15:688b3e3958fd 20 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
jakeb 15:688b3e3958fd 21 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
jakeb 15:688b3e3958fd 22 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
jakeb 15:688b3e3958fd 23 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
jakeb 15:688b3e3958fd 24 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jakeb 15:688b3e3958fd 25
jakeb 15:688b3e3958fd 26 // DESCRIPTION OF FILE:
jakeb 15:688b3e3958fd 27 //
jakeb 15:688b3e3958fd 28 // This file contains the source to the http server.
jakeb 15:688b3e3958fd 29 //
jakeb 15:688b3e3958fd 30 // The main function is "http_server_task" which is intended to be run in its
jakeb 15:688b3e3958fd 31 // own thread. The other functions are helper functions. This function assumes
jakeb 15:688b3e3958fd 32 // that the EthernetInterface is already configured and ready for use.
jakeb 15:688b3e3958fd 33 //
jakeb 15:688b3e3958fd 34
jakeb 15:688b3e3958fd 35 #include "httpd.h"
jakeb 15:688b3e3958fd 36 #include "readline.h"
jakeb 15:688b3e3958fd 37 #include "main.h"
jakeb 15:688b3e3958fd 38 #include "settings.h"
jakeb 15:688b3e3958fd 39
jakeb 15:688b3e3958fd 40 // Some Private methods
jakeb 15:688b3e3958fd 41 // HTML rendering
jakeb 15:688b3e3958fd 42 void sendHTTPHeader(ClientSocket* sock, int response_code, char* response_descr, char* contentType);
jakeb 15:688b3e3958fd 43 void sendStatusContent(ClientSocket* sock);
jakeb 15:688b3e3958fd 44 void sendPageHeader(ClientSocket* sock);
jakeb 15:688b3e3958fd 45 void sendPageFooter(ClientSocket* sock);
jakeb 15:688b3e3958fd 46 void sendCommandTable(ClientSocket* sock);
jakeb 15:688b3e3958fd 47 void sendHTTPRedirect(ClientSocket* sock, char* redirectTo);
jakeb 15:688b3e3958fd 48 void redirectToMessage(ClientSocket* sock, char* message, int isErr);
jakeb 15:688b3e3958fd 49 void renderMessageBox(ClientSocket* sock, char* message, int isBadMessage);
jakeb 15:688b3e3958fd 50 int handleAuthorizationHeaders(ClientSocket* sock, char* token);
jakeb 15:688b3e3958fd 51 void discardHeaders(ClientSocket* socket);
jakeb 15:688b3e3958fd 52
jakeb 15:688b3e3958fd 53 //Helper functions
jakeb 15:688b3e3958fd 54 int findOccuranceOfChar(char* buffer, char c, int occur, int max_length);
jakeb 15:688b3e3958fd 55 char *url_encode(char *str);
jakeb 15:688b3e3958fd 56 char *url_decode(char *str);
jakeb 15:688b3e3958fd 57
jakeb 15:688b3e3958fd 58 // Network server task: setup and listen for network connections
jakeb 15:688b3e3958fd 59 void http_server_task(void const*) {
jakeb 15:688b3e3958fd 60 //This thread assumes that the EthernetInterface is connected with an IP address.
jakeb 15:688b3e3958fd 61 static int HTTPD_PORT = 80;
jakeb 15:688b3e3958fd 62
jakeb 15:688b3e3958fd 63 //Use SimpleSocket library to setup a server socket
jakeb 15:688b3e3958fd 64 DISPLAY("Setting up httpd on %d\r\n", HTTPD_PORT);
jakeb 15:688b3e3958fd 65 ServerSocket serverSocket(HTTPD_PORT);
jakeb 15:688b3e3958fd 66
jakeb 15:688b3e3958fd 67 while (1) {
jakeb 15:688b3e3958fd 68 //Debug: DISPLAY("Waiting for connection\r\n");
jakeb 15:688b3e3958fd 69 //Single-threaded: get the next incoming connection
jakeb 15:688b3e3958fd 70 ClientSocket socket = serverSocket.accept();
jakeb 15:688b3e3958fd 71
jakeb 15:688b3e3958fd 72 retryAuth:
jakeb 15:688b3e3958fd 73 //Get the request line (the first line) of the HTTP request
jakeb 15:688b3e3958fd 74 char linebuffer[HTTP_LINE_BUFFER_SIZE];
jakeb 15:688b3e3958fd 75 memset(linebuffer, 0, HTTP_LINE_BUFFER_SIZE);
jakeb 15:688b3e3958fd 76 int length = readline(&socket, linebuffer, HTTP_LINE_BUFFER_SIZE);
jakeb 15:688b3e3958fd 77
jakeb 15:688b3e3958fd 78 //debug: printf("%s: '%s'\r\n", socket.get_address(), linebuffer);
jakeb 15:688b3e3958fd 79
jakeb 15:688b3e3958fd 80 // Make sure its a GET (we only support GET currently)
jakeb 15:688b3e3958fd 81 if (memcmp(linebuffer, "GET ", 4) != 0) {
jakeb 15:688b3e3958fd 82 printf("Request Line: '%s'\r\n", linebuffer);
jakeb 15:688b3e3958fd 83 DISPLAY("SENDING 400 Bad Request\r\n");
jakeb 15:688b3e3958fd 84 sendHTTPHeader(&socket, 400, "Bad Request", "text/html");
jakeb 15:688b3e3958fd 85 sendPageHeader(&socket);
jakeb 15:688b3e3958fd 86 socket.printf("400 Bad Request");
jakeb 15:688b3e3958fd 87 sendPageFooter(&socket);
jakeb 15:688b3e3958fd 88 socket.close();
jakeb 15:688b3e3958fd 89 continue;
jakeb 15:688b3e3958fd 90 }
jakeb 15:688b3e3958fd 91
jakeb 15:688b3e3958fd 92 // If we have a password stored in flash, then check to see if we're authorized.
jakeb 15:688b3e3958fd 93 char* token = getAuthenticationToken();
jakeb 15:688b3e3958fd 94 if (token != NULL) {
jakeb 15:688b3e3958fd 95 //Process the headers, look for an authoirzation header and discard the rest.
jakeb 15:688b3e3958fd 96 char* base64token=base64(token);
jakeb 15:688b3e3958fd 97 int authState = handleAuthorizationHeaders(&socket, base64token);
jakeb 15:688b3e3958fd 98 free(base64token);
jakeb 15:688b3e3958fd 99 discardHeaders(&socket);
jakeb 15:688b3e3958fd 100
jakeb 15:688b3e3958fd 101 // If authentication was not good, then send out the 401
jakeb 15:688b3e3958fd 102 if (!authState) {
jakeb 15:688b3e3958fd 103 //Auth failed or not present.
jakeb 15:688b3e3958fd 104 socket.printf("HTTP/1.1 %d %s\r\n", 401, "Not Authorized");
jakeb 15:688b3e3958fd 105 socket.printf("WWW-Authenticate: Basic realm=\"makerbot_server\"\r\n");
jakeb 15:688b3e3958fd 106 socket.printf("\r\n");
jakeb 15:688b3e3958fd 107 Thread::yield();
jakeb 15:688b3e3958fd 108 goto retryAuth; //TODO: goto's bad, but I'm lazy. Can probably be refactored somehow
jakeb 15:688b3e3958fd 109 } else {
jakeb 15:688b3e3958fd 110 //Authentication OK
jakeb 15:688b3e3958fd 111 }
jakeb 15:688b3e3958fd 112 } else {
jakeb 15:688b3e3958fd 113 //A password wasn't set in flash, we don't need the rest of the headers, discard
jakeb 15:688b3e3958fd 114 discardHeaders(&socket);
jakeb 15:688b3e3958fd 115 }
jakeb 15:688b3e3958fd 116
jakeb 15:688b3e3958fd 117 //Find the path section of the request line.
jakeb 15:688b3e3958fd 118 int endOfPath = findOccuranceOfChar(linebuffer, ' ', 2, HTTP_LINE_BUFFER_SIZE);
jakeb 15:688b3e3958fd 119 int lengthOfPath = endOfPath - 4;
jakeb 15:688b3e3958fd 120 char* path = (char*)malloc(lengthOfPath+1);
jakeb 15:688b3e3958fd 121 strncpy(path, linebuffer+4, lengthOfPath);
jakeb 15:688b3e3958fd 122 path[lengthOfPath]=0;
jakeb 15:688b3e3958fd 123
jakeb 15:688b3e3958fd 124 //debugging: DISPLAY("PATH (%d): '%s'\r\n", lengthOfPath, path);
jakeb 15:688b3e3958fd 125
jakeb 15:688b3e3958fd 126 // Process the given path and return the page
jakeb 15:688b3e3958fd 127 if (lengthOfPath == 1 && path[0] == '/') {
jakeb 15:688b3e3958fd 128 //request for the root (default) page
jakeb 15:688b3e3958fd 129 sendHTTPHeader(&socket, 200, "OK", "text/html");
jakeb 15:688b3e3958fd 130 sendPageHeader(&socket);
jakeb 15:688b3e3958fd 131 sendStatusContent(&socket);
jakeb 15:688b3e3958fd 132 sendCommandTable(&socket);
jakeb 15:688b3e3958fd 133 sendPageFooter(&socket);
jakeb 15:688b3e3958fd 134 } else if (lengthOfPath > 6 && memcmp(path, "/?msg=",6) == 0) {
jakeb 15:688b3e3958fd 135 char* message = url_decode(path+6);
jakeb 15:688b3e3958fd 136 sendHTTPHeader(&socket, 200, "OK", "text/html");
jakeb 15:688b3e3958fd 137 sendPageHeader(&socket);
jakeb 15:688b3e3958fd 138 renderMessageBox(&socket, message, 0);
jakeb 15:688b3e3958fd 139 sendStatusContent(&socket);
jakeb 15:688b3e3958fd 140 sendCommandTable(&socket);
jakeb 15:688b3e3958fd 141 sendPageFooter(&socket);
jakeb 15:688b3e3958fd 142 free(message);
jakeb 15:688b3e3958fd 143 } else if (lengthOfPath > 6 && memcmp(path, "/?err=",6) == 0) {
jakeb 15:688b3e3958fd 144 char* message = url_decode(path+6);
jakeb 15:688b3e3958fd 145 sendHTTPHeader(&socket, 200, "OK", "text/html");
jakeb 15:688b3e3958fd 146 sendPageHeader(&socket);
jakeb 15:688b3e3958fd 147 renderMessageBox(&socket, message, 1);
jakeb 15:688b3e3958fd 148 sendStatusContent(&socket);
jakeb 15:688b3e3958fd 149 sendCommandTable(&socket);
jakeb 15:688b3e3958fd 150 sendPageFooter(&socket);
jakeb 15:688b3e3958fd 151 free(message);
jakeb 15:688b3e3958fd 152 } else if (strncmp(path, "/command?", 9) == 0) {
jakeb 15:688b3e3958fd 153 //This is a command, figure out which one
jakeb 15:688b3e3958fd 154 if (lengthOfPath >= 9+5 && memcmp(path+9, "pause", 5) == 0) {
jakeb 15:688b3e3958fd 155 //pause command
jakeb 15:688b3e3958fd 156 //GET /command?pause HTTP/1.1
jakeb 15:688b3e3958fd 157 if (bot) {
jakeb 15:688b3e3958fd 158 Makerbot::MakerbotBuildState state = bot->getBuildState();
jakeb 15:688b3e3958fd 159 if (state.build_state == Makerbot::BUILD_STATE_ERROR) {
jakeb 15:688b3e3958fd 160 redirectToMessage(&socket, "Error: unable to determine build state. Sending pause/resume command in the blind.", 1);
jakeb 15:688b3e3958fd 161 bot->pauseResume();
jakeb 15:688b3e3958fd 162 } else if (state.build_state == Makerbot::BUILD_RUNNING) {
jakeb 15:688b3e3958fd 163 bot->pauseResume();
jakeb 15:688b3e3958fd 164 redirectToMessage(&socket, "Pausing build.", 0);
jakeb 15:688b3e3958fd 165 } else {
jakeb 15:688b3e3958fd 166 redirectToMessage(&socket, "Error: Not currently running. Cannot pause.", 1);
jakeb 15:688b3e3958fd 167 }
jakeb 15:688b3e3958fd 168 } else {
jakeb 15:688b3e3958fd 169 redirectToMessage(&socket, "Error: connection to Makerbot not established.", 1);
jakeb 15:688b3e3958fd 170 }
jakeb 15:688b3e3958fd 171 } else if (lengthOfPath >= 9+6 && memcmp(path+9, "cancel", 6) == 0) {
jakeb 15:688b3e3958fd 172 if (bot) {
jakeb 15:688b3e3958fd 173 bot->abortImmediately();
jakeb 15:688b3e3958fd 174 redirectToMessage(&socket, "Cancelling build.", 0);
jakeb 15:688b3e3958fd 175 } else {
jakeb 15:688b3e3958fd 176 redirectToMessage(&socket, "Error: connection to Makerbot not established.", 1);
jakeb 15:688b3e3958fd 177 }
jakeb 15:688b3e3958fd 178 } else if (lengthOfPath >= 9+6 && memcmp(path+9, "resume", 6) == 0) {
jakeb 15:688b3e3958fd 179 if (bot) {
jakeb 15:688b3e3958fd 180 Makerbot::MakerbotBuildState state = bot->getBuildState();
jakeb 15:688b3e3958fd 181 if (state.build_state == Makerbot::BUILD_STATE_ERROR) {
jakeb 15:688b3e3958fd 182 redirectToMessage(&socket, "Error: unable to determine build state. Sending pause/resume command in the blind.", 1);
jakeb 15:688b3e3958fd 183 bot->pauseResume();
jakeb 15:688b3e3958fd 184 } else if (state.build_state == Makerbot::BUILD_PAUSED) {
jakeb 15:688b3e3958fd 185 bot->pauseResume();
jakeb 15:688b3e3958fd 186 redirectToMessage(&socket, "Resuming build.", 0);
jakeb 15:688b3e3958fd 187 } else {
jakeb 15:688b3e3958fd 188 redirectToMessage(&socket, "Error: Not currently paused. Cannot resume.", 1);
jakeb 15:688b3e3958fd 189 }
jakeb 15:688b3e3958fd 190 } else {
jakeb 15:688b3e3958fd 191 redirectToMessage(&socket, "Error: connection to Makerbot not established.", 1);
jakeb 15:688b3e3958fd 192 }
jakeb 15:688b3e3958fd 193 } else if (lengthOfPath >= 9+6 && memcmp(path + 9, "build=", 6) == 0) {
jakeb 15:688b3e3958fd 194 int startOfFilename = findOccuranceOfChar(path, '=', 1, lengthOfPath)+1;
jakeb 15:688b3e3958fd 195 int lengthOfFileName = lengthOfPath - startOfFilename;
jakeb 15:688b3e3958fd 196 char* filename = path+startOfFilename;
jakeb 15:688b3e3958fd 197 if (lengthOfFileName < 5) {
jakeb 15:688b3e3958fd 198 //Minimum filename is of the form "x.x3g" = 5 characters
jakeb 15:688b3e3958fd 199 redirectToMessage(&socket, "Filename too short", 1);
jakeb 15:688b3e3958fd 200 } else if (lengthOfFileName > 12) {
jakeb 15:688b3e3958fd 201 //According to the s3g spec, only 12-character FN's supported
jakeb 15:688b3e3958fd 202 redirectToMessage(&socket, "Filename too long", 1);
jakeb 15:688b3e3958fd 203 } else if (memcmp(filename+lengthOfFileName-4, ".X3G", 4)==0 ||
jakeb 15:688b3e3958fd 204 memcmp(filename+lengthOfFileName-4, ".x3g", 4)==0) {
jakeb 15:688b3e3958fd 205 //Send command to bot to print
jakeb 15:688b3e3958fd 206 if (bot) {
jakeb 15:688b3e3958fd 207 bot->playbackCapture(filename);
jakeb 15:688b3e3958fd 208 redirectToMessage(&socket, "Beginning build", 0);
jakeb 15:688b3e3958fd 209 } else {
jakeb 15:688b3e3958fd 210 redirectToMessage(&socket, "Error: connection to Makerbot not established.", 1);
jakeb 15:688b3e3958fd 211 }
jakeb 15:688b3e3958fd 212 } else {
jakeb 15:688b3e3958fd 213 redirectToMessage(&socket, "Error: filename doesn't end with X3G.", 1);
jakeb 15:688b3e3958fd 214 }
jakeb 15:688b3e3958fd 215 } else {
jakeb 15:688b3e3958fd 216 redirectToMessage(&socket, "Unknown command", 1);
jakeb 15:688b3e3958fd 217 }
jakeb 15:688b3e3958fd 218 } else {
jakeb 15:688b3e3958fd 219 sendHTTPHeader(&socket, 404, "Not Found", "text/html");
jakeb 15:688b3e3958fd 220 sendPageHeader(&socket);
jakeb 15:688b3e3958fd 221 socket.printf("404 Not Found");
jakeb 15:688b3e3958fd 222 sendPageFooter(&socket);
jakeb 15:688b3e3958fd 223 }
jakeb 15:688b3e3958fd 224
jakeb 15:688b3e3958fd 225 //DISPLAY("Closing socket\r\n");
jakeb 15:688b3e3958fd 226 socket.close();
jakeb 15:688b3e3958fd 227 free(path);
jakeb 15:688b3e3958fd 228 }
jakeb 15:688b3e3958fd 229
jakeb 15:688b3e3958fd 230 // never gets here
jakeb 15:688b3e3958fd 231 //EthernetInterface::disconnect();
jakeb 15:688b3e3958fd 232 }
jakeb 15:688b3e3958fd 233
jakeb 15:688b3e3958fd 234 int findOccuranceOfChar(char* buffer, char c, int occur, int max_length) {
jakeb 15:688b3e3958fd 235 int cnt = 0;
jakeb 15:688b3e3958fd 236 for (int x=0; x<max_length; x++) {
jakeb 15:688b3e3958fd 237 if (buffer[x] == c) {
jakeb 15:688b3e3958fd 238 cnt++;
jakeb 15:688b3e3958fd 239 if (cnt == occur) {
jakeb 15:688b3e3958fd 240 return x;
jakeb 15:688b3e3958fd 241 }
jakeb 15:688b3e3958fd 242 }
jakeb 15:688b3e3958fd 243 }
jakeb 15:688b3e3958fd 244 return -1;
jakeb 15:688b3e3958fd 245 }
jakeb 15:688b3e3958fd 246
jakeb 15:688b3e3958fd 247 void sendPageHeader(ClientSocket* sock) {
jakeb 15:688b3e3958fd 248 sock->printf("<html>");
jakeb 15:688b3e3958fd 249 sock->printf("<head><style>");
jakeb 15:688b3e3958fd 250 sock->printf("body {font-family:Helvetica, Arial, sans-serif}");
jakeb 15:688b3e3958fd 251 sock->printf("table { border:1px solid #000; border-collapse:collapse;}");
jakeb 15:688b3e3958fd 252 sock->printf("td {border: 1px solid}");
jakeb 15:688b3e3958fd 253 sock->printf("tr { vertical-align:middle; }");
jakeb 15:688b3e3958fd 254 sock->printf("td.icon { text-align:center; padding:5px; }");
jakeb 15:688b3e3958fd 255 sock->printf("a {color: #000000; text-decoration: none; }");
jakeb 15:688b3e3958fd 256 sock->printf("</style>");
jakeb 15:688b3e3958fd 257 sock->printf("<body>");
jakeb 15:688b3e3958fd 258 }
jakeb 15:688b3e3958fd 259
jakeb 15:688b3e3958fd 260 void sendStatusContent(ClientSocket* sock) {
jakeb 15:688b3e3958fd 261 if (bot) {
jakeb 15:688b3e3958fd 262 Makerbot::MakerbotBuildState state = bot->getBuildState();
jakeb 15:688b3e3958fd 263 sock->printf("<H1>Build Status</H1>");
jakeb 15:688b3e3958fd 264 sock->printf("Machine Name: %s<br>", bot->getMachineName());
jakeb 15:688b3e3958fd 265 sock->printf("Bot firmware version: %0.1f<br><br>", bot->getMakerbotVersion());
jakeb 15:688b3e3958fd 266 sock->printf("<table width=350>");
jakeb 15:688b3e3958fd 267 sock->printf("<tr><td>Build state</td><td>%s</td></tr>", Makerbot::humanReadableBuildState(state));
jakeb 15:688b3e3958fd 268 sock->printf("<tr><Td>Build file name</td><td>%s</td></tr>", bot->getBuildName());
jakeb 15:688b3e3958fd 269 sock->printf("<tr><td>Elapsed Build Time</td><td>%d:%02d</td></tr>", state.hours, state.minutes);
jakeb 15:688b3e3958fd 270 sock->printf("<tr><td>Line Number</td><td>%d</td></tr>", state.line);
jakeb 15:688b3e3958fd 271 sock->printf("</table>");
jakeb 15:688b3e3958fd 272
jakeb 15:688b3e3958fd 273 sock->printf("<H1>Temperatures</H1>");
jakeb 15:688b3e3958fd 274 sock->printf("<table width=350>");
jakeb 15:688b3e3958fd 275 for (int x=0; x<bot->getToolCount(); x++) {
jakeb 15:688b3e3958fd 276 sock->printf("<tr><td>Tool %d</td><td>%d&#176;C</td><td>%d&#176;C</td></tr>", x, bot->getToolTemperature(x), bot->getToolSetPoint(x));
jakeb 15:688b3e3958fd 277 }
jakeb 15:688b3e3958fd 278 if (bot->hasPlatform()) {
jakeb 15:688b3e3958fd 279 sock->printf("<tr><td>Platform</td><td>%d&#176;C</td><td>%d&#176;C</td></tr>", bot->getPlatformTemperature(0), bot->getPlatformSetPoint(0));
jakeb 15:688b3e3958fd 280 }
jakeb 15:688b3e3958fd 281 sock->printf("</table>");
jakeb 15:688b3e3958fd 282 } else {
jakeb 15:688b3e3958fd 283 sock->printf("<H1>No connection to Makerbot</H1>");
jakeb 15:688b3e3958fd 284 }
jakeb 15:688b3e3958fd 285 }
jakeb 15:688b3e3958fd 286
jakeb 15:688b3e3958fd 287 void sendHTTPHeader(ClientSocket* sock, int response_code, char* response_descr, char* contentType) {
jakeb 15:688b3e3958fd 288 sock->printf("HTTP/1.1 %d %s\r\n", response_code, response_descr);
jakeb 15:688b3e3958fd 289 if (contentType != NULL)
jakeb 15:688b3e3958fd 290 sock->printf("Content-Type: %s\r\n", contentType);
jakeb 15:688b3e3958fd 291 sock->printf("\r\n");
jakeb 15:688b3e3958fd 292 }
jakeb 15:688b3e3958fd 293
jakeb 15:688b3e3958fd 294 void sendHTTPRedirect(ClientSocket* sock, char* redirectTo) {
jakeb 15:688b3e3958fd 295 sock->printf("HTTP/1.1 %d %s\r\n", 302, "Found");
jakeb 15:688b3e3958fd 296 sock->printf("Location: %s\r\n", redirectTo);
jakeb 15:688b3e3958fd 297 sock->printf("\r\n");
jakeb 15:688b3e3958fd 298 }
jakeb 15:688b3e3958fd 299
jakeb 15:688b3e3958fd 300 void redirectToMessage(ClientSocket* sock, char* message, int isErr) {
jakeb 15:688b3e3958fd 301 sock->printf("HTTP/1.1 %d %s\r\n", 302, "Found");
jakeb 15:688b3e3958fd 302 char* escapedMsg = url_encode(message);
jakeb 15:688b3e3958fd 303 sock->printf("Location: /?%s=%s\r\n", isErr?"err":"msg", escapedMsg);
jakeb 15:688b3e3958fd 304 free(escapedMsg);
jakeb 15:688b3e3958fd 305 sock->printf("\r\n");
jakeb 15:688b3e3958fd 306 }
jakeb 15:688b3e3958fd 307
jakeb 15:688b3e3958fd 308 void renderMessageBox(ClientSocket* sock, char* message, int isBadMessage) {
jakeb 15:688b3e3958fd 309 if (isBadMessage)
jakeb 15:688b3e3958fd 310 sock->printf("<table><tr><td style='padding: 15px; background: #FF9999'>");
jakeb 15:688b3e3958fd 311 else
jakeb 15:688b3e3958fd 312 sock->printf("<table><tr><td style='padding: 15px; background: #99FF99'>");
jakeb 15:688b3e3958fd 313 sock->printf("%s", message);
jakeb 15:688b3e3958fd 314 sock->printf("</td><tr></table>");
jakeb 15:688b3e3958fd 315
jakeb 15:688b3e3958fd 316 }
jakeb 15:688b3e3958fd 317
jakeb 15:688b3e3958fd 318
jakeb 15:688b3e3958fd 319 void sendCommandTable(ClientSocket* sock) {
jakeb 15:688b3e3958fd 320 if (bot) {
jakeb 15:688b3e3958fd 321 Makerbot::MakerbotBuildState state = bot->getBuildState();
jakeb 15:688b3e3958fd 322 sock->printf("<H1>Commands</H1>");
jakeb 15:688b3e3958fd 323 sock->printf("<table width=500>");
jakeb 15:688b3e3958fd 324 if (state.build_state == Makerbot::BUILD_RUNNING ||
jakeb 15:688b3e3958fd 325 state.build_state == Makerbot::BUILD_PAUSED) {
jakeb 15:688b3e3958fd 326 sock->printf("<tr><td class=icon><a href='/command?cancel'>&#9608</a></td><td><a href='/command?cancel'>Cancel</a></td><td>Halt build immediately (does not clear build plate)</td></tr>");
jakeb 15:688b3e3958fd 327 }
jakeb 15:688b3e3958fd 328 if (state.build_state == Makerbot::BUILD_RUNNING) {
jakeb 15:688b3e3958fd 329 sock->printf("<tr><td class=icon><a href='/command?pause'>&#x275a; &#x275a;</a></td><td><a hred='/command?pause'>Pause</a></td><td>Pause build and clear build area</td></tr>");
jakeb 15:688b3e3958fd 330 }
jakeb 15:688b3e3958fd 331 if (state.build_state == Makerbot::BUILD_PAUSED) {
jakeb 15:688b3e3958fd 332 sock->printf("<tr><td class=icon><a href='/command?resume'>&#9658</a></td><td><a href='/command?resume'>Resume</a></td><td>Resume from pause</td></tr>");
jakeb 15:688b3e3958fd 333 }
jakeb 15:688b3e3958fd 334 if (state.build_state == Makerbot::NO_BUILD ||
jakeb 15:688b3e3958fd 335 state.build_state == Makerbot::BUILD_FINISHED_NORMALLY ||
jakeb 15:688b3e3958fd 336 state.build_state == Makerbot::BUILD_CANCELLED ||
jakeb 15:688b3e3958fd 337 state.build_state == Makerbot::BUILD_SLEEPING) {
jakeb 15:688b3e3958fd 338 sock->printf("<tr><td class=icon>&#9658</td><td>Play</TD><TD><form action='/command' method=get><input length=12 maxlength=12 name=build><input type=submit></form></td></tr>");
jakeb 15:688b3e3958fd 339 }
jakeb 15:688b3e3958fd 340 sock->printf("</table>");
jakeb 15:688b3e3958fd 341 }
jakeb 15:688b3e3958fd 342 }
jakeb 15:688b3e3958fd 343
jakeb 15:688b3e3958fd 344 void sendPageFooter(ClientSocket* sock) {
jakeb 15:688b3e3958fd 345 sock->printf("<br><hr><br>Server firmware version: %s<br>", FIRMWARE_VERSION);
jakeb 15:688b3e3958fd 346 time_t ctTime = time(NULL);
jakeb 15:688b3e3958fd 347 sock->printf("Page generated at %s\r\n", ctime(&ctTime));
jakeb 15:688b3e3958fd 348 sock->printf("</body>");
jakeb 15:688b3e3958fd 349 sock->printf("</html>");
jakeb 15:688b3e3958fd 350 }
jakeb 15:688b3e3958fd 351
jakeb 15:688b3e3958fd 352 // Public domain code from http://www.geekhideout.com/urlcode.shtml
jakeb 15:688b3e3958fd 353 // To the extent possible under law, Fred Bulback has waived all copyright and related or neighboring rights to URL Encoding/Decoding in C. This work is published from: United States.
jakeb 15:688b3e3958fd 354 bool isalnum(char c) {
jakeb 15:688b3e3958fd 355 if (c >= 48 && c <= 57) return 1; //numbers
jakeb 15:688b3e3958fd 356 if (c >= 65 && c <= 90) return 1; //upercase
jakeb 15:688b3e3958fd 357 if (c >= 97 && c <= 122) return 1; //lowercase
jakeb 15:688b3e3958fd 358 return 0;
jakeb 15:688b3e3958fd 359 }
jakeb 15:688b3e3958fd 360
jakeb 15:688b3e3958fd 361 bool isdigit(char c) {
jakeb 15:688b3e3958fd 362 if (c >= 48 && c <= 57) return 1; //numbers
jakeb 15:688b3e3958fd 363 return 0;
jakeb 15:688b3e3958fd 364 }
jakeb 15:688b3e3958fd 365
jakeb 15:688b3e3958fd 366 char tolower(char c) {
jakeb 15:688b3e3958fd 367 if (c >= 65 && c <= 90) {
jakeb 15:688b3e3958fd 368 return c+32;
jakeb 15:688b3e3958fd 369 } else {
jakeb 15:688b3e3958fd 370 return c;
jakeb 15:688b3e3958fd 371 }
jakeb 15:688b3e3958fd 372 }
jakeb 15:688b3e3958fd 373
jakeb 15:688b3e3958fd 374
jakeb 15:688b3e3958fd 375 /* Converts a hex character to its integer value */
jakeb 15:688b3e3958fd 376 char from_hex(char ch) {
jakeb 15:688b3e3958fd 377 return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
jakeb 15:688b3e3958fd 378 }
jakeb 15:688b3e3958fd 379
jakeb 15:688b3e3958fd 380 /* Converts an integer value to its hex character*/
jakeb 15:688b3e3958fd 381 char to_hex(char code) {
jakeb 15:688b3e3958fd 382 static char hex[] = "0123456789abcdef";
jakeb 15:688b3e3958fd 383 return hex[code & 15];
jakeb 15:688b3e3958fd 384 }
jakeb 15:688b3e3958fd 385
jakeb 15:688b3e3958fd 386 /* Returns a url-encoded version of str */
jakeb 15:688b3e3958fd 387 /* IMPORTANT: be sure to free() the returned string after use */
jakeb 15:688b3e3958fd 388 char *url_encode(char *str) {
jakeb 15:688b3e3958fd 389 char *pstr = str, *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf;
jakeb 15:688b3e3958fd 390 while (*pstr) {
jakeb 15:688b3e3958fd 391 if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
jakeb 15:688b3e3958fd 392 *pbuf++ = *pstr;
jakeb 15:688b3e3958fd 393 else if (*pstr == ' ')
jakeb 15:688b3e3958fd 394 *pbuf++ = '+';
jakeb 15:688b3e3958fd 395 else
jakeb 15:688b3e3958fd 396 *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
jakeb 15:688b3e3958fd 397 pstr++;
jakeb 15:688b3e3958fd 398 }
jakeb 15:688b3e3958fd 399 *pbuf = '\0';
jakeb 15:688b3e3958fd 400 return buf;
jakeb 15:688b3e3958fd 401 }
jakeb 15:688b3e3958fd 402
jakeb 15:688b3e3958fd 403 /* Returns a url-decoded version of str */
jakeb 15:688b3e3958fd 404 /* IMPORTANT: be sure to free() the returned string after use */
jakeb 15:688b3e3958fd 405 char *url_decode(char *str) {
jakeb 15:688b3e3958fd 406 char *pstr = str, *buf = (char*)malloc(strlen(str) + 1), *pbuf = buf;
jakeb 15:688b3e3958fd 407 while (*pstr) {
jakeb 15:688b3e3958fd 408 if (*pstr == '%') {
jakeb 15:688b3e3958fd 409 if (pstr[1] && pstr[2]) {
jakeb 15:688b3e3958fd 410 *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
jakeb 15:688b3e3958fd 411 pstr += 2;
jakeb 15:688b3e3958fd 412 }
jakeb 15:688b3e3958fd 413 } else if (*pstr == '+') {
jakeb 15:688b3e3958fd 414 *pbuf++ = ' ';
jakeb 15:688b3e3958fd 415 } else {
jakeb 15:688b3e3958fd 416 *pbuf++ = *pstr;
jakeb 15:688b3e3958fd 417 }
jakeb 15:688b3e3958fd 418 pstr++;
jakeb 15:688b3e3958fd 419 }
jakeb 15:688b3e3958fd 420 *pbuf = '\0';
jakeb 15:688b3e3958fd 421 return buf;
jakeb 15:688b3e3958fd 422 }
jakeb 15:688b3e3958fd 423
jakeb 15:688b3e3958fd 424 // Search through the HTTP headers and if there is an authentication line, process it
jakeb 15:688b3e3958fd 425 // Discard headers that are not relevant.
jakeb 15:688b3e3958fd 426 int handleAuthorizationHeaders(ClientSocket* socket, char* desiredToken) {
jakeb 15:688b3e3958fd 427 char line [HTTP_LINE_BUFFER_SIZE];
jakeb 15:688b3e3958fd 428 int length=0;
jakeb 15:688b3e3958fd 429 char* authToken = NULL;
jakeb 15:688b3e3958fd 430 while ((length = readline(socket, line, HTTP_LINE_BUFFER_SIZE)) > 0) {
jakeb 15:688b3e3958fd 431 if (memcmp(line, "Authorization: Basic ", 21) == 0) {
jakeb 15:688b3e3958fd 432 //we found the authorization line.
jakeb 15:688b3e3958fd 433 authToken = (char*)malloc(length - 21 + 1);
jakeb 15:688b3e3958fd 434 memcpy(authToken, line+21, length-21);
jakeb 15:688b3e3958fd 435 authToken[length-21] = 0; //terminator
jakeb 15:688b3e3958fd 436 break;
jakeb 15:688b3e3958fd 437 } else {
jakeb 15:688b3e3958fd 438 //nothing
jakeb 15:688b3e3958fd 439 }
jakeb 15:688b3e3958fd 440 Thread::yield();
jakeb 15:688b3e3958fd 441 }
jakeb 15:688b3e3958fd 442
jakeb 15:688b3e3958fd 443 if (authToken != NULL) {
jakeb 15:688b3e3958fd 444 int match = memcmp(desiredToken, authToken, strlen(authToken));
jakeb 15:688b3e3958fd 445 free(authToken);
jakeb 15:688b3e3958fd 446 if (match == 0) return 1;
jakeb 15:688b3e3958fd 447 }
jakeb 15:688b3e3958fd 448
jakeb 15:688b3e3958fd 449 return 0;
jakeb 15:688b3e3958fd 450 }
jakeb 15:688b3e3958fd 451
jakeb 15:688b3e3958fd 452 // Discard line by line until a blank line
jakeb 15:688b3e3958fd 453 // (the separator between the header and the body of the HTTP request)
jakeb 15:688b3e3958fd 454 void discardHeaders(ClientSocket* socket) {
jakeb 15:688b3e3958fd 455 if (!socket->available()) {
jakeb 15:688b3e3958fd 456 //printf("nothing available, so not doing anything about the headers\r\n");
jakeb 15:688b3e3958fd 457 return;
jakeb 15:688b3e3958fd 458 }
jakeb 15:688b3e3958fd 459
jakeb 15:688b3e3958fd 460 char line [HTTP_LINE_BUFFER_SIZE];
jakeb 15:688b3e3958fd 461 while (readline(socket, line, HTTP_LINE_BUFFER_SIZE) > 0) {
jakeb 15:688b3e3958fd 462 Thread::yield();
jakeb 15:688b3e3958fd 463 }
jakeb 15:688b3e3958fd 464 }
jakeb 15:688b3e3958fd 465
jakeb 15:688b3e3958fd 466 // Return the base64 representation of the input
jakeb 15:688b3e3958fd 467 // IMPORTANT: caller must call free() on the result when done with it.
jakeb 15:688b3e3958fd 468 char* base64(char* input) {
jakeb 15:688b3e3958fd 469 int inputLen = strlen(input);
jakeb 15:688b3e3958fd 470 int padLength = (3-(inputLen % 3))%3;
jakeb 15:688b3e3958fd 471
jakeb 15:688b3e3958fd 472 int outNdx=0;
jakeb 15:688b3e3958fd 473 char* output = (char*)malloc(((4*inputLen)/3)+1);
jakeb 15:688b3e3958fd 474
jakeb 15:688b3e3958fd 475 for (int i=0; i < inputLen; i+=3) {
jakeb 15:688b3e3958fd 476 //Assemble the 24 bit number
jakeb 15:688b3e3958fd 477 uint32_t a = input[i] << 16;
jakeb 15:688b3e3958fd 478 if (i+1 < inputLen) a += input[i+1] << 8;
jakeb 15:688b3e3958fd 479 if (i+2 < inputLen) a += input[i+2];
jakeb 15:688b3e3958fd 480
jakeb 15:688b3e3958fd 481 //Split the 24 bit number into 4 parts
jakeb 15:688b3e3958fd 482 uint8_t p1 = (uint8_t)(a >> 18) & 0x3F;
jakeb 15:688b3e3958fd 483 uint8_t p2 = (uint8_t)(a >> 12) & 0x3F;
jakeb 15:688b3e3958fd 484 uint8_t p3 = (uint8_t)(a >> 6) & 0x3F;
jakeb 15:688b3e3958fd 485 uint8_t p4 = (uint8_t)a & 0x3F;
jakeb 15:688b3e3958fd 486
jakeb 15:688b3e3958fd 487 //write the data to the output
jakeb 15:688b3e3958fd 488 output[outNdx++] = base64table[p1];
jakeb 15:688b3e3958fd 489 output[outNdx++] = base64table[p2];
jakeb 15:688b3e3958fd 490 output[outNdx++] = base64table[p3];
jakeb 15:688b3e3958fd 491 output[outNdx++] = base64table[p4];
jakeb 15:688b3e3958fd 492 }
jakeb 15:688b3e3958fd 493
jakeb 15:688b3e3958fd 494 // Put the padding in at the end
jakeb 15:688b3e3958fd 495 for (int i=0; i<padLength; i++) {
jakeb 15:688b3e3958fd 496 output[outNdx-i-1]='=';
jakeb 15:688b3e3958fd 497 }
jakeb 15:688b3e3958fd 498
jakeb 15:688b3e3958fd 499 // Terminating \0
jakeb 15:688b3e3958fd 500 output[outNdx]='\0';
jakeb 15:688b3e3958fd 501
jakeb 15:688b3e3958fd 502 return output;
jakeb 15:688b3e3958fd 503 }