A few days ago I started to work with the following libraries: json and curl by Polarhigh
My target is to create an authentication system base on Laravel. For example you can login to your web account by entering email and password. This plugin supposed to send your data via http and waiting for the user id delivered by response. A bit tricky but not overcomplicated.
Lets discuss the results. Pros and cons. This is the first working version. What things would you change for optimalisation or cause my implementation is kinda stupid and why?
PHP Code:
#include <amxmisc>
#include <curl>
#include <json>
new Array: g_requests;
enum _: REQUEST_DATA {
REQUEST_DATA_ID,
REQUEST_DATA_USERID,
REQUEST_DATA_BODY[512]
}
public plugin_init() {
register_plugin("HTTPS", "1.0.0", "JohanCorn");
g_requests = ArrayCreate(REQUEST_DATA);
register_clcmd("login", "cmd_login");
}
public cmd_login(id) {
new args[64];
read_args(args, charsmax(args));
remove_quotes(args);
new email[32], password[32];
argbreak(args, email, charsmax(email), password, charsmax(password));
client_print(id, print_chat, "Try to login with %s and %s", email, password);
// Init curl;
new CURL: pCurl = curl_easy_init();
// Create headers;
new curl_slist: pHeaders;
pHeaders = curl_slist_append(pHeaders, "Content-Type: application/json");
pHeaders = curl_slist_append(pHeaders, "User-Agent: Curl");
// Create request data; (Player id, userid and empty body)
new request_data[REQUEST_DATA];
request_data[REQUEST_DATA_ID] = id;
request_data[REQUEST_DATA_USERID] = get_user_userid(id);
request_data[REQUEST_DATA_BODY][0] = EOS;
// Put it into the global array;
new request_id = ArrayPushArray(g_requests, request_data);
// Log;
log_to_file("test.log", "Start! Request ID: %i | %i %i", request_id, request_data[REQUEST_DATA_ID], request_data[REQUEST_DATA_USERID]);
// Body text;
new body[256];
format(body, charsmax(body), "{^"email^": ^"%s^", ^"password^": ^"%s^"}", email, password);
// Set curl things;
curl_easy_setopt(pCurl, CURLOPT_URL, "http://johancorn.sunwell.hu/api/login");
curl_easy_setopt(pCurl, CURLOPT_COPYPOSTFIELDS, body);
curl_easy_setopt(pCurl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pHeaders);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, "response_write");
curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, request_id);
curl_easy_setopt(pCurl, CURLOPT_CAINFO, "/server143/cstrike/cacert.pem");
// Tmp data;
new data[1];
data[0] = request_id;
// FIRE!
curl_easy_perform(pCurl, "request_complete", data, sizeof(data));
}
public response_write(const data[], const size, const nmemb, const request_id) {
// Somehow data size is more than the real size so create a new place and copy into it;
// Thats how we remove garbage from the end of the string;
new data_fix[512];
add(data_fix, charsmax(data_fix), data, nmemb);
// Get request data by request_id;
new request_data[REQUEST_DATA];
ArrayGetArray(g_requests, request_id, request_data);
// Add body to stored body; (Empty by default); Required for chunks;
add(request_data[REQUEST_DATA_BODY], charsmax(request_data[REQUEST_DATA_BODY]), data_fix);
// Overwrite request data in array;
ArraySetArray(g_requests, request_id, request_data);
// Return size (curl)
return size * nmemb;
}
public request_complete(CURL: curl, CURLcode: code, const data[1]) {
// Get stored request_id;
new request_id = data[0];
// Get request data by request_id;
new request_data[REQUEST_DATA];
ArrayGetArray(g_requests, request_id, request_data);
// Get player id by request data;
new id = request_data[REQUEST_DATA_ID];
// Is stored player userid equals current player's userid;
if ( get_user_userid(id) == request_data[REQUEST_DATA_USERID] ) {
// Is Curl status OK;
if ( code == CURLE_OK ) {
// Get status code; (200, 401, etc.)
new status_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status_code);
// Log;
log_to_file("test.log", "Completed! Request ID: %i | %i %i | Body: %s | Status: %i", request_id, request_data[REQUEST_DATA_ID], request_data[REQUEST_DATA_USERID], request_data[REQUEST_DATA_BODY], status_code);
// All good;
if ( status_code == 200 ) {
// Do login display;
login_authorized(id, request_data[REQUEST_DATA_BODY]);
}
// Something is wrong;
else {
switch ( status_code ) {
case 401: login_failed(id, "Invalid login details.");
case 402..499: {
// Error text;
new error[64]; format(error, charsmax(error), "Client Error: %i", code);
login_failed(id, error);
}
case 500..599: {
// Error text;
new error[64]; format(error, charsmax(error), "Server Error: %i", code);
login_failed(id, error);
}
default: {
// Error text;
new error[64]; format(error, charsmax(error), "Other Error: %i", code);
login_failed(id, error);
}
}
}
}
// Some weird error;
else {
// Log;
log_to_file("test.log", "Failed! Request ID: %i | %i %i", request_id, request_data[REQUEST_DATA_ID], request_data[REQUEST_DATA_USERID]);
// Error text;
new error[64]; format(error, charsmax(error), "Weird Code: %i", code);
// Do error display;
login_failed(id, error);
}
}
else {
// Response received too late. (Player disconnected.)
// Do nothing.
}
curl_easy_cleanup(curl);
}
public login_authorized(id, body[]) {
new JSON: json; json = json_parse(body);
client_print(id, print_console, "You are logged in as #%i!", json_object_get_number(json, "id"));
}
public login_failed(id, error[]) {
client_print(id, print_console, "Login failed! Error: %s", error);
}
If you want to check the code in runtime I can give you a test account and a game server installed with this plugin.
You can pass array of data to request_complete function by setting the 3-4th params. But you only can send an integer value to response_write function. It doesnt make sense to me. Since the source is expecting a pointer of data it should work with an array too. Isnt it?
Edit: I know the address is not secured. Cause its only for tests.