//attempt to login to KerbalX with the user's auth token. If the token doesn't exist, or is no longer valid the response will be a 401 internal void login(RequestCallback callback) { try{ if (File.Exists(KerbalXAPI.token_path)) { KXAPI.log("Logging into KerbalX.com with Token..."); string current_token = File.ReadAllText(KerbalXAPI.token_path); authenticate_token(current_token, (resp, code) => { if (code == 200) { KXAPI.log("Logged in"); var resp_data = JSON.Parse(resp); KerbalXAPI.kx_username = resp_data["username"]; KerbalXAPI.token = current_token; } else { KXAPI.log("Login token is invalid"); } callback(resp, code); }); } else { callback("", 401); } } catch { callback("", 401); } }
//Used in request to url entered by user for image, returns just the content type header info private IEnumerator transmit(UnityWebRequest request, ImageUrlCheck callback) { KXAPI.log("sending request to: " + request.url); yield return(request.Send()); callback(request.GetResponseHeaders()["Content-Type"]); }
internal void send(KerbalXAPI api, RequestCallback callback, bool authenticate = true) { if (String.IsNullOrEmpty(api.client_version) || String.IsNullOrEmpty(api.client)) { KXAPI.log("client info has not been set"); return; } if (authenticate) { if (api.logged_in) { set_header("token", KerbalXAPI.token); } else { KerbalXLoginUI.add_login_callback(api, (login_succsessful) => { if (login_succsessful) { this.send(api, callback, authenticate); } }); KerbalXLoginUI.open(); return; } } set_header("MODCLIENT", api.client); set_header("MODCLIENTVERSION", api.client_version); set_header("MODCLIENTSIGNITURE", api.client_signiture); set_header("KSPVERSION", Versioning.GetVersionString()); if (RequestHandler.instance == null) { KerbalXAPIHelper.instance.start_request_handler(); } RequestHandler.instance.send_request(api, request, callback); }
//nukes the authentication token file and user variables and sets the login gui to enable login again. internal void logout(RequestCallback callback) { token = null; kx_username = null; File.Delete(KerbalXAPI.token_path); callback("", 200); KXAPI.log("Logged out of KerbalX"); }
//Start the Request Handler internal void start_request_handler() { if (RequestHandler.instance == null) { KXAPI.log("starting web request handler"); RequestHandler request_handler = gameObject.AddOrGetComponent <RequestHandler>(); RequestHandler.instance = request_handler; } }
//Internal Authentication POST requests //make request to site to authenticate username and password and get token back internal void login(string username, string password, RequestCallback callback) { KXAPI.log("Logging into KerbalX.com..."); NameValueCollection data = new NameValueCollection() { { "username", username }, { "password", password } }; RequestHandler.show_401_message = false; //don't show standard 401 error dialog HTTP.post(url_to("api/login"), data).send(this, (resp, code) => { if (code == 200) { KXAPI.log("Logged in"); var resp_data = JSON.Parse(resp); KerbalXAPI.token = resp_data["token"]; KerbalXAPI.save_token(resp_data["token"]); KerbalXAPI.kx_username = resp_data["username"]; } callback(resp, code); }, false); }
//Constructor - create a new instance of KerbalXAPI. Requires mod name and version and generates a checksum of the dll //that contains the assembly which called new KerbalXAPI(), which will be used as a signature of the mod using the API. public KerbalXAPI(string mod_name, string mod_version) { if (instances.ContainsKey(mod_name)) { KXAPI.log("An API instance for " + mod_name + " already exists"); instances.Remove(mod_name); } instances.Add(mod_name, this); this.client = mod_name; this.client_version = mod_version; //generate checksum of the calling assembly's dll which will act as the mods' signiture var calling_method = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod(); string caller_file = calling_method.ReflectedType.Assembly.Location; this.client_signiture = Checksum.from_file(caller_file); if (mod_name != "KerbalXAPI") { KXAPI.log("Instantiating KerbalXAPI for '" + mod_name); // + "'. Sig: " + this.client_signiture); } }
//Used in all interacton with KerbalX, called from a Coroutine and handles the response error codes from the site private IEnumerator transmit(KerbalXAPI api, UnityWebRequest request, RequestCallback callback) { // last_request = null; // last_callback = null; // api_instance = api; api.server_error_message = null; api.failed_to_connect = false; api.upgrade_required = false; KXAPI.log("sending request to: " + request.url); yield return(request.Send()); if (request.isNetworkError) //Request Failed, most likely due to being unable to get a response, therefore no status code { api.failed_to_connect = true; KXAPI.log("request failed: " + request.error); // last_request = new UnityWebRequest(request.url, request.method); // \ create a copy of the request which is about to be sent // if(request.method != "GET"){ // | if the request fails because of inability to connect to site // last_request.uploadHandler = new UploadHandlerRaw(request.uploadHandler.data); // < then try_again() can be used to fire the copied request // } // | and the user can carry on from where they were when connection was lost. // last_request.downloadHandler = request.downloadHandler; // | upload and download handlers have to be duplicated too // last_callback = callback; // / and the callback is also stuffed into a var for reuse. } else { int status_code = (int)request.responseCode; //server responded - get status code KXAPI.log("request returned " + status_code + " " + status_codes[status_code.ToString()]); if (status_code == 500) //KerbalX server error { string error_message = "KerbalX server error!!\n" + //default error message incase server doesn't come back with something more helpful "An error has occurred on KerbalX (it was probably Jebs fault)"; var resp_data = JSON.Parse(request.downloadHandler.text); //read response message and assuming there is one change the error_message if (!(resp_data["error"] == null || resp_data["error"] == "")) { error_message = "KerbalX server error!!\n" + resp_data["error"]; } KXAPI.log(error_message); api.server_error_message = error_message; //Set the error_message on KerbalX, any open window will pick this up and render error dialog callback(request.downloadHandler.text, status_code); //Still call the callback, assumption is all callbacks will test status code for 200 before proceeding, this allows for further handling if needed } else if (status_code == 426) //426 - Upgrade Required, only for a major version change that makes past versions incompatible with the site's API { api.upgrade_required = true; var resp_data = JSON.Parse(request.downloadHandler.text); api.upgrade_required_message = resp_data["message"]; callback("", status_code); } else if (status_code == 401) //401s (Unauthorized) - response to the user's token not being recognized. { if (RequestHandler.show_401_message == true) //In the case of login/authenticate, the 401 message is not shown (handled by login dialog) { api.server_error_message = "Login to KerbalX.com failed"; api.logout((resp, code) => {}); } callback(request.downloadHandler.text, status_code); } else if (status_code == 200 || status_code == 400 || status_code == 422) //Error codes returned for OK and failed validations which are handled by the requesting method { callback(request.downloadHandler.text, status_code); } else //Unhandled error codes - All other error codes. { api.server_error_message = "Error " + status_code + "\n"; var resp_data = JSON.Parse(request.downloadHandler.text); if (!(resp_data["error"] == null || resp_data["error"] == "")) { api.server_error_message += resp_data["error"]; } else if (!(resp_data["message"] == null || resp_data["message"] == "")) { api.server_error_message += resp_data["message"]; } else { api.server_error_message += request.downloadHandler.text; } callback(request.downloadHandler.text, status_code); } request.Dispose(); RequestHandler.show_401_message = true; } }