commit f8345d586f784aee24122694873a81acbb47cd35 Author: cfif Date: Mon May 26 14:41:45 2025 +0300 Init diff --git a/Inc/auth.h b/Inc/auth.h new file mode 100644 index 0000000..adfb76f --- /dev/null +++ b/Inc/auth.h @@ -0,0 +1,19 @@ +// +// Created by cfif on 13.02.23. +// + +#ifndef GONEC_GSM_AUTH_H +#define GONEC_GSM_AUTH_H + +#include "DeviceStorageIni.h" + +typedef enum { + AUTH_NONE = 0, + AUTH_USER = 1, + AUTH_INTEG = 2, + AUTH_ADMIN = 3 +} typeAuth; + +typeAuth Auth(tDeviceStorageIni *storage, char *authStr); + +#endif //GONEC_GSM_AUTH_H diff --git a/Inc/fs_base_func.h b/Inc/fs_base_func.h new file mode 100644 index 0000000..36205c3 --- /dev/null +++ b/Inc/fs_base_func.h @@ -0,0 +1,90 @@ +// +// Created by cfif on 30.01.23. +// +#include "string.h" +#include "inttypes.h" +//#include "stream.h" +#include "stdbool.h" +#include "time.h" +#include "RtcIO.h" +#include "fs_interface.h" +#include "md5alg.h" + +#ifndef GONEC_GSM_FS_BASE_FUNC_H +#define GONEC_GSM_FS_BASE_FUNC_H + +void toAddPrefixStr(char *out, char *in, size_t len); + +bool CopyFileFs_crc_md5(tFs *fs, uint32_t typeCrc, md5a_context *md5ctx, char *filenameTo, char *filenameFrom, + uint32_t offset, uint32_t initCrc32); + + +void getDelFileDirDatTracert(tFs *fs, char *path); +void getPath(char *pathIn, char *pathOut, int count_dir); + +unsigned char crc8(unsigned char *pcBlock, unsigned char len); + +uint8_t SF04_CheckCrc(const uint8_t data[], uint8_t nbrOfBytes); + +void reportScan(char *str, uint32_t AT); + +FRESULT scan_files(char *path, uint32_t *globDirCount, uint32_t *globFileCount); + +void getDelFilesDirDat(tFs *fs, char *path, char *ext); +void getDelFileDirDat(tFs *fs, char *path, char *ext); + +// Получение указателя на расширение файла +char *getVoidExt(char *buf); + +// Получение имени файла лога +size_t getFileNameLog(tRtcIO *rtc, char *buf, size_t len); + +// Создание файла из буфера +bool createFileFromBuf(tFs *fs, char *path_fileName, char *buf, size_t buf_len, bool isCreate); + +// Получение строки дата +size_t getDateStr(tRtcIO *rtc, char *buf, size_t len); + +// Получение максимального id включая поддиректории +uint32_t getNameMaxDirId(tFs *fs, char *path, char *ext); + +// Получение максимального id без поддиректорий +uint32_t getNameMaxFileId(tFs *fs, char *path, char *ext); + +// Получение строки дата +size_t getNameDir(tRtcIO *rtc, char *buf, size_t len); + +// Получение строки дата время в UTC +size_t getDataTimeUTC(time_t *timestamp, char *buf, size_t len); + +// Получение строки дата/время +size_t getDataTime(time_t *timestamp, char *buf, size_t len); + +bool CopyFileFs(tFs *fs, char *filenameTo, char *filenameFrom, uint32_t offset); + +bool CopyFileFsSize(tFs *fs, char *filenameTo, char *filenameFrom, uint32_t offset, uint32_t max); + +bool +CopyFileTrucSizeFs(tFs *fs, uint32_t typeCrc, md5a_context *md5ctx, uint32_t *crc32, uint8_t *md5sum, + uint32_t *crc32sumFile, uint8_t *md5sumFile, + char *filenameTo, char *filenameFrom, uint32_t offset, uint32_t truncBytes, uint32_t size); + +void getDelFilesDirClean(tFs *fs, char *path); + +// Удаление всех файлов в директории +bool delDirFileFs(tFs *fs, char *dir); + +// Удаление всех файлов с директориями +FRESULT deleteAllDir(tFs *fs, char *path); + +// Получене crc +uint32_t GetCrcFileFs(tFs *fs, char *filename, uint32_t offset); + +// Получене crc +uint32_t GetCrcFileisOpenFs(tFs *fs, FIL *fsrc, uint32_t offset); + +void getDelFilesDir(tFs *fs, char *path); + +uint32_t CrcFileFs(tFs *fs, char *filename); + +#endif //GONEC_GSM_FS_BASE_FUNC_H diff --git a/Inc/fsdata.h b/Inc/fsdata.h new file mode 100644 index 0000000..d31550d --- /dev/null +++ b/Inc/fsdata.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_FSDATA_H +#define LWIP_FSDATA_H + +#include "lwip/apps/httpd_opts.h" +#include "lwip/apps/fs.h" + +/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */ +/* content was moved to fs.h to simplify #include structure */ + +#endif /* LWIP_FSDATA_H */ diff --git a/Inc/http_server.h b/Inc/http_server.h new file mode 100644 index 0000000..1242a68 --- /dev/null +++ b/Inc/http_server.h @@ -0,0 +1,35 @@ +// +// Created by cfif on 20.10.22. +// + +#ifndef ARTERY_PROJECT_HTTP_SERVER_H +#define ARTERY_PROJECT_HTTP_SERVER_H + +#include "cmsis_os2.h" + +#define FileTableDescriptorAddress 0x08350000 + +typedef struct { + char name[64]; + uint32_t size; + uint32_t address; +} tFileTableDescriptor; + +#include "RtcArtery.h" +#include "httpd_post.h" +#include "DeviceStorageIni.h" +#include "TaskAdc.h" +#include "Gpios.h" +#include "ModemLog.h" + +void httpd_init(tExternal *external, tRtcIO *rtc, tTaskAdc *taskAdc, tDeviceStorageIni *storage, tFs *fs, + tSpiPorts *spiPorts, + tFirmwareLoader *firmwareMainLoader, + tFirmwareLoader *firmwareBootLoader, + tModemMain *modemMain, osMutexId_t accessHTTP, osMutexId_t accessMODEM, osMutexId_t accessLOG, + osMutexId_t accessLOG2, + tGpios *gpios, tFileLogger *flog, tModemMainLog *modemMainLog); + +//void httpd_init(); + +#endif //ARTERY_PROJECT_HTTP_SERVER_H diff --git a/Inc/httpd.h b/Inc/httpd.h new file mode 100644 index 0000000..e872429 --- /dev/null +++ b/Inc/httpd.h @@ -0,0 +1,255 @@ +/** + * @file + * HTTP server + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * This version of the file has been modified by Texas Instruments to offer + * simple server-side-include (SSI) and Common Gateway Interface (CGI) + * capability. + */ + +#ifndef LWIP_HDR_APPS_HTTPD_H +#define LWIP_HDR_APPS_HTTPD_H + +#include "httpd_opts.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_HTTPD_CGI + +/** + * @ingroup httpd + * Function pointer for a CGI script handler. + * + * This function is called each time the HTTPD server is asked for a file + * whose name was previously registered as a CGI function using a call to + * http_set_cgi_handlers. The iIndex parameter provides the index of the + * CGI within the cgis array passed to http_set_cgi_handlers. Parameters + * pcParam and pcValue provide access to the parameters provided along with + * the URI. iNumParams provides a count of the entries in the pcParam and + * pcValue arrays. Each entry in the pcParam array contains the name of a + * parameter with the corresponding entry in the pcValue array containing the + * value for that parameter. Note that pcParam may contain multiple elements + * with the same name if, for example, a multi-selection list control is used + * in the form generating the data. + * + * The function should return a pointer to a character string which is the + * path and filename of the response that is to be sent to the connected + * browser, for example "/thanks.htm" or "/response/error.ssi". + * + * The maximum number of parameters that will be passed to this function via + * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in + * the incoming HTTP request above this number will be discarded. + * + * Requests intended for use by this CGI mechanism must be sent using the GET + * method (which encodes all parameters within the URI rather than in a block + * later in the request). Attempts to use the POST method will result in the + * request being ignored. + * + */ +typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[], + char *pcValue[]); + +/** + * @ingroup httpd + * Structure defining the base filename (URL) of a CGI and the associated + * function which is to be called when that URL is requested. + */ +typedef struct +{ + const char *pcCGIName; + tCGIHandler pfnCGIHandler; +} tCGI; + +void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers); + +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI + +#if LWIP_HTTPD_CGI_SSI +/* we have to prototype this struct here to make it available for the handler */ +struct fs_file; + +/** Define this generic CGI handler in your application. + * It is called once for every URI with parameters. + * The parameters can be stored to the object passed as connection_state, which + * is allocated to file->state via fs_state_init() from fs_open() or fs_open_custom(). + * Content creation via SSI or complete dynamic files can retrieve the CGI params from there. + */ +extern void httpd_cgi_handler(struct fs_file *file, const char* uri, int iNumParams, + char **pcParam, char **pcValue +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , void *connection_state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); +#endif /* LWIP_HTTPD_CGI_SSI */ + +#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ + +#if LWIP_HTTPD_SSI + +/** + * @ingroup httpd + * Function pointer for the SSI tag handler callback. + * + * This function will be called each time the HTTPD server detects a tag of the + * form in files with extensions mentioned in the g_pcSSIExtensions + * array (currently .shtml, .shtm, .ssi, .xml, .json) where "name" appears as + * one of the tags supplied to http_set_ssi_handler in the tags array. The + * returned insert string, which will be appended after the the string + * "" in file sent back to the client, should be written to pointer + * pcInsert. iInsertLen contains the size of the buffer pointed to by + * pcInsert. The iIndex parameter provides the zero-based index of the tag as + * found in the tags array and identifies the tag that is to be processed. + * + * The handler returns the number of characters written to pcInsert excluding + * any terminating NULL or HTTPD_SSI_TAG_UNKNOWN when tag is not recognized. + * + * Note that the behavior of this SSI mechanism is somewhat different from the + * "normal" SSI processing as found in, for example, the Apache web server. In + * this case, the inserted text is appended following the SSI tag rather than + * replacing the tag entirely. This allows for an implementation that does not + * require significant additional buffering of output data yet which will still + * offer usable SSI functionality. One downside to this approach is when + * attempting to use SSI within JavaScript. The SSI tag is structured to + * resemble an HTML comment but this syntax does not constitute a comment + * within JavaScript and, hence, leaving the tag in place will result in + * problems in these cases. In order to avoid these problems, define + * LWIP_HTTPD_SSI_INCLUDE_TAG as zero in your lwip options file, or use JavaScript + * style block comments in the form / * # name * / (without the spaces). + */ +typedef u16_t (*tSSIHandler)( +#if LWIP_HTTPD_SSI_RAW + const char* ssi_tag_name, +#else /* LWIP_HTTPD_SSI_RAW */ + int iIndex, +#endif /* LWIP_HTTPD_SSI_RAW */ + char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , u16_t current_tag_part, u16_t *next_tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , void *connection_state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + +/** Set the SSI handler function + * (if LWIP_HTTPD_SSI_RAW==1, only the first argument is used) + */ +void http_set_ssi_handler(tSSIHandler pfnSSIHandler, + const char **ppcTags, int iNumTags); + +/** For LWIP_HTTPD_SSI_RAW==1, return this to indicate the tag is unknown. + * In this case, the webserver writes a warning into the page. + * You can also just return 0 to write nothing for unknown tags. + */ +#define HTTPD_SSI_TAG_UNKNOWN 0xFFFF + +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_SUPPORT_POST + +/* These functions must be implemented by the application */ + +/** + * @ingroup httpd + * Called when a POST request has been received. The application can decide + * whether to accept it or not. + * + * @param connection Unique connection identifier, valid until httpd_post_end + * is called. + * @param uri The HTTP header URI receiving the POST request. + * @param http_request The raw HTTP request (the first packet, normally). + * @param http_request_len Size of 'http_request'. + * @param content_len Content-Length from HTTP header. + * @param response_uri Filename of response file, to be filled when denying the + * request + * @param response_uri_len Size of the 'response_uri' buffer. + * @param post_auto_wnd Set this to 0 to let the callback code handle window + * updates by calling 'httpd_post_data_recved' (to throttle rx speed) + * default is 1 (httpd handles window updates automatically) + * @return ERR_OK: Accept the POST request, data may be passed in + * another err_t: Deny the POST request, send back 'bad request'. + */ +err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, + u16_t http_request_len, int content_len, char *response_uri, + u16_t response_uri_len, u8_t *post_auto_wnd); + +/** + * @ingroup httpd + * Called for each pbuf of data that has been received for a POST. + * ATTENTION: The application is responsible for freeing the pbufs passed in! + * + * @param connection Unique connection identifier. + * @param p Received data. + * @return ERR_OK: Data accepted. + * another err_t: Data denied, http_post_get_response_uri will be called. + */ +err_t httpd_post_receive_data(void *connection, struct pbuf *p); + +/** + * @ingroup httpd + * Called when all data is received or when the connection is closed. + * The application must return the filename/URI of a file to send in response + * to this POST request. If the response_uri buffer is untouched, a 404 + * response is returned. + * + * @param connection Unique connection identifier. + * @param response_uri Filename of response file, to be filled when denying the request + * @param response_uri_len Size of the 'response_uri' buffer. + */ +void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len); + +#if LWIP_HTTPD_POST_MANUAL_WND +void httpd_post_data_recved(void *connection, u16_t recved_len); +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +void httpd_init(void); + +#if HTTPD_ENABLE_HTTPS +struct altcp_tls_config; +void httpd_inits(struct altcp_tls_config *conf); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_HTTPD_H */ diff --git a/Inc/httpd_TablesPostGet.h b/Inc/httpd_TablesPostGet.h new file mode 100644 index 0000000..3947b55 --- /dev/null +++ b/Inc/httpd_TablesPostGet.h @@ -0,0 +1,90 @@ +// +// Created by cfif on 13.12.22. +// + +#ifndef GONEC_ARTERY_HTTPD_TABLESPOSTGET_H +#define GONEC_ARTERY_HTTPD_TABLESPOSTGET_H +#include "httpd_post.h" +#include "httpd_get.h" +#include "Settings_General.h" +#include "Settings_Basic_Access.h" +#include "Settings_Frequency_Speed.h" +#include "Settings_Tracert.h" +#include "Settings_Transceiver.h" +#include "Settings_Network.h" +#include "Settings_Date_Time.h" +#include "Settings_RS485_Bluetooth.h" +#include "httpd_post_handlers.h" +#include "httpd_get_handlers.h" +#include "JSONSettings.h" +#include "httpd_types.h" + +typedef enum { + ID_Post_Var = 0, + ID_Post_Message_Create_Cmd = 1, + ID_Post_Message_Create_Msg2 = 2, + ID_Post_Message_Create_Msg3 = 3, + ID_Post_Key_Load = 4, + ID_Post_Update_Web_File = 5, + ID_Post_Update_Main_Init_File = 6, + ID_Post_Update_Main_Write_File = 7, + ID_Post_Update_Write_Crypto = 8, + ID_Post_Update_Write_Modem = 9, + ID_Post_Update_Init_Write_Modem = 10, + ID_Post_Update_Start_Modem = 11, + ID_Post_Update_Init_Write_Crypto = 12, + ID_Post_Update_Start_Crypto = 13, + ID_Post_Update_Start_Main = 14, + ID_Post_Write_Other_File = 15, + ID_Post_sendmsg2 = 16, + ID_Post_Update_Boot_Init_File = 17, + ID_Post_Update_Boot_Write_File = 18 + +} idPost_t; + +typedef enum { + ID_Get_Settings = 0, + ID_Get_Key_File = 1, + ID_Get_Messages_Inbox = 2, + ID_Get_Messages_Outbox = 3, + ID_Get_Messages_Sentbox = 4, + ID_Get_Messages_Content_Inbox = 5, + ID_Get_Messages_Content_Outbox = 6, + ID_Get_Messages_Content_Sentbox = 7, + ID_Get_Messages_Meta_Content_Inbox = 8, + ID_Get_Messages_Meta_Content_Outbox = 9, + ID_Get_Messages_Meta_Content_Sentbox = 10, + ID_Get_Get_Version = 11 +} idGet_t; + +struct tablePostGetSetting { + const void *json; + const char *path; +}; + + +typedef idPostResult_t (*tPostMethod)(tHttpPostSetting *env); +typedef idPostResult_t (*tGetMethod)(tHttpSetting *env); + +struct tablePost { + const char *path; + uint8_t id; +}; + +struct tableGet { + const char *path; + uint8_t id; + tGetMethod method; +}; + +struct tableKeysAddr { + uint32_t addr; + uint8_t countPage; +}; + +extern const struct tablePostGetSetting tablePostGetSettings[]; +extern const struct tablePost tablePosts[]; +extern const struct tableGet tableGets[]; +extern const struct to_json jsonResponse_Post[]; + +#endif //GONEC_ARTERY_HTTPD_TABLESPOSTGET_H diff --git a/Inc/httpd_base_func.h b/Inc/httpd_base_func.h new file mode 100644 index 0000000..73d475e --- /dev/null +++ b/Inc/httpd_base_func.h @@ -0,0 +1,23 @@ +// +// Created by cfif on 12.12.22. +// + +#ifndef GONEC_ARTERY_HTTPD_BASE_FUNC_H +#define GONEC_ARTERY_HTTPD_BASE_FUNC_H + +size_t getDataTimeUTCex(struct tm *dateTime, char *buf, size_t len); + +int extract_path_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max); +int extract_grp_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max); + +uint8_t HexToData(char* string, uint8_t* bytes); +void string2hexString(char* input, int size, char* output); +void StrToStr(char *str1, char *str2); +int extract_utf8_parameters(char *inBuf, char *outBuf, int maxlen); +int extract_uri_ex_parameters(char *params, char *param_names[], char *param_vals[], int max_params); +int extract_ip_ex_parameters(char *params, char *param_names[], char *param_vals[], int max_params); +int extract_mac_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max); + +int b64_decode(const char *in, unsigned char *out, size_t outlen); + +#endif //GONEC_ARTERY_HTTPD_BASE_FUNC_H diff --git a/Inc/httpd_get.h b/Inc/httpd_get.h new file mode 100644 index 0000000..aacf60d --- /dev/null +++ b/Inc/httpd_get.h @@ -0,0 +1,19 @@ +// +// Created by cfif on 10.11.22. +// + +#ifndef GONEC_ARTERY_HTTPD_GET_H +#define GONEC_ARTERY_HTTPD_GET_H + +#include "httpd_types.h" +#include "DeviceStorageIni.h" +#include "SpiPorts.h" +#include "fs_interface.h" +#include "httpd_post.h" +#include "auth.h" + +int httpd_get_begin(tHttpSetting *httpSettings, typeAuth auth, const char *uri, char *params, bool *isContentFile, + char *fileNameContent, char *bufAnswer, /*char *bigBuffer, bool *isBigBuffer,*/ + uint32_t *tot_len); + +#endif //GONEC_ARTERY_HTTPD_GET_H diff --git a/Inc/httpd_get_handlers.h b/Inc/httpd_get_handlers.h new file mode 100644 index 0000000..1812d05 --- /dev/null +++ b/Inc/httpd_get_handlers.h @@ -0,0 +1,61 @@ +// +// Created by cfif on 13.12.22. +// + +#ifndef GONEC_ARTERY_HTTPD_GET_HANDLERS_H +#define GONEC_ARTERY_HTTPD_GET_HANDLERS_H + +#include "httpd_get.h" +#include "httpd_types.h" +#include "json_func.h" +#include "fs_base_func.h" + +idPostResult_t getDirMemToFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir, char *flt); + +// Создание и запись в файл +idPostResult_t +handlerGet_Message_Outbox_CreateTempOrRealFile_Sendmsg(tHttpSetting *env, bool isFile, char *filename, char *buf, + uint32_t buf_len, time_t timestamp, char *to, + uint32_t urgency, uint32_t chSv, uint32_t isfile, uint32_t kvs, + uint32_t type, uint32_t sos); + +idPostResult_t createFileView(tHttpSetting *env, char *dir, char *fileNameContent); + +// Формирование ответа с содержимым директории +idPostResult_t +getDirFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir); + +// Формирование ответа с содержимым директории +idPostResult_t getDirMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, char *flt); + +// Количество файлов в каталоге +int getCountDir(tHttpSetting *env, char *dir, char *flt); + +// Формирование ответа с содержимым файла +idPostResult_t +getFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file, int32_t pos, size_t len); + +// Получение размера файла +idPostResult_t getFileSize(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name); + +// Удаление файла +idPostResult_t delFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name); + +// Удаление всех файлов в директории +idPostResult_t delDirFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name); + +idPostResult_t +getSampleDirMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, uint32_t pos, uint32_t len); + + +// Формирование ответа с содержимым директории +idPostResult_t +getDirExFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir, + uint32_t count, uint32_t pos, uint32_t len); + +// Формирование ответа с содержимым директории +idPostResult_t +getDirExMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, uint32_t count, uint32_t pos, + uint32_t len); + +#endif //GONEC_ARTERY_HTTPD_GET_HANDLERS_H diff --git a/Inc/httpd_post.h b/Inc/httpd_post.h new file mode 100644 index 0000000..a9ae135 --- /dev/null +++ b/Inc/httpd_post.h @@ -0,0 +1,154 @@ +// +// Created by cfif on 10.11.22. +// + +#ifndef GONEC_ARTERY_HTTPD_POST_H +#define GONEC_ARTERY_HTTPD_POST_H + +#include "inttypes.h" +#include "RtcArtery.h" +#include "DeviceStorageIni.h" +#include "httpd_types.h" +#include "SpiPorts.h" +#include "fs_interface.h" +#include +#include "ModemGonec.h" +#include "auth.h" +#include "TaskAdc.h" +#include "Gpios.h" +#include "ModemLog.h" +#include "External.h" + +typedef struct { + +// uint32_t *tot_len; + // Размер контента из файла +// bool *isContentFile; + // Указатель на буфер ответа +// char *bufAnswer; + // Признак использования большлго буфера +// bool *isBigBuffer; + + + struct { + int paramcount; + char *params_names[MAX_POST_GET_PARAMETERS]; + char *params_vals[MAX_POST_GET_PARAMETERS]; + } params_get_uri; + +// struct { +// char filename[MAX_LEN_PATH_FS]; +// char filenamePathContent[MAX_LEN_PATH_FS]; +// } sdcard; + + int idTempFileKey; + int idTempFileOutBox; + + tRtcIO *rtc; + tDeviceStorageIni *storage; + tSpiPorts *spiPorts; + tFs *fs; + tFirmwareLoader *firmwareMainLoader; + tFirmwareLoader *firmwareBootLoader; + tModemMain *modemMain; + tTaskAdc *taskAdc; + + tExternal *external; + + typeAuth auth; + + tFileLogger *flog; + +// tAuthUser authUser; + + // Мьютекс + osMutexId_t accessHTTP; + osMutexId_t accessMODEM; + osMutexId_t accessLOG; + osMutexId_t accessLOG2; + + tGpios *gpios; + + tModemMainLog *modemMainLog; + + + uint32_t countTmp; +/* + struct { + uint32_t key; + uint16_t id; + } FileSort[2048]; +*/ + +} tHttpSetting; + +typedef struct { + struct { + int paramcount; + char *params_names[MAX_POST_GET_PARAMETERS]; + char *params_vals[MAX_POST_GET_PARAMETERS]; + } params_post_uri; + + struct { + char filename[MAX_LEN_PATH_FS]; + uint32_t urgency; + uint32_t chSv; + uint32_t kvs; + char to[MAX_LEN_PATH_FS]; + uint32_t isfile; + uint32_t routing; + } createPostData; + + // Путь к текущему временному файлу +// char temp_filename[MAX_LEN_PATH_FS]; + // Путь к файлу в хранилище +// char real_filename[MAX_LEN_PATH_FS]; + + char *bufAnswer; + tRtcIO *rtc; + tDeviceStorageIni *storage; + tSpiPorts *spiPorts; + tFs *fs; + tFirmwareLoader *firmwareMainLoader; + tFirmwareLoader *firmwareBootLoader; + tModemMain *modemMain; + + tExternal *external; + + // Признак уже сформированной квитанции + bool isAnswerGeneration; + uint32_t lenAnswerGeneration; + + +// tAuthUser *authUser; + + int indexTableSettings; + int responsePostId; + idPostResult_t result; + uint32_t error; + // Текущая длина принятого пакета + //int packet_len; + // Счетчик длины принятых пакетов + int step_packet_len; + // Размер принимаемого контента + int content_len; + + // Смещение для прошивки обновления + //uint32_t offset_update; + + // Идентификатор временного файла + uint32_t idTempFileOutBox; + + // Мьютекс + osMutexId_t accessHTTP; + osMutexId_t accessMODEM; + + tGpios *gpios; + + bool isFirst; + +} tHttpPostSetting; + +extern tHttpSetting httpSettings; + +#endif //GONEC_ARTERY_HTTPD_POST_H diff --git a/Inc/httpd_post_handlers.h b/Inc/httpd_post_handlers.h new file mode 100644 index 0000000..be476d8 --- /dev/null +++ b/Inc/httpd_post_handlers.h @@ -0,0 +1,58 @@ +// +// Created by cfif on 12.12.22. +// + +#ifndef GONEC_ARTERY_HTTPD_POST_HANDLERS_H +#define GONEC_ARTERY_HTTPD_POST_HANDLERS_H + +#include "httpd_post.h" +#include "auth.h" + +typedef enum { + FIRMWARE_XFSB_MAIN = 0, + FIRMWARE_XFSB_BOOT = 1 +} tFirmwareLoaderXFSB; + +// Создание и запись в файл +idPostResult_t handlerPost_Message_Outbox_DynamicWrfile(tHttpPostSetting *env, uint32_t offset, uint32_t len); + +// Сохранение настроек +idPostResult_t handlerPost_Var(tHttpPostSetting *env, typeAuth auth); + +idPostResult_t handlerPost_Update_Web_File(tHttpPostSetting *env); + +// Запись прошивки +// Инициализация прошивки КОНТРОЛЛЕРА +idPostResult_t handlerPost_Update_File_EraseFlash(tHttpPostSetting *env, tFirmwareLoaderXFSB firmwareLoaderXFSB); +// Запись прошивки +idPostResult_t handlerPost_Update_File_WriteFlash(tHttpPostSetting *env, tFirmwareLoaderXFSB firmwareLoaderXFSB); +// Запуск прошивки +idPostResult_t handlerPost_Update_Start_Main(tHttpPostSetting *env); + +// Создание и запись в файл +idPostResult_t handlerPost_Message_Outbox_Wrfile(tHttpPostSetting *env); + +// Запись прошивки криптоплаты +idPostResult_t handlerPost_Update_Crypto_WriteFlash(tHttpPostSetting *env); + +// Запись прошивки модема +idPostResult_t handlerPost_Update_Modem_WriteFlash(tHttpPostSetting *env); + +// Инициализация записи прошивки модема +idPostResult_t handlerPost_Update_Init_Modem_WriteFlash(tHttpPostSetting *env); + +// Запуск модема после прошивки +idPostResult_t handlerPost_Update_Start_Modem(tHttpPostSetting *env); + +// Инициализация записи прошивки крипто-платы +idPostResult_t handlerPost_Update_Init_Crypto_WriteFlash(tHttpPostSetting *env); + +// Запуск крипто-платы после прошивки +idPostResult_t handlerPost_Update_Start_Crypto(tHttpPostSetting *env); + +// Создание и запись в файл +idPostResult_t handlerPost_Write_Other_File(tHttpPostSetting *env); + +// Загрузка ключей +idPostResult_t handlerPost_Key_Load(tHttpPostSetting *env); +#endif //GONEC_ARTERY_HTTPD_POST_HANDLERS_H diff --git a/Inc/httpd_structs.h b/Inc/httpd_structs.h new file mode 100644 index 0000000..20beb51 --- /dev/null +++ b/Inc/httpd_structs.h @@ -0,0 +1,154 @@ +#ifndef LWIP_HTTPD_STRUCTS_H +#define LWIP_HTTPD_STRUCTS_H + +#include "lwip/apps/httpd.h" + +//#define LWIP_HTTPD_DYNAMIC_HEADERS 1 + +#ifdef __cplusplus + extern "C" { +#endif + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** This struct is used for a list of HTTP header strings for various + * Copyright notice & Disclaimer + * + * The software Board Support Package (BSP) that is made available to + * download from Artery official website is the copyrighted work of Artery. + * Artery authorizes customers to use, copy, and distribute the BSP + * software and its related documentation for the purpose of design and + * development in conjunction with Artery microcontrollers. Use of the + * software is governed by this copyright notice and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES, + * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS, + * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR + * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS, + * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * + ************************************************************************** + * filename extensions. */ +typedef struct { + const char *extension; + const char *content_type; +} tHTTPHeader; + +/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and + * RFC 2616 HTTP/1.1 for header field definitions) */ +static const char *const g_psHTTPHeaderStrings[] = { + "HTTP/1.0 200 OK\r\n", + "HTTP/1.0 404 File not found\r\n", + "HTTP/1.0 400 Bad Request\r\n", + "HTTP/1.0 501 Not Implemented\r\n", + "HTTP/1.1 200 OK\r\n", + "HTTP/1.1 404 File not found\r\n", + "HTTP/1.1 400 Bad Request\r\n", + "HTTP/1.1 501 Not Implemented\r\n", + "Content-Length: ", + "Connection: Close\r\n", + "Connection: keep-alive\r\n", + "Connection: keep-alive\r\nContent-Length: ", + "Expires: Mon, 29 Apr 2060 21:44:55 GMT\r\n", + // "Server: "HTTPD_SERVER_AGENT"\r\n", + "\r\n

404: The requested file cannot be found.

\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + , "Connection: keep-alive\r\nContent-Length: 77\r\n\r\n

404: The requested file cannot be found.

\r\n" +#endif +}; + +/* Indexes into the g_psHTTPHeaderStrings array */ +#define HTTP_HDR_OK 0 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */ +#define HTTP_HDR_OK_11 4 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */ +#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/ +#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */ +#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */ +#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/ +#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */ +#define DEFAULT_404_HTML 13 /* default 404 body */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */ +#endif + +#define HTTP_CONTENT_TYPE(contenttype) "Content-Type: "contenttype"\r\n\r\n" +#define HTTP_CONTENT_TYPE_ENCODING(contenttype, encoding) "Content-Type: "contenttype"\r\nContent-Encoding: "encoding"\r\n\r\n" + +#define HTTP_HDR_HTML HTTP_CONTENT_TYPE("text/html") +#define HTTP_HDR_SSI HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache") +#define HTTP_HDR_GIF HTTP_CONTENT_TYPE("image/gif") +#define HTTP_HDR_PNG HTTP_CONTENT_TYPE("image/png") +#define HTTP_HDR_JPG HTTP_CONTENT_TYPE("image/jpeg") +#define HTTP_HDR_BMP HTTP_CONTENT_TYPE("image/bmp") +#define HTTP_HDR_ICO HTTP_CONTENT_TYPE("image/x-icon") +#define HTTP_HDR_APP HTTP_CONTENT_TYPE("application/octet-stream") +#define HTTP_HDR_JS HTTP_CONTENT_TYPE("application/javascript") +#define HTTP_HDR_RA HTTP_CONTENT_TYPE("application/javascript") +#define HTTP_HDR_CSS HTTP_CONTENT_TYPE("text/css") +#define HTTP_HDR_SWF HTTP_CONTENT_TYPE("application/x-shockwave-flash") +#define HTTP_HDR_XML HTTP_CONTENT_TYPE("text/xml") +#define HTTP_HDR_PDF HTTP_CONTENT_TYPE("application/pdf") +#define HTTP_HDR_JSON HTTP_CONTENT_TYPE("application/json") +#define HTTP_HDR_CSV HTTP_CONTENT_TYPE("text/csv") +#define HTTP_HDR_TSV HTTP_CONTENT_TYPE("text/tsv") +#define HTTP_HDR_SVG HTTP_CONTENT_TYPE("image/svg+xml") +#define HTTP_HDR_SVGZ HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip") +#define HTTP_HDR_LOG HTTP_CONTENT_TYPE("text/plain; charset=utf-8") + +#define HTTP_HDR_DEFAULT_TYPE HTTP_CONTENT_TYPE("text/plain") + +/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES + * and http://www.iana.org/assignments/media-types for registered content types + * and subtypes) */ +static const tHTTPHeader g_psHTTPHeaders[] = { + { "html", HTTP_HDR_HTML}, + { "htm", HTTP_HDR_HTML}, + { "shtml", HTTP_HDR_SSI}, + { "shtm", HTTP_HDR_SSI}, + { "ssi", HTTP_HDR_SSI}, + { "gif", HTTP_HDR_GIF}, + { "png", HTTP_HDR_PNG}, + { "jpg", HTTP_HDR_JPG}, + { "bmp", HTTP_HDR_BMP}, + { "ico", HTTP_HDR_ICO}, + { "class", HTTP_HDR_APP}, + { "cls", HTTP_HDR_APP}, + { "js", HTTP_HDR_JS}, + { "ram", HTTP_HDR_RA}, + { "css", HTTP_HDR_CSS}, + { "swf", HTTP_HDR_SWF}, + { "xml", HTTP_HDR_XML}, + { "xsl", HTTP_HDR_XML}, + { "pdf", HTTP_HDR_PDF}, + { "svg", HTTP_HDR_SVG}, + { "log", HTTP_HDR_LOG}, + { "json", HTTP_HDR_JSON} +#ifdef HTTPD_ADDITIONAL_CONTENT_TYPES + /* If you need to add content types not listed here: + * #define HTTPD_ADDITIONAL_CONTENT_TYPES {"ct1", HTTP_CONTENT_TYPE("text/ct1")}, {"exe", HTTP_CONTENT_TYPE("application/exe")} + */ + , HTTPD_ADDITIONAL_CONTENT_TYPES +#endif +}; + +#define NUM_HTTP_HEADERS LWIP_ARRAYSIZE(g_psHTTPHeaders) + +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI +static const char *const g_pcSSIExtensions[] = { + ".shtml", ".shtm", ".ssi", ".xml", ".json" +}; +#define NUM_SHTML_EXTENSIONS LWIP_ARRAYSIZE(g_pcSSIExtensions) +#endif /* LWIP_HTTPD_SSI */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HTTPD_STRUCTS_H */ diff --git a/Inc/httpd_types.h b/Inc/httpd_types.h new file mode 100644 index 0000000..e87c233 --- /dev/null +++ b/Inc/httpd_types.h @@ -0,0 +1,68 @@ +// +// Created by cfif on 13.12.22. +// + +#ifndef GONEC_ARTERY_HTTPD_TYPES_H +#define GONEC_ARTERY_HTTPD_TYPES_H + +#define TRANS_MIN(x, y) (((x) < (y)) ? (x) : (y)) + +// Время удержания мьютекса +#define TIME_MUTEX_HTTP_ACCESS 8000 +#define TIME_MUTEX_MODEM_ACCESS 8000 +#define TIME_MUTEX_LOG_ACCESS 8000 + +// Размер маленького буфера для ответов HTTP +#define LEN_BUF_SMALL_ANSWER_HTTP (28 * 1024) +// Размер маленького буфера для приема HTTP +#define LEN_BUF_SMALL_ANSWER_POST_HTTP (14 * 1024) +// Размер буфера для хранения путей к файлу в файловой системе +#define MAX_LEN_PATH_FS 96 +// Максимальное количество параметров в GET и POST запросах +#define MAX_POST_GET_PARAMETERS 16 +// Счетчики для хранения временных файлов в TEMP +#define MAX_COUNTER_TEMP_FILE 30 + +typedef enum { + ContentBufferSmall = 0, + ContentBufferFile = 1, + ContentBufferSmallAndFile = 2, + ContentBufferBigAndFile = 3 +} tHttpContentBuffer; + +typedef enum { + Post_OK = 0, + Post_PARAM_ERR = 1, + Post_404_ERR = 2, + Post_FS_ERR = 3, + Post_SIZE_ERR = 4, + Post_DATATIME_ERR = 5, + Post_FLASH_ERR = 6, + Post_LOAD_KEY_ERR = 7, + Post_SET_MODEM = 8, + Post_CRC = 9, + Post_SET_VAR = 10, + Post_MUTEX_ERR = 11, + Post_GNSS_ERR = 12, + Post_NO_MODEM_BOOT = 13, + Post_NO_MODEM_MAIN = 14, + Post_NO_CRYPTO_BOOT = 15, + Post_NO_CRYPTO_MAIN = 16, + Post_NO_AUTH = 17, + Post_NO_ALMA = 18, + Post_OLD_ALMA = 19 +} idPostResult_t; + +/* +typedef enum { + Get_OK = 0, + Get_PARAM_ERR = 1, + Get_404_ERR = 2, + Get_FS_ERR = 3, + Get_SIZE_ERR = 4, + Get_MUTEX_ERR = 11 +} idGetResult_t; +*/ +//extern char bigBuffer[LEN_BUF_BIG_ANSWER_HTTP]; + +#endif //GONEC_ARTERY_HTTPD_TYPES_H diff --git a/Inc/lwippools.h b/Inc/lwippools.h new file mode 100644 index 0000000..9fe64e3 --- /dev/null +++ b/Inc/lwippools.h @@ -0,0 +1,41 @@ +// +// Created by cfif on 04.07.23. +// + +/* OPTIONAL: Pools to replace heap allocation + * Optional: Pools can be used instead of the heap for mem_malloc. If + * so, these should be defined here, in increasing order according to + * the pool element size. + * + * LWIP_MALLOC_MEMPOOL(number_elements, element_size) + */ +#if MEM_USE_POOLS +LWIP_MALLOC_MEMPOOL_START +//LWIP_MALLOC_MEMPOOL(10, 256) +//LWIP_MALLOC_MEMPOOL(50, 512) +//LWIP_MALLOC_MEMPOOL(20, 1024) +//LWIP_MALLOC_MEMPOOL(20, 1536) + +//LWIP_MALLOC_MEMPOOL(30, 8) +//LWIP_MALLOC_MEMPOOL(30, 16) +//LWIP_MALLOC_MEMPOOL(30, 32) +//LWIP_MALLOC_MEMPOOL(30, 64) +LWIP_MALLOC_MEMPOOL(20, 128) +//LWIP_MALLOC_MEMPOOL(30, 256) +//LWIP_MALLOC_MEMPOOL(10, 512) +//LWIP_MALLOC_MEMPOOL(10, 1024) +LWIP_MALLOC_MEMPOOL(16, 1536) + +LWIP_MALLOC_MEMPOOL_END +#endif /* MEM_USE_POOLS */ + +/* Optional: Your custom pools can go here if you would like to use + * lwIP's memory pools for anything else. + */ + +//LWIP_MEMPOOL(TCP_PCB, 12, sizeof(struct tcp_pcb), "TCP_PCB") + +//LWIP_MEMPOOL(SYS_MBOX, 22, sizeof(struct sys_mbox_struct), "SYS_MBOX") +//LWIP_MEMPOOL(SYS_SEM, 12, sizeof(struct sys_sem_struct), "SYS_SEM") + + diff --git a/Src/auth.c b/Src/auth.c new file mode 100644 index 0000000..66ec33a --- /dev/null +++ b/Src/auth.c @@ -0,0 +1,38 @@ +// +// Created by cfif on 13.02.23. +// +#include "StorageOnFlash.h" +#include "string.h" +#include "auth.h" + +typeAuth Auth(tDeviceStorageIni *storage, char *authStr) { + char authUser[255]; + char authInteg[255]; + char authAdmin[255]; + + authUser[0] = '\0'; + strcat(authUser, storage->nvm.Settings_General.loginUSER_v); + strcat(authUser, ":"); + strcat(authUser, storage->nvm.Settings_General.UserPass_v); + + authInteg[0] = '\0'; + strcat(authInteg, storage->nvm.Settings_General.loginINTEG_v); + strcat(authInteg, ":"); + strcat(authInteg, storage->nvm.Settings_General.passINTEG_v); + + authAdmin[0] = '\0'; + strcat(authAdmin, storage->nvm.Settings_General.loginADMIN_v); + strcat(authAdmin, ":"); + strcat(authAdmin, storage->nvm.Settings_General.passADMIN_v); + + if (strcmp(authUser, authStr) == 0) + return AUTH_USER; + + if (strcmp(authInteg, authStr) == 0) + return AUTH_INTEG; + + if (strcmp(authAdmin, authStr) == 0) + return AUTH_ADMIN; + + return AUTH_NONE; +} diff --git a/Src/fs.c b/Src/fs.c new file mode 100644 index 0000000..b4db3cb --- /dev/null +++ b/Src/fs.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/apps/httpd_opts.h" +#include "lwip/def.h" +#include "lwip/apps/fs.h" +#include + + +#include HTTPD_FSDATA_FILE + +/*-----------------------------------------------------------------------------------*/ + +#if LWIP_HTTPD_CUSTOM_FILES +int fs_open_custom(struct fs_file *file, const char *name); +void fs_close_custom(struct fs_file *file); +#if LWIP_HTTPD_FS_ASYNC_READ +u8_t fs_canread_custom(struct fs_file *file); +u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_read_custom(struct fs_file *file, char *buffer, int count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + +/*-----------------------------------------------------------------------------------*/ +err_t +fs_open(struct fs_file *file, const char *name) +{ + const struct fsdata_file *f; + + if ((file == NULL) || (name == NULL)) { + return ERR_ARG; + } + +#if LWIP_HTTPD_CUSTOM_FILES + if (fs_open_custom(file, name)) { + file->is_custom_file = 1; + return ERR_OK; + } + file->is_custom_file = 0; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + for (f = FS_ROOT; f != NULL; f = f->next) { + if (!strcmp(name, (const char *)f->name)) { + file->data = (const char *)f->data; + file->len = f->len; + file->index = f->len; +// file->pextension = NULL; + file->flags = f->flags; +#if HTTPD_PRECALCULATED_CHECKSUM + file->chksum_count = f->chksum_count; + file->chksum = f->chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +#if LWIP_HTTPD_FILE_STATE + file->state = fs_state_init(file, name); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + return ERR_OK; + } + } + /* file not found */ + return ERR_VAL; +} + +/*-----------------------------------------------------------------------------------*/ +void +fs_close(struct fs_file *file) +{ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { + fs_close_custom(file); + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + fs_state_free(file, file->state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + LWIP_UNUSED_ARG(file); +} +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg) +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int +fs_read(struct fs_file *file, char *buffer, int count) +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +{ + int read; + if (file->index == file->len) { + return FS_READ_EOF; + } +#if LWIP_HTTPD_FS_ASYNC_READ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { +#if LWIP_HTTPD_FS_ASYNC_READ + return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + return fs_read_custom(file, buffer, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + read = file->len - file->index; + if (read > count) { + read = count; + } + + MEMCPY(buffer, (file->data + file->index), read); + file->index += read; + + return (read); +} +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg) +{ + if (file != NULL) { +#if LWIP_HTTPD_FS_ASYNC_READ +#if LWIP_HTTPD_CUSTOM_FILES + if (!fs_canread_custom(file)) { + if (fs_wait_read_custom(file, callback_fn, callback_arg)) { + return 0; + } + } +#else /* LWIP_HTTPD_CUSTOM_FILES */ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } + return 1; +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +/*-----------------------------------------------------------------------------------*/ +int +fs_bytes_left(struct fs_file *file) +{ + return file->len - file->index; +} diff --git a/Src/fs_base_func.c b/Src/fs_base_func.c new file mode 100644 index 0000000..d8063ad --- /dev/null +++ b/Src/fs_base_func.c @@ -0,0 +1,779 @@ +// +// Created by cfif on 30.01.23. +// +#include "fs_base_func.h" +#include "stdlib.h" +#include "httpd_types.h" +#include "GonetsCrcs.h" + + +unsigned char crc8(unsigned char *pcBlock, unsigned char len) { + unsigned char crc = 0xFF; + unsigned char i; + + while (len--) { + crc ^= *pcBlock++; + + for (i = 0; i < 8; i++) + crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1; + } + + return crc; +} + +//CRC +#define POLYNOMIAL 0x131 //P(x)=x^8+x^5+x^4+1 = 100110001 + +//============================================================ +uint8_t SF04_CheckCrc(const uint8_t data[], uint8_t nbrOfBytes) +//============================================================ +//calculates checksum for n bytes of data +//and compares it with expected checksum +//input: data[] checksum is built based on this data +// nbrOfBytes checksum is built for n bytes of data +// checksum expected checksum +//return: error: CHECKSUM_ERROR = checksum does not match +// 0 = checksum matches +//============================================================ +{ + uint8_t crc = 0; + uint8_t byteCtr; +//calculates 8-Bit checksum with given polynomial + for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr) { + crc ^= (data[byteCtr]); + for (uint8_t bit = 8; bit > 0; --bit) { + if (crc & 0x80) { + crc = (crc << 1) ^ POLYNOMIAL; + } else { crc = (crc << 1); } + } + } + + return crc; +} + +FRESULT scan_files(char *path, uint32_t *globDirCount, uint32_t *globFileCount) { + FRESULT res; + DIR dir; + UINT i; + static FILINFO fno; + + + res = f_opendir(&dir, path); // Open the directory + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); // Read a directory item + if (res != FR_OK || fno.fname[0] == 0) break; // Break on error or end of dir + if (fno.fattrib & AM_DIR) { // It is a directory + i = strlen(path); + strcat(path, "/"); + strcat(path, fno.fname); + ++*globDirCount; + res = scan_files(path, globDirCount, globFileCount); // Enter the directory + if (res != FR_OK) break; + path[i] = 0; + } else { // It is a file. + ++*globFileCount; + } + } + f_closedir(&dir); + } + + return res; +} + +void reportScan(char *str, uint32_t AT) { + char buf[12]; + + char buff[256]; + buff[0] = '\0'; + strcat(buff, "1:/"); + + uint32_t globDirCount = 0; + uint32_t globFileCount = 0; + scan_files(buff, &globDirCount, &globFileCount); + + str[0] = '\0'; + + strcat(str, "АТ-"); + utoa(AT, buf, 10); + strcat(str, buf); + strcat(str, "\n"); + + strcat(str, "Проверка диска \n"); + + strcat(str, "Директорий "); + utoa(globDirCount, buf, 10); + strcat(str, buf); + strcat(str, "\n"); + + strcat(str, "Файлов "); + utoa(globFileCount, buf, 10); + strcat(str, buf); + strcat(str, "\n"); + + strcat(str, "Ошибок 0 \n"); + +} + + +void toAddPrefixStr(char *out, char *in, size_t len) { + + if (len <= strlen(in)) { + out[0] = '\0'; + strcat(out, in); + return; + } + + size_t countPrefix = len - strlen(in); + + for (size_t i = 0; i < countPrefix; ++i) + out[i] = '0'; + + out[countPrefix] = '\0'; + + strcat(out, in); +} + +void getDelFileDir(tFs *fs, char *path) { + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS + MAX_LEN_PATH_FS]; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*"); + while (fr == FR_OK && fno.fname[0]) { + + pathTmp[0] = '\0'; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + + fr = f_unlink_i(fs, pathTmp); + + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); +} + +void getDelFilesDir(tFs *fs, char *path) { + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS + MAX_LEN_PATH_FS]; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*"); + while (fr == FR_OK && fno.fname[0]) { + + if (fno.fattrib & AM_DIR) { + pathTmp[0] = '\0'; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + strcat(pathTmp, "/"); + + getDelFileDir(fs, pathTmp); + } + + pathTmp[0] = '\0'; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + + fr = f_unlink_i(fs, pathTmp); + + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); + + +} + + +bool getDelFileDirClean(tFs *fs, char *path) { + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS + 32]; + bool isClean = true; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*"); + + if (fr == FR_OK && fno.fname[0]) { + isClean = false; + } + fr = f_closedir_i(fs, &dj); + + return isClean; +} + + +void getDelFilesDirClean(tFs *fs, char *path) { + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS + 32]; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*"); + while (fr == FR_OK && fno.fname[0]) { + + if (fno.fattrib & AM_DIR) { + pathTmp[0] = '\0'; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + strcat(pathTmp, "/"); + + if (getDelFileDirClean(fs, pathTmp)) { + fr = f_unlink_i(fs, pathTmp); + } + } + + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); + + +} + +void getDelFileDirDatTracert(tFs *fs, char *path) { + FILINFO fnoInf; + FRESULT frInf; + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS]; + bool isClean = true; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*.DAT"); + + while (fr == FR_OK && fno.fname[0]) { + + pathTmp[0] = 0; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + pathTmp[strlen(pathTmp) - 3] = 'O'; + pathTmp[strlen(pathTmp) - 2] = 'T'; + pathTmp[strlen(pathTmp) - 1] = 'M'; + + frInf = f_stat(pathTmp, &fnoInf); + if (frInf == FR_NO_FILE) { + pathTmp[0] = 0; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + + fr = f_unlink_i(fs, pathTmp); + } + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); + +} + + +void getDelFileDirDatInbox(tFs *fs, char *path, char *ext) { + FILINFO fnoInf; + FRESULT frInf; + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS]; + bool isClean = true; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*.DAT"); + + while (fr == FR_OK && fno.fname[0]) { + + pathTmp[0] = 0; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + pathTmp[strlen(pathTmp) - 3] = ext[0];//'I'; + pathTmp[strlen(pathTmp) - 2] = ext[1];//'T'; + pathTmp[strlen(pathTmp) - 1] = ext[2];//'M'; + + frInf = f_stat(pathTmp, &fnoInf); + if (frInf == FR_NO_FILE) { + pathTmp[0] = 0; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + + fr = f_unlink_i(fs, pathTmp); + } + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); + +} + +void getDelFilesDirDat(tFs *fs, char *path, char *ext) { + FRESULT fr; + DIR dj; + FILINFO fno; + char pathTmp[MAX_LEN_PATH_FS]; + + fr = f_findfirst_i(fs, &dj, &fno, path, "*"); + while (fr == FR_OK && fno.fname[0]) { + + if (fno.fattrib & AM_DIR) { + pathTmp[0] = '\0'; + strcat(pathTmp, path); + strcat(pathTmp, fno.fname); + strcat(pathTmp, "/"); + + getDelFileDirDatInbox(fs, pathTmp, ext); + } + + fr = f_findnext_i(fs, &dj, &fno); + } + fr = f_closedir_i(fs, &dj); + + +} + + +void getPath(char *pathIn, char *pathOut, int count_dir) { + + char tmp[2]; + + int step_dir = 0; + pathOut[0] = 0; + + for (uint32_t i = 0; i < strlen(pathIn); ++i) { + + if (pathIn[i] == '/') + ++step_dir; + + tmp[0] = toupper(pathIn[i]); + tmp[1] = 0; + + strcat(pathOut, tmp); + + if (step_dir == count_dir) + break; + } + +} + + +// Получение указателя на расширение файла +char *getVoidExt(char *buf) { + char *p = buf; + + for (size_t i = 0; i < strlen(buf); ++i) { + if (*p == '.') + return p; + ++p; + } + + return p; +} + + +// Получение имени файла лога +size_t getFileNameLog(tRtcIO *rtc, char *buf, size_t len) { + struct tm dateTime; + rtc->getTM(rtc, &dateTime); + return strftime(buf, len, "%d-%m-%y.LOG", &dateTime); +} + + +// Получение строки дата +size_t getDateStr(tRtcIO *rtc, char *buf, size_t len) { + struct tm dateTime; + rtc->getTM(rtc, &dateTime); + return strftime(buf, len, "%Y%m%d", &dateTime); +} + +// Получение строки дата время в UTC +size_t getDataTimeUTC(time_t *timestamp, char *buf, size_t len) { + struct tm dateTime; + localtime_r(timestamp, &dateTime); + +// return strftime(buf, len, "%FT%T+03:00", dateTime); + return strftime(buf, len, "%FT%T", &dateTime); +} + +// Получение строки дата время в UTC +size_t getDataTimeUTC_Ex(struct tm *dateTime, char *buf, size_t len) { + +// return strftime(buf, len, "%FT%T+03:00", dateTime); + return strftime(buf, len, "%FT%T", dateTime); +} + +// Получение строки дата/время +size_t getDataTime(time_t *timestamp, char *buf, size_t len) { + struct tm dateTime; + localtime_r(timestamp, &dateTime); + + return strftime(buf, len, "%d-%m-%g %T", &dateTime); +} + + +// Удаление всех файлов в директории +bool delDirFileFs(tFs *fs, char *dir) { + FRESULT fr; + DIR dj; + FILINFO fno; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, dir); + + char dirFileScan[MAX_LEN_PATH_FS]; + + fr = f_findfirst_i(fs, &dj, &fno, dirScan, "*"); + + if (fr) + return false; + + while (fr == FR_OK && fno.fname[0]) { + + dirFileScan[0] = '\0'; + strcat(dirFileScan, dirScan); + strcat(dirFileScan, fno.fname); + + fr = f_unlink_i(fs, dirFileScan); + + fr = f_findnext_i(fs, &dj, &fno); + + if (fr) { + fr = f_closedir_i(fs, &dj); + return Post_FS_ERR; + } + + } + fr = f_closedir_i(fs, &dj); + + + return true; +} + + +FRESULT deleteAllDir(tFs *fs, char *path) { + FRESULT res; + FILINFO fno; + DIR dir; + int i; + char *fn; + + res = f_opendir_i(fs, &dir, path); // Открытие директории + if (res == FR_OK) { + i = strlen(path); + for (;;) { + res = f_readdir(&dir, &fno); // Чтение объекта директории + if (res != FR_OK || fno.fname[0] == 0) + break; // Останов цикла при ошибке или при достижении конца списка директории + + fn = fno.fname; + if (fno.fattrib & AM_DIR) { // Это директория + //strcat(path, "/"); + //strcat(path, fn); + + strcat(&path[i], "/"); + strcat(&path[i + 1], fn); + res = deleteAllDir(fs, path); + + f_unlink_i(fs, path); + + if (res != FR_OK) break; + path[i] = 0; + } else { // Это файл + + char pathFile[MAX_LEN_PATH_FS]; + pathFile[0] = '\0'; + strcat(pathFile, path); + strcat(pathFile, "/"); + strcat(pathFile, fn); + f_unlink_i(fs, pathFile); + + } + } + f_closedir_i(fs, &dir); + } + return res; +} + +bool CopyFileFs_crc_md5(tFs *fs, uint32_t typeCrc, md5a_context *md5ctx, char *filenameTo, char *filenameFrom, + uint32_t offset, uint32_t initCrc32) { + FIL fsrc, fdst; // File objects + BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br, bw; // File read/write count + uint32_t crc32 = 0; + uint32_t crc32_update = 0; + uint32_t init = initCrc32; + + fr = f_open_i(fs, &fsrc, filenameFrom, FA_READ); + if (fr) return false; + + fr = f_lseek_i(fs, &fsrc, offset); + if (fr) return false; + + fr = f_open_i(fs, &fdst, filenameTo, FA_OPEN_APPEND | FA_WRITE); + if (fr) return false; + + for (;;) { + f_read_i(fs, &fsrc, buffer, sizeof(buffer), &br); + if (br == 0) break; + + // crc32 + if (typeCrc == 1) { + crc32_update = GonetsCrc32_update(buffer, br, init); + init = crc32_update; + } + + // md5 + if (typeCrc == 2) { + md5a_update(md5ctx, buffer, (int) br); + } + + f_write_i(fs, &fdst, buffer, br, &bw); + if (bw < br) break; + } + + // crc32 + if (typeCrc == 1) { + crc32 = GonetsCrc32_finish(crc32_update); + + fr = f_write_i(fs, &fdst, &crc32, 4, &bw); + } + + // md5 + if (typeCrc == 2) { + uint8_t md5sum[16]; + md5a_finish(md5ctx, md5sum); + + f_write_i(fs, &fdst, md5sum, sizeof(md5sum), &bw); + } + + f_close_i(fs, &fsrc); + f_close_i(fs, &fdst); + + return true; +} + +//BYTE buffer_crc[512 * 20]; + +uint32_t CrcFileFs(tFs *fs, char *filename) { + FIL fsrc; // File objects +// BYTE buffer_crc[512 * 2]; + +// BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br; // File read/write count + + BYTE buffer_crc[512]; + + uint32_t init = 0xFFFFFFFF; + + fr = f_open_i(fs, &fsrc, filename, FA_READ); + + for (;;) { + f_read_i(fs, &fsrc, buffer_crc, sizeof(buffer_crc), &br); + if (br == 0) break; + + init = GonetsCrc32_noreverse_update(buffer_crc, br, init); + + } + + f_close_i(fs, &fsrc); + + init = GonetsCrc32_noreverse_finish(init); + + return init; +} + +bool CopyFileFs(tFs *fs, char *filenameTo, char *filenameFrom, uint32_t offset) { + FIL fsrc, fdst; // File objects + BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br, bw; // File read/write count + + fr = f_open_i(fs, &fsrc, filenameFrom, FA_READ); + if (fr) return false; + + fr = f_lseek_i(fs, &fsrc, offset); + if (fr) return false; + + fr = f_open_i(fs, &fdst, filenameTo, FA_OPEN_APPEND | FA_WRITE); + if (fr) return false; + + for (;;) { + f_read_i(fs, &fsrc, buffer, sizeof(buffer), &br); + if (br == 0) break; + f_write_i(fs, &fdst, buffer, br, &bw); + if (bw < br) break; + } + + f_close_i(fs, &fsrc); + f_close_i(fs, &fdst); + + return true; +} + +bool +CopyFileTrucSizeFs(tFs *fs, uint32_t typeCrc, md5a_context *md5ctx, + uint32_t *crc32sum, uint8_t *md5sum, + uint32_t *crc32sumFile, uint8_t *md5sumFile, + char *filenameTo, char *filenameFrom, + uint32_t offset, uint32_t truncBytes, uint32_t size) { + FIL fsrc, fdst; // File objects + BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br, bw; // File read/write count + + uint32_t crc32_update; + uint32_t init = 0xFFFFFFFF; + + uint32_t count = (size - offset - truncBytes) / sizeof(buffer); + uint32_t tail = (size - offset - truncBytes) % sizeof(buffer); + + fr = f_open_i(fs, &fsrc, filenameFrom, FA_READ); + if (fr) return false; + + fr = f_lseek_i(fs, &fsrc, offset); + if (fr) return false; + + fr = f_open_i(fs, &fdst, filenameTo, FA_OPEN_APPEND | FA_WRITE); + if (fr) return false; + + for (uint32_t i = 0; i < count; ++i) { + f_read_i(fs, &fsrc, buffer, sizeof(buffer), &br); + + // crc32 + if (typeCrc == 1) { + crc32_update = GonetsCrc32_update(buffer, br, init); + init = crc32_update; + } + + // md5 + if (typeCrc == 2) { + md5a_update(md5ctx, buffer, (int) br); + } + + if (br == 0) break; + f_write_i(fs, &fdst, buffer, br, &bw); + if (bw < br) break; + } + + f_read_i(fs, &fsrc, buffer, tail, &br); + + // crc32 + if (typeCrc == 1) { + crc32_update = GonetsCrc32_update(buffer, br, init); + init = crc32_update; + } + + // md5 + if (typeCrc == 2) { + md5a_update(md5ctx, buffer, (int) br); + } + + f_write_i(fs, &fdst, buffer, br, &bw); + + + // crc32 + if (typeCrc == 1) { + f_read_i(fs, &fsrc, crc32sumFile, 4, &br); + *crc32sum = GonetsCrc32_finish(crc32_update); + } + + // md5 + if (typeCrc == 2) { + f_read_i(fs, &fsrc, md5sumFile, 16, &br); + md5a_finish(md5ctx, md5sum); + } + + + f_close_i(fs, &fsrc); + f_close_i(fs, &fdst); + + return true; +} + + +// Получене crc +uint32_t GetCrcFileisOpenFs(tFs *fs, FIL *fsrc, uint32_t offset) { + BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br; // File read/write count + uint32_t crc = 0; + + fr = f_lseek_i(fs, fsrc, offset); + if (fr) return false; + + for (;;) { + f_read_i(fs, fsrc, buffer, sizeof(buffer), &br); + + for (size_t i = 0; i < br; ++i) { + crc += buffer[i]; + } + + if (br == 0) break; + } + + return crc; +} + + +// Получене crc +uint32_t GetCrcFileFs(tFs *fs, char *filename, uint32_t offset) { + FIL fsrc; // File objects + BYTE buffer[512]; // File copy buffer + FRESULT fr; // FatFs function common result code + UINT br; // File read/write count + uint32_t crc = 0; + + fr = f_open_i(fs, &fsrc, filename, FA_READ); + if (fr) return 0; + + fr = f_lseek_i(fs, &fsrc, offset); + if (fr) return false; + + for (;;) { + fr = f_read_i(fs, &fsrc, buffer, sizeof(buffer), &br); + + for (size_t i = 0; i < br; ++i) { + crc += buffer[i]; + } + + if (fr) { + return 0; + } + + if (br == 0) + break; + } + + f_close_i(fs, &fsrc); + + return crc; +} + +/* +BYTE bufferSizeMaxCopy[1300]; // File copy buffer + +bool CopyFileFsSize(tFs *fs, char *filenameTo, char *filenameFrom, uint32_t offset, uint32_t max) { + FIL fsrc, fdst; // File objects + FRESULT fr; // FatFs function common result code + UINT br, bw; // File read/write count + + fr = f_open_i(fs, &fsrc, filenameFrom, FA_READ); + if (fr) return false; + + fr = f_lseek_i(fs, &fsrc, offset); + if (fr) return false; + + fr = f_open_i(fs, &fdst, filenameTo, FA_OPEN_APPEND | FA_WRITE); + if (fr) return false; + +// for (;;) { + f_read_i(fs, &fsrc, bufferSizeMaxCopy, max, &br); +// if (br == 0) break; + f_write_i(fs, &fdst, bufferSizeMaxCopy, br, &bw); +// if (bw < br) break; +// } + + f_close_i(fs, &fsrc); + f_close_i(fs, &fdst); + + return true; +} +*/ + + diff --git a/Src/httpd.c b/Src/httpd.c new file mode 100644 index 0000000..a97907b --- /dev/null +++ b/Src/httpd.c @@ -0,0 +1,3946 @@ +/** + * @file + * LWIP HTTP server implementation + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +/** + * @defgroup httpd HTTP server + * @ingroup apps + * + * This httpd supports for a + * rudimentary server-side-include facility which will replace tags of the form + * in any file whose extension is .shtml, .shtm or .ssi with + * strings provided by an include handler whose pointer is provided to the + * module via function http_set_ssi_handler(). + * Additionally, a simple common + * gateway interface (CGI) handling mechanism has been added to allow clients + * to hook functions to particular request URIs. + * + * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. + * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. + * + * By default, the server assumes that HTTP headers are already present in + * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in + * lwipopts.h, this behavior can be changed such that the server inserts the + * headers automatically based on the extension of the file being served. If + * this mode is used, be careful to ensure that the file system image used + * does not already contain the header information. + * + * File system images without headers can be created using the makefsfile + * tool with the -h command line option. + * + * + * Notes about valid SSI tags + * -------------------------- + * + * The following assumptions are made about tags used in SSI markers: + * + * 1. No tag may contain '-' or whitespace characters within the tag name. + * 2. Whitespace is allowed between the tag leadin "". + * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. + * + * Notes on CGI usage + * ------------------ + * + * The simple CGI support offered here works with GET method requests only + * and can handle up to 16 parameters encoded into the URI. The handler + * function may not write directly to the HTTP output but must return a + * filename that the HTTP server will send to the browser as a response to + * the incoming CGI request. + * + * + * + * The list of supported file types is quite short, so if makefsdata complains + * about an unknown extension, make sure to add it (and its doctype) to + * the 'g_psHTTPHeaders' list. + */ +#include "lwip/init.h" +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/apps/fs.h" +#include "httpd_structs.h" +#include "lwip/def.h" +#include "httpd_post.h" +#include "httpd_get.h" +#include +#include "httpd_base_func.h" +#include "SystemDelayInterface.h" +#include "json_func.h" +#include "ModemGonec.h" +#include "auth.h" +#include "fs_base_func.h" + +#include "lwip/altcp.h" +#include "lwip/altcp_tcp.h" + +#if HTTPD_ENABLE_HTTPS +#include "lwip/altcp_tls.h" +#endif +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif +#if LWIP_HTTPD_TIMING +#include "lwip/sys.h" +#endif /* LWIP_HTTPD_TIMING */ + +#include /* memset */ +#include /* atoi */ +#include + +//#include "at32f4xx_adc.h" +//#include "at32_board.h" + +extern char bufLwIpWebStats[4096]; +uint32_t webup_status = 255; + +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_DE; // desciptor error: no more descriptors for receive frame +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_OE; // overflow error: frame was damaged due to buffer overflow +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_IPV4HCE; // ipc checksum error: rx ipv4 header checksum error +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_LC; // late collision occurred during reception +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_RWT; // receive watchdog timeout: watchdog timer expired during reception +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_RE; // receive error: error reported by mii interface +extern volatile uint32_t EMAC_COUNTER_DMARXDESC_CE; // crc error + +#if LWIP_TCP && LWIP_CALLBACK_API + +/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ +#define MIN_REQ_LEN 7 + +#define CRLF "\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" +#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive" +#endif + +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL) +#else +#define HTTP_IS_DYNAMIC_FILE(hs) 0 +#endif + +/* This defines checks whether tcp_write has to copy data or not */ + +#ifndef HTTP_IS_DATA_VOLATILE +/** tcp_write does not have to copy data when sent from rom-file-system directly */ +#define HTTP_IS_DATA_VOLATILE(hs) (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0) +#endif +/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */ +#ifndef HTTP_IS_HDR_VOLATILE +#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 +#endif + +/* Return values for http_send_*() */ +#define HTTP_DATA_TO_SEND_FREED 3 +#define HTTP_DATA_TO_SEND_BREAK 2 +#define HTTP_DATA_TO_SEND_CONTINUE 1 +#define HTTP_NO_DATA_TO_SEND 0 + +//#define CUSTOM_HTTP_SERVER + +//char html_tmp[4096] = {0}; + +//char bigBuffer[LEN_BUF_BIG_ANSWER_HTTP]; + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** HTTP request is copied here from pbufs for simple parsing */ +static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH + 1]; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_SUPPORT_POST +#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN +#endif +#endif +#ifndef LWIP_HTTPD_URI_BUF_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN +#endif +#if LWIP_HTTPD_URI_BUF_LEN +/* Filename for response file to send when POST is finished or + * search for default files when a directory is requested. */ +static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN + 1]; +#endif + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/* The number of individual strings that comprise the headers sent before each + * requested file. + */ +#define NUM_FILE_HDR_STRINGS 7 +#define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */ +#define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */ +#define HDR_STRINGS_IDX_CORS 4 /* the content type (or default answer content type including default document) */ +#define HDR_STRINGS_IDX_EXPIRES 5 /* the content type (or default answer content type including default document) */ +#define HDR_STRINGS_IDX_CONTENT_TYPE 6 /* the content type (or default answer content type including default document) */ + +/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3 +#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE +/* The dynamically generated Content-Length buffer shall be able to work with + ~953 MB (9 digits) */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) +#endif +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI + +#define HTTPD_LAST_TAG_PART 0xFFFF + +enum tag_check_state { + TAG_NONE, /* Not processing an SSI tag */ + TAG_LEADIN, /* Tag lead in "" being processed */ + TAG_SENDING /* Sending tag replacement string */ +}; + +struct http_ssi_state { + const char *parsed; /* Pointer to the first unparsed byte in buf. */ +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + const char *tag_started;/* Pointer to the first opening '<' of the tag. */ +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ + u32_t parse_left; /* Number of unparsed bytes in buf. */ + u16_t tag_index; /* Counter used by tag parsing state machine */ + u16_t tag_insert_len; /* Length of insert in string tag_insert */ +#if LWIP_HTTPD_SSI_MULTIPART + u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + u8_t tag_type; /* index into http_ssi_tag_desc array */ + u8_t tag_name_len; /* Length of the tag name in string tag_name */ + char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ + char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ + enum tag_check_state tag_state; /* State of the tag processor */ +}; + +struct http_ssi_tag_description { + const char *lead_in; + const char *lead_out; +}; + +#endif /* LWIP_HTTPD_SSI */ + +char bufAnswerGetGlobal[LEN_BUF_SMALL_ANSWER_HTTP]; +char bufAnswerGetFileGlobal[1460 * 2]; +char bufAnswerPostGlobal[LEN_BUF_SMALL_ANSWER_POST_HTTP]; + +struct http_state { +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + struct http_state *next; +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + struct fs_file file_handle; + struct fs_file *handle; + const char *file; /* Pointer to first unsent byte in buf. */ + + // Буфер данных + //char bufAnswer[LEN_BUF_SMALL_ANSWER_HTTP]; + char *bufAnswer; + //char *bufAnswer; + + // Признак зарузки контента из файла + bool isContentFile; + // Имя файла для динамической передачи контента + char fileNameContent[MAX_LEN_PATH_FS]; + // Текущий указатель на данные в файле + u32_t contentFilePos; + + char url[MAX_LEN_PATH_FS * 2]; + +// bool isBigBuffer; + + typeAuth auth; + + bool isExpires; + + // Счетчик оставшихся данных для передачи + u32_t postLeft; + + // Ключ + uint32_t authKey; + + tHttpPostSetting settings_post; + + struct altcp_pcb *pcb; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *req; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_DYNAMIC_FILE_READ + char *buf; /* File read buffer. */ + int buf_len; /* Size of file read buffer, buf. */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + u32_t left; /* Number of unsent bytes in buf. */ + u8_t retries; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + u8_t keepalive; +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ +#if LWIP_HTTPD_SSI + struct http_ssi_state *ssi; +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_CGI + char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ + char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ +#if LWIP_HTTPD_DYNAMIC_HEADERS + const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ + char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE]; + u16_t hdr_pos; /* The position of the first unsent header byte in the + current string */ + u16_t hdr_index; /* The index of the hdr string currently being sent. */ +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_TIMING + u32_t time_started; +#endif /* LWIP_HTTPD_TIMING */ +#if LWIP_HTTPD_SUPPORT_POST + u32_t post_content_len_left; +#if LWIP_HTTPD_POST_MANUAL_WND + u32_t unrecved_bytes; + u8_t no_auto_wnd; + u8_t post_finished; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ +#endif /* LWIP_HTTPD_SUPPORT_POST*/ +}; + +#if HTTPD_USE_MEM_POOL +LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE") +#if LWIP_HTTPD_SSI +LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE") +#define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x)) +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE) +#endif /* LWIP_HTTPD_SSI */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE) +#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x)) +#else /* HTTPD_USE_MEM_POOL */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) +#define HTTP_FREE_HTTP_STATE(x) mem_free(x) +#if LWIP_HTTPD_SSI +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) +#define HTTP_FREE_SSI_STATE(x) mem_free(x) +#endif /* LWIP_HTTPD_SSI */ +#endif /* HTTPD_USE_MEM_POOL */ + +static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs); + +static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); + +static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); + +static err_t +http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char *params); + +static err_t http_poll(void *arg, struct altcp_pcb *pcb); + +static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs); + +#if LWIP_HTTPD_FS_ASYNC_READ +static void http_continue(void *connection); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_SSI +/* SSI insert handler function pointer. */ +static tSSIHandler httpd_ssi_handler; +#if !LWIP_HTTPD_SSI_RAW +static int httpd_num_tags; +static const char **httpd_tags; +#endif /* !LWIP_HTTPD_SSI_RAW */ + +/* Define the available tag lead-ins and corresponding lead-outs. + * ATTENTION: for the algorithm below using this array, it is essential + * that the lead in differs in the first character! */ +const struct http_ssi_tag_description http_ssi_tag_desc[] = { + {""}, + {"/*#", "*/"} +}; + +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/* CGI handler information */ +static const tCGI *httpd_cgis; +static int httpd_num_cgis; +static int http_cgi_paramcount; +#define http_cgi_params hs->params +#define http_cgi_param_vals hs->param_vals +#elif LWIP_HTTPD_CGI_SSI +static char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ +static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +/** global list of active HTTP connections, use to kill the oldest when + running out of memory */ +static struct http_state *http_connections; + +static void +http_add_connection(struct http_state *hs) +{ + /* add the connection to the list */ + hs->next = http_connections; + http_connections = hs; +} + +static void +http_remove_connection(struct http_state *hs) +{ + /* take the connection off the list */ + if (http_connections) { + if (http_connections == hs) { + http_connections = hs->next; + } else { + struct http_state *last; + for (last = http_connections; last->next != NULL; last = last->next) { + if (last->next == hs) { + last->next = hs->next; + break; + } + } + } + } +} + +static void +http_kill_oldest_connection(u8_t ssi_required) +{ + struct http_state *hs = http_connections; + struct http_state *hs_free_next = NULL; + while (hs && hs->next) { +#if LWIP_HTTPD_SSI + if (ssi_required) { + if (hs->next->ssi != NULL) { + hs_free_next = hs; + } + } else +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(ssi_required); +#endif /* LWIP_HTTPD_SSI */ + { + hs_free_next = hs; + } + LWIP_ASSERT("broken list", hs != hs->next); + hs = hs->next; + } + if (hs_free_next != NULL) { + LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); + LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); + /* send RST when killing a connection because of memory shortage */ + http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ + } +} +#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#define http_add_connection(hs) +#define http_remove_connection(hs) + +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#if LWIP_HTTPD_SSI +/** Allocate as struct http_ssi_state. */ +static struct http_ssi_state * +http_ssi_state_alloc(void) +{ + struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(1); + ret = HTTP_ALLOC_SSI_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + memset(ret, 0, sizeof(struct http_ssi_state)); + } + return ret; +} + +/** Free a struct http_ssi_state. */ +static void +http_ssi_state_free(struct http_ssi_state *ssi) +{ + if (ssi != NULL) { + HTTP_FREE_SSI_STATE(ssi); + } +} +#endif /* LWIP_HTTPD_SSI */ + +/** Initialize a struct http_state. + */ +static void +http_state_init(struct http_state *hs) { + /* Initialize the structure. */ + memset(hs, 0, sizeof(struct http_state)); + +/* + memset(&hs->bufAnswer, 0, sizeof(hs->bufAnswer)); + + hs->file = 0; + + //memset(&hs->file_handle, 0, sizeof(struct fs_file)); + hs->handle = 0; + + // Признак зарузки контента из файла + hs->isContentFile = 0; + // Текущий указатель на данные в файле + hs->contentFilePos = 0; + // Счетчик оставшихся данных для передачи + hs->postLeft = 0; + // Ключ + hs->authKey = 0; +// hs->req = 0; + hs->left = 0; + hs->retries = 0; + + hs->isExpires = 0; + + for (int i=0; ihdrs[i] = 0; + } + + memset(hs->hdr_content_len, 0, LWIP_HTTPD_MAX_CONTENT_LEN_SIZE); + hs->hdr_pos = 0; + hs->post_content_len_left = 0; + + + hs->pcb = 0; + +*/ + + + + hs->bufAnswer = bufAnswerGetGlobal; +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Indicate that the headers are not yet valid */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +} + +/** Allocate a struct http_state. */ +static struct http_state * +http_state_alloc(void) { + struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(0); + ret = HTTP_ALLOC_HTTP_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + http_state_init(ret); + http_add_connection(ret); + } + return ret; +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_eof(struct http_state *hs) { + if (hs->handle) { +#if LWIP_HTTPD_TIMING + u32_t ms_needed = sys_now() - hs->time_started; + u32_t needed = LWIP_MAX(1, (ms_needed / 100)); + LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", + ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); +#endif /* LWIP_HTTPD_TIMING */ + fs_close(hs->handle); + hs->handle = NULL; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + if (hs->buf != NULL) { + mem_free(hs->buf); + hs->buf = NULL; + } +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_SSI + if (hs->ssi) { + http_ssi_state_free(hs->ssi); + hs->ssi = NULL; + } +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (hs->req) { + pbuf_free(hs->req); + hs->req = NULL; + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_free(struct http_state *hs) { + if (hs != NULL) { + http_state_eof(hs); + http_remove_connection(hs); + HTTP_FREE_HTTP_STATE(hs); + } +} + +/** Call tcp_write() in a loop trying smaller and smaller length + * + * @param pcb altcp_pcb to send + * @param ptr Data to send + * @param length Length of data to send (in/out: on return, contains the + * amount of data sent) + * @param apiflags directly passed to tcp_write + * @return the return value of tcp_write + */ +static err_t +http_write(struct altcp_pcb *pcb, const void *ptr, u16_t *length, u8_t apiflags) { + + u16_t len, max_len; + err_t err; + LWIP_ASSERT("length != NULL", length != NULL); + len = *length; + if (len == 0) { + return ERR_OK; + } + /* We cannot send more data than space available in the send buffer. */ + max_len = altcp_sndbuf(pcb); + if (max_len < len) { + len = max_len; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_len = HTTPD_MAX_WRITE_LEN(pcb); + if (len > max_len) { + len = max_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len)); + err = altcp_write(pcb, ptr, len, apiflags); + if (err == ERR_MEM) { + if ((altcp_sndbuf(pcb) == 0) || + (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { + /* no need to try smaller sizes */ + len = 1; + } else { + len /= 2; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, + ("Send failed, trying less (%d bytes)\n", len)); + } + } while ((err == ERR_MEM) && (len > 1)); + + if (err == ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); + *length = len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + *length = 0; + } + +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* ensure nagle is normally enabled (only disabled for persistent connections + when all data has been enqueued but the connection stays open for the next + request */ + altcp_nagle_enable(pcb); +#endif + + return err; +} + + +/** + * The connection shall be actively closed (using RST to close from fault states). + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) { + err_t err; + LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void *) pcb)); + +#if LWIP_HTTPD_SUPPORT_POST + if (hs != NULL) { + if ((hs->post_content_len_left != 0) +#if LWIP_HTTPD_POST_MANUAL_WND + || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + ) { + /* make sure the post code knows that the connection is closed */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + } + } +#endif /* LWIP_HTTPD_SUPPORT_POST*/ + + + altcp_arg(pcb, NULL); + altcp_recv(pcb, NULL); + altcp_err(pcb, NULL); + altcp_poll(pcb, NULL, 0); + altcp_sent(pcb, NULL); + if (hs != NULL) { + http_state_free(hs); + } + + if (abort_conn) { + altcp_abort(pcb); + return ERR_OK; + } + err = altcp_close(pcb); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void *) pcb)); + /* error closing, try again later in poll */ + altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + } + return err; +} + +/** + * The connection shall be actively closed. + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_conn(struct altcp_pcb *pcb, struct http_state *hs) { + return http_close_or_abort_conn(pcb, hs, 0); +} + +/** End of file: either close the connection (Connection: close) or + * close the file (Connection: keep-alive) + */ +static void +http_eof(struct altcp_pcb *pcb, struct http_state *hs) { + /* HTTP/1.1 persistent connection? (Not supported for SSI) */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + http_remove_connection(hs); + + http_state_eof(hs); + http_state_init(hs); + /* restore state: */ + hs->pcb = pcb; + hs->keepalive = 1; + http_add_connection(hs); + /* ensure nagle doesn't interfere with sending all data as fast as possible: */ + altcp_nagle_disable(pcb); + } else +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + { + http_close_conn(pcb, hs); + } +} + +#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI + +/** + * Extract URI parameters from the parameter-part of an URI in the form + * "test.cgi?x=y" @todo: better explanation! + * Pointers to the parameters are stored in hs->param_vals. + * + * @param hs http connection state + * @param params pointer to the NULL-terminated parameter string from the URI + * @return number of parameters extracted + */ +static int +extract_uri_parameters(struct http_state *hs, char *params) { + char *pair; + char *equals; + int loop; + + LWIP_UNUSED_ARG(hs); + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { + + /* Save the name of the parameter */ + http_cgi_params[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '&'); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '='); + if (equals) { + *equals = '\0'; + http_cgi_param_vals[loop] = equals + 1; + } else { + http_cgi_param_vals[loop] = NULL; + } + } + + return loop; +} + +#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ + +#if LWIP_HTTPD_SSI +/** + * Insert a tag (found in an shtml in the form of "" into the file. + * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement + * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). + * The amount of data written is stored to ssi->tag_insert_len. + * + * @todo: return tag_insert_len - maybe it can be removed from struct http_state? + * + * @param hs http connection state + */ +static void +get_tag_insert(struct http_state *hs) +{ +#if LWIP_HTTPD_SSI_RAW + const char *tag; +#else /* LWIP_HTTPD_SSI_RAW */ + int tag; +#endif /* LWIP_HTTPD_SSI_RAW */ + size_t len; + struct http_ssi_state *ssi; +#if LWIP_HTTPD_SSI_MULTIPART + u16_t current_tag_part; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + LWIP_ASSERT("hs != NULL", hs != NULL); + ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); +#if LWIP_HTTPD_SSI_MULTIPART + current_tag_part = ssi->tag_part; + ssi->tag_part = HTTPD_LAST_TAG_PART; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_SSI_RAW + tag = ssi->tag_name; +#endif + + if (httpd_ssi_handler +#if !LWIP_HTTPD_SSI_RAW + && httpd_tags && httpd_num_tags +#endif /* !LWIP_HTTPD_SSI_RAW */ + ) { + + /* Find this tag in the list we have been provided. */ +#if LWIP_HTTPD_SSI_RAW + { +#else /* LWIP_HTTPD_SSI_RAW */ + for (tag = 0; tag < httpd_num_tags; tag++) { + if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert, + LWIP_HTTPD_MAX_TAG_INSERT_LEN +#if LWIP_HTTPD_SSI_MULTIPART + , current_tag_part, &ssi->tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_FILE_STATE + , (hs->handle ? hs->handle->state : NULL) +#endif /* LWIP_HTTPD_FILE_STATE */ + ); +#if LWIP_HTTPD_SSI_RAW + if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + return; + } + } + } + } + + /* If we drop out, we were asked to serve a page which contains tags that + * we don't have a handler for. Merely echo back the tags with an error + * marker. */ +#define UNKNOWN_TAG1_TEXT "***UNKNOWN TAG " +#define UNKNOWN_TAG1_LEN 18 +#define UNKNOWN_TAG2_TEXT "***" +#define UNKNOWN_TAG2_LEN 7 + len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name), + LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN))); + MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); + ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; + + len = strlen(ssi->tag_insert); + LWIP_ASSERT("len <= 0xffff", len <= 0xffff); + ssi->tag_insert_len = (u16_t)len; +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS + +/** + * Generate the relevant HTTP headers for the given filename and write + * them into the supplied buffer. + */ +static void +get_http_headers(struct http_state *hs, const char *uri) { + size_t content_type; + char *tmp; + char *ext; + char *vars; + + if (hs->isExpires) { + hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; + } + + /* In all cases, the second header we send is the server identification + so set it here. */ + //hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; + + + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = NULL; + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL; + + /* Is this a normal file or the special case we use to send back the + default "404: Page not found" response? */ + if (uri == NULL) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT]; + } else +#endif + { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; + } + + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; + return; + } + /* We are dealing with a particular filename. Look for one other + special case. We assume that any filename with "404" in it must be + indicative of a 404 server error whereas all other files require + the 200 OK header. */ + if (strstr(uri, "401.html")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = "HTTP/1.0 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"Realm-Name\"\r\n"; + } else if (strstr(uri, "404.html")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; + } else if (strstr(uri, "400.html")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; + } else if (strstr(uri, "501.html")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; + } else { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; + } + + /* Determine if the URI has any variables and, if so, temporarily remove + them. */ + vars = strchr(uri, '?'); + if (vars) { + *vars = '\0'; + } + + /* Get a pointer to the file extension. We find this by looking for the + last occurrence of "." in the filename passed. */ + ext = NULL; + tmp = strchr(uri, '.'); + while (tmp) { + ext = tmp + 1; + tmp = strchr(ext, '.'); + } + if (ext != NULL) { + /* Now determine the content type and add the relevant header for that. */ + for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) { + /* Have we found a matching extension? */ + if (!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) { + break; + } + } + } else { + content_type = NUM_HTTP_HEADERS; + } + + /* Reinstate the parameter marker if there was one in the original URI. */ + if (vars) { + *vars = '?'; + } + +#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI + /* Does the URL passed have any file extension? If not, we assume it + is a special-case URL used for control state notification and we do + not send any HTTP headers with the response. */ + if (!ext) { + /* Force the header index to a value indicating that all headers + have already been sent. */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; + return; + } +#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */ + /* Did we find a matching extension? */ + if (content_type < NUM_HTTP_HEADERS) { + /* yes, store it */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type; + } else if (!ext) { + /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP; + } else { + /* No - use the default, plain text file type. */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE; + } + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; +} + +/* Add content-length header? */ +static void +get_http_content_length(struct http_state *hs) { + u8_t add_content_len = 0; + LWIP_ASSERT("already been here?", hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL); + + add_content_len = 0; +#if LWIP_HTTPD_SSI + if (hs->ssi == NULL) /* @todo: get maximum file length from SSI */ +#endif /* LWIP_HTTPD_SSI */ + { + if ((hs->handle != NULL) && (hs->handle->flags & FS_FILE_FLAGS_HEADER_PERSISTENT)) { + add_content_len = 1; + } + } + add_content_len = 1; + + if (add_content_len) { + size_t len; + + lwip_itoa(hs->hdr_content_len, (size_t) LWIP_HTTPD_MAX_CONTENT_LEN_SIZE, + hs->left); + len = strlen(hs->hdr_content_len); + + + if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) { + SMEMCPY(&hs->hdr_content_len[len], CRLF, 3); + + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len; + } else { + add_content_len = 0; + } + } +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN]; + } else { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; + hs->keepalive = 0; + } +#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ +} + +/** Sub-function of http_send(): send dynamic headers + * + * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued + * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body + * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, + * so don't send HTTP body yet + * - HTTP_DATA_TO_SEND_FREED: http_state and pcb are already freed + */ + + +char CORSA[] = "Access-Control-Allow-Origin: *\r\n"; +//char EXPIRES[] = "Expires: Wed, 21 Oct 2060 07:28:00 GMT\r\n"; +//char EXPIRES[] = "Expires: Now\r\n"; +char EXPIRES[] = "Access-Control-Allow-Headers: Origin, Authorization, X-Requested-With, Content-Type, Accept\r\n"; + +static u8_t +http_send_headers(struct altcp_pcb *pcb, struct http_state *hs) { + err_t err; + u16_t len; + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + u16_t hdrlen, sendlen; + + if (hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL) { + /* set up "content-length" and "connection:" headers */ + get_http_content_length(hs); + } + + hs->hdrs[HDR_STRINGS_IDX_CORS] = CORSA; + hs->hdrs[HDR_STRINGS_IDX_EXPIRES] = EXPIRES; + /* How much data can we send? */ + len = altcp_sndbuf(pcb); + sendlen = len; + + while (len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { + const void *ptr; + u16_t old_sendlen; + u8_t apiflags; + /* How much do we have to send from the current header? */ + hdrlen = (u16_t) strlen(hs->hdrs[hs->hdr_index]); + + /* How much of this can we send? */ + sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); + + /* Send this amount of data or as much as we can given memory + * constraints. */ + ptr = (const void *) (hs->hdrs[hs->hdr_index] + hs->hdr_pos); + old_sendlen = sendlen; + apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr); + if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) { + /* content-length is always volatile */ + apiflags |= TCP_WRITE_FLAG_COPY; + } + if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) { + apiflags |= TCP_WRITE_FLAG_MORE; + } + err = http_write(pcb, ptr, &sendlen, apiflags); + if ((err == ERR_OK) && (old_sendlen != sendlen)) { + /* Remember that we added some more data to be transmitted. */ + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } else if (err != ERR_OK) { + /* special case: http_write does not try to send 1 byte */ + sendlen = 0; + } + + /* Fix up the header position for the next time round. */ + hs->hdr_pos += sendlen; + len -= sendlen; + + /* Have we finished sending this string? */ + if (hs->hdr_pos == hdrlen) { + /* Yes - move on to the next one */ + hs->hdr_index++; + /* skip headers that are NULL (not all headers are required) */ + while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) && + (hs->hdrs[hs->hdr_index] == NULL)) { + hs->hdr_index++; + } + hs->hdr_pos = 0; + } + } + + if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) { + /* When we are at the end of the headers, check for data to send + * instead of waiting for ACK from remote side to continue + * (which would happen when sending files from async read). */ + if (http_check_eof(pcb, hs)) { + data_to_send = HTTP_DATA_TO_SEND_BREAK; + } else { + /* At this point, for non-keepalive connections, hs is deallocated an + pcb is closed. */ + return HTTP_DATA_TO_SEND_FREED; + } + } + /* If we get here and there are still header bytes to send, we send + * the header information we just wrote immediately. If there are no + * more headers to send, but we do have file data to send, drop through + * to try to send some file data too. */ + if ((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { + LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); + return HTTP_DATA_TO_SEND_BREAK; + } + return data_to_send; +} + +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +/** Sub-function of http_send(): end-of-file (or block) is reached, + * either close the file or read the next block (if supported). + * + * @returns: 0 if the file is finished or no data has been read + * 1 if the file is not finished and data has been read + */ +static u8_t +http_check_eof(struct altcp_pcb *pcb, struct http_state *hs) { + int bytes_left; +#if LWIP_HTTPD_DYNAMIC_FILE_READ + int count; +#ifdef HTTPD_MAX_WRITE_LEN + int max_write_len; +#endif /* HTTPD_MAX_WRITE_LEN */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + + /* Do we have a valid file handle? */ + if (hs->handle == NULL) { + /* No - close the connection. */ + http_eof(pcb, hs); + return 0; + } + bytes_left = fs_bytes_left(hs->handle); + if (bytes_left <= 0) { + /* We reached the end of the file so this request is done. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + /* Do we already have a send buffer allocated? */ + if (hs->buf) { + /* Yes - get the length of the buffer */ + count = LWIP_MIN(hs->buf_len, bytes_left); + } else { + /* We don't have a send buffer so allocate one now */ + count = altcp_sndbuf(pcb); + if (bytes_left < count) { + count = bytes_left; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_write_len = HTTPD_MAX_WRITE_LEN(pcb); + if (count > max_write_len) { + count = max_write_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + hs->buf = (char *)mem_malloc((mem_size_t)count); + if (hs->buf != NULL) { + hs->buf_len = count; + break; + } + count = count / 2; + } while (count > 100); + + /* Did we get a send buffer? If not, return immediately. */ + if (hs->buf == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); + return 0; + } + } + + /* Read a block of data from the file. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); + +#if LWIP_HTTPD_FS_ASYNC_READ + count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + count = fs_read(hs->handle, hs->buf, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + if (count < 0) { + if (count == FS_READ_DELAYED) { + /* Delayed read, wait for FS to unblock us */ + return 0; + } + /* We reached the end of the file so this request is done. + * @todo: close here for HTTP/1.1 when reading file fails */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + + /* Set up to send the block of data we just read */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); + hs->left = count; + hs->file = hs->buf; +#if LWIP_HTTPD_SSI + if (hs->ssi) { + hs->ssi->parse_left = count; + hs->ssi->parsed = hs->buf; + } +#endif /* LWIP_HTTPD_SSI */ +#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); +#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ + return 1; +} + +/** Sub-function of http_send(): This is the normal send-routine for non-ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ + +static u8_t +http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs) { + err_t err; + u16_t len; + u8_t data_to_send = 0; + + /* We are not processing an SHTML file so no tag checking is necessary. + * Just send the data as we received it from the file. */ +// len = (u16_t) LWIP_MIN(hs->left, 0xffff); + len = (u16_t) LWIP_MIN(hs->left, 1460 * 2); + + if (hs->isContentFile) { + FIL file; + UINT bytes_read; + + // Получение указателя на расширение файла +// char *pExt = getVoidExt(hs->fileNameContent); + +// if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) != osOK) { +// return 0; +// } + + + + + //len = (u16_t) LWIP_MIN(hs->left, LEN_BUF_SMALL_ANSWER_HTTP); + len = (u16_t) LWIP_MIN(hs->left, 1460 * 2); + + + FRESULT fr = f_open_i(httpSettings.fs, &file, hs->fileNameContent, FA_READ); + + f_lseek_i(httpSettings.fs, &file, hs->contentFilePos); + +// uint32_t t1 = SystemGetMs(); + f_read_i(httpSettings.fs, &file, bufAnswerGetFileGlobal, len, &bytes_read); +// uint32_t t2 = SystemGetMs(); +// uint32_t t3 = t2 - t1; + + f_close_i(httpSettings.fs, &file); + + +// osMutexRelease(httpSettings.accessHTTP); + + + //err = http_write(pcb, hs->bufAnswer, &bytes_read, HTTP_IS_DATA_VOLATILE(hs)); + //err = http_write(pcb, hs->bufAnswer, &bytes_read, HTTP_IS_DATA_VOLATILE(hs)); + + err = http_write(pcb, bufAnswerGetFileGlobal, &bytes_read, TCP_WRITE_FLAG_COPY); + //err = http_write(pcb, hs->bufAnswer, &bytes_read, TCP_WRITE_FLAG_MORE); + + + if (err == ERR_OK) { + data_to_send = 1; + hs->contentFilePos += bytes_read; + hs->left -= bytes_read; + } + + +/* + err = http_write(pcb, hs->bufAnswer, &len, TCP_WRITE_FLAG_COPY); + + if (err == ERR_OK) { + data_to_send = 1; + hs->contentFilePos += len; + hs->left -= len; + } +*/ + } else { + + //len = (u16_t) LWIP_MIN(len, 1460 * 2); + + + err = http_write(pcb, hs->file, &len, TCP_WRITE_FLAG_COPY); +// err = http_write(pcb, hs->file, &len, TCP_WRITE_FLAG_MORE); + + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + } + + + return data_to_send; +} + + +#if LWIP_HTTPD_SSI +/** Sub-function of http_send(): This is the send-routine for ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs) +{ + err_t err = ERR_OK; + u16_t len; + u8_t data_to_send = 0; + u8_t tag_type; + + struct http_ssi_state *ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); + /* We are processing an SHTML file so need to scan for tags and replace + * them with insert strings. We need to be careful here since a tag may + * straddle the boundary of two blocks read from the file and we may also + * have to split the insert string between two tcp_write operations. */ + + /* How much data could we send? */ + len = altcp_sndbuf(pcb); + + /* Do we have remaining data to send before parsing more? */ + if (ssi->parsed > hs->file) { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + /* If the send buffer is full, return now. */ + if (altcp_sndbuf(pcb) == 0) { + return data_to_send; + } + } + + LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); + + /* We have sent all the data that was already parsed so continue parsing + * the buffer contents looking for SSI tags. */ + while (((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) { + if (len == 0) { + return data_to_send; + } + switch (ssi->tag_state) { + case TAG_NONE: + /* We are not currently processing an SSI tag so scan for the + * start of the lead-in marker. */ + for (tag_type = 0; tag_type < LWIP_ARRAYSIZE(http_ssi_tag_desc); tag_type++) { + if (*ssi->parsed == http_ssi_tag_desc[tag_type].lead_in[0]) { + /* We found what could be the lead-in for a new tag so change + * state appropriately. */ + ssi->tag_type = tag_type; + ssi->tag_state = TAG_LEADIN; + ssi->tag_index = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->tag_started = ssi->parsed; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + break; + } + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + + case TAG_LEADIN: + /* We are processing the lead-in marker, looking for the start of + * the tag name. */ + + /* Have we reached the end of the leadin? */ + if (http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index] == 0) { + ssi->tag_index = 0; + ssi->tag_state = TAG_FOUND; + } else { + /* Have we found the next character we expect for the tag leadin? */ + if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadin, in which case we start looking for the tag itself */ + ssi->tag_index++; + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->tag_state = TAG_NONE; + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + } + break; + + case TAG_FOUND: + /* We are reading the tag name, looking for the start of the + * lead-out marker and removing any whitespace found. */ + + /* Remove leading whitespace between the tag leading and the first + * tag name character. */ + if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the end of the tag name? This is signalled by + * us finding the first leadout character or whitespace */ + if ((*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) || + (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || + (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { + + if (ssi->tag_index == 0) { + /* We read a zero length tag so ignore it. */ + ssi->tag_state = TAG_NONE; + } else { + /* We read a non-empty tag so go ahead and look for the + * leadout string. */ + ssi->tag_state = TAG_LEADOUT; + LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); + ssi->tag_name_len = (u8_t)ssi->tag_index; + ssi->tag_name[ssi->tag_index] = '\0'; + if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) { + ssi->tag_index = 1; + } else { + ssi->tag_index = 0; + } + } + } else { + /* This character is part of the tag name so save it */ + if (ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { + ssi->tag_name[ssi->tag_index++] = *ssi->parsed; + } else { + /* The tag was too long so ignore it. */ + ssi->tag_state = TAG_NONE; + } + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + break; + + /* We are looking for the end of the lead-out marker. */ + case TAG_LEADOUT: + /* Remove leading whitespace between the tag leading and the first + * tag leadout character. */ + if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the next character we expect for the tag leadout? */ + if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadout, in which case we need to call the client to process + * the tag. */ + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + ssi->tag_index++; + + if (http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index] == 0) { + /* Call the client to ask for the insert string for the + * tag we just found. */ +#if LWIP_HTTPD_SSI_MULTIPART + ssi->tag_part = 0; /* start with tag part 0 */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + get_tag_insert(hs); + + /* Next time through, we are going to be sending data + * immediately, either the end of the block we start + * sending here or the insert string. */ + ssi->tag_index = 0; + ssi->tag_state = TAG_SENDING; + ssi->tag_end = ssi->parsed; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_started; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + /* If there is any unsent data in the buffer prior to the + * tag, we need to send it now. */ + if (ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if (ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += (u16_t)(ssi->tag_end - ssi->tag_started); + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } + } + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->parse_left--; + ssi->parsed++; + ssi->tag_state = TAG_NONE; + } + break; + + /* + * We have found a valid tag and are in the process of sending + * data as a result of that discovery. We send either remaining data + * from the file prior to the insert point or the insert string itself. + */ + case TAG_SENDING: + /* Do we have any remaining file data to send from the buffer prior + * to the tag? */ + if (ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + if (len != 0) { + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + } else { + err = ERR_OK; + } + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if (ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += (u16_t)(ssi->tag_end - ssi->tag_started); + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if (ssi->tag_index >= ssi->tag_insert_len) { + /* Did the last SSIHandler have more to send? */ + if (ssi->tag_part != HTTPD_LAST_TAG_PART) { + /* If so, call it again */ + ssi->tag_index = 0; + get_tag_insert(hs); + } + } +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + /* Do we still have insert data left to send? */ + if (ssi->tag_index < ssi->tag_insert_len) { + /* We are sending the insert string itself. How much of the + * insert can we send? */ + len = (ssi->tag_insert_len - ssi->tag_index); + + /* Note that we set the copy flag here since we only have a + * single tag insert buffer per connection. If we don't do + * this, insert corruption can occur if more than one insert + * is processed before we call tcp_output. */ + err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, + HTTP_IS_TAG_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + ssi->tag_index += len; + /* Don't return here: keep on sending data */ + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if (ssi->tag_part == HTTPD_LAST_TAG_PART) +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + { + /* We have sent all the insert data so go back to looking for + * a new tag. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_end; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + } + } + break; + default: + break; + } + } + } + + /* If we drop out of the end of the for loop, this implies we must have + * file data to send so send it now. In TAG_SENDING state, we've already + * handled this so skip the send if that's the case. */ + if ((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { +#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG + if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) { + /* If we found tag on the edge of the read buffer: just throw away the first part + (we have copied/saved everything required for parsing on later). */ + len = (u16_t)(ssi->tag_started - hs->file); + hs->left -= (ssi->parsed - ssi->tag_started); + ssi->parsed = ssi->tag_started; + ssi->tag_started = hs->buf; + } else +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */ + { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + } + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + } + return data_to_send; +} +#endif /* LWIP_HTTPD_SSI */ + +/** + * Try to send more data on this pcb. + * + * @param pcb the pcb to send data + * @param hs connection state + */ +static u8_t +http_send(struct altcp_pcb *pcb, struct http_state *hs) { + + + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void *) pcb, + (void *) hs, hs != NULL ? (int) hs->left : 0)); + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return 0; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + + /* If we were passed a NULL state structure pointer, ignore the call. */ + if (hs == NULL) { + return 0; + } + +#if LWIP_HTTPD_FS_ASYNC_READ + /* Check if we are allowed to read from this file. + (e.g. SSI might want to delay sending until data is available) */ + if (!fs_is_file_ready(hs->handle, http_continue, hs)) { + return 0; + } +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Do we have any more header data to send for this file? */ + + if (hs->hdr_index < NUM_FILE_HDR_STRINGS) { + data_to_send = http_send_headers(pcb, hs); + if ((data_to_send == HTTP_DATA_TO_SEND_FREED) || + ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) && + (hs->hdr_index < NUM_FILE_HDR_STRINGS))) { + return data_to_send; + } + } +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + + + /* Have we run out of file data to send? If so, we need to read the next + * block from the file. */ + if (hs->left == 0) { + if (!http_check_eof(pcb, hs)) { + return 0; + } + } + +#if LWIP_HTTPD_SSI + if (hs->ssi) { + data_to_send = http_send_data_ssi(pcb, hs); + } else +#endif /* LWIP_HTTPD_SSI */ + + { + data_to_send = http_send_data_nonssi(pcb, hs); + } + + if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { + /* We reached the end of the file so this request is done. + * This adds the FIN flag right into the last data segment. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + +// if (hs->isBigBuffer == true) { +// osMutexRelease(httpSettings.accessHTTP); +// } + + http_eof(pcb, hs); + return 0; + } + + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); + return data_to_send; +} + +#if LWIP_HTTPD_SUPPORT_EXTSTATUS +/** Initialize a http connection with a file to send for an error message + * + * @param hs http connection state + * @param error_nr HTTP error number + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_error_file(struct http_state *hs, u16_t error_nr) +{ + const char *uri, *uri1, *uri2, *uri3; + + if (error_nr == 501) { + uri1 = "/501.html"; + uri2 = "/501.htm"; + uri3 = "/501.shtml"; + } else { + /* 400 (bad request is the default) */ + uri1 = "/400.html"; + uri2 = "/400.htm"; + uri3 = "/400.shtml"; + } + if (fs_open(&hs->file_handle, uri1) == ERR_OK) { + uri = uri1; + } else if (fs_open(&hs->file_handle, uri2) == ERR_OK) { + uri = uri2; + } else if (fs_open(&hs->file_handle, uri3) == ERR_OK) { + uri = uri3; + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", + error_nr)); + return ERR_ARG; + } + return http_init_file(hs, &hs->file_handle, 0, uri, 0, NULL); +} +#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ +#define http_find_error_file(hs, error_nr) ERR_ARG +#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ + +/** + * Get the file struct for a 404 error page. + * Tries some file names and returns NULL if none found. + * + * @param uri pointer that receives the actual file name URI + * @return file struct for the error page or NULL no matching file was found + */ +static struct fs_file * +http_get_404_file(struct http_state *hs, const char **uri) { + err_t err; + + *uri = "/404.html"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.html doesn't exist. Try 404.htm instead. */ + *uri = "/404.htm"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Try 404.shtml instead. */ + *uri = "/404.shtml"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Indicate to the caller that it should + * send back a default 404 page. + */ + *uri = NULL; + return NULL; + } + } + } + + return &hs->file_handle; +} + +#if LWIP_HTTPD_SUPPORT_POST + +static err_t +http_handle_post_finished(struct http_state *hs) { +#if LWIP_HTTPD_POST_MANUAL_WND + /* Prevent multiple calls to httpd_post_finished, since it might have already + been called before from httpd_post_data_recved(). */ + if (hs->post_finished) { + return ERR_OK; + } + hs->post_finished = 1; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + /* NULL-terminate the buffer */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + return http_find_file(hs, http_uri_buf, 0); +} + +/** Pass received POST body data to the application and correctly handle + * returning a response document or closing the connection. + * ATTENTION: The application is responsible for the pbuf now, so don't free it! + * + * @param hs http connection state + * @param p pbuf to pass to the application + * @return ERR_OK if passed successfully, another err_t if the response file + * hasn't been found (after POST finished) + */ +static err_t +http_post_rxpbuf(struct http_state *hs, struct pbuf *p) { + err_t err; + + if (p != NULL) { + /* adjust remaining Content-Length */ + if (hs->post_content_len_left < p->tot_len) { + hs->post_content_len_left = 0; + } else { + hs->post_content_len_left -= p->tot_len; + } + } +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + /* prevent connection being closed if httpd_post_data_recved() is called nested */ + hs->unrecved_bytes++; +#endif + if (p != NULL) { + err = httpd_post_receive_data(hs, p); + } else { + err = ERR_OK; + } +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + hs->unrecved_bytes--; +#endif + if (err != ERR_OK) { + /* Ignore remaining content in case of application error */ + hs->post_content_len_left = 0; + } + if (hs->post_content_len_left == 0) { +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return ERR_OK; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + return http_handle_post_finished(hs); + } + + return ERR_OK; +} + +/** Handle a post request. Called from http_parse_request when method 'POST' + * is found. + * + * @param p The input pbuf (containing the POST header and body). + * @param hs The http connection state. + * @param data HTTP request (header and part of body) from input pbuf(s). + * @param data_len Size of 'data'. + * @param uri The HTTP URI parsed from input pbuf(s). + * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP + * header starts). + * @return ERR_OK: POST correctly parsed and accepted by the application. + * ERR_INPROGRESS: POST not completely parsed (no error yet) + * another err_t: Error parsing POST or denied by the application + */ +static err_t +http_post_request(struct pbuf *inp, struct http_state *hs, + char *data, u16_t data_len, char *uri, char *uri_end) { + err_t err; + /* search for end-of-header (first double-CRLF) */ + char *crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); + + if (crlfcrlf != NULL) { + /* search for "Content-Length: " */ +#define HTTP_HDR_CONTENT_LEN "Content-Length: " +#define HTTP_HDR_CONTENT_LEN_LEN 16 +#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 + char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); + if (scontent_len != NULL) { + char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, + HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); + if (scontent_len_end != NULL) { + int content_len; + char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; + content_len = atoi(content_len_num); + if (content_len == 0) { + /* if atoi returns 0 on error, fix this */ + if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) { + content_len = -1; + } + } + if (content_len >= 0) { + /* adjust length of HTTP header passed to application */ + const char *hdr_start_after_uri = uri_end + 1; + u16_t hdr_len = (u16_t) LWIP_MIN(data_len, crlfcrlf + 4 - data); + u16_t hdr_data_len = (u16_t) LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); + u8_t post_auto_wnd = 1; + http_uri_buf[0] = 0; + /* trim http header */ + *crlfcrlf = 0; + err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, + http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd); + if (err == ERR_OK) { + /* try to pass in data of the first pbuf(s) */ + struct pbuf *q = inp; + u16_t start_offset = hdr_len; +#if LWIP_HTTPD_POST_MANUAL_WND + hs->no_auto_wnd = !post_auto_wnd; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* set the Content-Length to be received for this POST */ + hs->post_content_len_left = (u32_t) content_len; + + /* get to the pbuf where the body starts */ + while ((q != NULL) && (q->len <= start_offset)) { + start_offset -= q->len; + q = q->next; + } + if (q != NULL) { + /* hide the remaining HTTP header */ + pbuf_remove_header(q, start_offset); +#if LWIP_HTTPD_POST_MANUAL_WND + if (!post_auto_wnd) { + /* already tcp_recved() this data... */ + hs->unrecved_bytes = q->tot_len; + } +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + pbuf_ref(q); + return http_post_rxpbuf(hs, q); + } else if (hs->post_content_len_left == 0) { + q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + return http_post_rxpbuf(hs, q); + } else { + return ERR_OK; + } + } else { + /* return file passed from application */ + return http_find_file(hs, http_uri_buf, 0); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", + content_len_num)); + return ERR_ARG; + } + } + } + /* If we come here, headers are fully received (double-crlf), but Content-Length + was not included. Since this is currently the only supported method, we have + to fail in this case! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n")); + return ERR_ARG; + } + /* if we come here, the POST is incomplete */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + return ERR_INPROGRESS; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + return ERR_ARG; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +#if LWIP_HTTPD_POST_MANUAL_WND + +/** + * @ingroup httpd + * A POST implementation can call this function to update the TCP window. + * This can be used to throttle data reception (e.g. when received data is + * programmed to flash and data is received faster than programmed). + * + * @param connection A connection handle passed to httpd_post_begin for which + * httpd_post_finished has *NOT* been called yet! + * @param recved_len Length of data received (for window update) + */ +void httpd_post_data_recved(void *connection, u16_t recved_len) { + struct http_state *hs = (struct http_state *) connection; + if (hs != NULL) { + if (hs->no_auto_wnd) { + u16_t len = recved_len; + if (hs->unrecved_bytes >= recved_len) { + hs->unrecved_bytes -= recved_len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); + len = (u16_t) hs->unrecved_bytes; + hs->unrecved_bytes = 0; + } + if (hs->pcb != NULL) { + if (len != 0) { + altcp_recved(hs->pcb, len); + } + if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { + /* finished handling POST */ + http_handle_post_finished(hs); + http_send(hs->pcb, hs); + } + } + } + } +} + +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +#if LWIP_HTTPD_FS_ASYNC_READ +/** Try to send more data if file has been blocked before + * This is a callback function passed to fs_read_async(). + */ +static void +http_continue(void *connection) +{ + struct http_state *hs = (struct http_state *)connection; + LWIP_ASSERT_CORE_LOCKED(); + if (hs && (hs->pcb) && (hs->handle)) { + LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); + if (http_send(hs->pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + altcp_output(hs->pcb); + } + } +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +/** + * When data has been received in the correct state, try to parse it + * as a HTTP request. + * + * @param inp the received pbuf + * @param hs the connection state + * @param pcb the altcp_pcb which received this packet + * @return ERR_OK if request was OK and hs has been initialized correctly + * ERR_INPROGRESS if request was OK so far but not fully received + * another err_t otherwise + */ +static err_t +http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb) { + char *data; + char *crlf; + u16_t data_len; + struct pbuf *p = inp; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + u16_t clen; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +#if LWIP_HTTPD_SUPPORT_POST + err_t err; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + + LWIP_UNUSED_ARG(pcb); /* only used for post */ + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("hs != NULL", hs != NULL); + + if ((hs->handle != NULL) || (hs->file != NULL)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); + /* already sending a file */ + /* @todo: abort? */ + return ERR_USE; + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + + LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); + + /* first check allowed characters in this pbuf? */ + + /* enqueue the pbuf */ + if (hs->req == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); + hs->req = p; + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); + pbuf_cat(hs->req, p); + } + /* increase pbuf ref counter as it is freed when we return but we want to + keep it on the req list */ + pbuf_ref(p); + + if (hs->req->next != NULL) { + data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); + pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); + data = httpd_req_buf; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { + data = (char *) p->payload; + data_len = p->len; + if (p->len != p->tot_len) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); + } + } + + /* received enough data for minimal request? */ + if (data_len >= MIN_REQ_LEN) { + /* wait for CRLF before parsing anything */ + crlf = lwip_strnstr(data, CRLF, data_len); + if (crlf != NULL) { +#if LWIP_HTTPD_SUPPORT_POST + int is_post = 0; + int is_options = 0; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + int is_09 = 0; + char *sp1, *sp2; + u16_t left_len, uri_len; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); + + /* parse method */ + if (!strncmp(data, "GET ", 4)) { + sp1 = data + 3; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); +#if LWIP_HTTPD_SUPPORT_POST + } else if (!strncmp(data, "POST ", 5)) { + /* store request type */ + is_post = 1; + sp1 = data + 4; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } else if (!strncmp(data, "OPTIONS ", 8)) { + is_options = true; + sp1 = data + 7; + } else { + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + data[4] = 0; + /* unsupported method! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", + data)); + return http_find_error_file(hs, 501); + } + /* if we come here, method is OK, parse URI */ + left_len = (u16_t) (data_len - ((sp1 + 1) - data)); + sp2 = lwip_strnstr(sp1 + 1, " ", left_len); +#if LWIP_HTTPD_SUPPORT_V09 + if (sp2 == NULL) { + /* HTTP 0.9: respond with correct protocol version */ + sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len); + is_09 = 1; +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { + /* HTTP/0.9 does not support POST */ + goto badrequest; + } +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } +#endif /* LWIP_HTTPD_SUPPORT_V09 */ + uri_len = (u16_t) (sp2 - (sp1 + 1)); + if ((sp2 != 0) && (sp2 > sp1)) { + /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ + if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) { + char *uri = sp1 + 1; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* This is HTTP/1.0 compatible: for strict 1.1, a connection + would always be persistent unless "close" was specified. */ + if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) || + lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) { + hs->keepalive = 1; + } else { + hs->keepalive = 0; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + *sp1 = 0; + uri[uri_len] = 0; + LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", + data, uri)); + + +#define HTTP_HDR_AUTH1 "authorization: Basic " +#define HTTP_HDR_AUTH2 "Authorization: Basic " +#define HTTP_HDR_AUTH_LEN 21 + + char authEncode[128]; + memset(authEncode, 0, sizeof(authEncode)); + hs->auth = AUTH_NONE; + + // Поиск окончания заголовка + char *crlfcrlf = lwip_strnstr(sp2 + 1, CRLF CRLF, data_len - (sp2 + 1 - data)); + // Поиск начала Authorization + char *auth_start = lwip_strnstr(sp2 + 1, HTTP_HDR_AUTH1, crlfcrlf - (sp2 + 1)); + if (auth_start == NULL) + auth_start = lwip_strnstr(sp2 + 1, HTTP_HDR_AUTH2, crlfcrlf - (sp2 + 1)); + if (auth_start != NULL) { + // Поиск конца строки Auth + char *auth_end = lwip_strnstr(auth_start + HTTP_HDR_AUTH_LEN, CRLF, + auth_start + HTTP_HDR_AUTH_LEN - (sp2 + 1)); + if (auth_end != NULL) { + char auth[128]; + memset(auth, 0, sizeof(auth)); + + size_t auth_len = auth_end - (auth_start + HTTP_HDR_AUTH_LEN); + if (auth_len > 127) + auth_len = 127; + + memcpy(auth, auth_start + HTTP_HDR_AUTH_LEN, auth_len); + + b64_decode(auth, (unsigned char *) authEncode, sizeof(authEncode)); + + hs->auth = Auth(httpSettings.storage, authEncode); + + } + + } + + +/* +#define HTTP_HDR_COOKIE "Cookie: " +#define HTTP_HDR_COOKIE_LEN 8 + +#define HTTP_HDR_AUTH "Auth=" +#define HTTP_HDR_AUTH_LEN 5 + + hs->authKey = 0; + // Поиск окончания заголовка + char *crlfcrlf = lwip_strnstr(sp2 + 1, CRLF CRLF, data_len - (sp2 + 1 - data)); + // Поиск начала Cookie + char *cookie_start = lwip_strnstr(sp2 + 1, HTTP_HDR_COOKIE, crlfcrlf - (sp2 + 1)); + if (cookie_start != NULL) { + // Поиск начала Auth + char *auth_start = lwip_strnstr(cookie_start + HTTP_HDR_COOKIE_LEN, HTTP_HDR_AUTH, + cookie_start + HTTP_HDR_COOKIE_LEN - (sp2 + 1)); + if (auth_start != NULL) { + // Поиск конца строки Auth + char *auth_end = lwip_strnstr(auth_start + HTTP_HDR_AUTH_LEN, CRLF, + auth_start + HTTP_HDR_AUTH_LEN - (sp2 + 1)); + if (auth_end != NULL) { + char auth[12]; + memset(auth, 0, sizeof(auth)); + + size_t auth_len = auth_end - (auth_start + HTTP_HDR_AUTH_LEN); + if (auth_len > 10) + auth_len = 10; + + memcpy(auth, auth_start + HTTP_HDR_AUTH_LEN, auth_len); + + hs->authKey = atoi(auth); + + //b64_decode(auth, authEncode, sizeof(authEncode)); + } + } + + } +*/ + + if (is_options) { + get_http_headers(hs, uri); + return ERR_OK; +#if LWIP_HTTPD_SUPPORT_POST + } else if (is_post) { +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *q = hs->req; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + struct pbuf *q = inp; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + err = http_post_request(q, hs, data, data_len, uri, sp2); + if (err != ERR_OK) { + /* restore header for next try */ + *sp1 = ' '; + *sp2 = ' '; + uri[uri_len] = ' '; + } + if (err == ERR_ARG) { + goto badrequest; + } + return err; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + return http_find_file(hs, uri, is_09); + } + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); + } + } + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + clen = pbuf_clen(hs->req); + if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && + (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { + /* request not fully received (too short or CRLF is missing) */ + return ERR_INPROGRESS; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { +#if LWIP_HTTPD_SUPPORT_POST + badrequest: +#endif /* LWIP_HTTPD_SUPPORT_POST */ + LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); + /* could not parse request */ + return http_find_error_file(hs, 400); + } +} + +#if LWIP_HTTPD_SSI && (LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 1) +/* Check if SSI should be parsed for this file/URL + * (With LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 2, this function can be + * overridden by an external implementation.) + * + * @return 1 for SSI, 0 for standard files + */ +static u8_t +http_uri_is_ssi(struct fs_file *file, const char *uri) +{ + size_t loop; + u8_t tag_check = 0; + if (file != NULL) { + /* See if we have been asked for an shtml file and, if so, + enable tag checking. */ + const char *ext = NULL, *sub; + char *param = (char *)strstr(uri, "?"); + if (param != NULL) { + /* separate uri from parameters for now, set back later */ + *param = 0; + } + sub = uri; + ext = uri; + for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) { + ext = sub; + sub++; + } + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) { + tag_check = 1; + break; + } + } + if (param != NULL) { + *param = '?'; + } + } + return tag_check; +} +#endif /* LWIP_HTTPD_SSI */ + +#include "http_server.h" + +tFileTableDescriptor *findFileTableDescriptor(char *uri) { + + + uint32_t fileTableDescriptorSize = ((uint32_t *) (FileTableDescriptorAddress))[0]; + tFileTableDescriptor *pFileTableDescriptor = (tFileTableDescriptor *) (FileTableDescriptorAddress + 4); + + if ((uri[0] == '/') && (uri[1] == 0)) { + strcpy(uri, "/index.html"); + } + + for (uint32_t i = 0; i < fileTableDescriptorSize; ++i) { +// char uri_upper[255]; +// memset(uri_upper, 0, sizeof(uri_upper)); + +// size_t len = strlen(uri); +// for (size_t j=0; jname, strlen(pFileTableDescriptor->name)) == 0) { +// if (strncmp(uri_upper, pFileTableDescriptor->name, strlen(pFileTableDescriptor->name)) == 0) { + return pFileTableDescriptor;; + } + + ++pFileTableDescriptor; + } + + return NULL; +} + +/** Try to find the file specified by uri and, if found, initialize hs + * accordingly. + * + * @param hs the connection state + * @param uri the HTTP header URI + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t http_find_file(struct http_state *hs, const char *uri, int is_09) { + size_t loop; + struct fs_file *file = NULL; + char *params = NULL; + err_t err; +#if LWIP_HTTPD_CGI + int i; +#endif /* LWIP_HTTPD_CGI */ +#if !LWIP_HTTPD_SSI + const +#endif /* !LWIP_HTTPD_SSI */ + /* By default, assume we will not be processing server-side-includes tags */ + u8_t tag_check = 0; + + if ((uri[0] == '/') && (uri[1] == 0)) { + strcpy(uri, "/index.html"); + } + + /* Have we been asked for the default file (in root or a directory) ? */ +#if LWIP_HTTPD_MAX_REQUEST_URI_LEN + size_t uri_len = strlen(uri); + if ((uri_len > 0) && (uri[uri_len - 1] == '/') && + ((uri != http_uri_buf) || (uri_len == 1))) { + size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1); + if (copy_len > 0) { + MEMCPY(http_uri_buf, uri, copy_len); + http_uri_buf[copy_len] = 0; + } +#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + if ((uri[0] == '/') && (uri[1] == 0)) { +#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + /* Try each of the configured default filenames until we find one + that exists. */ + + + } + if (file == NULL) { + /* No - we've been asked for a specific file. */ + /* First, isolate the base URI (without any parameters) */ + params = (char *) strchr(uri, '?'); + if (params != NULL) { + /* URI contains parameters. NULL-terminate the base URI */ + *params = '\0'; + params++; + } + +#if LWIP_HTTPD_CGI + http_cgi_paramcount = -1; + /* Does the base URI we have isolated correspond to a CGI handler? */ + if (httpd_num_cgis && httpd_cgis) { + for (i = 0; i < httpd_num_cgis; i++) { + if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) { + /* + * We found a CGI that handles this URI so extract the + * parameters and call the handler. + */ + http_cgi_paramcount = extract_uri_parameters(hs, params); + uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params, + hs->param_vals); + break; + } + } + } +#endif /* LWIP_HTTPD_CGI */ + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); +/* + tFileTableDescriptor *pFileTableDescriptor = findFileTableDescriptor((char *) uri); + + if (pFileTableDescriptor != NULL) { + uint32_t size = pFileTableDescriptor->size; + uint32_t address = pFileTableDescriptor->address; + + get_http_headers(hs, pFileTableDescriptor->name); + + hs->file = (void *) address; + hs->left = size; + hs->retries = 0; + + return ERR_OK; + } +*/ + + hs->isExpires = false; + + if (strcmp(uri, "/web.txt") == 0) { + FRESULT fr; + FILINFO fno; + char fileStats[32]; + + fileStats[0] = '\0'; + strcat(fileStats, "1:/TMP/web.txt"); + + FIL fileLog; + fr = f_open_i(httpSettings.fs, &fileLog, (TCHAR *) fileStats, FA_WRITE | FA_CREATE_ALWAYS); + UINT bytes_written; + fr = f_write_i( httpSettings.fs, &fileLog, bufLwIpWebStats, sizeof(bufLwIpWebStats), &bytes_written); + fr = f_close_i(httpSettings.fs, &fileLog); + + fr = f_stat(fileStats, &fno); + + hs->fileNameContent[0] = '\0'; + strcat(hs->fileNameContent, fileStats); + + hs->file = hs->bufAnswer; + hs->isContentFile = true; + hs->contentFilePos = 0; + hs->left = fno.fsize; + hs->handle = NULL; + hs->retries = 0; + + return ERR_OK; + } + + if (strcmp(uri, "/stats.txt") == 0) { + char str[400]; + char fileStats[32]; + char bufcv[9]; + FRESULT fr; + FILINFO fno; + + + + fileStats[0] = '\0'; + strcat(fileStats, "1:/TMP/stats.txt"); + + str[0] = '\0'; + + strcat(str, "Desciptor error (no more descriptors for receive frame): "); + utoa(EMAC_COUNTER_DMARXDESC_DE, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Overflow error (frame was damaged due to buffer overflow): "); + utoa(EMAC_COUNTER_DMARXDESC_OE, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Ipc checksum error (rx ipv4 header checksum error): "); + utoa(EMAC_COUNTER_DMARXDESC_IPV4HCE, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Late collision occurred during reception: "); + utoa(EMAC_COUNTER_DMARXDESC_LC, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Receive watchdog timeout (watchdog timer expired during reception): "); + utoa(EMAC_COUNTER_DMARXDESC_RWT, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Receive error (error reported by mii interface): "); + utoa(EMAC_COUNTER_DMARXDESC_RE, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + strcat(str, "Crc error: "); + utoa(EMAC_COUNTER_DMARXDESC_CE, bufcv, 10); + strcat(str, bufcv); + strcat(str, "\n"); + + FIL fileLog; + fr = f_open_i(httpSettings.fs, &fileLog, (TCHAR *) fileStats, FA_WRITE | FA_CREATE_ALWAYS); + UINT bytes_written; + fr = f_write_i( httpSettings.fs, &fileLog, str, strlen(str), &bytes_written); + fr = f_close_i(httpSettings.fs, &fileLog); + + + + fr = f_stat(fileStats, &fno); + + hs->fileNameContent[0] = '\0'; + strcat(hs->fileNameContent, fileStats); + + hs->file = hs->bufAnswer; + hs->isContentFile = true; + hs->contentFilePos = 0; + hs->left = fno.fsize; + hs->handle = NULL; + hs->retries = 0; + + return ERR_OK; + } + + + + + + + +// get_http_headers(hs, "401"); +// return ERR_OK; + + + if (hs->auth == AUTH_NONE) { + + char dirWebDir[64]; + FRESULT fr; + FILINFO fno; + + dirWebDir[0] = '\0'; + strcat(dirWebDir, "1:/WEB"); + strcat(dirWebDir, uri); + + fr = f_stat(dirWebDir, &fno); + + if (fr != FR_OK) { + get_http_headers(hs, "501.html"); + return ERR_OK; + } + } + +// hs->auth = AUTH_ADMIN; + httpSettings.auth = hs->auth; + + +/* + if (strcmp(uri, "/app.html") == 0) { + + if (hs->auth == AUTH_NONE) + strcpy(uri, "/login.html"); + + + } else if (strcmp(uri, "/login.html") == 0) { + + if (hs->auth != AUTH_NONE) + strcpy(uri, "/app.html"); + + + } else { + if (hs->auth == AUTH_NONE) { + + char dirWebDir[64]; + FRESULT fr; + FILINFO fno; + + dirWebDir[0] = '\0'; + strcat(dirWebDir, "1:/WEB"); + strcat(dirWebDir, uri); + + fr = f_stat(dirWebDir, &fno); + + if (fr != FR_OK) { + get_http_headers(hs, "403"); + return ERR_OK; + } + } + } +*/ + +// httpSettings.auth = hs->auth; +// if (hs->auth == AUTH_NONE) { +// get_http_headers(hs, "401"); +// return ERR_OK; +// } + + idPostResult_t resultGet = Post_MUTEX_ERR; + + if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) == osOK) { + + resultGet = httpd_get_begin(&httpSettings, hs->auth, uri, params, + &hs->isContentFile, + hs->fileNameContent, + hs->bufAnswer, +// bigBuffer, +// &hs->isBigBuffer, + &hs->left); + + + + osMutexRelease(httpSettings.accessHTTP); + } + + + if (resultGet == Post_OK) { + + get_http_headers(hs, "get.json"); + +// if (hs->isBigBuffer) { +// hs->file = bigBuffer; +// } else { + hs->file = hs->bufAnswer; + +// } + + hs->handle = NULL; + hs->retries = 0; + + return ERR_OK; + } else { + + char dir[MAX_LEN_PATH_FS]; + if (strlen(uri) < sizeof(dir) + 4) { + FRESULT fr; + FILINFO fno; + + dir[0] = '\0'; + //strcat(dir, "1:"); + strcat(dir, dir_web); + strcat(dir, uri); + + fr = f_stat(dir, &fno); + + if (fr == FR_OK) { + + // Получение указателя на расширение файла + char *pExt = getVoidExt((char *) uri); + + if (strcmp(pExt, ".html") == 0) { + hs->isExpires = false; + } else { + hs->isExpires = true; + } + + get_http_headers(hs, uri); + + hs->fileNameContent[0] = '\0'; + strcat(hs->fileNameContent, dir); + + hs->file = hs->bufAnswer; + hs->isContentFile = true; + hs->contentFilePos = 0; + hs->left = fno.fsize; //1024*1024 * 10; + hs->handle = NULL; + hs->retries = 0; + + return ERR_OK; + } + + + dir[0] = '\0'; + strcat(dir, "1:"); + strcat(dir, uri); + + fr = f_stat(dir, &fno); + + if (fr == FR_OK) { + get_http_headers(hs, uri); + + hs->fileNameContent[0] = '\0'; + strcat(hs->fileNameContent, dir); + + hs->file = hs->bufAnswer; + hs->isContentFile = true; + hs->contentFilePos = 0; + hs->left = fno.fsize; + hs->handle = NULL; + hs->retries = 0; + + return ERR_OK; + } + + + } + + + } + + + if (strcmp(uri, "200.json") == 0) { + get_http_headers(hs, uri); + + hs->handle = NULL; + hs->file = hs->bufAnswer; +// hs->file = hs->settings_post.bufAnswer; + + hs->retries = 0; + + if (hs->settings_post.isAnswerGeneration == false) { + hs->left = json_generate_err_answer(hs->bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, hs->settings_post.result); +// hs->left = json_generate_err_answer(hs->settings_post.bufAnswer, sizeof(hs->settings_post.bufAnswer), hs->settings_post.result); + } else { + hs->left = hs->settings_post.lenAnswerGeneration; + } + + return ERR_OK; + } + + file = http_get_404_file(hs, &uri); +// } +#if LWIP_HTTPD_SSI + if (file != NULL) { + if (file->flags & FS_FILE_FLAGS_SSI) { + tag_check = 1; + } else { +#if LWIP_HTTPD_SSI_BY_FILE_EXTENSION + tag_check = http_uri_is_ssi(file, uri); +#endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */ + } + } +#endif /* LWIP_HTTPD_SSI */ + } + if (file == NULL) { + /* None of the default filenames exist so send back a 404 page */ + file = http_get_404_file(hs, &uri); + } + return http_init_file(hs, file, is_09, uri, tag_check, params); +} + +/** Initialize a http connection with a file to send (if found). + * Called by http_find_file and http_find_error_file. + * + * @param hs http connection state + * @param file file structure to send (or NULL if not found) + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @param uri the HTTP header URI + * @param tag_check enable SSI tag checking + * @param params != NULL if URI has parameters (separated by '?') + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, + u8_t tag_check, char *params) { +#if !LWIP_HTTPD_SUPPORT_V09 + LWIP_UNUSED_ARG(is_09); +#endif + if (file != NULL) { + /* file opened, initialise struct http_state */ +#if !LWIP_HTTPD_DYNAMIC_FILE_READ + /* If dynamic read is disabled, file data must be in one piece and available now */ + LWIP_ASSERT("file->data != NULL", file->data != NULL); +#endif + +#if LWIP_HTTPD_SSI + if (tag_check) { + struct http_ssi_state *ssi = http_ssi_state_alloc(); + if (ssi != NULL) { + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; + ssi->parsed = file->data; + ssi->parse_left = file->len; + ssi->tag_end = file->data; + hs->ssi = ssi; + } + } +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(tag_check); +#endif /* LWIP_HTTPD_SSI */ + hs->handle = file; +#if LWIP_HTTPD_CGI_SSI + if (params != NULL) { + /* URI contains parameters, call generic CGI handler */ + int count; +#if LWIP_HTTPD_CGI + if (http_cgi_paramcount >= 0) { + count = http_cgi_paramcount; + } else +#endif + { + count = extract_uri_parameters(hs, params); + } + httpd_cgi_handler(file, uri, count, http_cgi_params, http_cgi_param_vals +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , file->state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + } +#else /* LWIP_HTTPD_CGI_SSI */ + LWIP_UNUSED_ARG(params); +#endif /* LWIP_HTTPD_CGI_SSI */ + hs->file = file->data; + LWIP_ASSERT("File length must be positive!", (file->len >= 0)); +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file && (file->data == NULL)) { + /* custom file, need to read data first (via fs_read_custom) */ + hs->left = 0; + } else +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + { + hs->left = (u32_t) file->len; + } + hs->retries = 0; +#if LWIP_HTTPD_TIMING + hs->time_started = sys_now(); +#endif /* LWIP_HTTPD_TIMING */ +#if !LWIP_HTTPD_DYNAMIC_HEADERS + LWIP_ASSERT("HTTP headers not included in file system", + (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0); +#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_V09 + if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) { + /* HTTP/0.9 responses are sent without HTTP header, + search for the end of the header. */ + char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left); + if (file_start != NULL) { + int diff = file_start + 4 - hs->file; + hs->file += diff; + hs->left -= (u32_t) diff; + } + } +#endif /* LWIP_HTTPD_SUPPORT_V09*/ + } else { + hs->handle = NULL; + hs->file = NULL; + hs->left = 0; + hs->retries = 0; + } +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Determine the HTTP headers to send based on the file extension of + * the requested URI. */ + if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) { + get_http_headers(hs, uri); + } +#else /* LWIP_HTTPD_DYNAMIC_HEADERS */ + LWIP_UNUSED_ARG(uri); +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { +#if LWIP_HTTPD_SSI + if (hs->ssi != NULL) { + hs->keepalive = 0; + } else +#endif /* LWIP_HTTPD_SSI */ + { + if ((hs->handle != NULL) && + ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { + hs->keepalive = 0; + } + } + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + return ERR_OK; +} + +/** + * The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + */ +static void +http_err(void *arg, err_t err) { + struct http_state *hs = (struct http_state *) arg; + LWIP_UNUSED_ARG(err); + + LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); + + if (hs != NULL) { + http_state_free(hs); + } +} + +/** + * Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + */ +static err_t +http_sent(void *arg, struct altcp_pcb *pcb, u16_t len) { + struct http_state *hs = (struct http_state *) arg; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void *) pcb)); + + LWIP_UNUSED_ARG(len); + + if (hs == NULL) { + return ERR_OK; + } + + hs->retries = 0; + + //cfif + + http_send(pcb, hs); + + return ERR_OK; +} + +/** + * The poll function is called every 2nd second. + * If there has been no data sent (which resets the retries) in 8 seconds, close. + * If the last portion of a file has not been sent in 2 seconds, close. + * + * This could be increased, but we don't want to waste resources for bad connections. + */ +static err_t +http_poll(void *arg, struct altcp_pcb *pcb) { + struct http_state *hs = (struct http_state *) arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", + (void *) pcb, (void *) hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb)))); + + if (hs == NULL) { + err_t closed; + /* arg is null, close. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); + closed = http_close_conn(pcb, NULL); + LWIP_UNUSED_ARG(closed); +#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR + if (closed == ERR_MEM) { + altcp_abort(pcb); + return ERR_ABRT; + } +#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ + return ERR_OK; + } else { + hs->retries++; + if (hs->retries == HTTPD_MAX_RETRIES) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); + http_close_conn(pcb, hs); + return ERR_OK; + } + + /* If this connection has a file open, try to send some more data. If + * it has not yet received a GET request, don't do this since it will + * cause the connection to close immediately. */ + if (hs->handle) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); + if (http_send(pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + altcp_output(pcb); + } + } + } + + return ERR_OK; +} + +/** + * Data has been received on this pcb. + * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). + */ +static err_t +http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) { +#ifndef CUSTOM_HTTP_SERVER + struct http_state *hs = (struct http_state *) arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *) pcb, + (void *) p, lwip_strerr(err))); + + if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { + /* error or closed by other side? */ + if (p != NULL) { + /* Inform TCP that we have taken the data. */ + altcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + if (hs == NULL) { + /* this should not happen, only to be robust */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); + } + http_close_conn(pcb, hs); + return ERR_OK; + } + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->no_auto_wnd) { + hs->unrecved_bytes += p->tot_len; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + { + /* Inform TCP that we have taken the data. */ + altcp_recved(pcb, p->tot_len); + } + +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left > 0) { + /* reset idle counter when POST data is received */ + hs->retries = 0; + /* this is data for a POST, pass the complete pbuf to the application */ + http_post_rxpbuf(hs, p); + /* pbuf is passed to the application, don't free it! */ + if (hs->post_content_len_left == 0) { + /* all data received, send response or close connection */ + http_send(pcb, hs); + } + return ERR_OK; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + if (hs->handle == NULL) { + err_t parsed = http_parse_request(p, hs, pcb); + LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK + || parsed == ERR_INPROGRESS || + parsed == ERR_ARG || parsed == ERR_USE); +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (parsed != ERR_INPROGRESS) { + /* request fully parsed or error */ + if (hs->req != NULL) { + pbuf_free(hs->req); + hs->req = NULL; + } + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + pbuf_free(p); + if (parsed == ERR_OK) { +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left == 0) +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, + ("http_recv: data %p len %"S32_F"\n", (const void *) hs->file, hs->left)); + + http_send(pcb, hs); + + } + } else if (parsed == ERR_ARG) { + /* @todo: close on ERR_USE? */ + http_close_conn(pcb, hs); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); + /* already sending but still receiving data, we might want to RST here? */ + pbuf_free(p); + } + } + return ERR_OK; +#else + int i, j; + char *data; + char fname[40]; + struct fs_file file = {0, 0}; + struct http_state *hs; + char LED2_html[32] = {0}; + char LED3_html[32] = {0}; + char LED4_html[32] = {0}; + char LED2_Status = 0, LED3_Status = 0, LED4_Status = 0; + char *Led_Select = "checked=\"checked\""; + + 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; + if (strncmp(data, "GET /AT32F407ADC", 16) == 0) + { + int ADCVal = 0, iADCVa; + float fADCVal = 0; + + pbuf_free(p); + + ADCVal = ADC_GetConversionValue(ADC1); + printf("ADCVal = %d\n", ADCVal); + fADCVal = (float)((float)ADCVal / (float)4096.00) * (float)3.3; + + iADCVa = ((ADCVal * 100 ) / 4096 ) * 512 / 100; + + fs_open(&file, "/AT32F407ADC.html"); + sprintf(html_tmp, file.data, fADCVal, iADCVa); + hs->file = html_tmp; + hs->left = strlen(html_tmp); + + http_send(pcb, hs); + + /* Tell TCP that we wish be to informed of data that has been + successfully sent by a call to the http_sent() function. */ + tcp_sent(pcb, http_sent); + } + else if (strncmp(data, "GET /AT32F407LED", 16) == 0) + { + fs_open(&file, "/AT32F407LED.html"); + hs->file = file.data; + hs->left = file.len; + + http_send(pcb, hs); + + /* Tell TCP that we wish be to informed of data that has been + successfully sent by a call to the http_sent() function. */ + tcp_sent(pcb, http_sent); + } + else if (strncmp(data, "GET /method=get", 15) == 0) + { + i = 15; + while(data[i]!=0x20/* */) + { + i++; + if (data[i] == 0x6C /* l */) + { + i++; + if (data[i] == 0x65 /* e */) + { + i++; + if (data[i] == 0x64 /* d*/) + { + i+=2; + + if(data[i]==0x32 /* 2 */) + { + AT32_LEDn_ON(LED2); + memcpy(LED2_html, Led_Select, strlen(Led_Select)); + LED2_Status = 1; + } + + if(data[i]==0x33 /* 3 */) + { + AT32_LEDn_ON(LED3); + memcpy(LED3_html, Led_Select, strlen(Led_Select)); + LED3_Status = 1; + } + + if(data[i]==0x34 /* 4 */) + { + AT32_LEDn_ON(LED4); + memcpy(LED4_html, Led_Select, strlen(Led_Select)); + LED4_Status = 1; + } + } + } + } + } + if ( LED2_Status == 0) + { + AT32_LEDn_OFF(LED2); + } + if ( LED3_Status == 0 ) + { + AT32_LEDn_OFF(LED3); + } + if ( LED4_Status == 0 ) + { + AT32_LEDn_OFF(LED4); + } + pbuf_free(p); + + fs_open(&file, "/AT32F407LED.html"); + sprintf(html_tmp, file.data, LED2_html, LED3_html, LED4_html); + hs->file = html_tmp; + hs->left = strlen(html_tmp); + + http_send(pcb, hs); + + /* Tell TCP that we wish be to informed of data that has been + successfully sent by a call to the http_sent() function. */ + tcp_sent(pcb, http_sent); + } + else if (strncmp(data, "GET ", 4) == 0) + { + for (i = 0; i < 40; i++) + { + if (((char *)data + 4)[i] == ' ' || + ((char *)data + 4)[i] == '\r' || + ((char *)data + 4)[i] == '\n') + { + ((char *)data + 4)[i] = 0; + } + } + + i = 0; + j = 0; + + do + { + fname[i] = ((char *)data + 4)[j]; + j++; + i++; + } while (fname[i - 1] != 0 && i < 40); + + pbuf_free(p); + + + fs_open(&file, "/AT32F407.html"); + + hs->file = file.data; + hs->left = file.len; + http_send(pcb, hs); + + /* Tell TCP that we wish be to informed of data that has been + successfully sent by a call to the http_sent() function. */ + tcp_sent(pcb, http_sent); + } + else + { + http_close_conn(pcb, hs); + } + } + else + { + pbuf_free(p); + } + } + + if (err == ERR_OK && p == NULL) + { + + http_close_conn(pcb, hs); + } + + return ERR_OK; +#endif +} + +/** + * A new incoming connection has been accepted. + */ +static err_t +http_accept(void *arg, struct altcp_pcb *pcb, err_t err) { + struct http_state *hs; + LWIP_UNUSED_ARG(err); + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *) pcb, arg)); + + if ((err != ERR_OK) || (pcb == NULL)) { + return ERR_VAL; + } + + /* Set priority */ + altcp_setprio(pcb, HTTPD_TCP_PRIO); + + /* Allocate memory for the structure that holds the state of the + connection - initialized by that function. */ + hs = http_state_alloc(); + if (hs == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); + return ERR_MEM; + } + hs->pcb = pcb; + + /* Tell TCP that this is the structure we wish to be passed for our + callbacks. */ + altcp_arg(pcb, hs); + + /* Set up the various callback functions */ + altcp_recv(pcb, http_recv); + altcp_err(pcb, http_err); + altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + altcp_sent(pcb, http_sent); + + return ERR_OK; +} + +static void +httpd_init_pcb(struct altcp_pcb *pcb, u16_t port) { + err_t err; + + if (pcb) { + altcp_setprio(pcb, HTTPD_TCP_PRIO); + /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ + err = altcp_bind(pcb, IP_ANY_TYPE, port); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); + pcb = altcp_listen(pcb); + LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); + altcp_accept(pcb, http_accept); + } +} + +/** + * @ingroup httpd + * Initialize the httpd: set up a listening PCB and bind it to the defined port + */ +void httpd_init(tExternal *external, tRtcIO *rtc, tTaskAdc *taskAdc, tDeviceStorageIni *storage, tFs *fs, + tSpiPorts *spiPorts, + tFirmwareLoader *firmwareMainLoader, + tFirmwareLoader *firmwareBootLoader, + tModemMain *modemMain, osMutexId_t accessHTTP, osMutexId_t accessMODEM, osMutexId_t accessLOG, + osMutexId_t accessLOG2, + tGpios *gpios, tFileLogger *flog, tModemMainLog *modemMainLog) { + +// httpSettings.authUser.countAuth = 0; + httpSettings.external = external; + httpSettings.rtc = rtc; + httpSettings.storage = storage; + httpSettings.spiPorts = spiPorts; + httpSettings.fs = fs; + httpSettings.firmwareMainLoader = firmwareMainLoader; + httpSettings.firmwareBootLoader = firmwareBootLoader; + httpSettings.accessHTTP = accessHTTP; + httpSettings.accessMODEM = accessMODEM; + httpSettings.idTempFileOutBox = 0; + httpSettings.idTempFileKey = 0; + httpSettings.countTmp = 0; + httpSettings.modemMain = modemMain; + httpSettings.accessLOG = accessLOG; + httpSettings.accessLOG2 = accessLOG2; + httpSettings.taskAdc = taskAdc; + httpSettings.gpios = gpios; + httpSettings.flog = flog; + httpSettings.auth = AUTH_NONE; + httpSettings.modemMainLog = modemMainLog; + + struct altcp_pcb *pcb; + +#if HTTPD_USE_MEM_POOL + LWIP_MEMPOOL_INIT(HTTPD_STATE); +#if LWIP_HTTPD_SSI + LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE); +#endif +#endif + LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); + + /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */ + + pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); + httpd_init_pcb(pcb, HTTPD_SERVER_PORT); +} + +#if HTTPD_ENABLE_HTTPS +/** + * @ingroup httpd + * Initialize the httpd: set up a listening PCB and bind it to the defined port. + * Also set up TLS connection handling (HTTPS). + */ +void +httpd_inits(struct altcp_tls_config *conf) +{ +#if LWIP_ALTCP_TLS + struct altcp_pcb *pcb_tls = altcp_tls_new(conf, IPADDR_TYPE_ANY); + LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL); + httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS); +#else /* LWIP_ALTCP_TLS */ + LWIP_UNUSED_ARG(conf); +#endif /* LWIP_ALTCP_TLS */ +} +#endif /* HTTPD_ENABLE_HTTPS */ + +#if LWIP_HTTPD_SSI +/** + * @ingroup httpd + * Set the SSI handler function. + * + * @param ssi_handler the SSI handler function + * @param tags an array of SSI tag strings to search for in SSI-enabled files + * @param num_tags number of tags in the 'tags' array + */ +void +http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) +{ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); + + LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); + httpd_ssi_handler = ssi_handler; + +#if LWIP_HTTPD_SSI_RAW + LWIP_UNUSED_ARG(tags); + LWIP_UNUSED_ARG(num_tags); +#else /* LWIP_HTTPD_SSI_RAW */ + LWIP_ASSERT("no tags given", tags != NULL); + LWIP_ASSERT("invalid number of tags", num_tags > 0); + + httpd_tags = tags; + httpd_num_tags = num_tags; +#endif /* !LWIP_HTTPD_SSI_RAW */ +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI + +/** + * @ingroup httpd + * Set an array of CGI filenames/handler functions + * + * @param cgis an array of CGI filenames/handler functions + * @param num_handlers number of elements in the 'cgis' array + */ +void +http_set_cgi_handlers(const tCGI *cgis, int num_handlers) { + LWIP_ASSERT("no cgis given", cgis != NULL); + LWIP_ASSERT("invalid number of handlers", num_handlers > 0); + + httpd_cgis = cgis; + httpd_num_cgis = num_handlers; +} + +#endif /* LWIP_HTTPD_CGI */ + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ + + +//------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------- +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/apps/fs.h" +#include +#include "httpd.h" +#include "httpd_post.h" +#include "Settings_RS485_Bluetooth.h" +#include "httpd_base_func.h" +#include "JSONSettings.h" +#include "httpd_TablesPostGet.h" +//#include "stream.h" + +bool isPostBusy = false; + +tHttpSetting httpSettings; + +void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len) { + struct http_state *hs = (struct http_state *) connection; + + + if (hs == NULL) { + isPostBusy = false; + return; + } + + if (hs->settings_post.result != Post_404_ERR) { + strcpy(response_uri, "200.json"); + } + + if (hs->settings_post.result == Post_OK) { + + // Запись файла + if (hs->settings_post.responsePostId == ID_Post_Message_Create_Cmd) { + + for (int i = 0; i < hs->settings_post.params_post_uri.paramcount; ++i) { + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "wrfile") == 0) { + + hs->settings_post.result = handlerPost_Message_Outbox_Wrfile(&hs->settings_post); + + } + + } + + } + + // Запись произвольного файла в файловую систему + if (hs->settings_post.responsePostId == ID_Post_Write_Other_File) { + hs->settings_post.result = handlerPost_Write_Other_File(&hs->settings_post); + } + + // Сохранение настроек + if (hs->settings_post.responsePostId == ID_Post_Var) { + emac_stop(); + hs->settings_post.result = handlerPost_Var(&hs->settings_post, httpSettings.auth); + emac_start(); + } + + // Загрузка ключей + if (hs->settings_post.responsePostId == ID_Post_Key_Load) { + hs->settings_post.result = handlerPost_Key_Load(&hs->settings_post); + } + + // Запись прошивки WEB + if (hs->settings_post.responsePostId == ID_Post_Update_Web_File) { + + emac_stop(); + webup_status = 255; + hs->settings_post.result = handlerPost_Update_Web_File(&hs->settings_post); + webup_status = hs->settings_post.result; + emac_start(); + } + + // Запись ОСНОВНОЙ прошивки запись + if (hs->settings_post.responsePostId == ID_Post_Update_Main_Write_File) { + hs->settings_post.result = handlerPost_Update_File_WriteFlash(&hs->settings_post, FIRMWARE_XFSB_MAIN); + + } + + // Запись ОСНОВНОЙ прошивки инициализация + if (hs->settings_post.responsePostId == ID_Post_Update_Main_Init_File) { + emac_stop(); + hs->settings_post.result = handlerPost_Update_File_EraseFlash(&hs->settings_post, FIRMWARE_XFSB_MAIN); + emac_start(); + + } + + + + // Запись ЗАГРУЗЧИКА прошивки запись + if (hs->settings_post.responsePostId == ID_Post_Update_Boot_Write_File) { + hs->settings_post.result = handlerPost_Update_File_WriteFlash(&hs->settings_post, FIRMWARE_XFSB_BOOT); + } + + // Запись ЗАГРУЗЧИКА прошивки инициализация + if (hs->settings_post.responsePostId == ID_Post_Update_Boot_Init_File) { + emac_stop(); + hs->settings_post.result = handlerPost_Update_File_EraseFlash(&hs->settings_post, FIRMWARE_XFSB_BOOT); + emac_start(); + } + + + + // Инициализация записи прошивки крипто-платы + if (hs->settings_post.responsePostId == ID_Post_Update_Init_Write_Crypto) { + hs->settings_post.result = handlerPost_Update_Init_Crypto_WriteFlash(&hs->settings_post); + } + + // Запись прошивки криптоплаты + if (hs->settings_post.responsePostId == ID_Post_Update_Write_Crypto) { + hs->settings_post.result = handlerPost_Update_Crypto_WriteFlash(&hs->settings_post); + } + + // Запуск крипто-платы после прошивки + if (hs->settings_post.responsePostId == ID_Post_Update_Start_Crypto) { + hs->settings_post.result = handlerPost_Update_Start_Crypto(&hs->settings_post); + } + + // Запись прошивки модема + if (hs->settings_post.responsePostId == ID_Post_Update_Write_Modem) { + hs->settings_post.result = handlerPost_Update_Modem_WriteFlash(&hs->settings_post); + } + + // Инициализация записи прошивки модема + if (hs->settings_post.responsePostId == ID_Post_Update_Init_Write_Modem) { + emac_stop(); + hs->settings_post.result = handlerPost_Update_Init_Modem_WriteFlash(&hs->settings_post); + emac_start(); + } + + // Запуск модема после прошивки + if (hs->settings_post.responsePostId == ID_Post_Update_Start_Modem) { + emac_stop(); + hs->settings_post.result = handlerPost_Update_Start_Modem(&hs->settings_post); + emac_start(); + } + + // Запуск платы после прошивки + if (hs->settings_post.responsePostId == ID_Post_Update_Start_Main) { + hs->settings_post.result = handlerPost_Update_Start_Main(&hs->settings_post); + } + + if (hs->settings_post.responsePostId == ID_Post_sendmsg2) { + time_t timestamp = 0; + httpSettings.rtc->get(&httpSettings.rtc, ×tamp); + timestamp += httpSettings.storage->nvm.Settings_General.GMTcorr_v * 3600; + + hs->settings_post.result = handlerGet_Message_Outbox_CreateTempOrRealFile_Sendmsg(&httpSettings, + true, + &hs->settings_post.createPostData.filename[2], + 0, + 0, + timestamp, + hs->settings_post.createPostData.to, + hs->settings_post.createPostData.urgency, + hs->settings_post.createPostData.chSv, + hs->settings_post.createPostData.isfile, + hs->settings_post.createPostData.kvs, + TYPE_FILE_MESSAGE, + 0); + + + hs->settings_post.rtc->get(&hs->settings_post.rtc, + &hs->settings_post.modemMain->eventWebState.EVENT_OUTBOX); + } + + } + + isPostBusy = false; + +} + +err_t httpd_post_receive_data(void *connection, struct pbuf *p) { + + struct http_state *hs = (struct http_state *) connection; + + if (hs == NULL) { + return ERR_OK; + } + + if (hs->settings_post.result == Post_OK) { + + if (hs->settings_post.responsePostId == ID_Post_sendmsg2) { + + pbuf_copy_partial(p, &hs->settings_post.bufAnswer[0], p->tot_len, 0); + + uint32_t offset = 0; + + if (hs->settings_post.isFirst) { + + hs->settings_post.createPostData.chSv = 1; + hs->settings_post.createPostData.isfile = 0; + hs->settings_post.createPostData.kvs = 0; + hs->settings_post.createPostData.urgency = 0; + hs->settings_post.createPostData.routing = 0; + + uint32_t stepParam = 0; + + hs->settings_post.params_post_uri.paramcount = extract_uri_ex_parameters( + &hs->settings_post.bufAnswer[0], + hs->settings_post.params_post_uri.params_names, + hs->settings_post.params_post_uri.params_vals, + MAX_POST_GET_PARAMETERS); + + + for (int i = 0; i < hs->settings_post.params_post_uri.paramcount; ++i) { + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "urgency") == 0) { + hs->settings_post.createPostData.urgency = atoi( + hs->settings_post.params_post_uri.params_vals[i]); + } + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "chSv") == 0) { + hs->settings_post.createPostData.chSv = atoi(hs->settings_post.params_post_uri.params_vals[i]); + } + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "kvs") == 0) { + hs->settings_post.createPostData.kvs = atoi(hs->settings_post.params_post_uri.params_vals[i]); + } + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "to") == 0) { + extract_utf8_parameters(hs->settings_post.params_post_uri.params_vals[i], + hs->settings_post.createPostData.to, + sizeof(hs->settings_post.createPostData.to)); + ++stepParam; + } + + char subj[12]; + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "subj") == 0) { + extract_utf8_parameters(hs->settings_post.params_post_uri.params_vals[i], subj, sizeof(subj)); + + ++stepParam; + + if (strcmp(subj, "file") == 0) { + hs->settings_post.createPostData.isfile = 1; + } + } + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "routing") == 0) { + hs->settings_post.createPostData.routing = atoi( + hs->settings_post.params_post_uri.params_vals[i]); + } + + if (strcmp(hs->settings_post.params_post_uri.params_names[i], "msg") == 0) { + ++stepParam; + + + if (hs->settings_post.createPostData.isfile == 1) { + + hs->settings_post.createPostData.filename[0] = '\0'; + strcat(hs->settings_post.createPostData.filename, "1:/TMP/"); + extract_utf8_parameters(hs->settings_post.params_post_uri.params_vals[i], + &hs->settings_post.createPostData.filename[strlen( + hs->settings_post.createPostData.filename)], + sizeof(hs->settings_post.createPostData.filename) - + strlen(hs->settings_post.createPostData.filename)); + + + offset = hs->settings_post.params_post_uri.params_vals[i] - hs->settings_post.bufAnswer; + for (int j = hs->settings_post.params_post_uri.params_vals[i] - hs->settings_post.bufAnswer; + j < (int) p->tot_len; ++j) { + if (hs->settings_post.bufAnswer[j] == '\0') { + ++offset; + break; + } + ++offset; + } + } else { + hs->settings_post.createPostData.filename[0] = '\0'; + strcat(hs->settings_post.createPostData.filename, "1:/TMP/tmp.dat"); + + offset = hs->settings_post.params_post_uri.params_vals[i] - hs->settings_post.bufAnswer; + } + + } + } + + if (stepParam < 3) { + hs->settings_post.result = Post_PARAM_ERR; + return ERR_OK; + } + + handlerPost_Message_Outbox_DynamicWrfile(&hs->settings_post, offset, p->tot_len - offset); + + hs->settings_post.isFirst = false; + + } else { + handlerPost_Message_Outbox_DynamicWrfile(&hs->settings_post, 0, p->tot_len); + } + + } else { + + if ((hs->settings_post.step_packet_len + p->tot_len) > LEN_BUF_SMALL_ANSWER_POST_HTTP) { + pbuf_free(p); + hs->settings_post.result = Post_SIZE_ERR; + return ERR_OK; + } + + pbuf_copy_partial(p, &hs->settings_post.bufAnswer[hs->settings_post.step_packet_len], p->tot_len, 0); + //memcpy(&hs->settings_post.bufAnswer[hs->settings_post.step_packet_len], p->payload, p->tot_len); + + hs->settings_post.step_packet_len += p->tot_len; + } + + pbuf_free(p); + + + } else { + pbuf_free(p); + } + + return ERR_OK; +} + + +err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, + u16_t http_request_len, int content_len, char *response_uri, + u16_t response_uri_len, u8_t *post_auto_wnd) { + + + struct http_state *hs = (struct http_state *) connection; + + if (hs->auth == AUTH_NONE) { + return ERR_ARG; + } + + if (isPostBusy) { + isPostBusy = false; + return ERR_ARG; + } + + hs->url[0] = 0; + strcpy(hs->url, uri); + + hs->settings_post.isFirst = true; + + //hs->settings_post.bufAnswer = hs->bufAnswer; + hs->settings_post.bufAnswer = bufAnswerPostGlobal; + hs->settings_post.rtc = httpSettings.rtc; + hs->settings_post.storage = httpSettings.storage; + hs->settings_post.spiPorts = httpSettings.spiPorts; + hs->settings_post.fs = httpSettings.fs; + hs->settings_post.modemMain = httpSettings.modemMain; + hs->settings_post.firmwareMainLoader = httpSettings.firmwareMainLoader; + hs->settings_post.firmwareBootLoader = httpSettings.firmwareBootLoader; + hs->settings_post.accessHTTP = httpSettings.accessHTTP; + hs->settings_post.accessMODEM = httpSettings.accessMODEM; + hs->settings_post.external = httpSettings.external; +// hs->settings_post.offset_update = 0; + hs->postLeft = 0; +// hs->settings_post.authUser = &httpSettings.authUser; + hs->settings_post.gpios = httpSettings.gpios; + + hs->settings_post.isAnswerGeneration = false; + + hs->settings_post.result = Post_OK; + hs->settings_post.content_len = content_len; + hs->settings_post.responsePostId = -1; + hs->settings_post.indexTableSettings = -1; + hs->settings_post.step_packet_len = 0; + + *post_auto_wnd = 1; + + int index = 0; + + char *params = NULL; + params = (char *) strchr(hs->url, '?'); + if (params != NULL) { + *params = '\0'; + params++; + } + + hs->settings_post.params_post_uri.paramcount = extract_uri_ex_parameters(params, + hs->settings_post.params_post_uri.params_names, + hs->settings_post.params_post_uri.params_vals, + MAX_POST_GET_PARAMETERS); + + + while (*(uint32_t *) &tablePosts[index] != 0) { + if (strcmp(hs->url, tablePosts[index].path) == 0) { + hs->settings_post.responsePostId = tablePosts[index].id; + + time_t timestamp; + hs->settings_post.rtc->get(&hs->settings_post.rtc, ×tamp); + hs->settings_post.modemMain->isEnableTracerTimeStamp = timestamp + 1; + + isPostBusy = true; + return ERR_OK; + } + ++index; + } + + + hs->settings_post.result = Post_404_ERR; + + return ERR_OK; +} + diff --git a/Src/httpd_TablesPostGet.c b/Src/httpd_TablesPostGet.c new file mode 100644 index 0000000..163b395 --- /dev/null +++ b/Src/httpd_TablesPostGet.c @@ -0,0 +1,55 @@ +// +// Created by cfif on 13.12.22. +// +#include "httpd_TablesPostGet.h" + +const struct tablePostGetSetting tablePostGetSettings[] = { + {.path = "/Settings_General", .json = &jsonSettings_General}, + {.path = "/Settings_Basic_Access", .json = &jsonSettings_Basic_Access}, + {.path = "/Settings_Frequency_Speed", .json = &jsonSettings_Frequency_Speed}, + {.path = "/Settings_Tracert", .json = &jsonSettings_Tracert}, + {.path = "/Settings_Transceiver", .json = &jsonSettings_Transceiver}, + {.path = "/Settings_Network", .json = &jsonSettings_Network}, + {.path = "/Settings_Date_Time", .json = &jsonSettings_Date_Time}, + {.path = "/Settings_RS485_Bluetooth", .json = &jsonSettings_RS485_Bluetooth}, + {NULL} +}; + +const struct tablePost tablePosts[] = { + {.path = "/var.xip", .id = ID_Post_Var}, + {.path = "/cmd.xip", .id = ID_Post_Message_Create_Cmd}, + {.path = "/keylo.xip", .id = ID_Post_Key_Load}, + {.path = "/Software_Update_Web_File", .id = ID_Post_Update_Web_File}, + {.path = "/Software_Init_File", .id = ID_Post_Update_Main_Init_File}, + {.path = "/Software_Write_File", .id = ID_Post_Update_Main_Write_File}, + {.path = "/Software_Write_Crypto", .id = ID_Post_Update_Write_Crypto}, + {.path = "/Software_Write_Modem", .id = ID_Post_Update_Write_Modem}, + {.path = "/Software_Init_Write_Modem", .id = ID_Post_Update_Init_Write_Modem}, + {.path = "/Software_Start_Modem", .id = ID_Post_Update_Start_Modem}, + {.path = "/Software_Init_Write_Crypto", .id = ID_Post_Update_Init_Write_Crypto}, + {.path = "/Software_Start_Crypto", .id = ID_Post_Update_Start_Crypto}, + {.path = "/Software_Start_Main", .id = ID_Post_Update_Start_Main}, + {.path = "/Write_Other_File", .id = ID_Post_Write_Other_File}, + {.path = "/sendmsg2.xip", .id = ID_Post_sendmsg2}, + {.path = "/Software_Boot_Init_File", .id = ID_Post_Update_Boot_Init_File}, + {.path = "/Software_Boot_Write_File", .id = ID_Post_Update_Boot_Write_File}, + {NULL} +}; + + +const struct tableGet tableGets[] = { + /* + {.path = "/Key_File", .id = ID_Get_Key_File, .method = &handlerKey_File}, + {.path = "/Messages_Inbox", .id = ID_Get_Messages_Inbox, .method = &handlerMessages_Inbox}, + {.path = "/Messages_Outbox", .id = ID_Get_Messages_Outbox, .method = &handlerMessages_Outbox}, + {.path = "/Messages_Sentbox", .id = ID_Get_Messages_Sentbox, .method = &handlerMessages_Sentbox}, + {.path = "/Messages_Content_Inbox", .id = ID_Get_Messages_Content_Inbox, .method = &handlerMessages_Content_Inbox}, + {.path = "/Messages_Content_Outbox", .id = ID_Get_Messages_Content_Outbox, .method = &handlerMessages_Content_Outbox}, + {.path = "/Messages_Content_Sentbox", .id = ID_Get_Messages_Content_Sentbox, .method = &handlerMessages_Content_Sentbox}, + {.path = "/Messages_Meta_Content_Inbox", .id = ID_Get_Messages_Meta_Content_Inbox, .method = &handlerMessages_Meta_Content_Inbox}, + {.path = "/Messages_Meta_Content_Outbox", .id = ID_Get_Messages_Meta_Content_Outbox, .method = &handlerMessages_Meta_Content_Outbox}, + {.path = "/Messages_Meta_Content_Sentbox", .id = ID_Get_Messages_Meta_Content_Sentbox, .method = &handlerMessages_Meta_Content_Sentbox}, + {.path = "/Get_Version", .id = ID_Get_Get_Version, .method = &handlerGet_Version}, + */ + {NULL} +}; diff --git a/Src/httpd_base_func.c b/Src/httpd_base_func.c new file mode 100644 index 0000000..b53c884 --- /dev/null +++ b/Src/httpd_base_func.c @@ -0,0 +1,403 @@ +// +// Created by cfif on 12.12.22. +// +#include "string.h" +#include "inttypes.h" +//#include "stream.h" +#include "httpd_base_func.h" +#include "stdio.h" +#include "AsciiStringParsingUtils.h" + + +int extract_mac_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max) { + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < params_max) && pair; loop++) { + + /* Save the name of the parameter */ + param_names[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, ':'); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '.'); + if (equals) { + *equals = '\0'; + param_vals[loop] = equals + 1; + } else { + param_vals[loop] = NULL; + } + } + + return loop; +} + + +int extract_ip_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max) { + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < params_max) && pair; loop++) { + + /* Save the name of the parameter */ + param_names[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '.'); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '.'); + if (equals) { + *equals = '\0'; + param_vals[loop] = equals + 1; + } else { + param_vals[loop] = NULL; + } + } + + return loop; +} + + +int extract_grp_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max) { + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < params_max) && pair; loop++) { + + /* Save the name of the parameter */ + param_names[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, ','); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, ','); + if (equals) { + *equals = '\0'; + param_vals[loop] = equals + 1; + } else { + param_vals[loop] = NULL; + } + } + + return loop; +} + +int extract_path_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max) { + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < params_max) && pair; loop++) { + + /* Save the name of the parameter */ + param_names[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '/'); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '/'); + if (equals) { + *equals = '\0'; + param_vals[loop] = equals + 1; + } else { + param_vals[loop] = NULL; + } + } + + return loop; +} + +int extract_uri_ex_parameters(char *params, char *param_names[], char *param_vals[], int params_max) { + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if (!params || (params[0] == '\0')) { + return (0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for (loop = 0; (loop < params_max) && pair; loop++) { + + /* Save the name of the parameter */ + param_names[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '&'); + if (pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if (pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '='); + if (equals) { + *equals = '\0'; + param_vals[loop] = equals + 1; + } else { + param_vals[loop] = NULL; + } + } + + return loop; +} + +int extract_utf8_parameters(char *inBuf, char *outBuf, int maxlen) { + unsigned char *p; + uint8_t n; + int count = 0; + + for (p = (unsigned char *) inBuf; *p; ++p) { + + if (*p == '%') { +// if (debug_scanf(p + 1, "%x", &n)) { + // ' + + n = iAsciiStringParseHexByte(p + 1); + +// if (scanf(p + 1, "%x", &n)) { + + if (n != 0x27) { + outBuf[count] = n; + ++count; + } + p += 2; +// } + } else { + outBuf[count] = *p; + ++count; + + if (count >= maxlen) + break; + } + } + + outBuf[count] = '\0'; + + return count; +} + +void StrToStr(char *str1, char *str2) { + while (*str1 == '0') + ++str1; + + if (strlen(str1) == 0) { + strcpy(str2, "0"); + } else { + strcpy(str2, str1); + } +} + +void string2hexString(char *input, int size, char *output) { + int loop; + int i; + + i = 0; + loop = 0; + + while (input[loop] != '\0') { + debug_sprintf((char *) (output + i), size, "%02X", input[loop]); + loop += 1; + i += 2; + } + + output[i++] = '\0'; +} + +// mapping of ASCII characters to hex values +const uint8_t hashmap[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // !"#$%&' + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ()*+,-./ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567 + 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>? + 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // PQRSTUVW + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // XYZ[\]^_ + 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // `abcdefg + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hijklmno + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pqrstuvw + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xyz{|}~. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ........ + }; + +uint8_t HexToData(char *str, uint8_t *bytes) { + uint8_t pos; + uint8_t idx0; + uint8_t idx1; + + for (pos = 0; ((pos < (strlen(str))) && (pos < strlen(str))); pos += 2) { + idx0 = (uint8_t) str[pos + 0]; + idx1 = (uint8_t) str[pos + 1]; + bytes[pos / 2] = (uint8_t) (hashmap[idx0] << 4) | hashmap[idx1]; + } + + bytes[pos / 2] = 0; + + return strlen(str) / 2; +} + diff --git a/Src/httpd_get.c b/Src/httpd_get.c new file mode 100644 index 0000000..cc39e96 --- /dev/null +++ b/Src/httpd_get.c @@ -0,0 +1,1687 @@ +// +// Created by cfif on 10.11.22. +// +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/apps/fs.h" +#include +#include "httpd.h" +#include "httpd_get.h" +#include "httpd_TablesPostGet.h" +#include "httpd_base_func.h" +#include "Gnss.h" +#include "ModemGonecFunc.h" +#include "stdlib.h" +#include "BootJump.h" +#include "ModemLog.h" +#include "Modem_Timer.h" +#include "ModemSendFunc.h" +#include "ModemBufCreateHeader.h" + +extern uint32_t webup_status; + +//uint8_t random2[20] = {80, 82, 85, 81, 86, 90, 95, 97, 98, 110, 102, 103, 104, 99, 90, 89, 88, 86, 84, 83}; +//uint8_t random2_i = 0; + +int httpd_get_begin(tHttpSetting *httpSetting, typeAuth auth, const char *uri, char *params, + bool *isContentFile, + char *fileNameContent, + char *bufAnswer, +// char *bigBuffer, +// bool *isBigBuffer, + uint32_t *tot_len) { + + *isContentFile = false; + *tot_len = 0; +// *isBigBuffer = false; + +// httpSetting->isContentFile = isContentFile; +// httpSetting->tot_len = tot_len; +// httpSetting->bufAnswer = bufAnswer; +// httpSetting->isBigBuffer = isBigBuffer; + + httpSetting->params_get_uri.paramcount = extract_uri_ex_parameters(params, httpSetting->params_get_uri.params_names, + httpSetting->params_get_uri.params_vals, + MAX_POST_GET_PARAMETERS); + + int pos = -1; + int len = -1; + + char to[MAX_LEN_PATH_FS]; + to[0] = '\0'; + int urgency = -1; + int chSv = -1; + int isfile = -1; + int kvs = -1; + int routing = -1; + + char fileName[MAX_LEN_PATH_FS]; + fileName[0] = '\0'; + + + //начало--------Общий запрос данных------------------------------------------------------------------ + //начало--------Общий запрос данных------------------------------------------------------------------ + //начало--------Общий запрос данных------------------------------------------------------------------ + + if (strcmp(uri, "/getdata") == 0) { + + + time_t timestamp_block; + httpSetting->rtc->get(httpSetting->rtc, ×tamp_block); + + httpSetting->modemMain->timestamp_start_block_work = + (uint32_t) timestamp_block - httpSetting->modemMain->timestamp_start_block; + + + uint32_t get_timestamp_gnss; + Gnss_GetTime(httpSetting->modemMain->gnss, &get_timestamp_gnss); + + uint32_t vcc = getVcc(httpSetting->taskAdc); + + *tot_len = json_generate_get_data(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + get_timestamp_gnss, + timestamp_block + httpSetting->storage->nvm.Settings_General.GMTcorr_v * 3600, + timestamp_block, + httpSetting->modemMain->timestamp_start_block_work + + httpSetting->modemMain->timestamp_start_block_work_add, + + vcc, + + httpSetting->modemMain->temp, + + httpSetting->modemMain->dataRssi, + + httpSetting->storage->nvm.Settings_Tracert.mTracer_v, + + httpSetting->modemMain->location.valid, + (float) httpSetting->modemMain->location.latitude, + (float) httpSetting->modemMain->location.longitude, + + &httpSetting->modemMain->eventWebState, + getStateWriteFileLogger(httpSetting->flog), + getStateWriteLog2(httpSetting->modemMainLog) + + ); + + return Post_OK; + } + + //конец--------Общий запрос данных------------------------------------------------------------------- + //конец--------Общий запрос данных------------------------------------------------------------------- + //конец--------Общий запрос данных------------------------------------------------------------------ + + + //начало--------Процесс обновления WEB----------------------------------------------- + //начало--------Процесс обновления WEB----------------------------------------------- + //начало--------Процесс обновления WEB----------------------------------------------- + + if (strcmp(uri, "/webup") == 0) { + + *tot_len = json_generate_webup_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, webup_status); + + return Post_OK; + } + + //конец--------Процесс обновления WEB------------------------------------------------ + //конец--------Процесс обновления WEB------------------------------------------------ + //конец--------Процесс обновления WEB------------------------------------------------ + + + //начало--------SOS------------------------------------------------------------------ + //начало--------SOS------------------------------------------------------------------ + //начало--------SOS------------------------------------------------------------------ + + if (strcmp(uri, "/sos") == 0) { + + sendMsd(httpSetting->modemMain); + + //httpSetting->modemMain->testOne = 1; + //httpSetting->modemMain->stateRequest.stateReqCreateExtW = StateReqExtForCreate; + return Post_OK; + } + + //конец--------SOS------------------------------------------------------------------ + //конец--------SOS------------------------------------------------------------------ + //конец--------SOS------------------------------------------------------------------ + + //начало--------Дистанция------------------------------------------------------------------ + //начало--------Дистанция------------------------------------------------------------------ + //начало--------Дистанция------------------------------------------------------------------ + + if (strcmp(uri, "/dist") == 0) { + + uint32_t distAlm = 0; + + int stepParam = 0; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "dist") == 0) { + char *end; + distAlm = strtoul(httpSetting->params_get_uri.params_vals[j], &end, 10); + ++stepParam; + } + + } + + if ((stepParam != 1) || (distAlm < 1500) || (distAlm > 5000)) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + if (httpSetting->storage->nvm.dist != distAlm) { + httpSetting->storage->nvm.dist = distAlm; + DeviceStorageCounterIni_DumpDist(httpSetting->storage); + + } + + uint32_t err = Post_OK; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + //конец--------Дистанция------------------------------------------------------------------ + //конец--------Дистанция------------------------------------------------------------------ + //конец--------Дистанция------------------------------------------------------------------ + + //начало--------Маршрутизация------------------------------------------------------------------ + //начало--------Маршрутизация------------------------------------------------------------------ + //начало--------Маршрутизация------------------------------------------------------------------ + + if (strcmp(uri, "/routing") == 0) { + + uint32_t routingAlm = 0; + uint32_t timeZoneInSatAlm = 0; + + int stepParam = 0; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "routing") == 0) { + char *end; + routingAlm = strtoul(httpSetting->params_get_uri.params_vals[j], &end, 10); + ++stepParam; + } + + if (strcmp(httpSetting->params_get_uri.params_names[j], "time") == 0) { + char *end; + timeZoneInSatAlm = strtoul(httpSetting->params_get_uri.params_vals[j], &end, 10); + ++stepParam; + } + + } + + if ((stepParam != 2) || (routingAlm > 1) || (timeZoneInSatAlm < 1) || (timeZoneInSatAlm > 15)) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + time_t now; + httpSetting->rtc->get(httpSetting->rtc, &now); + now += httpSetting->storage->nvm.Settings_General.GMTcorr_v * 3600; + + bool almanacIsValid = GonetsAlmanac_IsValid(httpSetting->modemMain->alms, now); + if (almanacIsValid == false) { + httpSetting->storage->runtime.almaError = true; + } else { + httpSetting->storage->runtime.almaError = false; + } + + FILINFO fno; + FRESULT fr = f_stat(file_cur_almhs, &fno); + // Нет файла альманаха + if (fr) { + uint32_t err = Post_NO_ALMA; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + // Ошибка альманаха + if ((routingAlm == 1) && (httpSetting->storage->runtime.almaError)) { + uint32_t err = Post_OLD_ALMA; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + httpSetting->storage->nvm.routing = routingAlm; + httpSetting->storage->nvm.timeZoneInSat = timeZoneInSatAlm; + + if (routingAlm == 0) { + httpSetting->storage->runtime.BannedSAT_ex[0] = '\0'; + strcat(httpSetting->storage->runtime.BannedSAT_ex, + httpSetting->storage->runtime.Settings_General.BannedSAT_v); + + SetRoutingRestrictSc(httpSetting->modemMain, httpSetting->storage->nvm.Settings_General.BannedSAT_v); + } + + DeviceStorageCounterIni_DumpRouting(httpSetting->storage); + + uint32_t err = Post_OK; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + //конец--------Маршрутизация------------------------------------------------------------------ + //конец--------Маршрутизация------------------------------------------------------------------ + //конец--------Маршрутизация------------------------------------------------------------------ + + + //начало--------RSSI------------------------------------------------------------------ + //начало--------RSSI------------------------------------------------------------------ + //начало--------RSSI------------------------------------------------------------------ + + if (strcmp(uri, "/rssi") == 0) { + int32_t rssi; + rssi = httpSetting->modemMain->rssi; + *tot_len = json_generate_rssi_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, rssi); + return Post_OK; + } + + //конец--------RSSI------------------------------------------------------------------ + //конец--------RSSI------------------------------------------------------------------ + //конец--------RSSI------------------------------------------------------------------ + + //начало--------Удаление логов------------------------------------------------------------------ + //начало--------Удаление логов------------------------------------------------------------------ + //начало--------Удаление логов------------------------------------------------------------------ + + if (strcmp(uri, "/dellog") == 0) { + + if (osMutexAcquire(httpSettings.accessLOG, TIME_MUTEX_LOG_ACCESS) == osOK) { + bool result = delDirFileFs(httpSetting->fs, (char *) dir_logs); + osMutexRelease(httpSettings.accessLOG); + } else { + return Post_MUTEX_ERR; + } + + return Post_OK; + } + + if (strcmp(uri, "/dellog2") == 0) { + + if (osMutexAcquire(httpSettings.accessLOG2, TIME_MUTEX_LOG_ACCESS) == osOK) { + bool result = delDirFileFs(httpSetting->fs, (char *) dir_logs2); + osMutexRelease(httpSettings.accessLOG2); + } else { + return Post_MUTEX_ERR; + } + + return Post_OK; + } + + //конец--------Удаление логов------------------------------------------------------------------- + //конец--------Удаление логов------------------------------------------------------------------- + //конец--------Удаление логов------------------------------------------------------------------ + + //начало--------Тестирование АФУ------------------------------------------------------------------ + //начало--------Тестирование АФУ------------------------------------------------------------------ + //начало--------Тестирование АФУ------------------------------------------------------------------ + + if (strcmp(uri, "/testafu") == 0) { + + uint32_t err = Post_SET_MODEM; + + bool result = TestAFU(httpSetting->modemMain); + + if (result) + return Post_OK; + + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + + } + //конец--------Тестирование АФУ------------------------------------------------------------------- + //конец--------Тестирование АФУ------------------------------------------------------------------- + //конец--------Тестирование АФУ------------------------------------------------------------------ + + + //начало--------Измерение уровня шума АПО------------------------------------------------------------------ + //начало--------Измерение уровня шума АПО------------------------------------------------------------------ + //начало--------Измерение уровня шума АПО------------------------------------------------------------------ + + if (strcmp(uri, "/testapo") == 0) { + + uint32_t err = Post_SET_MODEM; + + bool result = TestAPO(httpSetting->modemMain); + + if (result) + return Post_OK; + + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + + } + //конец--------Измерение уровня шума АПО------------------------------------------------------------------- + //конец--------Измерение уровня шума АПО------------------------------------------------------------------- + //конец--------Измерение уровня шума АПО------------------------------------------------------------------ + + //начало--------Перезагрузка платы------------------------------------------------------------------ + //начало--------Перезагрузка платы------------------------------------------------------------------ + //начало--------Перезагрузка платы------------------------------------------------------------------ + + if (strcmp(uri, "/reboot_modem") == 0) { + + GpioPinSet(&httpSetting->gpios->Power.gonec_reset, false); + SystemDelayMs(1000); + GpioPinSet(&httpSetting->gpios->Power.gonec_reset, true); + + return Post_OK; + } + + if (strcmp(uri, "/reboot") == 0) { + + if ((osMutexAcquire(httpSetting->accessLOG2, TIME_MUTEX_LOG_ACCESS) == osOK) && + (osMutexAcquire(httpSetting->accessLOG, TIME_MUTEX_LOG_ACCESS) == osOK)) { + + //GpioPinSet(&httpSetting->gpios->Power.gonec_reset, false); + + NVIC_SystemReset(); + //BootJumpToAddress(0x08000000); + } + + return Post_OK; + } + //конец--------Перезагрузка платы------------------------------------------------------------------- + //конец--------Перезагрузка платы------------------------------------------------------------------- + //конец--------Перезагрузка платы------------------------------------------------------------------ + + + //начало--------Проверка событий------------------------------------------------------------------ + //начало--------Проверка событий------------------------------------------------------------------ + //начало--------Проверка событий------------------------------------------------------------------ + + if (strcmp(uri, "/event") == 0) { + + *tot_len = json_generate_event(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &httpSetting->modemMain->eventWebState, + getStateWriteFileLogger(httpSetting->flog), + getStateWriteLog2(httpSetting->modemMainLog)); + return Post_OK; + } + //конец--------Проверка событий------------------------------------------------------------------- + //конец--------Проверка событий------------------------------------------------------------------- + //конец--------Проверка событий------------------------------------------------------------------- + + + //начало--------Тест прошивки------------------------------------------------------------------ + //начало--------Тест прошивки------------------------------------------------------------------ + //начало--------Тест прошивки------------------------------------------------------------------ + + if (strcmp(uri, "/Software_Test_Web_File") == 0) { + + bool updateFwOk = FirmwareLoader_CheckBlock(httpSetting->firmwareMainLoader, + &httpSetting->firmwareMainLoader->update); + + uint32_t err = Post_CRC; + if (updateFwOk) { + err = Post_OK; + + } + + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + if (strcmp(uri, "/Software_Test_Boot_Web_File") == 0) { + + bool updateFwOk = FirmwareLoader_CheckBlock(httpSetting->firmwareBootLoader, + &httpSetting->firmwareBootLoader->update); + + uint32_t err = Post_CRC; + if (updateFwOk) { + err = Post_OK; + + } + + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + //конец--------Тест прошивки------------------------------------------------------------------- + //конец--------Тест прошивки------------------------------------------------------------------- + //конец--------Тест прошивки------------------------------------------------------------------- + + //начало--------Получение даты и времени-------------------------------------------------------------------------------------- + //начало--------Получение даты и времени-------------------------------------------------------------------------------------- + //начало--------Получение даты и времени-------------------------------------------------------------------------------------- + + if (strcmp(uri, "/setdatetime") == 0) { + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "timestamp") == 0) { + + if (httpSetting->modemMain->location.isOneDateTime) { + char *end; + + time_t timestamp; + + // Сколько прошло до этого времени + httpSetting->modemMain->timestamp_start_block_work_add = SystemGetMs() / 1000; + + + timestamp = strtoul(httpSetting->params_get_uri.params_vals[j], &end, 10); + httpSetting->rtc->set(&httpSetting->rtc, ×tamp); + + httpSetting->modemMain->timestamp_start_block = timestamp; + + timeModemStuckGetMs = SystemGetMs(); + httpSetting->modemMain->location.isOneDateTime = false; + + } + + uint32_t err = Post_OK; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + } + + return Post_OK; + } + //конец--------Получение даты и времени -------------------------------------------------------------------------------------- + //конец--------Получение даты и времени -------------------------------------------------------------------------------------- + //конец--------Получение даты и времени -------------------------------------------------------------------------------------- + + + //начало--------Версия-------------------------------------------------------------------------------------- + //начало--------Версия-------------------------------------------------------------------------------------- + //начало--------Версия-------------------------------------------------------------------------------------- + + if (strcmp(uri, "/version") == 0) { + + uint8_t sizeModem; + uint8_t sizeCrypto; + char versionModem[128]; + char versionCrypto[128]; + + memset(versionModem, 0, sizeof(versionModem)); + memset(versionCrypto, 0, sizeof(versionCrypto)); + strcpy(versionModem, "null,null"); + strcpy(versionCrypto, "null,null"); + + if (httpSetting->modemMain->isModemCheck) + Modem_Get_Version_Modem(httpSetting->modemMain, versionModem, &sizeModem, versionCrypto, &sizeCrypto); + + *tot_len = json_generate_version_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, versionModem, + versionCrypto); + return Post_OK; + } + //конец--------Версия -------------------------------------------------------------------------------------- + //конец--------Версия -------------------------------------------------------------------------------------- + //конец--------Версия -------------------------------------------------------------------------------------- + + + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + + if (strcmp(uri, "/getnumvar") == 0) { + uint32_t diskfree = 0; + uint32_t diskall = 0; + + FRESULT ret; + DWORD fre_clust, fre_sect, tot_sect; + FATFS *pt_fs; + + pt_fs = &httpSetting->fs->fs; + ret = f_getfree("1:", &fre_clust, &pt_fs); + if (ret == FR_OK) { + tot_sect = (pt_fs->n_fatent - 2) * pt_fs->csize; + fre_sect = fre_clust * pt_fs->csize; + diskfree = fre_sect / 2; + diskall = tot_sect / 2; + +// LoggerPrintf(LOGGER, "%10u кб всего на носителе.\r\n%10u кб свободно.\r\n", tot_sect / 2, fre_sect / 2); + } + + char *username = "\0"; + if (auth == 1) + username = httpSetting->storage->nvm.Settings_General.loginUSER_v; + if (auth == 2) + username = httpSetting->storage->nvm.Settings_General.loginINTEG_v; + if (auth == 3) + username = httpSetting->storage->nvm.Settings_General.loginADMIN_v; + + *tot_len = json_generate_vars_send_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + (uint32_t) httpSetting->storage->nvm.Settings_General.ATnum_v, + username, diskall, diskfree); + return Post_OK; + } + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + + //начало--------Запрос процентов передачи-------------------------------------------------------------------------------------- + //начало--------Запрос процентов передачи-------------------------------------------------------------------------------------- + //начало--------Запрос процентов передачи-------------------------------------------------------------------------------------- + + if (strcmp(uri, "/getstatussend") == 0) { + + FRESULT frFile; + FILINFO fno; + + + fileNameContent[0] = '\0'; + strcat(fileNameContent, file_dir_proc); + + frFile = f_stat(fileNameContent, &fno); + + + if (frFile) { + createBufStateFileSend(httpSetting->fs); + uint32_t err = Post_FS_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } else { + *tot_len = fno.fsize; + *isContentFile = true; + } + +// *tot_len = json_generate_proc_send_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, +// httpSetting->modemMain->fileSendInfoForJson); + return Post_OK; + } + //конец--------Запрос процентов передачи -------------------------------------------------------------------------------------- + //конец--------Запрос процентов передачи -------------------------------------------------------------------------------------- + //конец--------Запрос процентов передачи -------------------------------------------------------------------------------------- + + if (strcmp(uri, "/status.xip") == 0) { + for (int i = 0; i < httpSetting->params_get_uri.paramcount; ++i) { + + + //начало--------Получение текущего времени------------------------------------------------------------------ + //начало--------Получение текущего времени------------------------------------------------------------------ + //начало--------Получение текущего времени------------------------------------------------------------------ + + if (strcmp(httpSetting->params_get_uri.params_names[i], "timestamp") == 0) { + time_t timestamp_block; + httpSetting->rtc->get(httpSetting->rtc, ×tamp_block); + + uint32_t get_timestamp_gnss; + Gnss_GetTime(httpSetting->modemMain->gnss, &get_timestamp_gnss); + + httpSetting->modemMain->timestamp_start_block_work = + timestamp_block - httpSetting->modemMain->timestamp_start_block; + + + *tot_len = json_generate_timestamp_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + get_timestamp_gnss, + timestamp_block, + httpSetting->modemMain->timestamp_start_block_work + ); + return Post_OK; + } + //конец--------Получение текущего времени------------------------------------------------------------------- + //конец--------Получение текущего времени------------------------------------------------------------------- + //конец--------Получение текущего времени------------------------------------------------------------------- + + //начало--------Получение текущих координат----------------------------------------------------------------- + //начало--------Получение текущих координат----------------------------------------------------------------- + //начало--------Получение текущих координат------------------------------------------------------------------ + if (strcmp(httpSetting->params_get_uri.params_names[i], "alm_setting") == 0) { + + *tot_len = json_generate_coordinates_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + httpSetting->modemMain->location.valid, + (float) httpSetting->modemMain->location.latitude, + (float) httpSetting->modemMain->location.longitude, + httpSetting->storage->nvm.routing, + httpSetting->storage->nvm.dist, + httpSetting->storage->nvm.timeZoneInSat + ); + return Post_OK; + } + //конец--------Получение текущих координат------------------------------------------------------------------ + //конец--------Получение текущих координат------------------------------------------------------------------ + //конец--------Получение текущих координат------------------------------------------------------------------ + + //начало--------Получение данных АЦП------------------------------------------------------------------------ + //начало--------Получение данных АЦП------------------------------------------------------------------------ + //начало--------Получение данных АЦП------------------------------------------------------------------------ + if (strcmp(httpSetting->params_get_uri.params_names[i], "adc") == 0) { + + uint32_t vcc = getVcc(httpSetting->taskAdc); + *tot_len = json_generate_adc_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, vcc); + return Post_OK; + } + //конец--------Получение данных АЦП------------------------------------------------------------------------- + //конец--------Получение данных АЦП------------------------------------------------------------------------- + //конец--------Получение данных АЦП------------------------------------------------------------------------- + + //начало--------Получение RSSI------------------------------------------------------------------------------ + //начало--------Получение RSSI------------------------------------------------------------------------------ + //начало--------Получение RSSI------------------------------------------------------------------------------ + + if (strcmp(httpSetting->params_get_uri.params_names[i], "rssi") == 0) { + + int32_t rssi; + + rssi = httpSetting->modemMain->rssi; + //rssi = 80 + rand() % (110 - 80); + //rssi = random2[random2_i]; + //++random2_i; + //if (random2_i == 20) + // random2_i = 0; + + *tot_len = json_generate_rssi_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, rssi); + return Post_OK; + } + //конец--------Получение RSSI------------------------------------------------------------------------------- + //конец--------Получение RSSI------------------------------------------------------------------------------- + //конец--------Получение RSSI------------------------------------------------------------------------------- + + + } + } + + + //начало--------Запрос текущих статусов----------------------------------------------------------------------- + //начало--------Запрос текущих статусов-------------------------------------------------------------- + //начало--------Запрос текущих статусов-------------------------------------------------------------- + + if (strcmp(uri, "/stat.xip") == 0) { + + uint32_t stn1 = 0; + uint32_t stn2 = 0; + uint32_t stn3 = 0; + uint8_t numArray[16]; + uint8_t numArrayCount = 0; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "num") == 0) { + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + if (strlen(fileName) == 0) { + + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + struct { + int paramcount; + char *params_names[MAX_POST_GET_PARAMETERS]; + char *params_vals[MAX_POST_GET_PARAMETERS]; + } params_grp; + + params_grp.paramcount = extract_grp_ex_parameters(fileName, + params_grp.params_names, + params_grp.params_vals, + MAX_POST_GET_PARAMETERS); + + numArrayCount = params_grp.paramcount; + for (int i = 0; i < params_grp.paramcount; ++i) { + numArray[i] = atoi(params_grp.params_names[i]); + } + + } + } + + if (numArrayCount == 0) { + stn1 = 1; + stn2 = 1; + stn3 = 1; + } + + for (int i = 0; i < numArrayCount; ++i) { + if (numArray[i] == 1) + stn1 = 1; + if (numArray[i] == 2) + stn2 = 1; + if (numArray[i] == 3) + stn3 = 1; + } + + char pathScan[MAX_LEN_PATH_FS]; + + pathScan[0] = '\0'; + strcat(pathScan, dir_sentbox); + uint32_t globDirCount = 0; + uint32_t globFileCount = 0; + scan_files(pathScan, &globDirCount, &globFileCount); + uint32_t NumFileOutBox = globFileCount; + + pathScan[0] = '\0'; + strcat(pathScan, dir_inbox); + globDirCount = 0; + globFileCount = 0; + scan_files(pathScan, &globDirCount, &globFileCount); + uint32_t NumFileInbox = globFileCount; + + pathScan[0] = '\0'; + strcat(pathScan, dir_outbox); + globDirCount = 0; + globFileCount = 0; + scan_files(pathScan, &globDirCount, &globFileCount); + uint32_t CntMsgsOutBox = globFileCount; + + *tot_len = json_generate_stat_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, NumFileOutBox, NumFileInbox, + CntMsgsOutBox, stn1, stn2, stn3); + return Post_OK; + } + + //конец--------Запрос текущих статусов----------------------------------------------------------------------- + //конец--------Запрос текущих статусов-------------------------------------------------------------- + //конец--------Запрос текущих статусов-------------------------------------------------------------- + + + //начало--------Запись переменных----------------------------------------------------------------------- + //начало--------Запись переменных-------------------------------------------------------------- + //начало--------Запись переменных-------------------------------------------------------------- + + if (strcmp(uri, "/wvar.xip") == 0) { + tHttpPostSetting envPost; + + envPost.storage = httpSetting->storage; + envPost.modemMain = httpSetting->modemMain; + + char *json = httpSetting->params_get_uri.params_names[0]; + + envPost.bufAnswer = json; + idPostResult_t result = handlerPost_Var(&envPost, AUTH_INTEG); + + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + //конец--------Запись переменных----------------------------------------------------------------------- + //конец--------Запись переменных-------------------------------------------------------------- + //конец--------Запись переменных-------------------------------------------------------------- + + if (strcmp(uri, "/var.xip") == 0) { +// for (int i = 0; i < httpSetting->params_get_uri.paramcount; ++i) { + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + //начало--------Запрос настроек по группам-------------------------------------------------------------- + //начало--------Запрос настроек по группам-------------------------------------------------------------- + //начало--------Запрос настроек по группам-------------------------------------------------------------- + if (strcmp(httpSetting->params_get_uri.params_names[j], "grp") == 0) { + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + if (strlen(fileName) == 0) { + + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + // Сохранение настроек во временные + Nvm_To_RuntimeSettings(httpSetting->storage); + + bool isWriteMem = false; + + if (osMutexAcquire(httpSetting->accessMODEM, TIME_MUTEX_MODEM_ACCESS) != osOK) { + uint32_t err = Post_MUTEX_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + + emac_stop(); + idPostResult_t result = ModemGetInitAt(httpSetting->modemMain, &isWriteMem, true, true, true, true); + emac_start(); + + + osMutexRelease(httpSetting->accessMODEM); + + if (isWriteMem) { + // Загрузка настроек из временных + //RuntimeToNvmSettings(); + // Загрузка настроек из временных + Runtime_To_NvmSettings(httpSetting->storage); + + // Сохранение во флеш + DeviceStorageSetupIni_Dump(httpSetting->storage); + } + + *tot_len = SettingToJSON_grp(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, fileName, auth); + + return Post_OK; + } + + //конец--------Запрос настроек по группам-------------------------------------------------------------- + //конец--------Запрос настроек по группам-------------------------------------------------------------- + //конец--------Запрос настроек по группам-------------------------------------------------------------- + + //начало--------Запрос настроек по переменным----------------------------------------------------------------------- + //начало--------Запрос настроек по переменным-------------------------------------------------------------- + //начало--------Запрос настроек по переменным-------------------------------------------------------------- + if (strcmp(httpSetting->params_get_uri.params_names[j], "num") == 0) { + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + if (strlen(fileName) == 0) { + + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + // Сохранение настроек во временные + Nvm_To_RuntimeSettings(httpSetting->storage); + + *tot_len = SettingToJSON_num(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, fileName, auth); + + return Post_OK; + } + + //конец--------Запрос настроек по переменным-------------------------------------------------------------- + //конец--------Запрос настроек по переменным-------------------------------------------------------------- + //конец--------Запрос настроек по переменным-------------------------------------------------------------- + } +// } + } + + + + //начало--------Получение содержимого директории------------------------------------------------------------ + //начало--------Получение содержимого директории------------------------------------------------------------ + //начало--------Получение содержимого директории------------------------------------------------------------ + + if (strcmp(uri, "/getexdir") == 0) { + + pos = 0; + len = 10; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "pos") == 0) + pos = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "len") == 0) + len = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "getdir") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + } + + if (strlen(fileName) > 0) + fileName[strlen(fileName) - 1] = '\0'; + + idPostResult_t result; + + + int countFiles = getCountDir(httpSetting, &fileName[1], "*"); + + if (countFiles > 400) { + result = getDirExFile(httpSetting, tot_len, fileNameContent, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &fileName[1], countFiles, pos, pos + len); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } else { + *isContentFile = true; + } + } else { + + result = getDirExMem(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, &fileName[1], countFiles, + pos, pos + len); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + } + + return Post_OK; + } + //конец--------Получение содержимого директории------------------------------------------------------------- + //конец--------Получение содержимого директории------------------------------------------------------------- + //конец--------Получение содержимого директории------------------------------------------------------------- + + + + //начало--------Получение содержимого директории с выборкой------------------------------------------------------------ + //начало--------Получение содержимого директории с выборкой------------------------------------------------------------ + //начало--------Получение содержимого директории с выборкой------------------------------------------------------------ + + + + if (strcmp(uri, "/getsampledir") == 0) { + + pos = 0; + len = 10; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "pos") == 0) + pos = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "len") == 0) + len = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "getdir") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + } + + if (strlen(fileName) > 0) + fileName[strlen(fileName) - 1] = '\0'; + + idPostResult_t result; + + result = getSampleDirMem(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, &fileName[1], pos, + pos + len); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + + return Post_OK; + } + //конец--------Получение содержимого директории с выборкой------------------------------------------------------------- + //конец--------Получение содержимого директории с выборкой------------------------------------------------------------- + //конец--------Получение содержимого директории с выборкой------------------------------------------------------------- + + + + //начало--------Получение альманаха------------------------------------------------------------------------- + //начало--------Получение альманаха------------------------------------------------------------------------- + //начало--------Получение альманаха------------------------------------------------------------------------- + + if (strcmp(uri, "/alma.xip") == 0) { + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "run") == 0) { + pos = atoi(httpSetting->params_get_uri.params_vals[j]); + + if ((pos < 0) || (pos > 1)) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + uint32_t err = 0; + + if (pos == 1) { + httpSetting->modemMain->stateRequest.stateReqAlmaWebRunStop = StateReqAlmaWebRun; + } + + if (pos == 0) { + httpSetting->modemMain->stateRequest.stateReqAlmaWebRunStop = StateReqAlmaWebStop; + } + + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + + } + + if (strcmp(httpSetting->params_get_uri.params_names[j], "state") == 0) { + + *tot_len = json_generate_alma_state_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + httpSetting->modemMain->stateRequest.stateReqAlma); + + return Post_OK; + } + + } + + } + + //конец--------Получение альманаха-------------------------------------------------------------------------- + //конец--------Получение альманаха-------------------------------------------------------------------------- + //конец--------Получение альманаха------------------------------------------------------------------------- + + + + if (strcmp(uri, "/cmd.xip") == 0) { + + for (int i = 0; i < httpSetting->params_get_uri.paramcount; ++i) { + + //начало--------Запись файла----------------------------------------------------------------------- + //начало--------Запись файла-------------------------------------------------------------- + //начало--------Запись файла-------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "wrfile") == 0) { + tHttpPostSetting envPost; + + envPost.params_post_uri.paramcount = httpSetting->params_get_uri.paramcount; + for (int k = 0; k < httpSetting->params_get_uri.paramcount; ++k) { + envPost.params_post_uri.params_names[k] = httpSetting->params_get_uri.params_names[k]; + envPost.params_post_uri.params_vals[k] = httpSetting->params_get_uri.params_vals[k]; + } + + envPost.fs = httpSetting->fs; + envPost.bufAnswer = bufAnswer; + idPostResult_t result = handlerPost_Message_Outbox_Wrfile(&envPost); + + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + //конец--------Запись файла----------------------------------------------------------------------- + //конец--------Запись файла-------------------------------------------------------------- + //конец--------Запись файла-------------------------------------------------------------- + + + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + //начало--------Запрос номера терминала-------------------------------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getauth") == 0) { + + *tot_len = json_generate_auth_send_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + (uint32_t) httpSetting->auth); + return Post_OK; + } + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + //конец--------Запрос номера терминала -------------------------------------------------------------------------------------- + + + //начало--------Создание входящего файла-------------------------------------------------------------------- + //начало--------Создание входящего файла-------------------------------------------------------------------- + //начало--------Создание входящего файла-------------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "sendmsg") == 0) { + + chSv = 1; + isfile = 0; + kvs = 0; + urgency = 0; + routing = 0; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "urgency") == 0) + urgency = atoi(httpSetting->params_get_uri.params_vals[j]); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "chSv") == 0) + chSv = atoi(httpSetting->params_get_uri.params_vals[j]); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "file") == 0) + isfile = atoi(httpSetting->params_get_uri.params_vals[j]); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "kvs") == 0) + kvs = atoi(httpSetting->params_get_uri.params_vals[j]); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "routing") == 0) + routing = atoi(httpSetting->params_get_uri.params_vals[j]); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "sendmsg") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + if (strcmp(httpSetting->params_get_uri.params_names[j], "to") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], to, sizeof(to)); + } + + if ((urgency == -1) || (chSv == -1) || (isfile == -1) || (kvs == -1) || (strlen(fileName) == 0) || + (strlen(to) == 0) || (urgency > 9) || (chSv > 2) || (isfile > 1)) { + + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + // Время не получено + if (httpSetting->modemMain->location.isOneDateTime) { + uint32_t err = Post_GNSS_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + bool isToGonec = false; + uint32_t gonecTo; + + if ((chSv == 1) || (chSv == 0)) { + char *end; + gonecTo = strtoul(to, &end, 10); + if ((gonecTo > 0) && (gonecTo <= 99999)) { + isToGonec = true; + } + } + + if (isTo(to, '@')) { + isToGonec = true; + } + + idPostResult_t result = Post_PARAM_ERR; + + if (isToGonec) { + + fileName[strlen(fileName) - 1] = '\0'; + +// if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) != osOK) { +// uint32_t err = Post_MUTEX_ERR; +// *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); +// return Post_OK; +// } + + time_t timestamp = 0; + httpSettings.rtc->get(&httpSettings.rtc, ×tamp); + timestamp += httpSettings.storage->nvm.Settings_General.GMTcorr_v * 3600; + + result = handlerGet_Message_Outbox_CreateTempOrRealFile_Sendmsg(httpSetting, + true, + &fileName[1], + 0, + 0, + timestamp, + to, + urgency, + chSv, + isfile, + kvs, + routing, + 0); + // osMutexRelease(httpSettings.accessHTTP); + + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_OUTBOX); + + } + + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + + } + //конец--------Создание входящего файла-------------------------------------------------------------------- + //конец--------Создание входящего файла-------------------------------------------------------------------- + //конец--------Создание входящего файла-------------------------------------------------------------------- + + //начало--------Получение состояния кнопок------------------------------------------------------------------ + //начало--------Получение состояния кнопок------------------------------------------------------------------ + //начало--------Получение состояния кнопок------------------------------------------------------------------ + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getalmastate") == 0) { + + *tot_len = json_generate_state_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + httpSetting->modemMain->stateRequest.stateReqAlma); + + return Post_OK; + } + + //конец--------Получение состояния кнопок------------------------------------------------------------------ + //конец--------Получение состояния кнопок------------------------------------------------------------------ + //конец--------Получение состояния кнопок------------------------------------------------------------------ + + //начало--------Получение спутника в ЗВР-------------------------------------------------------------------- + //начало--------Получение спутника в ЗВР-------------------------------------------------------------------- + //начало--------Получение спутника в ЗВР-------------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getalma") == 0) { + + + // Время не получено + if (httpSetting->modemMain->location.isOneDateTime) { + uint32_t err = Post_GNSS_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + FILINFO fno; + FRESULT fr = f_stat(file_cur_almhs, &fno); + // Нет файла альманаха + if (fr) { + uint32_t err = Post_NO_ALMA; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + time_t now; + httpSetting->rtc->get(httpSetting->rtc, &now); + now += httpSetting->storage->nvm.Settings_General.GMTcorr_v * 3600; + + bool almanacIsValid = GonetsAlmanac_IsValid(httpSetting->modemMain->alms, now); + + // Просрочен альманах + if (almanacIsValid == false) { + uint32_t err = Post_OLD_ALMA; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + + uint8_t inRange[10]; // массив со спутниками внутри конуса + size_t countRange = 0; + + vector2 selfPosition; + selfPosition.x = (float) httpSetting->modemMain->location.latitude; + selfPosition.y = (float) httpSetting->modemMain->location.longitude; + uint32_t dist = httpSetting->storage->nvm.dist; + + uint16_t sat[100]; + uint16_t count_sat = 0; + + char *front; + char *div = httpSetting->storage->nvm.Settings_General.BannedSAT_v - 1; + char *end = div + strlen(httpSetting->storage->nvm.Settings_General.BannedSAT_v); + + for (;;) { + if (iAsciiStringMoveToNextParsingBlock(&front, &div, end, ',')) { + sat[count_sat] = atoi(front); + ++count_sat; + } else { + break; + } + } + + time_t interval_begin = now; + + for (i = 0; i < 1200; ++i) { + + countRange = GonetsAlmanac_GetInRangeForMoment( + httpSetting->modemMain->alms, + (size_t) httpSetting->modemMain->countAlmanac, + + selfPosition, + + (float) dist, + + interval_begin, + + inRange + ); + + + if (countRange > 0) { + + bool isBan = false; + for (int j = 0; j < count_sat; ++j) { + if (sat[j] == inRange[0]) + isBan = true; + } + + if (isBan == false) + break; + } + + + interval_begin += 10; + + } + + *tot_len = json_generate_getalma(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, interval_begin - now, + inRange[0]); + return Post_OK; + } + + //конец---------Получение спутника в ЗВР-------------------------------------------------------------------- + //конец---------Получение спутника в ЗВР-------------------------------------------------------------------- + //конец---------Получение спутника в ЗВР-------------------------------------------------------------------- + + //начало--------Получение содержимого директории------------------------------------------------------------ + //начало--------Получение содержимого директории------------------------------------------------------------ + //начало--------Получение содержимого директории------------------------------------------------------------ + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getdir") == 0) { + + char dir[MAX_LEN_PATH_FS]; + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[i], dir, sizeof(dir)); + + char flt[12]; + flt[0] = '\0'; + strcat(flt, " * "); + + if (httpSetting->params_get_uri.paramcount > 1) { + if (strcmp(httpSetting->params_get_uri.params_names[i + 1], "flt") == 0) { + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[i + 1], flt, sizeof(flt)); + } + } + + if (strlen(flt) > 0) + flt[strlen(flt) - 1] = '\0'; + + if (strlen(dir) > 0) + dir[strlen(dir) - 1] = '\0'; + + idPostResult_t result; + + int countFiles = getCountDir(httpSetting, &dir[1], &flt[1]); + + if (countFiles > 400) { + +// uint32_t t1 = SystemGetMs(); + + result = getDirMemToFile(httpSetting, tot_len, fileNameContent, bufAnswer, + LEN_BUF_SMALL_ANSWER_HTTP, + &dir[1], &flt[1]); + +// uint32_t t2 = SystemGetMs(); +// volatile uint32_t t3 = t2 - t1; + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } else { + *isContentFile = true; + } + } else { + + result = getDirMem(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, &dir[1], &flt[1]); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + } + + return Post_OK; + } + //конец--------Получение содержимого директории------------------------------------------------------------- + //конец--------Получение содержимого директории------------------------------------------------------------- + //конец--------Получение содержимого директории------------------------------------------------------------- + + //начало--------Получение содержимого файла----------------------------------------------------------------- + //начало--------Получение содержимого файла----------------------------------------------------------------- + //начало--------Получение содержимого файла----------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getfile") == 0) { + + pos = 0; + len = 1024; + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "pos") == 0) + pos = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "len") == 0) + len = atoi(httpSetting->params_get_uri.params_vals[j]); + + + if (strcmp(httpSetting->params_get_uri.params_names[j], "getfile") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + } + + if ((len < 0) || (len > 1024) || (strlen(fileName) == 0)) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + fileName[strlen(fileName) - 1] = '\0'; + + // Получение указателя на расширение файла + char *pExt = getVoidExt((char *) &fileName[1]); + +// if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) != osOK) { +// uint32_t err = Post_MUTEX_ERR; +// *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); +// return Post_OK; +// } + + // if (strcmp(pExt, ".log") == 0) { + // if (osMutexAcquire(httpSetting->accessLOG, TIME_MUTEX_LOG_ACCESS) != osOK) { + // osMutexRelease(httpSettings.accessHTTP); + // uint32_t err = Post_MUTEX_ERR; + // *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + // return Post_OK; + // } +// } + + idPostResult_t result = getFile(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &fileName[1], pos, len); + +// if (strcmp(pExt, ".log") == 0) { +// osMutexRelease(httpSetting->accessLOG); +// } + + +// osMutexRelease(httpSettings.accessHTTP); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + return Post_OK; + } + //конец--------Получение содержимого файла------------------------------------------------------------------ + //конец--------Получение содержимого файла------------------------------------------------------------------ + //конец--------Получение содержимого файла------------------------------------------------------------------ + + //начало--------Получение информации о файле---------------------------------------------------------------- + //начало--------Получение информации о файле---------------------------------------------------------------- + //начало--------Получение информации о файле---------------------------------------------------------------- + + if (strcmp(httpSetting->params_get_uri.params_names[i], "getfilesize") == 0) { + + for (int j = 0; j < httpSetting->params_get_uri.paramcount; ++j) { + + if (strcmp(httpSetting->params_get_uri.params_names[j], "getfilesize") == 0) + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[j], + fileName, + sizeof(fileName)); + + } + + if (strlen(fileName) == 0) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + + fileName[strlen(fileName) - 1] = '\0'; + + // Получение указателя на расширение файла + char *pExt = getVoidExt((char *) &fileName[1]); + + if (strcmp(pExt, ".log") == 0) { + if (osMutexAcquire(httpSetting->accessLOG, TIME_MUTEX_LOG_ACCESS) != osOK) { + uint32_t err = Post_MUTEX_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + return Post_OK; + } + } + + idPostResult_t result = getFileSize(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &fileName[1]); + if (strcmp(pExt, ".log") == 0) + osMutexRelease(httpSetting->accessLOG); + + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + return Post_OK; + } + //конец--------Получение информации о файле----------------------------------------------------------------- + //конец--------Получение информации о файле----------------------------------------------------------------- + //конец--------Получение информации о файле----------------------------------------------------------------- + + + //начало--------Удаление файла------------------------------------------------------------------------------ + //начало--------Удаление файла------------------------------------------------------------------------------ + //начало--------Удаление файла------------------------------------------------------------------------------ + if (strcmp(httpSetting->params_get_uri.params_names[i], "fildel") == 0) { + + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[i], fileName, sizeof(fileName)); + + if (strlen(fileName) == 0) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + fileName[strlen(fileName) - 1] = '\0'; + +// if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) != osOK) { +// uint32_t err = Post_MUTEX_ERR; +// *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); +// return Post_OK; +// } + + + idPostResult_t result = delFile(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &fileName[1]); + + + char dirFileNameIn[MAX_LEN_PATH_FS]; + dirFileNameIn[0] = '\0'; + strcat(dirFileNameIn, "1:"); + strcat(dirFileNameIn, &fileName[1]); + + char dirFileNameOut[MAX_LEN_PATH_FS]; + getPath(dirFileNameIn, dirFileNameOut, 2); + + if (strcmp(dirFileNameOut, dir_sentbox) == 0) { + getDelFilesDirDat(httpSetting->fs, (char *) dir_sentbox, "STM"); + + getDelFilesDirClean(httpSetting->fs, (char *) dir_sentbox); + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_SENTBOX); + } + + if (strcmp(dirFileNameOut, dir_tracert) == 0) { + getDelFileDirDatTracert(httpSetting->fs, (char *) dir_tracert); + + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_TRACERT); + httpSetting->modemMain->stateRequest.stateReqDelExtT = StateReqExtForDel; + httpSetting->modemMain->stateRequest.stateReqCreateExtT = StateReqExtForCreate; + } + + if (strcmp(dirFileNameOut, dir_inbox) == 0) { + getDelFilesDirDat(httpSetting->fs, (char *) dir_inbox, "ITM"); + + getDelFilesDirClean(httpSetting->fs, (char *) dir_inbox); + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_INBOX); + } + + if (strcmp(dirFileNameOut, dir_outbox) == 0) { + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_OUTBOX); + httpSetting->modemMain->stateRequest.stateReqDelExtW = StateReqExtForDel; + httpSetting->modemMain->stateRequest.stateReqCreateExtW = StateReqExtForCreate; + } + + +// osMutexRelease(httpSettings.accessHTTP); + + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + return Post_OK; + + } + //конец--------Удаление файла------------------------------------------------------------------------------- + //конец--------Удаление файла------------------------------------------------------------------------------- + //конец--------Удаление файла------------------------------------------------------------------------------- + + //начало--------Удаление всех файлов в директории----------------------------------------------------------- + //начало--------Удаление всех файлов в директории----------------------------------------------------------- + //начало--------Удаление всех файлов в директории----------------------------------------------------------- + if (strcmp(httpSetting->params_get_uri.params_names[i], "fildirdel") == 0) { + + extract_utf8_parameters(httpSetting->params_get_uri.params_vals[i], fileName, sizeof(fileName)); + + if (strlen(fileName) == 0) { + uint32_t err = Post_PARAM_ERR; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + + return Post_OK; + } + + fileName[strlen(fileName) - 1] = '\0'; + +// if (osMutexAcquire(httpSettings.accessHTTP, TIME_MUTEX_HTTP_ACCESS) != osOK) { +// uint32_t err = Post_MUTEX_ERR; +// *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); +// return Post_OK; +// } + + idPostResult_t result = delDirFile(httpSetting, tot_len, bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, + &fileName[1]); +// osMutexRelease(httpSettings.accessHTTP); + + if (result) { + uint32_t err = result; + *tot_len = json_generate_err_answer(bufAnswer, LEN_BUF_SMALL_ANSWER_HTTP, err); + } + + char dirFileNameIn[MAX_LEN_PATH_FS]; + dirFileNameIn[0] = '\0'; + strcat(dirFileNameIn, "1:"); + strcat(dirFileNameIn, &fileName[1]); + strcat(dirFileNameIn, "/"); + + char dirFileNameOut[MAX_LEN_PATH_FS]; + getPath(dirFileNameIn, dirFileNameOut, 2); + + + if (strcmp(dirFileNameOut, dir_sentbox) == 0) { + getDelFilesDirDat(httpSetting->fs, (char *) dir_sentbox, "STM"); + + getDelFilesDirClean(httpSetting->fs, (char *) dir_sentbox); + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_SENTBOX); + } + + if (strcmp(dirFileNameOut, dir_tracert) == 0) { + getDelFileDirDatTracert(httpSetting->fs, (char *) dir_tracert); + + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_TRACERT); + httpSetting->modemMain->stateRequest.stateReqDelExtT = StateReqExtForDel; + httpSetting->modemMain->stateRequest.stateReqCreateExtT = StateReqExtForCreate; + } + + if (strcmp(dirFileNameOut, dir_inbox) == 0) { + getDelFilesDirDat(httpSetting->fs, (char *) dir_inbox, "ITM"); + + getDelFilesDirClean(httpSetting->fs, (char *) dir_inbox); + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_INBOX); + } + + if (strcmp(dirFileNameOut, dir_outbox) == 0) { + httpSetting->rtc->get(&httpSetting->rtc, &httpSetting->modemMain->eventWebState.EVENT_OUTBOX); + httpSetting->modemMain->stateRequest.stateReqDelExtW = StateReqExtForDel; + httpSetting->modemMain->stateRequest.stateReqCreateExtW = StateReqExtForCreate; + } + + return Post_OK; + + } + //конец--------Удаление всех файлов в директории------------------------------------------------------------ + //конец--------Удаление всех файлов в директории------------------------------------------------------------ + //конец--------Удаление всех файлов в директории------------------------------------------------------------ + + } + + + } + + + return Post_404_ERR; +} diff --git a/Src/httpd_get_handlers.c b/Src/httpd_get_handlers.c new file mode 100644 index 0000000..8383f9a --- /dev/null +++ b/Src/httpd_get_handlers.c @@ -0,0 +1,1074 @@ +// +// Created by cfif on 13.12.22. +// +#include "httpd_get_handlers.h" +#include "JSONSettings.h" +#include +#include + + +// Создание и запись в файл +idPostResult_t +handlerGet_Message_Outbox_CreateTempOrRealFile_Sendmsg(tHttpSetting *env, bool isFile, char *filename, char *buf, + uint32_t buf_len, + time_t timestamp, char *to, + uint32_t urgency, uint32_t chSv, uint32_t isfile, uint32_t kvs, + uint32_t type, uint32_t sos) { + + FIL file; + FRESULT fr; + + char temp_filename[MAX_LEN_PATH_FS]; + char real_filename[MAX_LEN_PATH_FS]; + + char bufTemp[12]; + + char bufIdFileOutBox[12]; + char bufWithPrefixIdFileOutBox[12]; + + ++env->storage->nvm.maxId_OutBox; + if (env->storage->nvm.maxId_OutBox > 32767) + env->storage->nvm.maxId_OutBox = 1; + + DeviceStorageCounterIni_Dump(env->storage); + + utoa(env->storage->nvm.maxId_OutBox, bufIdFileOutBox, 10); + toAddPrefixStr(bufWithPrefixIdFileOutBox, bufIdFileOutBox, 5); + + temp_filename[0] = '\0'; + strcat(temp_filename, "1:"); +// strcat(temp_filename, dir_temp); + strcat(temp_filename, filename); + + real_filename[0] = '\0'; + strcat(real_filename, dir_outbox); + strcat(real_filename, bufWithPrefixIdFileOutBox); + strcat(real_filename, ".OTM"); + + // Запись заголовка в файл + char BufFileWrite[512]; + BufFileWrite[0] = '\0'; + strcat(BufFileWrite, &real_filename[2]); + strcat(BufFileWrite, "&timec="); + + if (isFile) { + + FILINFO fno; + fr = f_stat(temp_filename, &fno); + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(&BufFileWrite[strlen(BufFileWrite)], sizeof(BufFileWrite), "%d-%m-%g %T", &stm); + + } else { + struct tm stm; + localtime_r(×tamp, &stm); + + strftime(&BufFileWrite[strlen(BufFileWrite)], sizeof(BufFileWrite), "%d-%m-%g %T", &stm); + } + + + strcat(BufFileWrite, "&from="); + + utoa(env->storage->nvm.Settings_General.ATnum_v, &BufFileWrite[strlen(BufFileWrite)], 10); + + strcat(BufFileWrite, "&mnum="); + strcat(BufFileWrite, bufIdFileOutBox); + + strcat(BufFileWrite, "&to="); + strcat(BufFileWrite, to); + + if ((urgency > 0) && (urgency <= 4)) { + urgency = 1; + } + + if ((urgency > 4) && (urgency <= 8)) { + urgency = 2; + } + + if ((urgency > 8) && (urgency <= 13)) { + urgency = 3; + } + + strcat(BufFileWrite, "&urgency="); + utoa(urgency, bufTemp, 10); + strcat(BufFileWrite, bufTemp); + + strcat(BufFileWrite, "&type="); + utoa(type, bufTemp, 10); + strcat(BufFileWrite, bufTemp); + + strcat(BufFileWrite, "&sos="); + utoa(sos, bufTemp, 10); + strcat(BufFileWrite, bufTemp); + + + strcat(BufFileWrite, "&chSv="); + utoa(chSv, bufTemp, 10); + strcat(BufFileWrite, bufTemp); + + if (isfile == 0) { + strcat(BufFileWrite, "&subj=msg"); + } else { + strcat(BufFileWrite, "&subj=file"); + } + + if (kvs == 0) { + strcat(BufFileWrite, "&kvs=000"); + } else { + strcat(BufFileWrite, "&kvs=001"); + } + + if (isfile == 0) { + strcat(BufFileWrite, "&msg="); + } else { + strcat(BufFileWrite, "&msg="); + strcat(BufFileWrite, &filename[5]); + } + + fr = f_open_i(env->fs, &file, (TCHAR *) real_filename, FA_WRITE | FA_CREATE_ALWAYS); + if (fr) { + return Post_FS_ERR; + } + + UINT bytes_written; + fr = f_write_i(env->fs, &file, BufFileWrite, strlen(BufFileWrite), &bytes_written); + + if (isfile > 0) { + bufTemp[0] = '\0'; +// bufTemp[1] = '\0'; + + fr = f_write_i(env->fs, &file, bufTemp, 1, &bytes_written); + + } + + if (isFile == false) { + fr = f_write_i(env->fs, &file, buf, buf_len, &bytes_written); + } + + fr = f_close_i(env->fs, &file); + + + if (isFile) { + + bool res = CopyFileFs(env->fs, real_filename, temp_filename, 0); + + if (res == false) { + return Post_FS_ERR; + } + +// f_unlink_i(env->fs, temp_filename); + } + + env->modemMain->stateRequest.stateReqCreateExtW = StateReqExtForCreate; + + return Post_OK; +} + +/* +idPostResult_t createFileView(tHttpSetting *env, char *dir, char *fileNameContent) { + FRESULT frDir; + DIR dj; + FILINFO fno; + FRESULT frFile; + FIL file; + UINT bw; + + uint32_t countTimestamp = 0; + + char buf[164]; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + + frDir = f_findfirst_i(env->fs, &dj, &fno, dirScan, "*"); + if (frDir) + return Post_FS_ERR; + + uint32_t result = 0; + + frFile = f_unlink_i(env->fs, file_dir_tmp); + + frFile = f_open_i(env->fs, &file, (TCHAR *) file_dir_tmp, FA_WRITE | FA_CREATE_ALWAYS); + if (frFile) { + return Post_FS_ERR; + } + + while (frDir == FR_OK && fno.fname[0]) { + + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%FT%T", &stm); + + time_t timestamp = mktime(&stm); + env->FileSort[countTimestamp].key = timestamp; + env->FileSort[countTimestamp].id = countTimestamp; + + ++countTimestamp; + + size_t len_json = json_generate_get_elem_fs(buf, sizeof(buf), fno.fname, tpf, fno.fsize, fdt); + + frFile = f_write_i(env->fs, &file, buf, sizeof(buf), &bw); + + frDir = f_findnext_i(env->fs, &dj, &fno); + if (frDir) + return Post_FS_ERR; + } + + f_closedir_i(env->fs, &dj); + + f_close_i(env->fs, &file); + + + for (uint32_t i = 0; i < countTimestamp - 1; ++i) + for (uint32_t j = 0; j < countTimestamp - i - 1; ++j) { + + if (env->FileSort[j + 1].key < env->FileSort[j].key) { + uint64_t tmpKey = env->FileSort[j].key; + env->FileSort[j].key = env->FileSort[j + 1].key; + env->FileSort[j + 1].key = tmpKey; + + uint32_t tmpId = env->FileSort[j].id; + env->FileSort[j].id = env->FileSort[j + 1].id; + env->FileSort[j + 1].id = tmpId; + + } + } + + + fileNameContent[0] = '\0'; + strcat(fileNameContent, file_dir_tmp); + + return Post_OK; +} +*/ + +// Формирование ответа с содержимым директории +idPostResult_t getSampleDirMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, uint32_t pos, + uint32_t len) { + FRESULT fr; + DIR dj; + FILINFO fno; + + uint32_t stepPos = 0; + + DIR dj2; + FILINFO fno2; + char dirScanFile[MAX_LEN_PATH_FS]; + char dirScan2[MAX_LEN_PATH_FS]; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + uint32_t offsetBuf = 1; + buf[0] = '['; + + fr = f_findfirst_i(env->fs, &dj, &fno, dirScan, "*"); + + if (fr) + return Post_FS_ERR; + + uint32_t result = 0; + size_t len_json = json_generate_answer_get_elem_fs(&buf[offsetBuf], buf_len, result, dir); + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + while (fr == FR_OK && fno.fname[0]) { + + tpf = 0; + if (fno.fattrib & AM_DIR) { + dirScan2[0] = '\0'; + strcat(dirScan2, dirScan); + strcat(dirScan2, "/"); + strcat(dirScan2, fno.fname); + + fr = f_findfirst_i(env->fs, &dj2, &fno2, dirScan2, "*"); + + if (fr) + return Post_FS_ERR; + + while (fr == FR_OK && fno2.fname[0]) { + + + if ((stepPos >= pos) && (stepPos < len)) { + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, + env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%d-%m-%g %T", &stm); + + dirScanFile[0] = '\0'; + strcat(dirScanFile, fno.fname); + strcat(dirScanFile, "/"); + strcat(dirScanFile, fno2.fname); + + len_json = json_generate_get_elem_fs(&buf[offsetBuf], buf_len, dirScanFile, tpf, fno2.fsize, fdt); + + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + } + + ++stepPos; + + if (stepPos >= len) + break; + + + fr = f_findnext_i(env->fs, &dj2, &fno2); + + if (fr) + return Post_FS_ERR; + } + fr = f_closedir_i(env->fs, &dj2); + + + } else { + + if ((stepPos >= pos) && (stepPos < len)) { + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%d-%m-%g %T", &stm); + + len_json = json_generate_get_elem_fs(&buf[offsetBuf], buf_len, fno.fname, tpf, fno.fsize, fdt); + + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + } + + ++stepPos; + + if (stepPos >= len) + break; + + } + + fr = f_findnext_i(env->fs, &dj, &fno); + + if (fr) + return Post_FS_ERR; + + } + fr = f_closedir_i(env->fs, &dj); + if (fr) + return Post_FS_ERR; + + buf[offsetBuf - 1] = ']'; + buf[offsetBuf] = '\0'; + + *tot_len = offsetBuf; + + return Post_OK; +} + + +// Формирование ответа с содержимым директории +idPostResult_t +getDirExFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir, + uint32_t count, uint32_t pos, uint32_t len) { + + + FRESULT frDir; + DIR dj; + FILINFO fno; + FRESULT frFile; + FIL file; + UINT bw; + + uint32_t stepPos = 0; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + + frDir = f_findfirst_i(env->fs, &dj, &fno, dirScan, "*"); + if (frDir) + return Post_FS_ERR; + + uint32_t result = 0; + buf[0] = '['; + size_t len_json = json_generate_answer_get_ex_elem_fs(&buf[1], buf_len, result, count, dir); + + + ++env->countTmp; + if (env->countTmp > 20) + env->countTmp = 1; + + char dirTmpFile[MAX_LEN_PATH_FS]; + dirTmpFile[0] = '\0'; + strcat(dirTmpFile, file_dir_tmp); + utoa(env->countTmp, &dirTmpFile[strlen(dirTmpFile)], 10); + + frFile = f_unlink_i(env->fs, dirTmpFile); + + frFile = f_open_i(env->fs, &file, (TCHAR *) dirTmpFile, FA_WRITE | FA_CREATE_ALWAYS); + if (frFile) { + return Post_FS_ERR; + } + + frFile = f_write_i(env->fs, &file, buf, len_json + 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + while (frDir == FR_OK && fno.fname[0]) { + + if ((stepPos >= pos) && (stepPos < len)) { + + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%d-%m-%g %T", &stm); + + frFile = f_write_i(env->fs, &file, ",", 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + len_json = json_generate_get_elem_fs(buf, buf_len, fno.fname, tpf, fno.fsize, fdt); + + frFile = f_write_i(env->fs, &file, buf, len_json, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + } + + ++stepPos; + + if (stepPos >= len) + break; + + frDir = f_findnext_i(env->fs, &dj, &fno); + if (frDir) + return Post_FS_ERR; + } + + frDir = f_closedir_i(env->fs, &dj); + if (frDir) + return Post_FS_ERR; + + + frFile = f_write_i(env->fs, &file, "]", 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + frFile = f_close_i(env->fs, &file); + + frFile = f_stat(dirTmpFile, &fno); + *tot_len = fno.fsize; + fileNameContent[0] = '\0'; + strcat(fileNameContent, dirTmpFile); + + return Post_OK; +} + + +// Формирование ответа с содержимым директории +idPostResult_t +getDirExMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, uint32_t count, uint32_t pos, + uint32_t len) { + FRESULT fr; + DIR dj; + FILINFO fno; + + uint32_t stepPos = 0; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + uint32_t offsetBuf = 1; + buf[0] = '['; + + fr = f_findfirst_i(env->fs, &dj, &fno, dirScan, "*"); + + if (fr) + return Post_FS_ERR; + + uint32_t result = 0; + size_t len_json = json_generate_answer_get_ex_elem_fs(&buf[offsetBuf], buf_len, result, count, dir); + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + while (fr == FR_OK && fno.fname[0]) { + + + if ((stepPos >= pos) && (stepPos < len)) { + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%FT%T", &stm); + + + len_json = json_generate_get_elem_fs(&buf[offsetBuf], buf_len, fno.fname, tpf, fno.fsize, fdt); + + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + if (offsetBuf > buf_len - 200) + break; + + } + + ++stepPos; + + if (stepPos >= len) + break; + + fr = f_findnext_i(env->fs, &dj, &fno); + + if (fr) + return Post_FS_ERR; + + } + fr = f_closedir_i(env->fs, &dj); + if (fr) + return Post_FS_ERR; + + buf[offsetBuf - 1] = ']'; + buf[offsetBuf] = '\0'; + + *tot_len = offsetBuf; + + return Post_OK; +} + + +// Формирование ответа с содержимым директории +idPostResult_t getDirMem(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir, char *flt) { + FRESULT fr; + DIR dj; + FILINFO fno; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + + uint32_t offsetBuf = 1; + buf[0] = '['; + + fr = f_findfirst_i(env->fs, &dj, &fno, dirScan, flt); + + if (fr) + return Post_FS_ERR; + + uint32_t result = 0; + size_t len_json = json_generate_answer_get_elem_fs(&buf[offsetBuf], buf_len, result, dir); + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + while (fr == FR_OK && fno.fname[0]) { + + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%FT%T", &stm); + + len_json = json_generate_get_elem_fs(&buf[offsetBuf], buf_len, fno.fname, tpf, fno.fsize, fdt); + + offsetBuf += len_json; + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + + if (offsetBuf > buf_len - 200) + break; + + fr = f_findnext_i(env->fs, &dj, &fno); + + } + fr = f_closedir_i(env->fs, &dj); + + + buf[offsetBuf - 1] = ']'; + buf[offsetBuf] = '\0'; + + *tot_len = offsetBuf; + + return Post_OK; +} + +// Количество файлов в каталоге +int getCountDir(tHttpSetting *env, char *dir, char *flt) { + int count = 0; + FRESULT frDir; + DIR dj; + FILINFO fno; + + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + frDir = f_findfirst_i(env->fs, &dj, &fno, dirScan, flt); + if (frDir) + return 0; + + + while (frDir == FR_OK && fno.fname[0]) { + + ++count; + + frDir = f_findnext_i(env->fs, &dj, &fno); + if (frDir) + return 0; + } + + frDir = f_closedir_i(env->fs, &dj); + + + return count; +} + + +idPostResult_t +getDirMemToFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir, + char *flt) { + FRESULT fr; + DIR dj; + FILINFO fno; + FIL file; + FRESULT frFile; + UINT bw; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + + uint32_t offsetBuf = 1; + buf[0] = '['; + + /* + if (env->modemMain->crunchTrace) { + env->modemMain->crunchTrace = false; + pathFiltr[0] = '\0'; + strcat(pathFiltr, "*.OTM"); + } +*/ + fr = f_findfirst_i(env->fs, &dj, &fno, dirScan, flt); + if (fr) + return Post_FS_ERR; + + uint32_t result = 0; + size_t len_json = json_generate_answer_get_elem_fs(&buf[offsetBuf], buf_len, result, dir); + offsetBuf += len_json; + +// buf[offsetBuf] = ','; +// offsetBuf += 1; +// buf[offsetBuf] = '\0'; + + ++env->countTmp; + if (env->countTmp > 20) + env->countTmp = 1; + + char dirTmpFile[MAX_LEN_PATH_FS]; + dirTmpFile[0] = '\0'; + strcat(dirTmpFile, file_dir_tmp); + utoa(env->countTmp, &dirTmpFile[strlen(dirTmpFile)], 10); + + frFile = f_unlink_i(env->fs, dirTmpFile); + + frFile = f_open_i(env->fs, &file, (TCHAR *) dirTmpFile, FA_WRITE | FA_CREATE_ALWAYS); + if (frFile) { + return Post_FS_ERR; + } + + + uint32_t stepFiles = 0; + + while (fr == FR_OK && fno.fname[0]) { + + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%FT%T", &stm); + + buf[offsetBuf] = ','; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + + len_json = json_generate_get_elem_fs(&buf[offsetBuf], buf_len, fno.fname, tpf, fno.fsize, fdt); + offsetBuf += len_json; + + // buf[offsetBuf] = ','; +// offsetBuf += 1; +// buf[offsetBuf] = '\0'; + + ++stepFiles; + if (stepFiles >= 100) { + stepFiles = 0; + + frFile = f_write_i(env->fs, &file, buf, offsetBuf, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + offsetBuf = 0; + } + + + fr = f_findnext_i(env->fs, &dj, &fno); + + if (fr) + return Post_FS_ERR; + + } + fr = f_closedir_i(env->fs, &dj); + if (fr) + return Post_FS_ERR; + + if (stepFiles > 0) { + stepFiles = 0; + + frFile = f_write_i(env->fs, &file, buf, offsetBuf, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + } + + frFile = f_write_i(env->fs, &file, "]", 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + frFile = f_close_i(env->fs, &file); + + frFile = f_stat(dirTmpFile, &fno); + *tot_len = fno.fsize; + fileNameContent[0] = '\0'; + strcat(fileNameContent, dirTmpFile); + + return Post_OK; +} + + +// Формирование ответа с содержимым директории +idPostResult_t +getDirFile(tHttpSetting *env, uint32_t *tot_len, char *fileNameContent, char *buf, size_t buf_len, char *dir) { + FRESULT frDir; + DIR dj; + FILINFO fno; + FRESULT frFile; + FIL file; + UINT bw; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + uint8_t tpf; + char fdt[26]; + + char pathFiltr[12]; + pathFiltr[0] = '\0'; + strcat(pathFiltr, "*"); + + + frDir = f_findfirst_i(env->fs, &dj, &fno, dirScan, pathFiltr); + if (frDir) + return Post_FS_ERR; + + uint32_t result = 0; + buf[0] = '['; + size_t len_json = json_generate_answer_get_elem_fs(&buf[1], buf_len, result, dir); + + ++env->countTmp; + if (env->countTmp > 20) + env->countTmp = 1; + + char dirTmpFile[MAX_LEN_PATH_FS]; + dirTmpFile[0] = '\0'; + strcat(dirTmpFile, file_dir_tmp); + utoa(env->countTmp, &dirTmpFile[strlen(dirTmpFile)], 10); + + frFile = f_unlink_i(env->fs, dirTmpFile); + + frFile = f_open_i(env->fs, &file, (TCHAR *) dirTmpFile, FA_WRITE | FA_CREATE_ALWAYS); + if (frFile) { + return Post_FS_ERR; + } + + frFile = f_write_i(env->fs, &file, buf, len_json + 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + while (frDir == FR_OK && fno.fname[0]) { + + tpf = 0; + if (fno.fattrib & AM_DIR) + tpf = 1; + + struct tm stm; + fatTimeToTimeStamp(fno.ftime | (fno.fdate << 16), &stm, env->storage->nvm.Settings_General.GMTcorr_v); + strftime(fdt, sizeof(fdt), "%FT%T", &stm); + + frFile = f_write_i(env->fs, &file, ",", 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + len_json = json_generate_get_elem_fs(buf, buf_len, fno.fname, tpf, fno.fsize, fdt); + + frFile = f_write_i(env->fs, &file, buf, len_json, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + frDir = f_findnext_i(env->fs, &dj, &fno); + if (frDir) + return Post_FS_ERR; + } + + frDir = f_closedir_i(env->fs, &dj); + if (frDir) + return Post_FS_ERR; + + + frFile = f_write_i(env->fs, &file, "]", 1, &bw); + if (frFile) { + frFile = f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + + frFile = f_close_i(env->fs, &file); + + frFile = f_stat(dirTmpFile, &fno); + *tot_len = fno.fsize; + fileNameContent[0] = '\0'; + strcat(fileNameContent, dirTmpFile); + + return Post_OK; +} + + +// Формирование ответа с содержимым файла +idPostResult_t +getFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name, int32_t pos, size_t len) { + char bufDumpFile[1024]; + + if (len > 1024) + return Post_SIZE_ERR; + + char dirFileName[MAX_LEN_PATH_FS]; + dirFileName[0] = '\0'; + strcat(dirFileName, "1:"); + strcat(dirFileName, file_name); + + uint32_t offsetBuf = 1; + buf[0] = '['; + + FIL file; + FRESULT fr = f_open_i(env->fs, &file, (TCHAR *) dirFileName, FA_READ); + + if (fr) return Post_FS_ERR; + + if (pos < 0) { + FILINFO fno; + fr = f_stat(dirFileName, &fno); + + pos = fno.fsize + pos; + if (pos < 0) pos = 0; + } + + fr = f_lseek_i(env->fs, &file, pos); + if (fr) return Post_FS_ERR; + + UINT bytes_read; + fr = f_read_i(env->fs, &file, bufDumpFile, len, &bytes_read); + + uint32_t result = 0; + size_t len_json = json_generate_answer_get_elem_file_fs(&buf[offsetBuf], buf_len, result, file_name, pos, + bytes_read); + offsetBuf += len_json; + buf[offsetBuf] = ']'; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + offsetBuf += 1; + buf[offsetBuf] = '\0'; + + memcpy(&buf[offsetBuf], bufDumpFile, bytes_read); + offsetBuf += bytes_read; + + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + fr = f_close_i(env->fs, &file); + if (fr) + return Post_FS_ERR; + + *tot_len = offsetBuf; + + return Post_OK; +} + + +// Получение размера файла +idPostResult_t getFileSize(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name) { + char dirFileName[MAX_LEN_PATH_FS]; + dirFileName[0] = '\0'; + strcat(dirFileName, "1:"); + strcat(dirFileName, file_name); + + FRESULT fr; + FILINFO fno; + + fr = f_stat(dirFileName, &fno); + + if (fr == FR_OK) { + uint32_t err = 0; + *tot_len = json_generate_fileinfo_answer(buf, buf_len, fno.fsize); + + } else { + return Post_FS_ERR; + } + + return Post_OK; +} + + +// Удаление файла +idPostResult_t delFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *file_name) { + char dirFileName[MAX_LEN_PATH_FS]; + dirFileName[0] = '\0'; + strcat(dirFileName, "1:"); + strcat(dirFileName, file_name); + + FRESULT result = f_unlink_i(env->fs, dirFileName); + if (result) { + if (result != FR_NO_FILE) + return Post_FS_ERR; + } + + uint32_t err = 0; + *tot_len = json_generate_err_answer(buf, buf_len, err); + + return Post_OK; +} + +// Удаление всех файлов в директории +idPostResult_t delDirFile(tHttpSetting *env, uint32_t *tot_len, char *buf, size_t buf_len, char *dir) { + FRESULT fr; + DIR dj; + FILINFO fno; + + char dirScan[MAX_LEN_PATH_FS]; + dirScan[0] = '\0'; + strcat(dirScan, "1:"); + strcat(dirScan, dir); + + char dirFileScan[MAX_LEN_PATH_FS]; + + fr = f_findfirst_i(env->fs, &dj, &fno, dirScan, "*"); + + if (fr) + return Post_FS_ERR; + + while (fr == FR_OK && fno.fname[0]) { + + dirFileScan[0] = '\0'; + strcat(dirFileScan, dirScan); + strcat(dirFileScan, "/"); + strcat(dirFileScan, fno.fname); + + fr = f_unlink_i(env->fs, dirFileScan); + + fr = f_findnext_i(env->fs, &dj, &fno); + + if (fr) + return Post_FS_ERR; + + } + fr = f_closedir_i(env->fs, &dj); + if (fr) + return Post_FS_ERR; + + uint32_t err = 0; + *tot_len = json_generate_err_answer(buf, buf_len, err); + + return Post_OK; +} diff --git a/Src/httpd_post.c b/Src/httpd_post.c new file mode 100644 index 0000000..ef2f556 --- /dev/null +++ b/Src/httpd_post.c @@ -0,0 +1,15 @@ +// +// Created by cfif on 10.11.22. +// +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/apps/fs.h" +#include +#include "httpd.h" +#include "httpd_post.h" +#include "Settings_RS485_Bluetooth.h" +#include "httpd_base_func.h" +#include "JSONSettings.h" +#include "httpd_TablesPostGet.h" +//#include "stream.h" + diff --git a/Src/httpd_post_handlers.c b/Src/httpd_post_handlers.c new file mode 100644 index 0000000..3d642c4 --- /dev/null +++ b/Src/httpd_post_handlers.c @@ -0,0 +1,787 @@ +// +// Created by cfif on 12.12.22. +// +#include "httpd_post_handlers.h" +#include "string.h" +#include "httpd_base_func.h" +#include "stdlib.h" +#include "JSONSettings.h" +#include "Flash_MT29F2G01ABAGDWB.h" +//#include "stream.h" +#include "httpd_TablesPostGet.h" +#include "stdlib.h" +#include "fs_base_func.h" +#include "fs_interface.h" +#include "ModemGonec.h" +#include "http_server.h" +#include "ModemGonecFunc.h" +#include "DeviceStorageIni.h" +#include "BootJump.h" +#include "ModemSend.h" + +/* +char bufNameDir[9]; + +size_t len = getNameDir(env->rtc, bufNameDir, sizeof(bufNameDir)); +if (len != 8) +return Post_DATATIME_ERR; +*/ + + +// начало-------------------Создание входящего сообщения handlerPost_Write_Other_File----------------------------------- +// начало-------------------Создание входящего сообщения handlerPost_Write_Other_File----------------------------------- +// начало-------------------Создание входящего сообщения handlerPost_Write_Other_File----------------------------------- + +// Создание и запись в файл +idPostResult_t handlerPost_Write_Other_File(tHttpPostSetting *env) { + int pos = -1; + int len = -1; + char filename[MAX_LEN_PATH_FS]; + filename[0] = '\0'; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "pos") == 0) + pos = atoi(env->params_post_uri.params_vals[i]); + + if (strcmp(env->params_post_uri.params_names[i], "len") == 0) + len = atoi(env->params_post_uri.params_vals[i]); + + if (strcmp(env->params_post_uri.params_names[i], "wrfile") == 0) + extract_utf8_parameters(env->params_post_uri.params_vals[i], filename, + sizeof(filename)); + } + + if ((pos == -1) || (len == -1) || (strlen(filename) == 0) || (len > 1024 * 11)) { + return Post_PARAM_ERR; + } + + FIL file; + FRESULT fr; + + char temp_filename[MAX_LEN_PATH_FS]; + + temp_filename[0] = '\0'; + strcat(temp_filename, "1:/"); + filename[strlen(filename) - 1] = '\0'; + strcat(temp_filename, &filename[1]); + + fr = f_open_i(env->fs, &file, (TCHAR *) temp_filename, FA_WRITE | FA_CREATE_ALWAYS); + if (fr) { + return Post_FS_ERR; + } + + fr = f_lseek_i(env->fs, &file, pos); + + UINT bytes_written; + fr = f_write_i(env->fs, &file, env->bufAnswer, len, &bytes_written); + + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + fr = f_close_i(env->fs, &file); + + return Post_OK; +} + +// конец-------------------Создание входящего сообщения handlerPost_Write_Other_File------------------------------------ +// конец-------------------Создание входящего сообщения handlerPost_Write_Other_File------------------------------------ +// конец-------------------Создание входящего сообщения handlerPost_Write_Other_File------------------------------------ + +// начало-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- +// начало-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- +// начало-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- + +// Создание и запись в файл +idPostResult_t handlerPost_Message_Outbox_Wrfile(tHttpPostSetting *env) { + int pos = -1; + int len = -1; + char filename[MAX_LEN_PATH_FS]; + filename[0] = '\0'; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "pos") == 0) + pos = atoi(env->params_post_uri.params_vals[i]); + + if (strcmp(env->params_post_uri.params_names[i], "len") == 0) + len = atoi(env->params_post_uri.params_vals[i]); + + if (strcmp(env->params_post_uri.params_names[i], "wrfile") == 0) + extract_utf8_parameters(env->params_post_uri.params_vals[i], filename, + sizeof(filename)); + } + + if ((pos == -1) || (len == -1) || (strlen(filename) == 0) || (len > 1024 * 11)) { + return Post_PARAM_ERR; + } + + FIL file; + FRESULT fr; + + char temp_filename[MAX_LEN_PATH_FS]; + + temp_filename[0] = '\0'; + strcat(temp_filename, dir_temp); + filename[strlen(filename) - 1] = '\0'; + strcat(temp_filename, &filename[1]); + + fr = f_open_i(env->fs, &file, (TCHAR *) temp_filename, FA_WRITE | FA_CREATE_ALWAYS); + if (fr) { + return Post_FS_ERR; + } + + fr = f_lseek_i(env->fs, &file, pos); + + + UINT bytes_written; + fr = f_write_i(env->fs, &file, env->bufAnswer, len, &bytes_written); + +/* + uint32_t lenInteger = len / 512; + uint32_t lenTail = len % 512; + + uint32_t stepMiniBuf = 0; + for (uint32_t i = 0; i < lenInteger; ++i) { + fr = f_write_i(env->fs, &file, &env->bufAnswer[stepMiniBuf], 512, &bytes_written); + stepMiniBuf += 512; + } + + if (lenTail > 0) { + fr = f_write_i(env->fs, &file, &env->bufAnswer[stepMiniBuf], lenTail, &bytes_written); + } +*/ + + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + fr = f_close_i(env->fs, &file); + + return Post_OK; +} + +// конец-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- +// конец-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- +// конец-------------------Создание входящего сообщения cmd.xip?wrfile--------------------------------------------------- + + +// начало-------------------Создание динамического файла--------------------------------------------------- +// начало-------------------Создание динамического файла--------------------------------------------------- +// начало-------------------Создание динамического файла--------------------------------------------------- + +// Создание и запись в файл +idPostResult_t handlerPost_Message_Outbox_DynamicWrfile(tHttpPostSetting *env, uint32_t offset, uint32_t len) { + FIL file; + FRESULT fr; + + if (env->isFirst) { + fr = f_unlink_i(env->fs, env->createPostData.filename); + } + + fr = f_open_i(env->fs, &file, (TCHAR *) env->createPostData.filename, FA_OPEN_APPEND | FA_WRITE); + if (fr) { + return Post_FS_ERR; + } + + UINT bytes_written; + fr = f_write_i(env->fs, &file, &env->bufAnswer[offset], len, &bytes_written); + + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + fr = f_close_i(env->fs, &file); + + return Post_OK; +} + +// конец-------------------Создание динамического файла--------------------------------------------------- +// конец-------------------Создание динамического файла--------------------------------------------------- +// конец-------------------Создание динамического файла--------------------------------------------------- + + +// начало-------------------Загрузка ключей----------------------------------------------------------------------------- +// начало-------------------Загрузка ключей----------------------------------------------------------------------------- +// начало-------------------Загрузка ключей----------------------------------------------------------------------------- + +// Загрузка ключей +idPostResult_t handlerPost_Key_Load(tHttpPostSetting *env) { + + int key_num = 0; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "key") == 0) { + key_num = atoi(env->params_post_uri.params_vals[i]); + } + + } + + if ((key_num < 1) || (key_num > 12)) { + return Post_PARAM_ERR; + } + + // Загрузка ключей с проверкой + idPostResult_t result = ModemLoadKey(env->modemMain, key_num, env->bufAnswer, env->content_len); + + return result; +} + +// конец--------------------Загрузка ключей----------------------------------------------------------------------------- +// конец--------------------Загрузка ключей----------------------------------------------------------------------------- +// конец--------------------Загрузка ключей----------------------------------------------------------------------------- + + +// начало--------------------Сохранение настроек------------------------------------------------------------------------ +// начало--------------------Сохранение настроек------------------------------------------------------------------------ +// начало--------------------Сохранение настроек------------------------------------------------------------------------ + +idPostResult_t handlerPost_Var(tHttpPostSetting *env, typeAuth auth) { + idPostResult_t result = Post_OK; + + // Сохранение настроек во временные + // NvmToRuntimeSettings(); + // Сохранение настроек во временные + Nvm_To_RuntimeSettings(env->storage); + + bool grp1, grp2, grp3, grp8, grp11, grp12, grp14, grp25, grp26; + result = vJsonToStructure(env->bufAnswer, settings, env->modemMain, auth, &grp1, &grp2, &grp3, + &grp8, &grp11, &grp12, &grp14, &grp25, &grp26); + + // Ошибка + if (result) { + Nvm_To_RuntimeSettings(env->storage); + return result; + } + + + if ((auth == AUTH_ADMIN) || ((auth == AUTH_INTEG))) { + + if (env->modemMain->isModemCheck) { + if ((grp1) || (grp2) || (grp3) || (grp25)) { + + if (osMutexAcquire(httpSettings.accessMODEM, TIME_MUTEX_MODEM_ACCESS) != osOK) { + return Post_MUTEX_ERR; + } + result = ModemInitAt(env->modemMain, grp1, grp2, grp3, grp25); + osMutexRelease(httpSettings.accessMODEM); + + // Ошибка + if (result) { + Nvm_To_RuntimeSettings(env->storage); + return result; + } + } + } + + // Основной режим доступа + if (grp3) { + if (env->storage->runtime.Settings_Basic_Access.MaxSzDataOnKB_v != + env->storage->nvm.Settings_Basic_Access.MaxSzDataOnKB_v) { + + + if (env->storage->runtime.Settings_Basic_Access.MaxSzDataOnKB_v < + env->storage->nvm.Settings_Basic_Access.MaxSzDataOnKB_v) { + + // Удалить все пакеты из модема + delRepout(env->modemMain, TYPE_FILE_MESSAGE, false, true); + } + + env->modemMain->stateRequest.stateReqCreateExtT = StateReqExtForCreate; + env->modemMain->stateRequest.stateReqCreateExtW = StateReqExtForCreate; + + } + } + + // Загрузка настроек из временных + Runtime_To_NvmSettings(env->storage); + // Сохранение во флеш + DeviceStorageSetupIni_Dump(env->storage); + + // Настройки 485 и Блютуз + if (grp14) { + // Перенастройка порта ComInt485 + vSerialPort_ReInitUSART2(env->storage->nvm.Settings_RS485_Bluetooth.rs485baudrate_v); + ReInitExternalProtocol(env->external); + } + + + return result; + } else { + return Post_NO_AUTH; + } +} +// конец--------------------Сохранение настроек------------------------------------------------------------------------- +// конец--------------------Сохранение настроек------------------------------------------------------------------------- +// конец--------------------Сохранение настроек------------------------------------------------------------------------- + +// начало-------------------Запись WEB---------------------------------------------------------------------------------- +// начало-------------------Запись WEB---------------------------------------------------------------------------------- +// начало-------------------Запись WEB---------------------------------------------------------------------------------- + +idPostResult_t handlerPost_Update_Web_File(tHttpPostSetting *env) { + char BufWrite[512]; + char fileName[MAX_LEN_PATH_FS * 2]; + char dirUpdateFileName[MAX_LEN_PATH_FS]; + dirUpdateFileName[0] = '\0'; + strcat(dirUpdateFileName, "1:/TMP/web.dat"); + + FIL file; + FRESULT fr = f_open_i(env->fs, &file, (TCHAR *) dirUpdateFileName, FA_READ); + + if (fr) { + return Post_FS_ERR; + } + + + UINT bytes_read; + fr = f_read_i(env->fs, &file, env->bufAnswer, 12800, &bytes_read); + + if (fr) { + f_close_i(env->fs, &file); + return Post_FS_ERR; + } + + f_close_i(env->fs, &file); + + + uint32_t crcFileCalc = GetCrcFileFs(env->fs, dirUpdateFileName, 4); + uint32_t crcFile = ((uint32_t *) (env->bufAnswer))[0]; + + if (crcFileCalc != crcFile) { + return Post_CRC; + } + + char dir_web_local[MAX_LEN_PATH_FS]; + dir_web_local[0] = '\0'; + strcat(dir_web_local, dir_web); + + getDelFilesDir(env->fs, dir_web_local); + + + uint32_t fileTableDescriptorSize = ((uint32_t *) (env->bufAnswer))[1]; + tFileTableDescriptor *pFileTableDescriptor = (tFileTableDescriptor *) (env->bufAnswer + 8); + + for (uint32_t i = 0; i < fileTableDescriptorSize; ++i) { + + struct { + int paramcount; + char *params_names[MAX_POST_GET_PARAMETERS]; + char *params_vals[MAX_POST_GET_PARAMETERS]; + } params_path; + + fileName[0] = '\0'; + strcat(fileName, &pFileTableDescriptor->name[1]); + + params_path.paramcount = extract_path_ex_parameters(fileName, + params_path.params_names, + params_path.params_vals, + MAX_POST_GET_PARAMETERS); + + char dirPathFileWeb[MAX_LEN_PATH_FS]; + dirPathFileWeb[0] = '\0'; + strcat(dirPathFileWeb, dir_web); + + for (int j = 0; j < params_path.paramcount - 1; ++j) { + strcat(dirPathFileWeb, params_path.params_names[j]); + strcat(dirPathFileWeb, "/"); + fr = f_mkdir(dirPathFileWeb); + } + + ++pFileTableDescriptor; + } + + + pFileTableDescriptor = (tFileTableDescriptor *) (env->bufAnswer + 8); + + FIL fsrc, fdst; // File objects + UINT br, bw; // File read/write count + + fr = f_open_i(env->fs, &fsrc, (TCHAR *) dirUpdateFileName, FA_READ); + if (fr) { + return Post_FS_ERR; + } + + for (uint32_t i = 0; i < fileTableDescriptorSize; ++i) { + + fileName[0] = '\0'; + strcat(fileName, dir_web); + strcat(fileName, &pFileTableDescriptor->name[1]); + + fr = f_open_i(env->fs, &fdst, fileName, FA_CREATE_ALWAYS | FA_WRITE); + if (fr) { + f_close_i(env->fs, &fsrc); + return Post_FS_ERR; + } + + uint32_t leftFileSize = pFileTableDescriptor->size; + uint32_t lenTransPaket; + uint32_t offset = pFileTableDescriptor->address; + + while (leftFileSize > 0) { + + fr = f_lseek_i(env->fs, &fsrc, offset); + if (fr) { + f_close_i(env->fs, &fsrc); + f_close_i(env->fs, &fdst); + return Post_FS_ERR; + } + + lenTransPaket = TRANS_MIN(leftFileSize, sizeof(BufWrite)); + + f_read_i(env->fs, &fsrc, (BufWrite), lenTransPaket, &br); + f_write_i(env->fs, &fdst, (BufWrite), br, &bw); + + offset += lenTransPaket; + leftFileSize -= lenTransPaket; + } + + ++pFileTableDescriptor; + + f_close_i(env->fs, &fdst); + } + + f_close_i(env->fs, &fsrc); + + + fileName[0] = '\0'; + strcat(fileName, dir_web); + strcat(fileName, "load"); + fr = f_open_i(env->fs, &file, fileName, FA_CREATE_ALWAYS | FA_WRITE); + if (fr) { + return Post_FS_ERR; + } + f_close_i(env->fs, &file); + + return Post_OK; +} + +// конец-------------------Запись WEB----------------------------------------------------------------------------------- +// конец-------------------Запись WEB----------------------------------------------------------------------------------- +// конец-------------------Запись WEB----------------------------------------------------------------------------------- + + +// Инициализация прошивки КОНТРОЛЛЕРА +idPostResult_t handlerPost_Update_File_EraseFlash(tHttpPostSetting *env, tFirmwareLoaderXFSB firmwareLoaderXFSB) { + + uint32_t size = 0; + uint32_t crc = 0; + uint8_t descriptionLen = 0; + uint8_t description[MAX_LEN_PATH_FS]; + uint8_t stepParam = 0; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "size") == 0) { + size = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "crc") == 0) { + crc = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "description") == 0) { + extract_utf8_parameters(env->params_post_uri.params_vals[i], (char *) description, MAX_LEN_PATH_FS); + descriptionLen = strlen((char *) &description[1]) - 1; + ++stepParam; + } + } + + if (stepParam < 3) { + return Post_PARAM_ERR; + } + + if (firmwareLoaderXFSB == FIRMWARE_XFSB_MAIN) { + if (FirmwareLoader_PrepareNewUpdate(env->firmwareMainLoader, size, crc, &description[1], descriptionLen)) { + return Post_OK; + } + } + + if (firmwareLoaderXFSB == FIRMWARE_XFSB_BOOT) { + if (FirmwareLoader_PrepareNewUpdate(env->firmwareBootLoader, size, crc, &description[1], descriptionLen)) { + return Post_OK; + } + } + + return Post_FLASH_ERR; +} + +// Запись прошивки КОНТРОЛЛЕРА +idPostResult_t handlerPost_Update_File_WriteFlash(tHttpPostSetting *env, tFirmwareLoaderXFSB firmwareLoaderXFSB) { + uint32_t offset = 0; + uint8_t stepParam = 0; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "offset") == 0) { + offset = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + } + + if (stepParam < 1) { + return Post_PARAM_ERR; + } + + if (firmwareLoaderXFSB == FIRMWARE_XFSB_MAIN) { + if (FirmwareLoader_WriteUpdatePortion(env->firmwareMainLoader, offset, (uint8_t *) env->bufAnswer, + env->content_len)) { + return Post_OK; + } + } + + if (firmwareLoaderXFSB == FIRMWARE_XFSB_BOOT) { + if (FirmwareLoader_WriteUpdatePortion(env->firmwareBootLoader, offset, (uint8_t *) env->bufAnswer, + env->content_len)) { + return Post_OK; + } + } + + return Post_FLASH_ERR; +} + +idPostResult_t handlerPost_Update_Start_Main(tHttpPostSetting *env) { + + //GpioPinSet(&env->gpios->Power.gonec_reset, false); + + //BootJumpToAddress(0x08000000); + nvic_system_reset(); + + return Post_OK; +} + +// Запись прошивки криптоплаты +idPostResult_t handlerPost_Update_Crypto_WriteFlash(tHttpPostSetting *env) { + + uint32_t size = 0; + uint32_t crc = 0; + uint32_t offset = 0; + uint8_t stepParam = 0; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "size") == 0) { + size = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "crc") == 0) { + crc = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "offset") == 0) { + offset = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + } + + if (stepParam < 3) { + return Post_PARAM_ERR; + } + + idPostResult_t result = ModemCryptoWrite(env->modemMain, (uint8_t *) env->bufAnswer, crc, offset, size); + + return result; +} + +// Запись прошивки модема +idPostResult_t handlerPost_Update_Modem_WriteFlash(tHttpPostSetting *env) { + + uint32_t size = 0; + uint32_t crc = 0; + uint32_t offset = 0; + uint8_t stepParam = 0; + + for (int i = 0; i < env->params_post_uri.paramcount; ++i) { + + if (strcmp(env->params_post_uri.params_names[i], "size") == 0) { + size = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "crc") == 0) { + crc = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + if (strcmp(env->params_post_uri.params_names[i], "offset") == 0) { + offset = atoi(env->params_post_uri.params_vals[i]); + ++stepParam; + } + + } + + if (stepParam < 3) { + return Post_PARAM_ERR; + } + + idPostResult_t result = ModemCryptoWrite(env->modemMain, (uint8_t *) env->bufAnswer, crc, offset, size); + + return result; +} + + +// Инициализация записи прошивки крипто-платы +idPostResult_t handlerPost_Update_Init_Crypto_WriteFlash(tHttpPostSetting *env) { + + uint8_t sizeModem; + uint8_t sizeCrypto; + char versionModem[128]; + char versionCrypto[128]; + + memset(versionModem, 0, sizeof(versionModem)); + memset(versionCrypto, 0, sizeof(versionCrypto)); + strcpy(versionModem, "null,null"); + strcpy(versionCrypto, "null,null"); + + bool result = Modem_Get_Version_Modem(env->modemMain, versionModem, &sizeModem, versionCrypto, &sizeCrypto); + + if (result == false) { + return Post_SET_MODEM; + } + + char *firmwareCryptoBoot = "null\0"; + firmwareCryptoBoot = (char *) strchr(versionCrypto, ','); + if (firmwareCryptoBoot != NULL) { + *firmwareCryptoBoot = '\0'; + firmwareCryptoBoot++; + } + + if (strcmp(versionCrypto, "CRYPTO_BOOT") == 0) { + return Post_OK; + } + + return Post_NO_CRYPTO_BOOT; +} + + +// Запуск крипто-платы после прошивки +idPostResult_t handlerPost_Update_Start_Crypto(tHttpPostSetting *env) { + + uint8_t sizeModem; + uint8_t sizeCrypto; + char versionModem[128]; + char versionCrypto[128]; + + memset(versionModem, 0, sizeof(versionModem)); + memset(versionCrypto, 0, sizeof(versionCrypto)); + strcpy(versionModem, "null,null"); + strcpy(versionCrypto, "null,null"); + +// Запуск прошивки + bool result = RunModemOrCrypto(env->modemMain); + + SystemDelayMs(1000); + + result = Modem_Get_Version_Modem(env->modemMain, versionModem, &sizeModem, versionCrypto, &sizeCrypto); + + if (result == false) { + return Post_SET_MODEM; + } + + char *firmwareCryptoBoot = "null\0"; + firmwareCryptoBoot = (char *) strchr(versionCrypto, ','); + if (firmwareCryptoBoot != NULL) { + *firmwareCryptoBoot = '\0'; + firmwareCryptoBoot++; + } + if (strcmp(versionCrypto, "CRYPTO_MAIN") == 0) { + return Post_OK; + } + + return Post_NO_CRYPTO_MAIN; +} + +// Инициализация записи прошивки модема +idPostResult_t handlerPost_Update_Init_Modem_WriteFlash(tHttpPostSetting *env) { + + uint8_t sizeModem; + uint8_t sizeCrypto; + char versionModem[128]; + char versionCrypto[128]; + + memset(versionModem, 0, sizeof(versionModem)); + memset(versionCrypto, 0, sizeof(versionCrypto)); + strcpy(versionModem, "null,null"); + strcpy(versionCrypto, "null,null"); + + env->modemMain->isModemCheck = false; + + GpioPinSet(&env->gpios->Power.gonec_boot, true); + GpioPinSet(&env->gpios->Power.gonec_reset, false); + SystemDelayMs(1000); + GpioPinSet(&env->gpios->Power.gonec_reset, true); + + SystemDelayMs(6000); + + bool result = Modem_Get_Version_Modem(env->modemMain, versionModem, &sizeModem, versionCrypto, &sizeCrypto); + + + if (result == false) { + return Post_SET_MODEM; + } + + char *firmwareModemBoot = "null\0"; + firmwareModemBoot = (char *) strchr(versionModem, ','); + if (firmwareModemBoot != NULL) { + *firmwareModemBoot = '\0'; + firmwareModemBoot++; + } + + if (strcmp(versionModem, "MODEM_BOOT") == 0) { + return Post_OK; + } + + return Post_NO_MODEM_BOOT; +} + +// Запуск модема после прошивки +idPostResult_t handlerPost_Update_Start_Modem(tHttpPostSetting *env) { + + uint8_t sizeModem; + uint8_t sizeCrypto; + char versionModem[128]; + char versionCrypto[128]; + + memset(versionModem, 0, sizeof(versionModem)); + memset(versionCrypto, 0, sizeof(versionCrypto)); + strcpy(versionModem, "null,null"); + strcpy(versionCrypto, "null,null"); + + GpioPinSet(&env->gpios->Power.gonec_boot, false); + GpioPinSet(&env->gpios->Power.gonec_reset, false); + SystemDelayMs(1000); + GpioPinSet(&env->gpios->Power.gonec_reset, true); + + SystemDelayMs(6000); + + bool result = Modem_Get_Version_Modem(env->modemMain, versionModem, &sizeModem, versionCrypto, &sizeCrypto); + + if (result == false) { + return Post_SET_MODEM; + } + + char *firmwareModemBoot = "null\0"; + firmwareModemBoot = (char *) strchr(versionModem, ','); + if (firmwareModemBoot != NULL) { + *firmwareModemBoot = '\0'; + firmwareModemBoot++; + } + + env->modemMain->isModemCheck = false; + if (strcmp(versionModem, "MODEM_MAIN") == 0) { + env->modemMain->isModemCheck = true; + return Post_OK; + } + + return Post_NO_MODEM_MAIN; +} diff --git a/fsdata.c b/fsdata.c new file mode 100644 index 0000000..f0fe635 --- /dev/null +++ b/fsdata.c @@ -0,0 +1,1443 @@ +#include "lwip/apps/fs.h" +#include "lwip/def.h" + + +#define file_NULL (struct fsdata_file *) NULL + + +#ifndef FS_FILE_FLAGS_HEADER_INCLUDED +#define FS_FILE_FLAGS_HEADER_INCLUDED 1 +#endif +#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT +#define FS_FILE_FLAGS_HEADER_PERSISTENT 0 +#endif +/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */ +#ifndef FSDATA_FILE_ALIGNMENT +#define FSDATA_FILE_ALIGNMENT 0 +#endif +#ifndef FSDATA_ALIGN_PRE +#define FSDATA_ALIGN_PRE +#endif +#ifndef FSDATA_ALIGN_POST +#define FSDATA_ALIGN_POST +#endif +#if FSDATA_FILE_ALIGNMENT==2 +#include "fsdata_alignment.h" +#endif +#if FSDATA_FILE_ALIGNMENT==1 +static const unsigned int dummy_align__index_html = 0; +#endif +static const unsigned char FSDATA_ALIGN_PRE data__index_html[] FSDATA_ALIGN_POST = { +/* /index.html (12 chars) */ +0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00, + +/* HTTP header */ +/* "HTTP/1.0 200 OK +" (17 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, +0x0a, +/* "Server: lwIP/2.1.2 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x31, +0x2e,0x32,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-Length: 21996 +" (18+ bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20, +0x32,0x31,0x39,0x39,0x36,0x0d,0x0a, +/* "Content-Type: text/html + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, +0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, +/* raw file data (21996 bytes) */ +0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,0x20,0x68,0x74,0x6d,0x6c,0x3e,0x0a, +0x3c,0x68,0x74,0x6d,0x6c,0x20,0x6c,0x61,0x6e,0x67,0x3d,0x22,0x65,0x6e,0x22,0x3e, +0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c,0x6d,0x65,0x74, +0x61,0x20,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x3d,0x22,0x55,0x54,0x46,0x2d,0x38, +0x22,0x2f,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c,0x6d,0x65,0x74,0x61,0x20,0x68,0x74, +0x74,0x70,0x2d,0x65,0x71,0x75,0x69,0x76,0x3d,0x22,0x58,0x2d,0x55,0x41,0x2d,0x43, +0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x6c,0x65,0x22,0x20,0x63,0x6f,0x6e,0x74,0x65, +0x6e,0x74,0x3d,0x22,0x49,0x45,0x3d,0x65,0x64,0x67,0x65,0x22,0x2f,0x3e,0x0a,0x20, +0x20,0x20,0x20,0x3c,0x6d,0x65,0x74,0x61,0x20,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x76, +0x69,0x65,0x77,0x70,0x6f,0x72,0x74,0x22,0x20,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74, +0x3d,0x22,0x77,0x69,0x64,0x74,0x68,0x3d,0x64,0x65,0x76,0x69,0x63,0x65,0x2d,0x77, +0x69,0x64,0x74,0x68,0x2c,0x20,0x69,0x6e,0x69,0x74,0x69,0x61,0x6c,0x2d,0x73,0x63, +0x61,0x6c,0x65,0x3d,0x31,0x2e,0x30,0x22,0x2f,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c, +0x73,0x74,0x79,0x6c,0x65,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62, +0x6f,0x64,0x79,0x2e,0x6c,0x6f,0x67,0x69,0x6e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75, +0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23,0x31,0x35,0x32,0x32,0x33, +0x66,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x64,0x61,0x6e,0x67,0x65,0x72,0x20,0x7b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f, +0x72,0x3a,0x20,0x23,0x66,0x66,0x35,0x30,0x34,0x35,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68, +0x65,0x61,0x64,0x65,0x72,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x66,0x6c,0x65, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63, +0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23,0x66,0x66,0x66,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x69,0x64,0x74,0x68,0x3a,0x20,0x33, +0x30,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x20,0x61,0x75,0x74,0x6f,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x72,0x67, +0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20,0x31,0x37,0x2e,0x33,0x76,0x68,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x65,0x78, +0x2d,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x72,0x6f,0x77,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6a,0x75,0x73, +0x74,0x69,0x66,0x79,0x2d,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x20,0x63,0x65, +0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x61,0x6c,0x69,0x67,0x6e,0x2d,0x69,0x74,0x65,0x6d,0x73,0x3a,0x20,0x63, +0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x77,0x65,0x69,0x67,0x68,0x74,0x3a,0x20, +0x34,0x30,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x69,0x6e,0x65,0x2d,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x20,0x34,0x30, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x20,0x33,0x33,0x2e,0x33,0x70, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, +0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x20,0x22,0x41,0x72,0x69, +0x61,0x6c,0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x63,0x6f,0x6e,0x74,0x65,0x69,0x6e, +0x65,0x72,0x2d,0x66,0x6f,0x72,0x6d,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x20,0x61,0x75, +0x74,0x6f,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20,0x36,0x76,0x68,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x72, +0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20,0x32,0x34,0x70,0x78, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61, +0x64,0x64,0x69,0x6e,0x67,0x3a,0x20,0x34,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f, +0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x72,0x67,0x62,0x28,0x32, +0x34,0x35,0x2c,0x20,0x32,0x34,0x35,0x2c,0x20,0x32,0x34,0x35,0x29,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x78,0x2d,0x77, +0x69,0x64,0x74,0x68,0x3a,0x20,0x36,0x32,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3a, +0x20,0x33,0x39,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x66,0x6c,0x65, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, +0x6c,0x65,0x78,0x2d,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x63, +0x6f,0x6c,0x75,0x6d,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x6a,0x75,0x73,0x74,0x69,0x66,0x79,0x2d,0x63,0x6f,0x6e,0x74,0x65, +0x6e,0x74,0x3a,0x20,0x73,0x70,0x61,0x63,0x65,0x2d,0x62,0x65,0x74,0x77,0x65,0x65, +0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x74,0x69,0x74,0x6c,0x65,0x2d,0x68,0x65,0x61, +0x64,0x65,0x72,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20,0x63,0x65, +0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x20,0x33,0x34,0x70, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74, +0x65,0x78,0x74,0x2d,0x73,0x68,0x61,0x64,0x6f,0x77,0x3a,0x20,0x31,0x70,0x78,0x20, +0x30,0x70,0x78,0x20,0x33,0x70,0x78,0x20,0x72,0x67,0x62,0x61,0x28,0x30,0x2c,0x20, +0x30,0x2c,0x20,0x30,0x2c,0x20,0x30,0x2e,0x33,0x36,0x35,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x2e,0x62,0x75,0x74,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20, +0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2a, +0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20,0x61,0x75,0x74, +0x6f,0x3b,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20,0x63,0x65, +0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a, +0x20,0x33,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x62,0x74,0x6e,0x2d,0x6c, +0x6f,0x61,0x64,0x2d,0x73,0x6f,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x7b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x75,0x72,0x73,0x6f, +0x72,0x3a,0x20,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a, +0x65,0x3a,0x20,0x33,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x20,0x31,0x36, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20,0x39, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72, +0x3a,0x20,0x23,0x31,0x45,0x37,0x30,0x45,0x42,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3a, +0x20,0x23,0x66,0x66,0x66,0x3b,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x72,0x67,0x62, +0x28,0x32,0x35,0x33,0x2c,0x20,0x32,0x35,0x33,0x2c,0x20,0x32,0x35,0x33,0x29,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x72, +0x64,0x65,0x72,0x3a,0x20,0x6e,0x6f,0x6e,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x6c, +0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20, +0x66,0x6c,0x65,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x66,0x6c,0x65,0x78,0x2d,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e, +0x3a,0x20,0x63,0x6f,0x6c,0x75,0x6d,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2a,0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a, +0x20,0x32,0x70,0x78,0x20,0x73,0x6f,0x6c,0x69,0x64,0x20,0x23,0x36,0x39,0x37,0x36, +0x38,0x45,0x3b,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x6a,0x75,0x73,0x74,0x69,0x66,0x79,0x2d,0x63,0x6f,0x6e,0x74,0x65, +0x6e,0x74,0x3a,0x20,0x73,0x70,0x61,0x63,0x65,0x2d,0x62,0x65,0x74,0x77,0x65,0x65, +0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68, +0x65,0x69,0x67,0x68,0x74,0x3a,0x20,0x33,0x30,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x61,0x63,0x6b,0x67,0x72, +0x6f,0x75,0x6e,0x64,0x3a,0x20,0x23,0x46,0x46,0x46,0x46,0x46,0x46,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x78,0x2d,0x73, +0x68,0x61,0x64,0x6f,0x77,0x3a,0x20,0x30,0x70,0x78,0x20,0x34,0x70,0x78,0x20,0x31, +0x36,0x70,0x78,0x20,0x23,0x45,0x35,0x45,0x42,0x46,0x33,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d, +0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20,0x31,0x36,0x70,0x78,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2a,0x20,0x70,0x61,0x64, +0x64,0x69,0x6e,0x67,0x3a,0x20,0x32,0x34,0x70,0x78,0x3b,0x20,0x2a,0x2f,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x2e,0x74,0x69,0x74,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f, +0x6e,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20,0x32,0x35,0x70, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64, +0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x66,0x6c,0x65,0x78,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73, +0x69,0x7a,0x65,0x3a,0x20,0x32,0x36,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6a,0x75,0x73,0x74,0x69,0x66,0x79,0x2d,0x63, +0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x65,0x78,0x74, +0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f, +0x72,0x3a,0x20,0x23,0x36,0x39,0x37,0x36,0x38,0x45,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e, +0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20, +0x66,0x6c,0x65,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x66,0x6c,0x65,0x78,0x2d,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e, +0x3a,0x20,0x63,0x6f,0x6c,0x75,0x6d,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x6c,0x69,0x67,0x6e,0x2d,0x69,0x74,0x65,0x6d, +0x73,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6a,0x75,0x73,0x74,0x69,0x66,0x79,0x2d,0x63, +0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x20,0x73,0x70,0x61,0x63,0x65,0x2d,0x62,0x65, +0x74,0x77,0x65,0x65,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2f,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x20, +0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69, +0x7a,0x65,0x3a,0x20,0x33,0x32,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x6c,0x61, +0x62,0x65,0x6c,0x2d,0x66,0x6f,0x72,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64, +0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63, +0x75,0x72,0x73,0x6f,0x72,0x3a,0x20,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74, +0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x20,0x27,0x49,0x6e,0x74,0x65,0x72,0x27, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f, +0x6c,0x6f,0x72,0x3a,0x20,0x23,0x31,0x45,0x37,0x30,0x45,0x42,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73, +0x69,0x7a,0x65,0x3a,0x20,0x32,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67, +0x6e,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x2d,0x68,0x65,0x69, +0x67,0x68,0x74,0x3a,0x20,0x32,0x34,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x63, +0x6f,0x6e,0x63,0x65,0x61,0x6c,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x6e,0x6f, +0x6e,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x66,0x69,0x6c,0x65,0x2d,0x6c,0x6f,0x61, +0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x7b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, +0x6f,0x6e,0x3a,0x20,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x69,0x67,0x68,0x74, +0x3a,0x20,0x34,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x20,0x31, +0x36,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x69,0x6e,0x65,0x2d,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x20,0x32,0x34, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23,0x31,0x35,0x32,0x32,0x33,0x46,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x72,0x67, +0x69,0x6e,0x3a,0x20,0x31,0x32,0x70,0x78,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62, +0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x20,0x32,0x36,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2e,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, +0x6f,0x6e,0x3a,0x20,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x20,0x2a,0x2f, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x61,0x63, +0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23, +0x46,0x33,0x46,0x37,0x46,0x43,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x20,0x31,0x36,0x70, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62, +0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20,0x34,0x70, +0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70, +0x61,0x64,0x64,0x69,0x6e,0x67,0x2d,0x72,0x69,0x67,0x68,0x74,0x3a,0x20,0x33,0x35, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69,0x74, +0x6c,0x65,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x20,0x33,0x30, +0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a, +0x65,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23,0x42,0x39,0x42,0x44,0x43,0x35,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x2d,0x66,0x69,0x6c,0x65,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x6f,0x73, +0x69,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x75,0x72, +0x73,0x6f,0x72,0x3a,0x20,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x2e,0x63,0x72,0x6f,0x73,0x73,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x20, +0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x62, +0x6c,0x6f,0x63,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x77,0x69,0x64,0x74,0x68,0x3a,0x20,0x32,0x34,0x70,0x78,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x69,0x67,0x68, +0x74,0x3a,0x20,0x32,0x34,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x70,0x3a,0x20,0x2d,0x36,0x70,0x78,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2e,0x77,0x61,0x69,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x20,0x33, +0x30,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x20,0x23,0x46, +0x46,0x46,0x46,0x46,0x46,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x62,0x6f,0x78,0x2d,0x73,0x68,0x61,0x64,0x6f,0x77,0x3a,0x20,0x30, +0x70,0x78,0x20,0x34,0x70,0x78,0x20,0x31,0x36,0x70,0x78,0x20,0x23,0x45,0x35,0x45, +0x42,0x46,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20, +0x31,0x36,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x74,0x65,0x78,0x74,0x2d,0x63, +0x6f,0x6e,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x20,0x66,0x6c,0x65,0x78,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x65, +0x78,0x2d,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x63,0x6f,0x6c, +0x75,0x6d,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x61,0x6c,0x69,0x67,0x6e,0x2d,0x69,0x74,0x65,0x6d,0x73,0x3a,0x20,0x63,0x65, +0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6a,0x75,0x73,0x74,0x69,0x66,0x79,0x2d,0x63,0x6f,0x6e,0x74,0x65,0x6e, +0x74,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65, +0x3a,0x20,0x32,0x36,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20, +0x33,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x74,0x65,0x78,0x74,0x2d,0x77, +0x61,0x69,0x74,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a, +0x20,0x32,0x30,0x70,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23,0x36,0x39,0x37,0x36,0x38, +0x45,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x3c,0x2f,0x73,0x74,0x79,0x6c,0x65,0x3e,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x3c,0x74,0x69,0x74,0x6c,0x65,0x3e,0xd0,0x92,0xd0,0xbe,0xd1,0x81,0xd1,0x81, +0xd1,0x82,0xd0,0xbe,0xd0,0xbd,0xd0,0xbe,0xd0,0xb2,0xd0,0xbb,0xd0,0xb5,0xd0,0xbd, +0xd0,0xb8,0xd0,0xb5,0x20,0xd0,0xbf,0xd1,0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0, +0xb2,0xd0,0xba,0xd0,0xb8,0x20,0x56,0x30,0x30,0x31,0x28,0x41,0x68,0x32,0x67,0x68, +0x39,0x29,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c, +0x73,0x63,0x72,0x69,0x70,0x74,0x3e,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x64,0x65,0x28,0x6e,0x61,0x6d, +0x65,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74, +0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28, +0x6e,0x61,0x6d,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x75,0x6e,0x63,0x74,0x69, +0x6f,0x6e,0x20,0x64,0x71,0x28,0x6e,0x61,0x6d,0x65,0x29,0x20,0x7b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, +0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x71,0x75,0x65,0x72,0x79,0x53, +0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x28,0x6e,0x61,0x6d,0x65,0x29,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e, +0x20,0x73,0x6c,0x65,0x65,0x70,0x28,0x6d,0x73,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, +0x6e,0x65,0x77,0x20,0x50,0x72,0x6f,0x6d,0x69,0x73,0x65,0x28,0x28,0x72,0x65,0x73, +0x6f,0x6c,0x76,0x65,0x29,0x20,0x3d,0x3e,0x20,0x73,0x65,0x74,0x54,0x69,0x6d,0x65, +0x6f,0x75,0x74,0x28,0x28,0x29,0x20,0x3d,0x3e,0x20,0x72,0x65,0x73,0x6f,0x6c,0x76, +0x65,0x28,0x29,0x2c,0x20,0x6d,0x73,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x75, +0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x67,0x65,0x74,0x56,0x61,0x6c,0x69,0x64,0x50, +0x61,0x63,0x6b,0x61,0x67,0x65,0x28,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65, +0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x73,0x77,0x69,0x74,0x63,0x68,0x20,0x28,0x74,0x72,0x75,0x65,0x29,0x20,0x7b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x61,0x73,0x65,0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x20,0x3c, +0x3d,0x20,0x31,0x30,0x32,0x34,0x3a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x61,0x6c,0x69, +0x64,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a, +0x65,0x20,0x2b,0x20,0x22,0x20,0xd0,0x91,0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72, +0x65,0x74,0x75,0x72,0x6e,0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x61,0x73,0x65, +0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x20,0x3c,0x3d,0x20,0x31,0x30, +0x32,0x34,0x20,0x2a,0x20,0x31,0x30,0x32,0x34,0x3a,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76, +0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x4d,0x61,0x74,0x68,0x2e, +0x63,0x65,0x69,0x6c,0x28,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x20,0x2f, +0x20,0x31,0x30,0x32,0x34,0x29,0x20,0x2b,0x20,0x22,0x20,0x4b,0xd0,0x91,0x22,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x76,0x61,0x6c,0x69, +0x64,0x53,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x63,0x61,0x73,0x65,0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65, +0x20,0x3c,0x3d,0x20,0x31,0x30,0x32,0x34,0x20,0x2a,0x20,0x31,0x30,0x32,0x34,0x20, +0x2a,0x20,0x31,0x30,0x32,0x34,0x3a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x61,0x6c,0x69, +0x64,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x4d,0x61,0x74,0x68,0x2e,0x63,0x65,0x69, +0x6c,0x28,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x20,0x2f,0x20,0x31,0x30, +0x32,0x34,0x20,0x2f,0x20,0x31,0x30,0x32,0x34,0x29,0x20,0x2b,0x20,0x22,0x20,0x4d, +0xd0,0x91,0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, +0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62, +0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20, +0x67,0x65,0x74,0x54,0x69,0x74,0x6c,0x65,0x53,0x69,0x7a,0x65,0x28,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x64,0x20,0x3d,0x20,0x22, +0x66,0x69,0x6c,0x65,0x2d,0x6d,0x65,0x73,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x61,0x62,0x65,0x6c,0x20,0x3d,0x20,0x22, +0x6c,0x61,0x62,0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x22,0x2c,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x61,0x64,0x43,0x6f,0x6e,0x74, +0x61,0x69,0x69,0x6e,0x65,0x72,0x20,0x3d,0x20,0x22,0x66,0x69,0x6c,0x65,0x2d,0x6c, +0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x22,0x2c,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x69,0x74,0x6c, +0x65,0x20,0x3d,0x20,0x22,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69,0x74,0x6c,0x65,0x22, +0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x69, +0x7a,0x65,0x20,0x3d,0x20,0x22,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65,0x22, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x63,0x75,0x72, +0x46,0x69,0x6c,0x65,0x20,0x3d,0x20,0x64,0x65,0x28,0x69,0x64,0x29,0x2e,0x66,0x69, +0x6c,0x65,0x73,0x5b,0x30,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a, +0x65,0x20,0x3d,0x20,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x2e,0x73,0x69,0x7a,0x65, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65, +0x74,0x20,0x76,0x61,0x6c,0x69,0x64,0x50,0x61,0x63,0x6b,0x61,0x67,0x65,0x20,0x3d, +0x20,0x67,0x65,0x74,0x56,0x61,0x6c,0x69,0x64,0x50,0x61,0x63,0x6b,0x61,0x67,0x65, +0x28,0x76,0x61,0x6c,0x69,0x64,0x53,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x28,0x6c,0x61,0x62,0x65, +0x6c,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73,0x4c,0x69,0x73,0x74,0x2e,0x61,0x64,0x64, +0x28,0x22,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x28,0x6c,0x61,0x64,0x43, +0x6f,0x6e,0x74,0x61,0x69,0x69,0x6e,0x65,0x72,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73, +0x4c,0x69,0x73,0x74,0x2e,0x72,0x65,0x6d,0x6f,0x76,0x65,0x28,0x22,0x63,0x6f,0x6e, +0x63,0x65,0x61,0x6c,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28, +0x64,0x65,0x28,0x6c,0x61,0x64,0x43,0x6f,0x6e,0x74,0x61,0x69,0x69,0x6e,0x65,0x72, +0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x64,0x65,0x28,0x74,0x69,0x74,0x6c,0x65,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48, +0x54,0x4d,0x4c,0x20,0x3d,0x20,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x2e,0x6e,0x61, +0x6d,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x64,0x65,0x28,0x73,0x69,0x7a,0x65,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54, +0x4d,0x4c,0x20,0x3d,0x20,0x60,0x20,0x28,0x24,0x7b,0x76,0x61,0x6c,0x69,0x64,0x50, +0x61,0x63,0x6b,0x61,0x67,0x65,0x7d,0x29,0x20,0x60,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, +0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x63,0x6c,0x6f,0x73,0x65,0x4c,0x6f,0x61, +0x64,0x43,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x28,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x64,0x20,0x3d,0x20,0x22,0x66,0x69, +0x6c,0x65,0x2d,0x6d,0x65,0x73,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x6c,0x61,0x62,0x65,0x6c,0x20,0x3d,0x20,0x22,0x6c,0x61, +0x62,0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72, +0x20,0x3d,0x20,0x22,0x66,0x69,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f, +0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x69,0x74,0x6c,0x65,0x20,0x3d,0x20,0x22,0x66, +0x69,0x6c,0x65,0x2d,0x74,0x69,0x74,0x6c,0x65,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x22, +0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65,0x22,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x64,0x65,0x28,0x69,0x64,0x29,0x2e,0x76,0x61,0x6c,0x75,0x65,0x20, +0x3d,0x20,0x22,0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x64,0x65,0x28,0x6c,0x61,0x62,0x65,0x6c,0x29,0x2e,0x63,0x6c,0x61,0x73, +0x73,0x4c,0x69,0x73,0x74,0x2e,0x72,0x65,0x6d,0x6f,0x76,0x65,0x28,0x22,0x63,0x6f, +0x6e,0x63,0x65,0x61,0x6c,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x28,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65, +0x72,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73,0x4c,0x69,0x73,0x74,0x2e,0x61,0x64,0x64, +0x28,0x22,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x28,0x74,0x69,0x74,0x6c, +0x65,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x20,0x3d,0x20,0x22, +0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64, +0x65,0x28,0x73,0x69,0x7a,0x65,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d, +0x4c,0x20,0x3d,0x20,0x60,0x20,0x60,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x75,0x6e,0x63, +0x74,0x69,0x6f,0x6e,0x20,0x63,0x68,0x61,0x6e,0x67,0x65,0x43,0x6f,0x6e,0x74,0x28, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x61,0x69, +0x74,0x69,0x6e,0x67,0x20,0x3d,0x20,0x22,0x77,0x61,0x69,0x74,0x2d,0x66,0x69,0x6c, +0x65,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6c,0x6f,0x61,0x64,0x69,0x6e,0x67,0x20,0x3d,0x20,0x22,0x6c,0x6f,0x61,0x64,0x2d, +0x66,0x69,0x6c,0x65,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x20, +0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65, +0x28,0x6c,0x6f,0x61,0x64,0x69,0x6e,0x67,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73,0x4c, +0x69,0x73,0x74,0x2e,0x61,0x64,0x64,0x28,0x22,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c, +0x22,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64, +0x65,0x28,0x77,0x61,0x69,0x74,0x69,0x6e,0x67,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73, +0x4c,0x69,0x73,0x74,0x2e,0x72,0x65,0x6d,0x6f,0x76,0x65,0x28,0x22,0x63,0x6f,0x6e, +0x63,0x65,0x61,0x6c,0x22,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x75,0x6e,0x63,0x74,0x69, +0x6f,0x6e,0x20,0x63,0x68,0x61,0x6e,0x67,0x65,0x54,0x65,0x78,0x74,0x28,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x72,0x79,0x41,0x67, +0x61,0x69,0x6e,0x20,0x3d,0x20,0x22,0x74,0x72,0x79,0x22,0x2c,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20, +0x22,0x6d,0x61,0x69,0x6e,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29, +0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64, +0x65,0x28,0x6d,0x61,0x69,0x6e,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73,0x4c,0x69,0x73, +0x74,0x2e,0x61,0x64,0x64,0x28,0x22,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x29, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x28, +0x74,0x72,0x79,0x41,0x67,0x61,0x69,0x6e,0x29,0x2e,0x63,0x6c,0x61,0x73,0x73,0x4c, +0x69,0x73,0x74,0x2e,0x72,0x65,0x6d,0x6f,0x76,0x65,0x28,0x22,0x63,0x6f,0x6e,0x63, +0x65,0x61,0x6c,0x22,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, +0x20,0x20,0x20,0x20,0x3c,0x2f,0x73,0x63,0x72,0x69,0x70,0x74,0x3e,0x0a,0x0a,0x3c, +0x2f,0x68,0x65,0x61,0x64,0x3e,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x63,0x6c,0x61, +0x73,0x73,0x3d,0x22,0x6c,0x6f,0x67,0x69,0x6e,0x22,0x3e,0x0a,0x3c,0x68,0x65,0x61, +0x64,0x65,0x72,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c,0x73,0x70,0x61,0x6e,0x20,0x63, +0x6c,0x61,0x73,0x73,0x3d,0x22,0x74,0x69,0x74,0x6c,0x65,0x2d,0x67,0x6f,0x6e,0x65, +0x73,0x22,0x3e,0xd0,0x93,0xd0,0x9e,0xd0,0x9d,0xd0,0x95,0xd0,0xa6,0x20,0x50,0x52, +0x4f,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x3c,0x2f,0x68,0x65,0x61,0x64,0x65, +0x72,0x3e,0x0a,0x3c,0x6d,0x61,0x69,0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c,0x64, +0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x63,0x6f,0x6e,0x74,0x65,0x69, +0x6e,0x65,0x72,0x2d,0x66,0x6f,0x72,0x6d,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x74, +0x69,0x74,0x6c,0x65,0x2d,0x68,0x65,0x61,0x64,0x65,0x72,0x20,0x64,0x61,0x6e,0x67, +0x65,0x72,0x22,0x3e,0xd0,0x90,0xd0,0xb2,0xd0,0xb0,0xd1,0x80,0xd0,0xb8,0xd0,0xb9, +0xd0,0xbd,0xd0,0xbe,0xd0,0xb5,0x20,0xd0,0xb2,0xd0,0xbe,0xd1,0x81,0xd1,0x81,0xd1, +0x82,0xd0,0xb0,0xd0,0xbd,0xd0,0xbe,0xd0,0xb2,0xd0,0xbb,0xd0,0xb5,0xd0,0xbd,0xd0, +0xb8,0xd0,0xb5,0x20,0xd0,0xbf,0xd1,0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2, +0xd0,0xba,0xd0,0xb8,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x77, +0x61,0x69,0x74,0x20,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x20,0x69,0x64,0x3d, +0x22,0x77,0x61,0x69,0x74,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22, +0x74,0x65,0x78,0x74,0x2d,0x63,0x6f,0x6e,0x74,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x70,0x61, +0x6e,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x74,0x65,0x78,0x74,0x2d,0x77,0x61, +0x69,0x74,0x22,0x3e,0xd0,0xa3,0xd1,0x81,0xd0,0xbf,0xd0,0xb5,0xd1,0x88,0xd0,0xbd, +0xd0,0xbe,0x20,0xd0,0xb7,0xd0,0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb6,0xd0, +0xb5,0xd0,0xbd,0xd0,0xbe,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x70, +0x61,0x6e,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x74,0x65,0x78,0x74,0x2d,0x77, +0x61,0x69,0x74,0x22,0x3e,0xd0,0x9e,0xd0,0xb6,0xd0,0xb8,0xd0,0xb4,0xd0,0xb0,0xd0, +0xb9,0xd1,0x82,0xd0,0xb5,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73, +0x73,0x3d,0x22,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x22,0x20,0x69,0x64, +0x3d,0x22,0x6c,0x6f,0x61,0x64,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x70,0x61,0x6e,0x20,0x63,0x6c,0x61,0x73,0x73, +0x3d,0x22,0x74,0x69,0x74,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e, +0x74,0x22,0x20,0x69,0x64,0x3d,0x22,0x6d,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74, +0x22,0x3e,0xd0,0x92,0xd1,0x8b,0xd0,0xb1,0xd0,0xb5,0xd1,0x80,0xd0,0xb8,0xd1,0x82, +0xd0,0xb5,0x20,0xd1,0x84,0xd0,0xb0,0xd0,0xb9,0xd0,0xbb,0x20,0xd0,0xbe,0xd0,0xb1, +0xd0,0xbd,0xd0,0xbe,0xd0,0xb2,0xd0,0xbb,0xd0,0xb5,0xd0,0xbd,0xd0,0xb8,0xd1,0x8f, +0x2e,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x70,0x61,0x6e,0x20,0x63,0x6c,0x61,0x73,0x73, +0x3d,0x22,0x74,0x69,0x74,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e, +0x74,0x20,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x20,0x69,0x64,0x3d,0x22,0x74, +0x72,0x79,0x2d,0x61,0x67,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74,0x22,0x3e,0xd0, +0x9e,0xd1,0x88,0xd0,0xb8,0xd0,0xb1,0xd0,0xba,0xd0,0xb0,0x2e,0x20,0xd0,0x9f,0xd0, +0xbe,0xd0,0xbf,0xd1,0x80,0xd0,0xbe,0xd0,0xb1,0xd1,0x83,0xd0,0xb9,0xd1,0x82,0xd0, +0xb5,0x20,0xd0,0xb5,0xd1,0x89,0xd1,0x91,0x20,0xd1,0x80,0xd0,0xb0,0xd0,0xb7,0x2e, +0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22, +0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x6c,0x61,0x62,0x65, +0x6c,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x6c,0x61,0x62,0x65,0x6c,0x2d,0x66, +0x6f,0x72,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x22,0x20,0x69,0x64,0x3d, +0x22,0x6c,0x61,0x62,0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x22, +0x20,0x66,0x6f,0x72,0x3d,0x22,0x70,0x61,0x63,0x6b,0x61,0x67,0x65,0x2d,0x75,0x70, +0x64,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xd0,0x92,0xd1,0x8b,0xd0,0xb1,0xd1,0x80, +0xd0,0xb0,0xd1,0x82,0xd1,0x8c,0x20,0xd1,0x84,0xd0,0xb0,0xd0,0xb9,0xd0,0xbb,0x20, +0xd0,0xbe,0xd0,0xb1,0xd0,0xbd,0xd0,0xbe,0xd0,0xb2,0xd0,0xbb,0xd0,0xb5,0xd0,0xbd, +0xd0,0xb8,0xd1,0x8f,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x6c,0x61,0x62,0x65,0x6c,0x3e,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x69, +0x6e,0x70,0x75,0x74,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6c,0x61, +0x73,0x73,0x3d,0x22,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x74,0x79,0x70,0x65,0x3d,0x22,0x66,0x69,0x6c,0x65,0x22, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x64,0x3d,0x22,0x70,0x61,0x63, +0x6b,0x61,0x67,0x65,0x2d,0x75,0x70,0x64,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6f,0x6e,0x63,0x68,0x61,0x6e,0x67,0x65,0x3d,0x22,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x69,0x6e,0x64,0x6f,0x77,0x2e, +0x67,0x6c,0x6f,0x62,0x61,0x6c,0x54,0x68,0x69,0x73,0x2e,0x73,0x69,0x7a,0x65,0x49, +0x6e,0x20,0x3d,0x20,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x5b,0x30, +0x5d,0x2e,0x73,0x69,0x7a,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x67,0x65,0x74,0x54,0x69,0x74,0x6c,0x65,0x53,0x69,0x7a,0x65,0x28, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27, +0x70,0x61,0x63,0x6b,0x61,0x67,0x65,0x2d,0x75,0x70,0x64,0x27,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x6c,0x61,0x62, +0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x27,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c, +0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72, +0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69, +0x74,0x6c,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65, +0x2d,0x73,0x69,0x7a,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x22,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2f,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x69,0x64,0x3d,0x22,0x66,0x69,0x6c,0x65, +0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x2d, +0x75,0x70,0x64,0x61,0x74,0x65,0x22,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x66, +0x69,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e, +0x65,0x72,0x20,0x63,0x6f,0x6e,0x63,0x65,0x61,0x6c,0x22,0x3e,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x63,0x6f,0x6e, +0x74,0x65,0x6e,0x74,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c, +0x73,0x70,0x61,0x6e,0x20,0x69,0x64,0x3d,0x22,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69, +0x74,0x6c,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x22,0x20,0x63,0x6c,0x61,0x73, +0x73,0x3d,0x22,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69,0x74,0x6c,0x65,0x22,0x3e,0x3c, +0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c, +0x73,0x70,0x61,0x6e,0x20,0x69,0x64,0x3d,0x22,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69, +0x7a,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x22,0x20,0x63,0x6c,0x61,0x73,0x73, +0x3d,0x22,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65,0x22,0x3e,0x3c,0x2f,0x73, +0x70,0x61,0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x70, +0x61,0x6e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x63,0x6c,0x6f,0x73,0x65,0x2d, +0x66,0x69,0x6c,0x65,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x63,0x6c,0x69,0x63,0x6b,0x3d,0x22,0x63, +0x6c,0x6f,0x73,0x65,0x4c,0x6f,0x61,0x64,0x43,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65, +0x72,0x28,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x70,0x61,0x63,0x6b,0x61,0x67,0x65,0x2d,0x75, +0x70,0x64,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x6c,0x61,0x62,0x65,0x6c,0x2d,0x66,0x69, +0x6c,0x65,0x2d,0x75,0x70,0x64,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65, +0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x2d, +0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65, +0x2d,0x74,0x69,0x74,0x6c,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65,0x2d,0x75,0x70,0x64, +0x61,0x74,0x65,0x27,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x22,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x63, +0x72,0x6f,0x73,0x73,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x73,0x76,0x67,0x20,0x77,0x69,0x64,0x74, +0x68,0x3d,0x22,0x32,0x34,0x22,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3d,0x22,0x33, +0x32,0x22,0x20,0x76,0x69,0x65,0x77,0x42,0x6f,0x78,0x3d,0x22,0x30,0x20,0x30,0x20, +0x33,0x32,0x20,0x33,0x32,0x22,0x20,0x66,0x69,0x6c,0x6c,0x3d,0x22,0x6e,0x6f,0x6e, +0x65,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f, +0x2f,0x77,0x77,0x77,0x2e,0x77,0x33,0x2e,0x6f,0x72,0x67,0x2f,0x32,0x30,0x30,0x30, +0x2f,0x73,0x76,0x67,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x70,0x61,0x74,0x68,0x20,0x64, +0x3d,0x22,0x4d,0x32,0x35,0x20,0x37,0x4c,0x37,0x20,0x32,0x35,0x22,0x20,0x73,0x74, +0x72,0x6f,0x6b,0x65,0x3d,0x22,0x23,0x31,0x35,0x32,0x32,0x33,0x46,0x22,0x20,0x73, +0x74,0x72,0x6f,0x6b,0x65,0x2d,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x32,0x2e,0x35, +0x22,0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x2d,0x6c,0x69,0x6e,0x65,0x63,0x61,0x70, +0x3d,0x22,0x72,0x6f,0x75,0x6e,0x64,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x2d,0x6c,0x69,0x6e,0x65,0x6a,0x6f,0x69,0x6e, +0x3d,0x22,0x72,0x6f,0x75,0x6e,0x64,0x22,0x2f,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x70,0x61, +0x74,0x68,0x20,0x64,0x3d,0x22,0x4d,0x32,0x35,0x20,0x32,0x35,0x4c,0x37,0x20,0x37, +0x22,0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x3d,0x22,0x23,0x31,0x35,0x32,0x32,0x33, +0x46,0x22,0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x2d,0x77,0x69,0x64,0x74,0x68,0x3d, +0x22,0x32,0x2e,0x35,0x22,0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x2d,0x6c,0x69,0x6e, +0x65,0x63,0x61,0x70,0x3d,0x22,0x72,0x6f,0x75,0x6e,0x64,0x22,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x73,0x74,0x72,0x6f,0x6b,0x65,0x2d,0x6c,0x69,0x6e,0x65, +0x6a,0x6f,0x69,0x6e,0x3d,0x22,0x72,0x6f,0x75,0x6e,0x64,0x22,0x2f,0x3e,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c, +0x2f,0x73,0x76,0x67,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64, +0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x62,0x75,0x74,0x2d, +0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76, +0x20,0x69,0x64,0x3d,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x74,0x65,0x78,0x74, +0x22,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a, +0x20,0x32,0x30,0x70,0x78,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e, +0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76, +0x3e,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x69,0x64,0x3d,0x22,0x6c,0x6f,0x61,0x64, +0x69,0x6e,0x67,0x22,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x76,0x69,0x73,0x69, +0x62,0x69,0x6c,0x69,0x74,0x79,0x3a,0x20,0x68,0x69,0x64,0x64,0x65,0x6e,0x3b,0x20, +0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x20,0x32,0x30,0x70,0x78,0x22,0x3e,0x0a,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22, +0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74, +0x68,0x3a,0x20,0x31,0x30,0x30,0x25,0x3b,0x20,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f, +0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x67,0x72,0x61,0x79,0x3b, +0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x20,0x35, +0x70,0x78,0x3b,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x64, +0x69,0x76,0x20,0x69,0x64,0x3d,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x62,0x61, +0x72,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x30, +0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x20,0x30,0x3b,0x20,0x62,0x61,0x63, +0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x23, +0x32,0x34,0x63,0x33,0x38,0x65,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61, +0x64,0x69,0x75,0x73,0x3a,0x20,0x35,0x70,0x78,0x3b,0x22,0x3e,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c, +0x62,0x75,0x74,0x74,0x6f,0x6e,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x73,0x75, +0x6d,0x62,0x69,0x74,0x20,0x73,0x75,0x6d,0x62,0x69,0x74,0x2d,0x73,0x61,0x76,0x65, +0x20,0x62,0x74,0x6e,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x73,0x6f,0x66,0x74,0x77,0x61, +0x72,0x65,0x22,0x20,0x69,0x64,0x3d,0x22,0x73,0x61,0x76,0x65,0x2d,0x62,0x75,0x74, +0x74,0x6f,0x6e,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3e,0xd0,0x97,0xd0,0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83, +0xd0,0xb7,0xd0,0xb8,0xd1,0x82,0xd1,0x8c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x62,0x75,0x74,0x74,0x6f, +0x6e,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c, +0x2f,0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f, +0x64,0x69,0x76,0x3e,0x0a,0x20,0x20,0x20,0x20,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x0a, +0x3c,0x2f,0x6d,0x61,0x69,0x6e,0x3e,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0a, +0x0a,0x0a,0x3c,0x73,0x63,0x72,0x69,0x70,0x74,0x3e,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6e,0x73,0x74,0x20,0x67,0x65,0x74,0x4c,0x65,0x6e,0x67,0x74,0x68,0x46, +0x69,0x6c,0x65,0x20,0x3d,0x20,0x28,0x75,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x29, +0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f, +0x72,0x20,0x28,0x6c,0x65,0x74,0x20,0x69,0x20,0x3d,0x20,0x75,0x69,0x6e,0x74,0x38, +0x41,0x72,0x72,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x20,0x2d,0x20,0x31,0x3b,0x20, +0x69,0x20,0x3e,0x20,0x30,0x3b,0x20,0x69,0x2d,0x2d,0x29,0x20,0x7b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x75,0x69, +0x6e,0x74,0x38,0x41,0x72,0x72,0x5b,0x69,0x5d,0x20,0x21,0x3d,0x3d,0x20,0x30,0x29, +0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x69,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x63, +0x6c,0x61,0x73,0x73,0x20,0x46,0x69,0x72,0x6d,0x77,0x61,0x72,0x65,0x55,0x70,0x64, +0x61,0x74,0x65,0x72,0x20,0x7b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x6c,0x6f,0x61, +0x64,0x46,0x69,0x72,0x6d,0x77,0x61,0x72,0x65,0x28,0x75,0x72,0x6c,0x50,0x72,0x65, +0x66,0x69,0x78,0x2c,0x20,0x66,0x69,0x6c,0x65,0x2c,0x20,0x6f,0x6e,0x42,0x65,0x67, +0x69,0x6e,0x2c,0x20,0x6f,0x6e,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x2c,0x20, +0x6f,0x6e,0x45,0x6e,0x64,0x2c,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f,0x72,0x29,0x20, +0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e, +0x42,0x65,0x67,0x69,0x6e,0x28,0x66,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x2c, +0x20,0x66,0x69,0x6c,0x65,0x2e,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x66,0x69,0x6c, +0x65,0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x6d,0x65,0x74, +0x61,0x64,0x61,0x74,0x61,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x74,0x72,0x79,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65, +0x2e,0x6c,0x6f,0x67,0x28,0x22,0xd0,0xa7,0xd0,0xb8,0xd1,0x82,0xd0,0xb0,0xd0,0xb5, +0xd0,0xbc,0x20,0xd1,0x84,0xd0,0xb0,0xd0,0xb9,0xd0,0xbb,0x20,0xd0,0xbf,0xd1,0x80, +0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2,0xd0,0xba,0xd0,0xb8,0x22,0x29,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, +0x69,0x6c,0x65,0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x20,0x3d,0x20, +0x61,0x77,0x61,0x69,0x74,0x20,0x74,0x68,0x69,0x73,0x2e,0x72,0x65,0x61,0x64,0x46, +0x69,0x6c,0x65,0x28,0x66,0x69,0x6c,0x65,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73, +0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0xd0,0x9f,0xd0,0xb0,0xd1,0x80,0xd1, +0x81,0xd0,0xb8,0xd0,0xbc,0x20,0xd0,0xbc,0xd0,0xb5,0xd1,0x82,0xd0,0xb0,0xd0,0xb4, +0xd0,0xb0,0xd0,0xbd,0xd0,0xbd,0xd1,0x8b,0xd0,0xb5,0x22,0x29,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6d,0x65,0x74, +0x61,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x74,0x68,0x69,0x73,0x2e,0x70,0x61,0x72, +0x73,0x65,0x4d,0x65,0x74,0x61,0x44,0x61,0x74,0x61,0x28,0x66,0x69,0x6c,0x65,0x55, +0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73, +0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61, +0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20, +0x63,0x61,0x74,0x63,0x68,0x20,0x28,0x65,0x72,0x72,0x6f,0x72,0x29,0x20,0x7b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x65,0x72,0x72,0x6f, +0x72,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f,0x72,0x28,0x60,0xd0,0xbd,0xd0,0xb5, +0xd1,0x83,0xd0,0xb4,0xd0,0xb0,0xd0,0xbb,0xd0,0xbe,0xd1,0x81,0xd1,0x8c,0x20,0xd0, +0xbf,0xd1,0x80,0xd0,0xbe,0xd1,0x87,0xd0,0xb5,0xd1,0x81,0xd1,0x82,0xd1,0x8c,0x20, +0xd1,0x84,0xd0,0xb0,0xd0,0xb9,0xd0,0xbb,0x60,0x29,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72, +0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x72, +0x79,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28, +0x60,0xd0,0x9a,0xd0,0xb8,0xd0,0xb4,0xd0,0xb0,0xd0,0xb5,0xd0,0xbc,0x20,0xd0,0xb7, +0xd0,0xb0,0xd0,0xbf,0xd1,0x80,0xd0,0xbe,0xd1,0x81,0x20,0xd0,0xbd,0xd0,0xb0,0x20, +0xd0,0xbd,0xd0,0xb0,0xd1,0x87,0xd0,0xb0,0xd0,0xbb,0xd0,0xbe,0x20,0xd0,0xbf,0xd1, +0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2,0xd0,0xba,0xd0,0xb8,0x60,0x29,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2f,0x2f,0x20,0x61,0x77,0x61,0x69,0x74,0x20,0x73,0x6c,0x65,0x65,0x70,0x28,0x31, +0x30,0x30,0x30,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x61,0x77,0x61,0x69,0x74,0x20,0x74,0x68,0x69,0x73,0x2e, +0x70,0x72,0x65,0x70,0x61,0x72,0x65,0x28,0x75,0x72,0x6c,0x50,0x72,0x65,0x66,0x69, +0x78,0x2c,0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x2e,0x63,0x72,0x63,0x2c, +0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x2e,0x73,0x69,0x7a,0x65,0x2c,0x20, +0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x2e,0x64,0x65,0x73,0x63,0x72,0x69,0x70, +0x74,0x69,0x6f,0x6e,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x20,0x63,0x61,0x74,0x63,0x68,0x20,0x28,0x65,0x72,0x72,0x6f, +0x72,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67, +0x28,0x65,0x72,0x72,0x6f,0x72,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f,0x72,0x28, +0x60,0xd0,0xbd,0xd0,0xb5,0xd1,0x83,0xd0,0xb4,0xd0,0xb0,0xd0,0xbb,0xd0,0xbe,0xd1, +0x81,0xd1,0x8c,0x20,0xd0,0xb8,0xd0,0xbd,0xd0,0xb8,0xd1,0x86,0xd0,0xb8,0xd0,0xb8, +0xd1,0x80,0xd0,0xbe,0xd0,0xb2,0xd0,0xb0,0xd1,0x82,0xd1,0x8c,0x20,0xd0,0xbf,0xd1, +0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2,0xd0,0xba,0xd1,0x83,0x60,0x29,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22, +0xd0,0x9d,0xd0,0xb0,0xd1,0x87,0xd0,0xb8,0xd0,0xbd,0xd0,0xb0,0xd0,0xb5,0xd0,0xbc, +0x20,0xd0,0xbe,0xd1,0x82,0xd0,0xbf,0xd1,0x80,0xd0,0xb0,0xd0,0xb2,0xd0,0xba,0xd1, +0x83,0x20,0xd0,0xbf,0xd0,0xbe,0xd1,0x80,0xd1,0x86,0xd0,0xb8,0xd1,0x8f,0xd0,0xbc, +0xd0,0xb8,0x22,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a, +0x65,0x20,0x3d,0x20,0x31,0x30,0x32,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x66,0x75,0x6c,0x6c,0x5f,0x73, +0x69,0x7a,0x65,0x20,0x3d,0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x2e,0x73, +0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x73,0x5f,0x73,0x69, +0x7a,0x65,0x20,0x3d,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a, +0x65,0x20,0x2a,0x20,0x28,0x66,0x75,0x6c,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x20,0x2f, +0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, +0x74,0x61,0x69,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x66,0x75,0x6c,0x6c, +0x5f,0x73,0x69,0x7a,0x65,0x20,0x2d,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f, +0x73,0x69,0x7a,0x65,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20, +0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74, +0x72,0x79,0x20,0x7b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x3b,0x20,0x6f,0x66,0x66, +0x73,0x65,0x74,0x20,0x3c,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x73,0x5f,0x73, +0x69,0x7a,0x65,0x3b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x70, +0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x7b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x6f,0x6e,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x28,0x6f,0x66, +0x66,0x73,0x65,0x74,0x2c,0x20,0x66,0x69,0x6c,0x65,0x55,0x69,0x6e,0x74,0x38,0x41, +0x72,0x72,0x61,0x79,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x6c,0x65,0x74,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, +0x66,0x69,0x6c,0x65,0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x73, +0x6c,0x69,0x63,0x65,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x66,0x66, +0x73,0x65,0x74,0x20,0x2b,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69, +0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x77,0x61,0x69,0x74,0x20,0x74, +0x68,0x69,0x73,0x2e,0x73,0x65,0x6e,0x64,0x50,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x28, +0x75,0x72,0x6c,0x50,0x72,0x65,0x66,0x69,0x78,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65, +0x74,0x2c,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x69,0x66,0x20,0x28,0x74,0x61,0x69,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x28, +0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x66,0x69,0x6c,0x65,0x55,0x69,0x6e,0x74, +0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x20, +0x3d,0x20,0x66,0x69,0x6c,0x65,0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79, +0x2e,0x73,0x6c,0x69,0x63,0x65,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f, +0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x74,0x61,0x69,0x6c,0x5f,0x73,0x69,0x7a, +0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x77,0x61,0x69,0x74,0x20,0x74,0x68, +0x69,0x73,0x2e,0x73,0x65,0x6e,0x64,0x50,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x28,0x75, +0x72,0x6c,0x50,0x72,0x65,0x66,0x69,0x78,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74, +0x2c,0x20,0x70,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x63,0x61,0x74, +0x63,0x68,0x20,0x28,0x65,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65, +0x2e,0x6c,0x6f,0x67,0x28,0x65,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f,0x72,0x28, +0x60,0xd0,0xbd,0x65,0x20,0xd1,0x83,0xd0,0xb4,0xd0,0xb0,0xd0,0xbb,0xd0,0xbe,0xd1, +0x81,0xd1,0x8c,0x20,0xd0,0xb7,0xd0,0xb0,0xd0,0xbf,0xd0,0xb8,0xd1,0x81,0xd0,0xb0, +0xd1,0x82,0xd1,0x8c,0x20,0xd0,0xb4,0xd0,0xb0,0xd0,0xbd,0xd0,0xbd,0xd1,0x8b,0xd0, +0xb5,0x20,0xd0,0xbf,0xd0,0xbe,0x20,0xd1,0x81,0xd0,0xbc,0xd0,0xb5,0xd1,0x89,0xd0, +0xb5,0xd0,0xbd,0xd0,0xb8,0xd1,0x8e,0x20,0x24,0x7b,0x6f,0x66,0x66,0x73,0x65,0x74, +0x7d,0x60,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f, +0x6e,0x45,0x6e,0x64,0x28,0x66,0x69,0x6c,0x65,0x55,0x69,0x6e,0x74,0x38,0x41,0x72, +0x72,0x61,0x79,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x29,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e, +0x6c,0x6f,0x67,0x28,0x22,0xd0,0xbf,0xd1,0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0, +0xb2,0xd0,0xba,0xd0,0xb0,0x20,0xd1,0x83,0xd1,0x81,0xd0,0xbf,0xd0,0xb5,0xd1,0x88, +0xd0,0xbd,0xd0,0xbe,0x20,0xd0,0xbe,0xd1,0x82,0xd0,0xbf,0xd1,0x80,0xd0,0xb0,0xd0, +0xb2,0xd0,0xbb,0xd0,0xb5,0xd0,0xbd,0xd0,0xb0,0x22,0x29,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x70,0x61,0x72,0x73,0x65,0x4d,0x65,0x74,0x61, +0x44,0x61,0x74,0x61,0x28,0x75,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x29, +0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c, +0x65,0x74,0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x42,0x6c,0x6f,0x63,0x6b, +0x20,0x3d,0x20,0x75,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x73,0x6c, +0x69,0x63,0x65,0x28,0x75,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x6c, +0x65,0x6e,0x67,0x74,0x68,0x20,0x2d,0x20,0x32,0x35,0x36,0x2c,0x20,0x75,0x69,0x6e, +0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x29,0x3b, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65, +0x74,0x20,0x63,0x72,0x63,0x20,0x3d,0x20,0x28,0x6e,0x65,0x77,0x20,0x55,0x69,0x6e, +0x74,0x33,0x32,0x41,0x72,0x72,0x61,0x79,0x28,0x6e,0x65,0x77,0x20,0x55,0x69,0x6e, +0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x28,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61, +0x42,0x6c,0x6f,0x63,0x6b,0x2e,0x73,0x6c,0x69,0x63,0x65,0x28,0x30,0x2c,0x20,0x34, +0x29,0x29,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x29,0x5b,0x30,0x5d,0x29,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x73, +0x69,0x7a,0x65,0x20,0x3d,0x20,0x28,0x6e,0x65,0x77,0x20,0x55,0x69,0x6e,0x74,0x33, +0x32,0x41,0x72,0x72,0x61,0x79,0x28,0x6e,0x65,0x77,0x20,0x55,0x69,0x6e,0x74,0x38, +0x41,0x72,0x72,0x61,0x79,0x28,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x42,0x6c, +0x6f,0x63,0x6b,0x2e,0x73,0x6c,0x69,0x63,0x65,0x28,0x34,0x2c,0x20,0x38,0x29,0x29, +0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x29,0x5b,0x30,0x5d,0x29,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x64,0x65, +0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d, +0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x42,0x6c,0x6f,0x63,0x6b,0x5b,0x38, +0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c, +0x65,0x74,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x5f,0x64, +0x61,0x74,0x61,0x20,0x3d,0x20,0x6d,0x65,0x74,0x61,0x64,0x61,0x74,0x61,0x42,0x6c, +0x6f,0x63,0x6b,0x2e,0x73,0x6c,0x69,0x63,0x65,0x28,0x39,0x2c,0x20,0x39,0x20,0x2b, +0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x5f,0x73,0x69,0x7a, +0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6c,0x65,0x74,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x20, +0x3d,0x20,0x53,0x74,0x72,0x69,0x6e,0x67,0x2e,0x66,0x72,0x6f,0x6d,0x43,0x68,0x61, +0x72,0x43,0x6f,0x64,0x65,0x2e,0x61,0x70,0x70,0x6c,0x79,0x28,0x6e,0x75,0x6c,0x6c, +0x2c,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x5f,0x64,0x61, +0x74,0x61,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x69,0x66,0x20,0x28,0x73,0x69,0x7a,0x65,0x20,0x3e,0x20,0x28,0x75,0x69, +0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x2e,0x6c,0x65,0x6e,0x67,0x74,0x68,0x20, +0x2d,0x20,0x32,0x35,0x36,0x29,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x68,0x72,0x6f,0x77,0x20, +0x22,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb1,0xd0,0xbe,0xd1,0x87,0xd0,0xbd,0xd1, +0x8b,0xd0,0xb9,0x20,0xd1,0x80,0xd0,0xb0,0xd0,0xb7,0xd0,0xbc,0xd0,0xb5,0xd1,0x80, +0x20,0xd1,0x84,0xd0,0xb0,0xd0,0xb9,0xd0,0xbb,0xd0,0xb0,0x22,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x63,0x72,0x63,0x3a,0x20,0x63,0x72,0x63,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x69,0x7a,0x65,0x3a, +0x20,0x73,0x69,0x7a,0x65,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69, +0x6f,0x6e,0x3a,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x72,0x65, +0x61,0x64,0x46,0x69,0x6c,0x65,0x28,0x66,0x69,0x6c,0x65,0x29,0x20,0x7b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f, +0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0x72,0x65,0x61,0x64,0x20,0x66,0x69,0x6c, +0x65,0x20,0x22,0x20,0x2b,0x20,0x66,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x29, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74, +0x75,0x72,0x6e,0x20,0x6e,0x65,0x77,0x20,0x50,0x72,0x6f,0x6d,0x69,0x73,0x65,0x28, +0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x72,0x65,0x73,0x6f,0x6c,0x76,0x65,0x2c,0x20, +0x72,0x65,0x6a,0x65,0x63,0x74,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e, +0x73,0x74,0x20,0x72,0x65,0x61,0x64,0x65,0x72,0x20,0x3d,0x20,0x6e,0x65,0x77,0x20, +0x46,0x69,0x6c,0x65,0x52,0x65,0x61,0x64,0x65,0x72,0x28,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65, +0x61,0x64,0x65,0x72,0x2e,0x6f,0x6e,0x6c,0x6f,0x61,0x64,0x20,0x3d,0x20,0x66,0x75, +0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x20, +0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x6f,0x6c,0x76,0x65,0x28,0x6e,0x65, +0x77,0x20,0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x28,0x72,0x65,0x73, +0x75,0x6c,0x74,0x2e,0x74,0x61,0x72,0x67,0x65,0x74,0x2e,0x72,0x65,0x73,0x75,0x6c, +0x74,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x61,0x64,0x65,0x72,0x2e,0x6f,0x6e, +0x65,0x72,0x72,0x6f,0x72,0x20,0x3d,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x72,0x65,0x61,0x64,0x65,0x72,0x2e,0x72,0x65,0x61,0x64,0x41,0x73,0x41,0x72,0x72, +0x61,0x79,0x42,0x75,0x66,0x66,0x65,0x72,0x28,0x66,0x69,0x6c,0x65,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x29,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x73, +0x65,0x6e,0x64,0x50,0x6f,0x72,0x74,0x69,0x6f,0x6e,0x28,0x75,0x72,0x6c,0x50,0x72, +0x65,0x66,0x69,0x78,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x70,0x6f, +0x72,0x74,0x69,0x6f,0x6e,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6e,0x65,0x77,0x20, +0x50,0x72,0x6f,0x6d,0x69,0x73,0x65,0x28,0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x72, +0x65,0x73,0x6f,0x6c,0x76,0x65,0x2c,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x29,0x20, +0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x62,0x6c,0x6f,0x62,0x20, +0x3d,0x20,0x6e,0x65,0x77,0x20,0x42,0x6c,0x6f,0x62,0x28,0x5b,0x6e,0x65,0x77,0x20, +0x55,0x69,0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x28,0x70,0x6f,0x72,0x74,0x69, +0x6f,0x6e,0x29,0x5d,0x2c,0x20,0x7b,0x74,0x79,0x70,0x65,0x3a,0x20,0x22,0x61,0x70, +0x70,0x6c,0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x2f,0x6f,0x63,0x74,0x65,0x74,0x2d, +0x73,0x74,0x72,0x65,0x61,0x6d,0x22,0x7d,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, +0x75,0x72,0x6c,0x20,0x3d,0x20,0x60,0x24,0x7b,0x75,0x72,0x6c,0x50,0x72,0x65,0x66, +0x69,0x78,0x7d,0x2f,0x53,0x6f,0x66,0x74,0x77,0x61,0x72,0x65,0x5f,0x57,0x72,0x69, +0x74,0x65,0x5f,0x46,0x69,0x6c,0x65,0x3f,0x6f,0x66,0x66,0x73,0x65,0x74,0x3d,0x24, +0x7b,0x6f,0x66,0x66,0x73,0x65,0x74,0x7d,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20, +0x78,0x68,0x72,0x20,0x3d,0x20,0x6e,0x65,0x77,0x20,0x58,0x4d,0x4c,0x48,0x74,0x74, +0x70,0x52,0x65,0x71,0x75,0x65,0x73,0x74,0x28,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72, +0x2e,0x6f,0x70,0x65,0x6e,0x28,0x22,0x50,0x4f,0x53,0x54,0x22,0x2c,0x20,0x75,0x72, +0x6c,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x6f,0x6e,0x65,0x72,0x72,0x6f,0x72,0x20, +0x3d,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x6f,0x6e, +0x6c,0x6f,0x61,0x64,0x20,0x3d,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x65,0x29, +0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x78,0x68, +0x72,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x20,0x3d,0x3d,0x3d,0x20,0x32,0x30,0x30, +0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x6f, +0x6c,0x76,0x65,0x28,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c, +0x73,0x65,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x6a, +0x65,0x63,0x74,0x28,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x78,0x68,0x72,0x2e,0x73,0x65,0x6e,0x64,0x28,0x62,0x6c,0x6f,0x62,0x29,0x3b, +0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x29, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x61,0x73,0x79,0x6e, +0x63,0x20,0x70,0x72,0x65,0x70,0x61,0x72,0x65,0x28,0x75,0x72,0x6c,0x50,0x72,0x65, +0x66,0x69,0x78,0x2c,0x20,0x63,0x72,0x63,0x2c,0x20,0x73,0x69,0x7a,0x65,0x2c,0x20, +0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x29,0x20,0x7b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f, +0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0x70,0x72,0x65,0x70,0x61,0x72,0x65,0x22, +0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72, +0x65,0x74,0x75,0x72,0x6e,0x20,0x6e,0x65,0x77,0x20,0x50,0x72,0x6f,0x6d,0x69,0x73, +0x65,0x28,0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x72,0x65,0x73,0x6f,0x6c,0x76,0x65, +0x2c,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6c,0x65,0x74,0x20,0x75,0x72,0x6c,0x20,0x3d,0x20,0x60,0x24,0x7b,0x75,0x72,0x6c, +0x50,0x72,0x65,0x66,0x69,0x78,0x7d,0x2f,0x53,0x6f,0x66,0x74,0x77,0x61,0x72,0x65, +0x5f,0x49,0x6e,0x69,0x74,0x5f,0x46,0x69,0x6c,0x65,0x3f,0x63,0x72,0x63,0x3d,0x24, +0x7b,0x63,0x72,0x63,0x7d,0x26,0x73,0x69,0x7a,0x65,0x3d,0x24,0x7b,0x73,0x69,0x7a, +0x65,0x7d,0x26,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x3d,0x22, +0x24,0x7b,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x7d,0x22,0x60, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x78,0x68,0x72,0x20,0x3d,0x20,0x6e,0x65,0x77, +0x20,0x58,0x4d,0x4c,0x48,0x74,0x74,0x70,0x52,0x65,0x71,0x75,0x65,0x73,0x74,0x28, +0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x6f,0x70,0x65,0x6e,0x28,0x22,0x50,0x4f, +0x53,0x54,0x22,0x2c,0x20,0x75,0x72,0x6c,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x6f, +0x6e,0x65,0x72,0x72,0x6f,0x72,0x20,0x3d,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x78,0x68,0x72,0x2e,0x6f,0x6e,0x6c,0x6f,0x61,0x64,0x20,0x3d,0x20,0x61,0x73, +0x79,0x6e,0x63,0x20,0x28,0x65,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x69,0x66,0x20,0x28,0x78,0x68,0x72,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x20, +0x3d,0x3d,0x3d,0x20,0x32,0x30,0x30,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x72,0x65,0x73,0x6f,0x6c,0x76,0x65,0x28,0x65,0x29,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x28,0x65,0x29,0x3b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x73,0x65,0x6e,0x64, +0x28,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x7d,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65, +0x6e,0x74,0x2e,0x71,0x75,0x65,0x72,0x79,0x53,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72, +0x28,0x27,0x62,0x75,0x74,0x74,0x6f,0x6e,0x27,0x29,0x2e,0x61,0x64,0x64,0x45,0x76, +0x65,0x6e,0x74,0x4c,0x69,0x73,0x74,0x65,0x6e,0x65,0x72,0x28,0x27,0x63,0x6c,0x69, +0x63,0x6b,0x27,0x2c,0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x28,0x29, +0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x66, +0x69,0x6c,0x65,0x20,0x3d,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67, +0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x27,0x70, +0x61,0x63,0x6b,0x61,0x67,0x65,0x2d,0x75,0x70,0x64,0x27,0x29,0x2e,0x66,0x69,0x6c, +0x65,0x73,0x5b,0x30,0x5d,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6c,0x65,0x74,0x20,0x6f,0x6e,0x42,0x65,0x67,0x69,0x6e,0x20,0x3d,0x20,0x28,0x29, +0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c, +0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x61,0x76,0x65,0x2d, +0x62,0x75,0x74,0x74,0x6f,0x6e,0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x76, +0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x20,0x3d,0x20,0x60,0x68,0x69,0x64, +0x64,0x65,0x6e,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65, +0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x6c,0x6f,0x61,0x64,0x69,0x6e, +0x67,0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x76,0x69,0x73,0x69,0x62,0x69, +0x6c,0x69,0x74,0x79,0x20,0x3d,0x20,0x60,0x69,0x6e,0x68,0x65,0x72,0x69,0x74,0x60, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63, +0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74, +0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x62,0x61,0x72, +0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f, +0x75,0x6e,0x64,0x43,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x60,0x23,0x32,0x34,0x63, +0x33,0x38,0x65,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65, +0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73, +0x5f,0x74,0x65,0x78,0x74,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x54,0x65,0x78, +0x74,0x20,0x3d,0x20,0x22,0xd0,0x9d,0xd0,0xb0,0xd1,0x87,0xd0,0xb0,0xd0,0xbb,0xd0, +0xbe,0x20,0xd0,0xb7,0xd0,0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb7,0xd0,0xba, +0xd0,0xb8,0x22,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0xd0,0x9d,0xd0, +0xb0,0xd1,0x87,0xd0,0xb0,0xd0,0xbb,0xd0,0xbe,0x20,0xd0,0xb7,0xd0,0xb0,0xd0,0xb3, +0xd1,0x80,0xd1,0x83,0xd0,0xb7,0xd0,0xba,0xd0,0xb8,0x22,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x6f,0x6e,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x20, +0x3d,0x20,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x73,0x69,0x7a,0x65,0x29, +0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c, +0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75, +0x73,0x5f,0x62,0x61,0x72,0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x77,0x69, +0x64,0x74,0x68,0x20,0x3d,0x20,0x60,0x24,0x7b,0x31,0x30,0x30,0x20,0x2a,0x20,0x6f, +0x66,0x66,0x73,0x65,0x74,0x20,0x2f,0x20,0x73,0x69,0x7a,0x65,0x7d,0x25,0x60,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75, +0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42, +0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x74,0x65,0x78,0x74, +0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x54,0x65,0x78,0x74,0x20,0x3d,0x20,0x60, +0xd0,0x97,0xd0,0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb6,0xd0,0xb5,0xd0,0xbd, +0xd0,0xbe,0x20,0x24,0x7b,0x6f,0x66,0x66,0x73,0x65,0x74,0x7d,0x20,0xd0,0xb8,0xd0, +0xb7,0x20,0x24,0x7b,0x73,0x69,0x7a,0x65,0x7d,0x20,0xd0,0xb1,0xd0,0xb0,0xd0,0xb9, +0xd1,0x82,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x60,0xd0,0x97,0xd0, +0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb6,0xd0,0xb5,0xd0,0xbd,0xd0,0xbe,0x20, +0x24,0x7b,0x6f,0x66,0x66,0x73,0x65,0x74,0x7d,0x20,0xd0,0xb8,0xd0,0xb7,0x20,0x24, +0x7b,0x73,0x69,0x7a,0x65,0x7d,0x60,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74, +0x20,0x6f,0x6e,0x45,0x6e,0x64,0x20,0x3d,0x20,0x28,0x74,0x6f,0x74,0x61,0x6c,0x53, +0x69,0x7a,0x65,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67, +0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73, +0x61,0x76,0x65,0x2d,0x62,0x75,0x74,0x74,0x6f,0x6e,0x22,0x29,0x2e,0x73,0x74,0x79, +0x6c,0x65,0x2e,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x20,0x3d,0x20, +0x60,0x69,0x6e,0x68,0x65,0x72,0x69,0x74,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67, +0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x6c, +0x6f,0x61,0x64,0x69,0x6e,0x67,0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x76, +0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x20,0x3d,0x20,0x60,0x68,0x69,0x64, +0x64,0x65,0x6e,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65, +0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73, +0x5f,0x62,0x61,0x72,0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x77,0x69,0x64, +0x74,0x68,0x20,0x3d,0x20,0x60,0x31,0x30,0x30,0x25,0x20,0x28,0x24,0x7b,0x74,0x6f, +0x74,0x61,0x6c,0x53,0x69,0x7a,0x65,0x7d,0xd0,0xb1,0xd0,0xb0,0xd0,0xb9,0xd1,0x82, +0x29,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64, +0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65, +0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x74, +0x65,0x78,0x74,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x54,0x65,0x78,0x74,0x20, +0x3d,0x20,0x60,0xd0,0x9f,0xd1,0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2,0xd0, +0xba,0xd0,0xb0,0x20,0x24,0x7b,0x66,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x7d, +0x20,0xd1,0x83,0xd1,0x81,0xd0,0xbf,0xd0,0xb5,0xd1,0x88,0xd0,0xbd,0xd0,0xbe,0x20, +0xd0,0xb7,0xd0,0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb6,0xd0,0xb5,0xd0,0xbd, +0xd0,0xb0,0x2e,0x5c,0x6e,0xd0,0x9f,0xd0,0xb5,0xd1,0x80,0xd0,0xb5,0xd0,0xb7,0xd0, +0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb7,0xd0,0xb8,0xd1,0x82,0xd0,0xb5,0x20, +0xd1,0x83,0xd1,0x81,0xd1,0x82,0xd1,0x80,0xd0,0xbe,0xd0,0xb9,0xd1,0x81,0xd1,0x82, +0xd0,0xb2,0xd0,0xbe,0x21,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x60, +0xd0,0x9f,0xd1,0x80,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb2,0xd0,0xba,0xd0,0xb0, +0x20,0x24,0x7b,0x66,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x7d,0x20,0xd1,0x83, +0xd1,0x81,0xd0,0xbf,0xd0,0xb5,0xd1,0x88,0xd0,0xbd,0xd0,0xbe,0x20,0xd0,0xb7,0xd0, +0xb0,0xd0,0xb3,0xd1,0x80,0xd1,0x83,0xd0,0xb6,0xd0,0xb5,0xd0,0xbd,0xd0,0xb0,0x60, +0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f, +0x72,0x20,0x3d,0x20,0x28,0x65,0x72,0x72,0x6f,0x72,0x29,0x20,0x3d,0x3e,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x20, +0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d, +0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x6c,0x6f,0x61,0x64,0x69,0x6e,0x67, +0x22,0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x76,0x69,0x73,0x69,0x62,0x69,0x6c, +0x69,0x74,0x79,0x20,0x3d,0x20,0x60,0x68,0x69,0x64,0x64,0x65,0x6e,0x60,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d, +0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79, +0x49,0x64,0x28,0x22,0x73,0x61,0x76,0x65,0x2d,0x62,0x75,0x74,0x74,0x6f,0x6e,0x22, +0x29,0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69, +0x74,0x79,0x20,0x3d,0x20,0x60,0x69,0x6e,0x68,0x65,0x72,0x69,0x74,0x60,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d, +0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79, +0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x62,0x61,0x72,0x22,0x29, +0x2e,0x73,0x74,0x79,0x6c,0x65,0x2e,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e, +0x64,0x43,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x60,0x72,0x65,0x64,0x60,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x63,0x75,0x6d, +0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79, +0x49,0x64,0x28,0x22,0x73,0x74,0x61,0x74,0x75,0x73,0x5f,0x74,0x65,0x78,0x74,0x22, +0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x54,0x65,0x78,0x74,0x20,0x3d,0x20,0x60,0xd0, +0x92,0xd0,0xbe,0xd0,0xb7,0xd0,0xbd,0xd0,0xb8,0xd0,0xba,0xd0,0xbb,0xd0,0xb0,0x20, +0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb1,0xd0,0xba,0xd0,0xb0,0x20,0x24,0x7b,0x65, +0x72,0x72,0x6f,0x72,0x7d,0x60,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x60, +0xd0,0x92,0xd0,0xbe,0xd0,0xb7,0xd0,0xbd,0xd0,0xb8,0xd0,0xba,0xd0,0xbb,0xd0,0xb0, +0x20,0xd0,0xbe,0xd1,0x88,0xd0,0xb8,0xd0,0xb1,0xd0,0xba,0xd0,0xb0,0x3a,0x20,0x24, +0x7b,0x65,0x72,0x72,0x6f,0x72,0x7d,0x60,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x69, +0x72,0x6d,0x77,0x61,0x72,0x65,0x55,0x70,0x64,0x61,0x74,0x65,0x72,0x2e,0x6c,0x6f, +0x61,0x64,0x46,0x69,0x72,0x6d,0x77,0x61,0x72,0x65,0x28,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x69,0x6c,0x65,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x42,0x65,0x67,0x69, +0x6e,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f, +0x6e,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x45,0x6e,0x64,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x45,0x72,0x72,0x6f, +0x72,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x7d,0x29,0x0a,0x3c,0x2f,0x73,0x63,0x72,0x69,0x70,0x74,0x3e,0x0a,0x0a,0x0a, +0x3c,0x73,0x63,0x72,0x69,0x70,0x74,0x20,0x74,0x79,0x70,0x65,0x3d,0x22,0x6d,0x6f, +0x64,0x75,0x6c,0x65,0x22,0x3e,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74, +0x20,0x69,0x64,0x46,0x69,0x6c,0x65,0x54,0x6f,0x55,0x72,0x6c,0x20,0x3d,0x20,0x28, +0x69,0x64,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x63,0x75,0x72,0x49,0x64,0x20,0x3d,0x20,0x69,0x64,0x20, +0x3f,0x20,0x60,0x24,0x7b,0x69,0x64,0x7d,0x60,0x20,0x3a,0x20,0x22,0x22,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6b,0x65,0x79, +0x66,0x69,0x6c,0x65,0x3a,0x20,0x28,0x6e,0x61,0x6d,0x65,0x20,0x3d,0x20,0x22,0x22, +0x29,0x20,0x3d,0x3e,0x20,0x22,0x2f,0x4b,0x65,0x79,0x5f,0x46,0x69,0x6c,0x65,0x3f, +0x66,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x3d,0x22,0x20,0x2b,0x20,0x6e,0x61,0x6d, +0x65,0x20,0x2b,0x20,0x22,0x26,0x69,0x64,0x3d,0x22,0x20,0x2b,0x20,0x63,0x75,0x72, +0x49,0x64,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x73,0x6f,0x66,0x74,0x57,0x61,0x72,0x65,0x3a,0x20,0x28,0x73,0x69,0x7a,0x65,0x2c, +0x20,0x63,0x72,0x63,0x2c,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f, +0x6e,0x29,0x20,0x3d,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x2f,0x53,0x6f,0x66,0x74,0x77,0x61,0x72,0x65, +0x5f,0x46,0x69,0x6c,0x65,0x3f,0x73,0x69,0x7a,0x65,0x3d,0x22,0x20,0x2b,0x20,0x73, +0x69,0x7a,0x65,0x20,0x2b,0x20,0x22,0x26,0x63,0x72,0x63,0x3d,0x22,0x20,0x2b,0x20, +0x63,0x72,0x63,0x20,0x2b,0x20,0x22,0x26,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74, +0x69,0x6f,0x6e,0x3d,0x22,0x20,0x2b,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74, +0x69,0x6f,0x6e,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x7d,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x63,0x6c,0x61,0x73, +0x73,0x20,0x53,0x65,0x6e,0x64,0x44,0x61,0x74,0x61,0x20,0x7b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x75,0x72,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x66,0x69,0x6c,0x65,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x66,0x69,0x6c,0x65,0x73,0x50,0x75,0x74,0x68,0x20,0x3d,0x20,0x69,0x64,0x46, +0x69,0x6c,0x65,0x54,0x6f,0x55,0x72,0x6c,0x28,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x72,0x75,0x63,0x74,0x6f,0x72, +0x28,0x75,0x72,0x6c,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x74,0x68,0x69,0x73,0x2e,0x75,0x72,0x6c,0x20,0x3d,0x20,0x75, +0x72,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x73,0x75,0x63, +0x63,0x65,0x73,0x73,0x28,0x78,0x68,0x72,0x2c,0x20,0x65,0x29,0x20,0x7b,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f, +0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x73, +0x75,0x63,0x63,0x65,0x73,0x73,0x3a,0x20,0x22,0x2c,0x20,0x78,0x68,0x72,0x2e,0x73, +0x74,0x61,0x74,0x75,0x73,0x54,0x65,0x78,0x74,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e, +0x6c,0x6f,0x67,0x28,0x22,0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x66,0x75,0x6c,0x6c, +0x20,0x72,0x65,0x73,0x70,0x6f,0x6e,0x73,0x65,0x3a,0x20,0x22,0x2c,0x20,0x78,0x68, +0x72,0x2c,0x20,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x68,0x61,0x6e,0x67,0x65,0x43,0x6f,0x6e,0x74,0x28,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22, +0x77,0x61,0x69,0x74,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x6c,0x6f,0x61,0x64,0x22,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, +0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x65,0x72,0x72,0x6f, +0x72,0x28,0x78,0x68,0x72,0x2c,0x20,0x65,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e, +0x6c,0x6f,0x67,0x28,0x22,0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x65,0x72,0x72,0x6f, +0x72,0x3a,0x20,0x22,0x2c,0x20,0x78,0x68,0x72,0x2e,0x73,0x74,0x61,0x74,0x75,0x73, +0x54,0x65,0x78,0x74,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22, +0x53,0x65,0x72,0x76,0x65,0x72,0x20,0x66,0x75,0x6c,0x6c,0x20,0x72,0x65,0x73,0x70, +0x6f,0x6e,0x73,0x65,0x3a,0x20,0x22,0x2c,0x20,0x78,0x68,0x72,0x2c,0x20,0x65,0x29, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6c, +0x6f,0x73,0x65,0x4c,0x6f,0x61,0x64,0x43,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72, +0x28,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x27,0x70,0x61,0x63,0x6b,0x61,0x67,0x65,0x2d,0x75,0x70,0x64,0x27,0x2c, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x27,0x6c,0x61,0x62,0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64, +0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f, +0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69,0x74,0x6c,0x65,0x2d,0x75,0x70,0x64, +0x61,0x74,0x65,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65, +0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x63,0x68,0x61,0x6e,0x67,0x65,0x54,0x65,0x78,0x74,0x28,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x22,0x74,0x72,0x79,0x2d,0x61,0x67,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74,0x22, +0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x22,0x6d,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74,0x22,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, +0x20,0x30,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x73,0x65,0x6e,0x64, +0x46,0x69,0x6c,0x65,0x73,0x28,0x73,0x75,0x63,0x20,0x3d,0x20,0x74,0x68,0x69,0x73, +0x2e,0x73,0x75,0x63,0x63,0x65,0x73,0x73,0x2c,0x20,0x65,0x72,0x72,0x20,0x3d,0x20, +0x74,0x68,0x69,0x73,0x2e,0x65,0x72,0x72,0x6f,0x72,0x2c,0x20,0x6e,0x65,0x65,0x64, +0x55,0x72,0x6c,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22, +0x66,0x66,0x66,0x72,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6e,0x65,0x77,0x20,0x50, +0x72,0x6f,0x6d,0x69,0x73,0x65,0x28,0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x72,0x65, +0x73,0x6f,0x6c,0x76,0x65,0x2c,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x29,0x20,0x3d, +0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c, +0x65,0x73,0x20,0x26,0x26,0x20,0x4f,0x62,0x6a,0x65,0x63,0x74,0x2e,0x6b,0x65,0x79, +0x73,0x28,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x29,0x2e,0x6c,0x65, +0x6e,0x67,0x74,0x68,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, +0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x20,0x3d,0x20,0x4f,0x62, +0x6a,0x65,0x63,0x74,0x2e,0x6b,0x65,0x79,0x73,0x28,0x74,0x68,0x69,0x73,0x2e,0x66, +0x69,0x6c,0x65,0x73,0x29,0x5b,0x30,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f, +0x6e,0x73,0x74,0x20,0x78,0x68,0x72,0x20,0x3d,0x20,0x6e,0x65,0x77,0x20,0x58,0x4d, +0x4c,0x48,0x74,0x74,0x70,0x52,0x65,0x71,0x75,0x65,0x73,0x74,0x28,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x6e,0x61,0x6d,0x65,0x20,0x3d,0x20,0x74, +0x68,0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x5b,0x63,0x75,0x72,0x46,0x69,0x6c, +0x65,0x4e,0x61,0x6d,0x65,0x5d,0x2e,0x6e,0x61,0x6d,0x65,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x31,0x35,0x2c, +0x20,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x2c,0x20,0x74,0x68, +0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x50,0x75,0x74,0x68,0x2c,0x20,0x74,0x68, +0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x50,0x75,0x74,0x68,0x5b,0x63,0x75,0x72, +0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x5d,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x6c,0x65,0x74,0x20,0x63,0x75,0x72,0x55,0x72,0x6c,0x20,0x3d,0x20,0x6e,0x65,0x65, +0x64,0x55,0x72,0x6c,0x20,0x7c,0x7c,0x20,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c, +0x65,0x73,0x50,0x75,0x74,0x68,0x5b,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61, +0x6d,0x65,0x5d,0x3f,0x2e,0x28,0x6e,0x61,0x6d,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x78,0x68,0x72,0x2e,0x6f,0x70,0x65,0x6e,0x28,0x74,0x68,0x69,0x73,0x2e,0x6d, +0x65,0x74,0x68,0x6f,0x64,0x2c,0x20,0x63,0x75,0x72,0x55,0x72,0x6c,0x29,0x3b,0x0a, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x6f,0x6e,0x6c,0x6f,0x61,0x64,0x20, +0x3d,0x20,0x61,0x73,0x79,0x6e,0x63,0x20,0x28,0x65,0x29,0x20,0x3d,0x3e,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x73,0x74,0x61, +0x74,0x75,0x73,0x20,0x3d,0x3d,0x20,0x32,0x30,0x30,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3f,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65, +0x2e,0x6c,0x6f,0x67,0x28,0x22,0x73,0x65,0x6e,0x64,0x20,0x66,0x69,0x6c,0x65,0x20, +0x20,0x73,0x75,0x63,0x22,0x20,0x2b,0x20,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e, +0x61,0x6d,0x65,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x3a,0x20,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22, +0x73,0x65,0x6e,0x64,0x20,0x66,0x69,0x6c,0x65,0x20,0x20,0x65,0x72,0x72,0x22,0x20, +0x2b,0x20,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x78,0x68,0x72,0x2e, +0x73,0x74,0x61,0x74,0x75,0x73,0x20,0x3d,0x3d,0x20,0x32,0x30,0x30,0x29,0x20,0x7b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x6c, +0x65,0x74,0x65,0x20,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x5b,0x63, +0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x5d,0x3b,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x77,0x61,0x69,0x74,0x20,0x74, +0x68,0x69,0x73,0x2e,0x73,0x65,0x6e,0x64,0x46,0x69,0x6c,0x65,0x73,0x28,0x61,0x77, +0x61,0x69,0x74,0x20,0x73,0x75,0x63,0x28,0x78,0x68,0x72,0x2c,0x20,0x65,0x29,0x29, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65, +0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x28,0x61,0x77,0x61,0x69,0x74,0x20,0x65,0x72, +0x72,0x28,0x78,0x68,0x72,0x2c,0x20,0x65,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x2f,0x2f,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x28,0x61,0x77, +0x61,0x69,0x74,0x20,0x65,0x72,0x72,0x28,0x78,0x68,0x72,0x2c,0x20,0x65,0x29,0x29, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f, +0x20,0x6c,0x65,0x74,0x20,0x66,0x6f,0x72,0x6d,0x44,0x61,0x74,0x61,0x20,0x3d,0x20, +0x6e,0x65,0x77,0x20,0x46,0x6f,0x72,0x6d,0x44,0x61,0x74,0x61,0x28,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x2f,0x2f,0x20,0x66,0x6f,0x72,0x6d,0x44,0x61,0x74,0x61,0x2e, +0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61, +0x6d,0x65,0x2c,0x20,0x74,0x68,0x69,0x73,0x2e,0x66,0x69,0x6c,0x65,0x73,0x5b,0x63, +0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x5d,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x2f,0x2f,0x20,0x78,0x68,0x72,0x2e,0x73,0x65,0x6e,0x64,0x28,0x66,0x6f, +0x72,0x6d,0x44,0x61,0x74,0x61,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65, +0x74,0x20,0x66,0x69,0x6c,0x65,0x20,0x3d,0x20,0x74,0x68,0x69,0x73,0x2e,0x66,0x69, +0x6c,0x65,0x73,0x5b,0x63,0x75,0x72,0x46,0x69,0x6c,0x65,0x4e,0x61,0x6d,0x65,0x5d, +0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x72,0x65,0x61, +0x64,0x65,0x72,0x20,0x3d,0x20,0x6e,0x65,0x77,0x20,0x46,0x69,0x6c,0x65,0x52,0x65, +0x61,0x64,0x65,0x72,0x28,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x61,0x64, +0x65,0x72,0x2e,0x6f,0x6e,0x6c,0x6f,0x61,0x64,0x20,0x3d,0x20,0x66,0x75,0x6e,0x63, +0x74,0x69,0x6f,0x6e,0x20,0x28,0x65,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x62,0x6c,0x6f,0x62,0x20,0x3d,0x20, +0x6e,0x65,0x77,0x20,0x42,0x6c,0x6f,0x62,0x28,0x5b,0x6e,0x65,0x77,0x20,0x55,0x69, +0x6e,0x74,0x38,0x41,0x72,0x72,0x61,0x79,0x28,0x65,0x2e,0x74,0x61,0x72,0x67,0x65, +0x74,0x2e,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x5d,0x2c,0x20,0x7b,0x74,0x79,0x70, +0x65,0x3a,0x20,0x66,0x69,0x6c,0x65,0x2e,0x74,0x79,0x70,0x65,0x7d,0x29,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x78,0x68,0x72,0x2e,0x73,0x65,0x6e,0x64, +0x28,0x62,0x6c,0x6f,0x62,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x3b,0x0a,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x72,0x65,0x61,0x64,0x65,0x72,0x2e,0x6f,0x6e,0x65,0x72,0x72, +0x6f,0x72,0x20,0x3d,0x20,0x72,0x65,0x6a,0x65,0x63,0x74,0x3b,0x0a,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x72,0x65,0x61,0x64,0x65,0x72,0x2e,0x72,0x65,0x61,0x64,0x41,0x73,0x41, +0x72,0x72,0x61,0x79,0x42,0x75,0x66,0x66,0x65,0x72,0x28,0x66,0x69,0x6c,0x65,0x29, +0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x72,0x65,0x73,0x6f,0x6c,0x76,0x65, +0x28,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x7d,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x7d,0x0a,0x0a,0x0a,0x20,0x20,0x20,0x20,0x61,0x73,0x79,0x6e,0x63, +0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x75,0x70,0x64,0x61,0x74,0x65, +0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x65,0x72,0x28,0x73,0x69,0x7a,0x65,0x2c,0x20, +0x63,0x72,0x63,0x2c,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e, +0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x61,0x72,0x20, +0x73,0x75,0x6d,0x62,0x69,0x74,0x53,0x75,0x63,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x73,0x61,0x76, +0x65,0x55,0x70,0x64,0x61,0x74,0x69,0x6e,0x67,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x69,0x6e, +0x70,0x75,0x74,0x20,0x3d,0x20,0x64,0x65,0x28,0x22,0x70,0x61,0x63,0x6b,0x61,0x67, +0x65,0x2d,0x75,0x70,0x64,0x22,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x66,0x69,0x6c,0x65,0x20,0x3d,0x20, +0x69,0x6e,0x70,0x75,0x74,0x2e,0x66,0x69,0x6c,0x65,0x73,0x5b,0x30,0x5d,0x3b,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, +0x75,0x72,0x6c,0x20,0x3d,0x20,0x22,0x2f,0x53,0x6f,0x66,0x74,0x77,0x61,0x72,0x65, +0x5f,0x46,0x69,0x6c,0x65,0x3f,0x73,0x69,0x7a,0x65,0x3d,0x22,0x20,0x2b,0x20,0x73, +0x69,0x7a,0x65,0x20,0x2b,0x20,0x22,0x26,0x63,0x72,0x63,0x3d,0x22,0x20,0x2b,0x20, +0x63,0x72,0x63,0x20,0x2b,0x20,0x22,0x26,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74, +0x69,0x6f,0x6e,0x3d,0x22,0x20,0x2b,0x20,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74, +0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x73,0x65,0x6e,0x64,0x65,0x72,0x20,0x3d,0x20,0x6e,0x65, +0x77,0x20,0x53,0x65,0x6e,0x64,0x44,0x61,0x74,0x61,0x28,0x75,0x72,0x6c,0x29,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6e, +0x64,0x65,0x72,0x2e,0x66,0x69,0x6c,0x65,0x73,0x20,0x3d,0x20,0x7b,0x73,0x6f,0x66, +0x74,0x57,0x61,0x72,0x65,0x3a,0x20,0x66,0x69,0x6c,0x65,0x7d,0x3b,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6e,0x64,0x65, +0x72,0x2e,0x6d,0x65,0x74,0x68,0x6f,0x64,0x20,0x3d,0x20,0x22,0x50,0x4f,0x53,0x54, +0x22,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63, +0x6f,0x6e,0x73,0x74,0x20,0x73,0x75,0x63,0x20,0x3d,0x20,0x28,0x29,0x20,0x3d,0x3e, +0x20,0x63,0x68,0x61,0x6e,0x67,0x65,0x43,0x6f,0x6e,0x74,0x28,0x0a,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x77,0x61, +0x69,0x74,0x22,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x22,0x6c,0x6f,0x61,0x64,0x22,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x65,0x72, +0x72,0x20,0x3d,0x20,0x28,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x68,0x61,0x6e, +0x67,0x65,0x54,0x65,0x78,0x74,0x28,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x74,0x72,0x79, +0x2d,0x61,0x67,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74,0x22,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x22,0x6d,0x61,0x69,0x6e,0x2d,0x74,0x65,0x78,0x74,0x22,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x29,0x3b, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x63,0x6c,0x6f,0x73,0x65,0x4c,0x6f,0x61,0x64,0x43,0x6f,0x6e,0x74,0x61,0x69, +0x6e,0x65,0x72,0x28,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x70,0x61,0x63,0x6b,0x61,0x67, +0x65,0x2d,0x75,0x70,0x64,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x6c,0x61,0x62, +0x65,0x6c,0x2d,0x66,0x69,0x6c,0x65,0x2d,0x75,0x70,0x64,0x27,0x2c,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x6c,0x6f,0x61,0x64,0x2d,0x63,0x6f,0x6e, +0x74,0x61,0x69,0x6e,0x65,0x72,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x27,0x66,0x69,0x6c,0x65,0x2d,0x74,0x69,0x74,0x6c,0x65,0x2d, +0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x2c,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x27,0x66,0x69, +0x6c,0x65,0x2d,0x73,0x69,0x7a,0x65,0x2d,0x75,0x70,0x64,0x61,0x74,0x65,0x27,0x0a, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, +0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6e, +0x64,0x65,0x72,0x2e,0x73,0x65,0x6e,0x64,0x46,0x69,0x6c,0x65,0x73,0x28,0x73,0x75, +0x63,0x2c,0x20,0x65,0x72,0x72,0x2c,0x20,0x75,0x72,0x6c,0x29,0x3b,0x0a,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x6c,0x65,0x74,0x20,0x73,0x61,0x76,0x65,0x42,0x74,0x6e,0x20,0x3d,0x20,0x64, +0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x71,0x75,0x65,0x72,0x79,0x53,0x65,0x6c, +0x65,0x63,0x74,0x6f,0x72,0x28,0x22,0x2e,0x62,0x74,0x6e,0x2d,0x6c,0x6f,0x61,0x64, +0x2d,0x73,0x6f,0x66,0x74,0x77,0x61,0x72,0x65,0x22,0x29,0x3b,0x0a,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x61,0x76,0x65,0x55,0x70,0x64,0x61,0x74, +0x69,0x6e,0x67,0x28,0x29,0x3b,0x0a,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x0a,0x20, +0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x73,0x65,0x6e,0x64,0x55,0x70,0x64, +0x61,0x74,0x69,0x6e,0x67,0x53,0x6f,0x66,0x74,0x57,0x61,0x72,0x65,0x20,0x3d,0x20, +0x28,0x68,0x2c,0x20,0x73,0x69,0x7a,0x65,0x2c,0x20,0x63,0x72,0x63,0x2c,0x20,0x73, +0x74,0x72,0x29,0x20,0x3d,0x3e,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x75,0x70,0x64,0x61,0x74,0x65,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x65,0x72, +0x28,0x73,0x69,0x7a,0x65,0x2c,0x20,0x63,0x72,0x63,0x2c,0x20,0x73,0x74,0x72,0x29, +0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x77,0x69,0x6e,0x64,0x6f, +0x77,0x2e,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x54,0x68,0x69,0x73,0x2e,0x73,0x65,0x6e, +0x64,0x55,0x70,0x64,0x61,0x74,0x69,0x6e,0x67,0x53,0x6f,0x66,0x74,0x57,0x61,0x72, +0x65,0x20,0x3d,0x20,0x73,0x65,0x6e,0x64,0x55,0x70,0x64,0x61,0x74,0x69,0x6e,0x67, +0x53,0x6f,0x66,0x74,0x57,0x61,0x72,0x65,0x3b,0x0a,0x3c,0x2f,0x73,0x63,0x72,0x69, +0x70,0x74,0x3e,0x0a,0x3c,0x2f,0x68,0x74,0x6d,0x6c,0x3e,0x0a,}; + + + +const struct fsdata_file file__index_html[] = { { +file_NULL, +data__index_html, +data__index_html + 12, +sizeof(data__index_html) - 12, +FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT, +}}; + +#define FS_ROOT file__index_html +#define FS_NUMFILES 1 + diff --git a/modular.json b/modular.json new file mode 100644 index 0000000..f5e4909 --- /dev/null +++ b/modular.json @@ -0,0 +1,17 @@ +{ + "dep": [ + { + "type": "git", + "provider": "GONEC_NEW", + "repo": "lwip_ARTERY_AT32" + } + ], + "cmake": { + "inc_dirs": [ + "Inc" + ], + "srcs": [ + "Src/**.c" + ] + } +} \ No newline at end of file