diff --git a/extensions/curl/FileDownloader.cpp b/extensions/curl/FileDownloader.cpp
new file mode 100644
index 000000000..f087f7fed
--- /dev/null
+++ b/extensions/curl/FileDownloader.cpp
@@ -0,0 +1,81 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Webternet Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include "FileDownloader.h"
+
+using namespace SourceMod;
+
+FileDownloader::FileDownloader( const char *file )
+{
+ fpLocal = fopen(file, "wb");
+}
+
+FileDownloader::~FileDownloader(void)
+{
+ if(this->fpLocal != NULL)
+ {
+ fclose(this->fpLocal);
+ }
+}
+
+DownloadWriteStatus FileDownloader::OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb)
+{
+ size_t total = size * nmemb;
+
+ if (this->fpLocal == NULL)
+ {
+ return DownloadWrite_Error;
+ }
+
+ if (fwrite(ptr, size, nmemb, this->fpLocal) == total)
+ {
+ return DownloadWrite_Okay;
+ }
+ else
+ {
+ return DownloadWrite_Error;
+ }
+}
+
+size_t FileDownloader::GetSize()
+{
+ return ftell(this->fpLocal);
+}
+
+char *SourceMod::FileDownloader::GetBuffer()
+{
+ // TODO: implement this!
+ return NULL;
+}
diff --git a/extensions/curl/FileDownloader.h b/extensions/curl/FileDownloader.h
new file mode 100644
index 000000000..f181b45c1
--- /dev/null
+++ b/extensions/curl/FileDownloader.h
@@ -0,0 +1,61 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Webternet Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_CURLEXT_FILE_DOWNLOADER_IMPL_H_
+#define _INCLUDE_SOURCEMOD_CURLEXT_FILE_DOWNLOADER_IMPL_H_
+
+#include
+
+namespace SourceMod
+{
+ class FileDownloader : public IBaseDownloader
+ {
+ public:
+ FileDownloader(const char *file);
+ ~FileDownloader(void);
+
+ virtual DownloadWriteStatus OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb);
+
+ virtual size_t GetSize();
+
+ virtual char * GetBuffer();
+
+
+ private:
+ FILE *fpLocal;
+ };
+}
+
+#endif
diff --git a/extensions/curl/MemoryDownloader.cpp b/extensions/curl/MemoryDownloader.cpp
new file mode 100644
index 000000000..73ed3a893
--- /dev/null
+++ b/extensions/curl/MemoryDownloader.cpp
@@ -0,0 +1,85 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Updater Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include
+#include
+#include
+#include "MemoryDownloader.h"
+
+using namespace SourceMod;
+
+MemoryDownloader::MemoryDownloader() : buffer(NULL), bufsize(0), bufpos(0)
+{
+}
+
+MemoryDownloader::~MemoryDownloader()
+{
+ free(buffer);
+}
+
+DownloadWriteStatus MemoryDownloader::OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb)
+{
+ size_t total = size * nmemb;
+
+ if (bufpos + total > bufsize)
+ {
+ size_t rem = (bufpos + total) - bufsize;
+ bufsize += rem + (rem / 2);
+ buffer = (char *)realloc(buffer, bufsize);
+ }
+
+ assert(bufpos + total <= bufsize);
+
+ memcpy(&buffer[bufpos], ptr, total);
+ bufpos += total;
+
+ return DownloadWrite_Okay;
+}
+
+void MemoryDownloader::Reset()
+{
+ bufpos = 0;
+}
+
+char *MemoryDownloader::GetBuffer()
+{
+ return buffer;
+}
+
+size_t MemoryDownloader::GetSize()
+{
+ return bufpos;
+}
+
diff --git a/extensions/curl/MemoryDownloader.h b/extensions/curl/MemoryDownloader.h
new file mode 100644
index 000000000..9cb189005
--- /dev/null
+++ b/extensions/curl/MemoryDownloader.h
@@ -0,0 +1,62 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Webternet Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
+#define _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
+
+#include
+
+namespace SourceMod
+{
+ class MemoryDownloader : public IBaseDownloader
+ {
+ public:
+ MemoryDownloader();
+ ~MemoryDownloader();
+ public:
+ DownloadWriteStatus OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb);
+ public:
+ void Reset();
+ char *GetBuffer();
+ size_t GetSize();
+ private:
+ char *buffer;
+ size_t bufsize;
+ size_t bufpos;
+ };
+}
+
+#endif /* _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ */
+
diff --git a/extensions/curl/extension.cpp b/extensions/curl/extension.cpp
index b7c29bc5c..e9a9bb236 100644
--- a/extensions/curl/extension.cpp
+++ b/extensions/curl/extension.cpp
@@ -35,6 +35,9 @@
#include
#include
#include "curlapi.h"
+#include "FileDownloader.h"
+#include "MemoryDownloader.h"
+
/**
* @file extension.cpp
@@ -45,6 +48,17 @@ CurlExt curl_ext; /**< Global singleton for extension's main interface */
SMEXT_LINK(&curl_ext);
+SessionHandler g_SessionHandler;
+HandleType_t g_SessionHandle = 0;
+FormHandler g_FormHandler;
+HandleType_t g_FormHandle = 0;
+DownloadHandler g_DownloadHandler;
+HandleType_t g_DownloadHandle = 0;
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+HTTPSessionManager& g_SessionManager = HTTPSessionManager::instance();
+
bool CurlExt::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
long flags;
@@ -68,11 +82,35 @@ bool CurlExt::SDK_OnLoad(char *error, size_t maxlength, bool late)
return false;
}
+ // Register natives
+ g_pShareSys->AddNatives(myself, curlext_natives);
+ // Register session handle handler
+ g_SessionHandle = g_pHandleSys->CreateType("HTTPSession",
+ &g_SessionHandler, 0, 0, NULL, myself->GetIdentity(), NULL);
+ // Register web form handle handler
+ g_FormHandle = g_pHandleSys->CreateType("HTTPWebForm",
+ &g_FormHandler, 0, 0, NULL, myself->GetIdentity(), NULL);
+ // Register web form handle handler
+ g_DownloadHandle = g_pHandleSys->CreateType("HTTPDownloader",
+ &g_DownloadHandler, 0, 0, NULL, myself->GetIdentity(), NULL);
+ plsys->AddPluginsListener(this);
+ smutils->AddGameFrameHook(&OnGameFrame);
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ g_SessionManager.Initialize();
+
return true;
}
void CurlExt::SDK_OnUnload()
{
+ g_pHandleSys->RemoveType(g_SessionHandle, myself->GetIdentity());
+ g_pHandleSys->RemoveType(g_FormHandle, myself->GetIdentity());
+ g_pHandleSys->RemoveType(g_DownloadHandle, myself->GetIdentity());
+ plsys->RemovePluginsListener(this);
+ smutils->RemoveGameFrameHook(&OnGameFrame);
+ g_SessionManager.Cleanup();
curl_global_cleanup();
}
@@ -86,3 +124,754 @@ const char *CurlExt::GetExtensionDateString()
return SOURCEMOD_BUILD_TIME;
}
+void OnGameFrame(bool simulating)
+{
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ g_SessionManager.RunFrame();
+}
+
+void CurlExt::OnPluginUnloaded( IPlugin *plugin )
+{
+ g_SessionManager.PluginUnloaded(plugin);
+}
+
+static cell_t HTTP_CreateFileDownloader(IPluginContext *pCtx, const cell_t *params)
+{
+ char *file;
+ // 1st param: file name
+ pCtx->LocalToString(params[1], &file);
+
+ char localPath[PLATFORM_MAX_PATH];
+ // Build absolute path for fopen()
+ smutils->BuildPath(Path_Game, localPath, PLATFORM_MAX_PATH, file);
+
+ // Use file-based download handler
+ FileDownloader *downloader = new FileDownloader(localPath);
+
+ // This should never happen but, oh well...
+ if (!downloader)
+ {
+ return pCtx->ThrowNativeError("Could not create downloader");
+ }
+
+ return g_pHandleSys->CreateHandle(g_DownloadHandle,
+ (void*)downloader,
+ pCtx->GetIdentity(),
+ myself->GetIdentity(),
+ NULL);
+}
+
+static cell_t HTTP_CreateMemoryDownloader(IPluginContext *pCtx, const cell_t *params)
+{
+ MemoryDownloader *downloader = new MemoryDownloader();
+
+ if (!downloader)
+ {
+ return pCtx->ThrowNativeError("Could not create downloader");
+ }
+
+ return g_pHandleSys->CreateHandle(g_DownloadHandle,
+ (void*)downloader,
+ pCtx->GetIdentity(),
+ myself->GetIdentity(),
+ NULL);
+}
+
+static cell_t HTTP_CreateWebForm(IPluginContext *pCtx, const cell_t *params)
+{
+ IWebForm *form = g_webternet.CreateForm();
+
+ if (!form)
+ {
+ return pCtx->ThrowNativeError("Could not create web form");
+ }
+
+ return g_pHandleSys->CreateHandle(g_FormHandle,
+ (void*)form,
+ pCtx->GetIdentity(),
+ myself->GetIdentity(),
+ NULL);
+}
+
+static cell_t HTTP_AddStringToWebForm(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: web form handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebForm *form;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl, g_FormHandle, &sec, (void **)&form)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid web form handle %x (error %d)", hndl, err);
+ }
+
+ if (!form)
+ {
+ return pCtx->ThrowNativeError("HTTP Web Form data not found\n");
+ }
+
+ char *name, *data;
+ // 2nd param: name of the POST variable
+ pCtx->LocalToString(params[2], &name);
+ // 3rd param: value of the POST variable
+ pCtx->LocalToString(params[3], &data);
+
+ return form->AddString(name, data);
+}
+
+static cell_t HTTP_AddFileToWebForm(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: web form handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebForm *form;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl, g_FormHandle, &sec, (void **)&form)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid web form handle %x (error %d)", hndl, err);
+ }
+
+ if (!form)
+ {
+ return pCtx->ThrowNativeError("HTTP Web Form data not found\n");
+ }
+
+ char *name, *path;
+ // 2nd param: name of the POST variable
+ pCtx->LocalToString(params[2], &name);
+ // 3rd param: value of the POST variable
+ pCtx->LocalToString(params[3], &path);
+
+ return form->AddFile(name, path);
+}
+
+static cell_t HTTP_CreateSession(IPluginContext *pCtx, const cell_t *params)
+{
+ IWebTransfer *x = g_webternet.CreateSession();
+
+ if (!x)
+ {
+ return pCtx->ThrowNativeError("Could not create session");
+ }
+
+ return g_pHandleSys->CreateHandle(g_SessionHandle,
+ (void*)x,
+ pCtx->GetIdentity(),
+ myself->GetIdentity(),
+ NULL);
+}
+
+static cell_t HTTP_GetLastError(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: session handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebTransfer *x;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl, g_SessionHandle, &sec, (void **)&x)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid session handle %x (error %d)", hndl, err);
+ }
+
+ if (!x)
+ {
+ pCtx->ThrowNativeError("HTTP Session data not found\n");
+
+ return false;
+ }
+
+ // Copy error message in output string
+ pCtx->StringToLocalUTF8(params[2], params[3], x->LastErrorMessage(), NULL);
+
+ return true;
+}
+
+static cell_t HTTP_PostAndDownload(IPluginContext *pCtx, const cell_t *params)
+{
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ HTTPRequestHandleSet handles;
+ // 1st param: session handle
+ handles.hndlSession = static_cast(params[1]);
+
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebTransfer *x;
+
+ if ((err = g_pHandleSys->ReadHandle(handles.hndlSession,
+ g_SessionHandle, &sec, (void **)&x)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid session handle %x (error %d)",
+ handles.hndlSession, err);
+ }
+
+ if (!x)
+ {
+ return pCtx->ThrowNativeError("HTTP Session data not found\n");
+ }
+
+ // 2nd param: downloader handle
+ handles.hndlDownloader = static_cast(params[2]);
+ IBaseDownloader *downloader;
+
+ if ((err = g_pHandleSys->ReadHandle(handles.hndlDownloader,
+ g_DownloadHandle, &sec, (void **)&downloader)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid downloader handle %x (error %d)",
+ handles.hndlDownloader, err);
+ }
+
+ if (!downloader)
+ {
+ return pCtx->ThrowNativeError("HTTP Downloader data not found\n");
+ }
+
+ // 3rd param: web form handle
+ handles.hndlForm = static_cast(params[3]);;
+ IWebForm *form;
+
+ if ((err = g_pHandleSys->ReadHandle(handles.hndlForm,
+ g_FormHandle, &sec, (void **)&form)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid web form handle %x (error %d)",
+ handles.hndlForm, err);
+ }
+
+ if (!form)
+ {
+ return pCtx->ThrowNativeError("HTTP Web Form data not found\n");
+ }
+
+ char *url;
+ // 4th param: target URL
+ pCtx->LocalToString(params[4], &url);
+
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ HTTPRequestCompletedContextPack contextPack;
+ contextPack.pCallbackFunction = new HTTPRequestCompletedContextFunction;
+
+ contextPack.pCallbackFunction->pContext = pCtx;
+ // 5th param: callback function
+ contextPack.pCallbackFunction->uPluginFunction = params[5];
+
+ // 6th param: custom user data
+ if (params[0] >= 6)
+ {
+ contextPack.pCallbackFunction->bHasContext = true;
+ contextPack.iPluginContextValue = params[6];
+ }
+
+ // Queue request for asynchronous execution
+ g_SessionManager.PostAndDownload(pCtx, handles, url, contextPack);
+
+ return true;
+}
+
+static cell_t HTTP_Download(IPluginContext *pCtx, const cell_t *params)
+{
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ HTTPRequestHandleSet handles;
+ // 1st param: session handle
+ handles.hndlSession = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebTransfer *x;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(handles.hndlSession,
+ g_SessionHandle, &sec, (void **)&x)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid session handle %x (error %d)",
+ handles.hndlSession, err);
+ }
+
+ if (!x)
+ {
+ return pCtx->ThrowNativeError("HTTP Session data not found\n");
+ }
+
+ // 2nd param: downloader handle
+ handles.hndlDownloader = static_cast(params[2]);
+ IBaseDownloader *downloader;
+
+ if ((err = g_pHandleSys->ReadHandle(handles.hndlDownloader,
+ g_DownloadHandle, &sec, (void **)&downloader)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid downloader handle %x (error %d)",
+ handles.hndlDownloader, err);
+ }
+
+ if (!downloader)
+ {
+ return pCtx->ThrowNativeError("HTTP Downloader data not found\n");
+ }
+
+ char *url;
+ // 3rd param: target URL
+ pCtx->LocalToString(params[3], &url);
+
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ HTTPRequestCompletedContextPack contextPack;
+ contextPack.pCallbackFunction = new HTTPRequestCompletedContextFunction;
+
+ contextPack.pCallbackFunction->pContext = pCtx;
+ // 4th param: callback function
+ contextPack.pCallbackFunction->uPluginFunction = params[4];
+
+ // 5th param: custom user data
+ if (params[0] >= 5)
+ {
+ contextPack.pCallbackFunction->bHasContext = true;
+ contextPack.iPluginContextValue = params[5];
+ }
+
+ // Queue request for asynchronous execution
+ g_SessionManager.Download(pCtx, handles, url, contextPack);
+
+ return true;
+}
+
+static cell_t HTTP_GetBodySize(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: session handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IBaseDownloader *dldr = NULL;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl,
+ g_DownloadHandle, &sec, (void **)&dldr)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid downloader handle %x (error %d)", hndl, err);
+ }
+
+ if (!dldr)
+ {
+ return pCtx->ThrowNativeError("HTTP Downloader data not found\n");
+ }
+
+ return dldr->GetSize();
+}
+
+static cell_t HTTP_GetBodyContent(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: session handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IBaseDownloader *dldr = NULL;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl,
+ g_DownloadHandle, &sec, (void **)&dldr)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid downloader handle %x (error %d)", hndl, err);
+ }
+
+ if (!dldr)
+ {
+ return pCtx->ThrowNativeError("HTTP Downloader data not found\n");
+ }
+
+ char *body;
+ // 2nd param: receiving buffer
+ pCtx->LocalToString(params[2], &body);
+ // 3rd param: max buffer length
+ uint32_t bodySize = params[3];
+
+ // NOTE: we need one additional byte for the null terminator
+ if (bodySize < (dldr->GetSize() + 1))
+ {
+ return pCtx->ThrowNativeError("Buffer too small\n");
+ }
+
+ memcpy(body, dldr->GetBuffer(), params[3]);
+ body[dldr->GetSize()] = '\0';
+
+ return pCtx->StringToLocal(params[2], params[3], body);
+}
+
+static cell_t HTTP_SetFailOnHTTPError(IPluginContext *pCtx, const cell_t *params)
+{
+ // 1st param: session handle
+ Handle_t hndl = static_cast(params[1]);
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebTransfer *xfer;
+
+ // Validate handle data
+ if ((err = g_pHandleSys->ReadHandle(hndl,
+ g_SessionHandle, &sec, (void **)&xfer)) != HandleError_None)
+ {
+ return pCtx->ThrowNativeError("Invalid session handle %x (error %d)", hndl, err);
+ }
+
+ if (!xfer)
+ {
+ return pCtx->ThrowNativeError("HTTP Session data not found\n");
+ }
+
+ return xfer->SetFailOnHTTPError(static_cast(params[2]));
+}
+
+const sp_nativeinfo_t curlext_natives[] =
+{
+ {"HTTP_CreateFileDownloader", HTTP_CreateFileDownloader},
+ {"HTTP_CreateMemoryDownloader", HTTP_CreateMemoryDownloader},
+ {"HTTP_CreateSession", HTTP_CreateSession},
+ {"HTTP_SetFailOnHTTPError", HTTP_SetFailOnHTTPError},
+ {"HTTP_GetLastError", HTTP_GetLastError},
+ {"HTTP_Download", HTTP_Download},
+ {"HTTP_PostAndDownload", HTTP_PostAndDownload},
+ {"HTTP_CreateWebForm", HTTP_CreateWebForm},
+ {"HTTP_AddStringToWebForm", HTTP_AddStringToWebForm},
+ {"HTTP_AddFileToWebForm", HTTP_AddFileToWebForm},
+ {"HTTP_GetBodySize", HTTP_GetBodySize},
+ {"HTTP_GetBodyContent", HTTP_GetBodyContent},
+ {NULL, NULL},
+};
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::PluginUnloaded( IPlugin *plugin )
+{
+ /************************************************************************/
+ /* NEW CODE */
+ /************************************************************************/
+ // Check for pending requests and cancel them
+ {
+ pRequestsLock->Lock();
+
+ if (!requests.empty())
+ {
+ // Run through requests queue
+ for (std::deque::iterator i(requests.begin()), end(requests.end()); i != end; ++i)
+ {
+ // Identify requests associated to (nearly) unmapped plugin context
+ if (i->pCtx == plugin->GetBaseContext())
+ {
+ // All context related data and callbacks are marked invalid
+ i->pCtx = NULL;
+ i->contextPack.pCallbackFunction = NULL;
+ }
+ }
+ }
+
+ pRequestsLock->Unlock();
+ }
+
+ // Wait for running requests to finish
+ if (!threads.empty())
+ {
+ for (std::list::iterator i(threads.begin()), end(threads.end()); i != end; ++i)
+ {
+ if ((*i) != NULL)
+ {
+ (*i)->WaitForThread();
+ // NOTE: do not remove handles here as it would break
+ // the iteration. Dead thread handles are destroyed
+ // on the next available game frame in the main thread
+
+ // Check for pending callbacks and cancel them
+ {
+ pCallbacksLock->Lock();
+
+ if (!callbacks.empty())
+ {
+ // Run through callback queue
+ for (std::deque::iterator i(callbacks.begin()), end(callbacks.end()); i != end; ++i)
+ {
+ // Identify callbacks associated to (nearly) unmapped plugin context
+ if (i->pCtx == plugin->GetBaseContext())
+ {
+ // All context related data and callbacks are marked invalid
+ i->pCtx = NULL;
+ i->contextPack.pCallbackFunction = NULL;
+ }
+ }
+ }
+
+ pCallbacksLock->Unlock();
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::PostAndDownload( IPluginContext *pCtx,
+ HTTPRequestHandleSet handles, const char *url,
+ HTTPRequestCompletedContextPack contextPack )
+{
+ HTTPRequest request;
+ BurnSessionHandle(pCtx, handles);
+
+ request.pCtx = pCtx;
+ request.handles = handles;
+ request.method = HTTP_POST;
+ request.url = url;
+ request.contextPack = contextPack;
+
+ pRequestsLock->Lock();
+ this->requests.push_front(request);
+ pRequestsLock->Unlock();
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::Download( IPluginContext *pCtx,
+ HTTPRequestHandleSet handles, const char *url,
+ HTTPRequestCompletedContextPack contextPack )
+{
+ HTTPRequest request;
+ BurnSessionHandle(pCtx, handles);
+
+ request.pCtx = pCtx;
+ request.handles = handles;
+ request.method = HTTP_GET;
+ request.url = url;
+ request.contextPack = contextPack;
+
+ pRequestsLock->Lock();
+ this->requests.push_front(request);
+ pRequestsLock->Unlock();
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::BurnSessionHandle( IPluginContext * pCtx, HTTPRequestHandleSet &handles )
+{
+ HandleSecurity sec;
+ sec.pOwner = pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ // TODO: maybe better way to do this?
+ // Make session handle inaccessible to the user
+ Handle_t hndlNew = g_pHandleSys->FastCloneHandle(handles.hndlSession);
+ if (g_pHandleSys->FreeHandle(handles.hndlSession, &sec) !=
+ HandleError_None)
+ {
+ pCtx->ThrowNativeError("Couldn't free HTTP session handle");
+ return;
+ }
+
+ handles.hndlSession = hndlNew;
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::RunFrame()
+{
+ // Try to execute pending callbacks
+ if (this->pCallbacksLock->TryLock())
+ {
+ if (!this->callbacks.empty())
+ {
+ HTTPRequest request = this->callbacks.front();
+ IPluginContext *pCtx = request.pCtx;
+
+ // Is the requesting plugin still alive?
+ if (pCtx != NULL)
+ {
+ // TODO: this may be unnecessary
+ if (plsys->FindPluginByContext(request.pCtx->GetContext()))
+ {
+ funcid_t id = request.contextPack.pCallbackFunction->uPluginFunction;
+ IPluginFunction *pFunction = NULL;
+ pFunction = pCtx->GetFunctionById(id);
+
+ if (pFunction != NULL)
+ {
+ // Push data and execute callback
+ pFunction->PushCell(request.handles.hndlSession);
+ pFunction->PushCell(request.result);
+ pFunction->PushCell(request.handles.hndlDownloader);
+ if (request.contextPack.pCallbackFunction->bHasContext)
+ {
+ pFunction->PushCell(request.contextPack.iPluginContextValue);
+ }
+ pFunction->Execute(NULL);
+ }
+ }
+ }
+
+ this->callbacks.pop_front();
+ }
+
+ this->pCallbacksLock->Unlock();
+ }
+
+ // Try to fire up some new asynchronous requests
+ if (pRequestsLock->TryLock())
+ {
+ if (!this->requests.empty())
+ {
+ // NOTE: this is my "burst thread creation" solution
+ // Using a thread pool is slow as it executes the threads
+ // sequentially and not parallel.
+ // Not using a thread pool might cause SRCDS to crash, so
+ // we are spawning just a few threads every frame to not
+ // affect performance too much and still having the advantage
+ // of parallel execution.
+ for (unsigned int i = 0; i < iMaxRequestsPerFrame; i++)
+ {
+ // Create new thread object
+ HTTPAsyncRequestHandler *async =
+ new HTTPAsyncRequestHandler(this->requests.front());
+ // Skip requests with unloaded parent plugin
+ if (this->requests.front().pCtx != NULL)
+ {
+ // Create new thread
+ IThreadHandle *pThread =
+ threader->MakeThread(async, Thread_Default);
+ // Save thread handle
+ this->threads.push_front(pThread);
+ }
+ // Remove request as it's being handled now
+ this->requests.pop_front();
+ }
+ }
+
+ pRequestsLock->Unlock();
+ }
+
+ // Do some quick "garbage collection" on finished threads
+ if (!this->threads.empty())
+ {
+ for (std::list::iterator i(threads.begin()), end(threads.end()); i != end; ++i)
+ {
+ if ((*i) != NULL)
+ {
+ if ((*i)->GetState() == Thread_Done)
+ {
+ (*i)->DestroyThis();
+ this->threads.remove((*i));
+ // NOTE: this action breaks the iteration so we have
+ // to leave the loop and start over on next frame
+ break;
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::Initialize()
+{
+ pRequestsLock = threader->MakeMutex();
+ pCallbacksLock = threader->MakeMutex();
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::Cleanup()
+{
+ if (pRequestsLock != NULL)
+ {
+ pRequestsLock->DestroyThis();
+ }
+
+ if (pCallbacksLock != NULL)
+ {
+ pCallbacksLock->DestroyThis();
+ }
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::AddCallback( HTTPRequest request )
+{
+ this->pCallbacksLock->Lock();
+ this->callbacks.push_front(request);
+ this->pCallbacksLock->Unlock();
+}
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+void HTTPSessionManager::HTTPAsyncRequestHandler::RunThread( IThreadHandle *pHandle )
+{
+ HandleError err;
+ HandleSecurity sec;
+ sec.pOwner = this->request.pCtx->GetIdentity();
+ sec.pIdentity = myself->GetIdentity();
+
+ IWebTransfer *xfer = NULL;
+ g_pHandleSys->ReadHandle(this->request.handles.hndlSession,
+ g_SessionHandle, &sec, (void **)&xfer);
+
+ IBaseDownloader *downldr = NULL;
+ g_pHandleSys->ReadHandle(this->request.handles.hndlDownloader,
+ g_DownloadHandle, &sec, (void **)&downldr);
+
+ switch (this->request.method)
+ {
+ case HTTP_GET:
+ this->request.result =
+ xfer->Download(this->request.url, downldr, NULL);
+ break;
+ case HTTP_POST:
+ IWebForm *form = NULL;
+ g_pHandleSys->ReadHandle(this->request.handles.hndlForm,
+ g_FormHandle, &sec, (void **)&form);
+
+ this->request.result =
+ xfer->PostAndDownload(this->request.url, form, downldr, NULL);
+ break;
+ }
+
+ // DEBUG
+ //g_pHandleSys->FreeHandle(this->request.handles.hndlDownloader, &sec);
+ //smutils->AddFrameAction(ExecuteCallback, new HTTPRequest(this->request));
+ g_SessionManager.AddCallback(request);
+}
diff --git a/extensions/curl/extension.h b/extensions/curl/extension.h
index 9241961b8..7efea50cf 100644
--- a/extensions/curl/extension.h
+++ b/extensions/curl/extension.h
@@ -38,13 +38,17 @@
*/
#include "smsdk_ext.h"
+#include "IWebternet.h"
+#include "IBaseDownloader.h"
+#include
+#include
/**
* @brief Sample implementation of the SDK Extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
*/
-class CurlExt : public SDKExtension
+class CurlExt : public SDKExtension, IPluginsListener
{
public:
/**
@@ -83,6 +87,9 @@ public:
//virtual bool QueryRunning(char *error, size_t maxlength);
const char *GetExtensionVerString();
const char *GetExtensionDateString();
+
+ virtual void OnPluginUnloaded( IPlugin *plugin );
+
public:
#if defined SMEXT_CONF_METAMOD
/**
@@ -121,4 +128,146 @@ public:
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap);
+// Handle helper class
+class SessionHandler : public IHandleTypeDispatch
+{
+public:
+ virtual void OnHandleDestroy( HandleType_t type, void *object )
+ {
+ delete ((IWebTransfer *)object);
+ }
+};
+
+extern SessionHandler g_SessionHandler;
+extern HandleType_t g_SessionHandle;
+
+class FormHandler : public IHandleTypeDispatch
+{
+ virtual void OnHandleDestroy( HandleType_t type, void *object )
+ {
+ delete ((IWebForm *)object);
+ }
+};
+
+extern FormHandler g_FormHandler;
+extern HandleType_t g_FormHandle;
+
+class DownloadHandler : public IHandleTypeDispatch
+{
+ virtual void OnHandleDestroy( HandleType_t type, void *object )
+ {
+ delete ((IBaseDownloader *)object);
+ }
+};
+
+extern DownloadHandler g_DownloadHandler;
+extern HandleType_t g_DownloadHandle;
+
+struct HTTPRequestCompletedContextFunction {
+ // NOTE: deprecated!
+ IPluginContext *pContext;
+ funcid_t uPluginFunction;
+ bool bHasContext;
+};
+
+union HTTPRequestCompletedContextPack {
+ uint64_t ulContextValue;
+ struct {
+ HTTPRequestCompletedContextFunction *pCallbackFunction;
+ cell_t iPluginContextValue;
+ };
+};
+
+
+/************************************************************************/
+/* NEW CODE */
+/************************************************************************/
+struct HTTPRequestHandleSet
+{
+ Handle_t hndlSession;
+ Handle_t hndlDownloader;
+ Handle_t hndlForm;
+};
+
+class HTTPSessionManager
+{
+public:
+ static HTTPSessionManager& instance()
+ {
+ static HTTPSessionManager _instance;
+ return _instance;
+ }
+ ~HTTPSessionManager() {}
+
+ void Initialize();
+ void Cleanup();
+ void PluginUnloaded(IPlugin *plugin);
+ void RunFrame();
+ void BurnSessionHandle(IPluginContext * pCtx, HTTPRequestHandleSet &handles);
+ void PostAndDownload(IPluginContext *pCtx,
+ HTTPRequestHandleSet handles,
+ const char *url,
+ HTTPRequestCompletedContextPack contextPack);
+ void Download(IPluginContext *pCtx,
+ HTTPRequestHandleSet handles,
+ const char *url,
+ HTTPRequestCompletedContextPack contextPack);
+protected:
+
+private:
+ HTTPSessionManager() {}
+ HTTPSessionManager(const HTTPSessionManager&);
+ HTTPSessionManager & operator = (const HTTPSessionManager &);
+
+ enum HTTPRequestMethod
+ {
+ HTTP_GET,
+ HTTP_POST
+ };
+
+ struct HTTPRequest
+ {
+ IPluginContext *pCtx;
+ HTTPRequestMethod method;
+ HTTPRequestHandleSet handles;
+ const char *url;
+ HTTPRequestCompletedContextPack contextPack;
+ cell_t result;
+ };
+
+ static const unsigned int iMaxRequestsPerFrame = 20;
+ IMutex *pRequestsLock;
+ std::deque requests;
+ // NOTE: this needs no lock since it's only accessed from main thread
+ std::list threads;
+ IMutex *pCallbacksLock;
+ std::deque callbacks;
+
+ class HTTPAsyncRequestHandler : public IThread
+ {
+ public:
+ HTTPAsyncRequestHandler(HTTPRequest request)
+ {
+ this->request = request;
+ }
+ ~HTTPAsyncRequestHandler() {}
+ protected:
+ private:
+ HTTPRequest request;
+ virtual void RunThread( IThreadHandle *pHandle );
+ virtual void OnTerminate( IThreadHandle *pHandle, bool cancel )
+ {
+ delete this;
+ }
+ static void ExecuteCallback(void *data);
+ };
+
+ void AddCallback(HTTPRequest request);
+};
+
+void OnGameFrame(bool simulating);
+
+// Natives
+extern const sp_nativeinfo_t curlext_natives[];
+
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
diff --git a/extensions/curl/msvc10/curl.sln b/extensions/curl/msvc10/curl.sln
new file mode 100644
index 000000000..9e47eaab9
--- /dev/null
+++ b/extensions/curl/msvc10/curl.sln
@@ -0,0 +1,98 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "curl", "curl.vcxproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "curllib", "..\curl-src\lib\curllib.vcxproj", "{32E22C76-2FE8-2308-E702-DB81C76357DB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "curlsrc", "..\curl-src\src\curlsrc.vcxproj", "{1E99F660-6BDD-C14B-4931-6D6FCFB83177}"
+ ProjectSection(ProjectDependencies) = postProject
+ {32E22C76-2FE8-2308-E702-DB81C76357DB} = {32E22C76-2FE8-2308-E702-DB81C76357DB}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ DLL Debug|Win32 = DLL Debug|Win32
+ DLL Release|Win32 = DLL Release|Win32
+ LIB Debug|Win32 = LIB Debug|Win32
+ LIB Release|Win32 = LIB Release|Win32
+ Release|Win32 = Release|Win32
+ Template|Win32 = Template|Win32
+ using libcurl DLL Debug|Win32 = using libcurl DLL Debug|Win32
+ using libcurl DLL Release|Win32 = using libcurl DLL Release|Win32
+ using libcurl LIB Debug|Win32 = using libcurl LIB Debug|Win32
+ using libcurl LIB Release|Win32 = using libcurl LIB Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.Build.0 = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.DLL Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.DLL Debug|Win32.Build.0 = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.DLL Release|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.DLL Release|Win32.Build.0 = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.LIB Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.LIB Debug|Win32.Build.0 = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.LIB Release|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.LIB Release|Win32.Build.0 = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.Build.0 = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Template|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Template|Win32.Build.0 = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl DLL Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl DLL Debug|Win32.Build.0 = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl DLL Release|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl DLL Release|Win32.Build.0 = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl LIB Debug|Win32.ActiveCfg = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl LIB Debug|Win32.Build.0 = Debug|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl LIB Release|Win32.ActiveCfg = Release|Win32
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.using libcurl LIB Release|Win32.Build.0 = Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Debug|Win32.ActiveCfg = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Debug|Win32.Build.0 = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.DLL Debug|Win32.ActiveCfg = DLL Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.DLL Debug|Win32.Build.0 = DLL Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.DLL Release|Win32.ActiveCfg = DLL Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.DLL Release|Win32.Build.0 = DLL Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.LIB Debug|Win32.ActiveCfg = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.LIB Debug|Win32.Build.0 = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.LIB Release|Win32.ActiveCfg = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.LIB Release|Win32.Build.0 = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Release|Win32.ActiveCfg = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Release|Win32.Build.0 = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Template|Win32.ActiveCfg = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.Template|Win32.Build.0 = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl DLL Debug|Win32.ActiveCfg = DLL Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl DLL Debug|Win32.Build.0 = DLL Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl DLL Release|Win32.ActiveCfg = DLL Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl DLL Release|Win32.Build.0 = DLL Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl LIB Debug|Win32.ActiveCfg = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl LIB Debug|Win32.Build.0 = LIB Debug|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl LIB Release|Win32.ActiveCfg = LIB Release|Win32
+ {32E22C76-2FE8-2308-E702-DB81C76357DB}.using libcurl LIB Release|Win32.Build.0 = LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Debug|Win32.ActiveCfg = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Debug|Win32.Build.0 = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.DLL Debug|Win32.ActiveCfg = using libcurl DLL Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.DLL Debug|Win32.Build.0 = using libcurl DLL Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.DLL Release|Win32.ActiveCfg = using libcurl DLL Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.DLL Release|Win32.Build.0 = using libcurl DLL Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.LIB Debug|Win32.ActiveCfg = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.LIB Debug|Win32.Build.0 = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.LIB Release|Win32.ActiveCfg = using libcurl LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.LIB Release|Win32.Build.0 = using libcurl LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Release|Win32.ActiveCfg = using libcurl LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Release|Win32.Build.0 = using libcurl LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Template|Win32.ActiveCfg = Template|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.Template|Win32.Build.0 = Template|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl DLL Debug|Win32.ActiveCfg = using libcurl DLL Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl DLL Debug|Win32.Build.0 = using libcurl DLL Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl DLL Release|Win32.ActiveCfg = using libcurl DLL Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl DLL Release|Win32.Build.0 = using libcurl DLL Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl LIB Debug|Win32.ActiveCfg = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl LIB Debug|Win32.Build.0 = using libcurl LIB Debug|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl LIB Release|Win32.ActiveCfg = using libcurl LIB Release|Win32
+ {1E99F660-6BDD-C14B-4931-6D6FCFB83177}.using libcurl LIB Release|Win32.Build.0 = using libcurl LIB Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/extensions/curl/smsdk_config.h b/extensions/curl/smsdk_config.h
index ee2fbdf2e..ed6ba9ba6 100644
--- a/extensions/curl/smsdk_config.h
+++ b/extensions/curl/smsdk_config.h
@@ -60,18 +60,18 @@
/** Enable interfaces you want to use here by uncommenting lines */
//#define SMEXT_ENABLE_FORWARDSYS
-//#define SMEXT_ENABLE_HANDLESYS
+#define SMEXT_ENABLE_HANDLESYS
//#define SMEXT_ENABLE_PLAYERHELPERS
//#define SMEXT_ENABLE_DBMANAGER
//#define SMEXT_ENABLE_GAMECONF
//#define SMEXT_ENABLE_MEMUTILS
//#define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS
-//#define SMEXT_ENABLE_THREADER
+#define SMEXT_ENABLE_THREADER
//#define SMEXT_ENABLE_LIBSYS
//#define SMEXT_ENABLE_MENUS
//#define SMEXT_ENABLE_ADTFACTORY
-//#define SMEXT_ENABLE_PLUGINSYS
+#define SMEXT_ENABLE_PLUGINSYS
//#define SMEXT_ENABLE_ADMINSYS
//#define SMEXT_ENABLE_TEXTPARSERS
//#define SMEXT_ENABLE_USERMSGS
diff --git a/extensions/curl/version.rc b/extensions/curl/version.rc
index e62259257..ee3baa3f6 100644
--- a/extensions/curl/version.rc
+++ b/extensions/curl/version.rc
@@ -50,7 +50,7 @@ BEGIN
VALUE "FileVersion", SM_VERSION_STRING
VALUE "InternalName", "SourceMod Webternet Extension"
VALUE "LegalCopyright", "Copyright (c) 2004-2009, AlliedModders LLC"
- VALUE "OriginalFilename", BINARY_NAME
+ VALUE "OriginalFilename", "webternet.ext.dll"
VALUE "ProductName", "SourceMod Webternet Extension"
VALUE "ProductVersion", SM_VERSION_STRING
END
diff --git a/plugins/include/webternet.inc b/plugins/include/webternet.inc
new file mode 100644
index 000000000..7ff458468
--- /dev/null
+++ b/plugins/include/webternet.inc
@@ -0,0 +1,58 @@
+#if defined _webternet_included
+ #endinput
+#endif
+
+#define _webternet_included
+
+#define HTTP_OK 1
+#define HTTP_ERROR 0
+
+funcenum HTTP_SessionCallback {
+ public(Handle:session, result),
+ public(Handle:session, result, Handle:downloader),
+ public(Handle:session, result, Handle:downloader, any:data),
+};
+
+native Handle:HTTP_CreateSession();
+
+native HTTP_SetFailOnHTTPError(Handle:session, bool:fail);
+
+native HTTP_GetLastError(Handle:session, String:error[], maxlen);
+
+native Handle:HTTP_CreateFileDownloader(const String:file[]);
+
+native Handle:HTTP_CreateMemoryDownloader();
+
+native HTTP_Download(Handle:session, Handle:downloader, const String:url[], HTTP_SessionCallback:callback, any:data = INVALID_HANDLE);
+
+native HTTP_PostAndDownload(Handle:session, Handle:downloader, Handle:form, const String:url[], HTTP_SessionCallback:callback, any:data = INVALID_HANDLE);
+
+native Handle:HTTP_CreateWebForm();
+
+native HTTP_AddStringToWebForm(Handle:form, const String:name[], const String:data[]);
+
+native HTTP_AddFileToWebForm(Handle:form, const String:name[], const String:path[]);
+
+native HTTP_GetBodySize(Handle:downloader);
+
+native HTTP_GetBodyContent(Handle:downloader, String:body[], maxlen);
+
+
+/**
+ * Do not edit below this line!
+ */
+public Extension:__ext_webternet =
+{
+ name = "webternet",
+ file = "webternet.ext",
+#if defined AUTOLOAD_EXTENSIONS
+ autoload = 1,
+#else
+ autoload = 0,
+#endif
+#if defined REQUIRE_EXTENSIONS
+ required = 1,
+#else
+ required = 0,
+#endif
+};
diff --git a/tools/gamedata_md5/msvc9/gamedatamd5.sln b/tools/gamedata_md5/msvc9/gamedatamd5.sln
deleted file mode 100644
index 3f7872324..000000000
--- a/tools/gamedata_md5/msvc9/gamedatamd5.sln
+++ /dev/null
@@ -1,20 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gamedatamd5", "gamedatamd5.vcproj", "{E1C96598-E1AC-4928-8930-F8B6CD245B12}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {E1C96598-E1AC-4928-8930-F8B6CD245B12}.Debug|Win32.ActiveCfg = Debug|Win32
- {E1C96598-E1AC-4928-8930-F8B6CD245B12}.Debug|Win32.Build.0 = Debug|Win32
- {E1C96598-E1AC-4928-8930-F8B6CD245B12}.Release|Win32.ActiveCfg = Release|Win32
- {E1C96598-E1AC-4928-8930-F8B6CD245B12}.Release|Win32.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/tools/gamedata_md5/msvc9/gamedatamd5.vcproj b/tools/gamedata_md5/msvc9/gamedatamd5.vcproj
deleted file mode 100644
index 835ebf848..000000000
--- a/tools/gamedata_md5/msvc9/gamedatamd5.vcproj
+++ /dev/null
@@ -1,219 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-