A native .NET C# library for developing Matrix clients.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
11 KiB

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
{
/// <summary>
/// Gets or sets the authentication token.
/// </summary>
public string Token { get; set; }
/// <summary>
/// Gets or sets the Matrix homeserver FQDN.
/// </summary>
public string Server { get; set; }
/// <summary>
/// Gets or sets the login session as a MatrixLoginResponse object.
/// </summary>
public MatrixLoginResponse LoginSession { get; set; }
/// <summary>
/// Logs the user in with specified server and credentials.
/// </summary>
/// <param name="server">Required</param>
/// <param name="user">Required</param>
/// <param name="pass">Required</param>
/// <param name="device">Optional</param>
/// <returns></returns>
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<MatrixLoginResponse>(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;
}
/// <summary>
/// Gets the logged-in user's currently-joined rooms.
/// </summary>
/// <returns></returns>
public MatrixUserRooms GetRooms()
{
string responseJson = ApiResult(MatrixApi.User.Rooms, httpAction.GET);
return (MatrixUserRooms)JsonConvert.DeserializeObject<MatrixUserRooms>(responseJson);
}
/// <summary>
/// Gets the aliases for the given (string)roomId
/// </summary>
/// <param name="roomId">Full string ID (not alias) of a room</param>
/// <returns></returns>
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<MatrixRoomAliases>(responseJson);
}
/// <summary>
/// Joins the user to the given room by (string)roomId
/// </summary>
/// <param name="roomId">Full string ID (not alias) of a room</param>
/// <returns></returns>
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<MatrixRoom>(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<MatrixRoomDirectory>(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<MatrixError>(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<MatrixError>(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<MatrixLoginResponse>(responseString);
// return response;
//}