diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index efc2f01..7171b06 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -3,9 +3,13 @@ on: push: branches: - master + tags: + - '*' pull_request: branches: - master +permissions: + contents: read jobs: main: name: Compile plugins @@ -20,7 +24,38 @@ jobs: steps: - name: Check out the repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Compile plugins run: ./build.sh ${{ matrix.amxx_version }} test + - + name: Package release zip + run: ./package.sh ${{ matrix.amxx_version }} + - + name: Archive build artifacts + uses: actions/upload-artifact@v4 + with: + name: artifacts-${{ matrix.amxx_version }} + path: out + + release: + name: Publish Release + if: startsWith(github.ref, 'refs/tags/') + needs: main + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - + name: Download all artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + merge-multiple: true + path: out + - + name: Publish Release + uses: softprops/action-gh-release@v2 + with: + files: out/*.zip + generate_release_notes: true diff --git a/.gitignore b/.gitignore index e07452e..23e99ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -plugins \ No newline at end of file +cstrike/addons/amxmodx/plugins +server/storage diff --git a/README.md b/README.md index 3ddc243..ff6cf17 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,23 @@ Should the above commands be visible on the chat after usage. #### shopsms_commercial "137" How often (seconds) should the shop commercial be displayed on the chat? 0 - never + +## Build + +The repository contains the full plugin source: the shop engine (`shopsms.sma`) and all service modules. Requires Docker and the `budziam/amxmodx-builder` image. + +```bash +./build.sh 1.10.0 # compiles everything into cstrike/addons/amxmodx/plugins/ +./package.sh 1.10.0 # produces out/amxmodx-1.10.0.zip (full cstrike/ tree, sources included) +``` + +Supported compiler versions: 1.8.2, 1.8.3, 1.9.0, 1.10.0. + +## Test server + +A runnable CS 1.6 test server (Docker HLDS + Metamod + AMX Mod X) lives in `server/`: + +```bash +./build.sh 1.10.0 +./server/run.sh 1.10.0 +``` diff --git a/build.sh b/build.sh index 5c7e1d0..9e469a7 100755 --- a/build.sh +++ b/build.sh @@ -12,16 +12,23 @@ MODE=${2:-watch} DIR="$( cd "$( dirname $( dirname "${BASH_SOURCE[0]}" ) )" && pwd )" # PREPARE -mkdir -p "$DIR/plugins" -chmod 777 "$DIR/plugins" -rm -f $DIR/plugins/* +PLUGINS_DIR="$DIR/cstrike/addons/amxmodx/plugins" +SCRIPTING_DIR="$DIR/cstrike/addons/amxmodx/scripting" -docker run --rm \ - -v "$DIR/plugins":/home/builder/builds \ - -v "$DIR/scripting":/home/builder/sources \ - -v "$DIR/scripting/include/colorchat.inc":/home/builder/addons/amxmodx/scripting/include/colorchat.inc \ - -v "$DIR/scripting/include/custom_color_chat.inc":/home/builder/addons/amxmodx/scripting/include/custom_color_chat.inc \ - -v "$DIR/scripting/include/shopsms_const.inc":/home/builder/addons/amxmodx/scripting/include/shopsms_const.inc \ - -v "$DIR/scripting/include/shopsms.inc":/home/builder/addons/amxmodx/scripting/include/shopsms.inc \ - budziam/amxmodx-builder:${BUILD_VERSION} \ +mkdir -p "$PLUGINS_DIR" +chmod 777 "$PLUGINS_DIR" +rm -f $PLUGINS_DIR/* + +docker run \ + --rm \ + -v "$PLUGINS_DIR":/home/builder/builds \ + -v "$SCRIPTING_DIR":/home/builder/sources \ + -v "$SCRIPTING_DIR/include/colorchat.inc":/home/builder/addons/amxmodx/scripting/include/colorchat.inc \ + -v "$SCRIPTING_DIR/include/curl.inc":/home/builder/addons/amxmodx/scripting/include/curl.inc \ + -v "$SCRIPTING_DIR/include/curl_consts.inc":/home/builder/addons/amxmodx/scripting/include/curl_consts.inc \ + -v "$SCRIPTING_DIR/include/custom_color_chat.inc":/home/builder/addons/amxmodx/scripting/include/custom_color_chat.inc \ + -v "$SCRIPTING_DIR/include/shopsms_const.inc":/home/builder/addons/amxmodx/scripting/include/shopsms_const.inc \ + -v "$SCRIPTING_DIR/include/shopsms.inc":/home/builder/addons/amxmodx/scripting/include/shopsms.inc \ + -v "$DIR/cstrike/addons/amxmodx/modules/curl_amxx_i386.so":/home/builder/addons/amxmodx/scripting/modules/curl_amxx_i386.so \ + "budziam/amxmodx-builder:${BUILD_VERSION}" \ ${MODE} diff --git a/cstrike/addons/amxmodx/configs/plugins-sklep_sms.ini b/cstrike/addons/amxmodx/configs/plugins-sklep_sms.ini new file mode 100644 index 0000000..93d35b3 --- /dev/null +++ b/cstrike/addons/amxmodx/configs/plugins-sklep_sms.ini @@ -0,0 +1,11 @@ +; Services +shopsms_service_bf2_badge.amxx +shopsms_service_cod_exp.amxx +shopsms_service_cod_te.amxx +shopsms_service_gxm_bm.amxx +shopsms_service_gxm_exp.amxx +shopsms_service_nickres.amxx +shopsms_service_slotres.amxx +shopsms_service_vippro.amxx +shopsms_service_vip.amxx +shopsms_service_zp_ap.amxx diff --git a/cstrike/addons/amxmodx/configs/shopsms.cfg b/cstrike/addons/amxmodx/configs/shopsms.cfg new file mode 100644 index 0000000..b72676c --- /dev/null +++ b/cstrike/addons/amxmodx/configs/shopsms.cfg @@ -0,0 +1,34 @@ +// ---------------------------------- +// SHOP SMS: - Configuration file +// ---------------------------------- + +// Turn the shop On / Off +// 1 - ON +// 0 - OFF +shopsms_on "1" + +// Comma-separated commands list, which can be used via chat, to display the shop menu +shopsms_shop_commands "/sklepsms,/shopsms" + +// Comma-separated commands list, which can be used via chat, to reload players flags +shopsms_reload_commands "/przeladuj" + +// Comma-separated commands list, which can be used via chat, to display your active services +shopsms_services_commands "/uslugi" + +// Should the above commands be visible on the chat after usage +shopsms_silent_commands "0" + +// How often (seconds) should the shop commercial be displayed on the chat? 0 - never +shopsms_commercial "137" + + +// ##################### +// ### Shop WEB ### +// ##################### + +// Shop URL +shopsms_url "" + +// Server token +shopsms_token "" diff --git a/cstrike/addons/amxmodx/data/lang/shopsms.txt b/cstrike/addons/amxmodx/data/lang/shopsms.txt new file mode 100644 index 0000000..e0bfbf1 --- /dev/null +++ b/cstrike/addons/amxmodx/data/lang/shopsms.txt @@ -0,0 +1,117 @@ +[en] +ACTIVE_PLAYER_SERVICES = Active services +CHOOSE_PAYMENT_METHOD = Choose a payment method +CHOOSE_SERVICE = Choose a service +CHOOSE_SERVICE_TYPE = Choose a service type +CHOOSE_QUANTITY = Choose quantity for: %s +COMMERCIAL = We encourage everyone to use ^3the shop! ^1Just type: ^4%s +CONTACT_US = In case of an issue contact us. +ERROR_10 = Error 10 - The response from the license validation server cannot be read. See logs. +ERROR_11 = Error 11 - The license validation server returned invalid an HTTP CODE [%d]. +ERROR_12 = Error 12 - License data validation failed. +ERROR_13 = Error 13 - Invalid license token. +ERROR_14 = Error 14 - The license doesn't support AMXX servers or the website limit has been exceeded. +ERROR_15 = Error 15 - License error. %s +ERROR_20 = Error 20 - Couldn't load server configuration. Error [%d] +ERROR_21 = Error 21 - Invalid server token. +ERROR_22 = Error 22 - The current version of the plugin is no longer supported. Please update the plugin. +ERROR_23 = Error 23 - The server configuration response cannot be read. +ERROR_24 = Error 24 - The following data were not provided: URL or server token. +FLAGS_RELOADED = The list of flags bought in the shop has been reloaded correctly. +FLAGS_RELOADED_RECENTLY = Someone has just reloaded the list. Wait a moment. +FOREVER = Forever +INVALID_PASSWORD = Invalid password +LANG=english +LICENSE_VALIDATED = License was validated. +LICENSE_VALIDATION_FAILED = License validation failed. +LOADING = Loading... +LOAD_PLAYER_SERVICES_FAILED = Couldn't load information about your services. +NO_AVAILABLE_SERVICE = No services are available for purchase. +NO_PLAYER_SERVICES = You don't have ^3any services ^1yet. Change it! Type: ^3%s +NO_PRICE_LIST = No price list for this service. +PASSWORD_TOO_LONG = The password is too long. The maximum length is %d. +PASSWORD_TOO_SHORT = The password is too short. The minimum length is %d. +PAYMENT_METHOD_SMS = SMS +PAYMENT_METHOD_WALLET = Wallet +PAYMENT_SMS_INFO = Chosen service: %s ^n\wInfo: \y%s^n^n\rIn order to make a purchase send SMS^n\wMessage: \y%s^n\wNumber: \y%s^n\wPrice: \y%s + VAT\d ( %s )^n^n\wWait for the return code after sending and SMS.^nIn order to enter it press: \y1^n^n\wTo exit press: \y0 +PLAYER_SERVICES_LOADED = %d players services have been loaded. +PLAYER_SERVICE_INFO = %s - expires: %s +PLAYER_SERVICES_REQUEST_FAILED = Active services request failed. An HTTP error has occured. +PURCHASE_DETAILS = Purchase details +PURCHASE_ERRORS = Purchase errors +PURCHASE_FAILED = An purchase ^3error ^1occurred. Contact us: ^4%s +PURCHASE_IN_PROGRESS = Purchae in progress... +PURCHASE_REQUEST_FAILED = Purchase ^3failed^1. An HTTP error has occured. +PURCHASE_SUCCEED = Purchase succeed! +REUSE_SMS_CODE = Reuse the same SMS code once again. +SERVICES_LOADED = %d services have been loaded. +SETINFO_INSTRUCTION = Save password by adding the following entry to the file: %s +SHOP_IS_BLOCKED = There are shop related errors. Check logs. +SHOP_IS_DISABLED = Shop is ^3disabled^1. +SHOP_IS_UNAVAILABLE = Shop isn't available. +SHOP_SMS = Shop SMS +SMS_CODE_NO_SPACE = SMS code ^3must not ^contain ^3space^1 character. +SMS_CODE_TOO_SHORT = SMS code is too short. +TRANSACTION_ABORTED = Transaction has been ^3aborted^1. +TYPE_IP_PASS = IP + Password +TYPE_NICK_PASS = Nick + Password +TYPE_STEAM_ID = SteamID + +[pl] +ACTIVE_PLAYER_SERVICES = Aktywne usługi +CHOOSE_PAYMENT_METHOD = Wybierz metodę płatności +CHOOSE_SERVICE = Wybierz usługę +CHOOSE_SERVICE_TYPE = Wybierz typ usługi +CHOOSE_QUANTITY = Wybierz ilość %s +CONTACT_US = W razie problemów skontaktuj się z nami +ERROR_10 = Error 10 - Wystąpił błąd podczas odczytywania odpowiedzi od serwera walidującego licencję. Zobacz logi. +ERROR_11 = Error 11 - Serwer walidacyjny zwrócił błędny HTTP CODE [%d]. +ERROR_12 = Error 12 - Walidacja danych wysłanych do serwera walidacyjnego nie powiodła się. +ERROR_13 = Error 13 - Błędny token licencji. +ERROR_14 = Error 14 - Licencja nie wspiera serwerów AMXX lub przekroczono limit stron WWW. +ERROR_15 = Error 15 - Błąd licencji. %s +ERROR_20 = Error 20 - Nie udało się pobrać konfiguracji serwera. Error [%d] +ERROR_21 = Error 21 - Błędny token serwera. +ERROR_22 = Error 22 - Obecna wersja pluginu nie jest już wspierana. Dokonaj aktualizacji pluginu. +ERROR_23 = Error 23 - Wystąpił błąd podczas ładowania konfiguracji serwera. +ERROR_24 = Error 24 - Nie podano następujących danych: URL lub token serwera. +COMMERCIAL = Zapraszamy wszystkich do korzystania ze ^3sklepu! ^1Wystarczy wpisać: ^4%s +FLAGS_RELOADED = Prawidlowo przeladowano liste flag wykupionych w sklepie. +FLAGS_RELOADED_RECENTLY = Ktoś przed chwilą przeładował listę. Odczekaj chwilę. +FOREVER = Na zawsze +INVALID_PASSWORD = Nieprawidłowe hasło +LANG=polish +LICENSE_VALIDATED = Licencja została zwalidowana. +LICENSE_VALIDATION_FAILED = Walidacja licencji nie powiodła się. +LOADING = Ładowanie... +LOAD_PLAYER_SERVICES_FAILED = Niestety nie udało się uzyskać informacji o Twoich usługach. +NO_AVAILABLE_SERVICE = Brak dostepnych uslug do kupienia. +NO_PLAYER_SERVICES = Nie masz jeszcze ^3żadnej usługi^1. Może czas to zmienić? Wpisz: ^3%s +NO_PRICE_LIST = Brak cennika dla tej uslugi. +PASSWORD_TOO_LONG = Podane hasło jest za długie. Maksymalna ilość znaków wynosi %d. +PASSWORD_TOO_SHORT = Hasło jest za krótkie. Minimalna ilość znaków wynosi %d. +PAYMENT_METHOD_SMS = SMS +PAYMENT_METHOD_WALLET = Portfel +PAYMENT_SMS_INFO = Wybrano usługę: %s ^n\wInfo: \y%s^n^n\rW celu dokonania zakupu, wyślij SMSa^n\wO treści: \y%s^n\wNa numer: \y%s^n\wKoszt: \y%s + VAT\d ( %s )^n^n\wPo wysłaniu SMSa poczekaj na kod zwrotny.^nW celu wprowadzenia go, wciśnij: \y1^n^n\wAby wyjść, wciśnij: \y0 +PLAYER_SERVICES_LOADED = Załadowano %d usług graczy. +PLAYER_SERVICE_INFO = %s - wygasa: %s +PLAYER_SERVICES_REQUEST_FAILED = Nie udało się pozyskać danych nt. aktywnych usług. Wystapił błąd HTTP. +PURCHASE_DETAILS = Szczegóły zakupu +PURCHASE_ERRORS = Błędy przy zakupie usługi +PURCHASE_FAILED = Wystąpił ^3bląd ^1podczas kupowania usługi. Skontaktuj się z nami: ^4%s +PURCHASE_IN_PROGRESS = Trwa przeprowadzanie zakupu... +PURCHASE_REQUEST_FAILED = Nie udało się dokonać zakupu. Wystąpił błąd HTTP. +PURCHASE_SUCCEED = Prawidłowo zakupiono usługę! +REUSE_SMS_CODE = Użyj ponownie tego samego kodu zwrotnego SMS. +SERVICES_LOADED = Załadowano %d usług. +SETINFO_INSTRUCTION = Zapisz hasło dodając poniższy wpis do pliku: %s +SHOP_IS_BLOCKED = Wystąpiły błędy ze sklepem. Zobacz logi. +SHOP_IS_DISABLE = Sklep jest obecnie ^3wylaczony^1. +SHOP_IS_UNAVAILABLE = Sklep jest tymczasowo nieaktywny. +SHOP_SMS = Sklep SMS +SMS_CODE_NO_SPACE = Kod zwrotny ^3nie moze ^1zawierac ^3spacji^1. +SMS_CODE_TOO_SHORT = Kod zwrotny jest zbyt krótki. +TRANSACTION_ABORTED = Transakcja zostala ^3przerwana^1. +TYPE_IP_PASS = IP + Hasło +TYPE_NICK_PASS = Nick + Hasło +TYPE_STEAM_ID = SteamID diff --git a/cstrike/addons/amxmodx/modules/curl_amxx_i386.so b/cstrike/addons/amxmodx/modules/curl_amxx_i386.so new file mode 100644 index 0000000..b722f58 Binary files /dev/null and b/cstrike/addons/amxmodx/modules/curl_amxx_i386.so differ diff --git a/cstrike/addons/amxmodx/scripting/amxbans_core.sma b/cstrike/addons/amxmodx/scripting/amxbans_core.sma new file mode 100755 index 0000000..e652d9e --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/amxbans_core.sma @@ -0,0 +1,799 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * AMX Bans - http://www.amxbans.net + * Plugin - Core + * + * Copyright (C) 2014 Ryan "YamiKaitou" LeBlanc + * Copyright (C) 2009, 2010 Thomas Kurz + * Forked from "Admin Base (SQL)" in AMX Mod X (version 1.8.1) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * 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, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * In addition, as a special exception, the author gives permission to + * link the code of this program with the Half-Life Game Engine ("HL + * Engine") and Modified Game Libraries ("MODs") developed by Valve, + * L.L.C ("Valve"). You must obey the GNU General Public License in all + * respects for all of the code used other than the HL Engine and MODs + * from Valve. If you modify this file, you may extend this exception + * to your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define PLUGINNAME "AMXBans Core" +#define PLUGINAUTHOR "YamiKaitou edited by SeeK" +new const PLUGINVERSION[] = "2.0"; + +#include +#include +#include + +new AdminCount; + +#define ADMIN_LOOKUP (1<<0) +#define ADMIN_NORMAL (1<<1) +#define ADMIN_STEAM (1<<2) +#define ADMIN_IPADDR (1<<3) +#define ADMIN_NAME (1<<4) + +new g_cmdLoopback[16] +new bool:g_CaseSensitiveName[33]; + +// pcvars +new amx_mode; +new amx_password_field; +new amx_default_access; + +//amxbans +new pcvarip +new pcvarprefix +new pcvaradminsfile +new g_ServerAddr[100] +new g_dbPrefix[32] +new g_AdminsFromFile +new g_szAdminNick[33][32] +new g_iAdminUseStaticBantime[33] +new Array:g_AdminNick +new Array:g_AdminUseStaticBantime + +//multi forward handles +new bool:g_isAdmin[33] +enum MFHANDLE_TYPES { + Amxbans_Sql_Initialized=0, + Admin_Connect, + Admin_Disconnect +} +new MFHandle[MFHANDLE_TYPES] + +new Handle:info + +public plugin_init() +{ + register_plugin(PLUGINNAME, PLUGINVERSION, PLUGINAUTHOR); + + register_dictionary("admin.txt") + register_dictionary("common.txt") + amx_mode=register_cvar("amx_mode", "1") + amx_password_field=register_cvar("amx_password_field", "_pw") + amx_default_access=register_cvar("amx_default_access", "") + + register_cvar("amx_vote_ratio", "0.02") + register_cvar("amx_vote_time", "10") + register_cvar("amx_vote_answers", "1") + register_cvar("amx_vote_delay", "60") + register_cvar("amx_last_voting", "0") + register_cvar("amx_show_activity", "2") + register_cvar("amx_votekick_ratio", "0.40") + register_cvar("amx_voteban_ratio", "0.40") + register_cvar("amx_votemap_ratio", "0.40") + + set_cvar_float("amx_last_voting", 0.0) + + + register_srvcmd("amx_sqladmins", "adminSql") + register_cvar("amx_sql_table", "admins") +// amxbans + pcvarip=register_cvar("amxbans_server_address","") + pcvarprefix=register_cvar("amx_sql_prefix", "amx") + pcvaradminsfile=register_cvar("amxbans_use_admins_file","0") + + g_AdminNick=ArrayCreate(32,32) + g_AdminUseStaticBantime=ArrayCreate(1,32) +// + register_cvar("amx_sql_host", "127.0.0.1") + register_cvar("amx_sql_user", "root") + register_cvar("amx_sql_pass", "") + register_cvar("amx_sql_db", "amx") + register_cvar("amx_sql_type", "mysql") + + register_concmd("amx_reloadadmins", "cmdReload", ADMIN_CFG) + //register_concmd("amx_addadmin", "addadminfn", ADMIN_RCON, " [password] [authtype] - add specified player as an admin to users.ini") + + format(g_cmdLoopback, 15, "amxauth%c%c%c%c", random_num('A', 'Z'), random_num('A', 'Z'), random_num('A', 'Z'), random_num('A', 'Z')) + + register_clcmd(g_cmdLoopback, "ackSignal") + + remove_user_flags(0, read_flags("z")) // Remove 'user' flag from server rights + + new configsDir[64] + get_configsdir(configsDir, 63) + + server_cmd("exec %s/amxx.cfg", configsDir) // Execute main configuration file + server_cmd("exec %s/sql.cfg", configsDir) + //server_cmd("exec %s/amxbans.cfg", configsDir) + +} + +public client_connect(id) +{ + g_CaseSensitiveName[id] = false; +} + +public plugin_cfg() +{ + //fixx to be sure cfgs are loaded + create_forwards() + set_task(0.1,"delayed_plugin_cfg") +} + +create_forwards() +{ + MFHandle[Amxbans_Sql_Initialized]=CreateMultiForward("amxbans_sql_initialized",ET_IGNORE,FP_CELL,FP_STRING) + MFHandle[Admin_Connect]=CreateMultiForward("amxbans_admin_connect",ET_IGNORE,FP_CELL) + MFHandle[Admin_Disconnect]=CreateMultiForward("amxbans_admin_disconnect",ET_IGNORE,FP_CELL) +} + +public delayed_plugin_cfg() +{ + //check if amxbans plugins are the first plugins and default admin plugins are disabled + //added for admins who cant read the docs + if(find_plugin_byfile("admin.amxx") != INVALID_PLUGIN_ID) + { + log_amx("[AMXBans] WARNING: admin.amxx plugin running! stopped.") + pause("acd","admin.amxx") + } + if(find_plugin_byfile("admin_sql.amxx") != INVALID_PLUGIN_ID) + { + log_amx("[AMXBans] WARNING: admin_sql.amxx plugin running! stopped.") + pause("acd","admin_sql.amxx") + } + //if(find_plugin_byfile("amxbans_core.amxx") != 0) log_amx("[AMXBans] WARNING: amxbans_core.amxx should be the fist entry in your plugins.ini!") + //if(find_plugin_byfile("amxbans_main.amxx") != 1) log_amx("[AMXBans] WARNING: amxbans_main.amxx should be the second entry in your plugins.ini!") + + get_pcvar_string(pcvarprefix,g_dbPrefix,charsmax(g_dbPrefix)) + get_pcvar_string(pcvarip,g_ServerAddr,charsmax(g_ServerAddr)) + g_AdminsFromFile=get_pcvar_num(pcvaradminsfile) + + if(strlen(g_ServerAddr) < 9) + { + new ip[32] + get_user_ip(0,ip,charsmax(ip)) + formatex(g_ServerAddr,charsmax(g_ServerAddr),"%s",ip) + } + if(get_cvar_num("amxbans_debug") >= 1) server_print("[AMXBans] plugin_cfg: ip %s / prefix %s",g_ServerAddr,g_dbPrefix) + + server_cmd("amx_sqladmins") + server_exec(); + + set_task(6.1, "delayed_load") +} + +public delayed_load() +{ + new configFile[128], curMap[64], configDir[128] + + get_configsdir(configDir, charsmax(configDir)) + get_mapname(curMap, charsmax(curMap)) + + new i=0; + + while (curMap[i] != '_' && curMap[i++] != '^0') {} + + if (curMap[i]=='_') + { + // this map has a prefix + curMap[i]='^0'; + formatex(configFile, charsmax(configFile), "%s/maps/prefix_%s.cfg", configDir, curMap); + + if (file_exists(configFile)) + { + server_cmd("exec %s", configFile); + } + } + + get_mapname(curMap, charsmax(curMap)) + + formatex(configFile, charsmax(configFile), "%s/maps/%s.cfg", configDir, curMap) + + if (file_exists(configFile)) + { + server_cmd("exec %s", configFile) + } +} + +loadSettings(szFilename[]) +{ + new File=fopen(szFilename,"r"); + + if (File) + { + new Text[512]; + new Flags[33]; + new Access[33] + new AuthData[44]; + new Password[33]; + new Name[34]; + new Static[2]; + + while (!feof(File)) + { + fgets(File,Text,charsmax(Text)); + + trim(Text); + + // comment + if (Text[0]==';') + { + continue; + } + + Flags[0]=0; + Access[0]=0; + AuthData[0]=0; + Password[0]=0; + Name[0] = 0; + Static[0] = 0; + + // not enough parameters + if (parse(Text, AuthData, charsmax(AuthData), Password, charsmax(Password), Access, charsmax(Access), Flags, charsmax(Flags), Name, charsmax(Name), Static, sizeof Static) < 2) + { + continue; + } + + admins_push(AuthData,Password,read_flags(Access),read_flags(Flags)); + ArrayPushString(g_AdminNick, Name); + ArrayPushCell(g_AdminUseStaticBantime, str_to_num(Static)); + + AdminCount++; + } + + fclose(File); + } + + if (AdminCount == 1) + { + server_print("[AMXBans] %L", LANG_SERVER, "LOADED_ADMIN"); + } + else + { + server_print("[AMXBans] %L", LANG_SERVER, "LOADED_ADMINS", AdminCount); + } + + return 1; +} + +public adminSql() +{ + new table[32], error[128], errno + AdminCount = 0; + admins_flush(); + + SQL_SetAffinity("mysql") + info = SQL_MakeStdTuple() + new Handle:sql = SQL_Connect(info, errno, error, 127) + + get_cvar_string("amx_sql_table", table, 31) + + //sql error or amxbans_use_admins_file == 1 + if (sql == Empty_Handle || g_AdminsFromFile > 0) + { + if(!g_AdminsFromFile) server_print("[AMXBans] %L", LANG_SERVER, "SQL_CANT_CON", error) + + //backup to users.ini + new configsDir[64] + + get_configsdir(configsDir, charsmax(configsDir)) + format(configsDir, charsmax(configsDir), "%s/users.ini", configsDir) + loadSettings(configsDir) // Load admins accounts + + new players[32], num, pv + new name[33] + get_players(players, num) + for (new i=0; i 1) + { + if (sql != Empty_Handle) + { + //Fix by EpicMorg: + SQL_FreeHandle(sql) + server_print("[amxbans_core.amxx] [AMXBans] SQL Connection closed.") + } + return PLUGIN_HANDLED + } + + ArrayClear(g_AdminNick) + ArrayClear(g_AdminUseStaticBantime) + + new Handle:query + +//amxbans + new pquery[1024] + + formatex(pquery,1023,"SELECT aa.steamid,aa.password,aa.access,aa.flags,aa.nickname,ads.custom_flags,ads.use_static_bantime \ + FROM %s_amxadmins as aa, %s_admins_servers as ads, %s_serverinfo as si \ + WHERE ((ads.admin_id=aa.id) AND (ads.server_id=si.id) AND \ + ((aa.days=0) OR (aa.expired>UNIX_TIMESTAMP(NOW()))) AND (si.address='%s'))", g_dbPrefix, g_dbPrefix, g_dbPrefix, g_ServerAddr) + + query = SQL_PrepareQuery(sql,pquery) + + SQL_Execute(query) +// + + if(SQL_NumRows(query)) + { + /* do this incase people change the query order and forget to modify below */ + new qcolAuth = SQL_FieldNameToNum(query, "steamid") + new qcolPass = SQL_FieldNameToNum(query, "password") + new qcolAccess = SQL_FieldNameToNum(query, "access") + new qcolFlags = SQL_FieldNameToNum(query, "flags") + new qcolNick = SQL_FieldNameToNum(query, "nickname") + new qcolCustom = SQL_FieldNameToNum(query, "custom_flags") + new qcolStatic = SQL_FieldNameToNum(query, "use_static_bantime") + + + new AuthData[44]; + new Password[44]; + new Access[33]; + new Flags[33]; + new Nick[33]; + new Static[5] + new iStatic + + while (SQL_MoreResults(query)) + { + SQL_ReadResult(query, qcolAuth, AuthData, charsmax(AuthData)); + SQL_ReadResult(query, qcolPass, Password, charsmax(Password)); + SQL_ReadResult(query, qcolStatic, Static, charsmax(Static)); + SQL_ReadResult(query, qcolCustom, Access, charsmax(Access)); + SQL_ReadResult(query, qcolNick, Nick, charsmax(Nick)); + SQL_ReadResult(query, qcolFlags, Flags, charsmax(Flags)); + + //if custom access not set get the global + trim(Access) + if(equal(Access,"")) SQL_ReadResult(query, qcolAccess, Access, charsmax(Access)); + + admins_push(AuthData,Password,read_flags(Access),read_flags(Flags)); + + //save nick + ArrayPushString(g_AdminNick,Nick) + + //save static bantime + iStatic=1 + if(equal(Static,"no")) iStatic=0 + ArrayPushCell(g_AdminUseStaticBantime,iStatic) + + ++AdminCount; + SQL_NextRow(query) + } + } + + if (AdminCount == 1) + { + server_print("[AMXBans] %L", LANG_SERVER, "SQL_LOADED_ADMIN") + } + else + { + server_print("[AMXBans] %L", LANG_SERVER, "SQL_LOADED_ADMINS", AdminCount) + } + + SQL_FreeHandle(query) + SQL_FreeHandle(sql) + + new players[32], num, pv + new name[33] + get_players(players, num) + for (new i=0; i<%s><>^" became an admin (account ^"%s^") (access ^"%s^") (address ^"%s^") (nick ^"%s^") (static %d)", + name, get_user_userid(id), authid, AuthData, sflags, ip,g_szAdminNick[id],g_iAdminUseStaticBantime[id]) + } + else + { + new password3[33], md5_password[34], md5_password2[34]; + admins_lookup(index,AdminProp_Password,password3,charsmax(password3)); + + vc_md5(password,md5_password); + vc_md5(password2,md5_password2); + + //log_amx("%s %s %s %s %s",password,md5_password,password2,md5_password2,password3); + if ( equal(password, password3) || equal(md5_password, password3) || equal(password2, password3) || equal(md5_password2, password3) ) + { + result |= 12 + set_user_flags(id, Access) + + new sflags[32] + get_flags(Access, sflags, 31) + + new ret + if(!g_isAdmin[id]) ExecuteForward(MFHandle[Admin_Connect],ret,id) + g_isAdmin[id] = true + + log_amx("Login: ^"%s<%d><%s><>^" became an admin (account ^"%s^") (access ^"%s^") (address ^"%s^") (nick ^"%s^") (static %d)", + name, get_user_userid(id), authid, AuthData, sflags, ip,g_szAdminNick[id],g_iAdminUseStaticBantime[id]) + } + else + { + result |= 1 + + if (Flags & FLAG_KICK) + { + result |= 2 + g_isAdmin[id] = false + log_amx("Login: ^"%s<%d><%s><>^" kicked due to invalid password (account ^"%s^") (address ^"%s^")", + name, get_user_userid(id), authid, AuthData, ip) + } + } + } + } + else if (get_pcvar_num(amx_mode) == 2) + { + result |= 2 + } + else + { + new defaccess[32] + + get_pcvar_string(amx_default_access, defaccess, 31) + + if (!strlen(defaccess)) + { + copy(defaccess, 32, "z") + } + + new idefaccess = read_flags(defaccess) + + if (idefaccess) + { + result |= 8 + set_user_flags(id, idefaccess) + } + } + + return result; +} + +accessUser(id, name[] = "") +{ + remove_user_flags(id) + + new userip[32], userauthid[32], password[32], password2[33], passfield[32], username[32] + + get_user_ip(id, userip, 31, 1) + get_user_authid(id, userauthid, 31) + + if (name[0]) + { + copy(username, 31, name) + } + else + { + get_user_name(id, username, 31) + } + + get_pcvar_string(amx_password_field, passfield, 31) + get_user_info(id, passfield, password, 31) + get_user_info(id, "_ss", password2, charsmax(password2)) + + new result = getAccess(id, username, userauthid, userip, password, password2) + + if (result & 1) + { + client_cmd(id, "echo ^"* %L^"", id, "INV_PAS") + } + + if (result & 2) + { + client_cmd(id, "%s", g_cmdLoopback) + return PLUGIN_HANDLED + } + + if (result & 4) + { + client_cmd(id, "echo ^"* %L^"", id, "PAS_ACC") + } + + if (result & 8) + { + client_cmd(id, "echo ^"* %L^"", id, "PRIV_SET") + } + + return PLUGIN_CONTINUE +} + +public client_infochanged(id) +{ + if (!is_user_connected(id) || !get_pcvar_num(amx_mode)) + { + return PLUGIN_CONTINUE + } + + new newname[32], oldname[32] + + get_user_name(id, oldname, 31) + get_user_info(id, "name", newname, 31) + + if (g_CaseSensitiveName[id]) + { + if (!equal(newname, oldname)) + { + accessUser(id, newname) + } + } + else + { + if (!equali(newname, oldname)) + { + accessUser(id, newname) + } + } + return PLUGIN_CONTINUE +} + +public client_disconnected(id) +{ + if(g_isAdmin[id]) + { + new ret + ExecuteForward(MFHandle[Admin_Disconnect],ret,id) + } + g_isAdmin[id]=false +} + +public ackSignal(id) +{ + server_cmd("kick #%d ^"%L^"", get_user_userid(id), id, "NO_ENTRY") + return PLUGIN_HANDLED +} + +public client_authorized(id) + return get_pcvar_num(amx_mode) ? accessUser(id) : PLUGIN_CONTINUE + +public client_putinserver(id) +{ + if (!is_dedicated_server() && id == 1) + return get_pcvar_num(amx_mode) ? accessUser(id) : PLUGIN_CONTINUE + + return PLUGIN_CONTINUE +} + +//natives +public plugin_natives() +{ + register_library("AMXBansCore") + + register_native("amxbans_get_db_prefix","native_amxbans_get_prefix") + register_native("amxbans_get_admin_nick","native_amxbans_get_nick") + register_native("amxbans_get_static_bantime","native_amxbans_static_bantime") +} + +public native_amxbans_get_prefix() +{ + new len= get_param(2) + set_array(1,g_dbPrefix,len) +} + +public native_amxbans_get_nick() +{ + + new id = get_param(1) + new len= get_param(3) + + set_array(2,g_szAdminNick[id],len) +} + +public native_amxbans_static_bantime() +{ + new id = get_param(1) + if(get_cvar_num("amxbans_debug") >= 3) log_amx("[AMXBans Core] Native static bantime: id: %d | result: %d",id,g_iAdminUseStaticBantime[id]) + return g_iAdminUseStaticBantime[id] +} + +vc_md5(const szString[], md5buffer[34]) +{ + #if defined hash_string + hash_string(szString, Hash_Md5, md5buffer, 34); + #else + md5(szString, md5buffer); + #endif +} diff --git a/scripting/include/colorchat.inc b/cstrike/addons/amxmodx/scripting/include/colorchat.inc similarity index 100% rename from scripting/include/colorchat.inc rename to cstrike/addons/amxmodx/scripting/include/colorchat.inc diff --git a/cstrike/addons/amxmodx/scripting/include/curl.inc b/cstrike/addons/amxmodx/scripting/include/curl.inc new file mode 100755 index 0000000..b9e4b00 --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/include/curl.inc @@ -0,0 +1,39 @@ +#if defined _curl_included + #endinput +#endif +#define _curl_included + +#include + +#if AMXX_VERSION_NUM >= 175 + #pragma reqlib curl + #if !defined AMXMODX_NOAUTOLOAD + #pragma loadlib curl + #endif +#else + #pragma library curl +#endif + +enum _:curl_off_t { + curl_off_left, + curl_off_right +} + +native curl_easy_escape(const CURL:handle, const url[], escapedUrl[], const len); +native curl_easy_unescape(const CURL:handle, const url[], unescapedUrl[], const len); + +native CURL:curl_easy_init(); +native void:curl_easy_perform(const CURL:handle, const callbackComplite[], const data[] = {}, const data_len = 0); +native CURLcode:curl_easy_setopt(const CURL:handle, const CURLoption:option, any:...); +native void:curl_easy_cleanup(const CURL:handle); +native void:curl_easy_reset(const CURL:handle); +native CURLcode:curl_easy_getinfo(const CURL:handle, const CURLINFO:info, any:...); +native void:curl_easy_strerror(const CURLcode:code, errorBuf[], const len); + +native CURLFORMcode:curl_formadd(&curl_httppost:first, &curl_httppost:last, any:...); +native void:curl_formfree(&curl_httppost:first); + +native curl_slist:curl_slist_append(curl_slist:list, buffer[]); +native void:curl_slist_free_all(curl_slist:list); + +native void:curl_version(buf[], len); diff --git a/cstrike/addons/amxmodx/scripting/include/curl_consts.inc b/cstrike/addons/amxmodx/scripting/include/curl_consts.inc new file mode 100755 index 0000000..2ffa0b4 --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/include/curl_consts.inc @@ -0,0 +1,1325 @@ +#if defined _curlconst_included + #endinput +#endif +#define _curlconst_included + +#define CURL_ERROR_SIZE 256 + +/*#if defined(WIN32) +#define CURL_SOCKET_BAD (~0) +#else +#define CURL_SOCKET_BAD -1 +#endif*/ + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_GSSNEGOTIATE - HTTP GSS-Negotiate authentication + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE 0 +#define CURLAUTH_BASIC (1<<0) +#define CURLAUTH_DIGEST (1<<1) +#define CURLAUTH_GSSNEGOTIATE (1<<2) +#define CURLAUTH_NTLM (1<<3) +#define CURLAUTH_DIGEST_IE (1<<4) +#define CURLAUTH_NTLM_WB (1<<5) +#define CURLAUTH_ONLY (1<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +enum curlioerr { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} + +enum curliocmd { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} + +enum curlsocktype { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +} + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + + CURL_SSLVERSION_LAST /* never use, keep last */ +} + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2.0 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +} + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +} + +enum curl_proxytype { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} /* this enum was added in 7.10 */ + +enum curl_infotype { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +enum CURLFORMcode { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +enum curl_ftpmethod { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +enum curl_ftpccc { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +enum curl_ftpauth { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +enum curl_ftpcreatedir { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} + +/* parameter for the CURLOPT_USE_SSL option */ +enum curl_usessl { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} + +#define CFINIT(%0) CURLFORM_%0 +enum CURLformoption { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + + CURLFORM_LASTENTRY /* the last unused */ +} + +#undef CFINIT /* done */ + +enum CURLcode { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_OBSOLETE16, /* 16 - NOT USED */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURL_LAST /* never use! */ +} + + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +enum CURLINFO { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_SLIST + 43, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 43 +} + +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T + +#define CINIT(%0,%1,%2) CURLOPT_%0 = %1 + %2 + +enum CURLoption { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(WRITEDATA, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "user:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(READDATA, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(HEADERDATA, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, OBJECTPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, OBJECTPOINT, 173), + CINIT(PASSWORD, OBJECTPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, OBJECTPOINT, 175), + CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, OBJECTPOINT, 186), + + /* set the SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CINIT(DNS_SERVERS, OBJECTPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of miliseconds. */ + CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + + /* Set TCP keepalive */ + CINIT(TCP_KEEPALIVE, LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CINIT(TCP_KEEPIDLE, LONG, 214), + CINIT(TCP_KEEPINTVL, LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CINIT(SSL_OPTIONS, LONG, 216), + + /* Set the SMTP auth originator */ + CINIT(MAIL_AUTH, OBJECTPOINT, 217), + + /* Enable/disable SASL initial response */ + CINIT(SASL_IR, LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CINIT(XOAUTH2_BEARER, OBJECTPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_INTERFACE, OBJECTPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP4, OBJECTPOINT, 222), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP6, OBJECTPOINT, 223), + + /* Set authentication options directly */ + CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + CURLOPT_LASTENTRY /* the last unused */ +} + +#undef LONG +#undef OBJECTPOINT +#undef FUNCTIONPOINT +#undef OFF_T +#undef CINIT \ No newline at end of file diff --git a/scripting/include/custom_color_chat.inc b/cstrike/addons/amxmodx/scripting/include/custom_color_chat.inc similarity index 100% rename from scripting/include/custom_color_chat.inc rename to cstrike/addons/amxmodx/scripting/include/custom_color_chat.inc diff --git a/scripting/include/shopsms.inc b/cstrike/addons/amxmodx/scripting/include/shopsms.inc similarity index 100% rename from scripting/include/shopsms.inc rename to cstrike/addons/amxmodx/scripting/include/shopsms.inc diff --git a/scripting/include/shopsms_const.inc b/cstrike/addons/amxmodx/scripting/include/shopsms_const.inc similarity index 100% rename from scripting/include/shopsms_const.inc rename to cstrike/addons/amxmodx/scripting/include/shopsms_const.inc diff --git a/cstrike/addons/amxmodx/scripting/shopsms.sma b/cstrike/addons/amxmodx/scripting/shopsms.sma new file mode 100755 index 0000000..c094c99 --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/shopsms.sma @@ -0,0 +1,2379 @@ +#include +#include +#include +#include +#include +#include + +#pragma semicolon 1 +#pragma dynamic 131072 + +forward amxbans_sql_initialized(Handle:sqlTuple, dbPrefix[]); +forward client_admin(id, flags); +forward amxx_admin_access(id, flags, timestamp); + +//#define IS_DEBUG; + +#define FLAG_NICK (1<<5) + +// Tasks ID +#define TASK_KEEP_INFO_SMS 2000 +#define TASK_DISPLAY_LOADING 6000 +#define TASK_KEEP_DISPLAYING_MENU 8000 + +// Available menu keys +#define KEYS (1<<0)|(1<<9) // Keys: 10 + +// LOGS +new const CFG_FILE[] = "shopsms.cfg"; +new const LOG_FILE[] = "shopsms.log"; + +const MAX_COMMAND = 32; +const MAX_CONTACT = 64; +const MAX_FLAGS = 26; +const MAX_SERVER_NAME = 128; +const MAX_SERVICE_TAG = 16; +const MAX_SERVICE_NAME = 32; +const MAX_SMS_TEXT = 32; +const MAX_PASSWORD = 32; +const MAX_DESCRIPTION = 28; +const MAX_CODE = 16; +const MAX_NUMBER = 16; +const MAX_AUTH_DATA = 32; +const MAX_NAME = 32; +const MAX_SID = 34; +const MAX_IP = 15; +const MAX_PORT = 8; +const MAX_SHOP_TOKEN = 32; +const MAX_FULL_IP = MAX_IP + MAX_PORT + 1; // ip + semicolon + port +const MAX_URL_HOST = 128; +const MAX_URL_PATH = 384; +const MAX_URL = MAX_URL_HOST + MAX_URL_PATH; +const MAX_BODY = 384; +const MAX_FUNCTION = 128; +const MAX_CONFIG_KEY = 64; +const MAX_NUMBER_LENGTH = 32; + +const TYPE_NICK = 1<<0; +const TYPE_IP = 1<<1; +const TYPE_SID = 1<<2; + +new g_szTemp[16384]; + +// nVault +new g_vaultHandle; +new const VAULT_PLAYER_FLAGS_KEY[] = "player_flags"; + +// FORWARDs +new forwrdClientAccess; + +// CVARs +new g_cvarOn, + g_cvarCommercial, + g_cvarShopCommands, + g_cvarReloadCommands, + g_cvarServicesCommands, + g_cvarSilentCommands, + g_cvarShopUrl, + g_cvarShopToken; + +new Trie:g_shopCommands, + Trie:g_serviceListCommands, + Trie:g_reloadCommands; + +// Server +new g_serverIp[MAX_IP + 1], + g_serverPort[MAX_PORT + 1], + g_serverName[MAX_SERVER_NAME + 1], + g_signature, + g_configPath[128]; + +// Server config +new g_currency[16], + g_contact[MAX_CONTACT + 1], + g_smsText[MAX_SMS_TEXT + 1], + Float:g_vat, + g_licenseToken[512 + 1]; + +// License +new bool:g_footer = true, + bool:g_blocked = false, + bool:g_validationAttempt = false, + bool:g_licenseValidated = false; + +enum e_Service { + sID[MAX_SERVICE_ID + 1], + PLUGIN, + sNAME[MAX_SERVICE_NAME + 1], + DESCRIPTION[MAX_DESCRIPTION + 1], + TAG[MAX_SERVICE_TAG + 1], + FLAGS[MAX_FLAGS + 1], + TYPES +}; + +new Array:service_plugins, + Array:service_ids, + Array:g_services; + +enum e_SMS { + NUMBER[MAX_NUMBER + 1], + PRICE, +}; +new Array:g_smses; + +enum e_Price { + pID, + pSERVICE_ID[MAX_SERVICE_ID + 1], + SMS_PRICE, + QUANTITY, + DISCOUNT, +}; +new Array:g_prices; + +enum e_PlayerFlags { + pfAUTH[MAX_AUTH_DATA+1], + pfFLAGS, + pfPASSWORD[MAX_PASSWORD+1], + pfTYPE +}; +new Array:g_playersFlags; +new bool:g_playersLoaded = false; +new g_lastReassign; + +// Purchase additional variables +new which_service[33], + which_price[33], + b_passwords[33][MAX_PASSWORD + 1], + b_types[33]; + +// Requests +const MAX_RESPONSE = 16384; +const MAX_REQUESTS_COUNT = 4; +new g_requestCounter = 0, + g_responses[MAX_REQUESTS_COUNT][MAX_RESPONSE + 1], + bool:g_requestsInProgress[MAX_REQUESTS_COUNT]; + +enum requestStruct { + RequestId, + curl_slist: RequestHeaders, + RequestCallback[MAX_FUNCTION + 1], + RequestPlayerId, +}; + +public plugin_natives() +{ + register_library("shopsms"); + register_native("ss_register_service", "native_registerService"); + register_native("ss_open_shop", "native_openShopMenu"); + register_native("ss_open_payment_method_menu", "native_openPaymentMethodMenu"); + register_native("ss_show_service_purchase", "native_showServicePurchase"); + register_native("ss_log", "native_addLog"); + register_native("ss_is_steam", "native_is_steam"); +} + +public plugin_precache() +{ + get_configsdir(g_configPath, charsmax(g_configPath)); + + _createShopSmsConfigFile(); + + g_cvarOn = register_cvar("shopsms_on", "1"); + g_cvarShopCommands = register_cvar("shopsms_shop_commands", "/sklepsms,/shopsms"); + g_cvarReloadCommands = register_cvar("shopsms_reload_commands", "/przeladuj"); + g_cvarServicesCommands = register_cvar("shopsms_services_commands", "/uslugi"); + g_cvarCommercial = register_cvar("shopsms_commercial", "137"); + g_cvarShopUrl = register_cvar("shopsms_url", ""); + g_cvarShopToken = register_cvar("shopsms_token", ""); + g_cvarSilentCommands = register_cvar("shopsms_silent_commands", "0"); + + // Read config + if (file_exists(g_configPath)) { + server_cmd("exec %s", g_configPath); + } + + service_plugins = ArrayCreate(); + service_ids = ArrayCreate(MAX_SERVICE_ID + 1); + + g_playersFlags = ArrayCreate(e_PlayerFlags); + g_smses = ArrayCreate(e_SMS); + g_services = ArrayCreate(e_Service); + g_prices = ArrayCreate(e_Price); + g_shopCommands = TrieCreate(); + g_serviceListCommands = TrieCreate(); + g_reloadCommands = TrieCreate(); +} + +public plugin_init() +{ + register_plugin("Shop SMS", VERSION, "SeeK"); + + register_dictionary("shopsms.txt"); + + register_menucmd(register_menuid("smsDetails"), KEYS, "smsDetails_handler"); + register_concmd("Podaj_Kod_Zwrotny", "onSmsCodeEntered"); + register_concmd("ssnick_Password", "onNickPasswordEntered"); + register_concmd("ssip_Password", "onIpPasswordEntered"); + register_concmd("ss_reload", "command_reloadServerConfig"); + register_concmd("ss_reload_players_flags", "command_reloadPlayersFlags"); + register_concmd("ss_reassign_players_flags", "command_reassignPlayersFlags"); + + register_clcmd("say", "sayHook"); + register_clcmd("say_team", "sayHook"); + + new Float:time = get_pcvar_float(g_cvarCommercial); + if (time) { + set_task(get_pcvar_float(g_cvarCommercial), "displayCommercial"); + } + + g_signature = random_num(1, 99999999); + forwrdClientAccess = CreateMultiForward("ss_client_access", ET_IGNORE, FP_CELL, FP_CELL); + + g_vaultHandle = nvault_open("shopsms"); + if (g_vaultHandle == INVALID_HANDLE) { + _log("Couldn't open 'shopsms' nvault."); + } +} + +public plugin_cfg() +{ + if (file_exists(g_configPath)) { + // Read config once again + server_cmd("exec %s", g_configPath); + server_exec(); + } else { + // File was not created (probably due to invalid permissions) + _log("Couldn't create the file: %s. Probably due to lack of permissions. Create the file manually.", g_configPath); + } + + new shop_url[MAX_URL + 1], shop_token[MAX_SHOP_TOKEN + 1]; + + _getShopToken(shop_token, charsmax(shop_token)); + _getShopUrl(shop_url, charsmax(shop_url)); + + if (!strlen(shop_url) || !strlen(shop_token)) { + _errorNoShopCreds(); + return; + } + + _loadCvars(); + + // Get server ip + new full_ip[MAX_FULL_IP]; + get_user_ip(0, full_ip, charsmax(full_ip), false); + split(full_ip, g_serverIp, charsmax(g_serverIp), g_serverPort, charsmax(g_serverPort), ":"); + + _loadServerConfig(); + _restorePlayerFlagsFromVault(); +} + +public plugin_end() +{ + nvault_close(g_vaultHandle); + TrieDestroy(g_shopCommands); + TrieDestroy(g_serviceListCommands); + TrieDestroy(g_reloadCommands); +} + +_loadCvars() +{ + get_pcvar_string(g_cvarShopCommands, g_szTemp, charsmax(g_szTemp)); + _explodeCommands(g_szTemp, g_shopCommands); + + get_pcvar_string(g_cvarReloadCommands, g_szTemp, charsmax(g_szTemp)); + _explodeCommands(g_szTemp, g_reloadCommands); + + get_pcvar_string(g_cvarServicesCommands, g_szTemp, charsmax(g_szTemp)); + _explodeCommands(g_szTemp, g_serviceListCommands); +} + +public client_authorized(id) +{ + #if defined IS_DEBUG + if (!g_playersLoaded) { + _log("Player [%d] joined before server config was loaded. It may result in a lack of proper permissions.", id); + } + #endif + + new access = _assignPlayerFlags(id); + + if (access) { + new name[MAX_NAME + 1], steam_id[MAX_SID + 1], ip[MAX_IP + 1], flags[MAX_FLAGS + 1]; + get_user_name(id, name, charsmax(name)); + get_user_authid(id, steam_id, charsmax(steam_id)); + get_user_ip(id, ip, charsmax(ip), true); + get_flags(access, flags, charsmax(flags)); + log_amx("User: ^"%s<%d><%s><%s>^" was given an access ^"%s^"", name, get_user_userid(id), steam_id, ip, flags); + } +} + +public amxbans_sql_initialized() +{ + _reassignPlayersFlags(); +} + +public client_admin(id, flags) +{ + _assignPlayerFlags(id); +} + +public amxx_admin_access(id, flags, timestamp) +{ + _assignPlayerFlags(id); +} + +public sayHook(id) +{ + new text[MAX_COMMAND + 1]; + read_argv(1, text, charsmax(text)); + strtolower(text); + + if (TrieKeyExists(g_shopCommands, text)) { + _openShopMenu(id); + } + else if (TrieKeyExists(g_reloadCommands, text)) { + _sayCommand_reassignPlayersFlags(id); + } + else if (TrieKeyExists(g_serviceListCommands, text)) { + _showPlayerServices(id); + } + + return get_pcvar_num(g_cvarSilentCommands) ? PLUGIN_HANDLED : PLUGIN_CONTINUE; +} + +// +// ----------------------------------------- SERVER CONFIGURATION -------------------------------------------- +// + +_loadServerConfig() +{ + new shop_url[MAX_URL + 1], shop_token[MAX_SHOP_TOKEN + 1], url[MAX_URL + 1]; + new curl_slist: headers; + + _getShopUrl(shop_url, charsmax(shop_url)); + _getShopToken(shop_token, charsmax(shop_token)); + + headers = curl_slist_append(headers, "Accept: application/assoc"); + formatex(url, charsmax(url), "%s/api/server/config?token=%s&version=%s&language=english", shop_url, shop_token, VERSION); + + if (!makeRequest("GET", url, headers, "", "onLoadServerConfigResponse")) { + _errorConfigFailed(999); + } +} + +public onLoadServerConfigResponse(const content[], CURLcode: code, statusCode, playerId) +{ + if (code != CURLE_OK) { + _errorConfigFailed(_:code); + return; + } + + if (statusCode == 400) { + _errorInvalidServerToken(); + return; + } + + if (statusCode == 402) { + _errorOutdatedVersion(); + return; + } + + if (statusCode >= 300) { + _log("Invalid server config response HTTP code [%d]", statusCode); + _errorConfigInvalidResponse(); + return; + } + + new Trie:dict = _createResponseDict(content); + + if (!TrieGetString(dict, "license_token", g_licenseToken, charsmax(g_licenseToken))) { + _log("Invalid server config response | %s", content); + _errorConfigInvalidResponse(); + TrieDestroy(dict); + return; + } + + // Do not validate twice, in case of reloading server config + if (!g_licenseValidated) { + _validateLicense(); + } + + TrieGetString(dict, "sms_text", g_smsText, charsmax(g_smsText)); + TrieGetString(dict, "currency", g_currency, charsmax(g_currency)); + TrieGetString(dict, "contact", g_contact, charsmax(g_contact)); + TrieGetTextFloat(dict, "vat", g_vat); + + _extractSmsNumbers(dict); + _extractPrices(dict); + _extractServices(dict); + _extractPlayersFlags(dict); + + TrieDestroy(dict); + + _stopPlugins(); + _checkLoadedPlayersFlags(); + _checkLoadedServices(); + _storePlayerFlagsDump(); +} + +_extractSmsNumbers(Trie:dict) +{ + new count, key[MAX_CONFIG_KEY + 1]; + + if (!TrieGetTextInt(dict, "sn.c", count)) { + _log("Invalid sms numbers dictionary."); + return; + } + + ArrayClear(g_smses); + + for (new i = 0; i < count; ++i) { + new sms[e_SMS]; + + formatex(key, charsmax(key), "sn.%d", i); + TrieGetString(dict, key, sms[NUMBER], MAX_NUMBER); + + sms[PRICE] = get_sms_cost(sms[NUMBER]); + + ArrayPushArray(g_smses, sms); + } +} + +_extractPrices(Trie:dict) +{ + new count, key[MAX_CONFIG_KEY + 1]; + + if (!TrieGetTextInt(dict, "pr.c", count)) { + _log("Invalid prices dictionary."); + return; + } + + ArrayClear(g_prices); + + for (new i = 0; i < count; ++i) { + new price[e_Price]; + + formatex(key, charsmax(key), "pr.%d.i", i); + TrieGetTextInt(dict, key, price[pID]); + + formatex(key, charsmax(key), "pr.%d.s", i); + TrieGetString(dict, key, price[pSERVICE_ID], MAX_SERVICE_ID); + + formatex(key, charsmax(key), "pr.%d.p", i); + TrieGetTextInt(dict, key, price[SMS_PRICE]); + + formatex(key, charsmax(key), "pr.%d.q", i); + TrieGetTextInt(dict, key, price[QUANTITY]); + + formatex(key, charsmax(key), "pr.%d.d", i); + TrieGetTextInt(dict, key, price[DISCOUNT]); + + ArrayPushArray(g_prices, price); + } +} + +_extractServices(Trie:dict) +{ + new count, key[MAX_CONFIG_KEY + 1]; + + if (!TrieGetTextInt(dict, "se.c", count)) { + _log("Invalid services dictionary."); + return; + } + + ArrayClear(g_services); + + for (new i = 0; i < count; ++i) { + new service[e_Service]; + + formatex(key, charsmax(key), "se.%d.i", i); + TrieGetString(dict, key, service[sID], MAX_SERVICE_ID); + + service[PLUGIN] = _getServicePluginByID(service[sID]); + if (service[PLUGIN] == -1) { + #if defined IS_DEBUG + _log("Service plugin was not found | service_id: [%s]", service[sID]); + #endif + continue; + } + + formatex(key, charsmax(key), "se.%d.n", i); + TrieGetString(dict, key, service[sNAME], MAX_SERVICE_NAME); + + formatex(key, charsmax(key), "se.%d.d", i); + TrieGetString(dict, key, service[DESCRIPTION], MAX_DESCRIPTION); + + formatex(key, charsmax(key), "se.%d.ta", i); + TrieGetString(dict, key, service[TAG], MAX_SERVICE_TAG); + + formatex(key, charsmax(key), "se.%d.f", i); + TrieGetString(dict, key, service[FLAGS], MAX_FLAGS); + + formatex(key, charsmax(key), "se.%d.ty", i); + TrieGetTextInt(dict, key, service[TYPES]); + + ArrayPushArray(g_services, service); + + new ret, forward_handle; + forward_handle = CreateOneForward(service[PLUGIN], "ss_service_data", FP_STRING, FP_STRING); + ExecuteForward(forward_handle, ret, service[sNAME], service[FLAGS]); + DestroyForward(forward_handle); + } +} + +_extractPlayersFlags(Trie:dict) +{ + new count, key[MAX_CONFIG_KEY + 1]; + + if (!TrieGetTextInt(dict, "pf.c", count)) { + _log("Invalid players flags dictionary."); + return; + } + + ArrayClear(g_playersFlags); + + for (new i = 0; i < count; ++i) { + new player_flags[e_PlayerFlags], flags[MAX_FLAGS + 1], type; + + formatex(key, charsmax(key), "pf.%d.t", i); + TrieGetTextInt(dict, key, type); + + formatex(key, charsmax(key), "pf.%d.a", i); + TrieGetString(dict, key, player_flags[pfAUTH], MAX_AUTH_DATA); + + formatex(key, charsmax(key), "pf.%d.p", i); + TrieGetString(dict, key, player_flags[pfPASSWORD], MAX_PASSWORD); + + formatex(key, charsmax(key), "pf.%d.f", i); + TrieGetString(dict, key, flags, charsmax(flags)); + + for (new i = 0; i < strlen(flags); ++i) { + player_flags[pfFLAGS] |= read_flags(flags[i]); + } + + switch (type) { + case TYPE_SID: player_flags[pfTYPE] = FLAG_AUTHID|FLAG_NOPASS; + case TYPE_IP: player_flags[pfTYPE] = FLAG_IP; + default: player_flags[pfTYPE] = FLAG_NICK|FLAG_KICK; + } + + if (player_flags[pfFLAGS] != 0) { + ArrayPushArray(g_playersFlags, player_flags); + } + } + + g_playersLoaded = true; + _reassignPlayersFlags(); +} + +_stopPlugins() +{ + new service_id[MAX_SERVICE_ID + 1]; + + for (new i = 0; i < ArraySize(service_ids); ++i) { + ArrayGetString(service_ids, i, service_id, charsmax(service_id)); + new index = _getServiceByID(service_id); + + // Service is inactive or does not exist + if (index == -1) { + // Stop the plugin + new plugin_name[64]; + get_plugin(ArrayGetCell(service_plugins, i), plugin_name, charsmax(plugin_name)); + server_print("Stop %s", plugin_name); + pause("acd", plugin_name); + ArrayDeleteItem(service_ids, i); + ArrayDeleteItem(service_plugins, i); + i -= 1; + } + } +} + +/** + * Load players flags from API + */ +public loadPlayersFlags() +{ + new shop_url[MAX_URL + 1], shop_token[MAX_SHOP_TOKEN + 1], url[MAX_URL + 1]; + new curl_slist: headers; + + _getShopUrl(shop_url, charsmax(shop_url)); + _getShopToken(shop_token, charsmax(shop_token)); + + headers = curl_slist_append(headers, "Accept: application/assoc"); + formatex(url, charsmax(url), "%s/api/server/players_flags?token=%s&language=english", shop_url, shop_token); + makeRequest("GET", url, headers, "", "onLoadPlayersFlagsResponse"); +} + +public onLoadPlayersFlagsResponse(const content[], CURLcode: code, statusCode, playerId) +{ + if (code != CURLE_OK) { + return; + } + + if (statusCode == 400) { + _errorInvalidServerToken(); + return; + } + + if (statusCode >= 300) { + _log("Invalid players flags response HTTP code | %d", statusCode); + return; + } + + new Trie:dict = _createResponseDict(content); + _extractPlayersFlags(dict); + TrieDestroy(dict); + + _storePlayerFlagsDump(); +} + +Trie:_createResponseDict(const content[]) +{ + new Trie:dict = TrieCreate(); + new cur_len = 0, max_len = strlen(content); + + while (cur_len < max_len) + { + new key[MAX_CONFIG_KEY + 1]; + + cur_len += copyc(g_szTemp, charsmax(g_szTemp), content[cur_len], '^n') + 1; + new position = copyc(key, charsmax(key), g_szTemp, ':') + 1; + + if (strlen(key)) { + TrieSetString(dict, key, g_szTemp[position]); + } + } + + return dict; +} + +// +// --------------------------------------------- PLAYERS FLAGS ------------------------------------------------ +// + +public command_reloadServerConfig() +{ + _loadServerConfig(); + log_amx("Server config has been reloaded."); +} + +public command_reloadPlayersFlags() +{ + loadPlayersFlags(); + log_amx("Players flags have been reloaded."); +} + +public command_reassignPlayersFlags() +{ + g_lastReassign = get_systime(); + _reassignPlayersFlags(); + log_amx("Players flags have been reassigned."); +} + +_sayCommand_reassignPlayersFlags(id) +{ + if (g_lastReassign + 10 > get_systime()) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "FLAGS_RELOADED_RECENTLY"); + return; + } + + g_lastReassign = get_systime(); + _reassignPlayersFlags(); + + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "FLAGS_RELOADED"); +} + +/** + * Reload players flags and assign them again + */ +_reassignPlayersFlags() +{ + _addUsersToGlobalArray(); + + for (new i = 1; i <= get_maxplayers(); ++i) { + if (is_user_connected(i)) { + _assignPlayerFlags(i); + } + } +} + +/** + * Add players flags to global array storing players' access. + */ +_addUsersToGlobalArray() +{ + if (!g_playersLoaded) { + return; + } + + // We want to persist order on the global admins list! + // We clone players flags. Then we iterate over admins stored in global admins storage. + // 1. If there is the same entry in our players flags list and in the global storage, + // then we join them into one entry and store it in the global_users. User is removed + // from the local_users. + // 2. If entry exists only in the global storage, then we simply copy it to global_users. + // Now we put local_users and global_users into the global admins storage. + + new Array:local_users, Array:global_users; + local_users = ArrayCreate(e_PlayerFlags); + global_users = ArrayCreate(e_PlayerFlags); + + for (new i = 0; i < ArraySize(g_playersFlags); ++i) { + new player_flags[e_PlayerFlags]; + ArrayGetArray(g_playersFlags, i, player_flags); + ArrayPushArray(local_users, player_flags); + } + + for (new i = 0; i < admins_num(); ++i) { + new global_player[e_PlayerFlags], global_player_name[33]; + + admins_lookup(i, AdminProp_Auth, global_player[pfAUTH], MAX_AUTH_DATA); + copy(global_player_name, charsmax(global_player_name), global_player[pfAUTH]); + strtolower(global_player_name); + + admins_lookup(i, AdminProp_Password, global_player[pfPASSWORD], MAX_PASSWORD); + + global_player[pfTYPE] = admins_lookup(i, AdminProp_Flags); + global_player[pfFLAGS] = admins_lookup(i, AdminProp_Access); + + // It is important to have sorted list of users, since we use binary search here + // to achieve O(nlogn) instead of O(n) + + new l = 0, p = ArraySize(local_users)-1, s; + while (l <= p) { + new local_player[e_PlayerFlags]; + s = (l + p) / 2; + + ArrayGetArray(local_users, s, local_player); + strtolower(local_player[pfAUTH]); + if (global_player[pfTYPE] == local_player[pfTYPE] && equali(global_player_name, local_player[pfAUTH])) { + // Leave global password + global_player[pfFLAGS] |= local_player[pfFLAGS]; + ArrayPushArray(global_users, global_player); + ArrayDeleteItem(local_users, s); + break; + } + else if (strcmp(global_player_name, local_player[pfAUTH]) > 0) { + l = s + 1; + } + else if ( + _getOrderOfType(global_player[pfTYPE]) > _getOrderOfType(local_player[pfTYPE]) && + equali(global_player_name, local_player[pfAUTH]) + ) { + l = s + 1; + } + else { + p = s - 1; + } + } + + if (p < l) { + ArrayPushArray(global_users, global_player); + } + } + + admins_flush(); + for (new i = 0; i < ArraySize(global_users); ++i) { + new player_flags[e_PlayerFlags]; + ArrayGetArray(global_users, i, player_flags); + admins_push(player_flags[pfAUTH], player_flags[pfPASSWORD], player_flags[pfFLAGS], player_flags[pfTYPE]); + } + for (new i = 0; i < ArraySize(local_users); ++i) { + new player_flags[e_PlayerFlags]; + ArrayGetArray(local_users, i, player_flags); + admins_push(player_flags[pfAUTH], player_flags[pfPASSWORD], player_flags[pfFLAGS], player_flags[pfTYPE]); + } +} + +_assignPlayerFlags(id) +{ + if (!g_playersLoaded) { + return 0; + } + + new access = 0; + + for (new i = 0; i < ArraySize(g_playersFlags); ++i) { + new player_flags[e_PlayerFlags]; + ArrayGetArray(g_playersFlags, i, player_flags); + + if (player_flags[pfTYPE] & FLAG_NICK) { + new name[MAX_NAME + 1]; + get_user_name(id, name, charsmax(name)); + if (! equali(name, player_flags[pfAUTH])) { + continue; + } + } + else if (player_flags[pfTYPE] & FLAG_IP) { + new ip[MAX_IP + 1]; + get_user_ip(id, ip, charsmax(ip), true); + if (! equali(ip, player_flags[pfAUTH])) { + continue; + } + } + else if (player_flags[pfTYPE] & FLAG_AUTHID) { + new sid[MAX_SID + 1], arr_sid[MAX_SID + 1]; + copy(arr_sid, charsmax(arr_sid), player_flags[pfAUTH]); + get_user_authid(id, sid, charsmax(sid)); + + strtolower(arr_sid); + strtolower(sid); + replace(sid, charsmax(sid), "steam_", ""); + replace(arr_sid, charsmax(arr_sid), "steam_", ""); + + if (! equali(sid, arr_sid)) { + continue; + } + } + + if ( + strlen(player_flags[pfPASSWORD]) && + !_checkPassword(id, "_pw", player_flags[pfPASSWORD]) && + !_checkPassword(id, "_pw2", player_flags[pfPASSWORD]) && + !_checkPassword(id, "_pw3", player_flags[pfPASSWORD]) && + !_checkPassword(id, "_ss", player_flags[pfPASSWORD]) + ) { + + if (player_flags[pfTYPE]&FLAG_KICK) { + server_cmd("kick #%d ^"%L^"", get_user_userid(id), id, "INVALID_PASSWORD"); + return 0; + } + + // Do not kick the player, instead do not assign him any flags + continue; + } + + access |= player_flags[pfFLAGS]; + } + + if (access) { + set_user_flags(id, access); + + new ret; + ExecuteForward(forwrdClientAccess, ret, id, access); + } + + return access; +} + +_checkPassword(id, const tag[], const valid_password[]) +{ + new password[MAX_PASSWORD + 1], password_md5[34]; + + get_user_info(id, tag, password, charsmax(password)); + vc_md5(password, password_md5); + + return equal(password_md5, valid_password) || equal(password, valid_password); +} + +// +// ----------------------------------------------- CONFIG VAULT --------------------------------------------------- +// + +_storePlayerFlagsDump() +{ + new iLen = 0, iMax = charsmax(g_szTemp); + + iLen += formatex(g_szTemp[iLen], iMax-iLen, "pf.c:%d^n", ArraySize(g_playersFlags)); + + for (new i = 0; i < ArraySize(g_playersFlags); ++i) { + new player_flags[e_PlayerFlags], flags[MAX_FLAGS + 1], type; + + ArrayGetArray(g_playersFlags, i, player_flags); + get_flags(player_flags[pfFLAGS], flags, charsmax(flags)); + + if (player_flags[pfTYPE] & FLAG_AUTHID) { + type = TYPE_SID; + } else if (player_flags[pfTYPE] & FLAG_IP) { + type = TYPE_IP; + } else { + type = TYPE_NICK; + } + + iLen += formatex(g_szTemp[iLen], iMax-iLen, "pf.%d.t:%d^n", i, type); + iLen += formatex(g_szTemp[iLen], iMax-iLen, "pf.%d.a:%s^n", i, player_flags[pfAUTH]); + iLen += formatex(g_szTemp[iLen], iMax-iLen, "pf.%d.p:%s^n", i, player_flags[pfPASSWORD]); + iLen += formatex(g_szTemp[iLen], iMax-iLen, "pf.%d.f:%s^n", i, flags); + } + + nvault_set(g_vaultHandle, VAULT_PLAYER_FLAGS_KEY, g_szTemp); +} + +_restorePlayerFlagsFromVault() +{ + nvault_get(g_vaultHandle, VAULT_PLAYER_FLAGS_KEY, g_szTemp, charsmax(g_szTemp)); + + if (strlen(g_szTemp)) { + new Trie:dict = _createResponseDict(g_szTemp); + _extractPlayersFlags(dict); + TrieDestroy(dict); + } +} + +// +// ------------------------------------------------- SHOP MENU ---------------------------------------------------- +// + +_openShopMenu(id) +{ + if (!is_user_connected(id)) { + return; + } + + if (!get_pcvar_num(g_cvarOn)) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "SHOP_IS_DISABLED"); + return; + } + + if (g_blocked) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "SHOP_IS_BLOCKED"); + } + + if (!ArraySize(g_services)) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "NO_AVAILABLE_SERVICE"); + return; + } + + if (!g_licenseValidated) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "LICENSE_VALIDATION_FAILED"); + return; + } + + new menu_title[256]; + if (g_footer) { + formatex(menu_title, charsmax(menu_title), "\r%L\d powered by\w sklep-sms.pl^n\y%L", id, "SHOP_SMS", id, "CHOOSE_SERVICE"); + } else { + formatex(menu_title, charsmax(menu_title), "\r%L^n\y%L", id, "SHOP_SMS", id, "CHOOSE_SERVICE"); + } + + new menu = menu_create(menu_title, "shopMenu_handler"); + new menu_callback = menu_makecallback("shopMenu_callback"); + + new menu_text[64], service[e_Service]; + for (new i = 0; i < ArraySize(g_services); ++i) { + new data[4]; + data[0] = i + 1; + ArrayGetArray(g_services, i, service); + + if (service[DESCRIPTION]) { + formatex(menu_text, charsmax(menu_text), "%s \d(%s)", service[sNAME], service[DESCRIPTION]); + } + else { + formatex(menu_text, charsmax(menu_text), "%s", service[sNAME]); + } + + new ret; + new forward_handle = CreateOneForward(service[PLUGIN], "ss_service_addingtolist", FP_CELL); + ExecuteForward(forward_handle, ret, id); + DestroyForward(forward_handle); + + data[1] = ret; + + if (data[1] != ITEM_OFF) { + menu_additem(menu, menu_text, data, 0, menu_callback); + } + } + + menu_display(id, menu); + + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "CONTACT_US"); + client_print_color(id, Red, "^4[%s] ^3%s", PREFIX, g_contact); +} + +public shopMenu_callback(id, menu, item) +{ + new data[4], zaccess, callback; + menu_item_getinfo(menu, item, zaccess, data, sizeof data, _, _, callback); + + return data[1] == ITEM_DISABLED ? ITEM_DISABLED : ITEM_ENABLED; +} + +public shopMenu_handler(id, menu, item) +{ + if (!is_user_connected(id) || item == MENU_EXIT) { + menu_destroy(menu); + return; + } + + new data[4], zaccess, callback; + menu_item_getinfo(menu, item, zaccess, data, sizeof data, _, _, callback); + + _openPricingMenu(id, data[0]-1); + menu_destroy(menu); +} + +_openPricingMenu(id, service_id) +{ + if (!is_user_connected(id)) { + return; + } + + new service[e_Service]; + new szText[256], data[3]; + + ArrayGetArray(g_services, service_id, service); + + formatex(szText, charsmax(szText), "\r%L^n\y%L", id, "SHOP_SMS", id, "CHOOSE_QUANTITY", service[sNAME]); + new menu = menu_create(szText, "pricingMenu_handler"); + + new price[e_Price]; + for (new i = 0; i < ArraySize(g_prices); ++i) { + ArrayGetArray(g_prices, i, price); + + if (!equali(price[pSERVICE_ID], service[sID])) { + continue; + } + + // There is no SMS with a given price + if (_getSmsIndexByPrice(price[SMS_PRICE]) == -1) { + continue; + } + + new quantity[32 + 1]; + _getPriceQuantity(i, service_id, id, quantity, charsmax(quantity)); + + formatex(szText, charsmax(szText), + "\y%s \w%s", + quantity, + format_price(floatround(price[SMS_PRICE] * g_vat)) + ); + + if (price[DISCOUNT]) { + formatex(szText[strlen(szText)], charsmax(szText) - strlen(szText), " \r[-%d%%]", price[DISCOUNT]); + } + + data[0] = service_id+1; + // Prices count limit is 256 + data[1] = min(127, i); + data[2] = min(127, i-data[1]); + + menu_additem(menu, szText, data); + } + + if (menu_items(menu) > 1) { + menu_display(id, menu); + } + // If there is only one element in the menu, let's choose it right away + else if (menu_items(menu) == 1) { + which_service[id] = service_id; + which_price[id] = data[1]+data[2]; + _openServiceTypeMenu(id); + } + else { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "NO_PRICE_LIST"); + } +} + +public pricingMenu_handler(id, menu, item) +{ + if (!is_user_connected(id) || item == MENU_EXIT) { + menu_destroy(menu); + return; + } + + new data[4], zaccess, callback; + menu_item_getinfo(menu, item, zaccess, data, sizeof data, _, _, callback); + + which_service[id] = data[0] - 1; + which_price[id] = data[1] + data[2]; + + _openServiceTypeMenu(id); + menu_destroy(menu); +} + +_openServiceTypeMenu(id) +{ + new service[e_Service]; + ArrayGetArray(g_services, which_service[id], service); + + if (service[TYPES] & (TYPE_NICK|TYPE_IP|TYPE_SID)) { + new menu_title[64], item_text[64]; + + formatex(menu_title, charsmax(menu_title), "%L", id, "CHOOSE_SERVICE_TYPE"); + new menu = menu_create(menu_title, "serviceTypeMenu_handler"); + new menu_callback = menu_makecallback("serviceTypeMenu_callback"); + + formatex(item_text, charsmax(item_text), "%L", id, "TYPE_NICK_PASS"); + menu_additem(menu, item_text, "", 0, menu_callback); + formatex(item_text, charsmax(item_text), "%L", id, "TYPE_IP_PASS"); + menu_additem(menu, item_text, "", 0, menu_callback); + formatex(item_text, charsmax(item_text), "%L", id, "TYPE_STEAM_ID"); + menu_additem(menu, item_text, "", 0, menu_callback); + + new data[4]; + data[0] = id; + data[1] = menu; + + keepDisplayingMenu(data); + } else { + _serviceChosen(id); + } +} + +public serviceTypeMenu_callback(id, menu, item) +{ + new service[e_Service]; + ArrayGetArray(g_services, which_service[id], service); + + switch (item) { + case 0: return service[TYPES]&TYPE_NICK ? ITEM_ENABLED : ITEM_DISABLED; + case 1: return service[TYPES]&TYPE_IP ? ITEM_ENABLED : ITEM_DISABLED; + case 2: return service[TYPES]&TYPE_SID && is_steam(id) ? ITEM_ENABLED : ITEM_DISABLED; + } + + return ITEM_DISABLED; +} + +public serviceTypeMenu_handler(id, menu, item) +{ + remove_task(TASK_KEEP_DISPLAYING_MENU+id); + menu_destroy(menu); + + switch (item) { + case 0: { + client_cmd(id, "messagemode ssnick_Password"); + } + case 1: { + client_cmd(id, "messagemode ssip_Password"); + } + case 2: { + _serviceChosen(id, TYPE_SID, ""); + } + } +} + +public onNickPasswordEntered(id) +{ + new szPassword[MAX_PASSWORD + 1]; + read_argv(1, szPassword, charsmax(szPassword)); + + if (!_serviceTypeMenu_checkPass(id, TYPE_NICK, szPassword)) { + client_cmd(id, "messagemode ssnick_Password"); + } +} + +public onIpPasswordEntered(id) +{ + new szPassword[MAX_PASSWORD + 1]; + read_argv(1, szPassword, charsmax(szPassword)); + + if (! _serviceTypeMenu_checkPass(id, TYPE_IP, szPassword)) { + client_cmd(id, "messagemode ssip_Password"); + } +} + +bool:_serviceTypeMenu_checkPass(id, type, const szPassword[MAX_PASSWORD + 1]) +{ + new max_length = 30; + new min_length = 2; + + if (strlen(szPassword) > max_length) { + client_print(id, print_center, "%L", id, "PASSWORD_TOO_LONG", max_length); + return false; + } + else if (strlen(szPassword) < min_length) { + client_print(id, print_center, "%L", id, "PASSWORD_TOO_SHORT", min_length); + return false; + } + + _serviceChosen(id, type, szPassword); + return true; +} + +_serviceChosen(id, type = 0, const szPassword[MAX_PASSWORD + 1] = "") +{ + new service[e_Service], price[e_Price]; + ArrayGetArray(g_services, which_service[id], service); + ArrayGetArray(g_prices, which_price[id], price); + copy(b_passwords[id], MAX_PASSWORD, szPassword); + b_types[id] = type; + + new ret; + new forward_handle = CreateOneForward(service[PLUGIN], "ss_service_chosen", FP_CELL, FP_CELL); + ExecuteForward(forward_handle, ret, id, price[QUANTITY]); + DestroyForward(forward_handle); + + if (ret != SS_STOP) { + _openPaymentMethodMenu(id); + } +} + +_openPaymentMethodMenu(id) +{ + new menu_title[64], item_text[64]; + formatex(menu_title, charsmax(menu_title), "%L", id, "CHOOSE_PAYMENT_METHOD"); + + new menu = menu_create(menu_title, "paymentMethodMenu_handler"); + new menu_callback = menu_makecallback("paymentMethodMenu_callback"); + + formatex(item_text, charsmax(item_text), "%L", id, "PAYMENT_METHOD_SMS"); + menu_additem(menu, item_text, "", 0, menu_callback); + + formatex(item_text, charsmax(item_text), "%L", id, "PAYMENT_METHOD_WALLET"); + menu_additem(menu, item_text, "", 0, menu_callback); + + new data[4]; + data[0] = id; + data[1] = menu; + + keepDisplayingMenu(data); +} + +public paymentMethodMenu_callback(id, menu, item) +{ + switch (item) { + case 0: { + return ITEM_ENABLED; + } + case 1: { + return ITEM_ENABLED; + } + } + + return ITEM_DISABLED; +} + +public paymentMethodMenu_handler(id, menu, item) +{ + remove_task(TASK_KEEP_DISPLAYING_MENU + id); + menu_destroy(menu); + + switch (item) { + case 0: { + _displaySmsDetails(id); + } + case 1: { + _purchaseService(id, "wallet"); + } + } +} + +_displaySmsDetails(id) +{ + if (!is_user_connected(id)) { + return; + } + + new service[e_Service], price[e_Price], sms[e_SMS], quantity[32 + 1]; + ArrayGetArray(g_services, which_service[id], service); + ArrayGetArray(g_prices, which_price[id], price); + + new sms_index = _getSmsIndexByPrice(price[SMS_PRICE]); + ArrayGetArray(g_smses, sms_index, sms); + + _getPriceQuantity(which_price[id], which_service[id], id, quantity, charsmax(quantity)); + + formatex(g_szTemp, charsmax(g_szTemp), + "\r%L^n^n%L", + id, "SHOP_SMS", + id, "PAYMENT_SMS_INFO", + service[sNAME], + quantity, + g_smsText, + sms[NUMBER], + format_price(price[SMS_PRICE]), + format_price(floatround(price[SMS_PRICE] * g_vat)) + ); + + keepDisplayingSmsDetails(g_szTemp, TASK_KEEP_INFO_SMS + id); +} + +public keepDisplayingSmsDetails(const info[], id) +{ + id -= TASK_KEEP_INFO_SMS; + if (!is_user_connected(id)) { + return; + } + + show_menu(id, KEYS, info, 1, "smsDetails"); + set_task(0.9, "keepDisplayingSmsDetails", id+TASK_KEEP_INFO_SMS, info, strlen(info)); +} + +public smsDetails_handler(id, key) +{ + if (task_exists(TASK_KEEP_INFO_SMS + id)) { + remove_task(TASK_KEEP_INFO_SMS + id); + } + + if (!is_user_connected(id)) { + return; + } + + if (key == 0) { + client_cmd(id, "messagemode Podaj_Kod_Zwrotny"); + } else if (key == 9) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "TRANSACTION_ABORTED"); + } +} + +public onSmsCodeEntered(id) +{ + if (!is_user_connected(id)) { + return; + } + + new sms_code[MAX_CODE + 1]; + read_argv(1, sms_code, charsmax(sms_code)); + + _logActor(id, "Entered sms code: [%s]", sms_code); + + if (strlen(sms_code) < 2) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "SMS_CODE_TOO_SHORT"); + return; + } + + if (containi(sms_code, " ") != -1) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "SMS_CODE_NO_SPACE"); + return; + } + + _purchaseService(id, "sms", sms_code); +} + +_purchaseService(id, const method[], const sms_code[] = "") { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "PURCHASE_IN_PROGRESS"); + + if (!_canUseShop()) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "SHOP_IS_UNAVAILABLE"); + return; + } + + new url[MAX_URL + 1]; + new body[MAX_BODY + 1]; + new curl_slist: headers; + { + new service[e_Service], auth_data[32 + 1], steam_id[32 + 1], price[e_Price], ip[MAX_IP + 1]; + new u_service_id[MAX_SERVICE_ID * 2 + 1], u_auth_data[64 + 1], u_password[MAX_PASSWORD * 2 + 1], u_sms_code[MAX_CODE * 2 + 1]; + + new sign[34], sign_body[1 + charsmax(auth_data) + MAX_CODE + MAX_SHOP_TOKEN + 1]; + new header[128 + 1]; + + new shop_url[MAX_URL + 1], shop_token[MAX_SHOP_TOKEN + 1]; + + _getShopUrl(shop_url, charsmax(shop_url)); + _getShopToken(shop_token, charsmax(shop_token)); + + // Get service's data + ArrayGetArray(g_services, which_service[id], service); + + // Get player's auth data + switch (b_types[id]) { + case TYPE_IP: get_user_ip(id, auth_data, charsmax(auth_data), true); + case TYPE_SID: get_user_authid(id, auth_data, charsmax(auth_data)); + case TYPE_NICK: get_user_name(id, auth_data, charsmax(auth_data)); + } + + // Get username for services without type - e.g. using OTHER service module + if (!strlen(auth_data)) { + get_user_name(id, auth_data, charsmax(auth_data)); + } + + get_user_ip(id, ip, charsmax(ip), true); + get_user_authid(id, steam_id, charsmax(steam_id)); + ArrayGetArray(g_prices, which_price[id], price); + + StringURLEncode(service[sID], u_service_id, charsmax(u_service_id)); + StringURLEncode(auth_data, u_auth_data, charsmax(u_auth_data)); + StringURLEncode(b_passwords[id], u_password, charsmax(u_password)); + StringURLEncode(sms_code, u_sms_code, charsmax(u_sms_code)); + + // Get players's language + new language[16]; + formatex(language, charsmax(language), "%L", id, "LANG"); + + // Calculate body sign + formatex(sign_body, charsmax(sign_body), + "%d#%s#%s#%s", + b_types[id], auth_data, sms_code, shop_token + ); + vc_md5(sign_body, sign); + + formatex(header, charsmax(header), "Authorization: Bearer %s", steam_id); + headers = curl_slist_append(headers, header); + headers = curl_slist_append(headers, "Accept: application/assoc"); + + formatex(body, charsmax(body), + "sign=%s&service_id=%s&type=%d&auth_data=%s&password=%s&method=%s&sms_code=%s&price_id=%d&ip=%s", + sign, u_service_id, b_types[id], u_auth_data, u_password, method, u_sms_code, price[pID], ip + ); + + formatex(url, charsmax(url), "%s/api/server/purchase?token=%s&language=%s", shop_url, shop_token, language); + } + + if (!makeRequest("POST", url, headers, body, "onPurchaseServiceResponse", id)) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "PURCHASE_REQUEST_FAILED"); + } +} + +public onPurchaseServiceResponse(const content[], CURLcode: code, statusCode, playerId) +{ + if (!is_user_connected(playerId)) { + return; + } + + if (code != CURLE_OK) { + client_print_color(playerId, Red, "^4[%s] ^1%L ^1Error: %d", PREFIX, playerId, "PURCHASE_REQUEST_FAILED", code); + return; + } + + if (statusCode >= 300) { + client_print_color(playerId, Red, "^4[%s] ^1%L ^1HTTP code: %d", PREFIX, playerId, "PURCHASE_REQUEST_FAILED", statusCode); + return; + } + + new status[128], text[128], warnings[512]; + + new Trie:dict = _createResponseDict(content); + TrieGetString(dict, "status", status, charsmax(status)); + TrieGetString(dict, "text", text, charsmax(text)); + TrieGetString(dict, "warnings", warnings, charsmax(warnings)); + TrieDestroy(dict); + + if (strlen(text)) { + client_print_color(playerId, Red, "^4[%s] ^1%s", PREFIX, text); + } else { + _log("Invalid purchase response: %s", content); + client_print_color(playerId, Red, "^4[%s] ^1%L ^1Invalid response.", PREFIX, playerId, "PURCHASE_REQUEST_FAILED"); + } + + if (equal(status, "warnings")) { + new title[64]; + formatex(title, charsmax(title), "%L", playerId, "PURCHASE_ERRORS"); + formatex(g_szTemp, charsmax(g_szTemp), + "\ + \ + \ +
%s
\ + %s
\ + %L\ + ", + title, + warnings, + playerId, "REUSE_SMS_CODE" + ); + show_motd(playerId, g_szTemp, title); + } + else if (equal(status, "purchased")) { + _handleServicePurchase(playerId); + } +} + +_handleServicePurchase(id) +{ + new ret, forward_handle, service[e_Service], price[e_Price]; + ArrayGetArray(g_services, which_service[id], service); + ArrayGetArray(g_prices, which_price[id], price); + + forward_handle = CreateOneForward(service[PLUGIN], "ss_service_bought", FP_CELL, FP_CELL); + ExecuteForward(forward_handle, ret, id, price[QUANTITY]); + DestroyForward(forward_handle); + + new quantity[32 + 1]; + _getPriceQuantity(which_price[id], which_service[id], id, quantity, charsmax(quantity)); + + if (ret == SS_ERROR) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "PURCHASE_FAILED", g_contact); + _logActor(id, "Purchase error. Service: [%s] Quantity: [%s] Price: [%d]", service[sNAME], quantity, price[SMS_PRICE]); + return; + } + + new title[64]; + formatex(title, charsmax(title), "%L", id, "PURCHASE_DETAILS"); + + if (b_types[id] & (TYPE_NICK | TYPE_IP)) { + // Set user a password + client_cmd(id, "setinfo _ss ^"%s^"", b_passwords[id]); + + formatex(g_szTemp, charsmax(g_szTemp), + "


\ +

%L %s

\ + %L
\ + setinfo _ss ^"%s^"

\ + %L\ +

", + id, "PURCHASE_SUCCEED", + service[sNAME], + id, "SETINFO_INSTRUCTION", "config.cfg", + b_passwords[id], + id, "CONTACT_US" + ); + show_motd(id, g_szTemp, title); + } + else if (b_types[id] & TYPE_SID) { + formatex(g_szTemp, charsmax(g_szTemp), + "


\ +

%L %s

\ + %L\ +

", + id, "PURCHASE_SUCCEED", + service[sNAME], + id, "CONTACT_US" + ); + show_motd(id, g_szTemp, title); + } + + client_print_color(id, Blue, "^4[%s] ^1%L ^4%s^1: %s", PREFIX, id, "PURCHASE_SUCCEED", service[sNAME], quantity); + + // If the service has flags assigned then let's reload players flags + // Reload has to be delayed a little bit so that user password would be loaded already + if (service[FLAGS]) { + set_task(2.0, "loadPlayersFlags"); + } +} + +_showPlayerServices(id) +{ + if (!is_user_connected(id)) { + return; + } + + new name[MAX_NAME + 1], ip[MAX_IP + 1], steam_id[MAX_SID + 1]; + new escaped_name[MAX_NAME * 2 + 1], escaped_ip[MAX_IP * 2 + 1], escaped_steam_id[MAX_SID * 2 + 1]; + + get_user_name(id, name, charsmax(name)); + get_user_ip(id, ip, charsmax(ip), true); + get_user_authid(id, steam_id, charsmax(steam_id)); + + StringURLEncode(name, escaped_name, charsmax(escaped_name)); + StringURLEncode(ip, escaped_ip, charsmax(escaped_ip)); + StringURLEncode(steam_id, escaped_steam_id, charsmax(escaped_steam_id)); + + // Get players's language + new language[16]; + formatex(language, charsmax(language), "%L", id, "LANG"); + + new shop_url[MAX_URL + 1], shop_token[MAX_SHOP_TOKEN + 1], url[MAX_URL + 1]; + new curl_slist: headers; + + _getShopUrl(shop_url, charsmax(shop_url)); + _getShopToken(shop_token, charsmax(shop_token)); + + headers = curl_slist_append(headers, "Accept: application/assoc"); + formatex( + url, charsmax(url), + "%s/api/server/user_services?token=%s&nick=%s&ip=%s&steam_id=%s&language=%s", + shop_url, shop_token, escaped_name, escaped_ip, escaped_steam_id, language + ); + + if (makeRequest("GET", url, headers, "", "onShowPlayerServicesResponse", id)) { + set_task(0.0, "task_displayLoading", TASK_DISPLAY_LOADING + id); + } else { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "LOAD_PLAYER_SERVICES_FAILED"); + } +} + +public onShowPlayerServicesResponse(const content[], CURLcode: code, statusCode, playerId) +{ + if (!is_user_connected(playerId)) { + return; + } + + if (task_exists(TASK_DISPLAY_LOADING + playerId)) { + remove_task(TASK_DISPLAY_LOADING + playerId); + } + + if (code != CURLE_OK) { + client_print_color(playerId, Red, "^4[%s] ^1%L ^1Error: %d", PREFIX, playerId, "LOAD_PLAYER_SERVICES_FAILED", code); + return; + } + + if (statusCode >= 300) { + client_print_color(playerId, Red, "^4[%s] ^1%L ^1HTTP code %d.", PREFIX, playerId, "PLAYER_SERVICES_REQUEST_FAILED", statusCode); + return; + } + + new count, key[MAX_CONFIG_KEY + 1], menu_title[64]; + formatex(menu_title, charsmax(menu_title), "%L", playerId, "ACTIVE_PLAYER_SERVICES"); + new menu = menu_create(menu_title, "showPlayerServices_handler"); + + new Trie:dict = _createResponseDict(content); + + if (!TrieGetTextInt(dict, "c", count)) { + _log("Invalid player services response. | %s", content); + client_print_color(playerId, Red, "^4[%s] ^1%L ^1Invalid response", PREFIX, playerId, "PLAYER_SERVICES_REQUEST_FAILED"); + TrieDestroy(dict); + return; + } + + for (new i = 0; i < count; ++i) { + new service_name[MAX_SERVICE_ID + 1], expire[32 + 1], item_text[128]; + + formatex(key, charsmax(key), "%d.s", i); + TrieGetString(dict, key, service_name, charsmax(service_name)); + + formatex(key, charsmax(key), "%d.e", i); + TrieGetString(dict, key, expire, charsmax(expire)); + + formatex(item_text, charsmax(item_text), "%L", playerId, "PLAYER_SERVICE_INFO", service_name, expire); + menu_additem(menu, item_text); + } + + TrieDestroy(dict); + + if (menu_items(menu)) { + menu_display(playerId, menu); + } + else { + new command[MAX_COMMAND + 1]; + _getShopCommand(command, charsmax(command)); + client_print_color(playerId, Red, "^4[%s] ^1%L", PREFIX, playerId, "NO_PLAYER_SERVICES", command); + } +} + +public showPlayerServices_handler(id, menu, item) +{ + menu_destroy(menu); +} + +// +// ----------------------------------------------- OTHER ---------------------------------------------------- +// + +_logActor(id, const format[], any:...) +{ + vformat(g_szTemp, charsmax(g_szTemp), format, 3); + + if (is_user_connected(id)) { + new name[MAX_NAME + 1], ip[MAX_IP + 1], sid[MAX_SID + 1]; + get_user_name(id, name, charsmax(name)); + get_user_ip(id, ip, charsmax(ip), true); + get_user_authid(id, sid, charsmax(sid)); + log_to_file(LOG_FILE, "%s; %s; %s. %s", name, ip, sid, g_szTemp); + } else { + log_to_file(LOG_FILE, "%s", g_szTemp); + } +} + +_log(const format[], any:...) +{ + vformat(g_szTemp, charsmax(g_szTemp), format, 2); + log_to_file(LOG_FILE, "%s", g_szTemp); +} + +public displayCommercial() +{ + new command[MAX_COMMAND + 1]; + _getShopCommand(command, charsmax(command)); + + for (new id = 0; id < get_maxplayers(); ++id) { + if (is_user_connected(id)) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "COMMERCIAL", command); + } + } + + new Float:time = get_pcvar_float(g_cvarCommercial); + + if (time) { + set_task(time, "displayCommercial"); + } +} + +_createShopSmsConfigFile() +{ + format(g_configPath, charsmax(g_configPath), "%s/%s", g_configPath, CFG_FILE); + + if (!file_exists(g_configPath)) { + new szText[1024], iLen=0, iMax=charsmax(szText); + iLen += formatex(szText[iLen], iMax-iLen, "// ----------------------------------^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// SHOP SMS: - Configuration file^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// ----------------------------------^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Turn the shop On / Off^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// 1 - ON^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// 0 - OFF^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_on ^"1^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Comma-separated commands list, which can be used via chat, to display the shop menu^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_shop_commands ^"/sklepsms,/shopsms^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Comma-separated commands list, which can be used via chat, to reload players flags^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_reload_commands ^"/przeladuj^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Comma-separated commands list, which can be used via chat, to display your active services^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_services_commands ^"/uslugi^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Should the above commands be visible on the chat after usage^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_silent_commands ^"0^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// How often (seconds) should the shop commercial be displayed on the chat? 0 - never^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_commercial ^"137^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// #####################^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// ### Shop WEB ###^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// #####################^n^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Shop URL^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_url ^"^"^n"); + iLen += formatex(szText[iLen], iMax-iLen, "^n"); + iLen += formatex(szText[iLen], iMax-iLen, "// Server token^n"); + iLen += formatex(szText[iLen], iMax-iLen, "shopsms_token ^"^"^n"); + write_file(g_configPath, szText); + } +} + +// +// ----------------------------------------------- LICENSE ---------------------------------------------------- +// + +_validateLicense() +{ + new curl_slist: headers; + new body[MAX_BODY + 1]; + + { + new headerAuthorization[512 + 1]; + new serverNameEscaped[MAX_SERVER_NAME * 2 + 1]; + get_cvar_string("hostname", g_serverName, charsmax(g_serverName)); + StringURLEncode(g_serverName, serverNameEscaped, charsmax(serverNameEscaped)); + + formatex(headerAuthorization, charsmax(headerAuthorization), + "Authorization: Bearer %s", + g_licenseToken + ); + headers = curl_slist_append(headers, headerAuthorization); + headers = curl_slist_append(headers, "Accept: application/assoc"); + + formatex(body, charsmax(body), + "url=%s:%s&name=%s&version=%s&signature=%d&platform_version=%s", + g_serverIp, g_serverPort, serverNameEscaped, VERSION, g_signature, AMXX_VERSION_STR + ); + } + + if (!makeRequest("POST", "https://license.sklep-sms.pl/v1/authorization/server", headers, body, "onValidateLicenseResponse")) { + _errorForbidden("There is no space in the request pool"); + } +} + +public onValidateLicenseResponse(const content[], CURLcode: code, statusCode, playerId) +{ + g_licenseValidated = false; + + if (g_validationAttempt) { + _errorForbidden(); + return; + } + + g_validationAttempt = true; + + if (code != CURLE_OK) { + _log("Invalid CURL code: %d", code); + _errorUnknown(); + return; + } + + if (statusCode == 401) { + _errorInvalidLicenseToken(); + return; + } + + if (statusCode == 422) { + _errorValidationError(); + return; + } + + if (statusCode == 402) { + _errorPaymentRequired(); + return; + } + + if (statusCode == 403) { + new text[128 + 1]; + + new Trie:dict = _createResponseDict(content); + TrieGetString(dict, "text", text, charsmax(text)); + TrieDestroy(dict); + + _errorForbidden(text); + return; + } + + if (statusCode >= 300) { + _errorLicenseInvalidResponse(statusCode); + return; + } + + new text[32 + 1], signature; + + new Trie:dict = _createResponseDict(content); + TrieGetString(dict, "text", text, charsmax(text)); + TrieGetTextInt(dict, "signature", signature); + TrieGetTextInt(dict, "footer", g_footer); + TrieDestroy(dict); + + if (!equal(text, "ok")) { + _log("Invalid license validation response | %s", content); + _errorUnknown(); + return; + } + + if (signature != g_signature) { + _errorForbidden(); + return; + } + + g_licenseValidated = true; + log_amx("%L", 0, "LICENSE_VALIDATED"); +} + +// +// ------------------------------------------------- HELPERS ---------------------------------------------------- +// + +public keepDisplayingMenu(data[]) +{ + new id = data[0]; + if (!is_user_connected(id)) { + menu_destroy(data[1]); + return; + } + + new menu, newmenu; + player_menu_info(id, menu, newmenu); + if (newmenu != data[1]) { + menu_display(id, data[1]); + } + + set_task(0.1, "keepDisplayingMenu", TASK_KEEP_DISPLAYING_MENU + id, data, 2); +} + +_checkLoadedPlayersFlags() +{ + log_amx("%L", 0, "PLAYER_SERVICES_LOADED", ArraySize(g_playersFlags)); +} + +_checkLoadedServices() +{ + log_amx("%L", 0, "SERVICES_LOADED", ArraySize(g_services)); +} + +_getServicePluginByID(const id[]) +{ + new tmp[MAX_SERVICE_ID + 1]; + for (new i = 0; i < ArraySize(service_ids); ++i) { + ArrayGetString(service_ids, i, tmp, charsmax(tmp)); + if (equali(id, tmp)) { + return ArrayGetCell(service_plugins, i); + } + } + + return -1; +} + +_getServiceByID(const id[]) +{ + new service[e_Service]; + for (new i = 0; i < ArraySize(g_services); ++i) { + ArrayGetArray(g_services, i, service); + if (equal(id, service[sID])) { + return i; + } + } + return -1; +} + +_getSmsIndexByPrice(price) +{ + new sms[e_SMS]; + for (new i = 0; i < ArraySize(g_smses); ++i) { + ArrayGetArray(g_smses, i, sms); + if (price == sms[PRICE]) { + return i; + } + } + + return -1; +} + +_getOrderOfType(type) +{ + switch (type) { + case FLAG_IP: return 1; + case FLAG_NICK : return 2; + case FLAG_AUTHID: return 3; + } + + return 0; +} + +_getPriceQuantity(price_index, service_index, player_id, output[], len) +{ + new price[e_Price]; + new service[e_Service]; + + ArrayGetArray(g_prices, price_index, price); + ArrayGetArray(g_services, service_index, service); + + if (price[QUANTITY] == -1) { + formatex(output, len, "%L", player_id, "FOREVER"); + } + else { + groupDigits(price[QUANTITY], output, len); + formatex(output[strlen(output)], len - strlen(output), " %s", service[TAG]); + } +} + +_canUseShop() +{ + return !g_blocked && g_licenseValidated && get_pcvar_num(g_cvarOn); +} + +_getShopCommand(output[], len) +{ + #if defined TrieIterCreate + new TrieIter:iter = TrieIterCreate(g_shopCommands); + if (!TrieIterEnded(iter)) { + TrieIterGetKey(iter, output, len); + } else { + formatex(output, len, ""); + } + TrieIterDestroy(iter); + #else + get_pcvar_string(g_cvarShopCommands, g_szTemp, charsmax(g_szTemp)); + copyc(output, len, g_szTemp, ','); + #endif +} + +_getShopToken(output[], len) +{ + get_pcvar_string(g_cvarShopToken, output, len); + trim(output); +} + +_getShopUrl(output[], len) +{ + get_pcvar_string(g_cvarShopUrl, output, len); + trim(output); + + // Remove trailing slash + if (strlen(output) && output[strlen(output) - 1] == '/') { + output[strlen(output)-1] = 0; + } +} + +/** + * https://amxx.pl/topic/43223-explode/ + */ +_explodeCommands(const text[], Trie:dict) +{ + TrieClear(dict); + + new cur_len = 0, max_len = strlen(text); + + while (cur_len < max_len) + { + new command[MAX_COMMAND + 1]; + cur_len += copyc(command, charsmax(command), text[cur_len], ',') + 1; + strtolower(command); + trim(command); + + if (strlen(command)) { + TrieSetCell(dict, command, 1); + } + } +} + +// +// ------------------------------------------------- ERRORS ---------------------------------------------------- +// + +// License +_errorUnknown() { + g_blocked = true; + _log("%L", 0, "ERROR_10"); +} + +_errorLicenseInvalidResponse(statusCode) { + g_blocked = true; + _log("%L", 0, "ERROR_11", statusCode); +} + +_errorValidationError() { + g_blocked = true; + _log("%L", 0, "ERROR_12"); +} + +_errorInvalidLicenseToken() { + g_blocked = true; + _log("%L", 0, "ERROR_13"); +} + +_errorPaymentRequired() { + _throwError("ERROR_14"); +} + +_errorForbidden(const text[] = "") { + #if AMXX_VERSION_NUM > 182 + set_fail_state("%L", 0, "ERROR_15", text); + #else + set_fail_state("ERROR_15"); + #endif +} + +// Config +_errorConfigFailed(code) { + g_blocked = true; + _log("%L", 0, "ERROR_20", code); +} + +_errorInvalidServerToken() { + _throwError("ERROR_21"); +} + +_errorOutdatedVersion() { + _throwError("ERROR_22"); +} + +_errorConfigInvalidResponse() { + g_blocked = true; + _log("%L", 0, "ERROR_23"); +} + +_errorNoShopCreds() { + _throwError("ERROR_24"); +} + +_throwError(const error[]) +{ + #if AMXX_VERSION_NUM > 182 + set_fail_state("%L", 0, error); + #else + set_fail_state(error); + #endif +} + +// +// ------------------------------------------------- HTTP Requests ---------------------------------------------------- +// + +public task_displayLoading(task_id) +{ + new id = task_id - TASK_DISPLAY_LOADING; + + if (is_user_connected(id)) { + client_print_color(id, Red, "^4[%s] ^1%L", PREFIX, id, "LOADING"); + } +} + +/** + * Make HTTP request + * + * @param const char[] method GET POST PUT etc. + * @param const char[] url path (eg. example.com/shop/ddd) + * @param const curl_slist headers headers + * @param const char[] body body params + * @param const char[] callback callback function + * @param const int playerId id of the player + * + * @return bool + */ +bool:makeRequest(const method[], const url[MAX_URL + 1], const curl_slist: headers, const body[], const callback[MAX_FUNCTION + 1] = "", const playerId = 0) +{ + new CURL: handle, curl_slist: newHeaders, data[requestStruct]; + + #if defined IS_DEBUG + _log("Request | %s %s %s", method, url, body); + #endif + + handle = curl_easy_init(); + if (!handle) { + _log("The CURL handle couldn't be initialized."); + return false; + } + + new requestId = g_requestCounter++ % MAX_REQUESTS_COUNT; + + if (g_requestsInProgress[requestId]) { + _log("Requests pool is full. Request has not been execeuted."); + return false; + } + + // Clear response array + setc(g_responses[requestId], MAX_RESPONSE, 0); + g_requestsInProgress[requestId] = true; + data[RequestId] = requestId; + data[RequestPlayerId] = playerId; + formatex(data[RequestCallback], MAX_FUNCTION, callback); + + newHeaders = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8"); + data[RequestHeaders] = newHeaders; + + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method); + curl_easy_setopt(handle, CURLOPT_URL, url); + curl_easy_setopt(handle, CURLOPT_USERAGENT, "amxmodx"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(handle, CURLOPT_COPYPOSTFIELDS, body); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, data[RequestId]); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, "@responseWrite"); + + curl_easy_perform(handle, "@requestComplete", data, requestStruct); + + return true; +} + +@responseWrite(const data[], const size, const nmemb, const requestId) +{ + new iLen = strlen(g_responses[requestId]); + new max_length = MAX_RESPONSE - iLen; + + if (max_length < nmemb) { + _log("Response too big error | %s", data); + } + + // We want to get "nmemb" chars from "data", because sometime there is some noise at the end od "data" + formatex(g_responses[requestId][iLen], min(nmemb, max_length), data); + + // Tell curl how many bytes we handled + return size * nmemb; +} + +@requestComplete(CURL: curl, CURLcode: code, const data[requestStruct]) +{ + if (code != CURLE_OK) { + new szError[128]; + curl_easy_strerror(code, szError, charsmax(szError)); + _log("CURL error: [%d] %s", code, szError); + } + + new statusCode; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, statusCode); + + runRequestCallback(data[RequestCallback], g_responses[data[RequestId]], data[RequestPlayerId], code, statusCode); + g_requestsInProgress[data[RequestId]] = false; + + curl_easy_cleanup(curl); + curl_slist_free_all(data[RequestHeaders]); +} + +runRequestCallback(const function[], const httpResponseContent[], playerId, CURLcode: code, statusCode) +{ + #if defined IS_DEBUG + _log("Request response | [%d] [%d] %s", code, statusCode, httpResponseContent); + #endif + + if (!strlen(function) || callfunc_begin(function) == 1) { + callfunc_push_str(httpResponseContent); + callfunc_push_int(_:code); + callfunc_push_int(statusCode); + callfunc_push_int(playerId); + callfunc_end(); + } else { + _log("Functions hasn't been execeuted properly [%s]", function); + } +} + + +// +// ------------------------------------------------- External Functions ---------------------------------------------------- +// + +public native_registerService(plugin, params) +{ + if (params != 1) { + _log("An error occurred while registering the service. Invalid number of arguments passed to the ss_register_service function."); + return SS_BAD_ARGS; + } + + new id[MAX_SERVICE_ID + 1]; + + get_string(1, id, charsmax(id)); + + ArrayPushCell(service_plugins, plugin); + ArrayPushString(service_ids, id); + + return SS_OK; +} + +public native_addLog(plugin, params) +{ + vdformat(g_szTemp, charsmax(g_szTemp), 1, 2); + log_to_file(LOG_FILE, "%s", g_szTemp); +} + +public native_showServicePurchase(plugin, params) +{ + if (params != 2) { + return SS_BAD_ARGS; + } + + new service_id[MAX_SERVICE_ID + 1]; + new id = get_param(1); + get_string(2, service_id, charsmax(service_id)); + + _openPricingMenu(id, _getServiceByID(service_id)); + return SS_OK; +} + +public native_openShopMenu(plugin, params) +{ + if (params != 1) { + return SS_BAD_ARGS; + } + + _openShopMenu(get_param(1)); + return SS_OK; +} + +public native_openPaymentMethodMenu(plugin, params) +{ + if (params != 1) { + return SS_BAD_ARGS; + } + + _openPaymentMethodMenu(get_param(1)); + return SS_OK; +} + +public bool:native_is_steam(plugin, params) +{ + if (params != 1) { + return false; + } + + return is_steam(get_param(1)); +} + +// +// ------------------------------------------------- Versions Compatibility ---------------------------------------------------- +// + +vc_md5(const szString[], md5buffer[34]) +{ + #if defined hash_string + hash_string(szString, Hash_Md5, md5buffer, 34); + #else + md5(szString, md5buffer); + #endif +} + +// --------------------------------------------------------- Stocks ------------------------------------------------------------ + +stock bool:is_steam(id) +{ + new auth[32]; + get_user_authid(id, auth, charsmax(auth)); + return contain(auth, "STEAM_0:0:") != -1 || contain(auth, "STEAM_0:1:") != -1; +} + +stock get_sms_cost(const number[]) +{ + if (strlen(number) < 4) { + return 0; + } + + if (number[0] == '7') { + new temp[4]; + formatex(temp, charsmax(temp), "%c", number[1]); + return number[1] == '0' ? 50 : str_to_num(temp) * 100; + } + + if (number[0] == '9') { + new temp[4]; + formatex(temp, charsmax(temp), "%c%c", number[1], number[2]); + return str_to_num(temp) * 100; + } + + return 0; +} + +stock format_price(value) +{ + new price[32]; + + formatex(price, charsmax(price), "%d.%s %s", value / 100, two_number_value(value % 100), g_currency); + + return price; +} + +stock two_number_value(value) +{ + new text[17]; + + if (value < 10) { + formatex(text, charsmax(text), "0%d", value); + } else { + formatex(text, charsmax(text), "%d", value); + } + + return text; +} + +stock groupDigits(a, output[], len) +{ + setc(output, len, 0); + new number[32]; + num_to_str(a, number, charsmax(number)); + + for (new i = 0; i < strlen(number); ++i) { + if (i && i % 3 == 0) + format(output, len, " %s", output); + + format(output, len, "%c%s", number[strlen(number) - 1 - i], output); + } +} + +stock StringURLEncode(const szInput[], szOutput[], const iLen) +{ + static const HEXCHARS[ 16 ] = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 + }; + + new iPos, cChar, iFLen; + while (( cChar = szInput[ iPos ] ) && iFLen < iLen ) + { + if (cChar == 0x20) { + szOutput[ iFLen++ ] = 0x2B; + } + else if (!( 0x41 <= cChar <= 0x5A ) + && !( 0x61 <= cChar <= 0x7A ) + && !( 0x30 <= cChar <= 0x39 ) + && cChar != 0x2D + && cChar != 0x2E + && cChar != 0x5F ) + { + if (( iFLen + 3 ) > iLen) { + break; + } + else if (cChar > 0xFF || cChar < 0x00) { + cChar = 0x2A; + } + + szOutput[ iFLen++ ] = 0x25; + szOutput[ iFLen++ ] = HEXCHARS[ cChar >> 4 ]; + szOutput[ iFLen++ ] = HEXCHARS[ cChar & 15 ]; + } + else { + szOutput[ iFLen++ ] = cChar; + } + + iPos++; + } + + szOutput[ iFLen ] = 0; + + return iFLen; +} + +stock bool:TrieGetTextInt(Trie: dict, const key[], &output) +{ + new szNumber[MAX_NUMBER_LENGTH + 1]; + + if (TrieGetString(dict, key, szNumber, charsmax(szNumber))) { + output = str_to_num(szNumber); + return true; + } + + return false; +} + +stock bool:TrieGetTextFloat(Trie: dict, const key[], &Float:output) +{ + new szNumber[MAX_NUMBER_LENGTH + 1]; + + if (TrieGetString(dict, key, szNumber, charsmax(szNumber))) { + output = str_to_float(szNumber); + return true; + } + + return false; +} diff --git a/scripting/shopsms_service_bf2_badge.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_bf2_badge.sma similarity index 100% rename from scripting/shopsms_service_bf2_badge.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_bf2_badge.sma diff --git a/scripting/shopsms_service_cod_exp.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_cod_exp.sma similarity index 100% rename from scripting/shopsms_service_cod_exp.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_cod_exp.sma diff --git a/scripting/shopsms_service_cod_te.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_cod_te.sma similarity index 100% rename from scripting/shopsms_service_cod_te.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_cod_te.sma diff --git a/scripting/shopsms_service_extra_flags.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_extra_flags.sma similarity index 100% rename from scripting/shopsms_service_extra_flags.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_extra_flags.sma diff --git a/scripting/shopsms_service_gxm_bm.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_gxm_bm.sma similarity index 100% rename from scripting/shopsms_service_gxm_bm.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_gxm_bm.sma diff --git a/scripting/shopsms_service_gxm_exp.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_gxm_exp.sma similarity index 100% rename from scripting/shopsms_service_gxm_exp.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_gxm_exp.sma diff --git a/scripting/shopsms_service_jbpack.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_jbpack.sma similarity index 100% rename from scripting/shopsms_service_jbpack.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_jbpack.sma diff --git a/scripting/shopsms_service_nickres.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_nickres.sma similarity index 100% rename from scripting/shopsms_service_nickres.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_nickres.sma diff --git a/scripting/shopsms_service_slotres.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_slotres.sma similarity index 100% rename from scripting/shopsms_service_slotres.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_slotres.sma diff --git a/scripting/shopsms_service_vip.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_vip.sma similarity index 100% rename from scripting/shopsms_service_vip.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_vip.sma diff --git a/scripting/shopsms_service_vippro.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_vippro.sma similarity index 100% rename from scripting/shopsms_service_vippro.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_vippro.sma diff --git a/scripting/shopsms_service_zp_ap.sma b/cstrike/addons/amxmodx/scripting/shopsms_service_zp_ap.sma similarity index 100% rename from scripting/shopsms_service_zp_ap.sma rename to cstrike/addons/amxmodx/scripting/shopsms_service_zp_ap.sma diff --git a/package.sh b/package.sh new file mode 100755 index 0000000..e0405b0 --- /dev/null +++ b/package.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +if [ "$#" -lt 1 ]; then + echo "Usage: ./package.sh " + exit 1 +fi + +BUILD_VERSION=$1 +DIR="$( cd "$( dirname $( dirname "${BASH_SOURCE[0]}" ) )" && pwd )" + +mkdir -p "$DIR/out" +rm -f "$DIR"/out/amxmodx-*.zip + +(cd "$DIR" && zip -r "out/amxmodx-${BUILD_VERSION}.zip" cstrike) diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..d51b182 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,56 @@ +FROM cm2network/steamcmd + +ARG BASE_URL +ARG CSTRIKE_URL +ARG metamod_version=1.20 + +# Install unzip +USER root +RUN apt-get update +RUN apt-get install -y unzip curl +USER steam + +# Install HLDS +RUN /home/steam/steamcmd/steamcmd.sh +login anonymous +force_install_dir /home/steam/dedicated +app_update 90 validate +quit; exit 0 +RUN /home/steam/steamcmd/steamcmd.sh +login anonymous +force_install_dir /home/steam/dedicated +app_update 90 validate +quit; exit 0 +RUN /home/steam/steamcmd/steamcmd.sh +login anonymous +force_install_dir /home/steam/dedicated +app_update 90 validate +quit + +RUN mkdir -p ~/.steam && ln -s /home/steam/dedicated ~/.steam/sdk32 +RUN ln -s /home/steam/steamcmd/ /home/steam/dedicated/steamcmd +ADD server/files/steam_appid.txt /home/steam/dedicated/steam_appid.txt + +# Add default config +# ADD files/server.cfg /opt/hlds/cstrike/server.cfg + +# Install metamod +RUN mkdir -p /home/steam/dedicated/cstrike/addons/metamod/dlls +RUN curl -sqL "http://prdownloads.sourceforge.net/metamod/metamod-$metamod_version-linux.tar.gz?download" | tar -C /home/steam/dedicated/cstrike/addons/metamod/dlls -zxvf - +ADD server/files/liblist.gam /home/steam/dedicated/cstrike/liblist.gam +ADD server/files/plugins.ini /home/steam/dedicated/cstrike/addons/metamod/plugins.ini + +# Install dproto +# RUN mkdir -p /home/steam/dedicated/cstrike/addons/dproto +# ADD files/dproto_i386.so /opt/hlds/cstrike/addons/dproto/dproto_i386.so +# ADD files/dproto.cfg /opt/hlds/cstrike/dproto.cfg + +# Install AMX mod X +RUN curl -sqL "${BASE_URL}" | tar -C /home/steam/dedicated/cstrike/ -zxvf - +RUN curl -sqL "${CSTRIKE_URL}" | tar -C /home/steam/dedicated/cstrike/ -zxvf - + +# Disable admin.amxx +RUN sed -i 's/admin.amxx.*/;admin.amxx/' /home/steam/dedicated/cstrike/addons/amxmodx/configs/plugins.ini + +# Add shopsms +RUN sed -i '1s/^/shopsms.amxx debug\n/' /home/steam/dedicated/cstrike/addons/amxmodx/configs/plugins.ini +ADD cstrike/addons/amxmodx/data/lang/* /home/steam/dedicated/cstrike/addons/amxmodx/data/lang/ +ADD cstrike/addons/amxmodx/modules/* /home/steam/dedicated/cstrike/addons/amxmodx/modules/ +ADD cstrike/addons/amxmodx/configs/* /home/steam/dedicated/cstrike/addons/amxmodx/configs/ +ADD cstrike/addons/amxmodx/plugins/* /home/steam/dedicated/cstrike/addons/amxmodx/plugins/ + +WORKDIR /home/steam/dedicated + +VOLUME /home/steam/dedicated + +EXPOSE 27015 + +CMD /home/steam/dedicated/hlds_run -game cstrike +maxplayers 16 +map de_dust2 diff --git a/server/files/liblist.gam b/server/files/liblist.gam new file mode 100644 index 0000000..39108d4 --- /dev/null +++ b/server/files/liblist.gam @@ -0,0 +1,16 @@ +game "Counter-Strike" +url_info "www.counter-strike.net" +url_dl "" +version "1.6" +size "184000000" +svonly "0" +secure "1" +type "multiplayer_only" +cldll "1" +hlversion "1111" +nomodels "1" +nohimodel "1" +mpentity "info_player_start" +gamedll_linux "addons/metamod/dlls/metamod_i386.so" +trainmap "tr_1" +edicts "1800" \ No newline at end of file diff --git a/server/files/plugins.ini b/server/files/plugins.ini new file mode 100644 index 0000000..726c452 --- /dev/null +++ b/server/files/plugins.ini @@ -0,0 +1 @@ +linux addons/amxmodx/dlls/amxmodx_mm_i386.so diff --git a/server/files/steam_appid.txt b/server/files/steam_appid.txt new file mode 100644 index 0000000..9a03714 --- /dev/null +++ b/server/files/steam_appid.txt @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/server/run.sh b/server/run.sh new file mode 100755 index 0000000..e1402bd --- /dev/null +++ b/server/run.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e + +if [ "$#" -lt 1 ]; then + echo "Usage: ./run.sh " + exit 1 +fi + +DIRECTORY=$(cd `dirname $0` && pwd) + +declare -A BASE_VERSIONS=( + ["1.8.2"]="https://www.amxmodx.org/release/amxmodx-1.8.2-base-linux.tar.gz" + ["1.8.3"]="https://www.amxmodx.org/amxxdrop/1.8/amxmodx-1.8.3-dev-git5201-base-linux.tar.gz" + ["1.9.0"]="https://www.amxmodx.org/amxxdrop/1.9/amxmodx-1.9.0-git5294-base-linux.tar.gz" + ["1.10.0"]="https://www.amxmodx.org/amxxdrop/1.10/amxmodx-1.10.0-git5465-base-linux.tar.gz" +) + +declare -A CSTRKE_VERSIONS=( + ["1.8.2"]="https://www.amxmodx.org/release/amxmodx-1.8.2-cstrike-linux.tar.gz" + ["1.8.3"]="https://www.amxmodx.org/amxxdrop/1.8/amxmodx-1.8.3-dev-git5201-cstrike-linux.tar.gz" + ["1.9.0"]="https://www.amxmodx.org/amxxdrop/1.9/amxmodx-1.9.0-git5294-cstrike-linux.tar.gz" + ["1.10.0"]="https://www.amxmodx.org/amxxdrop/1.10/amxmodx-1.10.0-git5465-cstrike-linux.tar.gz" +) + +BUILD_VERSION=$1 + +if [ -v ${BASE_VERSIONS[${BUILD_VERSION}]} ]; then + echo "Invalid version" + exit 1 +fi + +DIR="$( cd "$( dirname $( dirname "${BASH_SOURCE[0]}" ) )" && pwd )" +BASE_URL="${BASE_VERSIONS[${BUILD_VERSION}]}" +CSTRIKE_URL="${CSTRKE_VERSIONS[${BUILD_VERSION}]}" + +# RUN +IMAGE_NAME="cs16" +docker build -t ${IMAGE_NAME} --build-arg BASE_URL="${BASE_URL}" --build-arg CSTRIKE_URL="${CSTRIKE_URL}" -f "$DIR/server/Dockerfile" . \ +&& docker run --rm -it -p 27015:27015 --network sklep-sms_default --name sklepsms_cs16 -v "$DIRECTORY/storage/vault":"/home/steam/dedicated/cstrike/addons/amxmodx/data/vault" ${IMAGE_NAME}