Andrew Boyson / web

Dependents:   oldheating gps motorhome heating

web-base.c

Committer:
andrewboyson
Date:
2019-04-27
Revision:
103:91194cc19bbb
Child:
104:40097d08edd5
Child:
105:43ef124233cd

File content as of revision 103:91194cc19bbb:

#include "web-base.h"
#include "web-derived.h"
#include "http.h"
#include "fault.h"
#include "led.h"
#include "mstimer.h"

#define DO_LOGIN            DO_SERVER  +   0
#define DO_FAVICON          DO_SERVER  +   1
#define DO_BASE_CSS         DO_SERVER  +   2
#define DO_NAV_CSS          DO_SERVER  +   3
#define DO_TRACE_HTML       DO_SERVER  +   4
#define DO_TRACE_AJAX       DO_SERVER  +   5
#define DO_TRACE_SCRIPT     DO_SERVER  +   6
#define DO_CLOCK_HTML       DO_SERVER  +   7
#define DO_CLOCK_AJAX       DO_SERVER  +   8
#define DO_CLOCK_SCRIPT     DO_SERVER  +   9
#define DO_NET_HTML         DO_SERVER  +  10
#define DO_NET4_HTML        DO_SERVER  +  11
#define DO_NET4_AJAX        DO_SERVER  +  12
#define DO_NET4_SCRIPT      DO_SERVER  +  13
#define DO_NET6_HTML        DO_SERVER  +  14
#define DO_NET6_AJAX        DO_SERVER  +  15
#define DO_NET6_SCRIPT      DO_SERVER  +  16
#define DO_LOG_HTML         DO_SERVER  +  17
#define DO_FAULT_HTML       DO_SERVER  +  18
#define DO_FIRMWARE_HTML    DO_SERVER  +  19
#define DO_FIRMWARE_AJAX    DO_SERVER  +  20
#define DO_FIRMWARE_SCRIPT  DO_SERVER  +  21
#define DO_1WIRE_HTML       DO_SERVER  +  22
#define DO_1WIRE_AJAX       DO_SERVER  +  23
#define DO_1WIRE_SCRIPT     DO_SERVER  +  24
#define DO_SEND_SESSION_ID  DO_DERIVED + 100

#define LOGIN_DELAY_MS 200

static int decideWhatToDoFromPath(char *pPath, char* pLastModified)
{
    if (HttpSameStr(pPath, "/login"        )) return DO_LOGIN;
    if (HttpSameStr(pPath, "/clock"        )) return DO_CLOCK_HTML;
    if (HttpSameStr(pPath, "/clock-ajax"   )) return DO_CLOCK_AJAX;
    if (HttpSameStr(pPath, "/net"          )) return DO_NET_HTML;
    if (HttpSameStr(pPath, "/net4"         )) return DO_NET4_HTML;
    if (HttpSameStr(pPath, "/net4-ajax"    )) return DO_NET4_AJAX;
    if (HttpSameStr(pPath, "/net6"         )) return DO_NET6_HTML;
    if (HttpSameStr(pPath, "/net6-ajax"    )) return DO_NET6_AJAX;
    if (HttpSameStr(pPath, "/log"          )) return DO_LOG_HTML;
    if (HttpSameStr(pPath, "/trace"        )) return DO_TRACE_HTML;
    if (HttpSameStr(pPath, "/trace-ajax"   )) return DO_TRACE_AJAX;
    if (HttpSameStr(pPath, "/fault"        )) return DO_FAULT_HTML;
    if (HttpSameStr(pPath, "/firmware"     )) return DO_FIRMWARE_HTML;
    if (HttpSameStr(pPath, "/firmware-ajax")) return DO_FIRMWARE_AJAX;
#ifdef INCLUDE_1_WIRE
    if (HttpSameStr(pPath, "/1wire"        )) return DO_1WIRE_HTML;
    if (HttpSameStr(pPath, "/1wire-ajax"   )) return DO_1WIRE_AJAX;
#endif

    if (HttpSameStr(pPath, "/favicon.ico"  )) return HttpSameDate(WebFaviconDate,        WebFaviconTime,        pLastModified) ? DO_NOT_MODIFIED : DO_FAVICON;
    if (HttpSameStr(pPath, "/base.css"     )) return HttpSameDate(WebBaseCssDate,        WebBaseCssTime,        pLastModified) ? DO_NOT_MODIFIED : DO_BASE_CSS;
    if (HttpSameStr(pPath, "/settings.css" )) return HttpSameDate(WebNavCssDate,         WebNavCssTime,         pLastModified) ? DO_NOT_MODIFIED : DO_NAV_CSS;
    if (HttpSameStr(pPath, "/net4.js"      )) return HttpSameDate(WebNet4ScriptDate,     WebNet4ScriptTime,     pLastModified) ? DO_NOT_MODIFIED : DO_NET4_SCRIPT;
    if (HttpSameStr(pPath, "/net6.js"      )) return HttpSameDate(WebNet6ScriptDate,     WebNet6ScriptTime,     pLastModified) ? DO_NOT_MODIFIED : DO_NET6_SCRIPT;
    if (HttpSameStr(pPath, "/trace.js"     )) return HttpSameDate(WebTraceScriptDate,    WebTraceScriptTime,    pLastModified) ? DO_NOT_MODIFIED : DO_TRACE_SCRIPT;
    if (HttpSameStr(pPath, "/clock.js"     )) return HttpSameDate(WebClockScriptDate,    WebClockScriptTime,    pLastModified) ? DO_NOT_MODIFIED : DO_CLOCK_SCRIPT;
    if (HttpSameStr(pPath, "/firmware.js"  )) return HttpSameDate(WebFirmwareScriptDate, WebFirmwareScriptTime, pLastModified) ? DO_NOT_MODIFIED : DO_FIRMWARE_SCRIPT;
#ifdef INCLUDE_1_WIRE
    if (HttpSameStr(pPath, "/1wire.js"     )) return HttpSameDate(WebOneWireScriptDate,  WebOneWireScriptTime,  pLastModified) ? DO_NOT_MODIFIED : DO_1WIRE_SCRIPT;
#endif
    return WebServerDerivedRequest(pPath, pLastModified);
}
static void handleQuery(int todo, char* pQuery)
{
    switch (todo)
    {
        case DO_LOGIN:         WebLoginQuery   (pQuery); return;
        case DO_TRACE_AJAX:    WebTraceQuery   (pQuery); return;
        case DO_CLOCK_AJAX:    WebClockQuery   (pQuery); return;
        case DO_CLOCK_HTML:    WebClockQuery   (pQuery); return;
        case DO_LOG_HTML:      WebLogQuery     (pQuery); return;
        case DO_FAULT_HTML:    WebFaultQuery   (pQuery); return;
        case DO_FIRMWARE_HTML: WebFirmwareQuery(pQuery); return;
        case DO_FIRMWARE_AJAX: WebFirmwareQuery(pQuery); return;
#ifdef INCLUDE_1_WIRE
        case DO_1WIRE_HTML:    WebOneWireQuery (pQuery); return;
        case DO_1WIRE_AJAX:    WebOneWireQuery (pQuery); return;
#endif
    }
    WebServerDerivedGet(todo, pQuery);
}
static bool handlePost(int todo, int contentLength, int contentStart, int size, char* pRequestStream, uint32_t positionInRequestStream)
{
    switch (todo)
    {
        case DO_FIRMWARE_AJAX: return WebFirmwarePost(contentLength, contentStart, size, pRequestStream, positionInRequestStream);
    }
    return WebServerDerivedPost(todo, contentLength, size, pRequestStream, positionInRequestStream);
}

static void serverRequest(int size, char* pRequestStream, uint32_t positionInRequestStream, int* pToDo, bool* pPostComplete, uint32_t* pDelayUntil)
{
    //Handle request for the first packet of data received but leave todo the same after that.
    int contentLength = 0;
    int contentStart  = 0;
    if (size && positionInRequestStream == 0)
    {
        //Read the headers
        char* pMethod;
        char* pPath;
        char* pQuery;
        char* pLastModified;
        char* pCookies;
        contentStart = HttpRequestRead(pRequestStream, size, &pMethod, &pPath, &pQuery, &pLastModified, &pCookies, &contentLength);
        
        //Ask the web server what to do
        *pToDo = decideWhatToDoFromPath(pPath, pLastModified);
        
        //If what to do is NOTHING, NOT_FOUND or NOT_MODIFIED then no query or post will be valid so stop now
        if (*pToDo < DO_SERVER) { *pPostComplete = true; return; }
        
        //If what to do is LOGIN then the user has just returned the login form
        if (*pToDo == DO_LOGIN)
        {
            handleQuery(*pToDo, pQuery);                  //Read the password and the original location
            if (WebLoginQueryPasswordOk)
            {
                if (!WebLoginSessionIdIsSet())           //If there isn't a session id already
                {
                    WebLoginSessionIdNew();              //Create a new session id
                }
                *pToDo =  WebLoginOriginalToDo;          //Load the original todo and SEND_SESSION_ID
                *pToDo += DO_SEND_SESSION_ID;
            }
            *pDelayUntil = MsTimerCount + LOGIN_DELAY_MS; //To prevent brute forcing the hash delay the reply to the login
            *pPostComplete = true;
            return;                                       //Either way no query or post will be valid
        }
        
        //Have a normal request so authenticate
        if (!WebLoginCookiesContainValidSessionId(pCookies))
        {
            WebLoginOriginalToDo = *pToDo; //Record the original destination for redirection
            *pToDo = DO_LOGIN;
            *pPostComplete = true;
            return; //Ignore any query or post as the user is not authenticated
        }
        
        //Handle the query
        handleQuery(*pToDo, pQuery);
    }
    
    //Do the upload of anything that needs it. Todos it doesn't understand are ignored.
    if (!*pPostComplete) *pPostComplete = handlePost(*pToDo, contentLength, contentStart, size, pRequestStream, positionInRequestStream);
}

static void serverReply(int todo)
{
    //Check if todo includes the need to send a cookie
    if (todo >= DO_SEND_SESSION_ID)
    {
        HttpOkCookieName   = WebLoginSessionNameGet();
        HttpOkCookieValue  = WebLoginSessionIdGet();
        HttpOkCookieMaxAge = WebLoginSessionNameLife();
        todo -= DO_SEND_SESSION_ID;
    }
    else
    {
        HttpOkCookieName   = NULL;
        HttpOkCookieValue  = NULL;
        HttpOkCookieMaxAge = -1;
    }
    
    //Try all the base modules
    switch (todo)
    {
        case DO_NOT_FOUND:       HttpNotFound      (); return;
        case DO_NOT_MODIFIED:    HttpNotModified   (); return;
        case DO_LOGIN:           WebLoginHtml     (); return;
        case DO_FAVICON:         WebFavicon       (); return;
        case DO_BASE_CSS:        WebBaseCss       (); return;
        case DO_NAV_CSS:         WebNavCss        (); return;
        case DO_TRACE_HTML:      WebTraceHtml     (); return;
        case DO_TRACE_AJAX:      WebTraceAjax     (); return;
        case DO_TRACE_SCRIPT:    WebTraceScript   (); return;
        case DO_CLOCK_HTML:      WebClockHtml     (); return;
        case DO_CLOCK_AJAX:      WebClockAjax     (); return;
        case DO_CLOCK_SCRIPT:    WebClockScript   (); return;
        case DO_NET_HTML:        WebNetHtml       (); return;
        case DO_NET4_HTML:       WebNet4Html      (); return;
        case DO_NET4_AJAX:       WebNet4Ajax      (); return;
        case DO_NET4_SCRIPT:     WebNet4Script    (); return;
        case DO_NET6_HTML:       WebNet6Html      (); return;
        case DO_NET6_AJAX:       WebNet6Ajax      (); return;
        case DO_NET6_SCRIPT:     WebNet6Script    (); return;
        case DO_LOG_HTML:        WebLogHtml       (); return;
        case DO_FAULT_HTML:      WebFaultHtml     (); return;
        case DO_FIRMWARE_HTML:   WebFirmwareHtml  (); return;
        case DO_FIRMWARE_AJAX:   WebFirmwareAjax  (); return;
        case DO_FIRMWARE_SCRIPT: WebFirmwareScript(); return;
#ifdef INCLUDE_1_WIRE
        case DO_1WIRE_HTML:      WebOneWireHtml   (); return;
        case DO_1WIRE_AJAX:      WebOneWireAjax   (); return;
        case DO_1WIRE_SCRIPT:    WebOneWireScript (); return;
#endif
    }
    
    //If not called then call the derived (child) module
    WebServerDerivedReply(todo);
}

int WebServerInit()
{
    HttpRequestFunction = serverRequest;
    HttpReplyFunction   = serverReply;
    
    WebLoginInit();
    
    return 0;
}