Lib and example splitup: Removing lib from example

pull/30/head
Jeroen Domburg 9 years ago
parent 98304bc9e5
commit 6acbb69765
  1. 3
      .gitmodules
  2. 10
      libesphttpd/.gitignore
  3. 145
      libesphttpd/Makefile
  4. 61
      libesphttpd/core/auth.c
  5. 112
      libesphttpd/core/base64.c
  6. 6
      libesphttpd/core/base64.h
  7. 560
      libesphttpd/core/httpd.c
  8. 189
      libesphttpd/core/httpdespfs.c
  9. 274
      libesphttpd/espfs/espfs.c
  10. 33
      libesphttpd/espfs/espfsformat.h
  11. 13
      libesphttpd/espfs/espfstest/Makefile
  12. 67
      libesphttpd/espfs/espfstest/main.c
  13. 30
      libesphttpd/espfs/heatshrink_config_custom.h
  14. 19
      libesphttpd/espfs/heatshrink_decoder.c
  15. 24
      libesphttpd/espfs/mkespfsimage/Makefile
  16. 4
      libesphttpd/espfs/mkespfsimage/heatshrink_encoder.c
  17. 364
      libesphttpd/espfs/mkespfsimage/main.c
  18. 22
      libesphttpd/include/auth.h
  19. 5
      libesphttpd/include/captdns.h
  20. 15
      libesphttpd/include/cgiflash.h
  21. 13
      libesphttpd/include/cgiwifi.h
  22. 18
      libesphttpd/include/esp8266.h
  23. 23
      libesphttpd/include/espfs.h
  24. 60
      libesphttpd/include/espmissingincludes.h
  25. 67
      libesphttpd/include/httpd.h
  26. 9
      libesphttpd/include/httpdespfs.h
  27. 1
      libesphttpd/include/user_config.h
  28. 3
      libesphttpd/include/webpages-espfs.h
  29. 1
      libesphttpd/lib/heatshrink
  30. 252
      libesphttpd/util/captdns.c
  31. 77
      libesphttpd/util/cgiflash.c
  32. 311
      libesphttpd/util/cgiwifi.c

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "lib/heatshrink"]
path = libesphttpd/lib/heatshrink
url = https://github.com/atomicobject/heatshrink.git

@ -1,10 +0,0 @@
build/
espfs/mkespfsimage/*.o
espfs/mkespfsimage/mkespfsimage
webpages.espfs
libesphttpd.a
espfs/espfstest/*.o
espfs/espfstest/espfstest
*.DS_Store
html_compressed/
libwebpages-espfs.a

@ -1,145 +0,0 @@
# Directory the Makefile is in. Please don't include other Makefiles before this.
THISDIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
#Include httpd config from lower level, if it exists
-include ../esphttpdconfig.mk
#Default options. If you want to change them, please create ../esphttpdconfig.mk with the options you want in it.
GZIP_COMPRESSION ?= no
COMPRESS_W_YUI ?= no
YUI-COMPRESSOR ?= /usr/bin/yui-compressor
USE_HEATSHRINK ?= yes
# Output directors to store intermediate compiled files
# relative to the project directory
BUILD_BASE = build
# Base directory for the compiler. Needs a / at the end; if not set it'll use the tools that are in
# the PATH.
XTENSA_TOOLS_ROOT ?=
# base directory of the ESP8266 SDK package, absolute
SDK_BASE ?= /opt/Espressif/ESP8266_SDK
# name for the target project
LIB = libesphttpd.a
# which modules (subdirectories) of the project to include in compiling
MODULES = espfs core util
EXTRA_INCDIR = ./include \
. \
lib/heatshrink/
# compiler flags using during compilation of source files
CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -D_STDINT_H \
-Wno-address
# various paths from the SDK used in this project
SDK_LIBDIR = lib
SDK_LDDIR = ld
SDK_INCDIR = include
# select which tools to use as compiler, librarian and linker
CC := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
AR := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-ar
LD := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
OBJCOPY := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy
####
#### no user configurable options below here
####
SRC_DIR := $(MODULES)
BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES))
SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR))
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC))
INCDIR := $(addprefix -I,$(SRC_DIR))
EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR))
MODULE_INCDIR := $(addsuffix /include,$(INCDIR))
V ?= $(VERBOSE)
ifeq ("$(V)","1")
Q :=
vecho := @true
else
Q := @
vecho := @echo
endif
ifeq ("$(GZIP_COMPRESSION)","yes")
CFLAGS += -DGZIP_COMPRESSION
endif
ifeq ("$(USE_HEATSHRINK)","yes")
CFLAGS += -DESPFS_HEATSHRINK
endif
vpath %.c $(SRC_DIR)
define compile-objects
$1/%.o: %.c
$(vecho) "CC $$<"
$(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@
endef
.PHONY: all checkdirs clean webpages.espfs
all: checkdirs $(LIB) webpages.espfs libwebpages-espfs.a
$(LIB): $(OBJ)
$(vecho) "AR $@"
$(Q) $(AR) cru $@ $^
checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
$(Q) mkdir -p $@
webpages.espfs: $(HTMLDIR) espfs/mkespfsimage/mkespfsimage
ifeq ("$(COMPRESS_W_YUI)","yes")
$(Q) rm -rf html_compressed;
$(Q) cp -r ../html html_compressed;
$(Q) echo "Compression assets with yui-compressor. This may take a while..."
$(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done
$(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done
$(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s ../html/ | sed 's/\([0-9]*\).*/\1/'`)*100}"
# mkespfsimage will compress html, css and js files with gzip by default if enabled
# override with -g cmdline parameter
$(Q) cd html_compressed; find | $(THISDIR)/espfs/mkespfsimage/mkespfsimage > $(THISDIR)/webpages.espfs; cd ..;
else
$(Q) cd ../html; find | $(THISDIR)/espfs/mkespfsimage/mkespfsimage > $(THISDIR)/webpages.espfs; cd ..
endif
libwebpages-espfs.a: webpages.espfs
$(Q) $(OBJCOPY) -I binary -O elf32-xtensa-le -B xtensa --rename-section .data=.irom0.literal \
--redefine-sym _binary_webpages_espfs_start=webpages_espfs_start \
--redefine-sym _binary_webpages_espfs_end=webpages_espfs_end \
--redefine-sym _binary_webpages_espfs_size=webpages_espfs_size \
webpages.espfs build/webpages.espfs.o
$(Q) $(AR) cru $@ build/webpages.espfs.o
espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/
$(Q) $(MAKE) -C espfs/mkespfsimage USE_HEATSHRINK="$(USE_HEATSHRINK)" GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
clean:
$(Q) rm -f $(LIB)
$(Q) find $(BUILD_BASE) -type f | xargs rm -f
$(Q) make -C espfs/mkespfsimage/ clean
$(Q) rm -rf $(FW_BASE)
$(Q) rm -f webpages.espfs
ifeq ("$(COMPRESS_W_YUI)","yes")
$(Q) rm -rf html_compressed
endif
$(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir))))

@ -1,61 +0,0 @@
/*
HTTP auth implementation. Only does basic authentication for now.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <esp8266.h>
#include "auth.h"
#include "base64.h"
int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData) {
const char *forbidden="401 Forbidden.";
int no=0;
int r;
char hdr[(AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2)*10];
char userpass[AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2];
char user[AUTH_MAX_USER_LEN];
char pass[AUTH_MAX_PASS_LEN];
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
r=httpdGetHeader(connData, "Authorization", hdr, sizeof(hdr));
if (r && strncmp(hdr, "Basic", 5)==0) {
r=base64_decode(strlen(hdr)-6, hdr+6, sizeof(userpass), (unsigned char *)userpass);
if (r<0) r=0; //just clean out string on decode error
userpass[r]=0; //zero-terminate user:pass string
// os_printf("Auth: %s\n", userpass);
while (((AuthGetUserPw)(connData->cgiArg))(connData, no,
user, AUTH_MAX_USER_LEN, pass, AUTH_MAX_PASS_LEN)) {
//Check user/pass against auth header
if (strlen(userpass)==strlen(user)+strlen(pass)+1 &&
os_strncmp(userpass, user, strlen(user))==0 &&
userpass[strlen(user)]==':' &&
os_strcmp(userpass+strlen(user)+1, pass)==0) {
//Authenticated. Yay!
return HTTPD_CGI_AUTHENTICATED;
}
no++; //Not authenticated with this user/pass. Check next user/pass combo.
}
}
//Not authenticated. Go bug user with login screen.
httpdStartResponse(connData, 401);
httpdHeader(connData, "Content-Type", "text/plain");
httpdHeader(connData, "WWW-Authenticate", "Basic realm=\""HTTP_AUTH_REALM"\"");
httpdEndHeaders(connData);
httpdSend(connData, forbidden, -1);
//Okay, all done.
return HTTPD_CGI_DONE;
}

@ -1,112 +0,0 @@
/* base64.c : base-64 / MIME encode/decode */
/* PUBLIC DOMAIN - Jon Mayo - November 13, 2003 */
#include <esp8266.h>
#include "base64.h"
static const uint8_t base64dec_tab[256]= {
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
};
#if 0
static int ICACHE_FLASH_ATTR base64decode(const char in[4], char out[3]) {
uint8_t v[4];
v[0]=base64dec_tab[(unsigned)in[0]];
v[1]=base64dec_tab[(unsigned)in[1]];
v[2]=base64dec_tab[(unsigned)in[2]];
v[3]=base64dec_tab[(unsigned)in[3]];
out[0]=(v[0]<<2)|(v[1]>>4);
out[1]=(v[1]<<4)|(v[2]>>2);
out[2]=(v[2]<<6)|(v[3]);
return (v[0]|v[1]|v[2]|v[3])!=255 ? in[3]=='=' ? in[2]=='=' ? 1 : 2 : 3 : 0;
}
#endif
/* decode a base64 string in one shot */
int ICACHE_FLASH_ATTR base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out) {
unsigned int ii, io;
uint32_t v;
unsigned int rem;
for(io=0,ii=0,v=0,rem=0;ii<in_len;ii++) {
unsigned char ch;
if(isspace((int)in[ii])) continue;
if(in[ii]=='=') break; /* stop at = */
ch=base64dec_tab[(unsigned int)in[ii]];
if(ch==255) break; /* stop at a parse error */
v=(v<<6)|ch;
rem+=6;
if(rem>=8) {
rem-=8;
if(io>=out_len) return -1; /* truncation is failure */
out[io++]=(v>>rem)&255;
}
}
if(rem>=8) {
rem-=8;
if(io>=out_len) return -1; /* truncation is failure */
out[io++]=(v>>rem)&255;
}
return io;
}
//Only need decode functions for now.
#if 0
static const uint8_t base64enc_tab[64]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void base64encode(const unsigned char in[3], unsigned char out[4], int count) {
out[0]=base64enc_tab[(in[0]>>2)];
out[1]=base64enc_tab[((in[0]&3)<<4)|(in[1]>>4)];
out[2]=count<2 ? '=' : base64enc_tab[((in[1]&15)<<2)|(in[2]>>6)];
out[3]=count<3 ? '=' : base64enc_tab[(in[2]&63)];
}
int base64_encode(size_t in_len, const unsigned char *in, size_t out_len, char *out) {
unsigned ii, io;
uint_least32_t v;
unsigned rem;
for(io=0,ii=0,v=0,rem=0;ii<in_len;ii++) {
unsigned char ch;
ch=in[ii];
v=(v<<8)|ch;
rem+=8;
while(rem>=6) {
rem-=6;
if(io>=out_len) return -1; /* truncation is failure */
out[io++]=base64enc_tab[(v>>rem)&63];
}
}
if(rem) {
v<<=(6-rem);
if(io>=out_len) return -1; /* truncation is failure */
out[io++]=base64enc_tab[v&63];
}
while(io&3) {
if(io>=out_len) return -1; /* truncation is failure */
out[io++]='=';
}
if(io>=out_len) return -1; /* no room for null terminator */
out[io]=0;
return io;
}
#endif

@ -1,6 +0,0 @@
#ifndef BASE64_H
#define BASE64_H
int base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out);
#endif

@ -1,560 +0,0 @@
/*
Esp8266 http server - core routines
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <esp8266.h>
#include "httpd.h"
//Max length of request head
#define MAX_HEAD_LEN 1024
//Max amount of connections
#define MAX_CONN 8
//Max post buffer len
#define MAX_POST 1024
//Max send buffer len
#define MAX_SENDBUFF_LEN 2048
//This gets set at init time.
static HttpdBuiltInUrl *builtInUrls;
//Private data for http connection
struct HttpdPriv {
char head[MAX_HEAD_LEN];
int headPos;
char *sendBuff;
int sendBuffLen;
};
//Connection pool
static HttpdPriv connPrivData[MAX_CONN];
static HttpdConnData connData[MAX_CONN];
static HttpdPostData connPostData[MAX_CONN];
//Listening connection data
static struct espconn httpdConn;
static esp_tcp httpdTcp;
//Struct to keep extension->mime data in
typedef struct {
const char *ext;
const char *mimetype;
} MimeMap;
//The mappings from file extensions to mime types. If you need an extra mime type,
//add it here.
static const MimeMap mimeTypes[]={
{"htm", "text/htm"},
{"html", "text/html"},
{"css", "text/css"},
{"js", "text/javascript"},
{"txt", "text/plain"},
{"jpg", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"png", "image/png"},
{NULL, "text/html"}, //default value
};
//Returns a static char* to a mime type for a given url to a file.
const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
int i=0;
//Go find the extension
char *ext=url+(strlen(url)-1);
while (ext!=url && *ext!='.') ext--;
if (*ext=='.') ext++;
//ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here...
while (mimeTypes[i].ext!=NULL && os_strcmp(ext, mimeTypes[i].ext)!=0) i++;
return mimeTypes[i].mimetype;
}
//Looks up the connData info for a specific esp connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
int i;
for (i=0; i<MAX_CONN; i++) {
if (connData[i].conn==(struct espconn *)arg) return &connData[i];
}
//Shouldn't happen.
os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg);
return NULL;
}
//Retires a connection for re-use
static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
if (conn->post->buff!=NULL) os_free(conn->post->buff);
conn->post->buff=NULL;
conn->cgi=NULL;
conn->conn=NULL;
}
//Stupid li'l helper function that returns the value of a hex char.
static int httpdHexVal(char c) {
if (c>='0' && c<='9') return c-'0';
if (c>='A' && c<='F') return c-'A'+10;
if (c>='a' && c<='f') return c-'a'+10;
return 0;
}
//Decode a percent-encoded value.
//Takes the valLen bytes stored in val, and converts it into at most retLen bytes that
//are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
//zero-terminates the ret buffer.
int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) {
int s=0, d=0;
int esced=0, escVal=0;
while (s<valLen && d<retLen) {
if (esced==1) {
escVal=httpdHexVal(val[s])<<4;
esced=2;
} else if (esced==2) {
escVal+=httpdHexVal(val[s]);
ret[d++]=escVal;
esced=0;
} else if (val[s]=='%') {
esced=1;
} else if (val[s]=='+') {
ret[d++]=' ';
} else {
ret[d++]=val[s];
}
s++;
}
if (d<retLen) ret[d]=0;
return d;
}
//Find a specific arg in a string of get- or post-data.
//Line is the string of post/get-data, arg is the name of the value to find. The
//zero-terminated result is written in buff, with at most buffLen bytes used. The
//function returns the length of the result, or -1 if the value wasn't found. The
//returned string will be urldecoded already.
int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen) {
char *p, *e;
if (line==NULL) return 0;
p=line;
while(p!=NULL && *p!='\n' && *p!='\r' && *p!=0) {
os_printf("findArg: %s\n", p);
if (os_strncmp(p, arg, os_strlen(arg))==0 && p[strlen(arg)]=='=') {
p+=os_strlen(arg)+1; //move p to start of value
e=(char*)os_strstr(p, "&");
if (e==NULL) e=p+os_strlen(p);
os_printf("findArg: val %s len %d\n", p, (e-p));
return httpdUrlDecode(p, (e-p), buff, buffLen);
}
p=(char*)os_strstr(p, "&");
if (p!=NULL) p+=1;
}
os_printf("Finding %s in %s: Not found :/\n", arg, line);
return -1; //not found
}
//Get the value of a certain header in the HTTP client head
int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen) {
char *p=conn->priv->head;
p=p+strlen(p)+1; //skip GET/POST part
p=p+strlen(p)+1; //skip HTTP part
while (p<(conn->priv->head+conn->priv->headPos)) {
while(*p<=32 && *p!=0) p++; //skip crap at start
//See if this is the header
if (os_strncmp(p, header, strlen(header))==0 && p[strlen(header)]==':') {
//Skip 'key:' bit of header line
p=p+strlen(header)+1;
//Skip past spaces after the colon
while(*p==' ') p++;
//Copy from p to end
while (*p!=0 && *p!='\r' && *p!='\n' && retLen>1) {
*ret++=*p++;
retLen--;
}
//Zero-terminate string
*ret=0;
//All done :)
return 1;
}
p+=strlen(p)+1; //Skip past end of string and \0 terminator
}
return 0;
}
//Start the response headers.
void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) {
char buff[128];
int l;
l=os_sprintf(buff, "HTTP/1.0 %d OK\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\n", code);
httpdSend(conn, buff, l);
}
//Send a http header.
void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) {
char buff[256];
int l;
l=os_sprintf(buff, "%s: %s\r\n", field, val);
httpdSend(conn, buff, l);
}
//Finish the headers.
void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) {
httpdSend(conn, "\r\n", -1);
}
//ToDo: sprintf->snprintf everywhere... esp doesn't have snprintf tho' :/
//Redirect to the given URL.
void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) {
char buff[1024];
int l;
l=os_sprintf(buff, "HTTP/1.1 302 Found\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nLocation: %s\r\n\r\nMoved to %s\r\n", newUrl, newUrl);
httpdSend(conn, buff, l);
}
//Use this as a cgi function to redirect one url to another.
int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdRedirect(connData, (char*)connData->cgiArg);
return HTTPD_CGI_DONE;
}
//This CGI function redirects to a fixed url of http://[hostname]/ if hostname field of request isn't
//already that hostname. Use this in combination with a DNS server that redirects everything to the
//ESP in order to load a HTML page as soon as a phone connects to the ESP.
int ICACHE_FLASH_ATTR cgiCheckHostname(HttpdConnData *connData) {
char buff[1024];
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
//Check hostname; pass on if the same
if (connData->hostName==NULL || os_strcmp(connData->hostName, (char*)connData->cgiArg)==0) return HTTPD_CGI_NOTFOUND;
//Not the same. Redirect to real hostname.
os_sprintf(buff, "http://%s/", (char*)connData->cgiArg);
os_printf("Redirecting to hostname url %s\n", buff);
httpdRedirect(connData, buff);
return HTTPD_CGI_DONE;
}
//Add data to the send buffer. len is the length of the data. If len is -1
//the data is seen as a C-string.
//Returns 1 for success, 0 for out-of-memory.
int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) {
if (len<0) len=strlen(data);
if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) return 0;
os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len);
conn->priv->sendBuffLen+=len;
return 1;
}
//Helper function to send any data in conn->priv->sendBuff
static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) {
if (conn->priv->sendBuffLen!=0) {
espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
conn->priv->sendBuffLen=0;
}
}
//Callback called when the data on a socket has been successfully
//sent.
static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
int r;
HttpdConnData *conn=httpdFindConnData(arg);
char sendBuff[MAX_SENDBUFF_LEN];
if (conn==NULL) return;
conn->priv->sendBuff=sendBuff;
conn->priv->sendBuffLen=0;
if (conn->cgi==NULL) { //Marked for destruction?
os_printf("Conn %p is done. Closing.\n", conn->conn);
espconn_disconnect(conn->conn);
httpdRetireConn(conn);
return; //No need to call xmitSendBuff.
}
r=conn->cgi(conn); //Execute cgi fn.
if (r==HTTPD_CGI_DONE) {
conn->cgi=NULL; //mark for destruction.
}
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
os_printf("ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", r);
conn->cgi=NULL; //mark for destruction.
}
xmitSendBuff(conn);
}
static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n";
//This is called when the headers have been received and the connection is ready to send
//the result headers and data.
//We need to find the CGI function to call, call it, and dependent on what it returns either
//find the next cgi function, wait till the cgi data is sent or close up the connection.
static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
int r;
int i=0;
if (conn->url==NULL) {
os_printf("WtF? url = NULL\n");
return; //Shouldn't happen
}
//See if we can find a CGI that's happy to handle the request.
while (1) {
//Look up URL in the built-in URL table.
while (builtInUrls[i].url!=NULL) {
int match=0;
//See if there's a literal match
if (os_strcmp(builtInUrls[i].url, conn->url)==0) match=1;
//See if there's a wildcard match
if (builtInUrls[i].url[os_strlen(builtInUrls[i].url)-1]=='*' &&
os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1;
if (match) {
os_printf("Is url index %d\n", i);
conn->cgiData=NULL;
conn->cgi=builtInUrls[i].cgiCb;
conn->cgiArg=builtInUrls[i].cgiArg;
break;
}
i++;
}
if (builtInUrls[i].url==NULL) {
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
//generate a built-in 404 to handle this.
os_printf("%s not found. 404!\n", conn->url);
httpdSend(conn, httpNotFoundHeader, -1);
xmitSendBuff(conn);
conn->cgi=NULL; //mark for destruction
return;
}
//Okay, we have a CGI function that matches the URL. See if it wants to handle the
//particular URL we're supposed to handle.
r=conn->cgi(conn);
if (r==HTTPD_CGI_MORE) {
//Yep, it's happy to do so and has more data to send.
xmitSendBuff(conn);
return;
} else if (r==HTTPD_CGI_DONE) {
//Yep, it's happy to do so and already is done sending data.
xmitSendBuff(conn);
conn->cgi=NULL; //mark conn for destruction
return;
} else if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
//URL doesn't want to handle the request: either the data isn't found or there's no
//need to generate a login screen.
i++; //look at next url the next iteration of the loop.
}
}
}
//Parse a line of header data and modify the connection data accordingly.
static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
int i;
char first_line = false;
if (os_strncmp(h, "GET ", 4)==0) {
conn->requestType = HTTPD_METHOD_GET;
first_line = true;
} else if (os_strncmp(h, "Host:", 5)==0) {
i=5;
while (h[i]==' ') i++;
conn->hostName=&h[i];
} else if (os_strncmp(h, "POST ", 5)==0) {
conn->requestType = HTTPD_METHOD_POST;
first_line = true;
}
if (first_line) {
char *e;
//Skip past the space after POST/GET
i=0;
while (h[i]!=' ') i++;
conn->url=h+i+1;
//Figure out end of url.
e=(char*)os_strstr(conn->url, " ");
if (e==NULL) return; //wtf?
*e=0; //terminate url part
os_printf("URL = %s\n", conn->url);
//Parse out the URL part before the GET parameters.
conn->getArgs=(char*)os_strstr(conn->url, "?");
if (conn->getArgs!=0) {
*conn->getArgs=0;
conn->getArgs++;
os_printf("GET args = %s\n", conn->getArgs);
} else {
conn->getArgs=NULL;
}
} else if (os_strncmp(h, "Content-Length:", 15)==0) {
i=15;
//Skip trailing spaces
while (h[i]==' ') i++;
//Get POST data length
conn->post->len=atoi(h+i);
// Allocate the buffer
if (conn->post->len > MAX_POST) {
// we'll stream this in in chunks
conn->post->buffSize = MAX_POST;
} else {
conn->post->buffSize = conn->post->len;
}
os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1);
conn->post->buffLen=0;
} else if (os_strncmp(h, "Content-Type: ", 14)==0) {
if (os_strstr(h, "multipart/form-data")) {
// It's multipart form data so let's pull out the boundary for future use
char *b;
if ((b = os_strstr(h, "boundary=")) != NULL) {
conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
conn->post->multipartBoundary[0] = '-';
conn->post->multipartBoundary[1] = '-';
os_printf("boundary = %s\n", conn->post->multipartBoundary);
}
}
}
}
//Callback called when there's data available on a socket.
static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) {
int x;
char *p, *e;
char sendBuff[MAX_SENDBUFF_LEN];
HttpdConnData *conn=httpdFindConnData(arg);
if (conn==NULL) return;
conn->priv->sendBuff=sendBuff;
conn->priv->sendBuffLen=0;
//This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are:
//<0 (-1): Post len unknown because we're still receiving headers
//==0: No post data
//>0: Need to receive post data
//ToDo: See if we can use something more elegant for this.
for (x=0; x<len; x++) {
if (conn->post->len<0) {
//This byte is a header byte.
if (conn->priv->headPos!=MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++]=data[x];
conn->priv->head[conn->priv->headPos]=0;
//Scan for /r/n/r/n. Receiving this indicate the headers end.
if (data[x]=='\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) {
//Indicate we're done with the headers.
conn->post->len=0;
//Reset url data
conn->url=NULL;
//Iterate over all received headers and parse them.
p=conn->priv->head;
while(p<(&conn->priv->head[conn->priv->headPos-4])) {
e=(char *)os_strstr(p, "\r\n"); //Find end of header line
if (e==NULL) break; //Shouldn't happen.
e[0]=0; //Zero-terminate header
httpdParseHeader(p, conn); //and parse it.
p=e+2; //Skip /r/n (now /0/n)
}
//If we don't need to receive post data, we can send the response now.
if (conn->post->len==0) {
httpdProcessRequest(conn);
}
}
} else if (conn->post->len!=0) {
//This byte is a POST byte.
conn->post->buff[conn->post->buffLen++]=data[x];
conn->post->received++;
conn->hostName=NULL;
if (conn->post->buffLen >= conn->post->buffSize || conn->post->received == conn->post->len) {
//Received a chunk of post data
conn->post->buff[conn->post->buffLen]=0; //zero-terminate, in case the cgi handler knows it can use strings
//Send the response.
httpdProcessRequest(conn);
conn->post->buffLen = 0;
}
}
}
}
static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) {
HttpdConnData *conn=httpdFindConnData(arg);
os_printf("ReconCb\n");
if (conn==NULL) return;
//Yeah... No idea what to do here. ToDo: figure something out.
}
static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) {
//The esp sdk passes through wrong arg here, namely the one of the *listening* socket.
//That is why we can't use (HttpdConnData)arg->sock here.
//Just look at all the sockets and kill the slot if needed.
int i;
for (i=0; i<MAX_CONN; i++) {
if (connData[i].conn!=NULL) {
//Why the >=ESPCONN_CLOSE and not ==? Well, seems the stack sometimes de-allocates
//espconns under our noses, especially when connections are interrupted. The memory
//is then used for something else, and we can use that to capture *most* of the
//disconnect cases.
if (connData[i].conn->state==ESPCONN_NONE || connData[i].conn->state>=ESPCONN_CLOSE) {
connData[i].conn=NULL;
if (connData[i].cgi!=NULL) connData[i].cgi(&connData[i]); //flush cgi data
httpdRetireConn(&connData[i]);
}
}
}
}
static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
struct espconn *conn=arg;
int i;
//Find empty conndata in pool
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
espconn_disconnect(conn);
return;
}
connData[i].priv=&connPrivData[i];
connData[i].conn=conn;
connData[i].priv->headPos=0;
connData[i].post=&connPostData[i];
connData[i].post->buff=NULL;
connData[i].post->buffLen=0;
connData[i].post->received=0;
connData[i].post->len=-1;
connData[i].hostName=NULL;
espconn_regist_recvcb(conn, httpdRecvCb);
espconn_regist_reconcb(conn, httpdReconCb);
espconn_regist_disconcb(conn, httpdDisconCb);
espconn_regist_sentcb(conn, httpdSentCb);
}
//Httpd initialization routine. Call this to kick off webserver functionality.
void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) {
int i;
for (i=0; i<MAX_CONN; i++) {
connData[i].conn=NULL;
}
httpdConn.type=ESPCONN_TCP;
httpdConn.state=ESPCONN_NONE;
httpdTcp.local_port=port;
httpdConn.proto.tcp=&httpdTcp;
builtInUrls=fixedUrls;
os_printf("Httpd init, conn=%p\n", &httpdConn);
espconn_regist_connectcb(&httpdConn, httpdConnectCb);
espconn_accept(&httpdConn);
}

@ -1,189 +0,0 @@
/*
Connector to let httpd use the espfs filesystem to serve the files in it.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <esp8266.h>
#include "httpdespfs.h"
#include "espfs.h"
#include "espfsformat.h"
// The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression.
// If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.)
static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 52\r\n\r\nYour browser does not accept gzip-compressed data.\r\n";
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding
//path in the filesystem and if it exists, passes the file through. This simulates what a normal
//webserver would do with static files.
int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
EspFsFile *file=connData->cgiData;
int len;
char buff[1024];
char acceptEncodingBuffer[64];
int isGzip;
if (connData->conn==NULL) {
//Connection aborted. Clean up.
espFsClose(file);
return HTTPD_CGI_DONE;
}
if (file==NULL) {
//First call to this cgi. Open the file so we can read it.
file=espFsOpen(connData->url);
if (file==NULL) {
return HTTPD_CGI_NOTFOUND;
}
// The gzip checking code is intentionally without #ifdefs because checking
// for FLAG_GZIP (which indicates gzip compressed file) is very easy, doesn't
// mean additional overhead and is actually safer to be on at all times.
// If there are no gzipped files in the image, the code bellow will not cause any harm.
// Check if requested file was GZIP compressed
isGzip = espFsFlags(file) & FLAG_GZIP;
if (isGzip) {
// Check the browser's "Accept-Encoding" header. If the client does not
// advertise that he accepts GZIP send a warning message (telnet users for e.g.)
httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64);
if (os_strstr(acceptEncodingBuffer, "gzip") == NULL) {
//No Accept-Encoding: gzip header present
httpdSend(connData, gzipNonSupportedMessage, -1);
espFsClose(file);
return HTTPD_CGI_DONE;
}
}
connData->cgiData=file;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url));
if (isGzip) {
httpdHeader(connData, "Content-Encoding", "gzip");
}
httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate");
httpdEndHeaders(connData);
return HTTPD_CGI_MORE;
}
len=espFsRead(file, buff, 1024);
if (len>0) espconn_sent(connData->conn, (uint8 *)buff, len);
if (len!=1024) {
//We're done.
espFsClose(file);
return HTTPD_CGI_DONE;
} else {
//Ok, till next time.
return HTTPD_CGI_MORE;
}
}
//cgiEspFsTemplate can be used as a template.
typedef struct {
EspFsFile *file;
void *tplArg;
char token[64];
int tokenPos;
} TplData;
typedef void (* TplCallback)(HttpdConnData *connData, char *token, void **arg);
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) {
TplData *tpd=connData->cgiData;
int len;
int x, sp=0;
char *e=NULL;
char buff[1025];
if (connData->conn==NULL) {
//Connection aborted. Clean up.
((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg);
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_DONE;
}
if (tpd==NULL) {
//First call to this cgi. Open the file so we can read it.
tpd=(TplData *)os_malloc(sizeof(TplData));
tpd->file=espFsOpen(connData->url);
tpd->tplArg=NULL;
tpd->tokenPos=-1;
if (tpd->file==NULL) {
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_NOTFOUND;
}
if (espFsFlags(tpd->file) & FLAG_GZIP) {
os_printf("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!\n", connData->url);
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_NOTFOUND;
}
connData->cgiData=tpd;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url));
httpdEndHeaders(connData);
return HTTPD_CGI_MORE;
}
len=espFsRead(tpd->file, buff, 1024);
if (len>0) {
sp=0;
e=buff;
for (x=0; x<len; x++) {
if (tpd->tokenPos==-1) {
//Inside ordinary text.
if (buff[x]=='%') {
//Send raw data up to now
if (sp!=0) httpdSend(connData, e, sp);
sp=0;
//Go collect token chars.
tpd->tokenPos=0;
} else {
sp++;
}
} else {
if (buff[x]=='%') {
if (tpd->tokenPos==0) {
//This is the second % of a %% escape string.
//Send a single % and resume with the normal program flow.
httpdSend(connData, "%", 1);
} else {
//This is an actual token.
tpd->token[tpd->tokenPos++]=0; //zero-terminate token
((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg);
}
//Go collect normal chars again.
e=&buff[x+1];
tpd->tokenPos=-1;
} else {
if (tpd->tokenPos<(sizeof(tpd->token)-1)) tpd->token[tpd->tokenPos++]=buff[x];
}
}
}
}
//Send remaining bit.
if (sp!=0) httpdSend(connData, e, sp);
if (len!=1024) {
//We're done.
((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg);
espFsClose(tpd->file);
os_free(tpd);
return HTTPD_CGI_DONE;
} else {
//Ok, till next time.
return HTTPD_CGI_MORE;
}
}

@ -1,274 +0,0 @@
/*
This is a simple read-only implementation of a file system. It uses a block of data coming from the
mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
It's written for use with httpd, but doesn't need to be used as such.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
//These routines can also be tested by comping them in with the espfstest tool. This
//simplifies debugging, but needs some slightly different headers. The #ifdef takes
//care of that.
#ifdef __ets__
//esp build
#include <esp8266.h>
#else
//Test build
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define os_malloc malloc
#define os_free free
#define os_memcpy memcpy
#define os_strncmp strncmp
#define os_strcmp strcmp
#define os_strcpy strcpy
#define os_printf printf
#define ICACHE_FLASH_ATTR
#endif
#include "espfsformat.h"
#include "espfs.h"
#ifdef ESPFS_HEATSHRINK
#include "heatshrink_config_custom.h"
#include "heatshrink_decoder.h"
#endif
static char* espFsData = NULL;
struct EspFsFile {
EspFsHeader *header;
char decompressor;
int32_t posDecomp;
char *posStart;
char *posComp;
void *decompData;
};
/*
Available locations, at least in my flash, with boundaries partially guessed. This
is using 0.9.1/0.9.2 SDK on a not-too-new module.
0x00000 (0x10000): Code/data (RAM data?)
0x10000 (0x02000): Gets erased by something?
0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
0x40000 (0x20000): Code/data (ROM data?)
0x60000 (0x1C000): Free
0x7c000 (0x04000): Param store
0x80000 - end of flash
Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
*must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
a memory exception, crashing the program.
*/
EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) {
// base address must be aligned to 4 bytes
if (((int)flashAddress & 3) != 0) {
return ESPFS_INIT_RESULT_BAD_ALIGN;
}
// check if there is valid header at address
EspFsHeader testHeader;
os_memcpy(&testHeader, flashAddress, sizeof(EspFsHeader));
if (testHeader.magic != ESPFS_MAGIC) {
return ESPFS_INIT_RESULT_NO_IMAGE;
}
espFsData = (char *)flashAddress;
return ESPFS_INIT_RESULT_OK;
}
//Copies len bytes over from dst to src, but does it using *only*
//aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
//ToDo: perhaps os_memcpy also does unaligned accesses?
#ifdef __ets__
void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) {
int x;
int w, b;
for (x=0; x<len; x++) {
b=((int)src&3);
w=*((int *)(src-b));
if (b==0) *dst=(w>>0);
if (b==1) *dst=(w>>8);
if (b==2) *dst=(w>>16);
if (b==3) *dst=(w>>24);
dst++; src++;
}
}
#else
#define memcpyAligned memcpy
#endif
// Returns flags of opened file.
int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) {
if (fh == NULL) {
os_printf("File handle not ready\n");
return -1;
}
int8_t flags;
memcpyAligned((char*)&flags, (char*)&fh->header->flags, 1);
return (int)flags;
}
//Open a file and return a pointer to the file desc struct.
EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) {
if (espFsData == NULL) {
os_printf("Call espFsInit first!\n");
return NULL;
}
char *p=espFsData;
char *hpos;
char namebuf[256];
EspFsHeader h;
EspFsFile *r;
//Strip initial slashes
while(fileName[0]=='/') fileName++;
//Go find that file!
while(1) {
hpos=p;
//Grab the next file header.
os_memcpy(&h, p, sizeof(EspFsHeader));
if (h.magic!=ESPFS_MAGIC) {
os_printf("Magic mismatch. EspFS image broken.\n");
return NULL;
}
if (h.flags&FLAG_LASTFILE) {
os_printf("End of image.\n");
return NULL;
}
//Grab the name of the file.
p+=sizeof(EspFsHeader);
os_memcpy(namebuf, p, sizeof(namebuf));
// os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n",
// namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags);
if (os_strcmp(namebuf, fileName)==0) {
//Yay, this is the file we need!
p+=h.nameLen; //Skip to content.
r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem
// os_printf("Alloc %p\n", r);
if (r==NULL) return NULL;
r->header=(EspFsHeader *)hpos;
r->decompressor=h.compression;
r->posComp=p;
r->posStart=p;
r->posDecomp=0;
if (h.compression==COMPRESS_NONE) {
r->decompData=NULL;
#ifdef ESPFS_HEATSHRINK
} else if (h.compression==COMPRESS_HEATSHRINK) {
//File is compressed with Heatshrink.
char parm;
heatshrink_decoder *dec;
//Decoder params are stored in 1st byte.
memcpyAligned(&parm, r->posComp, 1);
r->posComp++;
os_printf("Heatshrink compressed file; decode parms = %x\n", parm);
dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf);
r->decompData=dec;
#endif
} else {
os_printf("Invalid compression: %d\n", h.compression);
return NULL;
}
return r;
}
//We don't need this file. Skip name and file
p+=h.nameLen+h.fileLenComp;
if ((int)p&3) p+=4-((int)p&3); //align to next 32bit val
}
}
//Read len bytes from the given file into buff. Returns the actual amount of bytes read.
int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
int flen, fdlen;
if (fh==NULL) return 0;
//Cache file length.
memcpyAligned((char*)&flen, (char*)&fh->header->fileLenComp, 4);
memcpyAligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
//Do stuff depending on the way the file is compressed.
if (fh->decompressor==COMPRESS_NONE) {
int toRead;
toRead=flen-(fh->posComp-fh->posStart);
if (len>toRead) len=toRead;
// os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
memcpyAligned(buff, fh->posComp, len);
fh->posDecomp+=len;
fh->posComp+=len;
// os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
return len;
#ifdef ESPFS_HEATSHRINK
} else if (fh->decompressor==COMPRESS_HEATSHRINK) {
int decoded=0;
size_t elen, rlen;
char ebuff[16];
heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData;
// os_printf("Alloc %p\n", dec);
if (fh->posDecomp == fdlen) {
return 0;
}
// We must ensure that whole file is decompressed and written to output buffer.
// This means even when there is no input data (elen==0) try to poll decoder until
// posDecomp equals decompressed file length
while(decoded<len) {
//Feed data into the decompressor
//ToDo: Check ret val of heatshrink fns for errors
elen=flen-(fh->posComp - fh->posStart);
if (elen>0) {
memcpyAligned(ebuff, fh->posComp, 16);
heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen);
fh->posComp+=rlen;
}
//Grab decompressed data and put into buff
heatshrink_decoder_poll(dec, (uint8_t *)buff, len-decoded, &rlen);
fh->posDecomp+=rlen;
buff+=rlen;
decoded+=rlen;
// os_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen);
if (elen == 0) {
if (fh->posDecomp == fdlen) {
// os_printf("Decoder finish\n");
heatshrink_decoder_finish(dec);
}
return decoded;
}
}
return len;
#endif
}
return 0;
}
//Close the file.
void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) {
if (fh==NULL) return;
#ifdef ESPFS_HEATSHRINK
if (fh->decompressor==COMPRESS_HEATSHRINK) {
heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData;
heatshrink_decoder_free(dec);
// os_printf("Freed %p\n", dec);
}
#endif
// os_printf("Freed %p\n", fh);
os_free(fh);
}

@ -1,33 +0,0 @@
#ifndef ESPROFSFORMAT_H
#define ESPROFSFORMAT_H
/*
Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module.
Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files,
headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself
when trying to do a <4byte or unaligned read.
*/
/*
The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data.
Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header
with the FLAG_LASTFILE flag set.
*/
#define FLAG_LASTFILE (1<<0)
#define FLAG_GZIP (1<<1)
#define COMPRESS_NONE 0
#define COMPRESS_HEATSHRINK 1
#define ESPFS_MAGIC 0x73665345
typedef struct {
int32_t magic;
int8_t flags;
int8_t compression;
int16_t nameLen;
int32_t fileLenComp;
int32_t fileLenDecomp;
} __attribute__((packed)) EspFsHeader;
#endif

@ -1,13 +0,0 @@
CFLAGS=-I../../lib/heatshrink -I.. -std=gnu99 -DESPFS_HEATSHRINK
espfstest: main.o espfs.o heatshrink_decoder.o
$(CC) -o $@ $^
espfs.o: ../espfs.c
$(CC) $(CFLAGS) -c $^ -o $@
heatshrink_decoder.o: ../heatshrink_decoder.c
$(CC) $(CFLAGS) -c $^ -o $@
clean:
rm -f *.o espfstest

@ -1,67 +0,0 @@
/*
Simple and stupid file decompressor for an espfs image. Mostly used as a testbed for espfs.c and
the decompressors: code compiled natively is way easier to debug using gdb et all :)
*/
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "espfs.h"
char *espFsData;
int main(int argc, char **argv) {
int f, out;
int len;
char buff[128];
EspFsFile *ef;
off_t size;
EspFsInitResult ir;
if (argc!=3) {
printf("Usage: %s espfs-image file\nExpands file from the espfs-image archive.\n", argv[0]);
exit(0);
}
f=open(argv[1], O_RDONLY);
if (f<=0) {
perror(argv[1]);
exit(1);
}
size=lseek(f, 0, SEEK_END);
espFsData=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0);
if (espFsData==MAP_FAILED) {
perror("mmap");
exit(1);
}
ir=espFsInit(espFsData);
if (ir != ESPFS_INIT_RESULT_OK) {
printf("Couldn't init espfs filesystem (code %d)\n", ir);
exit(1);
}
ef=espFsOpen(argv[2]);
if (ef==NULL) {
printf("Couldn't find %s in image.\n", argv[2]);
exit(1);
}
out=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (out<=0) {
perror(argv[2]);
exit(1);
}
while ((len=espFsRead(ef, buff, 128))!=0) {
write(out, buff, len);
}
espFsClose(ef);
//munmap, close, ... I can't be bothered.
}

@ -1,30 +0,0 @@
//Heatshrink config for the decompressor.
#ifndef HEATSHRINK_CONFIG_H
#define HEATSHRINK_CONFIG_H
/* Should functionality assuming dynamic allocation be used? */
#define HEATSHRINK_DYNAMIC_ALLOC 1
#if HEATSHRINK_DYNAMIC_ALLOC
/* Optional replacement of malloc/free */
#ifdef __ets__
#define HEATSHRINK_MALLOC(SZ) os_malloc(SZ)
#define HEATSHRINK_FREE(P, SZ) os_free(P)
#else
#define HEATSHRINK_MALLOC(SZ) malloc(SZ)
#define HEATSHRINK_FREE(P, SZ) free(P)
#endif
#else
/* Required parameters for static configuration */
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32
#define HEATSHRINK_STATIC_WINDOW_BITS 8
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
#endif
/* Turn on logging for debugging. */
#define HEATSHRINK_DEBUGGING_LOGS 0
/* Use indexing for faster compression. (This requires additional space.) */
#define HEATSHRINK_USE_INDEX 1
#endif

@ -1,19 +0,0 @@
#include "espfs.h"
#ifdef ESPFS_HEATSHRINK
//Stupid wrapper so we don't have to move c-files around
//Also loads httpd-specific config.
#ifdef __ets__
//esp build
#include <esp8266.h>
#define memset(x,y,z) os_memset(x,y,z)
#define memcpy(x,y,z) os_memcpy(x,y,z)
#endif
#include "heatshrink_config_custom.h"
#include "../lib/heatshrink/heatshrink_decoder.c"
#endif

@ -1,24 +0,0 @@
GZIP_COMPRESSION ?= no
USE_HEATSHRINK ?= yes
CFLAGS=-I../../lib/heatshrink -I../../include -I.. -std=gnu99
ifeq ("$(GZIP_COMPRESSION)","yes")
CFLAGS += -DESPFS_GZIP
endif
ifeq ("$(USE_HEATSHRINK)","yes")
CFLAGS += -DESPFS_HEATSHRINK
endif
OBJS=main.o heatshrink_encoder.o
TARGET=mkespfsimage
$(TARGET): $(OBJS)
ifeq ("$(GZIP_COMPRESSION)","yes")
$(CC) -o $@ $^ -lz
else
$(CC) -o $@ $^
endif
clean:
rm -f $(TARGET) $(OBJS)

@ -1,4 +0,0 @@
//Stupid wraparound include to make sure object file doesn't end up in heatshrink dir
#ifdef ESPFS_HEATSHRINK
#include "../lib/heatshrink/heatshrink_encoder.c"
#endif

@ -1,364 +0,0 @@
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <string.h>
#include "espfs.h"
#include "espfsformat.h"
//Heatshrink
#ifdef ESPFS_HEATSHRINK
#include "heatshrink_common.h"
#include "heatshrink_config.h"
#include "heatshrink_encoder.h"
#endif
//Gzip
#ifdef ESPFS_GZIP
// If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev"
// to install missing package.
#include <zlib.h>
#endif
//Routines to convert host format to the endianness used in the xtensa
short htoxs(short in) {
char r[2];
r[0]=in;
r[1]=in>>8;
return *((short *)r);
}
int htoxl(int in) {
unsigned char r[4];
r[0]=in;
r[1]=in>>8;
r[2]=in>>16;
r[3]=in>>24;
return *((int *)r);
}
#ifdef ESPFS_HEATSHRINK
size_t compressHeatshrink(char *in, int insize, char *out, int outsize, int level) {
char *inp=in;
char *outp=out;
size_t len;
int ws[]={5, 6, 8, 11, 13};
int ls[]={3, 3, 4, 4, 4};
HSE_poll_res pres;
HSE_sink_res sres;
size_t r;
if (level==-1) level=8;
level=(level-1)/2; //level is now 0, 1, 2, 3, 4
heatshrink_encoder *enc=heatshrink_encoder_alloc(ws[level], ls[level]);
if (enc==NULL) {
perror("allocating mem for heatshrink");
exit(1);
}
//Save encoder parms as first byte
*outp=(ws[level]<<4)|ls[level];
outp++; outsize--;
r=1;
do {
if (insize>0) {
sres=heatshrink_encoder_sink(enc, inp, insize, &len);
if (sres!=HSER_SINK_OK) break;
inp+=len; insize-=len;
if (insize==0) heatshrink_encoder_finish(enc);
}
do {
pres=heatshrink_encoder_poll(enc, outp, outsize, &len);
if (pres!=HSER_POLL_MORE && pres!=HSER_POLL_EMPTY) break;
outp+=len; outsize-=len;
r+=len;
} while (pres==HSER_POLL_MORE);
} while (insize!=0);
if (insize!=0) {
fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", insize, sres, pres);
exit(1);
}
heatshrink_encoder_free(enc);
return r;
}
#endif
#ifdef ESPFS_GZIP
size_t compressGzip(char *in, int insize, char *out, int outsize, int level) {
z_stream stream;
int zresult;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.next_in = in;
stream.avail_in = insize;
stream.next_out = out;
stream.avail_out = outsize;
// 31 -> 15 window bits + 16 for gzip
zresult = deflateInit2 (&stream, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
if (zresult != Z_OK) {
fprintf(stderr, "DeflateInit2 failed with code %d\n", zresult);
exit(1);
}
zresult = deflate(&stream, Z_FINISH);
if (zresult != Z_STREAM_END) {
fprintf(stderr, "Deflate failed with code %d\n", zresult);
exit(1);
}
zresult = deflateEnd(&stream);
if (zresult != Z_OK) {
fprintf(stderr, "DeflateEnd failed with code %d\n", zresult);
exit(1);
}
return stream.total_out;
}
char **gzipExtensions = NULL;
int shouldCompressGzip(char *name) {
char *ext = name + strlen(name);
while (*ext != '.') {
ext--;
if (ext < name) {
// no dot in file name -> no extension -> nothing to match against
return 0;
}
}
ext++;
int i = 0;
while (gzipExtensions[i] != NULL) {
if (strcmp(ext,gzipExtensions[i]) == 0) {
return 1;
}
i++;
}
return 0;
}
int parseGzipExtensions(char *input) {
char *token;
char *extList = input;
int count = 2; // one for first element, second for terminator
// count elements
while (*extList != 0) {
if (*extList == ',') count++;
extList++;
}
// split string
extList = input;
gzipExtensions = malloc(count * sizeof(char*));
count = 0;
token = strtok(extList, ",");
while (token) {
gzipExtensions[count++] = token;
token = strtok(NULL, ",");
}
// terminate list
gzipExtensions[count] = NULL;
return 1;
}
#endif
int handleFile(int f, char *name, int compression, int level, char **compName) {
char *fdat, *cdat;
off_t size, csize;
EspFsHeader h;
int nameLen;
int8_t flags = 0;
size=lseek(f, 0, SEEK_END);
fdat=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0);
if (fdat==MAP_FAILED) {
perror("mmap");
return 0;
}
#ifdef ESPFS_GZIP
if (shouldCompressGzip(name)) {
csize = size*3;
if (csize<100) // gzip has some headers that do not fit when trying to compress small files
csize = 100; // enlarge buffer if this is the case
cdat=malloc(csize);
csize=compressGzip(fdat, size, cdat, csize, level);
compression = COMPRESS_NONE;
flags = FLAG_GZIP;
} else
#endif
if (compression==COMPRESS_NONE) {
csize=size;
cdat=fdat;
#ifdef ESPFS_HEATSHRINK
} else if (compression==COMPRESS_HEATSHRINK) {
cdat=malloc(size*2);
csize=compressHeatshrink(fdat, size, cdat, size*2, level);
#endif
} else {
fprintf(stderr, "Unknown compression - %d\n", compression);
exit(1);
}
if (csize>size) {
//Compressing enbiggened this file. Revert to uncompressed store.
compression=COMPRESS_NONE;
csize=size;
cdat=fdat;
flags=0;
}
//Fill header data
h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
h.flags=flags;
h.compression=compression;
h.nameLen=nameLen=strlen(name)+1;
if (h.nameLen&3) h.nameLen+=4-(h.nameLen&3); //Round to next 32bit boundary
h.nameLen=htoxs(h.nameLen);
h.fileLenComp=htoxl(csize);
h.fileLenDecomp=htoxl(size);
write(1, &h, sizeof(EspFsHeader));
write(1, name, nameLen);
while (nameLen&3) {
write(1, "\000", 1);
nameLen++;
}
write(1, cdat, csize);
//Pad out to 32bit boundary
while (csize&3) {
write(1, "\000", 1);
csize++;
}
munmap(fdat, size);
if (compName != NULL) {
if (h.compression==COMPRESS_HEATSHRINK) {
*compName = "heatshrink";
} else if (h.compression==COMPRESS_NONE) {
if (h.flags & FLAG_GZIP) {
*compName = "gzip";
} else {
*compName = "none";
}
} else {
*compName = "unknown";
}
}
return (csize*100)/size;
}
//Write final dummy header with FLAG_LASTFILE set.
void finishArchive() {
EspFsHeader h;
h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
h.flags=FLAG_LASTFILE;
h.compression=COMPRESS_NONE;
h.nameLen=htoxs(0);
h.fileLenComp=htoxl(0);
h.fileLenDecomp=htoxl(0);
write(1, &h, sizeof(EspFsHeader));
}
int main(int argc, char **argv) {
int f, x;
char fileName[1024];
char *realName;
struct stat statBuf;
int serr;
int rate;
int err=0;
int compType; //default compression type - heatshrink
int compLvl=-1;
#ifdef ESPFS_HEATSHRINK
compType = COMPRESS_HEATSHRINK;
#else
compType = COMPRESS_NONE;
#endif
for (x=1; x<argc; x++) {
if (strcmp(argv[x], "-c")==0 && argc>=x-2) {
compType=atoi(argv[x+1]);
x++;
} else if (strcmp(argv[x], "-l")==0 && argc>=x-2) {
compLvl=atoi(argv[x+1]);
if (compLvl<1 || compLvl>9) err=1;
x++;
#ifdef ESPFS_GZIP
} else if (strcmp(argv[x], "-g")==0 && argc>=x-2) {
if (!parseGzipExtensions(argv[x+1])) err=1;
x++;
#endif
} else {
err=1;
}
}
#ifdef ESPFS_GZIP
if (gzipExtensions == NULL) {
parseGzipExtensions(strdup("html,css,js"));
}
#endif
if (err) {
fprintf(stderr, "%s - Program to create espfs images\n", argv[0]);
fprintf(stderr, "Usage: \nfind | %s [-c compressor] [-l compression_level] ", argv[0]);
#ifdef ESPFS_GZIP
fprintf(stderr, "[-g gzipped_extensions] ");
#endif
fprintf(stderr, "> out.espfs\n");
fprintf(stderr, "Compressors:\n");
#ifdef ESPFS_HEATSHRINK
fprintf(stderr, "0 - None\n1 - Heatshrink(default)\n");
#else
fprintf(stderr, "0 - None(default)\n");
#endif
fprintf(stderr, "\nCompression level: 1 is worst but low RAM usage, higher is better compression \nbut uses more ram on decompression. -1 = compressors default.\n");
#ifdef ESPFS_GZIP
fprintf(stderr, "\nGzipped extensions: list of comma separated, case sensitive file extensions \nthat will be gzipped. Defaults to 'html,css,js'\n");
#endif
exit(0);
}
while(fgets(fileName, sizeof(fileName), stdin)) {
//Kill off '\n' at the end
fileName[strlen(fileName)-1]=0;
//Only include files
serr=stat(fileName, &statBuf);
if ((serr==0) && S_ISREG(statBuf.st_mode)) {
//Strip off './' or '/' madness.
realName=fileName;
if (fileName[0]=='.') realName++;
if (realName[0]=='/') realName++;
f=open(fileName, O_RDONLY);
if (f>0) {
char *compName = "unknown";
rate=handleFile(f, realName, compType, compLvl, &compName);
fprintf(stderr, "%s (%d%%, %s)\n", realName, rate, compName);
close(f);
} else {
perror(fileName);
}
} else {
if (serr!=0) {
perror(fileName);
}
}
}
finishArchive();
return 0;
}

@ -1,22 +0,0 @@
#ifndef AUTH_H
#define AUTH_H
#include "httpd.h"
#ifndef HTTP_AUTH_REALM
#define HTTP_AUTH_REALM "Protected"
#endif
#define HTTPD_AUTH_SINGLE 0
#define HTTPD_AUTH_CALLBACK 1
#define AUTH_MAX_USER_LEN 32
#define AUTH_MAX_PASS_LEN 32
//Parameter given to authWhatever functions. This callback returns the usernames/passwords the device
//has.
typedef int (* AuthGetUserPw)(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen);
int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData);
#endif

@ -1,5 +0,0 @@
#ifndef CAPTDNS_H
#define CAPTDNS_H
void ICACHE_FLASH_ATTR captdnsInit(void);
#endif

@ -1,15 +0,0 @@
#ifndef CGIFLASH_H
#define CGIFLASH_H
#include "httpd.h"
typedef struct {
int espFsPos;
int espFsSize;
} CgiUploadEspfsParams;
int cgiReadFlash(HttpdConnData *connData);
int cgiUploadEspfs(HttpdConnData *connData);
#endif

@ -1,13 +0,0 @@
#ifndef CGIWIFI_H
#define CGIWIFI_H
#include "httpd.h"
int cgiWiFiScan(HttpdConnData *connData);
int tplWlan(HttpdConnData *connData, char *token, void **arg);
int cgiWiFi(HttpdConnData *connData);
int cgiWiFiConnect(HttpdConnData *connData);
int cgiWiFiSetMode(HttpdConnData *connData);
int cgiWiFiConnStatus(HttpdConnData *connData);
#endif

@ -1,18 +0,0 @@
// Combined include file for esp8266
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <c_types.h>
#include <ip_addr.h>
#include <espconn.h>
#include <ets_sys.h>
#include <gpio.h>
#include <mem.h>
#include <osapi.h>
#include <user_interface.h>
#include "espmissingincludes.h"

@ -1,23 +0,0 @@
#ifndef ESPFS_H
#define ESPFS_H
// This define is done in Makefile. If you do not use default Makefile, uncomment
// to be able to use Heatshrink-compressed espfs images.
//#define ESPFS_HEATSHRINK
typedef enum {
ESPFS_INIT_RESULT_OK,
ESPFS_INIT_RESULT_NO_IMAGE,
ESPFS_INIT_RESULT_BAD_ALIGN,
} EspFsInitResult;
typedef struct EspFsFile EspFsFile;
EspFsInitResult espFsInit(void *flashAddress);
EspFsFile *espFsOpen(char *fileName);
int espFsFlags(EspFsFile *fh);
int espFsRead(EspFsFile *fh, char *buff, int len);
void espFsClose(EspFsFile *fh);
#endif

@ -1,60 +0,0 @@
#ifndef ESPMISSINGINCLUDES_H
#define ESPMISSINGINCLUDES_H
#include <stdint.h>
#include <c_types.h>
#include <ets_sys.h>
#include <eagle_soc.h>
//Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere.
//MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler.
typedef struct espconn espconn;
int atoi(const char *nptr);
void ets_install_putc1(void *routine);
void ets_isr_attach(int intr, void *handler, void *arg);
void ets_isr_mask(unsigned intr);
void ets_isr_unmask(unsigned intr);
int ets_memcmp(const void *s1, const void *s2, size_t n);
void *ets_memcpy(void *dest, const void *src, size_t n);
void *ets_memset(void *s, int c, size_t n);
int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
int ets_str2macaddr(void *, void *);
int ets_strcmp(const char *s1, const char *s2);
char *ets_strcpy(char *dest, const char *src);
size_t ets_strlen(const char *s);
int ets_strncmp(const char *s1, const char *s2, int len);
char *ets_strncpy(char *dest, const char *src, size_t n);
char *ets_strstr(const char *haystack, const char *needle);
void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer);
void ets_timer_disarm(ETSTimer *a);
void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg);
void ets_update_cpu_frequency(int freqmhz);
int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void pvPortFree(void *ptr);
void *pvPortMalloc(size_t xWantedSize);
void *pvPortZalloc(size_t);
void uart_div_modify(int no, unsigned int freq);
void vPortFree(void *ptr);
void *vPortMalloc(size_t xWantedSize);
uint8 wifi_get_opmode(void);
uint32 system_get_time();
int rand(void);
void ets_bzero(void *s, size_t n);
void ets_delay_us(int ms);
//Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one.
#ifdef PIN_FUNC_SELECT
#undef PIN_FUNC_SELECT
#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \
WRITE_PERI_REG(PIN_NAME, \
(READ_PERI_REG(PIN_NAME) \
& (~(PERIPHS_IO_MUX_FUNC<<PERIPHS_IO_MUX_FUNC_S))) \
|( (((FUNC&BIT2)<<2)|(FUNC&0x3))<<PERIPHS_IO_MUX_FUNC_S) ); \
} while (0)
#endif
#endif

@ -1,67 +0,0 @@
#ifndef HTTPD_H
#define HTTPD_H
#define HTTPDVER "0.3"
#define HTTPD_CGI_MORE 0
#define HTTPD_CGI_DONE 1
#define HTTPD_CGI_NOTFOUND 2
#define HTTPD_CGI_AUTHENTICATED 3
#define HTTPD_METHOD_GET 1
#define HTTPD_METHOD_POST 2
typedef struct HttpdPriv HttpdPriv;
typedef struct HttpdConnData HttpdConnData;
typedef struct HttpdPostData HttpdPostData;
typedef int (* cgiSendCallback)(HttpdConnData *connData);
//A struct describing a http connection. This gets passed to cgi functions.
struct HttpdConnData {
struct espconn *conn;
char requestType;
char *url;
char *getArgs;
const void *cgiArg;
void *cgiData;
void *cgiPrivData; // Used for streaming handlers storing state between requests
char *hostName;
HttpdPriv *priv;
cgiSendCallback cgi;
HttpdPostData *post;
};
//A struct describing the POST data sent inside the http connection. This is used by the CGI functions
struct HttpdPostData {
int len; // POST Content-Length
int buffSize; // The maximum length of the post buffer
int buffLen; // The amount of bytes in the current post buffer
int received; // The total amount of bytes received so far
char *buff; // Actual POST data buffer
char *multipartBoundary;
};
//A struct describing an url. This is the main struct that's used to send different URL requests to
//different routines.
typedef struct {
const char *url;
cgiSendCallback cgiCb;
const void *cgiArg;
} HttpdBuiltInUrl;
int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiCheckHostname(HttpdConnData *connData);
void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl);
int httpdUrlDecode(char *val, int valLen, char *ret, int retLen);
int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen);
void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port);
const char *httpdGetMimetype(char *url);
void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code);
void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val);
void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn);
int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen);
int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len);
#endif

@ -1,9 +0,0 @@
#ifndef HTTPDESPFS_H
#define HTTPDESPFS_H
#include "httpd.h"
int cgiEspFsHook(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData);
#endif

@ -1,3 +0,0 @@
extern char webpages_espfs_start[];
extern char webpages_espfs_end[];
extern int webpages_espfs_size;

@ -1 +0,0 @@
Subproject commit 555f7cf0b0a508c2f804d4fdf6c1fd0d92f9a798

@ -1,252 +0,0 @@
#include <esp8266.h>
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
/*
This is a 'captive portal' DNS server: it basically replies with a fixed IP (in this case:
the one of the SoftAP interface of this ESP module) for any and all DNS queries. This can
be used to send mobile phones, tablets etc which connect to the ESP in AP mode directly to
the internal webserver.
*/
typedef struct __attribute__ ((packed)) {
uint16_t id;
uint8_t flags;
uint8_t rcode;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} DnsHeader;
typedef struct __attribute__ ((packed)) {
uint8_t len;
uint8_t data;
} DnsLabel;
typedef struct __attribute__ ((packed)) {
//before: label
uint16_t type;
uint16_t class;
} DnsQuestionFooter;
typedef struct __attribute__ ((packed)) {
//before: label
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t rdlength;
//after: rdata
} DnsResourceFooter;
typedef struct __attribute__ ((packed)) {
uint16_t prio;
uint16_t weight;
} DnsUriHdr;
#define FLAG_QR (1<<7)
#define FLAG_AA (1<<2)
#define FLAG_TC (1<<1)
#define FLAG_RD (1<<0)
#define QTYPE_A 1
#define QTYPE_NS 2
#define QTYPE_CNAME 5
#define QTYPE_SOA 6
#define QTYPE_WKS 11
#define QTYPE_PTR 12
#define QTYPE_HINFO 13
#define QTYPE_MINFO 14
#define QTYPE_MX 15
#define QTYPE_TXT 16
#define QTYPE_URI 256
#define QCLASS_IN 1
#define QCLASS_ANY 255
#define QCLASS_URI 256
//Function to put unaligned 16-bit network values
static void ICACHE_FLASH_ATTR setn16(void *pp, int16_t n) {
char *p=pp;
*p++=(n>>8);
*p++=(n&0xff);
}
//Function to put unaligned 32-bit network values
static void ICACHE_FLASH_ATTR setn32(void *pp, int32_t n) {
char *p=pp;
*p++=(n>>24)&0xff;
*p++=(n>>16)&0xff;
*p++=(n>>8)&0xff;
*p++=(n&0xff);
}
static uint16_t ICACHE_FLASH_ATTR ntohs(uint16_t *in) {
char *p=(char*)in;
return ((p[0]<<8)&0xff00)|(p[1]&0xff);
}
//Parses a label into a C-string containing a dotted
//Returns pointer to start of next fields in packet
static char* ICACHE_FLASH_ATTR labelToStr(char *packet, char *labelPtr, int packetSz, char *res, int resMaxLen) {
int i, j, k;
char *endPtr=NULL;
i=0;
do {
if ((*labelPtr&0xC0)==0) {
j=*labelPtr++; //skip past length
//Add separator period if there already is data in res
if (i<resMaxLen && i!=0) res[i++]='.';
//Copy label to res
for (k=0; k<j; k++) {
if ((labelPtr-packet)>packetSz) return NULL;
if (i<resMaxLen) res[i++]=*labelPtr++;
}
} else if ((*labelPtr&0xC0)==0xC0) {
//Compressed label pointer
endPtr=labelPtr+2;
int offset=ntohs(((uint16_t *)labelPtr))&0x3FFF;
//Check if offset points to somewhere outside of the packet
if (offset>packetSz) return NULL;
labelPtr=&packet[offset];
}
//check for out-of-bound-ness
if ((labelPtr-packet)>packetSz) return NULL;
} while (*labelPtr!=0);
res[i]=0; //zero-terminate
if (endPtr==NULL) endPtr=labelPtr+1;
return endPtr;
}
//Converts a dotted hostname to the weird label form dns uses.
static char ICACHE_FLASH_ATTR *strToLabel(char *str, char *label, int maxLen) {
char *len=label; //ptr to len byte
char *p=label+1; //ptr to next label byte to be written
while (1) {
if (*str=='.' || *str==0) {
*len=((p-len)-1); //write len of label bit
len=p; //pos of len for next part
p++; //data ptr is one past len
if (*str==0) break; //done
str++;
} else {
*p++=*str++; //copy byte
// if ((p-label)>maxLen) return NULL; //check out of bounds
}
}
*len=0;
return p; //ptr to first free byte in resp
}
//Receive a DNS packet and maybe send a response back
static void ICACHE_FLASH_ATTR captdnsRecv(void* arg, char *pusrdata, unsigned short length) {
struct espconn *conn=(struct espconn *)arg;
char buff[512];
char reply[512];
int i;
char *rend=&reply[length];
char *p=pusrdata;
DnsHeader *hdr=(DnsHeader*)p;
DnsHeader *rhdr=(DnsHeader*)&reply[0];
p+=sizeof(DnsHeader);
// os_printf("DNS packet: id 0x%X flags 0x%X rcode 0x%X qcnt %d ancnt %d nscount %d arcount %d len %d\n",
// ntohs(&hdr->id), hdr->flags, hdr->rcode, ntohs(&hdr->qdcount), ntohs(&hdr->ancount), ntohs(&hdr->nscount), ntohs(&hdr->arcount), length);
//Some sanity checks:
if (length>512) return; //Packet is longer than DNS implementation allows
if (length<sizeof(DnsHeader)) return; //Packet is too short
if (hdr->ancount || hdr->nscount || hdr->arcount) return; //this is a reply, don't know what to do with it
if (hdr->flags&FLAG_TC) return; //truncated, can't use this
//Reply is basically the request plus the needed data
os_memcpy(reply, pusrdata, length);
rhdr->flags|=FLAG_QR;
for (i=0; i<ntohs(&hdr->qdcount); i++) {
//Grab the labels in the q string
p=labelToStr(pusrdata, p, length, buff, sizeof(buff));
if (p==NULL) return;
DnsQuestionFooter *qf=(DnsQuestionFooter*)p;
p+=sizeof(DnsQuestionFooter);
os_printf("DNS: Q (type 0x%X class 0x%X) for %s\n", ntohs(&qf->type), ntohs(&qf->class), buff);
if (ntohs(&qf->type)==QTYPE_A) {
//They want to know the IPv4 address of something.
//Build the response.
rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
if (rend==NULL) return;
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
setn16(&rf->type, QTYPE_A);
setn16(&rf->class, QCLASS_IN);
setn32(&rf->ttl, 1);
setn16(&rf->rdlength, 4); //IPv4 addr is 4 bytes;
//Grab the current IP of the softap interface
struct ip_info info;
wifi_get_ip_info(SOFTAP_IF, &info);
*rend++=ip4_addr1(&info.ip);
*rend++=ip4_addr2(&info.ip);
*rend++=ip4_addr3(&info.ip);
*rend++=ip4_addr4(&info.ip);
setn16(&rhdr->ancount, ntohs(&rhdr->ancount)+1);
// os_printf("Added A rec to resp. Resp len is %d\n", (rend-reply));
} else if (ntohs(&qf->type)==QTYPE_NS) {
//Give ns server. Basically can be whatever we want because it'll get resolved to our IP later anyway.
rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
setn16(&rf->type, QTYPE_NS);
setn16(&rf->class, QCLASS_IN);
setn16(&rf->ttl, 1);
setn16(&rf->rdlength, 4);
*rend++=2;
*rend++='n';
*rend++='s';
*rend++=0;
setn16(&rhdr->ancount, ntohs(&rhdr->ancount)+1);
// os_printf("Added NS rec to resp. Resp len is %d\n", (rend-reply));
} else if (ntohs(&qf->type)==QTYPE_URI) {
//Give uri to us
rend=strToLabel(buff, rend, sizeof(reply)-(rend-reply)); //Add the label
DnsResourceFooter *rf=(DnsResourceFooter *)rend;
rend+=sizeof(DnsResourceFooter);
DnsUriHdr *uh=(DnsUriHdr *)rend;
rend+=sizeof(DnsUriHdr);
setn16(&rf->type, QTYPE_URI);
setn16(&rf->class, QCLASS_URI);
setn16(&rf->ttl, 1);
setn16(&rf->rdlength, 4+16);
setn16(&uh->prio, 10);
setn16(&uh->weight, 1);
memcpy(rend, "http://esp.local", 16);
rend+=16;
setn16(&rhdr->ancount, ntohs(&rhdr->ancount)+1);
// os_printf("Added NS rec to resp. Resp len is %d\n", (rend-reply));
}
}
//Send the response
espconn_sent(conn, (uint8*)reply, rend-reply);
}
void ICACHE_FLASH_ATTR captdnsInit(void) {
static struct espconn conn;
static esp_udp udpconn;
conn.type=ESPCONN_UDP;
conn.proto.udp=&udpconn;
conn.proto.udp->local_port = 53;
espconn_regist_recvcb(&conn, captdnsRecv);
espconn_create(&conn);
}

@ -1,77 +0,0 @@
/*
Some flash handling cgi routines. Used for reading the existing flash and updating the ESPFS image.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <esp8266.h>
#include "cgiflash.h"
#include "espfs.h"
//Cgi that reads the SPI flash. Assumes 512KByte flash.
int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) {
int *pos=(int *)&connData->cgiData;
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
if (*pos==0) {
os_printf("Start flash download.\n");
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/bin");
httpdEndHeaders(connData);
*pos=0x40200000;
return HTTPD_CGI_MORE;
}
//Send 1K of flash per call. We will get called again if we haven't sent 512K yet.
espconn_sent(connData->conn, (uint8 *)(*pos), 1024);
*pos+=1024;
if (*pos>=0x40200000+(512*1024)) return HTTPD_CGI_DONE; else return HTTPD_CGI_MORE;
}
//Cgi that allows the ESPFS image to be replaced via http POST
int ICACHE_FLASH_ATTR cgiUploadEspfs(HttpdConnData *connData) {
const CgiUploadEspfsParams *up=(CgiUploadEspfsParams*)connData->cgiArg;
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
if(connData->post->len > up->espFsSize){
// The uploaded file is too large
os_printf("ESPFS file too large\n");
httpdSend(connData, "HTTP/1.0 500 Internal Server Error\r\nServer: esp8266-httpd/0.3\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 24\r\n\r\nESPFS image loo large.\r\n", -1);
return HTTPD_CGI_DONE;
}
// The source should be 4byte aligned, so go ahead and flash whatever we have
int address = up->espFsPos + connData->post->received - connData->post->buffLen;
if(address % SPI_FLASH_SEC_SIZE == 0){
// We need to erase this block
os_printf("Erasing flash at %d\n", address/SPI_FLASH_SEC_SIZE);
spi_flash_erase_sector(address/SPI_FLASH_SEC_SIZE);
}
// Write the data
os_printf("Writing at: 0x%x\n", address);
spi_flash_write(address, (uint32 *)connData->post->buff, connData->post->buffLen);
os_printf("Wrote %d bytes (%dB of %d)\n", connData->post->buffSize, connData->post->received, connData->post->len);//&connData->postBuff));
if (connData->post->received == connData->post->len){
httpdSend(connData, "Finished uploading", -1);
return HTTPD_CGI_DONE;
} else {
return HTTPD_CGI_MORE;
}
}

@ -1,311 +0,0 @@
/*
Cgi/template routines for the /wifi url.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#include <esp8266.h>
#include "cgiwifi.h"
//Enable this to disallow any changes in AP settings
//#define DEMO_MODE
//WiFi access point data
typedef struct {
char ssid[32];
char rssi;
char enc;
} ApData;
//Scan result
typedef struct {
char scanInProgress; //if 1, don't access the underlying stuff from the webpage.
ApData **apData;
int noAps;
} ScanResultData;
//Static scan status storage.
static ScanResultData cgiWifiAps;
#define CONNTRY_IDLE 0
#define CONNTRY_WORKING 1
#define CONNTRY_SUCCESS 2
#define CONNTRY_FAIL 3
//Connection result var
static int connTryStatus=CONNTRY_IDLE;
static ETSTimer resetTimer;
//Callback the code calls when a wlan ap scan is done. Basically stores the result in
//the cgiWifiAps struct.
void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
int n;
struct bss_info *bss_link = (struct bss_info *)arg;
os_printf("wifiScanDoneCb %d\n", status);
if (status!=OK) {
cgiWifiAps.scanInProgress=0;
return;
}
//Clear prev ap data if needed.
if (cgiWifiAps.apData!=NULL) {
for (n=0; n<cgiWifiAps.noAps; n++) os_free(cgiWifiAps.apData[n]);
os_free(cgiWifiAps.apData);
}
//Count amount of access points found.
n=0;
while (bss_link != NULL) {
bss_link = bss_link->next.stqe_next;
n++;
}
//Allocate memory for access point data
cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n);
cgiWifiAps.noAps=n;
os_printf("Scan done: found %d APs\n", n);
//Copy access point data to the static struct
n=0;
bss_link = (struct bss_info *)arg;
while (bss_link != NULL) {
if (n>=cgiWifiAps.noAps) {
//This means the bss_link changed under our nose. Shouldn't happen!
//Break because otherwise we will write in unallocated memory.
os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps);
break;
}
//Save the ap data.
cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData));
cgiWifiAps.apData[n]->rssi=bss_link->rssi;
cgiWifiAps.apData[n]->enc=bss_link->authmode;
strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32);
bss_link = bss_link->next.stqe_next;
n++;
}
//We're done.
cgiWifiAps.scanInProgress=0;
}
//Routine to start a WiFi access point scan.
static void ICACHE_FLASH_ATTR wifiStartScan() {
// int x;
if (cgiWifiAps.scanInProgress) return;
cgiWifiAps.scanInProgress=1;
wifi_station_scan(NULL, wifiScanDoneCb);
}
//This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a
//scan for access points and if available will return the result of an earlier scan.
//The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl.
int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
int pos=(int)connData->cgiData;
int len;
char buff[1024];
if (!cgiWifiAps.scanInProgress && pos!=0) {
//Fill in json code for an access point
if (pos-1<cgiWifiAps.noAps) {
len=os_sprintf(buff, "{\"essid\": \"%s\", \"rssi\": \"%d\", \"enc\": \"%d\"}%s\n",
cgiWifiAps.apData[pos-1]->ssid, cgiWifiAps.apData[pos-1]->rssi,
cgiWifiAps.apData[pos-1]->enc, (pos-1==cgiWifiAps.noAps-1)?"":",");
httpdSend(connData, buff, len);
}
pos++;
if ((pos-1)>=cgiWifiAps.noAps) {
len=os_sprintf(buff, "]\n}\n}\n");
httpdSend(connData, buff, len);
//Also start a new scan.
wifiStartScan();
return HTTPD_CGI_DONE;
} else {
connData->cgiData=(void*)pos;
return HTTPD_CGI_MORE;
}
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/json");
httpdEndHeaders(connData);
if (cgiWifiAps.scanInProgress==1) {
//We're still scanning. Tell Javascript code that.
len=os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n");
httpdSend(connData, buff, len);
return HTTPD_CGI_DONE;
} else {
//We have a scan result. Pass it on.
len=os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"0\", \n\"APs\": [\n");
httpdSend(connData, buff, len);
if (cgiWifiAps.apData==NULL) cgiWifiAps.noAps=0;
connData->cgiData=(void *)1;
return HTTPD_CGI_MORE;
}
}
//Temp store for new ap info.
static struct station_config stconf;
//This routine is ran some time after a connection attempt to an access point. If
//the connect succeeds, this gets the module in STA-only mode.
static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
int x=wifi_station_get_connect_status();
if (x==STATION_GOT_IP) {
//Go to STA mode. This needs a reset, so do that.
os_printf("Got IP. Going into STA mode..\n");
wifi_set_opmode(1);
system_restart();
} else {
connTryStatus=CONNTRY_FAIL;
os_printf("Connect fail. Not going into STA-only mode.\n");
//Maybe also pass this through on the webpage?
}
}
//Actually connect to a station. This routine is timed because I had problems
//with immediate connections earlier. It probably was something else that caused it,
//but I can't be arsed to put the code back :P
static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) {
int x;
os_printf("Try to connect to AP....\n");
wifi_station_disconnect();
wifi_station_set_config(&stconf);
wifi_station_connect();
x=wifi_get_opmode();
connTryStatus=CONNTRY_WORKING;
if (x!=1) {
//Schedule disconnect/connect
os_timer_disarm(&resetTimer);
os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_arm(&resetTimer, 15000, 0); //time out after 15 secs of trying to connect
}
}
//This cgi uses the routines above to connect to a specific access point with the
//given ESSID using the given password.
int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
char essid[128];
char passwd[128];
static ETSTimer reassTimer;
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdFindArg(connData->post->buff, "essid", essid, sizeof(essid));
httpdFindArg(connData->post->buff, "passwd", passwd, sizeof(passwd));
os_strncpy((char*)stconf.ssid, essid, 32);
os_strncpy((char*)stconf.password, passwd, 64);
os_printf("Try to connect to AP %s pw %s\n", essid, passwd);
//Schedule disconnect/connect
os_timer_disarm(&reassTimer);
os_timer_setfn(&reassTimer, reassTimerCb, NULL);
//Set to 0 if you want to disable the actual reconnecting bit
#ifdef DEMO_MODE
httpdRedirect(connData, "/wifi");
#else
os_timer_arm(&reassTimer, 500, 0);
httpdRedirect(connData, "connecting.html");
#endif
return HTTPD_CGI_DONE;
}
//This cgi uses the routines above to connect to a specific access point with the
//given ESSID using the given password.
int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
int len;
char buff[1024];
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff));
if (len!=0) {
os_printf("cgiWifiSetMode: %s\n", buff);
#ifndef DEMO_MODE
wifi_set_opmode(atoi(buff));
system_restart();
#endif
}
httpdRedirect(connData, "/wifi");
return HTTPD_CGI_DONE;
}
int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
char buff[1024];
int len;
struct ip_info info;
int st=wifi_station_get_connect_status();
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/json");
httpdEndHeaders(connData);
if (connTryStatus==CONNTRY_IDLE) {
len=os_sprintf(buff, "{\n \"status\": \"idle\"\n }\n");
} else if (connTryStatus==CONNTRY_WORKING || connTryStatus==CONNTRY_SUCCESS) {
if (st==STATION_GOT_IP) {
wifi_get_ip_info(0, &info);
len=os_sprintf(buff, "{\n \"status\": \"success\",\n \"ip\": \"%d.%d.%d.%d\" }\n",
(info.ip.addr>>0)&0xff, (info.ip.addr>>8)&0xff,
(info.ip.addr>>16)&0xff, (info.ip.addr>>24)&0xff);
//Reset into AP-only mode sooner.
os_timer_disarm(&resetTimer);
os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_arm(&resetTimer, 1000, 0);
} else {
len=os_sprintf(buff, "{\n \"status\": \"working\"\n }\n");
}
} else {
len=os_sprintf(buff, "{\n \"status\": \"fail\"\n }\n");
}
httpdSend(connData, buff, len);
return HTTPD_CGI_DONE;
}
//Template code for the WLAN page.
int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) {
char buff[1024];
int x;
static struct station_config stconf;
if (token==NULL) return HTTPD_CGI_DONE;
wifi_station_get_config(&stconf);
os_strcpy(buff, "Unknown");
if (os_strcmp(token, "WiFiMode")==0) {
x=wifi_get_opmode();
if (x==1) os_strcpy(buff, "Client");
if (x==2) os_strcpy(buff, "SoftAP");
if (x==3) os_strcpy(buff, "STA+AP");
} else if (os_strcmp(token, "currSsid")==0) {
os_strcpy(buff, (char*)stconf.ssid);
} else if (os_strcmp(token, "WiFiPasswd")==0) {
os_strcpy(buff, (char*)stconf.password);
} else if (os_strcmp(token, "WiFiapwarn")==0) {
x=wifi_get_opmode();
if (x==2) {
os_strcpy(buff, "<b>Can't scan in this mode.</b> Click <a href=\"setmode.cgi?mode=3\">here</a> to go to STA+AP mode.");
} else {
os_strcpy(buff, "Click <a href=\"setmode.cgi?mode=2\">here</a> to go to standalone AP mode.");
}
}
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
Loading…
Cancel
Save