diff --git a/extensions/curl/curlapi.cpp b/extensions/curl/curlapi.cpp index 251ed859..b4e934ef 100644 --- a/extensions/curl/curlapi.cpp +++ b/extensions/curl/curlapi.cpp @@ -2,6 +2,32 @@ Webternet g_webternet; +WebForm::WebForm() : first(NULL), last(NULL), lastError(CURL_FORMADD_OK) +{ +} + +bool WebForm::AddString(const char *name, const char *data) +{ + lastError = curl_formadd(&first, + &last, + CURLFORM_COPYNAME, + name, + CURLFORM_COPYCONTENTS, + data, + CURLFORM_END); + return lastError == CURL_FORMADD_OK; +} + +curl_httppost *WebForm::GetFormData() +{ + return first; +} + +WebForm::~WebForm() +{ + curl_formfree(first); +} + WebTransfer *WebTransfer::CreateWebSession() { CURL *curl; @@ -46,6 +72,12 @@ bool WebTransfer::SetHeaderReturn(bool recv_hdr) return lastError == 0; } +bool WebTransfer::SetFailOnHTTPError(bool fail) +{ + lastError = curl_easy_setopt(curl, CURLOPT_FAILONERROR, fail ? 1 : 0); + return lastError == 0; +} + static size_t curl_write_to_sm(void *ptr, size_t bytes, size_t nmemb, void *stream) { void **userdata = (void **)stream; @@ -81,6 +113,12 @@ bool WebTransfer::Download(const char *url, ITransferHandler *handler, void *dat return false; } + lastError = curl_easy_setopt(curl, CURLOPT_HTTPPOST, NULL); + if (lastError) + { + return false; + } + lastError = curl_easy_setopt(curl, CURLOPT_URL, url); if (lastError) { @@ -92,6 +130,46 @@ bool WebTransfer::Download(const char *url, ITransferHandler *handler, void *dat return (lastError == 0); } +bool WebTransfer::PostAndDownload(const char *url, + IWebForm *form, + ITransferHandler *handler, + void *data) +{ + WebForm *realform = (WebForm*)form; + + lastError = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_to_sm); + if (lastError) + { + return false; + } + + void *userdata[3]; + userdata[0] = this; + userdata[1] = handler; + userdata[2] = data; + lastError = curl_easy_setopt(curl, CURLOPT_WRITEDATA, userdata); + if (lastError) + { + return false; + } + + lastError = curl_easy_setopt(curl, CURLOPT_HTTPPOST, realform->GetFormData()); + if (lastError) + { + return false; + } + + lastError = curl_easy_setopt(curl, CURLOPT_URL, url); + if (lastError) + { + return false; + } + + lastError = curl_easy_perform(curl); + + return (lastError == 0); +} + WebTransfer::~WebTransfer() { curl_easy_cleanup(curl); @@ -112,3 +190,7 @@ IWebTransfer *Webternet::CreateSession() return WebTransfer::CreateWebSession(); } +IWebForm *Webternet::CreateForm() +{ + return new WebForm(); +} diff --git a/extensions/curl/curlapi.h b/extensions/curl/curlapi.h index 938d1427..58445f78 100644 --- a/extensions/curl/curlapi.h +++ b/extensions/curl/curlapi.h @@ -37,6 +37,21 @@ using namespace SourceMod; +class WebForm : public IWebForm +{ +public: + WebForm(); + ~WebForm(); +public: + bool AddString(const char *name, const char *data); +public: + curl_httppost *GetFormData(); +private: + curl_httppost *first; + curl_httppost *last; + CURLFORMcode lastError; +}; + class WebTransfer : public IWebTransfer { public: @@ -48,6 +63,11 @@ public: int LastErrorCode(); bool SetHeaderReturn(bool recv_hdr); bool Download(const char *url, ITransferHandler *handler, void *data); + bool SetFailOnHTTPError(bool fail); + bool PostAndDownload(const char *url, + IWebForm *form, + ITransferHandler *handler, + void *data); private: CURL *curl; char errorBuffer[CURL_ERROR_SIZE]; @@ -61,6 +81,7 @@ public: const char *GetInterfaceName(); public: IWebTransfer *CreateSession(); + IWebForm *CreateForm(); }; extern Webternet g_webternet; diff --git a/public/extensions/IWebternet.h b/public/extensions/IWebternet.h index e5761ea2..96902ca8 100644 --- a/public/extensions/IWebternet.h +++ b/public/extensions/IWebternet.h @@ -40,7 +40,7 @@ */ #define SMINTERFACE_WEBTERNET_NAME "IWebternet" -#define SMINTERFACE_WEBTERNET_VERSION 1 +#define SMINTERFACE_WEBTERNET_VERSION 3 namespace SourceMod { @@ -56,6 +56,31 @@ namespace SourceMod class IWebTransfer; class IWebternet; + /** + * @brief Form for POSTing data. + */ + class IWebForm + { + public: + /** + * @brief Free with delete. + */ + virtual ~IWebForm() + { + } + public: + /** + * @brief Adds raw data to the form. + * + * All data is copied locally and may go out of scope. + * + * @param name Field name (null terminated). + * @param data Field data (null terminated). + * @return True on success, false on failure. + */ + virtual bool AddString(const char *name, const char *data) = 0; + }; + /** * @brief Transfer handler interface. */ @@ -139,6 +164,30 @@ namespace SourceMod * @return True on success, false on failure. */ virtual bool Download(const char *url, ITransferHandler *handler, void *data) = 0; + + /** + * @brief Downloads a URL with POST options. + * + * @param url URL to download. + * @param form Form to read POST info from. + * @param handler Handler object. + * @param userdata User data pointer. + * @return True on success, false on failure. + */ + virtual bool PostAndDownload(const char *url, + IWebForm *form, + ITransferHandler *handler, + void *data) = 0; + + /** + * @brief Sets whether an HTTP failure (>= 400) returns false from Download(). + * + * Note: defaults to false. + * + * @param fail True to fail, false otherwise. + * @return True on success, false otherwise. + */ + virtual bool SetFailOnHTTPError(bool fail) = 0; }; /** @@ -156,6 +205,13 @@ namespace SourceMod * @return Object, or NULL on failure. */ virtual IWebTransfer *CreateSession() = 0; + + /** + * @brief Creates a form for building POST data. + * + * @return New form, or NULL on failure. + */ + virtual IWebForm *CreateForm() = 0; }; }