#include "httpserver.h"
#include "lwip/def.h"
#include "lwip/tcp.h"
#include "fsdata.c"
#include "main.h"
#include "flash_if.h"
#include "common_config.h"
#include "conf.h"
#include "settings_api.h"
#include "crc.h"
#include "board.h"
#include "netconf.h"
#ifdef SLAVEBRD_ENABLE
#include "stm32sprog.h"
#endif
#ifdef LCD_ENABLE
#include "lcd.h"
#endif
#include "led.h"
#include "systick.h"
#include <string.h>
#include "tinystdio.h"


#undef DBG
#define DBG if(0)

/* Uncomment this macro to compleately skip any flash read/write operations/
   USE THIS ONLY FOR DEBUG AND TESTING PURPOSES!
   NEVER UNKOMMENT FOR RELEASE BUILDS! */
// #define DUMMY_PROG

#ifdef DUMMY_PROG
/* Disable main fw flash read/write operations */
#define FLASH_If_Write(X, ...)
#define FLASH_If_Erase(X, ...)
#define CRC_Read()              0x00
#define CRC_Calculate(X, ...)   0x00

/* Disable daughter fw flash read/write operations */
#define stmProg(X, ...)
#define stmRebootForFlash(X, ...)
#define stmConnect(X, ...)      ok
#define stmGetDevParams(X, ...) ok
#define stmEraseFW(X, ...)      ok
#define stmReadFlashCrc()       0x00
#define stmCalcFlashCrc(X, ...) 0x00
#endif /* DUMMY_PROG*/

#define FILENAME_MAX_LEN    30
#define BOUNDARY_MAX_LEN    70
#define BLOCK_PROG_TIMEOUT  20000

#define COOKIES_MAX_LEN         64
#define COOKIES_SET_ATTEMPTS    2

#define BD_CONNECT_RETRY    3

/* Max HTTP file name length including "/" */
#define HTTP_FNAME_LEN      32

/* Max HTTP Etag field length */
#define MAX_ETAG_LEN        48

#define DB_FW_SIZE      DB_CPU_FLASH_END_ADDRESS - DB_CPU_FLASH_FIRST_PAGE_ADDRESS + 1

#define HTTP_RET_UPLOAD_ERROR() do {\
                                    memset(sendBuf, 0, 32);\
                                    strcpy(sendBuf, HTTP_200_OK);\
                                    strcat(sendBuf, "0");\
                                    hs->file = sendBuf;\
                                    hs->left = strlen(sendBuf);\
                                    send_data(pcb, hs);\
                                    tcp_sent(pcb, http_sent);\
                                    htmlpage = UploadErrorPage;\
                                    SET_FWINVALID_FLAG();\
                                } while(0)

#define HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE() do {\
                                    memset(sendBuf, 0, 32);\
                                    strcpy(sendBuf, HTTP_200_OK);\
                                    strcat(sendBuf, "0");\
                                    hs->file = sendBuf;\
                                    hs->left = strlen(sendBuf);\
                                    send_data(pcb, hs);\
                                    tcp_sent(pcb, http_sent);\
                                    htmlpage = UploadErrorPage;\
                                } while(0)

static char sendBuf[512];
static char tempBuf[HW_REV_OFFSET + HW_REV_LEN];

extern bool fDoneReset;
extern bool fUpload;
extern bool fInvalidFw;

char lcdbuf[32] = {0};

static vu32 DataFlag = 0;
static vu32 BoundaryFlag = 0;
static vu32 size = 0;
static __IO uint32_t FlashWriteAddress;
static u32 TotalReceived = 0;
static char LeftBytesTab[4];
static u8 LeftBytes = 0;
static uint32_t BrowserFlag = 0;
static uint32_t ContentOffset = 0;
static __IO uint32_t TotalData = 0;
static uint32_t FwDestSize = 0;
static bool fEraseFlash = true;
uint8_t update_timeout = UPDATE_TIMEOUT;

struct http_state
{
  char *file;
  u32_t left;
};

typedef enum {
    fw_unknown,
    fw_main_board,
    fw_daughter_board
} fw_type_t;

static fw_type_t fw_type = fw_unknown;

htmlpageState htmlpage;
 
/* Content Length */
static const char Content_Length[17] = {
    0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20,
};

static const char If_None_Match[] = "If-None-Match: ";
static const char Etag[] = "ETag: ";
const char HTTP_200_OK[] = "HTTP/1.1 200 OK\r\n\r\n";
const char HTTP_304_NOT_MODIFIED[] = "HTTP/1.1 304 Not Modified\r\n\r\n";
const char HTTP_200_OK_TEXT_HTML[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html";
const char Redirect_Head[] = "<!DOCTYPE html><html lang=""><head><meta charset=\"UTF-8\"><meta http-equiv=\"refresh\" ";
const char Msg_Body[] = "<body style=\"background-color:#E8EBF1;\">\
<p style=\"height:300px;line-height:300px;font-family:Arial,Helvetica,sans-serif;text-align:center;color:#333;\">";

uint16_t printLen = 0;
char printBuf[1500];

uint32_t oldAdd = USER_FLASH_FIRST_PAGE_ADDRESS;
uint32_t deltaAdd = 0;

#ifdef SLAVEBRD_ENABLE
uint32_t stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS;

static void IAP_HTTP_stmProg(char *data, uint32_t len);
bool IAP_HTTP_stmConnect(void);
#endif


static uint32_t Parse_Content_Length(char *data, uint32_t len);
void Parse_IfNonMatch(char *data, uint32_t len, char *value);
static uint32_t Parse_Header(char *data, uint32_t len, const char *field, uint32_t flen, char *value);
static bool Parse_Filename(const char *data, uint32_t len, fw_type_t *fw_type, uint32_t *fw_size);
char *Parce_Boundary(const char *data, uint32_t len, char *dst, uint8_t dstlen);
static void IAP_HTTP_writedata(char *data, uint32_t len);
void ProgTimeout_Handler();
/* file must be allocated by caller and will be filled in by the function. */
static int fs_open(char *name, struct fs_file *file);
bool send_file(char *filename, char *pnonmatch, struct tcp_pcb *pcb, struct http_state *hs, struct fs_file *file);
void send_index_page(struct tcp_pcb *pcb, struct http_state *hs, struct pbuf *p, bool upload);
static void PeriodicHandle(uint8_t progress);
/* Get Cookie parameter value */
bool GetCookieValue(char *inStr, char *paramName, char *paramValue, uint8_t *paramLen);
/* Check if Cookie is set or not */
bool CookieIsSet(char *inStr, uint16_t inLen, char *paramName);


// Implements the GNU function memmem much faster (2x) than the standard memmem
char *memmemory(char *haystack, int hlen, char *needle, int nlen)
{
    if (nlen > hlen) return 0;
    int i = 0, j = 0;
    switch (nlen) { // we have a few specialized compares for certain needle sizes
        case 0:     // no needle? just give the haystack
            return haystack;
        case 1: // just use memchr for 1-byte needle
            return memchr(haystack, needle[0], hlen);
        case 2: // use 16-bit compares for 2-byte needles
            for (i = 0; i < hlen - nlen + 1; i++) {
                if (*(uint16_t *)(haystack + i) == *(uint16_t *)needle) {
                    return haystack + i;
                }
            }
            break;
        case 4: // use 32-bit compares for 4-byte needles
            for (i = 0; i < hlen - nlen + 1; i++) {
                if (*(uint32_t *)(haystack + i) == *(uint32_t *)needle) {
                    return haystack + i;
                }
            }
            break;
        /* actually slower on my 32-bit machine
    case 8: // use 64-bit compares for 8-byte needles
        for (i=0; i<hlen-nlen+1; i++) {
            if (*(uint64_t*)(haystack+i)==*(uint64_t*)needle) {
                return haystack+i;
            }
        }
        break;
    */
        default: // generic compare for any other needle size
            // walk i through the haystack, matching j as long as needle[j] matches haystack[i]
            for (i = 0; i < hlen; i++) {
                if (haystack[i] == needle[j]) {
                    if (j == nlen - 1) { // end of needle and it all matched?  win.
                        return haystack + i - j;
                    } else { // keep advancing j (and i, implicitly)
                        j++;
                    }
                } else { // no match, rewind i the length of the failed match (j), and reset j
                    i -= j;
                    j = 0;
                }
            }
    }
    return NULL;
}


/**
  * @brief  callback function for handling connection errors
  * @param  arg: pointer to an argument to be passed to callback function
  * @param  err: LwIP error code
  * @retval none
  */
static void conn_err(void *arg, err_t err)
{
    struct http_state *hs;

    (void)err;

    hs = arg;
    mem_free(hs);
}

/**
  * @brief  closes tcp connection
  * @param  pcb: pointer to a tcp_pcb struct
  * @param  hs: pointer to a http_state struct
  * @retval
  */
static void close_conn(struct tcp_pcb *pcb, struct http_state *hs)
{
    tcp_arg(pcb, NULL);
    tcp_sent(pcb, NULL);
    tcp_recv(pcb, NULL);
    mem_free(hs);
    tcp_close(pcb);
}

/**
  * @brief sends data found in  member "file" of a http_state struct
  * @param pcb: pointer to a tcp_pcb struct
  * @param hs: pointer to a http_state struct
  * @retval none
  */
static void send_data(struct tcp_pcb *pcb, struct http_state *hs)
{
    err_t err;
    u16_t len;

    /* We cannot send more data than space available in the send
     buffer */
    if (tcp_sndbuf(pcb) < hs->left) {
        len = tcp_sndbuf(pcb);
    } else {
        len = hs->left;
    }
    err = tcp_write(pcb, hs->file, len, 0);
    if (err == ERR_OK) {
        hs->file += len;
        hs->left -= len;
    }
}

/**
  * @brief tcp poll callback function
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @retval err_t
  */
static err_t http_poll(void *arg, struct tcp_pcb *pcb)
{
    if (arg == NULL) {
        tcp_close(pcb);
    } else {
        send_data(pcb, (struct http_state *)arg);
    }
    return ERR_OK;
}

/**
  * @brief callback function called after a successfull TCP data packet transmission
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @param len
  * @retval err : LwIP error code
  */
static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
    struct http_state *hs;
    hs = arg;
    (void)len;

    if (hs->left > 0) {
        send_data(pcb, hs);   
    } else {
        close_conn(pcb, hs);

        if (htmlpage == UploadDonePage) {
#ifndef DUMMY_PROG
            fDoneReset = true;
            fUpload = false;
#endif
        } else if (htmlpage == UploadErrorPage) {
            /* Don't reboot if update was failed, just reload main page */
            fUpload = false;
        }
    }
    return ERR_OK;
}


/**
  * @brief callback function for handling TCP HTTP traffic
  * @param arg: pointer to an argument structure to be passed to callback function
  * @param pcb: pointer to a tcp_pcb structure
  * @param p: pointer to a packet buffer
  * @param err: LwIP error code
  * @retval err
  */
/* goback.cgi - возврат в основную прошивку */
/* upload.cgi - загрузка новой прошивки */
static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    int32_t i, len = 0;
    static uint32_t temp_len = 0;
    uint32_t DataOffset, CrcRead = 0, CrcCalc = 0;
    char *data, *ptr;
    struct fs_file file = {0, 0};
    struct http_state *hs;
    static bool hw_validated = false;
    char fname[HTTP_FNAME_LEN];
    uint8_t fname_len;
    static struct tcp_pcb *upload_pcb = NULL;
    static cookie_attempt = 0;

    hs = arg;

    if (err == ERR_OK && p != NULL) {
        /* Inform TCP that we have taken the data */
        tcp_recved(pcb, p->tot_len);

        if (hs->file == NULL) {
            data = p->payload;
            len = p->tot_len;

            printLen = p->tot_len > 500 ? 500 : p->tot_len;
            memcpy(printBuf, p->payload, printLen);
            // DBG printf("\r\n[%d] %s...\r\n", p->tot_len, printBuf);
            // DBG printf("pcb: %p (%p)\r\n", pcb, upload_pcb);

            /* process HTTP GET requests */
            if (strncmp(data, "GET /", 5) == 0 && pcb != upload_pcb) {
                /* Возврат в основную прошивку. Сбрасываем флаг loadmode,
	             * сохраняем настройки и перезагружаемся */
                if (strncmp(data, "GET /goback.cgi", 15) == 0) {
                    /* Check if we realy can go back. Return to IAP if fw update was failed */
                    if (!fInvalidFw) {
                        /* Set loadMode = 0 */
                        RTC_WriteBackupRegister(RTC_BKP_DR1, 0);
                    } else {
                        /* Set loadMode = 1 */
                        RTC_WriteBackupRegister(RTC_BKP_DR1, 1);
                    }
#ifdef SLAVEBRD_ENABLE
                    /* Reboot daughter board */
                    stmReboot();
#endif
                    /* Self reboot */
                    NVIC_SystemReset();
                }
                /* Статус обновления и таймаут до перезагрузки */
                else if (strncmp(data, "GET /status.cgi", 15) == 0) {
                    memset(sendBuf, 0, sizeof(sendBuf));
                    /* Headers для поддержки saffari */
                    sprintf(sendBuf, "HTTP/1.1 200 OK\r\nContent-Type:text/json\r\n\r\n");
                    char buf[32];
                    uint8_t update_state = 0;

                    if (upload_pcb != NULL && fw_type == fw_unknown) {
                        update_state = 1;
                    } else if (upload_pcb != NULL && fw_type == fw_daughter_board) {
                        update_state = 2;
                    } else if (upload_pcb != NULL && fw_type == fw_main_board) {
                        update_state = 3;
                    } else {
                        update_state = 0;
                    }

                    strcat(sendBuf, "{");

                    sprintf(buf, "\"upload\":\"%u\",", (unsigned int)update_state);
                    strcat(sendBuf, buf);

                    sprintf(buf, "\"progress\":\"%u\",", (unsigned int)(TotalReceived * 100 / size));
                    strcat(sendBuf, buf);

                    sprintf(buf, "\"wrtmr\":\"%u\",", timer_GetCountdown(&ProgTimeout_Handler) / 1000);
                    strcat(sendBuf, buf);

                    sprintf(buf, "\"updtmr\":\"%u\",", update_timeout);
                    strcat(sendBuf, buf);

#ifdef SLAVEBRD_ENABLE
                    sprintf(buf, "\"file1\":\"%s\",", DAUGHTER_FW_NAME);
                    strcat(sendBuf, buf);
#endif
                    sprintf(buf, "\"file2\":\"%s\",", MAIN_FW_NAME);
                    strcat(sendBuf, buf);

                   /* sprintf(buf, "\"rev\":\"%s\",", REVISION);
                    strcat(sendBuf, buf); */

                    sprintf(buf, "\"invalidfw\":%s", fInvalidFw ? "true" : "false");
                    strcat(sendBuf, buf);

                    strcat(sendBuf, "}");

                    hs->file = sendBuf;
                    hs->left = strlen(sendBuf);
                    pbuf_free(p);
                    send_data(pcb, hs);
                    tcp_sent(pcb, http_sent);
                }
                else if ((strncmp(data, "GET /upload.html", 16) == 0) &&
                    (!CookieIsSet(p->payload, p->tot_len, "file1=") ||
                     !CookieIsSet(p->payload, p->tot_len, "file2="))) {
                    if (cookie_attempt < COOKIES_SET_ATTEMPTS) {
                        /* Cookies not found. Try to set. */
                        send_index_page(pcb, hs, p, upload_pcb != NULL);
                        cookie_attempt++;
                    } else {
                        cookie_attempt = 0;

                        /* Send Cookie set error message */
                        strcpy(sendBuf, HTTP_200_OK_TEXT_HTML);
                        strcat(sendBuf, "\r\n\r\n");

                        strcat(sendBuf, Redirect_Head);
                        strcat(sendBuf, "</head>");
                        strcat(sendBuf, Msg_Body);

                        strcat(sendBuf, "Включите файлы cookie в браузере и попробуйте <a href=\"index.html\">еще раз.</a></p></body></html>\r\n\r\n");
                        
                        hs->file = sendBuf;
                        hs->left = strlen(sendBuf);
                        pbuf_free(p);
                        send_data(pcb, hs);
                        tcp_sent(pcb, http_sent);
                    }
                }
                /* Check common GET request */
                else if (GetFileName(data, fname, &fname_len)) {
                    char nonmatch[MAX_ETAG_LEN];
                    char *pnonmatch = NULL;

                    /* Parce If-Non_Match value */
                    uint8_t nonmatch_len = Parse_Header(data, p->tot_len, If_None_Match, 15, nonmatch);
                    if (nonmatch_len < MAX_ETAG_LEN && nonmatch_len > 0) {
                        printf("If_None_Match: %s\r\n", nonmatch);
                        pnonmatch = nonmatch;
                    }

                    if (send_file(fname, pnonmatch, pcb, hs, &file)) {
                        pbuf_free(p);
                    } else {
                        send_index_page(pcb, hs, p, upload_pcb != NULL);
                    }
                }
                /* Send default index page */
                else {
                    send_index_page(pcb, hs, p, upload_pcb != NULL);
                }
            }
            /* Process POST request for file upload and incoming data packets after POST request */
            /* Tested MTU sizes: 400 - 1500. Sizes < 400 not supported currently. */
            else if ((strncmp(data, "POST /upload.cgi", 16) == 0 && upload_pcb == NULL) ||
                (DataFlag >= 1 && pcb == upload_pcb)) {
                static char boundary[BOUNDARY_MAX_LEN];
                static char boundary_part[BOUNDARY_MAX_LEN];
                static char *pbound = NULL;

                fUpload = true;
                DataOffset = 0;

#ifndef DUMMY_PROG
                DBG printf("\r\n[%u] ______\r\n", (unsigned int)len);
#endif

                /* POST Packet received */
                if (DataFlag == 0) {
                    BrowserFlag = 0;
                    TotalReceived = 0;
                    BoundaryFlag = 0;
                    temp_len = 0;
                    upload_pcb = pcb;
                    hw_validated = false;
                    fw_type = fw_unknown;

                    /* Reset block receive and prog timeout */
                    timer_Restart(&ProgTimeout_Handler);

                    /* Parse packet for Content-length field */
                    size = Parse_Content_Length(data, p->tot_len);
                    DBG printf("Content_Length: %u\r\n", (unsigned int)size);

                    /* Parse packet for the boundary field */
                    pbound = Parce_Boundary(data, p->tot_len, boundary, sizeof(boundary));
                    DBG printf("boundary: %s\r\n", pbound ? boundary : "header not found");

                    /* Find the boundary */
                    if (pbound != NULL) {
                        for (i = 0; i < len; i++) {
                            if (strncmp((char *)(data + i), boundary, strlen(boundary)) == 0) {
                                ContentOffset = i;
                                BoundaryFlag = 1;
                                DBG printf("boundary found\r\n");
                                /* Parse packet for "\r\n\r\n" field */
                                for (int32_t j = 0; j < len; j++) {
                                    if (strncmp((char *)(data + i + j), "\r\n\r\n", 4) == 0) {
                                        DataOffset = i + j + 4;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    /* TODO Handle partially received boundary */
                    if (BoundaryFlag) {
                        TotalReceived = len - ContentOffset;
                    }

                    DBG {
                        printf("Boundary: %s\r\n", BoundaryFlag ? "yes" : "no");
                        printf("ContentOffset: %u\r\n", (unsigned int)ContentOffset);
                        printf("DataOffset: %u\r\n", (unsigned int)DataOffset);
                        printf("TotalReceived: %u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size);
                    }

                    if (fw_type == fw_unknown) {
                        Parse_Filename(data, len, &fw_type, &FwDestSize);
                    }

                    /* No data in the first POST packet */
                    if (DataOffset == 0) {
                        DataFlag++;
                        BrowserFlag = 1;
                        pbuf_free(p);
                        return ERR_OK;
                    }
                }

                if (((DataFlag == 1) && (BrowserFlag == 1)) || ((DataFlag == 0) && (BrowserFlag == 0))) {
                    if ((DataFlag == 0) && (BrowserFlag == 0)) {
                        DataFlag++;
                    } else if ((DataFlag == 1) && (BrowserFlag == 1)) {
                        /* Find the boundary */
                        for (i = 0; i < len; i++) {
                            if (pbound != NULL) {
                                if (strncmp((char *)(data + i), boundary, strlen(boundary)) == 0 || BoundaryFlag == 1) {
                                    /* Parse packet for "\r\n\r\n" field */
                                    for (int32_t j = 0; j < len; j++) {
                                        if (strncmp((char *)(data + i + j), "\r\n\r\n", 4) == 0) {
                                            DataOffset = i + j + 4;
                                            break;
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                        /* TODO Handle partially received boundary */
                        TotalReceived += len;
                        DataFlag++;
                    }

                    if (fw_type == fw_unknown) {
                        Parse_Filename(data, len, &fw_type, &FwDestSize);
                    }

                    DBG {
                        printf("ContentOffset: %u\r\n", (unsigned int)ContentOffset);
                        printf("DataOffset: %u\r\n", (unsigned int)DataOffset);
                        printf("TotalReceived: %u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size);
                    }

                    /* Get next packet */
                    if (!DataOffset) {
                        DataFlag--;
                        pbuf_free(p);
                        return ERR_OK;
                    }

                    if (fw_type == fw_unknown) {
                        htmlpage = FileUploadPage;
                        /* No filename, in this case reload upload page */
                        fs_open("/index.html", &file);
                        hs->file = file.data;
                        hs->left = file.len;
                        pbuf_free(p);
                        send_data(pcb, hs);
                        tcp_sent(pcb, http_sent);
                        DataFlag = 0;
                        upload_pcb = NULL;

                        printf("Error: Unknown firmware\r\n");

#ifdef LCD_ENABLE
                        LCD_PrintAligned(7, alignCENTER, "Ошибка!");
#endif
                        return ERR_OK;
                    }

                    TotalData = 0;
                }
                /* DataFlag >1 => the packet is data only  */
                else {
                    TotalReceived += len;
                }

                ptr = (char *)(data + DataOffset);
                len -= DataOffset;

                /* Update total data received counter */
                TotalData += len;

#ifndef DUMMY_PROG
                DBG printf("TotalData: +%u %u/%u\r\n", (unsigned int)len,
                    (unsigned int)TotalData, (unsigned int)FwDestSize);
                DBG printf("%u/%u\r\n", (unsigned int)TotalReceived, (unsigned int)size);
#endif

                /* Check if last data packet */
                if (TotalReceived == size) {
                    /* Check if boundary was splited into two packets. Get second part. */
                    /* Data transfer sequence should end with "\r\n" + boundary + "--\rn" (6 extra bytes) */
                    if ((pbound) && (p->tot_len <= strlen(boundary) + 6)) {
                        /* No data in packet */
                        TotalData = TotalData - len;
                        len = 0;
                    } else {
                        char *bptr = memmemory(data, p->tot_len, boundary, strlen(boundary));
                        // DBG printf("boundary: %s\r\n", boundary);

                        if (bptr != NULL) {
                            /* Substruct length of "\r\n" - 2 bytes */
                            uint32_t last_len = bptr - data - 2;
                            TotalData = TotalData - len + last_len;
                            len = last_len;
                        } else {
                            printf("Boundary not found\r\n");
                        }
                    }
                }

                /* Not the last data packet */
                else if (TotalReceived < size) {
                    /* Check if boundary was splited into two packets. Get first part. */
                    /* Data transfer sequence should end with "\r\n" + boundary + "--\rn" (6 extra bytes) */
                    if ((pbound) && (size - TotalReceived < strlen(boundary) + 6)) {
                        uint16_t tail_len = size - TotalReceived;
                        uint16_t boundary_len = strlen(boundary) + 6;

                        /* Find "\r\n" or "\r" depending on a current packet length */
                        uint8_t marker_len = (tail_len - boundary_len >= 2) ? 2 : 1;
                        char *ptr = memmemory(data + p->tot_len - boundary_len, boundary_len,
                            marker_len == 2 ? "\r\n" : "\r", marker_len);

                        if (ptr != NULL) {
                            DBG {
                                /* Boundary first part is not actually used, just for debug output */
                                uint32_t buflen = len - (uint32_t)(ptr + marker_len - data);
                                printf("buflen: %u\r\n", (unsigned int)buflen);
                                if (buflen > BOUNDARY_MAX_LEN) buflen = BOUNDARY_MAX_LEN;
                                memcpy(boundary_part, ptr + marker_len, buflen);
                                printf("boundary part: %s\r\n", boundary_part);
                            }

                            uint32_t last_len = ptr - data;
                            TotalData = TotalData - len + last_len;
                            len = last_len;
                        } else {
                            DBG printf("Boundary part not found!\r\n");
                        }
                    }

                    /* Validate uploaded binary */
                    if ((TotalData >= HW_REV_OFFSET + HW_REV_LEN) && (!hw_validated)) {
                        char rev[HW_REV_LEN];

                        if (fw_type == fw_main_board) {
                            strncpy(rev, HW_REV, HW_REV_LEN);
                        }
#ifdef SLAVEBRD_ENABLE
                        else if (fw_type == fw_daughter_board) {
                            strncpy(rev, HW_REV_UPPER, HW_REV_LEN);
                        }
#endif
                        else {
                            strncpy(rev, "-", HW_REV_LEN);
                        }

                        char bin_rev[HW_REV_LEN];
                        memset(bin_rev, 0, sizeof(bin_rev));
                        uint32_t first_len = temp_len > HW_REV_OFFSET ? temp_len - HW_REV_OFFSET : 0;
                        /* Copy 1st part (if not empty) */
                        memcpy(bin_rev, tempBuf + HW_REV_OFFSET, first_len);
                        /* Copy 2nd part */
                        memcpy(bin_rev + first_len, ptr + HW_REV_OFFSET - (temp_len - first_len), HW_REV_LEN - first_len);

                        if (strncmp(bin_rev, rev, HW_REV_LEN) != 0) {
                            printf("HW version (%s) mismatch: found %s\r\n", rev, bin_rev);
                            printf("Update aborted!\r\n");
#ifdef LCD_ENABLE
                            LCD_PrintAligned(7, alignCENTER, "Ошибка!");
#endif
                            DataFlag = 0;
                            upload_pcb = NULL;

                            HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE();
                            pbuf_free(p);

                            return ERR_OK;
                        } else {
                            printf("HW version (%s) validated\r\n", bin_rev);
                            hw_validated = true;

                            printf("State: Erasing...\r\n");

                            SET_FWINVALID_FLAG();

#ifdef LCD_ENABLE
                            LCD_ClearRow(7);
#ifdef SLAVEBRD_ENABLE
                            sprintf(lcdbuf, "Очистка %d/2 ", fw_type == fw_main_board ? 2 : 1);
                            LCD_PrintAligned(5, alignCENTER, lcdbuf);
#else
                            LCD_PrintAligned(5, alignCENTER, "Очистка...");
#endif
#endif
                            switch (fw_type) {
                                case fw_main_board:

                                    /* Erase flash */
                                    if (fEraseFlash) {
                                        FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
                                        fEraseFlash = false;
                                    }
                                    /* Init flash */
                                    FLASH_If_Init();

                                    FlashWriteAddress = USER_FLASH_FIRST_PAGE_ADDRESS;

                                    break;
#ifdef SLAVEBRD_ENABLE
                                case fw_daughter_board:
                                    stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS;
                                    uint8_t retries = 0;

                                    /* Try to connect to stm32 */
                                    while ((!IAP_HTTP_stmConnect()) && (retries++ < (BD_CONNECT_RETRY - 1))) {
                                        ;
                                    }

                                    /* Error if no device found */
                                    if (retries >= BD_CONNECT_RETRY) {
#ifdef LCD_ENABLE
                                        LCD_PrintAligned(7, alignCENTER, "Ошибка!");
#endif
                                        DataFlag = 0;
                                        upload_pcb = NULL;

                                        HTTP_RET_UPLOAD_ERROR();

                                        pbuf_free(p);

                                        return ERR_OK;
                                    }
                                    break;
#endif
                                default:
                                    break;
                            }
#ifdef LCD_ENABLE
                            LCD_ClearRow(7);
#ifdef SLAVEBRD_ENABLE
                            sprintf(lcdbuf, "Запись %d/2", fw_type == fw_main_board ? 2 : 1);
                            LCD_PrintAligned(5, alignCENTER, lcdbuf);
#else
                            LCD_PrintAligned(5, alignCENTER, "Запись...");
#endif
#endif
                            /*indicate start of flash programming */
                            printf("\r\nState: Programming..\r\n");
                        }
                    }
                }

                if (hw_validated) {
                    /* Check fw data size overflow */
                    if (TotalData > FwDestSize) {
                        printf("FW binary size mismatch. Aborting update..\r\n");
                        printf("FW dest size %u was %u\r\n", (unsigned int)FwDestSize, (unsigned int)TotalData);

                        HTTP_RET_UPLOAD_ERROR_NO_FLASH_ERASE();
                    } else {
                        static uint8_t progress = 0;
                        if (progress != TotalReceived * 100 / size) {
                            progress = TotalReceived * 100 / size;
#ifndef DUMMY_PROG
                            printf("Writing: %u%%\r\n", (unsigned int)progress);
#ifdef LCD_ENABLE
                            LCD_PrintBar(7, (unsigned int)(100 * TotalReceived / size));
#endif
#endif
                        }
                        /* Reset block prog timeout */
                        timer_Restart(&ProgTimeout_Handler);

                        /* Write data in flash */
                        if (len) {
                            switch (fw_type) {
                                case fw_main_board:
                                    if (temp_len) {
                                        IAP_HTTP_writedata(tempBuf, temp_len);
                                        temp_len = 0;
                                    }
                                    IAP_HTTP_writedata(ptr, len);
                                    break;
#ifdef SLAVEBRD_ENABLE
                                case fw_daughter_board:
                                    if (temp_len) {
                                        IAP_HTTP_stmProg(tempBuf, temp_len);
                                        temp_len = 0;
                                    }
                                    IAP_HTTP_stmProg(ptr, len);
                                    break;
#endif
                                default:
                                    break;
                            }
                        }
                    }
                } else {
                    /* Save first part */
                    memcpy(tempBuf + temp_len, ptr, len);
                    temp_len += len;
                    DBG printf("Stashed: %u\r\n", (unsigned int)temp_len);
                }

                if (TotalReceived == size) {
                    /* Stop block receive timer */
                    timer_Stop(&ProgTimeout_Handler);

                    DataFlag = 0;
                    upload_pcb = NULL;

                    printf("State: Verifying..\r\n");
#ifdef LCD_ENABLE
                    LCD_ClearRow(7);
#ifdef SLAVEBRD_ENABLE
                    sprintf(lcdbuf, "Проверка CRC %d/2", fw_type == fw_main_board ? 2 : 1);
                    LCD_PrintAligned(5, alignCENTER, lcdbuf);
#else
                    LCD_PrintAligned(5, alignCENTER, "Проверка CRC...");
#endif
#endif
                    switch (fw_type) {
                        case fw_main_board:
                            CrcRead = CRC_Read();
                            CrcCalc = CRC_Calculate(&PeriodicHandle);
                            break;
#ifdef SLAVEBRD_ENABLE
                        case fw_daughter_board:
                            CrcRead = stmReadFlashCrc();
                            CrcCalc = stmCalcFlashCrc(&PeriodicHandle);
                            break;
#endif
                        default:
                            break;
                    }

                    printf("State: Prog Finished\r\n\r\n");
                    printf("Tot bytes received: %u bytes\r\n", (unsigned int)TotalData);
                    printf("CRC read: 0x%X\r\n", (unsigned int)CrcRead);
                    printf("CRC calcucated: 0x%X\r\n", (unsigned int)CrcCalc);

                    /* Check CRC */
                    if (CrcRead == CrcCalc) {
                        printf("CRC check: Pass\r\n");

                        /* Reset block receive and prog timeout */
                        timer_Restart(&ProgTimeout_Handler);

                        memset(sendBuf, 0, 32);
                        strcpy(sendBuf, HTTP_200_OK);
                        strcat(sendBuf, "1");
                        hs->file = sendBuf;
                        hs->left = strlen(sendBuf);
                        send_data(pcb, hs);
                        tcp_sent(pcb, http_sent);
#ifdef LCD_ENABLE
                        LCD_PrintAligned(7, alignCENTER, "Успешно!");
#endif
                        switch (fw_type) {
                            case fw_main_board:
                                /* Run main FW */
                                htmlpage = UploadDonePage;
                                break;
#ifdef SLAVEBRD_ENABLE
                            case fw_daughter_board:
                                timer_ChangeFrequency(&LED_Blinky_Green, 250);
                                /* Nothing to do */
                                break;
#endif
                            default:
                                break;
                        }
                    } else {
#ifdef LCD_ENABLE
                        LCD_PrintAligned(7, alignCENTER, "Ошибка!");
#endif
                        switch (fw_type) {
                            case fw_main_board:
                                /* Erase flash */
                                /* That's a way not to boot wrong FW next time if update was failed */
                                FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
                                fEraseFlash = false;
                                break;
#ifdef SLAVEBRD_ENABLE
                            case fw_daughter_board:
                                /* We can boot device if upper board update was failed. */
                                break;
#endif
                            default:
                                break;
                        }
                        printf("CRC check: Fail\r\n");

                        HTTP_RET_UPLOAD_ERROR();
                    }
                }
                pbuf_free(p);
            } else {
                /* Bad HTTP requests */
                printf("Bad HTTP request\r\n");

                pbuf_free(p);
                close_conn(pcb, hs);
            }
        } else {
            pbuf_free(p);
            close_conn(pcb, hs);
        }
    } else if (err == ERR_OK && p == NULL) {
        /* Received empty frame */
        printf("Received empty frame\r\n");

        close_conn(pcb, hs);
    }
    return ERR_OK;
}

/**
  * @brief  callback function on TCP connection setup ( on port 80)
  * @param  arg: pointer to an argument structure to be passed to callback function
  * @param  pcb: pointer to a tcp_pcb structure
  * &param  err: Lwip stack error code
  * @retval err
  */
static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
    struct http_state *hs;

    (void)err;
    (void)arg;

    /* Allocate memory for the structure that holds the state of the connection */
    hs = mem_malloc(sizeof(struct http_state));

    if (hs == NULL) {
        return ERR_MEM;
    }

    /* Initialize the structure. */
    hs->file = NULL;
    hs->left = 0;

    /* Tell TCP that this is the structure we wish to be passed for our
     callbacks. */
    tcp_arg(pcb, hs);

    /* Tell TCP that we wish to be informed of incoming data by a call
     to the http_recv() function. */
    tcp_recv(pcb, http_recv);

    tcp_err(pcb, conn_err);

    tcp_poll(pcb, http_poll, 10);
    return ERR_OK;
}

/**
  * @brief  intialize HTTP webserver
  * @param  none
  * @retval none
  */
void IAP_httpd_init(void)
{
    struct tcp_pcb *pcb;
    /*create new pcb*/
    pcb = tcp_new();
    /* bind HTTP traffic to pcb */
    tcp_bind(pcb, IP_ADDR_ANY, 80);
    /* start listening on port 80 */
    pcb = tcp_listen(pcb);
    /* define callback function for TCP connection setup */
    tcp_accept(pcb, http_accept);
    /* Set block receive and prog timeout */
    timer_AddFunction(BLOCK_PROG_TIMEOUT, &ProgTimeout_Handler);
    timer_Stop(&ProgTimeout_Handler);
}

/**
  * @brief  Opens a file defined in fsdata.c ROM filesystem
  * @param  name : pointer to a file name
  * @param  file : pointer to a fs_file structure
  * @retval  1 if success, 0 if fail
  */
static int fs_open(char *name, struct fs_file *file)
{
    struct fsdata_file_noconst *f;

    for (f = (struct fsdata_file_noconst *)FS_ROOT; f != NULL; f = (struct fsdata_file_noconst *)f->next) {
        if (!strcmp(name, f->name)) {
            file->data = f->data;
            file->len = f->len;
            return 1;
        }
    }
    return 0;
}

/**
  * @brief  sends file from flash FS
  * @param  filename: pointer to the file name to send
  * @param  pnonmatch: pointer to the If-Non_Match value
  * @param  pcb: pointer to a tcp_pcb struct
  * @param  hs: pointer to a http_state struct
  * @param  file: pointer to a fs_file struct
  * @retval
  */
bool send_file(char *filename, char *pnonmatch, struct tcp_pcb *pcb, struct http_state *hs, struct fs_file *file)
{
    int res = 0;
    char etag[MAX_ETAG_LEN];
    char *petag = NULL;

    res = fs_open(filename, file);

    if (res == 0) {
        return false;
    }

    /* Find Etag value */
    uint8_t etag_len = Parse_Header(file->data, file->len, Etag, 6, etag);
    if (etag_len < MAX_ETAG_LEN && etag_len > 0) {
        printf("Etag: %s\r\n", etag);
        petag = etag;
    }

    /* Compare Etag and If-Non-Match fields */
    if (pnonmatch && petag && (strcmp(pnonmatch, petag) == 0)) {
        /* Send 304 code */
        sprintf(sendBuf, HTTP_304_NOT_MODIFIED);
        printf(sendBuf);
        hs->file = sendBuf;
        hs->left = strlen(sendBuf);
    } else {
        /* Send file */
        printf("%s\r\n\r\n", filename);
        hs->file = file->data;
        hs->left = file->len;
    }

    send_data(pcb, hs);
    tcp_sent(pcb, http_sent);
    return true;
}

/**
  * @brief
  * @retval None
  */
void send_index_page(struct tcp_pcb *pcb, struct http_state *hs, struct pbuf *p, bool upload)
{
    char buf[32];

    /* Send cookie */
    strcpy(sendBuf, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n");

    strcat(sendBuf, "Set-Cookie: upload=");
    sprintf(buf, "%d", (unsigned char)upload);
    strcat(sendBuf, buf);

    strcat(sendBuf, "\r\nSet-Cookie: wrtmr=");
    sprintf(buf, "%d", timer_GetCountdown(&ProgTimeout_Handler) / 1000);
    strcat(sendBuf, buf);

    strcat(sendBuf, "\r\nSet-Cookie: updtmr=");
    sprintf(buf, "%d", update_timeout);
    strcat(sendBuf, buf);

    strcat(sendBuf, "\r\nSet-Cookie: file1=");
#ifdef SLAVEBRD_ENABLE
    strcat(sendBuf, DAUGHTER_FW_NAME);
#else
    strcat(sendBuf, "NONE");
#endif

    strcat(sendBuf, "\r\nSet-Cookie: file2=");

    char file_name_buf[40];
    memset(file_name_buf, 0, sizeof(file_name_buf));
    strcpy(file_name_buf, MAIN_FW_NAME);

    if(strcmp(file_name_buf, "NONE") != 0){
        strcat(sendBuf, MAIN_FW_NAME);
    } else {
        strcat(sendBuf, " ");
    }

    strcat(sendBuf,
           "\r\n\r\n<!DOCTYPE html><html lang="
           "><head><meta \
            http-equiv=\"refresh\" content=\"0;url=/upload.html\"/></head></html>\r\n\r\n");

    htmlpage = LoginPage;

    hs->file = sendBuf;
    hs->left = strlen(sendBuf);
    pbuf_free(p);
    send_data(pcb, hs);
    tcp_sent(pcb, http_sent);
}

/**
  * @brief  Extract the Content_Length data from HTML data
  * @param  data : pointer on receive packet buffer
  * @param  len  : buffer length
  * @retval size : Content_length in numeric format
  */
static uint32_t Parse_Content_Length(char *data, uint32_t len)
{
    uint32_t i = 0, size = 0, S = 1;
    int32_t j = 0;
    char sizestring[6], *ptr;
    uint32_t ContentLengthOffset = 0;

    /* find Content-Length data in packet buffer */
    for (i = 0; i < len; i++) {
        if (strncmp((char *)(data + i), Content_Length, 16) == 0) {
            ContentLengthOffset = i + 16;
            break;
        }
    }
    /* read Content-Length value */
    if (ContentLengthOffset) {
        i = 0;
        ptr = (char *)(data + ContentLengthOffset);
        while (*(ptr + i) != 0x0d && (i < len - ContentLengthOffset)) {
            sizestring[i] = *(ptr + i);
            i++;
            ContentLengthOffset++;
        }
        if (i > 0) {
            /* transform string data into numeric format */
            for (j = i - 1; j >= 0; j--) {
                size += (sizestring[j] - 0x30) * S;
                S = S * 10;
            }
        }
    }
    return size;
}

/**
  * @brief  Extract the custom field data from HTML data
  * @param  data : pointer on receive packet buffer
  * @param  len  : buffer length
  * @param  field : field name
  * @param  flen : field name length
  * @retval value : pointer for field data
  */
static uint32_t Parse_Header(char *data, uint32_t len, const char *field, uint32_t flen, char *value)
{
    uint32_t i = 0, size = 0;
    char *ptr;
    uint32_t Offset = 0;

    /* Find field name in data buffer */
    for (i = 0; i < len; i++) {
        if (strncmp((char *)(data + i), field, flen) == 0) {
            Offset = i + flen;
            break;
        }
    }
    /* Copy Field value */
    if (Offset) {
        i = 0;
        ptr = (char *)(data + Offset);
        while (*(ptr + i) != 0x0d && (i < len - Offset)) {
            value[i] = *(ptr + i);
            i++;
        }
        value[i] = '\0';
        size = i;
    }
    return size;
}

/**
  * @brief
  * @retval None
  */
char *Parce_Boundary(const char *data, uint32_t len, char *dst, uint8_t dstlen)
{
    char *ptr = NULL;
    char *boundary = NULL;
    uint8_t i = 0;

    for (uint32_t j = 0; j < len; j++) {
        if (strncmp((char *)(data + j), "boundary=", 9) == 0) {
            boundary = (char *)data + j + 9;
            break;
        }
    }

    if (!boundary) return NULL;

    *dst++ = '-';
    *dst++ = '-';

    ptr = boundary;
    while ((*ptr != 0x0d) && (i < dstlen - 4)) {
        *dst++ = *ptr++;
        i++;
    }
    //*dst++ = '-';
    //*dst++ = '-';
    *dst = '\0';

    if (i > 0)
        return boundary;
    else
        return NULL;
}

static bool Parse_Filename(const char *data, uint32_t len, fw_type_t *fw_type, uint32_t *fw_size)
{
    /* Parse packet for the filename field */
    uint32_t FilenameOffset = 0;
    char filename[FILENAME_MAX_LEN];

    char file_name_buf[40];
    memset(file_name_buf, 0, sizeof(file_name_buf));
    strcpy(file_name_buf, MAIN_FW_NAME);
    
    if(strcmp(file_name_buf, "NONE") == 0){
        *fw_type = fw_main_board;
        *fw_size = MAIN_FW_SIZE;
        return true;
    }

    for (uint32_t i = 0; i < len; i++) {
        if (strncmp((char *)(data + i), "filename=", 9) == 0) {
            FilenameOffset = i + 10;
            break;
        }
    }

    if (FilenameOffset) {
        uint32_t i = 0;
        while ((*(data + FilenameOffset + i) != 0x22) && 
            (i < FILENAME_MAX_LEN) &&
            (FilenameOffset + i <= len)) {
            filename[i] = *(data + FilenameOffset + i);
            i++;
        }
        filename[i] = 0x0;
        printf("File: %s\r\n", filename);

        /* Decide which FW is uploaded */
        if (strncmp(filename, MAIN_FW_NAME, 16) == 0) {
            *fw_type = fw_main_board;
            *fw_size = MAIN_FW_SIZE;
            printf("Updating main board FW..\r\n");
        }
#ifdef SLAVEBRD_ENABLE
        else if (strncmp(filename, DAUGHTER_FW_NAME, 16) == 0) {
            *fw_type = fw_daughter_board;
            *fw_size = DB_FW_SIZE;
            printf("Updating daughter FW..\r\n");
        }
#endif
        else {
            *fw_type = fw_unknown;
        }

        return true;
    }

    return false;
}

/**
  * @brief
  * @retval None
  */
bool GetFileName(char *inStr, char *fileName, uint8_t *fileNameLen)
{
    char *beginValue = NULL;
    char *endValue = NULL;
    int len = 0;
    char *strPtr = NULL;

    strPtr = strstr(inStr, "GET");
    if (strPtr == NULL) {
        strPtr = strstr(inStr, "POST");
    }

    if (strPtr == NULL) {
        *fileNameLen = 0;
        return false;
    } else {
        beginValue = strpbrk(strPtr, "/");

        endValue = strpbrk(beginValue, " ");
        if (endValue == NULL) {
            *fileNameLen = 0;
            return false;
        }
        len = endValue - beginValue;
        if (len < HTTP_FNAME_LEN) {
            strncpy(fileName, beginValue, len);
            *fileNameLen = len;
            fileName[len] = '\0';
            return true;
        } else {
            return false;
        }
    }
}

/**
  * @brief  writes received data in flash
  * @param  ptr: data pointer
  * @param  len: data length
  * @retval none
  */
void IAP_HTTP_writedata(char *ptr, uint32_t len)
{
    uint32_t count, i = 0, j = 0;
    /* check if any left bytes from previous packet transfer*/
    /* if it is the case do a concat with new data to create a 32-bit word */
    if (LeftBytes) {
        while (LeftBytes <= 3) {
            if (len > (j + 1)) {
                LeftBytesTab[LeftBytes++] = *(ptr + j);
            } else {
                LeftBytesTab[LeftBytes++] = 0xFF;
            }
            j++;
        }
        FLASH_If_Write(&FlashWriteAddress, (u32 *)(LeftBytesTab), 1);

        LeftBytes = 0;

        /* update data pointer */
        ptr = (char *)(ptr + j);
        len = len - j;
    }

    /* write received bytes into flash */
    count = len / 4;

    /* check if remaining bytes < 4 */
    i = len % 4;
    if (i > 0) {
        if (TotalReceived != size) {
            /* store bytes in LeftBytesTab */
            LeftBytes = 0;
            for (; i > 0; i--)
                LeftBytesTab[LeftBytes++] = *(char *)(ptr + len - i);
        } else
            count++;
    }
    FLASH_If_Write(&FlashWriteAddress, (u32 *)ptr, count);
}

uint32_t ReturnFlashWriteAddress(void)
{
    return FlashWriteAddress;
}

#ifdef SLAVEBRD_ENABLE
/**
  * @brief Connect stm32
  */
bool IAP_HTTP_stmConnect(void)
{
    bool ok = true;

    stmRebootForFlash();

    /* Connect */
    ok = stmConnect();

    if (!ok) {
        printf("Error: device not detected.\n");
        return false;
    } else
        printf("Device connected\n");

    /* Get params */
    ok = stmGetDevParams();
    if (!ok) {
        printf("Error: Device not supported.\n");
        return false;
    }

    /* Erase */
    printf("Erasing.. ");
    ok = stmEraseFW();
    if (ok)
        printf("OK\n");
    else {
        printf("Erase error!\n");
        return false;
    }

    stm32addr = DB_CPU_FLASH_FIRST_PAGE_ADDRESS;

    return true;
}

/**
  * @brief  Prog received data to connected stm32 on uart
  * @param  ptr: data pointer
  * @param  len: data length
  * @retval none
  */
void IAP_HTTP_stmProg(char *ptr, uint32_t len)
{
    uint32_t count, i, j = 0;

    /* check if any left bytes from previous packet transfer*/
    /* if it is the case do a concat with new data to create a 32-bit word */
    if (LeftBytes) {
        while (LeftBytes <= 3) {
            if (len > (j + 1)) {
                LeftBytesTab[LeftBytes++] = *(ptr + j);
            } else {
                LeftBytesTab[LeftBytes++] = 0xFF;
            }
            j++;
        }
        stmProg(&stm32addr, (uint8_t *)(LeftBytesTab), 4);

        LeftBytes = 0;

        /* update data pointer */
        ptr = (char *)(ptr + j);
        len = len - j;
    }

    /* write received bytes into flash */
    count = len / 4;

    /* check if remaining bytes < 4 */
    i = len % 4;
    if (i > 0) {
        if (TotalReceived != size) {
            /* store bytes in LeftBytesTab */
            LeftBytes = 0;
            for (; i > 0; i--) {
                LeftBytesTab[LeftBytes++] = *(char *)(ptr + len - i);
            }
        } else {
            count++;
        }
    }
    stmProg(&stm32addr, (uint8_t *)ptr, count * 4);
}
#endif

void ProgTimeout_Handler()
{
    /* If connection was broken during update,
     * abort process, erase main board flash and restart IAP */
    printf("Program timeout. Seems that connection was broken. Reboot...\r\n");

    SET_FWINVALID_FLAG();

#ifdef LCD_ENABLE
    LCD_PrintAligned(5, alignCENTER, "Таймаут обновления");
    LCD_PrintAligned(7, alignCENTER, "Перезагрузка..");
#endif

    switch (fw_type) {
        case fw_main_board:
            /* Erase flash */
            FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
            break;
#ifdef SLAVEBRD_ENABLE
        case fw_daughter_board:
            /*TODO Should we erase upper board here? */
            break;
#endif
        default:
            break;
    }
#ifdef SLAVEBRD_ENABLE
    /* Reboot daughter board */
    stmReboot();
#endif
    /* Self reboot */
    NVIC_SystemReset();
}

static void PeriodicHandle(uint8_t progress)
{
    (void)progress;
#ifdef LCD_ENABLE
    LCD_PrintBar(7, progress);
#endif
    /* Handle periodic timers for LwIP */
    LwIP_Periodic_Handle(0);
}


/* Get Cookie parameter value */
bool GetCookieValue(char *inStr, char *paramName, char *paramValue, uint8_t *paramLen)
{
    char *beginValue = 0;
    char *endValue = 0;
    char *endValueTemp = 0;
    int len = 0;

    char *strPtr = strstr(inStr, paramName);

    if (strPtr != NULL) {
        beginValue = strpbrk(strPtr, "=");
        endValue = strpbrk(strPtr, ";");
        endValueTemp = strpbrk(strPtr, "\r");
        if (endValueTemp != 0 && endValueTemp < endValue) {
            endValue = endValueTemp;
        }
        if (endValue == 0) {
            endValue = strpbrk(strPtr, "\n");
        }
        len = endValue - beginValue - 1;
        *endValue = '0';
        *beginValue = '0';
        if (paramValue && paramLen) {
            strncpy(paramValue, beginValue + 1, len);
            *paramLen = len;
        }
        return true;
    } else {
        return false;
    }
}

/* Check if Cookie is set or not */
bool CookieIsSet(char *inStr, uint16_t inLen, char *paramName)
{
    static char CookieBuf[COOKIES_MAX_LEN];

    if (!inStr) return false;
    char *CookiePtr = lwip_strnstr(inStr, "Cookie:", inLen);
    if (CookiePtr) {
        char *paramPtr = lwip_strnstr(CookiePtr, paramName, inLen - (inStr - CookiePtr));
        if (paramPtr) {
            strncpy(CookieBuf, paramPtr, sizeof(CookieBuf));
            if (GetCookieValue(CookieBuf, paramName, NULL, NULL)) {
                return true;
            }
        }
    }
    return false;
}


/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
