|
|
|
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;
|
|
|
|
//}
|