summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/Authenticator.cpp10
-rw-r--r--source/Authenticator.h5
-rw-r--r--source/Bindings.cpp441
-rw-r--r--source/Bindings.h2
-rw-r--r--source/BlockID.cpp2
-rw-r--r--source/Blocks/BlockFarmland.h44
-rw-r--r--source/Blocks/BlockPlanks.h2
-rw-r--r--source/Blocks/BlockWood.h2
-rw-r--r--source/ClientHandle.cpp1
-rw-r--r--source/ClientHandle.h1
-rw-r--r--source/CraftingRecipes.cpp4
-rw-r--r--source/Entities/Entity.cpp5
-rw-r--r--source/Entities/Entity.h51
-rw-r--r--source/Entities/Minecart.cpp55
-rw-r--r--source/Entities/Pickup.h2
-rw-r--r--source/FurnaceRecipe.cpp4
-rw-r--r--source/Generating/ChunkDesc.h6
-rw-r--r--source/Generating/ComposableGenerator.cpp2
-rw-r--r--source/GroupManager.cpp8
-rw-r--r--source/HTTPServer/EnvelopeParser.cpp132
-rw-r--r--source/HTTPServer/EnvelopeParser.h69
-rw-r--r--source/HTTPServer/HTTPConnection.cpp247
-rw-r--r--source/HTTPServer/HTTPConnection.h101
-rw-r--r--source/HTTPServer/HTTPFormParser.cpp278
-rw-r--r--source/HTTPServer/HTTPFormParser.h107
-rw-r--r--source/HTTPServer/HTTPMessage.cpp279
-rw-r--r--source/HTTPServer/HTTPMessage.h164
-rw-r--r--source/HTTPServer/HTTPServer.cpp258
-rw-r--r--source/HTTPServer/HTTPServer.h101
-rw-r--r--source/HTTPServer/MultipartParser.cpp256
-rw-r--r--source/HTTPServer/MultipartParser.h76
-rw-r--r--source/HTTPServer/NameValueParser.cpp412
-rw-r--r--source/HTTPServer/NameValueParser.h70
-rw-r--r--source/Items/ItemHandler.cpp2
-rw-r--r--source/ManualBindings.cpp74
-rw-r--r--source/Mobs/Horse.h2
-rw-r--r--source/Mobs/IronGolem.h2
-rw-r--r--source/Mobs/Magmacube.cpp2
-rw-r--r--source/Mobs/Zombiepigman.cpp2
-rw-r--r--source/OSSupport/File.cpp15
-rw-r--r--source/OSSupport/File.h3
-rw-r--r--source/OSSupport/IsThread.cpp14
-rw-r--r--source/OSSupport/IsThread.h3
-rw-r--r--source/OSSupport/ListenThread.cpp5
-rw-r--r--source/Plugin.cpp12
-rw-r--r--source/Plugin.h3
-rw-r--r--source/PluginLua.cpp3
-rw-r--r--source/PluginManager.cpp18
-rw-r--r--source/Protocol/Protocol125.cpp15
-rw-r--r--source/Protocol/Protocol16x.cpp2
-rw-r--r--source/Protocol/Protocol16x.h2
-rw-r--r--source/Protocol/ProtocolRecognizer.cpp6
-rw-r--r--source/Protocol/ProtocolRecognizer.h6
-rw-r--r--source/RCONServer.cpp4
-rw-r--r--source/Root.cpp183
-rw-r--r--source/Root.h14
-rw-r--r--source/Server.cpp9
-rw-r--r--source/Simulator/FireSimulator.cpp22
-rw-r--r--source/StringUtils.cpp155
-rw-r--r--source/StringUtils.h12
-rw-r--r--source/WebAdmin.cpp528
-rw-r--r--source/WebAdmin.h129
-rw-r--r--source/World.cpp4
-rw-r--r--source/World.h10
64 files changed, 3850 insertions, 608 deletions
diff --git a/source/Authenticator.cpp b/source/Authenticator.cpp
index dcc63299e..a45617f93 100644
--- a/source/Authenticator.cpp
+++ b/source/Authenticator.cpp
@@ -100,6 +100,16 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co
+void cAuthenticator::Start(void)
+{
+ m_ShouldTerminate = false;
+ super::Start();
+}
+
+
+
+
+
void cAuthenticator::Stop(void)
{
m_ShouldTerminate = true;
diff --git a/source/Authenticator.h b/source/Authenticator.h
index c9e647329..868476d80 100644
--- a/source/Authenticator.h
+++ b/source/Authenticator.h
@@ -42,7 +42,10 @@ public:
/// Queues a request for authenticating a user. If the auth fails, the user is kicked
void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash);
- // Stops the authenticator thread
+ /// Starts the authenticator thread. The thread may be started and stopped repeatedly
+ void Start(void);
+
+ /// Stops the authenticator thread. The thread may be started and stopped repeatedly
void Stop(void);
private:
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index a08985144..48d8f3f83 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/16/13 11:41:45.
+** Generated automatically by tolua++-1.0.92 on 10/08/13 20:07:51.
*/
#ifndef __cplusplus
@@ -240,18 +240,19 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cCraftingRecipe");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"cItemGrid");
- tolua_usertype(tolua_S,"cBlockArea");
+ tolua_usertype(tolua_S,"cHTTPServer::cCallbacks");
tolua_usertype(tolua_S,"cLuaWindow");
tolua_usertype(tolua_S,"cInventory");
tolua_usertype(tolua_S,"cBoundingBox");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
- tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cTracer");
+ tolua_usertype(tolua_S,"HTTPFormData");
+ tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cArrowEntity");
tolua_usertype(tolua_S,"cDropSpenserEntity");
- tolua_usertype(tolua_S,"cWindow");
- tolua_usertype(tolua_S,"Vector3i");
+ tolua_usertype(tolua_S,"cBlockArea");
tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cStringMap");
tolua_usertype(tolua_S,"cBlockEntity");
@@ -9676,15 +9677,15 @@ static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S)
else
#endif
{
- int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0));
- int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0));
- int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0));
+ double a_X = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f));
float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f));
float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f));
{
- cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
}
}
@@ -9718,15 +9719,15 @@ static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S)
else
#endif
{
- int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0));
- int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0));
- int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0));
+ double a_X = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f));
float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f));
float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f));
{
- cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
}
@@ -10828,6 +10829,38 @@ static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetLocalFolder of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00
+static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetLocalFolder();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* get function: __cWebPlugin__ of class cPluginLua */
#ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__
static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S)
@@ -13282,6 +13315,134 @@ static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: IsWeatherSunny of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00
+static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherSunny();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherRain of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00
+static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherRain();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherStorm of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00
+static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherStorm();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherWet of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00
+static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherWet();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: SetNextBlockTick of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00
static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S)
@@ -18621,8 +18782,8 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S)
#endif
{
{
- AString tolua_ret = (AString) cWebAdmin::GetMemoryUsage();
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ int tolua_ret = (int) cWebAdmin::GetMemoryUsage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
return 1;
@@ -18634,77 +18795,77 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetPort of class cWebAdmin */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPort00
-static int tolua_AllToLua_cWebAdmin_GetPort00(lua_State* tolua_S)
+/* method: GetPage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00
+static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+ const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPort'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL);
#endif
{
- int tolua_ret = (int) self->GetPort();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetPort'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetPage of class cWebAdmin */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00
-static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S)
+/* method: GetDefaultPage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00
+static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
- (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
- const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL);
#endif
{
- sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request);
- {
-#ifdef __cplusplus
- void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret));
- tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
- tolua_register_gc(tolua_S,lua_gettop(tolua_S));
-#else
- void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage));
- tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
- tolua_register_gc(tolua_S,lua_gettop(tolua_S));
-#endif
- }
+ AString tolua_ret = (AString) self->GetDefaultPage();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err);
return 0;
#endif
}
@@ -19385,6 +19546,62 @@ static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetVirtualRAMUsage of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00
+static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ int tolua_ret = (int) cRoot::GetVirtualRAMUsage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPhysicalRAMUsage of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00
+static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ int tolua_ret = (int) cRoot::GetPhysicalRAMUsage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: new of class Vector3f */
#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00
static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S)
@@ -26131,41 +26348,6 @@ static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: SetChunkCoords of class cChunkDesc */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetChunkCoords00
-static int tolua_AllToLua_cChunkDesc_SetChunkCoords00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,4,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
- int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0));
- int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetChunkCoords'", NULL);
-#endif
- {
- self->SetChunkCoords(a_ChunkX,a_ChunkZ);
- }
- }
- return 0;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetChunkCoords'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: FillBlocks of class cChunkDesc */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00
static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S)
@@ -29030,14 +29212,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL);
tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK);
tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD);
+ tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME);
tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT);
tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT);
tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO);
tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO);
tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO);
+ tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP);
tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT);
tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD);
tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK);
+ tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR);
tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE);
tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET);
tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR);
@@ -29231,11 +29416,35 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED);
tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X);
tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER);
- tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN);
@@ -29260,6 +29469,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE);
tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL);
tolua_constant(tolua_S,"dimNether",dimNether);
tolua_constant(tolua_S,"dimOverworld",dimOverworld);
tolua_constant(tolua_S,"dimEnd",dimEnd);
@@ -29471,20 +29681,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cEntity","cEntity","",NULL);
tolua_beginmodule(tolua_S,"cEntity");
- tolua_constant(tolua_S,"ENTITY_STATUS_HURT",cEntity::ENTITY_STATUS_HURT);
- tolua_constant(tolua_S,"ENTITY_STATUS_DEAD",cEntity::ENTITY_STATUS_DEAD);
- tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMING",cEntity::ENTITY_STATUS_WOLF_TAMING);
- tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMED",cEntity::ENTITY_STATUS_WOLF_TAMED);
- tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_SHAKING",cEntity::ENTITY_STATUS_WOLF_SHAKING);
- tolua_constant(tolua_S,"ENTITY_STATUS_EATING_ACCEPTED",cEntity::ENTITY_STATUS_EATING_ACCEPTED);
- tolua_constant(tolua_S,"ENTITY_STATUS_SHEEP_EATING",cEntity::ENTITY_STATUS_SHEEP_EATING);
- tolua_constant(tolua_S,"FIRE_TICKS_PER_DAMAGE",cEntity::FIRE_TICKS_PER_DAMAGE);
- tolua_constant(tolua_S,"FIRE_DAMAGE",cEntity::FIRE_DAMAGE);
- tolua_constant(tolua_S,"LAVA_TICKS_PER_DAMAGE",cEntity::LAVA_TICKS_PER_DAMAGE);
- tolua_constant(tolua_S,"LAVA_DAMAGE",cEntity::LAVA_DAMAGE);
- tolua_constant(tolua_S,"BURN_TICKS_PER_DAMAGE",cEntity::BURN_TICKS_PER_DAMAGE);
- tolua_constant(tolua_S,"BURN_DAMAGE",cEntity::BURN_DAMAGE);
- tolua_constant(tolua_S,"BURN_TICKS",cEntity::BURN_TICKS);
tolua_constant(tolua_S,"etEntity",cEntity::etEntity);
tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer);
tolua_constant(tolua_S,"etPickup",cEntity::etPickup);
@@ -29495,10 +29691,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"etTNT",cEntity::etTNT);
tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile);
tolua_constant(tolua_S,"etMob",cEntity::etMob);
- tolua_constant(tolua_S,"eEntityType_Entity",cEntity::eEntityType_Entity);
- tolua_constant(tolua_S,"eEntityType_Player",cEntity::eEntityType_Player);
- tolua_constant(tolua_S,"eEntityType_Pickup",cEntity::eEntityType_Pickup);
- tolua_constant(tolua_S,"eEntityType_Mob",cEntity::eEntityType_Mob);
tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00);
tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00);
tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00);
@@ -29797,6 +29989,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00);
tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00);
tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00);
+ tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL);
tolua_beginmodule(tolua_S,"cPluginLua");
@@ -29876,6 +30069,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00);
tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00);
tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00);
+ tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00);
+ tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00);
+ tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00);
+ tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00);
tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00);
tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00);
tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00);
@@ -30172,11 +30369,11 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName);
tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName);
tolua_endmodule(tolua_S);
- tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","",NULL);
+ tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL);
tolua_beginmodule(tolua_S,"cWebAdmin");
tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00);
- tolua_function(tolua_S,"GetPort",tolua_AllToLua_cWebAdmin_GetPort00);
tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00);
+ tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00);
tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL);
@@ -30204,6 +30401,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00);
tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00);
tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00);
+ tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00);
+ tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00);
tolua_endmodule(tolua_S);
#ifdef __cplusplus
tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f);
@@ -30493,7 +30692,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_beginmodule(tolua_S,"cChunkDesc");
tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00);
tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00);
- tolua_function(tolua_S,"SetChunkCoords",tolua_AllToLua_cChunkDesc_SetChunkCoords00);
tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00);
tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00);
tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00);
@@ -30599,34 +30797,35 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL);
tolua_beginmodule(tolua_S,"cMonster");
+ tolua_constant(tolua_S,"mtBat",cMonster::mtBat);
+ tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze);
+ tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider);
+ tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken);
+ tolua_constant(tolua_S,"mtCow",cMonster::mtCow);
tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper);
- tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton);
- tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider);
- tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant);
- tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie);
- tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime);
- tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast);
- tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman);
+ tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon);
tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman);
- tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider);
- tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish);
- tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze);
+ tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast);
+ tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant);
+ tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse);
+ tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem);
tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube);
- tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon);
- tolua_constant(tolua_S,"mtWither",cMonster::mtWither);
- tolua_constant(tolua_S,"mtBat",cMonster::mtBat);
- tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch);
+ tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom);
+ tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot);
tolua_constant(tolua_S,"mtPig",cMonster::mtPig);
tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep);
- tolua_constant(tolua_S,"mtCow",cMonster::mtCow);
- tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken);
- tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid);
- tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf);
- tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom);
+ tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish);
+ tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton);
+ tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime);
tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem);
- tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot);
- tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem);
+ tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider);
+ tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid);
tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager);
+ tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch);
+ tolua_constant(tolua_S,"mtWither",cMonster::mtWither);
+ tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf);
+ tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie);
+ tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman);
tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL);
diff --git a/source/Bindings.h b/source/Bindings.h
index 4d1777180..a1daea398 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/16/13 11:41:45.
+** Generated automatically by tolua++-1.0.92 on 10/08/13 20:07:52.
*/
/* Exported function */
diff --git a/source/BlockID.cpp b/source/BlockID.cpp
index ecdbc0c34..d91524de5 100644
--- a/source/BlockID.cpp
+++ b/source/BlockID.cpp
@@ -603,6 +603,7 @@ public:
g_BlockTransparent[E_BLOCK_FIRE] = true;
g_BlockTransparent[E_BLOCK_FLOWER_POT] = true;
g_BlockTransparent[E_BLOCK_GLASS] = true;
+ g_BlockTransparent[E_BLOCK_GLASS_PANE] = true;
g_BlockTransparent[E_BLOCK_ICE] = true;
g_BlockTransparent[E_BLOCK_IRON_DOOR] = true;
g_BlockTransparent[E_BLOCK_LEAVES] = true;
@@ -646,7 +647,6 @@ public:
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true;
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true;
g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
- g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true;
g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true;
g_BlockOneHitDig[E_BLOCK_REEDS] = true;
diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h
index 6cab1fa38..7bc71f7f3 100644
--- a/source/Blocks/BlockFarmland.h
+++ b/source/Blocks/BlockFarmland.h
@@ -30,30 +30,38 @@ public:
virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
- // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining.
- // NOTE: The desert biomes do not get precipitation, so another check needs to be made.
+ bool Found = false;
- // Search for water in a close proximity:
- // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
- cBlockArea Area;
- if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4))
+ int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ);
+ if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills))
{
- // Too close to the world edge, cannot check surroudnings; don't tick at all
- return;
+ // Rain hydrates farmland, too, except in Desert biomes.
+ Found = true;
}
- bool Found = false;
- int NumBlocks = Area.GetBlockCount();
- BLOCKTYPE * BlockTypes = Area.GetBlockTypes();
- for (int i = 0; i < NumBlocks; i++)
+ else
{
- if (
- (BlockTypes[i] == E_BLOCK_WATER) ||
- (BlockTypes[i] == E_BLOCK_STATIONARY_WATER)
- )
+ // Search for water in a close proximity:
+ // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4))
{
- Found = true;
- break;
+ // Too close to the world edge, cannot check surroudnings; don't tick at all
+ return;
}
+
+ int NumBlocks = Area.GetBlockCount();
+ BLOCKTYPE * BlockTypes = Area.GetBlockTypes();
+ for (int i = 0; i < NumBlocks; i++)
+ {
+ if (
+ (BlockTypes[i] == E_BLOCK_WATER) ||
+ (BlockTypes[i] == E_BLOCK_STATIONARY_WATER)
+ )
+ {
+ Found = true;
+ break;
+ }
+ } // for i - BlockTypes[]
}
NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
diff --git a/source/Blocks/BlockPlanks.h b/source/Blocks/BlockPlanks.h
index b30164741..f3b8dbfb6 100644
--- a/source/Blocks/BlockPlanks.h
+++ b/source/Blocks/BlockPlanks.h
@@ -24,7 +24,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- NIBBLETYPE Meta = a_Player->GetEquippedItem().m_ItemDamage;
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage);
a_BlockMeta = Meta;
return true;
}
diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h
index dd4544586..cb5ee995a 100644
--- a/source/Blocks/BlockWood.h
+++ b/source/Blocks/BlockWood.h
@@ -24,7 +24,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- NIBBLETYPE Meta = a_Player->GetEquippedItem().m_ItemDamage;
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage);
a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta);
return true;
}
diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp
index 074b50bad..f67a546fd 100644
--- a/source/ClientHandle.cpp
+++ b/source/ClientHandle.cpp
@@ -735,6 +735,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
cWorld * World = m_Player->GetWorld();
ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ);
+ // The ItemHandler is also responsible for spawning the pickups
BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
World->BroadcastSoundParticleEffect(2001, a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, a_OldBlock, this);
diff --git a/source/ClientHandle.h b/source/ClientHandle.h
index 01059c511..ef6dbd124 100644
--- a/source/ClientHandle.h
+++ b/source/ClientHandle.h
@@ -1,3 +1,4 @@
+
// cClientHandle.h
// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet
diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp
index 13a8ac1e0..9dc471781 100644
--- a/source/CraftingRecipes.cpp
+++ b/source/CraftingRecipes.cpp
@@ -310,7 +310,7 @@ void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid &
void cCraftingRecipes::LoadRecipes(void)
{
- LOG("-- Loading crafting recipes from crafting.txt --");
+ LOGD("Loading crafting recipes from crafting.txt...");
ClearRecipes();
// Load the crafting.txt file:
@@ -338,7 +338,7 @@ void cCraftingRecipes::LoadRecipes(void)
}
AddRecipeLine(LineNum, Recipe);
} // for itr - Split[]
- LOG("-- %d crafting recipes loaded from crafting.txt --", m_Recipes.size());
+ LOG("Loaded %d crafting recipes", m_Recipes.size());
}
diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp
index dc3c7796e..d465c75bd 100644
--- a/source/Entities/Entity.cpp
+++ b/source/Entities/Entity.cpp
@@ -549,12 +549,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
- BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ );
+ BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block
{
if (m_bOnGround) // check if it's still on the ground
{
- BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ );
if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water.
{
m_bOnGround = false;
@@ -613,7 +612,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
}
- else if ((IsBlockRail(BlockBelow)) && (IsMinecart())) // Rails aren't solid, except for Minecarts
+ else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts
{
fallspeed = 0;
m_bOnGround = true;
diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h
index a2c99d2a0..d6c449b92 100644
--- a/source/Entities/Entity.h
+++ b/source/Entities/Entity.h
@@ -61,7 +61,26 @@ struct TakeDamageInfo
// tolua_begin
class cEntity
{
-public:
+public:
+
+ enum eEntityType
+ {
+ etEntity, // For all other types
+ etPlayer,
+ etPickup,
+ etMonster,
+ etFallingBlock,
+ etMinecart,
+ etBoat,
+ etTNT,
+ etProjectile,
+
+ // Common variations
+ etMob = etMonster, // DEPRECATED, use etMonster instead!
+ } ;
+
+ // tolua_end
+
enum
{
ENTITY_STATUS_HURT = 2,
@@ -84,28 +103,6 @@ public:
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
} ;
- enum eEntityType
- {
- etEntity, // For all other types
- etPlayer,
- etPickup,
- etMonster,
- etFallingBlock,
- etMinecart,
- etBoat,
- etTNT,
- etProjectile,
-
- // DEPRECATED older constants, left over for compatibility reasons (plugins)
- etMob = etMonster, // DEPRECATED, use etMonster instead!
- eEntityType_Entity = etEntity,
- eEntityType_Player = etPlayer,
- eEntityType_Pickup = etPickup,
- eEntityType_Mob = etMob,
- } ;
-
- // tolua_end
-
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
virtual ~cEntity();
@@ -234,16 +231,16 @@ public:
/// Returns the curently equipped weapon; empty item if none
virtual cItem GetEquippedWeapon(void) const { return cItem(); }
- /// Returns the currently equipped helmet; empty item if nonte
+ /// Returns the currently equipped helmet; empty item if none
virtual cItem GetEquippedHelmet(void) const { return cItem(); }
- /// Returns the currently equipped chestplate; empty item if nonte
+ /// Returns the currently equipped chestplate; empty item if none
virtual cItem GetEquippedChestplate(void) const { return cItem(); }
- /// Returns the currently equipped leggings; empty item if nonte
+ /// Returns the currently equipped leggings; empty item if none
virtual cItem GetEquippedLeggings(void) const { return cItem(); }
- /// Returns the currently equipped boots; empty item if nonte
+ /// Returns the currently equipped boots; empty item if none
virtual cItem GetEquippedBoots(void) const { return cItem(); }
/// Called when the health drops below zero. a_Killer may be NULL (environmental damage)
diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp
index a2f1e5593..95bad6570 100644
--- a/source/Entities/Minecart.cpp
+++ b/source/Entities/Minecart.cpp
@@ -8,6 +8,7 @@
#include "Minecart.h"
#include "../World.h"
#include "../ClientHandle.h"
+#include "../Chunk.h"
#include "Player.h"
@@ -51,34 +52,43 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
- if ((GetPosY() > 0) && (GetPosY() < cChunkDef::Height))
+ int PosY = (int)floor(GetPosY());
+ if ((PosY <= 0) || (PosY >= cChunkDef::Height))
{
- BLOCKTYPE BelowType = GetWorld()->GetBlock(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ()));
- BLOCKTYPE InsideType = GetWorld()->GetBlock(floor(GetPosX()), floor(GetPosY()), floor(GetPosZ()));
+ // Outside the world, just process normal falling physics
+ super::HandlePhysics(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+ return;
+ }
+
+ int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
+ if (Chunk == NULL)
+ {
+ // Inside an unloaded chunk, bail out all processing
+ return;
+ }
+ BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ);
+ BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ);
- if (IsBlockRail(BelowType))
+ if (IsBlockRail(BelowType))
+ {
+ HandleRailPhysics(a_Dt, *Chunk);
+ }
+ else
+ {
+ if (IsBlockRail(InsideType))
{
- HandleRailPhysics(a_Dt, a_Chunk);
+ SetPosY(PosY + 1);
+ HandleRailPhysics(a_Dt, *Chunk);
}
else
{
- if (IsBlockRail(InsideType))
- {
- SetPosY(ceil(GetPosY()));
- HandleRailPhysics(a_Dt, a_Chunk);
- }
- else
- {
- super::HandlePhysics(a_Dt, a_Chunk);
- BroadcastMovementUpdate();
- }
+ super::HandlePhysics(a_Dt, *Chunk);
+ BroadcastMovementUpdate();
}
}
- else
- {
- super::HandlePhysics(a_Dt, a_Chunk);
- BroadcastMovementUpdate();
- }
}
@@ -87,6 +97,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
static const double MAX_SPEED = 8;
static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED);
+
void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
{
@@ -98,7 +109,9 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
*/
// Get block meta below the cart
- NIBBLETYPE BelowMeta = GetWorld()->GetBlockMeta(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ()));
+ int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ);
double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed
switch (BelowMeta)
diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h
index e35914684..488f91fb2 100644
--- a/source/Entities/Pickup.h
+++ b/source/Entities/Pickup.h
@@ -24,7 +24,7 @@ class cPickup :
public:
CLASS_PROTODEF(cPickup);
- cPickup(double a_MicroPosX, double a_MicroPosY, double a_MicroPosZ, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
+ cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
cItem & GetItem(void) {return m_Item; } // tolua_export
const cItem & GetItem(void) const {return m_Item; }
diff --git a/source/FurnaceRecipe.cpp b/source/FurnaceRecipe.cpp
index 8b1ee09a2..2e2276981 100644
--- a/source/FurnaceRecipe.cpp
+++ b/source/FurnaceRecipe.cpp
@@ -51,7 +51,7 @@ cFurnaceRecipe::~cFurnaceRecipe()
void cFurnaceRecipe::ReloadRecipes(void)
{
ClearRecipes();
- LOG("-- Loading furnace recipes --");
+ LOGD("Loading furnace recipes...");
std::ifstream f;
char a_File[] = "furnace.txt";
@@ -175,7 +175,7 @@ void cFurnaceRecipe::ReloadRecipes(void)
{
LOGERROR("ERROR: FurnaceRecipe, syntax error" );
}
- LOG("Got %u furnace recipes, and %u fuels.", m_pState->Recipes.size(), m_pState->Fuel.size());
+ LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
}
diff --git a/source/Generating/ChunkDesc.h b/source/Generating/ChunkDesc.h
index 41b85a814..067d8494a 100644
--- a/source/Generating/ChunkDesc.h
+++ b/source/Generating/ChunkDesc.h
@@ -36,13 +36,13 @@ public:
cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc();
+ void SetChunkCoords(int a_ChunkX, int a_ChunkZ);
+
// tolua_begin
int GetChunkX(void) const { return m_ChunkX; }
int GetChunkZ(void) const { return m_ChunkZ; }
- void SetChunkCoords(int a_ChunkX, int a_ChunkZ);
-
void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
@@ -74,7 +74,7 @@ public:
/// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk.
void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite);
- /// Reads an area from the chunk into a cBlockArea
+ /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
/// Returns the maximum height value in the heightmap
diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp
index 0852f559e..e2a8df11b 100644
--- a/source/Generating/ComposableGenerator.cpp
+++ b/source/Generating/ComposableGenerator.cpp
@@ -294,7 +294,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
);
CacheSize = 4;
}
- LOGINFO("Using a cache for Heightgen of size %d.", CacheSize);
+ LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_UnderlyingHeightGen = m_HeightGen;
m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize);
}
diff --git a/source/GroupManager.cpp b/source/GroupManager.cpp
index cef32dd58..b79fde9dc 100644
--- a/source/GroupManager.cpp
+++ b/source/GroupManager.cpp
@@ -43,7 +43,7 @@ cGroupManager::~cGroupManager()
cGroupManager::cGroupManager()
: m_pState( new sGroupManagerState )
{
- LOG("-- Loading Groups --");
+ LOGD("-- Loading Groups --");
cIniFile IniFile("groups.ini");
if (!IniFile.ReadFile())
{
@@ -57,7 +57,7 @@ cGroupManager::cGroupManager()
std::string KeyName = IniFile.GetKeyName( i );
cGroup* Group = GetGroup( KeyName.c_str() );
- LOG("Loading group: %s", KeyName.c_str() );
+ LOGD("Loading group: %s", KeyName.c_str() );
Group->SetName( KeyName );
char Color = IniFile.GetValue( KeyName, "Color", "-" )[0];
@@ -73,7 +73,6 @@ cGroupManager::cGroupManager()
for( unsigned int i = 0; i < Split.size(); i++)
{
Group->AddCommand( Split[i] );
- //LOG("%s", Split[i].c_str() );
}
}
@@ -84,7 +83,6 @@ cGroupManager::cGroupManager()
for( unsigned int i = 0; i < Split.size(); i++)
{
Group->AddPermission( Split[i] );
- //LOGINFO("Permission: %s", Split[i].c_str() );
}
}
@@ -98,7 +96,7 @@ cGroupManager::cGroupManager()
}
}
}
- LOG("-- Groups Successfully Loaded --");
+ LOGD("-- Groups Successfully Loaded --");
}
diff --git a/source/HTTPServer/EnvelopeParser.cpp b/source/HTTPServer/EnvelopeParser.cpp
new file mode 100644
index 000000000..8dbe05f14
--- /dev/null
+++ b/source/HTTPServer/EnvelopeParser.cpp
@@ -0,0 +1,132 @@
+
+// EnvelopeParser.cpp
+
+// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
+
+#include "Globals.h"
+#include "EnvelopeParser.h"
+
+
+
+
+
+cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsInHeaders(true)
+{
+}
+
+
+
+
+
+int cEnvelopeParser::Parse(const char * a_Data, int a_Size)
+{
+ if (!m_IsInHeaders)
+ {
+ return 0;
+ }
+
+ // Start searching 1 char from the end of the already received data, if available:
+ size_t SearchStart = m_IncomingData.size();
+ SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0;
+
+ m_IncomingData.append(a_Data, a_Size);
+
+ size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart);
+ if (idxCRLF == AString::npos)
+ {
+ // Not a complete line yet, all input consumed:
+ return a_Size;
+ }
+
+ // Parse as many lines as found:
+ size_t Last = 0;
+ do
+ {
+ if (idxCRLF == Last)
+ {
+ // This was the last line of the data. Finish whatever value has been cached and return:
+ NotifyLast();
+ m_IsInHeaders = false;
+ return a_Size - (m_IncomingData.size() - idxCRLF) + 2;
+ }
+ if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last))
+ {
+ // An error has occurred
+ m_IsInHeaders = false;
+ return -1;
+ }
+ Last = idxCRLF + 2;
+ idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);
+ } while (idxCRLF != AString::npos);
+ m_IncomingData.erase(0, Last);
+
+ // Parsed all lines and still expecting more
+ return a_Size;
+}
+
+
+
+
+
+void cEnvelopeParser::Reset(void)
+{
+ m_IsInHeaders = true;
+ m_IncomingData.clear();
+ m_LastKey.clear();
+ m_LastValue.clear();
+}
+
+
+
+
+
+void cEnvelopeParser::NotifyLast(void)
+{
+ if (!m_LastKey.empty())
+ {
+ m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue);
+ m_LastKey.clear();
+ }
+ m_LastValue.clear();
+}
+
+
+
+
+
+bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size)
+{
+ ASSERT(a_Size > 0);
+ if (a_Data[0] <= ' ')
+ {
+ // This line is a continuation for the previous line
+ if (m_LastKey.empty())
+ {
+ return false;
+ }
+ // Append, including the whitespace in a_Data[0]
+ m_LastValue.append(a_Data, a_Size);
+ return true;
+ }
+
+ // This is a line with a new key:
+ NotifyLast();
+ for (size_t i = 0; i < a_Size; i++)
+ {
+ if (a_Data[i] == ':')
+ {
+ m_LastKey.assign(a_Data, i);
+ m_LastValue.assign(a_Data + i + 2, a_Size - i - 2);
+ return true;
+ }
+ } // for i - a_Data[]
+
+ // No colon was found, key-less header??
+ return false;
+}
+
+
+
+
diff --git a/source/HTTPServer/EnvelopeParser.h b/source/HTTPServer/EnvelopeParser.h
new file mode 100644
index 000000000..6430fbebf
--- /dev/null
+++ b/source/HTTPServer/EnvelopeParser.h
@@ -0,0 +1,69 @@
+
+// EnvelopeParser.h
+
+// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cEnvelopeParser
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /// Called when a full header line is parsed
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0;
+ } ;
+
+
+ cEnvelopeParser(cCallbacks & a_Callbacks);
+
+ /** Parses the incoming data.
+ Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header
+ */
+ int Parse(const char * a_Data, int a_Size);
+
+ /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream
+ void Reset(void);
+
+ /// Returns true if more input is expected for the envelope header
+ bool IsInHeaders(void) const { return m_IsInHeaders; }
+
+ /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions
+ void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; }
+
+public:
+ /// Callbacks to call for the various events
+ cCallbacks & m_Callbacks;
+
+ /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data.
+ bool m_IsInHeaders;
+
+ /// Buffer for the incoming data until it is parsed
+ AString m_IncomingData;
+
+ /// Holds the last parsed key; used for line-wrapped values
+ AString m_LastKey;
+
+ /// Holds the last parsed value; used for line-wrapped values
+ AString m_LastValue;
+
+
+ /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them
+ void NotifyLast(void);
+
+ /// Parses one line of header data. Returns true if successful
+ bool ParseLine(const char * a_Data, size_t a_Size);
+} ;
+
+
+
+
diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp
new file mode 100644
index 000000000..68afdfc11
--- /dev/null
+++ b/source/HTTPServer/HTTPConnection.cpp
@@ -0,0 +1,247 @@
+
+// HTTPConnection.cpp
+
+// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server.
+
+#include "Globals.h"
+#include "HTTPConnection.h"
+#include "HTTPMessage.h"
+#include "HTTPServer.h"
+
+
+
+
+
+cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
+ m_HTTPServer(a_HTTPServer),
+ m_State(wcsRecvHeaders),
+ m_CurrentRequest(NULL)
+{
+ // LOGD("HTTP: New connection at %p", this);
+}
+
+
+
+
+
+cHTTPConnection::~cHTTPConnection()
+{
+ // LOGD("HTTP: Del connection at %p", this);
+}
+
+
+
+
+
+void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response)
+{
+ AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str());
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+}
+
+
+
+
+
+void cHTTPConnection::SendNeedAuth(const AString & a_Realm)
+{
+ AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str());
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+}
+
+
+
+
+
+void cHTTPConnection::Send(const cHTTPResponse & a_Response)
+{
+ ASSERT(m_State = wcsRecvIdle);
+ a_Response.AppendToData(m_OutgoingData);
+ m_State = wcsSendingResp;
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::Send(const void * a_Data, int a_Size)
+{
+ ASSERT(m_State == wcsSendingResp);
+ AppendPrintf(m_OutgoingData, "%x\r\n", a_Size);
+ m_OutgoingData.append((const char *)a_Data, a_Size);
+ m_OutgoingData.append("\r\n");
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::FinishResponse(void)
+{
+ ASSERT(m_State == wcsSendingResp);
+ m_OutgoingData.append("0\r\n\r\n");
+ m_State = wcsRecvHeaders;
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::AwaitNextRequest(void)
+{
+ switch (m_State)
+ {
+ case wcsRecvHeaders:
+ {
+ // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() )
+ break;
+ }
+
+ case wcsRecvIdle:
+ {
+ // The client is waiting for a response, send an "Internal server error":
+ m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n");
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+ break;
+ }
+
+ case wcsSendingResp:
+ {
+ // The response headers have been sent, we need to terminate the response body:
+ m_OutgoingData.append("0\r\n\r\n");
+ m_State = wcsRecvHeaders;
+ break;
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled state recovery");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cHTTPConnection::Terminate(void)
+{
+ if (m_CurrentRequest != NULL)
+ {
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ }
+ m_HTTPServer.CloseConnection(*this);
+}
+
+
+
+
+
+void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
+{
+ switch (m_State)
+ {
+ case wcsRecvHeaders:
+ {
+ if (m_CurrentRequest == NULL)
+ {
+ m_CurrentRequest = new cHTTPRequest;
+ }
+
+ int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
+ if (BytesConsumed < 0)
+ {
+ delete m_CurrentRequest;
+ m_CurrentRequest = NULL;
+ m_State = wcsInvalid;
+ m_HTTPServer.CloseConnection(*this);
+ return;
+ }
+ if (m_CurrentRequest->IsInHeaders())
+ {
+ // The request headers are not yet complete
+ return;
+ }
+
+ // The request has finished parsing its headers successfully, notify of it:
+ m_State = wcsRecvBody;
+ m_HTTPServer.NewRequest(*this, *m_CurrentRequest);
+ m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength();
+ if (m_CurrentRequestBodyRemaining < 0)
+ {
+ // The body length was not specified in the request, assume zero
+ m_CurrentRequestBodyRemaining = 0;
+ }
+
+ // Process the rest of the incoming data into the request body:
+ if (a_Size > BytesConsumed)
+ {
+ DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
+ }
+ else
+ {
+ DataReceived("", 0); // If the request has zero body length, let it be processed right-away
+ }
+ break;
+ }
+
+ case wcsRecvBody:
+ {
+ ASSERT(m_CurrentRequest != NULL);
+ if (m_CurrentRequestBodyRemaining > 0)
+ {
+ int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size);
+ m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume);
+ m_CurrentRequestBodyRemaining -= BytesToConsume;
+ }
+ if (m_CurrentRequestBodyRemaining == 0)
+ {
+ m_State = wcsRecvIdle;
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ delete m_CurrentRequest;
+ m_CurrentRequest = NULL;
+ }
+ break;
+ }
+
+ default:
+ {
+ // TODO: Should we be receiving data in this state?
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cHTTPConnection::GetOutgoingData(AString & a_Data)
+{
+ std::swap(a_Data, m_OutgoingData);
+}
+
+
+
+
+
+void cHTTPConnection::SocketClosed(void)
+{
+ if (m_CurrentRequest != NULL)
+ {
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ }
+ m_HTTPServer.CloseConnection(*this);
+}
+
+
+
+
+
diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h
new file mode 100644
index 000000000..14603bb70
--- /dev/null
+++ b/source/HTTPServer/HTTPConnection.h
@@ -0,0 +1,101 @@
+
+// HTTPConnection.h
+
+// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server.
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/SocketThreads.h"
+
+
+
+
+
+// fwd:
+class cHTTPServer;
+class cHTTPResponse;
+class cHTTPRequest;
+
+
+
+
+
+class cHTTPConnection :
+ public cSocketThreads::cCallback
+{
+public:
+
+ enum eState
+ {
+ wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL)
+ wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid)
+ wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL)
+ wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL)
+ wcsInvalid, ///< The request was malformed, the connection is closing
+ } ;
+
+ cHTTPConnection(cHTTPServer & a_HTTPServer);
+ ~cHTTPConnection();
+
+ /// Sends HTTP status code together with a_Reason (used for HTTP errors)
+ void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
+
+ /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm
+ void SendNeedAuth(const AString & a_Realm);
+
+ /// Sends the headers contained in a_Response
+ void Send(const cHTTPResponse & a_Response);
+
+ /// Sends the data as the response (may be called multiple times)
+ void Send(const void * a_Data, int a_Size);
+
+ /// Sends the data as the response (may be called multiple times)
+ void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
+
+ /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive)
+ void FinishResponse(void);
+
+ /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd"
+ void AwaitNextRequest(void);
+
+ /// Terminates the connection; finishes any request being currently processed
+ void Terminate(void);
+
+protected:
+ typedef std::map<AString, AString> cNameValueMap;
+
+ /// The parent webserver that is to be notified of events on this connection
+ cHTTPServer & m_HTTPServer;
+
+ /// All the incoming data until the entire request header is parsed
+ AString m_IncomingHeaderData;
+
+ /// Status in which the request currently is
+ eState m_State;
+
+ /// Data that is queued for sending, once the socket becomes writable
+ AString m_OutgoingData;
+
+ /// The request being currently received (valid only between having parsed the headers and finishing receiving the body)
+ cHTTPRequest * m_CurrentRequest;
+
+ /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody
+ int m_CurrentRequestBodyRemaining;
+
+
+ // cSocketThreads::cCallback overrides:
+ virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
+ virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
+ virtual void SocketClosed (void) override; // The socket has been closed for any reason
+} ;
+
+typedef std::vector<cHTTPConnection *> cHTTPConnections;
+
+
+
+
+
diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp
new file mode 100644
index 000000000..7db7b4e6d
--- /dev/null
+++ b/source/HTTPServer/HTTPFormParser.cpp
@@ -0,0 +1,278 @@
+
+// HTTPFormParser.cpp
+
+// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+#include "Globals.h"
+#include "HTTPFormParser.h"
+#include "HTTPMessage.h"
+#include "MultipartParser.h"
+#include "NameValueParser.h"
+
+
+
+
+
+cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsValid(true)
+{
+ if (a_Request.GetMethod() == "GET")
+ {
+ m_Kind = fpkURL;
+
+ // Directly parse the URL in the request:
+ const AString & URL = a_Request.GetURL();
+ size_t idxQM = URL.find('?');
+ if (idxQM != AString::npos)
+ {
+ Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1);
+ }
+ return;
+ }
+ if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT"))
+ {
+ if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0)
+ {
+ m_Kind = fpkFormUrlEncoded;
+ return;
+ }
+ if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0)
+ {
+ m_Kind = fpkMultipart;
+ BeginMultipart(a_Request);
+ return;
+ }
+ }
+ // Invalid method / content type combination, this is not a HTTP form
+ m_IsValid = false;
+}
+
+
+
+
+
+void cHTTPFormParser::Parse(const char * a_Data, int a_Size)
+{
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ switch (m_Kind)
+ {
+ case fpkURL:
+ case fpkFormUrlEncoded:
+ {
+ // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish()
+ m_IncomingData.append(a_Data, a_Size);
+ break;
+ }
+ case fpkMultipart:
+ {
+ ASSERT(m_MultipartParser.get() != NULL);
+ m_MultipartParser->Parse(a_Data, a_Size);
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled form kind");
+ break;
+ }
+ }
+}
+
+
+
+
+
+bool cHTTPFormParser::Finish(void)
+{
+ switch (m_Kind)
+ {
+ case fpkURL:
+ case fpkFormUrlEncoded:
+ {
+ // m_IncomingData has all the form data, parse it now:
+ ParseFormUrlEncoded();
+ break;
+ }
+ }
+ return (m_IsValid && m_IncomingData.empty());
+}
+
+
+
+
+
+bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
+{
+ const AString & ContentType = a_Request.GetContentType();
+ return (
+ (ContentType == "application/x-www-form-urlencoded") ||
+ (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) ||
+ (
+ (a_Request.GetMethod() == "GET") &&
+ (a_Request.GetURL().find('?') != AString::npos)
+ )
+ );
+ return false;
+}
+
+
+
+
+
+void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request)
+{
+ ASSERT(m_MultipartParser.get() == NULL);
+ m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this));
+}
+
+
+
+
+
+void cHTTPFormParser::ParseFormUrlEncoded(void)
+{
+ // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish()
+ // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway
+ AStringVector Lines = StringSplit(m_IncomingData, "&");
+ for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr)
+ {
+ AStringVector Components = StringSplit(*itr, "=");
+ switch (Components.size())
+ {
+ default:
+ {
+ // Neither name nor value, or too many "="s, mark this as invalid form:
+ m_IsValid = false;
+ return;
+ }
+ case 1:
+ {
+ // Only name present
+ (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = "";
+ break;
+ }
+ case 2:
+ {
+ // name=value format:
+ (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' '));
+ break;
+ }
+ }
+ } // for itr - Lines[]
+ m_IncomingData.clear();
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartStart(void)
+{
+ m_CurrentPartFileName.clear();
+ m_CurrentPartName.clear();
+ m_IsCurrentPartFile = false;
+ m_FileHasBeenAnnounced = false;
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value)
+{
+ if (NoCaseCompare(a_Key, "Content-Disposition") == 0)
+ {
+ size_t len = a_Value.size();
+ size_t ParamsStart = AString::npos;
+ for (size_t i = 0; i < len; ++i)
+ {
+ if (a_Value[i] > ' ')
+ {
+ if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0)
+ {
+ // Content disposition is not "form-data", mark the whole form invalid
+ m_IsValid = false;
+ return;
+ }
+ ParamsStart = a_Value.find(';', i + 9);
+ break;
+ }
+ }
+ if (ParamsStart == AString::npos)
+ {
+ // There is data missing in the Content-Disposition field, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+
+ // Parse the field name and optional filename from this header:
+ cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart);
+ Parser.Finish();
+ m_CurrentPartName = Parser["name"];
+ if (!Parser.IsValid() || m_CurrentPartName.empty())
+ {
+ // The required parameter "name" is missing, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+ m_CurrentPartFileName = Parser["filename"];
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size)
+{
+ if (m_CurrentPartName.empty())
+ {
+ // Prologue, epilogue or invalid part
+ return;
+ }
+ if (m_CurrentPartFileName.empty())
+ {
+ // This is a variable, store it in the map
+ iterator itr = find(m_CurrentPartName);
+ if (itr == end())
+ {
+ (*this)[m_CurrentPartName] = AString(a_Data, a_Size);
+ }
+ else
+ {
+ itr->second.append(a_Data, a_Size);
+ }
+ }
+ else
+ {
+ // This is a file, pass it on through the callbacks
+ if (!m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileStart(*this, m_CurrentPartFileName);
+ m_FileHasBeenAnnounced = true;
+ }
+ m_Callbacks.OnFileData(*this, a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartEnd(void)
+{
+ if (m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileEnd(*this);
+ }
+ m_CurrentPartName.clear();
+ m_CurrentPartFileName.clear();
+}
+
+
+
+
diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h
new file mode 100644
index 000000000..b92ef9d3c
--- /dev/null
+++ b/source/HTTPServer/HTTPFormParser.h
@@ -0,0 +1,107 @@
+
+// HTTPFormParser.h
+
+// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+
+
+
+#pragma once
+
+#include "MultipartParser.h"
+
+
+
+
+
+// fwd:
+class cHTTPRequest;
+
+
+
+
+
+class cHTTPFormParser :
+ public std::map<AString, AString>,
+ public cMultipartParser::cCallbacks
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /// Called when a new file part is encountered in the form data
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0;
+
+ /// Called when more file data has come for the current file in the form data
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0;
+
+ /// Called when the current file part has ended in the form data
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0;
+ } ;
+
+
+ cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks);
+
+ /// Adds more data into the parser, as the request body is received
+ void Parse(const char * a_Data, int a_Size);
+
+ /** Notifies that there's no more data incoming and the parser should finish its parsing.
+ Returns true if parsing successful
+ */
+ bool Finish(void);
+
+ /// Returns true if the headers suggest the request has form data parseable by this class
+ static bool HasFormData(const cHTTPRequest & a_Request);
+
+protected:
+ enum eKind
+ {
+ fpkURL, ///< The form has been transmitted as parameters to a GET request
+ fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded"
+ fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data"
+ };
+
+ /// The callbacks to call for incoming file data
+ cCallbacks & m_Callbacks;
+
+ /// The kind of the parser (decided in the constructor, used in Parse()
+ eKind m_Kind;
+
+ /// Buffer for the incoming data until it's parsed
+ AString m_IncomingData;
+
+ /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false.
+ bool m_IsValid;
+
+ /// The parser for the multipart data, if used
+ std::auto_ptr<cMultipartParser> m_MultipartParser;
+
+ /// Name of the currently parsed part in multipart data
+ AString m_CurrentPartName;
+
+ /// True if the currently parsed part in multipart data is a file
+ bool m_IsCurrentPartFile;
+
+ /// Filename of the current parsed part in multipart data (for file uploads)
+ AString m_CurrentPartFileName;
+
+ /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd
+ bool m_FileHasBeenAnnounced;
+
+
+ /// Sets up the object for parsing a fpkMultipart request
+ void BeginMultipart(const cHTTPRequest & a_Request);
+
+ /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds)
+ void ParseFormUrlEncoded(void);
+
+ // cMultipartParser::cCallbacks overrides:
+ virtual void OnPartStart (void) override;
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override;
+ virtual void OnPartData (const char * a_Data, int a_Size) override;
+ virtual void OnPartEnd (void) override;
+} ;
+
+
+
+
diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp
new file mode 100644
index 000000000..ab23866e6
--- /dev/null
+++ b/source/HTTPServer/HTTPMessage.cpp
@@ -0,0 +1,279 @@
+
+// HTTPMessage.cpp
+
+// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
+
+#include "Globals.h"
+#include "HTTPMessage.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPMessage:
+
+cHTTPMessage::cHTTPMessage(eKind a_Kind) :
+ m_Kind(a_Kind),
+ m_ContentLength(-1)
+{
+}
+
+
+
+
+
+void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value)
+{
+ AString Key = a_Key;
+ StrToLower(Key);
+ cNameValueMap::iterator itr = m_Headers.find(Key);
+ if (itr == m_Headers.end())
+ {
+ m_Headers[Key] = a_Value;
+ }
+ else
+ {
+ // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2)
+ itr->second.append(", ");
+ itr->second.append(a_Value);
+ }
+
+ // Special processing for well-known headers:
+ if (Key == "content-type")
+ {
+ m_ContentType = m_Headers[Key];
+ }
+ else if (Key == "content-length")
+ {
+ m_ContentLength = atoi(m_Headers[Key].c_str());
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPRequest:
+
+cHTTPRequest::cHTTPRequest(void) :
+ super(mkRequest),
+ m_EnvelopeParser(*this),
+ m_IsValid(true),
+ m_UserData(NULL),
+ m_HasAuth(false)
+{
+}
+
+
+
+
+
+int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
+{
+ if (!m_IsValid)
+ {
+ return -1;
+ }
+
+ if (m_Method.empty())
+ {
+ // The first line hasn't been processed yet
+ int res = ParseRequestLine(a_Data, a_Size);
+ if ((res < 0) || (res == a_Size))
+ {
+ return res;
+ }
+ int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
+ if (res2 < 0)
+ {
+ m_IsValid = false;
+ return res2;
+ }
+ return res2 + res;
+ }
+
+ if (m_EnvelopeParser.IsInHeaders())
+ {
+ int res = m_EnvelopeParser.Parse(a_Data, a_Size);
+ if (res < 0)
+ {
+ m_IsValid = false;
+ }
+ return res;
+ }
+ return 0;
+}
+
+
+
+
+
+AString cHTTPRequest::GetBareURL(void) const
+{
+ size_t idxQM = m_URL.find('?');
+ if (idxQM != AString::npos)
+ {
+ return m_URL.substr(0, idxQM);
+ }
+ else
+ {
+ return m_URL;
+ }
+}
+
+
+
+
+
+int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
+{
+ m_IncomingHeaderData.append(a_Data, a_Size);
+ size_t IdxEnd = m_IncomingHeaderData.size();
+
+ // Ignore the initial CRLFs (HTTP spec's "should")
+ size_t LineStart = 0;
+ while (
+ (LineStart < IdxEnd) &&
+ (
+ (m_IncomingHeaderData[LineStart] == '\r') ||
+ (m_IncomingHeaderData[LineStart] == '\n')
+ )
+ )
+ {
+ LineStart++;
+ }
+ if (LineStart >= IdxEnd)
+ {
+ m_IsValid = false;
+ return -1;
+ }
+
+ int NumSpaces = 0;
+ size_t MethodEnd = 0;
+ size_t URLEnd = 0;
+ for (size_t i = LineStart; i < IdxEnd; i++)
+ {
+ switch (m_IncomingHeaderData[i])
+ {
+ case ' ':
+ {
+ switch (NumSpaces)
+ {
+ case 0:
+ {
+ MethodEnd = i;
+ break;
+ }
+ case 1:
+ {
+ URLEnd = i;
+ break;
+ }
+ default:
+ {
+ // Too many spaces in the request
+ m_IsValid = false;
+ return -1;
+ }
+ }
+ NumSpaces += 1;
+ break;
+ }
+ case '\n':
+ {
+ if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7))
+ {
+ // LF too early, without a CR, without two preceeding spaces or too soon after the second space
+ m_IsValid = false;
+ return -1;
+ }
+ // Check that there's HTTP/version at the end
+ if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0)
+ {
+ m_IsValid = false;
+ return -1;
+ }
+ m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
+ m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);
+ return i + 1;
+ }
+ } // switch (m_IncomingHeaderData[i])
+ } // for i - m_IncomingHeaderData[]
+
+ // CRLF hasn't been encountered yet, consider all data consumed
+ return a_Size;
+}
+
+
+
+
+
+void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
+{
+ if (
+ (NoCaseCompare(a_Key, "Authorization") == 0) &&
+ (strncmp(a_Value.c_str(), "Basic ", 6) == 0)
+ )
+ {
+ AString UserPass = Base64Decode(a_Value.substr(6));
+ size_t idxCol = UserPass.find(':');
+ if (idxCol != AString::npos)
+ {
+ m_AuthUsername = UserPass.substr(0, idxCol);
+ m_AuthPassword = UserPass.substr(idxCol + 1);
+ m_HasAuth = true;
+ }
+ }
+ AddHeader(a_Key, a_Value);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPResponse:
+
+cHTTPResponse::cHTTPResponse(void) :
+ super(mkResponse)
+{
+}
+
+
+
+
+
+void cHTTPResponse::AppendToData(AString & a_DataStream) const
+{
+ a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: ");
+ a_DataStream.append(m_ContentType);
+ a_DataStream.append("\r\n");
+ for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr)
+ {
+ if ((itr->first == "Content-Type") || (itr->first == "Content-Length"))
+ {
+ continue;
+ }
+ a_DataStream.append(itr->first);
+ a_DataStream.append(": ");
+ a_DataStream.append(itr->second);
+ a_DataStream.append("\r\n");
+ } // for itr - m_Headers[]
+ a_DataStream.append("\r\n");
+}
+
+
+
+
diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h
new file mode 100644
index 000000000..f5284c535
--- /dev/null
+++ b/source/HTTPServer/HTTPMessage.h
@@ -0,0 +1,164 @@
+
+// HTTPMessage.h
+
+// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
+
+
+
+
+
+#pragma once
+
+#include "EnvelopeParser.h"
+
+
+
+
+
+class cHTTPMessage
+{
+public:
+ enum
+ {
+ HTTP_OK = 200,
+ HTTP_BAD_REQUEST = 400,
+ } ;
+
+ enum eKind
+ {
+ mkRequest,
+ mkResponse,
+ } ;
+
+ cHTTPMessage(eKind a_Kind);
+
+ /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length
+ void AddHeader(const AString & a_Key, const AString & a_Value);
+
+ void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
+ void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; }
+
+ const AString & GetContentType (void) const { return m_ContentType; }
+ int GetContentLength(void) const { return m_ContentLength; }
+
+protected:
+ typedef std::map<AString, AString> cNameValueMap;
+
+ eKind m_Kind;
+
+ cNameValueMap m_Headers;
+
+ /// Type of the content; parsed by AddHeader(), set directly by SetContentLength()
+ AString m_ContentType;
+
+ /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength()
+ int m_ContentLength;
+} ;
+
+
+
+
+
+class cHTTPRequest :
+ public cHTTPMessage,
+ protected cEnvelopeParser::cCallbacks
+{
+ typedef cHTTPMessage super;
+
+public:
+ cHTTPRequest(void);
+
+ /** Parses the request line and then headers from the received data.
+ Returns the number of bytes consumed or a negative number for error
+ */
+ int ParseHeaders(const char * a_Data, int a_Size);
+
+ /// Returns true if the request did contain a Content-Length header
+ bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
+
+ /// Returns the method used in the request
+ const AString & GetMethod(void) const { return m_Method; }
+
+ /// Returns the URL used in the request
+ const AString & GetURL(void) const { return m_URL; }
+
+ /// Returns the URL used in the request, without any parameters
+ AString GetBareURL(void) const;
+
+ /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)!
+ void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
+
+ /// Retrieves the UserData pointer that has been stored within this request.
+ void * GetUserData(void) const { return m_UserData; }
+
+ /// Returns true if more data is expected for the request headers
+ bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
+
+ /// Returns true if the request did present auth data that was understood by the parser
+ bool HasAuth(void) const { return m_HasAuth; }
+
+ /// Returns the username that the request presented. Only valid if HasAuth() is true
+ const AString & GetAuthUsername(void) const { return m_AuthUsername; }
+
+ /// Returns the password that the request presented. Only valid if HasAuth() is true
+ const AString & GetAuthPassword(void) const { return m_AuthPassword; }
+
+protected:
+ /// Parser for the envelope data
+ cEnvelopeParser m_EnvelopeParser;
+
+ /// True if the data received so far is parsed successfully. When false, all further parsing is skipped
+ bool m_IsValid;
+
+ /// Bufferred incoming data, while parsing for the request line
+ AString m_IncomingHeaderData;
+
+ /// Method of the request (GET / PUT / POST / ...)
+ AString m_Method;
+
+ /// Full URL of the request
+ AString m_URL;
+
+ /// Data that the HTTPServer callbacks are allowed to store.
+ void * m_UserData;
+
+ /// Set to true if the request contains auth data that was understood by the parser
+ bool m_HasAuth;
+
+ /// The username used for auth
+ AString m_AuthUsername;
+
+ /// The password used for auth
+ AString m_AuthPassword;
+
+
+ /** Parses the incoming data for the first line (RequestLine)
+ Returns the number of bytes consumed, or -1 for an error
+ */
+ int ParseRequestLine(const char * a_Data, int a_Size);
+
+ // cEnvelopeParser::cCallbacks overrides:
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
+} ;
+
+
+
+
+
+class cHTTPResponse :
+ public cHTTPMessage
+{
+ typedef cHTTPMessage super;
+
+public:
+ cHTTPResponse(void);
+
+ /** Appends the response to the specified datastream - response line and headers.
+ The body will be sent later directly through cConnection::Send()
+ */
+ void AppendToData(AString & a_DataStream) const;
+} ;
+
+
+
+
diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp
new file mode 100644
index 000000000..f6f5b0f8b
--- /dev/null
+++ b/source/HTTPServer/HTTPServer.cpp
@@ -0,0 +1,258 @@
+
+// HTTPServer.cpp
+
+// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
+
+#include "Globals.h"
+#include "HTTPServer.h"
+#include "HTTPMessage.h"
+#include "HTTPConnection.h"
+#include "HTTPFormParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+class cDebugCallbacks :
+ public cHTTPServer::cCallbacks,
+ protected cHTTPFormParser::cCallbacks
+{
+ virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
+ {
+ if (cHTTPFormParser::HasFormData(a_Request))
+ {
+ a_Request.SetUserData(new cHTTPFormParser(a_Request, *this));
+ }
+ }
+
+
+ virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override
+ {
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ FormParser->Parse(a_Data, a_Size);
+ }
+ }
+
+
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
+ {
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ if (FormParser->Finish())
+ {
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send("<html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr>\r\n");
+ for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr)
+ {
+ a_Connection.Send(Printf("<tr><td valign=\"top\"><pre>%s</pre></td><td valign=\"top\"><pre>%s</pre></td></tr>\r\n", itr->first.c_str(), itr->second.c_str()));
+ } // for itr - FormParser[]
+ a_Connection.Send("</table></body></html>");
+ return;
+ }
+
+ // Parsing failed:
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/plain");
+ a_Connection.Send(Resp);
+ a_Connection.Send("Form parsing failed");
+ return;
+ }
+
+ // Test the auth failure and success:
+ if (a_Request.GetURL() == "/auth")
+ {
+ if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b"))
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin");
+ return;
+ }
+ }
+
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/plain");
+ a_Connection.Send(Resp);
+ a_Connection.Send("Hello, world");
+ }
+
+
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override
+ {
+ // TODO
+ }
+
+} g_DebugCallbacks;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPServer:
+
+cHTTPServer::cHTTPServer(void) :
+ m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"),
+ m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"),
+ m_Callbacks(NULL)
+{
+}
+
+
+
+
+
+cHTTPServer::~cHTTPServer()
+{
+ Stop();
+}
+
+
+
+
+
+bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6)
+{
+ bool HasAnyPort;
+ HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4);
+ HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort;
+ if (!HasAnyPort)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cHTTPServer::Start(cCallbacks & a_Callbacks)
+{
+ m_Callbacks = &a_Callbacks;
+ if (!m_ListenThreadIPv4.Start())
+ {
+ return false;
+ }
+ if (!m_ListenThreadIPv6.Start())
+ {
+ m_ListenThreadIPv4.Stop();
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+void cHTTPServer::Stop(void)
+{
+ m_ListenThreadIPv4.Stop();
+ m_ListenThreadIPv6.Stop();
+
+ // Drop all current connections:
+ cCSLock Lock(m_CSConnections);
+ while (!m_Connections.empty())
+ {
+ m_Connections.front()->Terminate();
+ } // for itr - m_Connections[]
+}
+
+
+
+
+
+void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket)
+{
+ cHTTPConnection * Connection = new cHTTPConnection(*this);
+ m_SocketThreads.AddClient(a_Socket, Connection);
+ cCSLock Lock(m_CSConnections);
+ m_Connections.push_back(Connection);
+}
+
+
+
+
+
+void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection)
+{
+ m_SocketThreads.RemoveClient(&a_Connection);
+ cCSLock Lock(m_CSConnections);
+ for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
+ {
+ if (*itr == &a_Connection)
+ {
+ m_Connections.erase(itr);
+ break;
+ }
+ }
+ delete &a_Connection;
+}
+
+
+
+
+
+void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection)
+{
+ m_SocketThreads.NotifyWrite(&a_Connection);
+}
+
+
+
+
+
+void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ m_Callbacks->OnRequestBegun(a_Connection, a_Request);
+}
+
+
+
+
+
+void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
+{
+ m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size);
+}
+
+
+
+
+
+void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ m_Callbacks->OnRequestFinished(a_Connection, a_Request);
+ a_Connection.AwaitNextRequest();
+}
+
+
+
+
diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h
new file mode 100644
index 000000000..fea2a9029
--- /dev/null
+++ b/source/HTTPServer/HTTPServer.h
@@ -0,0 +1,101 @@
+
+// HTTPServer.h
+
+// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/ListenThread.h"
+#include "../OSSupport/SocketThreads.h"
+#include "../../iniFile/iniFile.h"
+
+
+
+
+
+// fwd:
+class cHTTPMessage;
+class cHTTPRequest;
+class cHTTPResponse;
+class cHTTPConnection;
+
+typedef std::vector<cHTTPConnection *> cHTTPConnections;
+
+
+
+
+
+
+class cHTTPServer :
+ public cListenThread::cCallback
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /** Called when a new request arrives over a connection and its headers have been parsed.
+ The request body needn't have arrived yet.
+ */
+ virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
+
+ /// Called when another part of request body has arrived.
+ virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0;
+
+ /// Called when the request body has been fully received in previous calls to OnRequestBody()
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
+ } ;
+
+ cHTTPServer(void);
+ ~cHTTPServer();
+
+ /// Initializes the server on the specified ports
+ bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6);
+
+ /// Starts the server and assigns the callbacks to use for incoming requests
+ bool Start(cCallbacks & a_Callbacks);
+
+ /// Stops the server, drops all current connections
+ void Stop(void);
+
+protected:
+ friend class cHTTPConnection;
+
+ cListenThread m_ListenThreadIPv4;
+ cListenThread m_ListenThreadIPv6;
+
+ cSocketThreads m_SocketThreads;
+
+ cCriticalSection m_CSConnections;
+ cHTTPConnections m_Connections; ///< All the connections that are currently being serviced
+
+ /// The callbacks to call for various events
+ cCallbacks * m_Callbacks;
+
+
+ // cListenThread::cCallback overrides:
+ virtual void OnConnectionAccepted(cSocket & a_Socket) override;
+
+ /// Called by cHTTPConnection to close the connection (presumably due to an error)
+ void CloseConnection(cHTTPConnection & a_Connection);
+
+ /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection
+ void NotifyConnectionWrite(cHTTPConnection & a_Connection);
+
+ /// Called by cHTTPConnection when it finishes parsing the request header
+ void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ /// Called by cHTTPConenction when it receives more data for the request body
+ void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size);
+
+ /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received)
+ void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+} ;
+
+
+
+
+
diff --git a/source/HTTPServer/MultipartParser.cpp b/source/HTTPServer/MultipartParser.cpp
new file mode 100644
index 000000000..b49f6ec07
--- /dev/null
+++ b/source/HTTPServer/MultipartParser.cpp
@@ -0,0 +1,256 @@
+
+// MultipartParser.cpp
+
+// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
+
+#include "Globals.h"
+#include "MultipartParser.h"
+#include "NameValueParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// self-test:
+
+#if 0
+
+class cMultipartParserTest :
+ public cMultipartParser::cCallbacks
+{
+public:
+ cMultipartParserTest(void)
+ {
+ cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this);
+ const char Data[] =
+"ThisIsIgnoredPrologue\r\n\
+--MyBoundaryString\r\n\
+\r\n\
+Body with confusing strings\r\n\
+--NotABoundary\r\n\
+--MyBoundaryStringWithPostfix\r\n\
+--\r\n\
+--MyBoundaryString\r\n\
+content-disposition: inline\r\n\
+\r\n\
+This is body\r\n\
+--MyBoundaryString\r\n\
+\r\n\
+Headerless body with trailing CRLF\r\n\
+\r\n\
+--MyBoundaryString--\r\n\
+ThisIsIgnoredEpilogue";
+ printf("Multipart parsing test commencing.\n");
+ Parser.Parse(Data, sizeof(Data) - 1);
+ // DEBUG: Check if the onscreen output corresponds with the data above
+ printf("Multipart parsing test finished\n");
+ }
+
+ virtual void OnPartStart(void) override
+ {
+ printf("Starting a new part\n");
+ }
+
+
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override
+ {
+ printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str());
+ }
+
+
+ virtual void OnPartData(const char * a_Data, int a_Size) override
+ {
+ printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data);
+ }
+
+
+ virtual void OnPartEnd(void) override
+ {
+ printf("Part end\n");
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMultipartParser:
+
+
+cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsValid(true),
+ m_EnvelopeParser(*this),
+ m_HasHadData(false)
+{
+ static AString s_Multipart = "multipart/";
+
+ // Check that the content type is multipart:
+ AString ContentType(a_ContentType);
+ if (strncmp(ContentType.c_str(), "multipart/", 10) != 0)
+ {
+ m_IsValid = false;
+ return;
+ }
+ size_t idxSC = ContentType.find(';', 10);
+ if (idxSC == AString::npos)
+ {
+ m_IsValid = false;
+ return;
+ }
+
+ // Find the multipart boundary:
+ ContentType.erase(0, idxSC + 1);
+ cNameValueParser CTParser(ContentType.c_str(), ContentType.size());
+ CTParser.Finish();
+ if (!CTParser.IsValid())
+ {
+ m_IsValid = false;
+ return;
+ }
+ m_Boundary = CTParser["boundary"];
+ m_IsValid = !m_Boundary.empty();
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body
+ m_EnvelopeParser.SetIsInHeaders(false);
+
+ // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught
+ m_IncomingData.assign("\r\n");
+
+ /*
+ m_Boundary = AString("\r\n--") + m_Boundary
+ m_BoundaryEnd = m_Boundary + "--\r\n";
+ m_Boundary = m_Boundary + "\r\n";
+ */
+}
+
+
+
+
+
+void cMultipartParser::Parse(const char * a_Data, int a_Size)
+{
+ // Skip parsing if invalid
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ // Append to buffer, then parse it:
+ m_IncomingData.append(a_Data, a_Size);
+ while (true)
+ {
+ if (m_EnvelopeParser.IsInHeaders())
+ {
+ int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size());
+ if (BytesConsumed < 0)
+ {
+ m_IsValid = false;
+ return;
+ }
+ if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders())
+ {
+ // All the incoming data has been consumed and still waiting for more
+ return;
+ }
+ m_IncomingData.erase(0, BytesConsumed);
+ }
+
+ // Search for boundary / boundary end:
+ size_t idxBoundary = m_IncomingData.find("\r\n--");
+ if (idxBoundary == AString::npos)
+ {
+ // Boundary string start not present, present as much data to the part callback as possible
+ if (m_IncomingData.size() > m_Boundary.size() + 8)
+ {
+ size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
+ m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
+ m_IncomingData.erase(0, BytesToReport);
+ }
+ return;
+ }
+ if (idxBoundary > 0)
+ {
+ m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary);
+ m_IncomingData.erase(0, idxBoundary);
+ }
+ idxBoundary = 4;
+ size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary);
+ if (LineEnd == AString::npos)
+ {
+ // Not a complete line yet, present as much data to the part callback as possible
+ if (m_IncomingData.size() > m_Boundary.size() + 8)
+ {
+ size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
+ m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
+ m_IncomingData.erase(0, BytesToReport);
+ }
+ return;
+ }
+ if (
+ (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary
+ (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end
+ )
+ {
+ // Got a line, but it's not a boundary, report it as data:
+ m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd);
+ m_IncomingData.erase(0, LineEnd);
+ continue;
+ }
+
+ if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0)
+ {
+ // Boundary or BoundaryEnd found:
+ m_Callbacks.OnPartEnd();
+ size_t idxSlash = idxBoundary + m_Boundary.size();
+ if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-'))
+ {
+ // This was the last part
+ m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4);
+ m_IncomingData.clear();
+ return;
+ }
+ m_Callbacks.OnPartStart();
+ m_IncomingData.erase(0, LineEnd + 2);
+
+ // Keep parsing for the headers that may have come with this data:
+ m_EnvelopeParser.Reset();
+ continue;
+ }
+
+ // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines
+ m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd);
+ m_IncomingData.erase(0, LineEnd);
+ } // while (true)
+}
+
+
+
+
+
+void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value)
+{
+ m_Callbacks.OnPartHeader(a_Key, a_Value);
+}
+
+
+
+
diff --git a/source/HTTPServer/MultipartParser.h b/source/HTTPServer/MultipartParser.h
new file mode 100644
index 000000000..d853929ed
--- /dev/null
+++ b/source/HTTPServer/MultipartParser.h
@@ -0,0 +1,76 @@
+
+// MultipartParser.h
+
+// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
+
+
+
+
+
+#pragma once
+
+#include "EnvelopeParser.h"
+
+
+
+
+
+class cMultipartParser :
+ protected cEnvelopeParser::cCallbacks
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /// Called when a new part starts
+ virtual void OnPartStart(void) = 0;
+
+ /// Called when a complete header line is received for a part
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0;
+
+ /// Called when body for a part is received
+ virtual void OnPartData(const char * a_Data, int a_Size) = 0;
+
+ /// Called when the current part ends
+ virtual void OnPartEnd(void) = 0;
+ } ;
+
+ /// Creates the parser, expects to find the boundary in a_ContentType
+ cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks);
+
+ /// Parses more incoming data
+ void Parse(const char * a_Data, int a_Size);
+
+protected:
+ /// The callbacks to call for various parsing events
+ cCallbacks & m_Callbacks;
+
+ /// True if the data parsed so far is valid; if false, further parsing is skipped
+ bool m_IsValid;
+
+ /// Parser for each part's envelope
+ cEnvelopeParser m_EnvelopeParser;
+
+ /// Buffer for the incoming data until it is parsed
+ AString m_IncomingData;
+
+ /// The boundary, excluding both the initial "--" and the terminating CRLF
+ AString m_Boundary;
+
+ /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting.
+ bool m_HasHadData;
+
+
+ /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size
+ void ParseLine(const char * a_Data, int a_Size);
+
+ /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size
+ void ParseHeaderLine(const char * a_Data, int a_Size);
+
+ // cEnvelopeParser overrides:
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
+} ;
+
+
+
+
diff --git a/source/HTTPServer/NameValueParser.cpp b/source/HTTPServer/NameValueParser.cpp
new file mode 100644
index 000000000..a27f07d19
--- /dev/null
+++ b/source/HTTPServer/NameValueParser.cpp
@@ -0,0 +1,412 @@
+
+// NameValueParser.cpp
+
+// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
+
+#include "Globals.h"
+#include "NameValueParser.h"
+
+
+
+
+
+
+// DEBUG: Self-test
+
+#if 0
+
+class cNameValueParserTest
+{
+public:
+ cNameValueParserTest(void)
+ {
+ const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\"";
+
+ // Now try parsing char-by-char, to debug transitions across datachunk boundaries:
+ cNameValueParser Parser2;
+ for (int i = 0; i < sizeof(Data) - 1; i++)
+ {
+ Parser2.Parse(Data + i, 1);
+ }
+ Parser2.Finish();
+
+ // Parse as a single chunk of data:
+ cNameValueParser Parser(Data, sizeof(Data) - 1);
+
+ // Use the debugger to inspect the Parser variable
+
+ // Check that the two parsers have the same content:
+ for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
+ {
+ ASSERT(Parser2[itr->first] == itr->second);
+ } // for itr - Parser[]
+
+ // Try parsing in 2-char chunks:
+ cNameValueParser Parser3;
+ for (int i = 0; i < sizeof(Data) - 2; i += 2)
+ {
+ Parser3.Parse(Data + i, 2);
+ }
+ if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char
+ {
+ Parser3.Parse(Data + sizeof(Data) - 2, 1);
+ }
+ Parser3.Finish();
+
+ // Check that the third parser has the same content:
+ for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
+ {
+ ASSERT(Parser3[itr->first] == itr->second);
+ } // for itr - Parser[]
+
+ printf("cNameValueParserTest done");
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNameValueParser:
+
+cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) :
+ m_State(psKeySpace),
+ m_AllowsKeyOnly(a_AllowsKeyOnly)
+{
+}
+
+
+
+
+
+cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) :
+ m_State(psKeySpace),
+ m_AllowsKeyOnly(a_AllowsKeyOnly)
+{
+ Parse(a_Data, a_Size);
+}
+
+
+
+
+
+void cNameValueParser::Parse(const char * a_Data, int a_Size)
+{
+ ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong!
+
+ if ((m_State == psInvalid) || (m_State == psFinished))
+ {
+ return;
+ }
+ int Last = 0;
+ for (int i = 0; i < a_Size;)
+ {
+ switch (m_State)
+ {
+ case psKeySpace:
+ {
+ // Skip whitespace until a non-whitespace is found, then start the key:
+ while ((i < a_Size) && (a_Data[i] <= ' '))
+ {
+ i++;
+ }
+ if ((i < a_Size) && (a_Data[i] > ' '))
+ {
+ m_State = psKey;
+ Last = i;
+ }
+ break;
+ }
+
+ case psKey:
+ {
+ // Read the key until whitespace or an equal sign:
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '=')
+ {
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ m_State = psEqual;
+ break;
+ }
+ else if (a_Data[i] <= ' ')
+ {
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ m_State = psEqualSpace;
+ break;
+ }
+ else if (a_Data[i] == ';')
+ {
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if ((a_Data[i] == '\"') || (a_Data[i] == '\''))
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ // Still the key, ran out of data to parse, store the part of the key parsed so far:
+ m_CurrentKey.append(a_Data + Last, a_Size - Last);
+ return;
+ }
+ break;
+ }
+
+ case psEqualSpace:
+ {
+ // The space before the expected equal sign; the current key is already assigned
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '=')
+ {
+ m_State = psEqual;
+ i++;
+ Last = i;
+ break;
+ }
+ else if (a_Data[i] == ';')
+ {
+ // Key-only
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if (a_Data[i] > ' ')
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ } // while (i < a_Size)
+ break;
+ } // case psEqualSpace
+
+ case psEqual:
+ {
+ // just parsed the equal-sign
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if (a_Data[i] == '\"')
+ {
+ i++;
+ Last = i;
+ m_State = psValueInDQuotes;
+ break;
+ }
+ else if (a_Data[i] == '\'')
+ {
+ i++;
+ Last = i;
+ m_State = psValueInSQuotes;
+ break;
+ }
+ else
+ {
+ m_CurrentValue.push_back(a_Data[i]);
+ i++;
+ Last = i;
+ m_State = psValueRaw;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ break;
+ } // case psEqual
+
+ case psValueInDQuotes:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '\"')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psAfterValue;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueInDQuotes
+
+ case psValueInSQuotes:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '\'')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psAfterValue;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueInSQuotes
+
+ case psValueRaw:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psKeySpace;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ }
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueRaw
+
+ case psAfterValue:
+ {
+ // Between the closing DQuote or SQuote and the terminating semicolon
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ m_State = psKeySpace;
+ i++;
+ Last = i;
+ break;
+ }
+ else if (a_Data[i] < ' ')
+ {
+ i++;
+ continue;
+ }
+ m_State = psInvalid;
+ return;
+ } // while (i < a_Size)
+ break;
+ }
+ } // switch (m_State)
+ } // for i - a_Data[]
+}
+
+
+
+
+
+bool cNameValueParser::Finish(void)
+{
+ switch (m_State)
+ {
+ case psInvalid:
+ {
+ return false;
+ }
+ case psFinished:
+ {
+ return true;
+ }
+ case psKey:
+ case psEqualSpace:
+ case psEqual:
+ {
+ if ((m_AllowsKeyOnly) && !m_CurrentKey.empty())
+ {
+ (*this)[m_CurrentKey] = "";
+ m_State = psFinished;
+ return true;
+ }
+ m_State = psInvalid;
+ return false;
+ }
+ case psValueRaw:
+ {
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_State = psFinished;
+ return true;
+ }
+ case psValueInDQuotes:
+ case psValueInSQuotes:
+ {
+ // Missing the terminating quotes, this is an error
+ m_State = psInvalid;
+ return false;
+ }
+ case psKeySpace:
+ case psAfterValue:
+ {
+ m_State = psFinished;
+ return true;
+ }
+ }
+ ASSERT(!"Unhandled parser state!");
+ return false;
+}
+
+
+
+
diff --git a/source/HTTPServer/NameValueParser.h b/source/HTTPServer/NameValueParser.h
new file mode 100644
index 000000000..07dc0b942
--- /dev/null
+++ b/source/HTTPServer/NameValueParser.h
@@ -0,0 +1,70 @@
+
+// NameValueParser.h
+
+// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cNameValueParser :
+ public std::map<AString, AString>
+{
+public:
+ /// Creates an empty parser
+ cNameValueParser(bool a_AllowsKeyOnly = true);
+
+ /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later
+ cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true);
+
+ /// Parses the data given
+ void Parse(const char * a_Data, int a_Size);
+
+ /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid
+ bool Finish(void);
+
+ /// Returns true if the data parsed so far was valid
+ bool IsValid(void) const { return (m_State != psInvalid); }
+
+ /// Returns true if the parser expects no more data
+ bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); }
+
+protected:
+ enum eState
+ {
+ psKeySpace, ///< Parsing the space in front of the next key
+ psKey, ///< Currently adding more chars to the key in m_CurrentKey
+ psEqualSpace, ///< Space after m_CurrentKey
+ psEqual, ///< Just parsed the = sign after a name
+ psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign
+ psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign
+ psValueRaw, ///< Just parsed a raw value without a quote
+ psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end
+ psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped
+ psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data
+ } ;
+
+ /// The current state of the parser
+ eState m_State;
+
+ /// If true, the parser will accept keys without an equal sign and the value
+ bool m_AllowsKeyOnly;
+
+ /// Buffer for the current Key
+ AString m_CurrentKey;
+
+ /// Buffer for the current Value;
+ AString m_CurrentValue;
+
+
+} ;
+
+
+
+
diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp
index 9d38e6f3a..13f5293b9 100644
--- a/source/Items/ItemHandler.cpp
+++ b/source/Items/ItemHandler.cpp
@@ -247,7 +247,7 @@ void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block);
- if (a_Player->GetGameMode() == gmSurvival)
+ if (a_Player->IsGameModeSurvival())
{
if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block))
{
diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp
index a80e186eb..a2b4c8810 100644
--- a/source/ManualBindings.cpp
+++ b/source/ManualBindings.cpp
@@ -1235,7 +1235,10 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
static int tolua_cPluginManager_BindCommand(lua_State * L)
{
- // Function signature: cPluginManager:BindCommand(Command, Permission, Function, HelpString)
+ /* Function signatures:
+ cPluginManager:BindCommand(Command, Permission, Function, HelpString)
+ cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param
+ */
cPluginLua * Plugin = GetLuaPlugin(L);
if (Plugin == NULL)
{
@@ -1244,26 +1247,30 @@ static int tolua_cPluginManager_BindCommand(lua_State * L)
// Read the arguments to this API call:
tolua_Error tolua_err;
+ int idx = 1;
+ if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
+ {
+ idx++;
+ }
if (
- !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) ||
- !tolua_iscppstring(L, 2, 0, &tolua_err) ||
- !tolua_iscppstring(L, 3, 0, &tolua_err) ||
- !tolua_iscppstring(L, 5, 0, &tolua_err) ||
- !tolua_isnoobj (L, 6, &tolua_err)
+ !tolua_iscppstring(L, idx, 0, &tolua_err) ||
+ !tolua_iscppstring(L, idx + 1, 0, &tolua_err) ||
+ !tolua_iscppstring(L, idx + 3, 0, &tolua_err) ||
+ !tolua_isnoobj (L, idx + 4, &tolua_err)
)
{
tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err);
return 0;
}
- if (!lua_isfunction(L, 4))
+ if (!lua_isfunction(L, idx + 2))
{
luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted.");
return 0;
}
- cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0);
- AString Command (tolua_tocppstring(L, 2, ""));
- AString Permission(tolua_tocppstring(L, 3, ""));
- AString HelpString(tolua_tocppstring(L, 5, ""));
+ cPluginManager * self = cPluginManager::Get();
+ AString Command (tolua_tocppstring(L, idx, ""));
+ AString Permission(tolua_tocppstring(L, idx + 1, ""));
+ AString HelpString(tolua_tocppstring(L, idx + 3, ""));
// Store the function reference:
lua_pop(L, 1); // Pop the help string off the stack
@@ -1290,37 +1297,42 @@ static int tolua_cPluginManager_BindCommand(lua_State * L)
static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
{
- // Function signature: cPluginManager:BindConsoleCommand(Command, Function, HelpString)
+ /* Function signatures:
+ cPluginManager:BindConsoleCommand(Command, Function, HelpString)
+ cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param
+ */
// Get the plugin identification out of LuaState:
- lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
- if (!lua_islightuserdata(L, -1))
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == NULL)
{
- LOGERROR("cPluginManager:BindConsoleCommand() cannot get plugin instance, what have you done to my Lua state? Command-binding aborted.");
+ return 0;
}
- cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1);
- lua_pop(L, 1);
// Read the arguments to this API call:
tolua_Error tolua_err;
+ int idx = 1;
+ if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
+ {
+ idx++;
+ }
if (
- !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || // self
- !tolua_iscppstring(L, 2, 0, &tolua_err) || // Command
- !tolua_iscppstring(L, 4, 0, &tolua_err) || // HelpString
- !tolua_isnoobj (L, 5, &tolua_err)
+ !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command
+ !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString
+ !tolua_isnoobj (L, idx + 3, &tolua_err)
)
{
tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err);
return 0;
}
- if (!lua_isfunction(L, 3))
+ if (!lua_isfunction(L, idx + 1))
{
luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted.");
return 0;
}
- cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0);
- AString Command (tolua_tocppstring(L, 2, ""));
- AString HelpString(tolua_tocppstring(L, 4, ""));
+ cPluginManager * self = cPluginManager::Get();
+ AString Command (tolua_tocppstring(L, idx, ""));
+ AString HelpString(tolua_tocppstring(L, idx + 2, ""));
// Store the function reference:
lua_pop(L, 1); // Pop the help string off the stack
@@ -1489,14 +1501,16 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
tolua_Error tolua_err;
tolua_err.array = 0;
- tolua_err.index = 0;
- tolua_err.type = 0;
+ tolua_err.index = 3;
+ tolua_err.type = "function";
std::string Title = "";
int Reference = LUA_REFNIL;
- if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) &&
- lua_isfunction( tolua_S, 3 ) )
+ if (
+ tolua_isstring(tolua_S, 2, 0, &tolua_err ) &&
+ lua_isfunction(tolua_S, 3 )
+ )
{
Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
Title = ((std::string) tolua_tocppstring(tolua_S,2,0));
@@ -2028,12 +2042,12 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPluginManager");
+ tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand);
tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand);
tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);
- tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlayer");
diff --git a/source/Mobs/Horse.h b/source/Mobs/Horse.h
index ea6e441bd..83e64308e 100644
--- a/source/Mobs/Horse.h
+++ b/source/Mobs/Horse.h
@@ -1,7 +1,7 @@
#pragma once
-#include "AggressiveMonster.h"
+#include "PassiveMonster.h"
diff --git a/source/Mobs/IronGolem.h b/source/Mobs/IronGolem.h
index d253aefac..d49ff4cab 100644
--- a/source/Mobs/IronGolem.h
+++ b/source/Mobs/IronGolem.h
@@ -1,7 +1,7 @@
#pragma once
-#include "AggressiveMonster.h"
+#include "PassiveAggressiveMonster.h"
diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp
index 7d553758e..c72b4831b 100644
--- a/source/Mobs/Magmacube.cpp
+++ b/source/Mobs/Magmacube.cpp
@@ -1,7 +1,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-#include "MagmaCube.h"
+#include "Magmacube.h"
diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp
index 1aea006a6..1e31a72d9 100644
--- a/source/Mobs/Zombiepigman.cpp
+++ b/source/Mobs/Zombiepigman.cpp
@@ -1,7 +1,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-#include "ZombiePigman.h"
+#include "Zombiepigman.h"
#include "../World.h"
diff --git a/source/OSSupport/File.cpp b/source/OSSupport/File.cpp
index cc0916711..a4c9a22f4 100644
--- a/source/OSSupport/File.cpp
+++ b/source/OSSupport/File.cpp
@@ -287,6 +287,21 @@ bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName
+bool cFile::IsFolder(const AString & a_Path)
+{
+ #ifdef _WIN32
+ DWORD FileAttrib = GetFileAttributes(a_Path.c_str());
+ return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0));
+ #else
+ struct stat st;
+ return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode));
+ #endif
+}
+
+
+
+
+
int cFile::Printf(const char * a_Fmt, ...)
{
AString buf;
diff --git a/source/OSSupport/File.h b/source/OSSupport/File.h
index 8a057afa8..d4ea0d3a8 100644
--- a/source/OSSupport/File.h
+++ b/source/OSSupport/File.h
@@ -99,6 +99,9 @@ public:
/// Renames a file, returns true if successful. May fail if dest already exists (libc-dependant)!
static bool Rename(const AString & a_OrigFileName, const AString & a_NewFileName);
+ /// Returns true if the specified path is a folder
+ static bool IsFolder(const AString & a_Path);
+
int Printf(const char * a_Fmt, ...);
private:
diff --git a/source/OSSupport/IsThread.cpp b/source/OSSupport/IsThread.cpp
index d5fbfcf19..e1ef84c17 100644
--- a/source/OSSupport/IsThread.cpp
+++ b/source/OSSupport/IsThread.cpp
@@ -53,11 +53,7 @@ static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
cIsThread::cIsThread(const AString & iThreadName) :
m_ThreadName(iThreadName),
m_ShouldTerminate(false),
- #ifdef _WIN32
- m_Handle(NULL)
- #else // _WIN32
- m_HasStarted(false)
- #endif // else _WIN32
+ m_Handle(NULL)
{
}
@@ -77,9 +73,9 @@ cIsThread::~cIsThread()
bool cIsThread::Start(void)
{
+ ASSERT(m_Handle == NULL); // Has already started one thread?
+
#ifdef _WIN32
- ASSERT(m_Handle == NULL); // Has already started one thread?
-
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure
DWORD ThreadID = 0;
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID);
@@ -99,14 +95,11 @@ bool cIsThread::Start(void)
#endif // _DEBUG and _MSC_VER
#else // _WIN32
- ASSERT(!m_HasStarted);
-
if (pthread_create(&m_Handle, NULL, thrExecute, this))
{
LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str());
return false;
}
- m_HasStarted = true;
#endif // else _WIN32
return true;
@@ -158,7 +151,6 @@ bool cIsThread::Wait(void)
LOGD("Thread %s finished", m_ThreadName.c_str());
#endif // LOGD
- m_HasStarted = false;
return (res == 0);
#endif // else _WIN32
}
diff --git a/source/OSSupport/IsThread.h b/source/OSSupport/IsThread.h
index 9b7f0b73e..2ea8bf6f9 100644
--- a/source/OSSupport/IsThread.h
+++ b/source/OSSupport/IsThread.h
@@ -48,7 +48,7 @@ public:
/// Returns the OS-dependent thread ID for the caller's thread
static unsigned long GetCurrentID(void);
-private:
+protected:
AString m_ThreadName;
#ifdef _WIN32
@@ -66,7 +66,6 @@ private:
#else // _WIN32
pthread_t m_Handle;
- bool m_HasStarted;
static void * thrExecute(void * a_Param)
{
diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp
index 0890aabc8..ba3198764 100644
--- a/source/OSSupport/ListenThread.cpp
+++ b/source/OSSupport/ListenThread.cpp
@@ -224,7 +224,10 @@ void cListenThread::Execute(void)
if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead))
{
cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6();
- m_Callback.OnConnectionAccepted(Client);
+ if (Client.IsValid())
+ {
+ m_Callback.OnConnectionAccepted(Client);
+ }
}
} // for itr - m_Sockets[]
} // while (!m_ShouldTerminate)
diff --git a/source/Plugin.cpp b/source/Plugin.cpp
index 229b997cd..98ccfb88c 100644
--- a/source/Plugin.cpp
+++ b/source/Plugin.cpp
@@ -2,10 +2,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Plugin.h"
-#include "Entities/Player.h"
-#include "World.h"
-#include "CommandOutput.h"
-#include "Mobs/Monster.h"
@@ -32,7 +28,11 @@ cPlugin::~cPlugin()
-AString cPlugin::GetLocalDirectory(void) const
+AString cPlugin::GetLocalFolder(void) const
{
return std::string("Plugins/") + m_Directory;
-} \ No newline at end of file
+}
+
+
+
+
diff --git a/source/Plugin.h b/source/Plugin.h
index be803bab2..06e5819df 100644
--- a/source/Plugin.h
+++ b/source/Plugin.h
@@ -121,7 +121,8 @@ public:
void SetVersion(int a_Version) { m_Version = a_Version; }
const AString & GetDirectory(void) const {return m_Directory; }
- AString GetLocalDirectory(void) const;
+ AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead
+ AString GetLocalFolder(void) const;
// tolua_end
diff --git a/source/PluginLua.cpp b/source/PluginLua.cpp
index 81a536838..03aefb098 100644
--- a/source/PluginLua.cpp
+++ b/source/PluginLua.cpp
@@ -81,6 +81,9 @@ bool cPluginLua::Initialize(void)
lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME);
lua_pushstring(m_LuaState, GetName().c_str());
lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME);
+
+ tolua_pushusertype(m_LuaState, this, "cPluginLua");
+ lua_setglobal(m_LuaState, "g_Plugin");
}
std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/";
diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp
index 93ee71926..a557bdc03 100644
--- a/source/PluginManager.cpp
+++ b/source/PluginManager.cpp
@@ -75,16 +75,16 @@ void cPluginManager::FindPlugins(void)
AStringList Files = GetDirectoryContents(PluginsPath.c_str());
for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
{
- if (itr->rfind(".") != AString::npos)
+ if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr)))
{
- // Ignore files, we only want directories
+ // We only want folders, and don't want "." or ".."
continue;
}
// Add plugin name/directory to the list
- if (m_Plugins.find( *itr ) == m_Plugins.end())
+ if (m_Plugins.find(*itr) == m_Plugins.end())
{
- m_Plugins[ *itr ] = NULL;
+ m_Plugins[*itr] = NULL;
}
}
}
@@ -95,7 +95,7 @@ void cPluginManager::FindPlugins(void)
void cPluginManager::ReloadPluginsNow(void)
{
- LOG("Loading plugins");
+ LOG("-- Loading Plugins --");
m_bReloadPlugins = false;
UnloadPluginsNow();
@@ -135,11 +135,15 @@ void cPluginManager::ReloadPluginsNow(void)
if (GetNumPlugins() == 0)
{
- LOG("No plugins loaded");
+ LOG("-- No Plugins Loaded --");
+ }
+ else if ((GetNumPlugins() > 1) || (GetNumPlugins() == 0))
+ {
+ LOG("-- Loaded %i Plugins --", GetNumPlugins());
}
else
{
- LOG("Loaded %i plugin(s)", GetNumPlugins());
+ LOG("-- Loaded 1 Plugin --");
}
}
diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp
index 54bd28c9f..050132917 100644
--- a/source/Protocol/Protocol125.cpp
+++ b/source/Protocol/Protocol125.cpp
@@ -436,15 +436,18 @@ void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_Bloc
WriteDouble (a_BlockZ);
WriteFloat (a_Radius);
WriteInt (a_BlocksAffected.size());
+ int BlockX = (int)a_BlockX;
+ int BlockY = (int)a_BlockY;
+ int BlockZ = (int)a_BlockZ;
for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
{
- WriteByte ((Byte)(itr->x - a_BlockX));
- WriteByte ((Byte)(itr->y - a_BlockY));
- WriteByte ((Byte)(itr->z - a_BlockZ));
+ WriteByte((Byte)(itr->x - BlockX));
+ WriteByte((Byte)(itr->y - BlockY));
+ WriteByte((Byte)(itr->z - BlockZ));
}
- WriteFloat ((float)a_PlayerMotion.x);
- WriteFloat ((float)a_PlayerMotion.y);
- WriteFloat ((float)a_PlayerMotion.z);
+ WriteFloat((float)a_PlayerMotion.x);
+ WriteFloat((float)a_PlayerMotion.y);
+ WriteFloat((float)a_PlayerMotion.z);
Flush();
}
diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp
index be5b45f19..0eac7b081 100644
--- a/source/Protocol/Protocol16x.cpp
+++ b/source/Protocol/Protocol16x.cpp
@@ -7,6 +7,8 @@ Implements the 1.6.x protocol classes:
- release 1.6.1 protocol (#73)
- cProtocol162
- release 1.6.2 protocol (#74)
+ - release 1.6.3 protocol (#77) - no relevant changes
+ - release 1.6.4 protocol (#78) - no relevant changes
(others may be added later in the future for the 1.6 release series)
*/
diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h
index 077c7958b..2447f90a7 100644
--- a/source/Protocol/Protocol16x.h
+++ b/source/Protocol/Protocol16x.h
@@ -7,6 +7,8 @@ Declares the 1.6.x protocol classes:
- release 1.6.1 protocol (#73)
- cProtocol162
- release 1.6.2 protocol (#74)
+ - release 1.6.3 protocol (#77) - no relevant changes
+ - release 1.6.4 protocol (#78) - no relevant changes
(others may be added later in the future for the 1.6 release series)
*/
diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp
index 853018329..fe99b22e1 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/source/Protocol/ProtocolRecognizer.cpp
@@ -55,6 +55,8 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
case PROTO_VERSION_1_5_2: return "1.5.2";
case PROTO_VERSION_1_6_1: return "1.6.1";
case PROTO_VERSION_1_6_2: return "1.6.2";
+ case PROTO_VERSION_1_6_3: return "1.6.3";
+ case PROTO_VERSION_1_6_4: return "1.6.4";
}
ASSERT(!"Unknown protocol version");
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
@@ -707,6 +709,8 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
return true;
}
case PROTO_VERSION_1_6_2:
+ case PROTO_VERSION_1_6_3:
+ case PROTO_VERSION_1_6_4:
{
m_Protocol = new cProtocol162(m_Client);
return true;
@@ -746,6 +750,8 @@ void cProtocolRecognizer::HandleServerPing(void)
case PROTO_VERSION_1_5_2:
case PROTO_VERSION_1_6_1:
case PROTO_VERSION_1_6_2:
+ case PROTO_VERSION_1_6_3:
+ case PROTO_VERSION_1_6_4:
{
// The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h
index 2178d5e61..c53288230 100644
--- a/source/Protocol/ProtocolRecognizer.h
+++ b/source/Protocol/ProtocolRecognizer.h
@@ -18,8 +18,8 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2"
-#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74"
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4"
+#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78"
@@ -42,6 +42,8 @@ public:
PROTO_VERSION_1_5_2 = 61,
PROTO_VERSION_1_6_1 = 73,
PROTO_VERSION_1_6_2 = 74,
+ PROTO_VERSION_1_6_3 = 77,
+ PROTO_VERSION_1_6_4 = 78,
PROTO_VERSION_NEXT,
PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp
index 04c06c887..93f2ccdd3 100644
--- a/source/RCONServer.cpp
+++ b/source/RCONServer.cpp
@@ -78,8 +78,8 @@ protected:
cRCONServer::cRCONServer(cServer & a_Server) :
m_Server(a_Server),
- m_ListenThread4(*this, cSocket::IPv4, "RCON"),
- m_ListenThread6(*this, cSocket::IPv6, "RCON")
+ m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"),
+ m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6")
{
}
diff --git a/source/Root.cpp b/source/Root.cpp
index 3933535f1..290a5269a 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -17,16 +17,23 @@
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
#include "CommandOutput.h"
#include "DeadlockDetect.h"
+#include "OSSupport/Timer.h"
#include "../iniFile/iniFile.h"
-#include <iostream>
+#ifdef _WIN32
+ #include <psapi.h>
+#elif defined(__linux__)
+ #include <fstream>
+#elif defined(__APPLE__)
+ #include <mach/mach.h>
+#endif
-cRoot* cRoot::s_Root = 0;
+cRoot* cRoot::s_Root = NULL;
@@ -98,6 +105,9 @@ void cRoot::Start(void)
m_bStop = false;
while (!m_bStop)
{
+ cTimer Time;
+ long long mseconds = Time.GetNowTime();
+
m_bRestart = false;
LoadGlobalSettings();
@@ -125,58 +135,57 @@ void cRoot::Start(void)
LOG("Starting server...");
if (!m_Server->InitServer(IniFile))
{
- LOGERROR("Failed to start server, shutting down.");
+ LOGERROR("Failure starting server, aborting...");
return;
}
IniFile.WriteFile();
- cIniFile WebIniFile("webadmin.ini");
- if (!WebIniFile.ReadFile())
- {
- LOGWARNING("webadmin.ini inaccessible, wabadmin is disabled");
- }
-
- if (WebIniFile.GetValueB("WebAdmin", "Enabled", false))
- {
- LOG("Creating WebAdmin...");
- m_WebAdmin = new cWebAdmin(8080);
- }
+ LOG("Initialising WebAdmin...");
+ m_WebAdmin = new cWebAdmin();
+ m_WebAdmin->Init();
- LOG("Loading settings...");
+ LOGD("Loading settings...");
m_GroupManager = new cGroupManager();
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
- LOG("Loading worlds...");
+ LOGD("Loading worlds...");
LoadWorlds();
- LOG("Loading plugin manager...");
+ LOGD("Loading plugin manager...");
m_PluginManager = new cPluginManager();
m_PluginManager->ReloadPluginsNow();
- LOG("Loading MonsterConfig...");
+ LOGD("Loading MonsterConfig...");
m_MonsterConfig = new cMonsterConfig;
// This sets stuff in motion
- LOG("Starting Authenticator...");
+ LOGD("Starting Authenticator...");
m_Authenticator.Start();
- LOG("Starting worlds...");
+ LOGD("Starting worlds...");
StartWorlds();
- LOG("Starting deadlock detector...");
+ LOGD("Starting deadlock detector...");
dd.Start();
- LOG("Starting server...");
+ LOGD("Finalising startup...");
m_Server->Start();
+
+ LOG("Starting WebAdmin...");
+ m_WebAdmin->Start();
#if !defined(ANDROID_NDK)
- LOG("Starting InputThread...");
+ LOGD("Starting InputThread...");
m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" );
m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread
#endif
- LOG("Initialization done, server running now.");
+ long long finishmseconds = Time.GetNowTime();
+ finishmseconds -= mseconds;
+
+ LOG("Startup complete, took %i ms!", finishmseconds);
+
while (!m_bStop && !m_bRestart) // These are modified by external threads
{
cSleep::MilliSleep(1000);
@@ -190,37 +199,37 @@ void cRoot::Start(void)
LOG("Shutting down server...");
m_Server->Shutdown();
- LOG("Shutting down deadlock detector...");
+ LOGD("Shutting down deadlock detector...");
dd.Stop();
- LOG("Stopping world threads...");
+ LOGD("Stopping world threads...");
StopWorlds();
- LOG("Stopping authenticator...");
+ LOGD("Stopping authenticator...");
m_Authenticator.Stop();
- LOG("Freeing MonsterConfig...");
+ LOGD("Freeing MonsterConfig...");
delete m_MonsterConfig; m_MonsterConfig = NULL;
- LOG("Stopping WebAdmin...");
+ LOGD("Stopping WebAdmin...");
delete m_WebAdmin; m_WebAdmin = NULL;
- LOG("Unloading recipes...");
+ LOGD("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
- LOG("Forgetting groups...");
+ LOGD("Forgetting groups...");
delete m_GroupManager; m_GroupManager = 0;
- LOG("Unloading worlds...");
+ LOGD("Unloading worlds...");
UnloadWorlds();
- LOG("Stopping plugin manager...");
+ LOGD("Stopping plugin manager...");
delete m_PluginManager; m_PluginManager = NULL;
cItemHandler::Deinit();
cBlockHandler::Deinit();
- LOG("Destroying server...");
+ LOG("Cleaning up...");
//delete HeartBeat; HeartBeat = 0;
delete m_Server; m_Server = 0;
- LOG("Shutdown done.");
+ LOG("Shutdown successful!");
}
delete m_Log; m_Log = 0;
@@ -568,6 +577,110 @@ AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
+int cRoot::GetVirtualRAMUsage(void)
+{
+ #ifdef _WIN32
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc)))
+ {
+ return (int)(pmc.PrivateUsage / 1024);
+ }
+ return -1;
+ #elif defined(__linux__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ std::ifstream StatFile("/proc/self/status");
+ if (!StatFile.good())
+ {
+ return -1;
+ }
+ while (StatFile.good())
+ {
+ AString Line;
+ std::getline(StatFile, Line);
+ if (strncmp(Line.c_str(), "VmSize:", 7) == 0)
+ {
+ int res = atoi(Line.c_str() + 8);
+ return (res == 0) ? -1 : res; // If parsing failed, return -1
+ }
+ }
+ return -1;
+ #elif defined (__APPLE__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ struct task_basic_info t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+ if (KERN_SUCCESS == task_info(
+ mach_task_self(),
+ TASK_BASIC_INFO,
+ (task_info_t)&t_info,
+ &t_info_count
+ ))
+ {
+ return (int)(t_info.virtual_size / 1024);
+ }
+ return -1;
+ #else
+ LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__);
+ return -1;
+ #endif
+}
+
+
+
+
+
+int cRoot::GetPhysicalRAMUsage(void)
+{
+ #ifdef _WIN32
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
+ {
+ return (int)(pmc.WorkingSetSize / 1024);
+ }
+ return -1;
+ #elif defined(__linux__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ std::ifstream StatFile("/proc/self/status");
+ if (!StatFile.good())
+ {
+ return -1;
+ }
+ while (StatFile.good())
+ {
+ AString Line;
+ std::getline(StatFile, Line);
+ if (strncmp(Line.c_str(), "VmRSS:", 7) == 0)
+ {
+ int res = atoi(Line.c_str() + 8);
+ return (res == 0) ? -1 : res; // If parsing failed, return -1
+ }
+ }
+ return -1;
+ #elif defined (__APPLE__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ struct task_basic_info t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+ if (KERN_SUCCESS == task_info(
+ mach_task_self(),
+ TASK_BASIC_INFO,
+ (task_info_t)&t_info,
+ &t_info_count
+ ))
+ {
+ return (int)(t_info.resident_size / 1024);
+ }
+ return -1;
+ #else
+ LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__);
+ return -1;
+ #endif
+}
+
+
+
+
+
void cRoot::LogChunkStats(cCommandOutputCallback & a_Output)
{
int SumNumValid = 0;
diff --git a/source/Root.h b/source/Root.h
index 194b1cbb5..2b15d3461 100644
--- a/source/Root.h
+++ b/source/Root.h
@@ -2,6 +2,7 @@
#pragma once
#include "Authenticator.h"
+#include "HTTPServer/HTTPServer.h"
@@ -104,8 +105,18 @@ public:
/// Finds a player from a partial or complete player name and calls the callback - case-insensitive
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ // tolua_begin
+
/// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
- static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export
+ static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
+
+ /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
+ static int GetVirtualRAMUsage(void);
+
+ /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
+ static int GetPhysicalRAMUsage(void);
+
+ // tolua_end
private:
class cCommand
@@ -141,6 +152,7 @@ private:
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
+ cHTTPServer m_HTTPServer;
cMCLogger * m_Log;
diff --git a/source/Server.cpp b/source/Server.cpp
index 5e65213fd..879bfae5a 100644
--- a/source/Server.cpp
+++ b/source/Server.cpp
@@ -103,8 +103,8 @@ void cServer::cTickThread::Execute(void)
// cServer:
cServer::cServer(void) :
- m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"),
- m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"),
+ m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"),
+ m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"),
m_bIsConnected(false),
m_bRestarting(false),
m_RCONServer(*this),
@@ -206,7 +206,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
return false;
}
- LOG("Starting up server.");
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
@@ -292,7 +291,7 @@ void cServer::PrepareKeys(void)
// TODO: Save and load key for persistence across sessions
// But generating the key takes only a moment, do we even need that?
- LOG("Generating protocol encryption keypair...");
+ LOGD("Generating protocol encryption keypair...");
time_t CurTime = time(NULL);
CryptoPP::RandomPool rng;
@@ -321,7 +320,7 @@ void cServer::OnConnectionAccepted(cSocket & a_Socket)
return;
}
- LOGD("Client \"%s\" connected!", ClientIP.c_str());
+ LOG("Client \"%s\" connected!", ClientIP.c_str());
cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance);
if (!m_SocketThreads.AddClient(a_Socket, NewHandle))
diff --git a/source/Simulator/FireSimulator.cpp b/source/Simulator/FireSimulator.cpp
index 587f45306..da1dc8d15 100644
--- a/source/Simulator/FireSimulator.cpp
+++ b/source/Simulator/FireSimulator.cpp
@@ -234,10 +234,6 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in
return m_BurnStepTimeFuel;
}
}
- if ((a_RelY < cChunkDef::Height - 1) && IsFuel(a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ)))
- {
- return m_BurnStepTimeFuel;
- }
for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
{
@@ -251,7 +247,23 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in
}
}
} // for i - gCrossCoords[]
- return m_BurnStepTimeNonfuel;
+
+ if ((a_RelY > 0) && (a_RelY < cChunkDef::Height - 1))
+ {
+ // Checked through everything, nothing was flammable
+ // If block below isn't solid, we can't have fire, otherwise, we have non-fueled fire
+ BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if (g_BlockIsSolid[BlockBelow])
+ {
+ return m_BurnStepTimeNonfuel;
+ }
+ else
+ {
+ // SetBlock just to make sure fire doesn't spawn
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ return 0;
+ }
+ }
}
diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp
index cb91a4da7..d52b1323f 100644
--- a/source/StringUtils.cpp
+++ b/source/StringUtils.cpp
@@ -196,6 +196,23 @@ AString & StrToUpper(AString & s)
+AString & StrToLower(AString & s)
+{
+ AString::iterator i = s.begin();
+ AString::iterator end = s.end();
+
+ while (i != end)
+ {
+ *i = (char)tolower(*i);
+ ++i;
+ }
+ return s;
+}
+
+
+
+
+
int NoCaseCompare(const AString & s1, const AString & s2)
{
#ifdef _MSC_VER
@@ -658,3 +675,141 @@ AString StripColorCodes(const AString & a_Message)
+
+AString URLDecode(const AString & a_String)
+{
+ AString res;
+ size_t len = a_String.length();
+ res.reserve(len);
+ for (size_t i = 0; i < len; i++)
+ {
+ char ch = a_String[i];
+ if ((ch != '%') || (i > len - 3))
+ {
+ res.push_back(ch);
+ continue;
+ }
+ // Decode the hex value:
+ char hi = a_String[i + 1], lo = a_String[i + 2];
+ if ((hi >= '0') && (hi <= '9'))
+ {
+ hi = hi - '0';
+ }
+ else if ((hi >= 'a') && (hi <= 'f'))
+ {
+ hi = hi - 'a' + 10;
+ }
+ else if ((hi >= 'A') && (hi <= 'F'))
+ {
+ hi = hi - 'F' + 10;
+ }
+ else
+ {
+ res.push_back(ch);
+ continue;
+ }
+ if ((lo >= '0') && (lo <= '9'))
+ {
+ lo = lo - '0';
+ }
+ else if ((lo >= 'a') && (lo <= 'f'))
+ {
+ lo = lo - 'a' + 10;
+ }
+ else if ((lo >= 'A') && (lo <= 'F'))
+ {
+ lo = lo - 'A' + 10;
+ }
+ else
+ {
+ res.push_back(ch);
+ continue;
+ }
+ res.push_back((hi << 4) | lo);
+ i += 2;
+ } // for i - a_String[]
+ return res;
+}
+
+
+
+
+
+AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To)
+{
+ AString res(a_String);
+ std::replace(res.begin(), res.end(), a_From, a_To);
+ return res;
+}
+
+
+
+
+
+/// Converts one Hex character in a Base64 encoding into the data value
+static inline int UnBase64(char c)
+{
+ if (c >='A' && c <= 'Z')
+ {
+ return c - 'A';
+ }
+ if (c >='a' && c <= 'z')
+ {
+ return c - 'a' + 26;
+ }
+ if (c >= '0' && c <= '9')
+ {
+ return c - '0' + 52;
+ }
+ if (c == '+')
+ {
+ return 62;
+ }
+ if (c == '/')
+ {
+ return 63;
+ }
+ if (c == '=')
+ {
+ return -1;
+ }
+ return -2;
+}
+
+
+
+
+
+AString Base64Decode(const AString & a_Base64String)
+{
+ AString res;
+ size_t i, len = a_Base64String.size();
+ int o, c;
+ res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length
+ for (o = 0, i = 0; i < len; i++)
+ {
+ c = UnBase64(a_Base64String[i]);
+ if (c >= 0)
+ {
+ switch (o & 7)
+ {
+ case 0: res[o >> 3] |= (c << 2); break;
+ case 6: res[o >> 3] |= (c >> 4); res[(o >> 3) + 1] |= (c << 4); break;
+ case 4: res[o >> 3] |= (c >> 2); res[(o >> 3) + 1] |= (c << 6); break;
+ case 2: res[o >> 3] |= c; break;
+ }
+ o += 6;
+ }
+ if (c == -1)
+ {
+ // Error while decoding, invalid input. Return as much as we've decoded:
+ res.resize(o >> 3);
+ return res;
+ }
+ }
+ res.resize(o >> 3);
+ return res;}
+
+
+
+
diff --git a/source/StringUtils.h b/source/StringUtils.h
index 211799e91..ec9ba96ce 100644
--- a/source/StringUtils.h
+++ b/source/StringUtils.h
@@ -45,6 +45,9 @@ extern AString TrimString(const AString & str); // tolua_export
/// In-place string conversion to uppercase; returns the same string
extern AString & StrToUpper(AString & s);
+/// In-place string conversion to lowercase; returns the same string
+extern AString & StrToLower(AString & s);
+
/// Case-insensitive string comparison; returns 0 if the strings are the same
extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export
@@ -72,6 +75,15 @@ extern AString EscapeString(const AString & a_Message); // tolua_export
/// Removes all control codes used by MC for colors and styles
extern AString StripColorCodes(const AString & a_Message); // tolua_export
+/// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact
+extern AString URLDecode(const AString & a_String); // Cannot export to Lua automatically - would generated an extra return value
+
+/// Replaces all occurrences of char a_From inside a_String with char a_To.
+extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway
+
+/// Decodes a Base64-encoded string into the raw data
+extern AString Base64Decode(const AString & a_Base64String);
+
// If you have any other string helper functions, declare them here
diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp
index 77a5865d3..08817139a 100644
--- a/source/WebAdmin.cpp
+++ b/source/WebAdmin.cpp
@@ -14,13 +14,8 @@
#include "Server.h"
#include "Root.h"
-#include "../iniFile/iniFile.h"
-
-#ifdef _WIN32
- #include <psapi.h>
-#else
- #include <sys/resource.h>
-#endif
+#include "HTTPServer/HTTPMessage.h"
+#include "HTTPServer/HTTPConnection.h"
@@ -47,313 +42,253 @@ public:
-cWebAdmin * WebAdmin = NULL;
+cWebAdmin::cWebAdmin(void) :
+ m_IsInitialized(false),
+ m_TemplateScript("<webadmin_template>"),
+ m_IniFile("webadmin.ini")
+{
+}
-cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) :
- m_Port(a_Port),
- m_bConnected(false),
- m_TemplateScript("<webadmin_template>")
+void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin )
{
- WebAdmin = this;
- m_Event = new cEvent();
- Init( m_Port );
+ m_Plugins.remove( a_Plugin );
+ m_Plugins.push_back( a_Plugin );
}
-cWebAdmin::~cWebAdmin()
+void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin )
{
+ m_Plugins.remove( a_Plugin );
+}
- WebAdmin = 0;
- m_WebServer->Stop();
- delete m_WebServer;
- delete m_IniFile;
- m_Event->Wait();
- delete m_Event;
+
+
+bool cWebAdmin::Init(void)
+{
+ if (!m_IniFile.ReadFile())
+ {
+ return false;
+ }
+
+ AString PortsIPv4 = m_IniFile.GetValue("WebAdmin", "Port", "8080");
+ AString PortsIPv6 = m_IniFile.GetValue("WebAdmin", "PortsIPv6", "");
+
+ if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6))
+ {
+ return false;
+ }
+ m_IsInitialized = true;
+ return true;
}
-void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin )
+bool cWebAdmin::Start(void)
{
- m_Plugins.remove( a_Plugin );
- m_Plugins.push_back( a_Plugin );
+ if (!m_IsInitialized)
+ {
+ // Not initialized
+ return false;
+ }
+
+ // Initialize the WebAdmin template script and load the file
+ m_TemplateScript.Create();
+ if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
+ {
+ LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua");
+ m_TemplateScript.Close();
+ }
+
+ return m_HTTPServer.Start(*this);
}
-void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin )
+AString cWebAdmin::GetTemplate()
{
- m_Plugins.remove( a_Plugin );
+ AString retVal = "";
+
+ char SourceFile[] = "webadmin/template.html";
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmRead))
+ {
+ return "";
+ }
+
+ // copy the file into the buffer:
+ f.ReadRestOfFile(retVal);
+
+ return retVal;
}
-void cWebAdmin::Request_Handler(webserver::http_request* r)
+void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
- if( WebAdmin == 0 ) return;
- LOG("Path: %s", r->path_.c_str() );
-
- if (r->path_ == "/")
+ if (!a_Request.HasAuth())
{
- r->answer_ += "<h1>MCServer WebAdmin</h1>";
- r->answer_ += "<center>";
- r->answer_ += "<form method='get' action='webadmin/'>";
- r->answer_ += "<input type='submit' value='Log in'>";
- r->answer_ += "</form>";
- r->answer_ += "</center>";
+ a_Connection.SendNeedAuth("MCServer WebAdmin");
return;
}
- if (r->path_.empty() || r->path_[0] != '/')
+ // Check auth:
+ AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", "");
+ if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword))
{
- r->answer_ += "<h1>Bad request</h1>";
- r->answer_ += "<p>";
- r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue.
- r->answer_ += "</p>";
+ a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password");
return;
}
- AStringVector Split = StringSplit(r->path_.substr(1), "/");
-
- if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin"))
+ // Check if the contents should be wrapped in the template:
+ AString URL = a_Request.GetBareURL();
+ ASSERT(URL.length() > 0);
+ bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~'));
+
+ // Retrieve the request data:
+ cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
{
- r->answer_ += "<h1>Bad request</h1>";
+ a_Connection.SendStatusAndReason(500, "Bad UserData");
return;
}
- if (!r->authentication_given_)
- {
- r->answer_ += "no auth";
- r->auth_realm_ = "MCServer WebAdmin";
- }
-
- bool bDontShowTemplate = false;
- if (Split[0] == "~webadmin")
+ // Wrap it all up for the Lua call:
+ AString Template;
+ HTTPTemplateRequest TemplateRequest;
+ TemplateRequest.Request.Username = a_Request.GetAuthUsername();
+ TemplateRequest.Request.Method = a_Request.GetMethod();
+ TemplateRequest.Request.Path = URL.substr(1);
+
+ if (Data->m_Form.Finish())
{
- bDontShowTemplate = true;
+ for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr)
+ {
+ HTTPFormData HTTPfd;
+ HTTPfd.Value = itr->second;
+ HTTPfd.Type = "";
+ HTTPfd.Name = itr->first;
+ TemplateRequest.Request.FormData[itr->first] = HTTPfd;
+ TemplateRequest.Request.PostParams[itr->first] = itr->second;
+ TemplateRequest.Request.Params[itr->first] = itr->second;
+ } // for itr - Data->m_Form[]
}
- AString UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", "");
- if ((UserPassword != "") && (r->password_ == UserPassword))
+ // Try to get the template from the Lua template script
+ if (ShouldWrapInTemplate)
{
- AString Template;
-
- HTTPTemplateRequest TemplateRequest;
- TemplateRequest.Request.Username = r->username_;
- TemplateRequest.Request.Method = r->method_;
- TemplateRequest.Request.Params = r->params_;
- TemplateRequest.Request.PostParams = r->params_post_;
- TemplateRequest.Request.Path = r->path_.substr(1);
-
- for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i )
+ if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template))
{
- webserver::formdata& fd = r->multipart_formdata_[i];
-
- HTTPFormData HTTPfd;//( fd.value_ );
- HTTPfd.Value = fd.value_;
- HTTPfd.Type = fd.content_type_;
- HTTPfd.Name = fd.name_;
- LOGINFO("Form data name: %s", fd.name_.c_str() );
- TemplateRequest.Request.FormData[ fd.name_ ] = HTTPfd;
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
+ return;
}
+ a_Connection.SendStatusAndReason(500, "m_TemplateScript failed");
+ return;
+ }
+
+ AString BaseURL = GetBaseURL(URL);
+ AString Menu;
+ Template = "{CONTENT}";
+ AString FoundPlugin;
- // Try to get the template from the Lua template script
- bool bLuaTemplateSuccessful = false;
- if (!bDontShowTemplate)
- {
- bLuaTemplateSuccessful = WebAdmin->m_TemplateScript.Call("ShowPage", WebAdmin, &TemplateRequest, cLuaState::Return, Template);
- }
-
- if (!bLuaTemplateSuccessful)
+ for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
+ {
+ cWebPlugin * WebPlugin = *itr;
+ std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
+ for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names)
{
- AString BaseURL = WebAdmin->GetBaseURL(Split);
- AString Menu;
- Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate();
- AString FoundPlugin;
-
- for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
- {
- cWebPlugin* WebPlugin = *itr;
- std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
- for( std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names )
- {
- Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
- }
- }
-
- sWebAdminPage Page = WebAdmin->GetPage(TemplateRequest.Request);
- AString Content = Page.Content;
- FoundPlugin = Page.PluginName;
- if (!Page.TabName.empty())
- FoundPlugin += " - " + Page.TabName;
-
- if( FoundPlugin.empty() ) // Default page
- {
- Content.clear();
- FoundPlugin = "Current Game";
- Content += "<h4>Server Name:</h4>";
- Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
-
- Content += "<h4>Plugins:</h4><ul>";
- cPluginManager* PM = cRoot::Get()->GetPluginManager();
- if( PM )
- {
- const cPluginManager::PluginMap & List = PM->GetAllPlugins();
- for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr )
- {
- if( itr->second == NULL ) continue;
- AString VersionNum;
- AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
- }
- }
- Content += "</ul>";
- Content += "<h4>Players:</h4><ul>";
-
- cPlayerAccum PlayerAccum;
- cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
- if( World != NULL )
- {
- World->ForEachPlayer(PlayerAccum);
- Content.append(PlayerAccum.m_Contents);
- }
- Content += "</ul><br>";
- }
-
-
-
- if (!bDontShowTemplate && (Split.size() > 1))
- {
- Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
- }
-
- AString MemUsage = GetMemoryUsage();
- ReplaceString(Template, "{MEM}", MemUsage);
- ReplaceString(Template, "{USERNAME}", r->username_);
- ReplaceString(Template, "{MENU}", Menu);
- ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
- ReplaceString(Template, "{CONTENT}", Content);
- ReplaceString(Template, "{TITLE}", "MCServer");
-
- AString NumChunks;
- Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
- ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
+ Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
}
-
- r->answer_ = Template;
}
- else
+
+ sWebAdminPage Page = GetPage(TemplateRequest.Request);
+ AString Content = Page.Content;
+ FoundPlugin = Page.PluginName;
+ if (!Page.TabName.empty())
{
- r->answer_ += "Wrong username/password";
- r->auth_realm_ = "MCServer WebAdmin";
+ FoundPlugin += " - " + Page.TabName;
}
-}
-
-
-
-
-bool cWebAdmin::Init(int a_Port)
-{
- m_Port = a_Port;
-
- m_IniFile = new cIniFile("webadmin.ini");
- if (m_IniFile->ReadFile())
+ if (FoundPlugin.empty()) // Default page
{
- m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080);
+ Content = GetDefaultPage();
}
- // Initialize the WebAdmin template script and load the file
- m_TemplateScript.Create();
- if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
+ if (ShouldWrapInTemplate && (URL.size() > 1))
{
- LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua");
- m_TemplateScript.Close();
+ Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
}
-
- LOG("Starting WebAdmin on port %i", m_Port);
-
-#ifdef _WIN32
- HANDLE hThread = CreateThread(
- NULL, // default security
- 0, // default stack size
- ListenThread, // name of the thread function
- this, // thread parameters
- 0, // default startup flags
- NULL);
- CloseHandle( hThread ); // Just close the handle immediately
-#else
- pthread_t LstnThread;
- pthread_create( &LstnThread, 0, ListenThread, this);
-#endif
-
- return true;
-}
-
-
-
-
-
-#ifdef _WIN32
-DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam)
-#else
-void *cWebAdmin::ListenThread( void *lpParam )
-#endif
-{
- cWebAdmin* self = (cWebAdmin*)lpParam;
-
- self->m_WebServer = new webserver(self->m_Port, Request_Handler );
- if (!self->m_WebServer->Begin())
+ int MemUsageKiB = GetMemoryUsage();
+ if (MemUsageKiB > 0)
{
- LOGWARN("WebServer failed to start! WebAdmin is disabled");
+ ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024));
+ ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB));
}
-
- self->m_Event->Set();
- return 0;
+ else
+ {
+ ReplaceString(Template, "{MEM}", "unknown");
+ ReplaceString(Template, "{MEMKIB}", "unknown");
+ }
+ ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername());
+ ReplaceString(Template, "{MENU}", Menu);
+ ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
+ ReplaceString(Template, "{CONTENT}", Content);
+ ReplaceString(Template, "{TITLE}", "MCServer");
+
+ AString NumChunks;
+ Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
+ ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
+
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
}
-AString cWebAdmin::GetTemplate()
+void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
- AString retVal = "";
-
- char SourceFile[] = "webadmin/template.html";
-
- cFile f;
- if (!f.Open(SourceFile, cFile::fmRead))
- {
- return "";
- }
-
- // copy the file into the buffer:
- f.ReadRestOfFile(retVal);
-
- return retVal;
+ static const char LoginForm[] = \
+ "<h1>MCServer WebAdmin</h1>" \
+ "<center>" \
+ "<form method='get' action='webadmin/'>" \
+ "<input type='submit' value='Log in'>" \
+ "</form>" \
+ "</center>";
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(LoginForm, sizeof(LoginForm) - 1);
+ a_Connection.FinishResponse();
}
-sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
+sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
{
sWebAdminPage Page;
AStringVector Split = StringSplit(a_Request.Path, "/");
@@ -362,7 +297,7 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
AString FoundPlugin;
if (Split.size() > 1)
{
- for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
+ for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
{
if ((*itr)->GetWebTitle() == Split[1])
{
@@ -385,6 +320,41 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
+AString cWebAdmin::GetDefaultPage(void)
+{
+ AString Content;
+ Content += "<h4>Server Name:</h4>";
+ Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
+
+ Content += "<h4>Plugins:</h4><ul>";
+ cPluginManager * PM = cPluginManager::Get();
+ const cPluginManager::PluginMap & List = PM->GetAllPlugins();
+ for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr)
+ {
+ if (itr->second == NULL)
+ {
+ continue;
+ }
+ AString VersionNum;
+ AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
+ }
+ Content += "</ul>";
+ Content += "<h4>Players:</h4><ul>";
+
+ cPlayerAccum PlayerAccum;
+ cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
+ if( World != NULL )
+ {
+ World->ForEachPlayer(PlayerAccum);
+ Content.append(PlayerAccum.m_Contents);
+ }
+ Content += "</ul><br>";
+ return Content;
+}
+
+
+
+
AString cWebAdmin::GetBaseURL( const AString& a_URL )
{
return GetBaseURL(StringSplit(a_URL, "/"));
@@ -412,26 +382,90 @@ AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit )
-AString cWebAdmin::GetMemoryUsage(void)
+int cWebAdmin::GetMemoryUsage(void)
+{
+ LOGWARNING("%s: This function is obsolete, use cRoot::GetPhysicalRAMUsage() or cRoot::GetVirtualRAMUsage() instead", __FUNCTION__);
+ return cRoot::GetPhysicalRAMUsage();
+}
+
+
+
+
+
+void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
- AString MemUsage;
-#ifndef _WIN32
- rusage resource_usage;
- if (getrusage(RUSAGE_SELF, &resource_usage) != 0)
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
{
- MemUsage = "Error :(";
+ a_Request.SetUserData(new cWebadminRequestData(a_Request));
+ return;
}
- else
+ if (URL == "/")
+ {
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ return;
+ }
+ // TODO: Handle other requests
+}
+
+
+
+
+
+void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
+{
+ cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
+ {
+ return;
+ }
+ Data->OnBody(a_Data, a_Size);
+}
+
+
+
+
+
+void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
+ {
+ HandleWebadminRequest(a_Connection, a_Request);
+ }
+ else if (URL == "/")
{
- Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) );
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ HandleRootRequest(a_Connection, a_Request);
}
-#else
- HANDLE hProcess = GetCurrentProcess();
- PROCESS_MEMORY_COUNTERS pmc;
- if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) )
+ else
{
- Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) );
+ // TODO: Handle other requests
}
-#endif
- return MemUsage;
+
+ // Delete any request data assigned to the request:
+ cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
+ delete Data;
}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWebAdmin::cWebadminRequestData
+
+void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size)
+{
+ m_Form.Parse(a_Data, a_Size);
+}
+
+
+
+
diff --git a/source/WebAdmin.h b/source/WebAdmin.h
index a62b532f1..16b5dd4dc 100644
--- a/source/WebAdmin.h
+++ b/source/WebAdmin.h
@@ -1,8 +1,26 @@
+
+// WebAdmin.h
+
+// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API)
+
#pragma once
-#include "../WebServer/WebServer.h"
#include "OSSupport/Socket.h"
#include "LuaState.h"
+#include "../iniFile/iniFile.h"
+#include "HTTPServer/HTTPServer.h"
+#include "HTTPServer/HTTPFormParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
@@ -10,7 +28,6 @@
// fwd:
class cStringMap;
class cEvent;
-class cIniFile;
class cWebPlugin;
@@ -73,7 +90,8 @@ struct sWebAdminPage
// tolua_begin
-class cWebAdmin
+class cWebAdmin :
+ public cHTTPServer::cCallbacks
{
public:
// tolua_end
@@ -81,57 +99,108 @@ public:
typedef std::list< cWebPlugin* > PluginList;
- cWebAdmin( int a_Port = 8080 );
- ~cWebAdmin();
+ cWebAdmin(void);
- bool Init( int a_Port );
+ /// Initializes the object. Returns true if successfully initialized and ready to start
+ bool Init(void);
+
+ /// Starts the HTTP server taking care of the admin. Returns true if successful
+ bool Start(void);
- void AddPlugin( cWebPlugin* a_Plugin );
- void RemovePlugin( cWebPlugin* a_Plugin );
+ void AddPlugin( cWebPlugin* a_Plugin );
+ void RemovePlugin( cWebPlugin* a_Plugin );
// TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such
PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS <<
- static void Request_Handler(webserver::http_request* r);
-
// tolua_begin
- static AString GetMemoryUsage(void);
-
- int GetPort() { return m_Port; }
+
+ /// Returns the amount of currently used memory, in KiB, or -1 if it cannot be queried
+ static int GetMemoryUsage(void);
sWebAdminPage GetPage(const HTTPRequest& a_Request);
+
+ /// Returns the contents of the default page - the list of plugins and players
+ AString GetDefaultPage(void);
+
AString GetBaseURL(const AString& a_URL);
// tolua_end
AString GetBaseURL(const AStringVector& a_URLSplit);
+
+protected:
+ /// Common base class for request body data handlers
+ class cRequestData
+ {
+ public:
+ virtual ~cRequestData() {} // Force a virtual destructor in all descendants
+
+ /// Called when a new chunk of body data is received
+ virtual void OnBody(const char * a_Data, int a_Size) = 0;
+ } ;
+
+ /// The body handler for requests in the "/webadmin" and "/~webadmin" paths
+ class cWebadminRequestData :
+ public cRequestData,
+ public cHTTPFormParser::cCallbacks
+ {
+ public:
+ cHTTPFormParser m_Form;
+
+
+ cWebadminRequestData(cHTTPRequest & a_Request) :
+ m_Form(a_Request, *this)
+ {
+ }
+
+ // cRequestData overrides:
+ virtual void OnBody(const char * a_Data, int a_Size) override;
+
+ // cHTTPFormParser::cCallbacks overrides. Files are ignored:
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {}
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {}
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {}
+ } ;
+
+
+ /// Set to true if Init() succeeds and the webadmin isn't to be disabled
+ bool m_IsInitialized;
+ /// The webadmin.ini file, used for the settings and allowed logins
+ cIniFile m_IniFile;
-private:
- int m_Port;
+ PluginList m_Plugins;
- bool m_bConnected;
- cSocket m_ListenSocket;
+ /// The Lua template script to provide templates:
+ cLuaState m_TemplateScript;
+
+ /// The HTTP server which provides the underlying HTTP parsing, serialization and events
+ cHTTPServer m_HTTPServer;
- cIniFile * m_IniFile;
- PluginList m_Plugins;
- cEvent * m_Event;
+ AString GetTemplate(void);
+
+ /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs
+ void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ /// Handles requests for the root page
+ void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ // cHTTPServer::cCallbacks overrides:
+ virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
+ virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override;
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
+} ; // tolua_export
- webserver * m_WebServer;
- /// The Lua template script to provide templates:
- cLuaState m_TemplateScript;
- #ifdef _WIN32
- static DWORD WINAPI ListenThread(LPVOID lpParam);
- #else
- static void * ListenThread(void * lpParam);
- #endif
- AString GetTemplate();
-} ; // tolua_export
+// Revert MSVC warnings back to orignal state:
+#if defined(_MSC_VER)
+ #pragma warning(pop)
+#endif
diff --git a/source/World.cpp b/source/World.cpp
index 606ef0787..96a4731d7 100644
--- a/source/World.cpp
+++ b/source/World.cpp
@@ -40,7 +40,7 @@
#include "Mobs/Giant.h"
#include "Mobs/Horse.h"
#include "Mobs/IronGolem.h"
-#include "Mobs/MagmaCube.h"
+#include "Mobs/Magmacube.h"
#include "Mobs/Mooshroom.h"
#include "Mobs/Ocelot.h"
#include "Mobs/Pig.h"
@@ -56,7 +56,7 @@
#include "Mobs/Wither.h"
#include "Mobs/Wolf.h"
#include "Mobs/Zombie.h"
-#include "Mobs/ZombiePigman.h"
+#include "Mobs/Zombiepigman.h"
#include "OSSupport/MakeDir.h"
#include "MersenneTwister.h"
diff --git a/source/World.h b/source/World.h
index dd856fc72..dcb197bd0 100644
--- a/source/World.h
+++ b/source/World.h
@@ -554,8 +554,16 @@ public:
/// Forces a weather change in the next game tick
void ChangeWeather (void);
- /// Returns the current weather
+ /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible
eWeather GetWeather (void) const { return m_Weather; };
+
+ bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
+ bool IsWeatherRain (void) const { return (m_Weather == wRain); }
+ bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
+
+ /// Returns true if the current weather has any precipitation - rain or storm
+ bool IsWeatherWet (void) const { return (m_Weather != wSunny); }
+
// tolua_end
cChunkGenerator & GetGenerator(void) { return m_Generator; }