using Newtonsoft.Json; using System; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection.Metadata.Ecma335; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace MatrixDotNetLib { public class MatrixSessionManager { /// /// Gets or sets the authentication token. /// public string Token { get; set; } /// /// Gets or sets the Matrix homeserver FQDN. /// public string Server { get; set; } /// /// Gets or sets the login session as a MatrixLoginResponse object. /// public MatrixLoginResponse LoginSession { get; set; } /// /// Logs the user in with specified server and credentials. /// /// Required /// Required /// Required /// Optional /// public MatrixLoginResponse Login(string server, string user, string pass, string device = "") { // set the object's server this.Server = server; MatrixLoginIdentifier userId = new MatrixLoginIdentifier() { IdType = "m.id.user", User = user }; MatrixLoginRequest theRequest = new MatrixLoginRequest() { Password = pass, Identifier = userId, LoginType = "m.login.password" }; if (device.Length > 0) { theRequest.DeviceId = device; } // serialize object into JSON string requestJson = JsonConvert.SerializeObject(theRequest); string responseJson = ApiResult(MatrixApi.User.Login, httpAction.POST, requestJson); MatrixLoginResponse response = JsonConvert.DeserializeObject(responseJson); // we're logged in, so set the object's token this.Token = response.Token; // also set the object's session data this.LoginSession = response; return response; } /// /// Gets the logged-in user's currently-joined rooms. /// /// public MatrixUserRooms GetRooms() { string responseJson = ApiResult(MatrixApi.User.Rooms, httpAction.GET); return (MatrixUserRooms)JsonConvert.DeserializeObject(responseJson); } /// /// Gets the aliases for the given (string)roomId /// /// Full string ID (not alias) of a room /// public MatrixRoomAliases GetRoomAliases(string roomId) { MatrixApiEntities entities = new MatrixApiEntities() { RoomId = roomId }; string responseJson = ApiResult(MatrixApi.Room.Aliases, httpAction.GET, entities: entities); return (MatrixRoomAliases)JsonConvert.DeserializeObject(responseJson); } /// /// Joins the user to the given room by (string)roomId /// /// Full string ID (not alias) of a room /// public MatrixRoom JoinRoom(string roomId) { // create entity object MatrixApiEntities apiEntities = new MatrixApiEntities() { RoomId = roomId }; string responseJson = ApiResult(MatrixApi.Room.Join, httpAction.POST, entities: apiEntities); return (MatrixRoom)JsonConvert.DeserializeObject(responseJson); } public MatrixRoomDirectory GetPublicRooms(int limit = 0, string page = null, string server = null) { string[] querystring = { }; if (limit > 0) { querystring.Append("limit=" + limit); } if (page != null) { querystring.Append("since=" + page); } if (server != null) { querystring.Append("server=" + server); } string responseJson = ""; if (querystring.Count() > 0) { string qs = string.Join("&", querystring); responseJson = ApiResult(MatrixApi.Server.RoomList, httpAction.GET, query: qs); } else { responseJson = ApiResult(MatrixApi.Server.RoomList, httpAction.GET); } return (MatrixRoomDirectory)JsonConvert.DeserializeObject(responseJson); } public enum httpAction { DELETE, GET, POST, PUT } public string ApiResult(string api, httpAction action = httpAction.GET, string json = null, MatrixApiEntities entities = null, string query = null) { // simplest implementation // might not work for UWP // sauce: https://stackoverflow.com/questions/5527316/how-to-set-the-content-of-an-httpwebrequest-in-c // replace all entities in param string url = "https://" + this.Server + api; HttpClient client = new HttpClient(); client.BaseAddress = new Uri(url); // if an access token exists, create a bearer authorization header if (this.Token != null) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.Token); } // init empty string string responseString = ""; // DELETE: replace all entities in uri with values in param // GET: replace all entities in uri with values in param // POST: replace entities, create content from json // PUT: replace entitites, create content from json if (entities != null) { // check if any populated entity property exists in the url // find {...} in url // replace with values from entity object string matchPattern = @"\{([A-Za-z]+)\}"; Regex rgx = new Regex(matchPattern); MatrixUtil util = new MatrixUtil(); foreach (Match match in rgx.Matches(url)) { string oldVal = match.Value; string prop = match.Groups[1].Value; // check if property exists in entity list try { // get value of property through GetType().GetProperty().GetValue() // sauce: https://www.techrepublic.com/article/applied-reflection-dynamically-accessing-properties-of-a-class-at-runtime/ string newVal = WebUtility.UrlEncode(entities.GetType().GetProperty(prop).GetValue(entities).ToString()); // return URL with entities replaced and URL-encoded url = url.Replace(oldVal, newVal); } catch { // shit broke } } } HttpResponseMessage responseMessage = new HttpResponseMessage(); if (json != null) { // create HTTP request body from JSON string HttpContent requestContent = new StringContent(json, Encoding.UTF8, "application/json"); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); switch (action) { case httpAction.POST: responseMessage = client.PostAsync(url, requestContent).Result; break; case httpAction.PUT: responseMessage = client.PutAsync(url, requestContent).Result; break; } } else { // no message body, so this must be a GET or DELETE operation switch (action) { case httpAction.DELETE: responseMessage = client.DeleteAsync(url).Result; break; case httpAction.GET: responseMessage = client.GetAsync(url).Result; break; } } responseString = responseMessage.Content.ReadAsStringAsync().Result; if (responseString.Contains("errcode")) { // deserialize into error object MatrixError error = JsonConvert.DeserializeObject(responseString); // convert error object to a string string errMsg = error.ErrorCode + ": " + error.ErrorMessage; errMsg += "\n\nAPI accessed : " + url; errMsg += "\nToken Used : " + (this.Token.Length > 0 ? "yes" : "no"); // throw exception (can be caught and handled gracefully) throw new Exception(errMsg); } else if (responseMessage.StatusCode != HttpStatusCode.OK) { // web server responded with an HTTP status code other than 200 OK string errMsg = "Server returned " + (int)responseMessage.StatusCode + ": " + responseMessage.StatusCode.ToString(); errMsg += "\n\nAPI accessed : " + url; errMsg += "\nToken Used : " + (this.Token.Length > 0 ? "yes" : "no"); throw new Exception(errMsg); } else { return responseString; } } } } //// simplest implementation //// might not work for UWP //// sauce: https://stackoverflow.com/questions/5527316/how-to-set-the-content-of-an-httpwebrequest-in-c //HttpContent requestContent = new StringContent(requestJson, Encoding.UTF8, "application/json"); //HttpClient client = new HttpClient(); //client.BaseAddress = new Uri(loginUrl); //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); //HttpResponseMessage responseMessage = client.PostAsync(loginUrl, requestContent).Result; //string responseString = responseMessage.Content.ReadAsStringAsync().Result; //if(responseString.Contains("errcode")) //{ // // deserialize into error object // MatrixError error = JsonConvert.DeserializeObject(responseString); // // convert error object to a string // string errMsg = error.ErrorCode + ": " + error.ErrorMessage; // // throw exception (can be caught and handled gracefully) // throw new Exception(errMsg); //} //else //{ // MatrixLoginResponse response = JsonConvert.DeserializeObject(responseString); // return response; //}