Example #1
0
//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;
}
Example #2
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 {
Example #3
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) {
		//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?
	}
}
Example #4
0
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;
}
Example #5
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;
}
Example #6
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 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;
}
Example #7
0
//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;
}
Example #8
0
// 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;
}
Example #9
0
//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());
    }
}
Example #10
0
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;
}
Example #11
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
	}
}
Example #12
0
//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
	}
}
Example #13
0
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;
}
Example #15
0
//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;
}