Firenet

Artifact [29c1a6b680]
Login

Artifact [29c1a6b680]

Artifact 29c1a6b680f595fde3b0125dba0f08456c7bb106:


//*****************************************************************************
/*
	Copyright © 2010 Jim Schimpf. Permission is hereby granted,
	free of charge, to any person obtaining a copy of this software and
	associated documentation files (the "Software"), to deal in the Software
	without restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell copies
	of the Software, and to permit persons to whom the Software is furnished
	to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included
	in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//*****************************************************************************
/*
	Module Name:         HTTP interface for Firenet

	Description:        This uses libmicrohttpd to build a RESTful
						interface to Firenet.  

	Library Extensions	rest_start()			- Start HTTP interface
						rest_stop()				- Stop HTTP interface
						rest_close()			- Close current connection & return data
						rest_url()				- Extract URL from current connection
						rest_data()				- Get data from current connection
						rest_client_lock()		- Lock server to current client
						
						
	Revision History:	
		24 Oct 2010	Initial version
		20 Nov 2010 UC both http & rest base
		16 Dec 2010	Add client lock to lock the webserver to one client
		 7-Mar-2011 Dropping POST data reader, as post data does not work...
		23-Dec-2011 [7b5a95ed75] Update to get rest  base 
					size in structure
		29-Jan-2012 [28bcb55998] Change Minnow so that it can work as a regular
					web server
					
*/
//****************************************************************************

#include <ctype.h>
#include <time.h>
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"
#include "lstate.h"
#include "lapi.h"
#include "qdj.h"
#include "rest.h"
#include "restclient.h"

#if 0
	#pragma mark -
	#pragma mark DATA
#endif

#define RESTFUL_HANDLE_VALID	'rest'
typedef struct rest_dummy 
{
	unsigned long valid;
	RESTQEL *rdata;
} RESTFUL_HANDLE;

static int rest_start( lua_State *L );
static int rest_stop( lua_State *L );
static int rest_open(lua_State *L);
static int rest_close(lua_State *L);
static int rest_url(lua_State *L);
static int rest_http_data(lua_State *L);
static int rest_client_lock(lua_State *L);

// Support

static int get_http_base( RSERV_DATA *rsrv );
static RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L );
static void uppercase_it(char *ptr );

#if 0
	#pragma mark -
	#pragma mark Lua API
#endif

static struct luaL_reg httplib_f[] = {
	{"start",rest_start},
	{"stop",rest_stop},  
	{"open",rest_open},
	{NULL,NULL}
};

static struct luaL_reg httplib_m[] = {	
	{"close",rest_close},
  	{"url",rest_url},
//	{"data",rest_http_data},		// TBD
	{"lock",rest_client_lock},
	{NULL,NULL}
};

#if 0
	#pragma mark --
	#pragma mark -- LUA SUPPORT --
#endif

/*******************************************************************************
*
*  luaopen_parselib	- Attach HTTP class to LUA
*
*	INPUT:	L	- Lua state
*
*	OUTPUT:	1	- Stacked HTTP library table
*
*********************************************************************************/
LUALIB_API int luaopen_httplib (lua_State *L) 
{			
	// (1) Create the class Metatable
	
	luaL_newmetatable(L,"PP.http");
	
	// (2) Put the __index element with the metatable in the
	// metatable
	
	lua_pushstring(L,"__index");
	lua_pushvalue(L,-2);				// Put the meta table (1) on stack
	lua_settable(L,-3);					// Add it to the metatable

	// (3) Add the method table to the stack
	
	luaL_openlib(L,NULL,httplib_m,0);
	
	// (3) Add the class table to the system & return that it's on the
	// stack
	
	luaL_openlib(L, "http", httplib_f, 0);
	return 1;
}

/*******************************************************************************
*
*  RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L )	- Check TOS for RESTFUL handle
*
*	INPUT:	NONE
*			Examine TOS
*
*	OUTPUT:	Pointer to RESTFUL_HANDLE object if it was TOS
*			NULL if not
*
*********************************************************************************/

static RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L )
{
	RESTFUL_HANDLE *h;
	
	// (1) Check the TOS to see if we have TG.limo metatable
	
	h = (RESTFUL_HANDLE *)luaL_checkudata(L,1,"PP.http");
	luaL_argcheck( L,(h != NULL),1,"Restful object expected");
	
	return( h );
}

#if 0
	#pragma mark -
	#pragma mark CREATE/DELETE API
#endif

//***************************************************************************
/*
*  rest_start( http_port,http_base,http_site,rest_base)
*
*	INPUT:	http_port	- # Value, port used for HTTP transactions
*			http_base	- ABSOLUTE loccation of site files (HTML file root)
*			rest_base	- RESTful tree root
*
*	OUTPUT:	1 if started nil if not 
*
*/
//***************************************************************************/

static int rest_start( lua_State *L )
{
	size_t llen;
	double dval;
	char *ptr;
	RSERV_DATA *rsrv = rest_data();
	int rtnval = 0;
	
	// (1) Pull in the values 1 at a time
	// PORT, BASE then Restful base
	
	if( lua_isnumber(L,1))
	{
		dval = lua_tonumber(L, 1);
		rsrv->http_port = (int)dval;
		
		// (2) Get the path to the files
		
		if(  lua_isstring(L,2 ) )
		{
			ptr = (char *)luaL_checklstring(L,2,&llen);
			rsrv->http_site = (char *)MEMALLOC(llen+1);
			if( rsrv->http_site != NULL )
			{
				// 3 Copy and set up http_base value
				// if OK continue
				
				memcpy(rsrv->http_site,ptr,llen);
				if( get_http_base(rsrv) )
				{
					uppercase_it(rsrv->http_base);

					// (3) Get the path to the RESTful location
					// if present
					
					if(  lua_isstring(L,3 ) )
					{
						ptr = (char *)luaL_checklstring(L,3,&llen);
						rsrv->rest_base = (char *)MEMALLOC(llen+1);
						rsrv->len_rest_base = llen;
						if( rsrv->rest_base != NULL )
						{
							memcpy(rsrv->rest_base,ptr,llen);
							uppercase_it(rsrv->rest_base);
						}
						else
							rsrv->len_rest_base = 0;	// No rest base
					}
					else
					{
						// (3a) There is no REST input so handle that
							
						rsrv->len_rest_base = 0;
						rsrv->rest_base = NULL;
					}
							
					// (5) Start server
								
					if( rest_server() == 0 )
					{
						// (6) Start client
						
						if( rest_support_open() == 0 )
						{
							dval = 1.0;
							lua_pushnumber( L,dval );	// Return true
							rtnval = 1;
						}
					}
				}
			}
		}
	}
	
	// (7) If rtnval == 0 call close to shut things down
	
	if( rtnval == 0 )
		rest_stop(L);

	return( rtnval );
}
//***************************************************************************
/*
*  rest_stop( )
*
*	INPUT:	NONE
*
*	OUTPUT:	Stop HTTP server
*/
//***************************************************************************/

static int rest_stop( lua_State *L )
{
	RSERV_DATA *rsrv = rest_data();
	
	// (1) Close com channel and stop server
	
	rest_support_close();	
	rsrv->run_flag = 0;
	rest_server_close();
	usleep( 5000000 );	// Wait 5 seconds
	
	// (2) Delete data
	
	if( rsrv->http_base != NULL )
	{
		MEMFREE( rsrv->http_base );
		rsrv->http_base = NULL;
	}
	
	if( rsrv->http_site != NULL )
	{
		MEMFREE( rsrv->http_site );
		rsrv->http_site = NULL;
	}
	
	if( rsrv->rest_base != NULL )
	{
		MEMFREE( rsrv->rest_base );
		rsrv->rest_base = NULL;
	}
	
	return( 0 );
}

#if 0
	#pragma mark -
	#pragma mark I/O API
#endif

//***************************************************************************
/*
*  http.open()	- NON-Blocking receive
*
*	INPUT:	NONE
*
*	OUTPUT:	NIL if no data, REST structure if receive
*
*/
//***************************************************************************/

static int rest_open(lua_State *L)
{
	int rtnval = 0;
	RESTQEL *packet;
	RESTFUL_HANDLE *h;
	
	// (1) Did we get anything ?
	
	packet = rest_support_receive();
	if( packet != NULL )
	{
		// (2) Build a lua user structure and return this to
		// the user
		
		h = (RESTFUL_HANDLE *)lua_newuserdata(L,sizeof(RESTFUL_HANDLE));
		if( h != NULL )
		{
			h->valid = RESTFUL_HANDLE_VALID;
			h->rdata = packet;
			
			// (2a) Return handle to user, attach metatable to mark this
			// as part of http class
			
			luaL_getmetatable(L,"PP.http");
			lua_setmetatable(L, -2);
			rtnval = 1;
		}
	}
	
	return rtnval;
}

//***************************************************************************
/*
*  http.close(h,data)	-	Close http transaction and return data
*
*	INPUT:	data	- Returned data for http I/O
*
*	OUTPUT:	NONE
*
*/
//***************************************************************************/

static int rest_close(lua_State *L)
{
	RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L);
	size_t llen = 0;
	char *ptr = NULL;
	char *send_data;
	
	if( h != NULL )
	{
		// (2) Check for data
		
		if( lua_isstring(L,2) )
		{
			ptr = (char *)luaL_checklstring(L, 2, &llen);
		}
		else
		{
			llen = 0;
			ptr = "";
		}
		
		// (3) We must locally allocate the data
		// to make MHD happy as it will deallocate
		// this copy of the data
		
		send_data = (char *)MEMALLOC( llen );
		if( send_data != NULL )
		{
			memcpy(send_data,ptr,llen);
		
			// (4) Now send the data off to the server
		
			rest_support_send((int)llen,send_data,h->rdata);
		}
	}
		
	return(0);
}

#if 0
	#pragma mark -
	#pragma mark HTTP DATA API
#endif

//***************************************************************************
/*
*  h:url()	- Return requested URL from restful handle
*
*	INPUT:	h	- Restful handle
*
*	OUTPUT:	Lua numeric list with URL elements starting at REST start = [1]
*			Followed by a number for the HTTP type of request
*			See HTTP_TYPE in rest.h
*
*	Uppercase all URL elements
*
*/
//***************************************************************************/

static int rest_url(lua_State *L)
{
	int rtnval = 0;
	RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L);
	int i,n;
	int count = 1;
	double dval;
	char *ptr;
	char *url;
	int list;
	
	// (1) If we have a handle then proceed
	
	if( h != NULL )
	{
		// (2) Create a table on the stack and set for return
		
		lua_newtable(L);
		
		// (3) Start the work on the URL, 
		
		n = strlen(h->rdata->url);
		url = (char *) MEMALLOC(n+1);
		if( url != NULL )
		{
			memcpy(url,h->rdata->url,n);
			ptr = url;
			
			for( i=0; i<n; i++ )
			{
				if( url[i] == '/' )
				{
					// We have a path element, so NULL out the 
					// the / and put the path onto the table
					
					url[i] = '\0';
					if( strlen( ptr ) > 0 )
					{
						list = lua_gettop(L);
						dval = (double)count++;					// Index
						lua_pushnumber(L, dval);
						uppercase_it( ptr );					// Upper case name
						lua_pushstring(L, (const char *)ptr);	// String value
						lua_rawset(L,list);						// Add to table
					}
					ptr = (char *)&url[i+1];
				}
			}
			
			// Add in last path element if present
			
			if( ptr != NULL && strlen(ptr) != 0 )
			{
				list = lua_gettop(L);
				dval = (double)count++;					// Index
				lua_pushnumber(L, dval);
				uppercase_it( ptr );					// Upper case name
				lua_pushstring(L, (const char *)ptr);	// String value
				lua_rawset(L,list);
			}

			MEMFREE(url);
		}
		
		// Add n=count-1 to table
		
		list = lua_gettop(L);
		lua_pushstring(L,"n");
		dval = (double)(count - 1);
		lua_pushnumber(L, dval);
		lua_rawset(L,list);						// Add to table
		
		// Put on HTTP type return as part of the result
		
		dval = (double)h->rdata->type;
		lua_pushnumber(L, dval);
		rtnval = 2;
	}
		
	return rtnval;
}

// ********** POST DATA READS DON'T WORK **********
#if 0
//***************************************************************************
/*
*  h:data()	- Return data from restful handle
*
*	INPUT:	h	- Restful handle
*
*	OUTPUT:	Lua data as a string
*
*/
//***************************************************************************/

static int rest_http_data(lua_State *L)
{
	int rtnval = 0;
	RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L);
	
	// (1) Do we have a handle ?
	
	if( h != NULL )
	{
		// (2) Ok then push the data as a string
		
		lua_pushlstring(L, (const char *)h->rdata->data,h->rdata->size);
		rtnval = 1;
	}
	
	return rtnval;
}
#endif

#if 0
	#pragma mark -
	#pragma mark HTTP LOGIN API
#endif

//***************************************************************************
/*
*  h:rest_client_lock(flag)	- Lock connection to current client (by IP)
*
*	INPUT:	h		- Restful handle
*			flag	- If <> nil then lock client, if nil unlock
*
*	OUTPUT:	NONE
*
*/
//***************************************************************************/

static int rest_client_lock(lua_State *L)
{
	int rtnval = 0;
	RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L);
	RSERV_DATA *rsrv = rest_data();
	
	// (1) Do we have a handle ?
	
	if( h != NULL )
	{
		// (2) Is this lock or unlock
		
		if( lua_isnil(L,2) )
		{
			// DELETE the lock
			
			rsrv->lock_addr_flag = 0;	// Clear flag
		}
		else 
		{
			// Lock requested, copy the current request IP
			// to checker location and set the flag
			
			strcpy(rsrv->client_ip_addr,rsrv->request_ip_addr);
			rsrv->lock_addr_flag = 1;	// Set flag
		}

	}
	
	return rtnval;
}



#if 0
	#pragma mark -
	#pragma mark Internal API
#endif

//***************************************************************************
/*
*  int get_http_site( RSERV_DATA *rsrv )	- Get HTTP base name from http_base
*
*	INPUT:	rsrv	- Struct with filled out http_base
*
*	OUTPUT:	1 if site name set up 0 if not
*
*/
//***************************************************************************/

static int get_http_base( RSERV_DATA *rsrv )
{
	int rtnval = 0;
	int n,i;
	char *ptr;
	
	// (1) Scan backward from the end of the http_site and find first "/"
	
	n = strlen(rsrv->http_site);
	for( i=n-1; i>=0; i-- )
	{
		if( rsrv->http_site[i] == '/' )
			break;
	}
	
	// (2) At this point i -> '/' so move fwd 1 and set string pointer there
	
	ptr = (char *)&(rsrv->http_site[i+1]);
	
	// (3) Move this string into structure
	
	n = strlen(ptr);
	rsrv->http_base = (char *)MEMALLOC(n+1);
	rsrv->len_http_base = n;
	if( rsrv->http_base != NULL )
	{
		memcpy(rsrv->http_base,ptr,n);
		rtnval = 1;
	}
	
	return rtnval;
}

//***************************************************************************
/*
*  void uppercase_it(char *ptr )	- Uppercase the URL parts
*
*	INPUT:	ptr	- Part of URL
*
*	OUTPUT:	NAME url part converted to uppercase 
*
*/
//***************************************************************************/

static void uppercase_it(char *ptr )
{
	int n = strlen(ptr);
	int i;
	
	for( i=0; i<n; i++ )
		ptr[i] = toupper(ptr[i]);
		
}