From 700ec92a9a9cc10b52a426808641eb7aff270044 Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Mon, 15 Feb 2016 15:05:16 +0000 Subject: [PATCH] Implement an auto-escaping Format native for SQL query construction --- core/logic/smn_database.cpp | 21 ++++++++++++++++ core/logic/sprintf.cpp | 50 ++++++++++++++++++++++++++++++++++--- core/logic/sprintf.h | 3 +++ plugins/include/dbi.inc | 23 +++++++++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/core/logic/smn_database.cpp b/core/logic/smn_database.cpp index 735d5210..c1f2701e 100644 --- a/core/logic/smn_database.cpp +++ b/core/logic/smn_database.cpp @@ -32,6 +32,7 @@ #include "common_logic.h" #include "Database.h" #include "ExtensionSys.h" +#include "sprintf.h" #include "stringutil.h" #include "ISourceMod.h" #include "AutoHandleRooter.h" @@ -732,6 +733,24 @@ static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params) return s ? 1 : 0; } +static cell_t SQL_FormatQuery(IPluginContext *pContext, const cell_t *params) +{ + IDatabase *db = NULL; + HandleError err; + + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err); + } + + g_FormatEscapeDatabase = db; + cell_t result = InternalFormat(pContext, params, 1); + g_FormatEscapeDatabase = NULL; + + return result; +} + static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params) { IDatabase *db = NULL; @@ -1814,6 +1833,7 @@ REGISTER_NATIVES(dbNatives) {"Database.Driver.get", Database_Driver_get}, {"Database.SetCharset", SQL_SetCharset}, {"Database.Escape", SQL_QuoteString}, + {"Database.Format", SQL_FormatQuery}, {"Database.IsSameConnection", SQL_IsSameConnection}, {"Database.Execute", SQL_ExecuteTransaction}, @@ -1827,6 +1847,7 @@ REGISTER_NATIVES(dbNatives) {"SQL_Connect", SQL_Connect}, {"SQL_ConnectEx", SQL_ConnectEx}, {"SQL_EscapeString", SQL_QuoteString}, + {"SQL_FormatQuery", SQL_FormatQuery}, {"SQL_Execute", SQL_Execute}, {"SQL_FastQuery", SQL_FastQuery}, {"SQL_FetchFloat", SQL_FetchFloat}, diff --git a/core/logic/sprintf.cpp b/core/logic/sprintf.cpp index c935dbe1..49a5b190 100644 --- a/core/logic/sprintf.cpp +++ b/core/logic/sprintf.cpp @@ -30,15 +30,19 @@ #include "sprintf.h" #include #include +#include #include #include #include using namespace SourceMod; +IDatabase *g_FormatEscapeDatabase = NULL; + #define LADJUST 0x00000001 /* left adjustment */ #define ZEROPAD 0x00000002 /* zero (as opposed to blank) pad */ #define UPPERDIGITS 0x00000004 /* make alpha digits uppercase */ +#define NOESCAPE 0x00000008 /* do not escape strings (they are only escaped if a database connection is provided) */ #define to_digit(c) ((c) - '0') #define is_digit(c) ((unsigned)to_digit(c) <= 9) @@ -158,6 +162,7 @@ bool AddString(char **buf_p, size_t &maxlen, const char *string, int width, int { string = nlstr; prec = -1; + flags |= NOESCAPE; } if (prec >= 0) @@ -181,12 +186,44 @@ bool AddString(char **buf_p, size_t &maxlen, const char *string, int width, int size = maxlen; } - maxlen -= size; width -= size; - while (size--) + if (g_FormatEscapeDatabase && (flags & NOESCAPE) == 0) { - *buf++ = *string++; + char *tempBuffer = NULL; + if (prec != -1) + { + // I doubt anyone will ever do this, so just allocate. + tempBuffer = new char[maxlen + 1]; + memcpy(tempBuffer, string, size); + tempBuffer[size] = '\0'; + } + + size_t newSize; + bool ret = g_FormatEscapeDatabase->QuoteString(tempBuffer ? tempBuffer : string, buf, maxlen + 1, &newSize); + + if (tempBuffer) + { + delete[] tempBuffer; + } + + if (!ret) + { + return false; + } + + maxlen -= newSize; + buf += newSize; + size = 0; // Consistency. + } + else + { + maxlen -= size; + + while (size--) + { + *buf++ = *string++; + } } while ((width-- > 0) && maxlen) @@ -213,7 +250,7 @@ void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, in if (ke::IsNaN(fval)) { - AddString(buf_p, maxlen, "NaN", width, prec, flags); + AddString(buf_p, maxlen, "NaN", width, prec, flags | NOESCAPE); return; } @@ -1031,6 +1068,11 @@ reswitch: flags |= LADJUST; goto rflag; } + case '!': + { + flags |= NOESCAPE; + goto rflag; + } case '.': { n = 0; diff --git a/core/logic/sprintf.h b/core/logic/sprintf.h index 756c7db4..f586bb72 100644 --- a/core/logic/sprintf.h +++ b/core/logic/sprintf.h @@ -30,6 +30,7 @@ #include namespace SourceMod { +class IDatabase; class IPhraseCollection; } @@ -57,4 +58,6 @@ bool gnprintf(char *buffer, size_t *pOutLength, const char **pFailPhrase); +extern SourceMod::IDatabase *g_FormatEscapeDatabase; + #endif // _include_sourcemod_core_logic_sprintf_h_ diff --git a/plugins/include/dbi.inc b/plugins/include/dbi.inc index ccfc4016..4334c1a7 100644 --- a/plugins/include/dbi.inc +++ b/plugins/include/dbi.inc @@ -380,6 +380,16 @@ methodmap Database < Handle // The buffer must be at least 2*strlen(string)+1. public native bool Escape(const char[] string, char[] buffer, int maxlength, int &written=0); + // Formats a string according to the SourceMod format rules (see documentation). + // All format specifiers are escaped (see SQL_EscapeString) unless the '!' flag is used. + // + // @param buffer Destination string buffer. + // @param maxlength Maximum length of output string buffer. + // @param format Formatting rules. + // @param ... Variable number of format parameters. + // @return Number of cells written. + public native int Format(const char[] buffer, int maxlength, const char[] format, any ...); + // Returns whether a database is the same connection as another database. public native bool IsSameConnection(Database other); @@ -641,6 +651,19 @@ native bool SQL_EscapeString(Handle database, int maxlength, int &written=0); +/** + * Formats a string according to the SourceMod format rules (see documentation). + * All format specifiers are escaped (see SQL_EscapeString) unless the '!' flag is used. + * + * @param database A database Handle. + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @return Number of cells written. + */ +native int SQL_FormatQuery(Handle database, const char[] buffer, int maxlength, const char[] format, any ...); + /** * This is a backwards compatibility stock. You should use SQL_EscapeString() * instead, as this function will probably be deprecated in SourceMod 1.1.