using LANCommander.SDK; using LANCommander.SDK.Models; using Microsoft.Extensions.Logging; using RestSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading.Tasks; namespace LANCommander.SDK { public class Client { private readonly ILogger Logger; private RestClient ApiClient; private AuthToken Token; public string BaseUrl; public Client(string baseUrl) { BaseUrl = baseUrl; if (!String.IsNullOrWhiteSpace(BaseUrl)) ApiClient = new RestClient(BaseUrl); } public Client(string baseUrl, ILogger logger) { BaseUrl = baseUrl; if (!String.IsNullOrWhiteSpace(BaseUrl)) ApiClient = new RestClient(BaseUrl); Logger = logger; } private T PostRequest(string route, object body) { var request = new RestRequest(route) .AddJsonBody(body) .AddHeader("Authorization", $"Bearer {Token.AccessToken}"); var response = ApiClient.Post(request); return response.Data; } private T PostRequest(string route) { var request = new RestRequest(route) .AddHeader("Authorization", $"Bearer {Token.AccessToken}"); var response = ApiClient.Post(request); return response.Data; } private T GetRequest(string route) { var request = new RestRequest(route) .AddHeader("Authorization", $"Bearer {Token.AccessToken}"); var response = ApiClient.Get(request); return response.Data; } private string DownloadRequest(string route, Action progressHandler, Action completeHandler) { route = route.TrimStart('/'); var client = new WebClient(); var tempFile = Path.GetTempFileName(); client.Headers.Add("Authorization", $"Bearer {Token.AccessToken}"); client.DownloadProgressChanged += (s, e) => progressHandler(e); client.DownloadFileCompleted += (s, e) => completeHandler(e); client.DownloadFileAsync(new Uri($"{ApiClient.BaseUrl}{route}"), tempFile); return tempFile; } private TrackableStream StreamRequest(string route) { route = route.TrimStart('/'); var client = new WebClient(); var tempFile = Path.GetTempFileName(); client.Headers.Add("Authorization", $"Bearer {Token.AccessToken}"); var ws = client.OpenRead(new Uri($"{ApiClient.BaseUrl}{route}")); return new TrackableStream(ws, true, Convert.ToInt64(client.ResponseHeaders["Content-Length"])); } public async Task AuthenticateAsync(string username, string password) { var response = await ApiClient.ExecuteAsync(new RestRequest("/api/Auth", Method.POST).AddJsonBody(new AuthRequest() { UserName = username, Password = password })); switch (response.StatusCode) { case HttpStatusCode.OK: Token = new AuthToken { AccessToken = response.Data.AccessToken, RefreshToken = response.Data.RefreshToken, Expiration = response.Data.Expiration }; return Token; case HttpStatusCode.Forbidden: case HttpStatusCode.BadRequest: case HttpStatusCode.Unauthorized: throw new WebException("Invalid username or password"); default: throw new WebException("Could not communicate with the server"); } } public async Task RegisterAsync(string username, string password) { var response = await ApiClient.ExecuteAsync(new RestRequest("/api/auth/register", Method.POST).AddJsonBody(new AuthRequest() { UserName = username, Password = password })); switch (response.StatusCode) { case HttpStatusCode.OK: Token = new AuthToken { AccessToken = response.Data.AccessToken, RefreshToken = response.Data.RefreshToken, Expiration = response.Data.Expiration }; return Token; case HttpStatusCode.BadRequest: case HttpStatusCode.Forbidden: case HttpStatusCode.Unauthorized: throw new WebException(response.Data.Message); default: throw new WebException("Could not communicate with the server"); } } public async Task PingAsync() { var response = await ApiClient.ExecuteAsync(new RestRequest("/api/Ping", Method.GET)); return response.StatusCode == HttpStatusCode.OK; } public AuthToken RefreshToken(AuthToken token) { Logger?.LogTrace("Refreshing token..."); var request = new RestRequest("/api/Auth/Refresh") .AddJsonBody(token); var response = ApiClient.Post(request); if (response.StatusCode != HttpStatusCode.OK) throw new WebException(response.ErrorMessage); Token = new AuthToken { AccessToken = response.Data.AccessToken, RefreshToken = response.Data.RefreshToken, Expiration = response.Data.Expiration }; return Token; } public bool ValidateToken() { return ValidateToken(Token); } public bool ValidateToken(AuthToken token) { Logger?.LogTrace("Validating token..."); if (token == null) { Logger?.LogTrace("Token is null!"); return false; } var request = new RestRequest("/api/Auth/Validate") .AddHeader("Authorization", $"Bearer {token.AccessToken}"); if (String.IsNullOrEmpty(token.AccessToken) || String.IsNullOrEmpty(token.RefreshToken)) { Logger?.LogTrace("Token is empty!"); return false; } var response = ApiClient.Post(request); var valid = response.StatusCode == HttpStatusCode.OK; if (valid) Logger?.LogTrace("Token is valid!"); else Logger?.LogTrace("Token is invalid!"); return response.StatusCode == HttpStatusCode.OK; } public void UseToken(AuthToken token) { Token = token; } public void UseServerAddress(string address) { BaseUrl = address; ApiClient = new RestClient(BaseUrl); } public IEnumerable GetGames() { return GetRequest>("/api/Games"); } public Game GetGame(Guid id) { return GetRequest($"/api/Games/{id}"); } public GameManifest GetGameManifest(Guid id) { return GetRequest($"/api/Games/{id}/Manifest"); } public string DownloadGame(Guid id, Action progressHandler, Action completeHandler) { return DownloadRequest($"/api/Games/{id}/Download", progressHandler, completeHandler); } public TrackableStream StreamGame(Guid id) { return StreamRequest($"/api/Games/{id}/Download"); } public string DownloadArchive(Guid id, Action progressHandler, Action completeHandler) { return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler); } public TrackableStream StreamRedistributable(Guid id) { return StreamRequest($"/api/Redistributables/{id}/Download"); } public string DownloadSave(Guid id, Action progressHandler, Action completeHandler) { return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler); } public string DownloadLatestSave(Guid gameId, Action progressHandler, Action completeHandler) { return DownloadRequest($"/api/Saves/DownloadLatest/{gameId}", progressHandler, completeHandler); } public GameSave UploadSave(string gameId, byte[] data) { Logger?.LogTrace("Uploading save..."); var request = new RestRequest($"/api/Saves/Upload/{gameId}", Method.POST) .AddHeader("Authorization", $"Bearer {Token.AccessToken}"); request.AddFile(gameId, data, gameId); var response = ApiClient.Post(request); return response.Data; } public string GetMediaUrl(Media media) { return (new Uri(ApiClient.BaseUrl, $"/api/Media/{media.Id}/Download?fileId={media.FileId}").ToString()); } public string GetKey(Guid id) { Logger?.LogTrace("Requesting key allocation..."); var macAddress = GetMacAddress(); var request = new KeyRequest() { GameId = id, MacAddress = macAddress, ComputerName = Environment.MachineName, IpAddress = GetIpAddress(), }; var response = PostRequest($"/api/Keys/Get", request); return response.Value; } public string GetAllocatedKey(Guid id) { Logger?.LogTrace("Requesting allocated key..."); var macAddress = GetMacAddress(); var request = new KeyRequest() { GameId = id, MacAddress = macAddress, ComputerName = Environment.MachineName, IpAddress = GetIpAddress(), }; var response = PostRequest($"/api/Keys/GetAllocated/{id}", request); if (response == null) return String.Empty; return response.Value; } public string GetNewKey(Guid id) { Logger?.LogTrace("Requesting new key allocation..."); var macAddress = GetMacAddress(); var request = new KeyRequest() { GameId = id, MacAddress = macAddress, ComputerName = Environment.MachineName, IpAddress = GetIpAddress(), }; var response = PostRequest($"/api/Keys/Allocate/{id}", request); if (response == null) return String.Empty; return response.Value; } public User GetProfile() { Logger?.LogTrace("Requesting player's profile..."); return GetRequest("/api/Profile"); } public string ChangeAlias(string alias) { Logger?.LogTrace("Requesting to change player alias..."); var response = PostRequest("/api/Profile/ChangeAlias", alias); return alias; } public void StartPlaySession(Guid gameId) { Logger?.LogTrace("Starting a game session..."); PostRequest($"/api/PlaySessions/Start/{gameId}"); } public void EndPlaySession(Guid gameId) { Logger?.LogTrace("Ending a game session..."); PostRequest($"/api/PlaySessions/End/{gameId}"); } private string GetMacAddress() { return NetworkInterface.GetAllNetworkInterfaces() .Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) .Select(nic => nic.GetPhysicalAddress().ToString()) .FirstOrDefault(); } private string GetIpAddress() { return Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString(); } } }