//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; httpd_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++) free(cgiWifiAps.apData[n]); 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 **)malloc(sizeof(ApData *)*n); if (cgiWifiAps.apData==NULL) { printf("Out of memory allocating apData\n"); return; } cgiWifiAps.noAps=n; httpd_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. httpd_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); break; } //Save the ap data. cgiWifiAps.apData[n]=(ApData *)malloc(sizeof(ApData)); if (cgiWifiAps.apData[n]==NULL) { httpd_printf("Can't allocate mem for ap buff.\n"); cgiWifiAps.scanInProgress=0; return; } cgiWifiAps.apData[n]->rssi=bss_link->rssi; cgiWifiAps.apData[n]->channel=bss_link->channel; cgiWifiAps.apData[n]->enc=bss_link->authmode; strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); strncpy(cgiWifiAps.apData[n]->bssid, (char*)bss_link->bssid, 6); bss_link = bss_link->next.stqe_next; n++; } //We're done. cgiWifiAps.scanInProgress=0; }
//Open a file and return a pointer to the file desc struct. EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { if (espFsData == NULL) { httpd_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. spi_flash_read((uint32)p, (uint32*)&h, sizeof(EspFsHeader)); if (h.magic!=ESPFS_MAGIC) { httpd_printf("Magic mismatch. EspFS image broken.\n"); return NULL; } if (h.flags&FLAG_LASTFILE) { httpd_printf("End of image.\n"); return NULL; } //Grab the name of the file. p+=sizeof(EspFsHeader); spi_flash_read((uint32)p, (uint32*)&namebuf, sizeof(namebuf)); // httpd_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 (strcmp(namebuf, fileName)==0) { //Yay, this is the file we need! p+=h.nameLen; //Skip to content. r=(EspFsFile *)malloc(sizeof(EspFsFile)); //Alloc file desc mem // httpd_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. readFlashUnaligned(&parm, r->posComp, 1); r->posComp++; httpd_printf("Heatshrink compressed file; decode parms = %x\n", parm); dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); r->decompData=dec; #endif } else {
//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. httpd_printf("Got IP. Going into STA mode..\n"); wifi_set_opmode(1); system_restart(); } else { connTryStatus=CONNTRY_FAIL; httpd_printf("Connect fail. Not going into STA-only mode.\n"); //Maybe also pass this through on the webpage? } }
static int onet_httpd_handler_cb(char *path, httpd_var_t *httpd_var, GByteArray *buf ) { onet_t *onet = onet_main; GList *elem; httpd_printf_page_title(buf, "NeoIp Router" ); httpd_printf(buf,"<b>Local ip address:</b> %s<br>", ip_addr_str(&onet->ip_iaddr) ); httpd_printf(buf,"<b>Local ip netmask:</b> %s<br>", ip_netmask_str(&onet->ip_netmask) ); httpd_printf(buf,"<b>Local cnxid:</b> %s<br>", nipid_str(&onet->local_cnxid) ); httpd_printf(buf,"<b>virtual network device:</b> %s<br>", onet->vdev.dev_name ); httpd_printf(buf,"<hr><h3><div align=\"center\">Tunnel List:</div></h3>" ); httpd_printf_table_start(buf); httpd_printf_tr_start(buf); httpd_printf_th(buf, "remote iaddr"); httpd_printf_th(buf, "state"); httpd_printf_th(buf, "remote identity"); httpd_printf_th(buf, "info on connection"); httpd_printf_th_title(buf, "number of packets per sec estimated of the last 5-sec", "packet rate"); httpd_printf_th_title(buf, "number of kbyte per sec estimated of the last 5-sec", "throughput"); httpd_printf_tr_end(buf); for( elem = onet->tunnel_list; elem; elem = g_list_next( elem ) ){ onet_tunnel_t *tunnel = elem->data; httpd_printf_tr_start(buf); if( tunnel->itor ){ httpd_printf_td(buf, "%s", ip_addr_str(&tunnel->remote_iaddr)); httpd_printf_td(buf, "Initiating"); httpd_printf_td(buf, "none yet"); httpd_printf_td(buf, "<a href=\"%s\" title=\"Provide details on this connection's initiator\">X</a>" , itor_httpd_get_link(tunnel->itor, "disp_single")); }else if( tunnel->resp_iaddr_req ){ httpd_printf_td(buf, "none yet"); httpd_printf_td(buf, "Responding"); httpd_printf_td(buf, "none yet"); httpd_printf_td(buf, "none yet"); }else{ httpd_printf_td(buf, "%s", ip_addr_str(&tunnel->remote_iaddr)); httpd_printf_td(buf, "Established"); DBG_ASSERT( tunnel->stun ); httpd_printf_td(buf, "%s", stun_get_remote_identity(tunnel->stun) ); httpd_printf_td(buf, "<a href=\"%s\" title=\"Provide details on this established connection\">X</a>" , stun_httpd_get_link(tunnel->stun, "disp_single")); } httpd_printf_td(buf, "%.2lf pkt/sec", rate_estim_get_avg_delay(tunnel->pkt_rate, 5*1000, 1000) ); httpd_printf_td(buf, "%.2lf kbyte/sec", rate_estim_get_avg_delay(tunnel->throughput, 5*1000, 1000)/1024 ); httpd_printf_tr_end(buf); } httpd_printf_table_end(buf); return 0; }
//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 os_timer_t 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)); strncpy((char*)stconf.ssid, essid, 32); strncpy((char*)stconf.password, passwd, 64); httpd_printf("Try to connect to AP %s pw %s\n", essid, passwd); connTryStatus=CONNTRY_IDLE; //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) { #ifndef DEMO_MODE if (os_strcmp(buff, "STA") == 0) newModeData.newMode = STATION_MODE; else if (os_strcmp(buff, "AP") == 0) newModeData.newMode = SOFTAP_MODE; else if (os_strcmp(buff, "STA+AP") == 0) newModeData.newMode = STATIONAP_MODE; else newModeData.newMode = atoi(buff); httpd_printf("cgiWiFiSetMode: '%s' (%d)\n", buff, newModeData.newMode); newModeData.inProgress = 1; newModeData.needScan = 0; os_timer_disarm(&newModeData.timer); os_timer_setfn(&newModeData.timer, setModeCb, NULL); os_timer_arm(&newModeData.timer, 1000, 0); #endif } httpdRedirect(connData, "/wifi"); return HTTPD_CGI_DONE; }
//Set wifi channel for AP mode CgiStatus ICACHE_FLASH_ATTR cgiWiFiSetChannel(HttpdConnData *connData) { int len; char buff[64]; if (connData->isConnectionClosed) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; } len=httpdFindArg(connData->getArgs, "ch", buff, sizeof(buff)); if (len!=0) { int channel = atoi(buff); if (channel > 0 && channel < 15) { httpd_printf("cgiWifiSetChannel: %s\n", buff); struct softap_config wificfg; wifi_softap_get_config(&wificfg); wificfg.channel = (uint8)channel; wifi_softap_set_config(&wificfg); } } httpdRedirect(connData, "/wifi"); return HTTPD_CGI_DONE; }
// Returns flags of opened file. int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { if (fh == NULL) { httpd_printf("File handle not ready\n"); return -1; } int8_t flags; readFlashUnaligned((char*)&flags, (char*)&fh->header->flags, 1); return (int)flags; }
//Routine to start a WiFi access point scan. static void ICACHE_FLASH_ATTR wifiStartScan() { // int x; if (cgiWifiAps.scanInProgress) return; if (newModeData.inProgress) { if (newModeData.newMode == STATIONAP_MODE) newModeData.needScan = 1; else httpd_printf("Must be in STA+AP mode to start AP scan: mode=%d\n", newModeData.newMode); } else { if (wifi_get_opmode() == STATIONAP_MODE) { httpd_printf("Starting scan...\n"); wifi_station_scan(NULL, wifiScanDoneCb); cgiWifiAps.scanInProgress=1; } else httpd_printf("Must be in STA+AP mode to start AP scan: mode=%d\n", wifi_get_opmode()); } }
static void ICACHE_FLASH_ATTR setModeCb(void *arg) { if (!newModeData.inProgress) return; switch (newModeData.newMode) { case STATION_MODE: case SOFTAP_MODE: case STATIONAP_MODE: httpd_printf("setModeCb: %d\n", newModeData.newMode); wifi_set_opmode(newModeData.newMode); break; default: httpd_printf("setModeCb: invalid mode %d\n", newModeData.newMode); break; } if (newModeData.needScan) { httpd_printf("Starting deferred scan...\n"); wifi_station_scan(NULL, wifiScanDoneCb); cgiWifiAps.scanInProgress=1; } newModeData.inProgress = 0; }
//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) { httpd_printf("Got IP address.\n"); #ifdef SWITCH_TO_STA_MODE_AFTER_CONNECT //Go to STA mode. This needs a reset, so do that. if (x!=STATION_MODE) { httpd_printf("Going into STA mode..\n"); wifi_set_opmode(STATION_MODE); } #endif } else { connTryStatus=CONNTRY_FAIL; #ifdef SWITCH_TO_STA_MODE_AFTER_CONNECT httpd_printf("Connect failed. Not going into STA-only mode.\n"); //Maybe also pass this through on the webpage? #else httpd_printf("Connect failed.\n"); #endif } }
//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; httpd_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 } }
int ICACHE_FLASH_ATTR wifiJoin(char *ssid, char *passwd) { static os_timer_t reassTimer; strncpy((char*)stconf.ssid, ssid, 32); strncpy((char*)stconf.password, passwd, 64); httpd_printf("Try to connect to AP %s pw %s\n", ssid, passwd); connTryStatus=CONNTRY_IDLE; //Schedule disconnect/connect os_timer_disarm(&reassTimer); os_timer_setfn(&reassTimer, reassTimerCb, NULL); os_timer_arm(&reassTimer, 500, 0); return 0; }
static stream_t *webapi_subscribe_response(ebb_connection_info *conninfo, const char *topic) { stream_t *stream = NULL; if (NULL == (stream = stream_create(topic))) { LOG_ERROR("failed to create stream for topic patt %s", topic); conninfo->http_status = 503; } else { char seckey_str[STREAM_SECKEY_SIZE * 2 + 1]; // hex print int i; if (0 != streamlist_insert(stream->streamid, stream)) { LOG_ERROR("streamlist_insert failed"); conninfo->http_status = 503; } // print hex string for (i=0;i<STREAM_SECKEY_SIZE;i++) sprintf(seckey_str+i*2, "%02X", stream->seckey[i]); if (!conninfo->rawmode) { httpd_printf(conninfo->connid, "{\"url\":\"/stream/%u\",\"id\":%u,\"seckey\":\"%s\"}", stream->streamid, stream->streamid, seckey_str); } else { // attach conn to stream stream_set_connection(stream, conninfo->connid); idset_insert(conninfo->streamids, stream->streamid); stream_refresh_timer(stream); // stop from timing out } } if (!conninfo->rawmode) httpd_close(conninfo->connid); return stream; }
//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 noAps, n, i; struct bss_info *bss_link = (struct bss_info *)arg; httpd_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++) free(cgiWifiAps.apData[n]); free(cgiWifiAps.apData); } //Count amount of access points found. noAps=0; while (bss_link != NULL) { bss_link = bss_link->next.stqe_next; noAps++; } //Allocate memory for access point data cgiWifiAps.apData=(ApData **)malloc(sizeof(ApData *)*noAps); memset(cgiWifiAps.apData, 0, sizeof(ApData *)*noAps); //Copy access point data to the static struct n=0; bss_link = (struct bss_info *)arg; while (bss_link != NULL) { if (n>=noAps) { //This means the bss_link changed under our nose. Shouldn't happen! //Break because otherwise we will write in unallocated memory. httpd_printf("Huh? I have more than the allocated %d aps!\n", noAps); break; } // check for duplicate SSIDs and keep the one with the strongest signal for (i = 0; i < n; ++i) { if (strncmp((char*)bss_link->ssid, cgiWifiAps.apData[i]->ssid, 32) == 0) { if (bss_link->rssi > cgiWifiAps.apData[i]->rssi) { cgiWifiAps.apData[i]->rssi=bss_link->rssi; cgiWifiAps.apData[i]->channel=bss_link->channel; cgiWifiAps.apData[i]->enc=bss_link->authmode; strncpy(cgiWifiAps.apData[i]->bssid, (char*)bss_link->bssid, 6); } break; } } //Save the ap data. if (i >= n) { cgiWifiAps.apData[n]=(ApData *)malloc(sizeof(ApData)); cgiWifiAps.apData[n]->rssi=bss_link->rssi; cgiWifiAps.apData[n]->channel=bss_link->channel; cgiWifiAps.apData[n]->enc=bss_link->authmode; strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); strncpy(cgiWifiAps.apData[n]->bssid, (char*)bss_link->bssid, 6); n++; } bss_link = bss_link->next.stqe_next; } cgiWifiAps.noAps = n; //We're done. httpd_printf("Scan done: found %d APs\n", cgiWifiAps.noAps); if (scanCallback) { (*scanCallback)(scanCallbackData, cgiWifiAps.noAps); scanCallback = NULL; } cgiWifiAps.scanInProgress=0; }
static void stream_drainer(stream_t *stream, const char *str, bool first, void *userdata) { uint32_t connid = (uint32_t)(uintptr_t)userdata; LOG_DEBUG("drainer: %s first=%d", str, first); httpd_printf(connid, "%s%s", first ? "" : ",", str); }
bool message_to_stream(uint32_t streamid, stream_t *stream, void *userdata) { struct topic_message *tm = (struct topic_message *)userdata; char *s = NULL; LOG_DEBUG("rx check: %s %s", stream->topic_patt, tm->topic); if (topic_match_string(stream->topic_patt, tm->topic)) { size_t len; LOG_DEBUG("%s:%s -> stream %u", tm->topic, tm->msg, streamid); len = strlen(tm->topic)*2 + strlen(tm->msg)*2 + 8; // {"":""} + null // FIXME if (NULL == (s = (char *)malloc(len))) LOG_ERROR("out of mem"); else { uint32_t connid; ebb_connection_info *conninfo = NULL; bool connected; connected = stream_get_connection(stream, &connid); if (connected) { if (NULL == (conninfo = httpd_get_conninfo(connid))) { LOG_ERROR("bad connid"); return false; } if (conninfo->finished) // if conn is in process of closing, connected = false; } if (conninfo && conninfo->rawmode) { // just the data memcpy(s, tm->msg, strlen(tm->msg)+1); } else { // wrapped in JSON obj cJSON *mj = NULL; char *msgesc; char *topicesc; if (NULL == (topicesc = json_escape(tm->topic))) { conninfo->http_status = 503; httpd_close(connid); return false; } if (tm->msg[0] == 0) { snprintf(s, len, "{%s:\"\"}", topicesc); } else if (0==strcmp(tm->msg, "inf")) { snprintf(s, len, "{%s:\"inf\"}", topicesc); } else if (NULL != (mj = cJSON_Parse(tm->msg))) { snprintf(s, len, "{%s:%s}", topicesc, cJSON_PrintUnformatted(mj)); cJSON_Delete(mj); } else { if (NULL == (msgesc = json_escape(tm->msg))) { conninfo->http_status = 503; httpd_close(connid); free(topicesc); return false; } else { snprintf(s, len, "{%s:%s}", topicesc, msgesc); free(msgesc); } } free(topicesc); } if (0 != stream_push(stream, s)) LOG_ERROR("stream_push %u failed", streamid); else { if (connected) { if (!conninfo->rawmode) httpd_printf(connid, "["); stream_drain(stream, stream_drainer, (void *)(uintptr_t)connid); if (!conninfo->rawmode) httpd_printf(connid, "]"); stream_clear_connection(stream); httpd_close(connid); } else { LOG_DEBUG("streamid %u not connected", stream->streamid); } } } } if (NULL != s) free(s); return false; }
static int webapi_stream(uint32_t connid, uint32_t method, int argc, char **argv, const char *body, const char *reqpath, const char *uri_str, const char *auth) { ebb_connection_info *conninfo; if (NULL == (conninfo = httpd_get_conninfo(connid))) { LOG_ERROR("bad connid"); return 1; } if (method == EBB_POST && argc == 0) { uint32_t streamids[MAX_STREAMS]; stream_t *streams[MAX_STREAMS]; uint8_t seckeys[MAX_STREAMS][STREAM_SECKEY_SIZE]; size_t num_streams; size_t i; bool close_conn = false; if (0 != json_parse_array_uint32_seckey(body, streamids, seckeys, &num_streams, MAX_STREAMS) || num_streams == 0) { conninfo->http_status = 400; // malformed return 1; } for (i=0;i<num_streams;i++) // reject request if any of the streams don't exist or wrong seckey { //LOG_INFO("webapi_stream %u for connid=%u", streamids[i], connid); if (NULL == (streams[i] = streamlist_find(streamids[i]))) { LOG_DEBUG("no such stream %u", streamids[i]); conninfo->http_status = 404; // no such return 1; } else { if (0!=memcmp(seckeys[i], streams[i]->seckey, STREAM_SECKEY_SIZE)) { LOG_INFO("invalid seckey for streamid %u", streamids[i]); conninfo->http_status = 403; // verboten return 1; } LOG_DEBUG("adding stream %u", streamids[i]); } } for (i=0;i<num_streams;i++) { if (!stream_isempty(streams[i])) { if (close_conn) httpd_printf(connid, "\r\n"); // FIXME, should this be a single JSON doc? httpd_printf(connid, "["); stream_drain(streams[i], stream_drainer, (void *)(uintptr_t)connid); httpd_printf(connid, "]"); close_conn = true; // defer close till all available streams have been drained } else { uint32_t old_connid; if (stream_get_connection(streams[i], &old_connid)) { ebb_connection_info *old_conninfo; LOG_DEBUG("closing old_connid %u replacing with %u for stream %u", old_connid, connid, streamids[i]); if (NULL != (old_conninfo = httpd_get_conninfo(old_connid))) idset_foreach(old_conninfo->streamids, strm_clr, NULL); httpd_close(old_connid); } stream_set_connection(streams[i], connid); // associate the stream to this connection if (0 != idset_insert(conninfo->streamids, streamids[i])) { conninfo->http_status = 503; return 1; } stream_refresh_timer(streams[i]); // stop from timing out } } if (close_conn) { LOG_DEBUG("closing"); httpd_close(connid); } return 0; } else { conninfo->http_status = 405; // method not allowed return 1; } return 0; }