2016-02-15 15:30:06 +00:00
|
|
|
|
/*
|
|
|
|
|
* This file is part of matrix-glib-sdk
|
|
|
|
|
*
|
|
|
|
|
* matrix-glib-sdk is free software: you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation, either
|
|
|
|
|
* version 3 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* matrix-glib-sdk is distributed in the hope that it will be
|
|
|
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
|
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
* See the GNU Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with matrix-glib-sdk. If not, see
|
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An event-driven client class to communicate with HTTP based
|
|
|
|
|
* Matrix.org servers.
|
|
|
|
|
*/
|
|
|
|
|
public class Matrix.HTTPClient : Matrix.HTTPAPI, Matrix.Client {
|
|
|
|
|
private bool _polling = false;
|
|
|
|
|
private ulong _event_timeout = 30000;
|
2016-02-15 15:30:06 +00:00
|
|
|
|
private string? _last_sync_token;
|
2017-11-02 06:33:14 +00:00
|
|
|
|
private HashTable<string, Profile> _user_global_profiles =
|
|
|
|
|
new HashTable<string, Profile>(str_hash, str_equal);
|
|
|
|
|
private HashTable<string, Presence> _user_global_presence =
|
|
|
|
|
new HashTable<string, Presence>(str_hash, str_equal);
|
|
|
|
|
private HashTable<string, Room> _rooms =
|
|
|
|
|
new HashTable<string, Room>(str_hash, str_equal);
|
2016-03-19 07:12:13 +00:00
|
|
|
|
private ulong _last_txn_id = 0;
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
|
|
|
|
public
|
|
|
|
|
HTTPClient(string base_url)
|
|
|
|
|
{
|
|
|
|
|
Object(base_url : base_url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
login_with_password(string username, string password)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
var builder = new Json.Builder();
|
|
|
|
|
|
|
|
|
|
builder.begin_object();
|
|
|
|
|
|
|
|
|
|
builder.set_member_name("user");
|
|
|
|
|
builder.add_string_value(username);
|
|
|
|
|
|
|
|
|
|
builder.set_member_name("password");
|
|
|
|
|
builder.add_string_value(password);
|
|
|
|
|
|
|
|
|
|
builder.end_object();
|
|
|
|
|
|
|
|
|
|
login((i, content_type, json_content, raw_content, error) => {
|
|
|
|
|
login_finished((error == null) || (error is Matrix.Error.NONE));
|
2016-02-15 15:30:06 +00:00
|
|
|
|
},
|
|
|
|
|
"m.login.password",
|
2016-02-15 15:30:06 +00:00
|
|
|
|
builder.get_root());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
register_with_password(string? username, string password)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
register_account(
|
|
|
|
|
(i, content_type, json_content, raw_content, error) => {
|
|
|
|
|
login_finished((error is Matrix.Error.NONE));
|
|
|
|
|
},
|
|
|
|
|
Matrix.AccountKind.USER,
|
|
|
|
|
false, username, password);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-28 05:59:14 +00:00
|
|
|
|
public new void
|
2016-02-15 15:30:06 +00:00
|
|
|
|
logout()
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
2017-10-28 05:59:14 +00:00
|
|
|
|
((Matrix.API)this).logout(
|
|
|
|
|
() => {
|
|
|
|
|
token = null;
|
|
|
|
|
refresh_token = null;
|
|
|
|
|
abort_pending();
|
|
|
|
|
});
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 18:57:50 +00:00
|
|
|
|
private void
|
2016-02-15 15:30:06 +00:00
|
|
|
|
process_event(Json.Node event_node, string? room_id)
|
2016-01-27 18:57:50 +00:00
|
|
|
|
{
|
2016-02-15 15:30:06 +00:00
|
|
|
|
Json.Object root_obj;
|
|
|
|
|
Json.Node node;
|
|
|
|
|
string event_type;
|
2016-03-07 08:44:06 +00:00
|
|
|
|
Matrix.Event.Base? evt = null;
|
2016-01-27 18:57:50 +00:00
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
if (event_node.get_node_type() != Json.NodeType.OBJECT) {
|
2016-03-05 07:02:39 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
warning("Received event that is not an object.");
|
|
|
|
|
}
|
2016-01-27 18:57:50 +00:00
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
return;
|
2016-01-27 18:57:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
root_obj = event_node.get_object();
|
2016-01-27 18:57:50 +00:00
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
if ((node = root_obj.get_member("type")) == null) {
|
2016-03-05 07:02:39 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
warning("Received event without type.");
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
|
|
|
|
return;
|
2016-01-27 18:57:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
event_type = node.get_string();
|
|
|
|
|
|
2016-01-27 18:57:50 +00:00
|
|
|
|
try {
|
2016-03-07 08:44:06 +00:00
|
|
|
|
evt = Matrix.Event.Base.new_from_json(event_type, event_node);
|
2016-02-15 15:30:06 +00:00
|
|
|
|
} catch (GLib.Error e) {
|
2016-03-07 08:44:06 +00:00
|
|
|
|
evt = null;
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 23:04:39 +00:00
|
|
|
|
if (evt != null) {
|
2016-03-16 16:13:05 +00:00
|
|
|
|
// Make sure Room events have room_id set, even if it was
|
|
|
|
|
// stripped by the HS
|
2016-03-18 10:59:32 +00:00
|
|
|
|
if (evt is Matrix.Event.Room) {
|
2016-03-16 16:13:05 +00:00
|
|
|
|
Matrix.Event.Room revt = (Matrix.Event.Room)evt;
|
|
|
|
|
|
|
|
|
|
if (revt.room_id == null) {
|
|
|
|
|
revt.room_id = room_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-14 23:04:39 +00:00
|
|
|
|
|
2016-03-18 10:59:32 +00:00
|
|
|
|
if (evt is Matrix.Event.Presence) {
|
2016-03-14 23:04:39 +00:00
|
|
|
|
var pevt = (Matrix.Event.Presence)evt;
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var user_id = pevt.user_id;
|
2016-03-14 23:04:39 +00:00
|
|
|
|
|
|
|
|
|
_user_global_presence[user_id] = pevt.presence;
|
|
|
|
|
|
|
|
|
|
Profile? profile = _user_global_profiles[user_id];
|
|
|
|
|
|
|
|
|
|
if (profile == null) {
|
|
|
|
|
profile = new Profile();
|
|
|
|
|
_user_global_profiles[user_id] = profile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
profile.avatar_url = pevt.avatar_url;
|
|
|
|
|
profile.display_name = pevt.display_name;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.Room) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var revt = (Matrix.Event.Room)evt;
|
|
|
|
|
var room = _get_or_create_room(revt.room_id);
|
|
|
|
|
|
2016-03-18 10:59:32 +00:00
|
|
|
|
if (evt is Matrix.Event.RoomMember) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var mevt = (Matrix.Event.RoomMember)evt;
|
|
|
|
|
var user_id = mevt.user_id;
|
|
|
|
|
Profile? profile = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
profile = room.get_or_add_member(
|
|
|
|
|
user_id,
|
|
|
|
|
(mevt.tpi_display_name != null));
|
|
|
|
|
} catch (Matrix.Error e) {}
|
|
|
|
|
|
|
|
|
|
profile.avatar_url = mevt.avatar_url;
|
|
|
|
|
profile.display_name = mevt.display_name;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomAliases) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var aevt = (Matrix.Event.RoomAliases)evt;
|
|
|
|
|
|
|
|
|
|
room.aliases = aevt.aliases;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomAvatar) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var aevt = (Matrix.Event.RoomAvatar)evt;
|
|
|
|
|
|
|
|
|
|
room.avatar_url = aevt.url;
|
|
|
|
|
room.avatar_info = aevt.info;
|
|
|
|
|
room.avatar_thumbnail_url = aevt.thumbnail_url;
|
|
|
|
|
room.avatar_thumbnail_info = aevt.thumbnail_info;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomCanonicalAlias) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var cevt = (Matrix.Event.RoomCanonicalAlias)evt;
|
|
|
|
|
|
|
|
|
|
room.canonical_alias = cevt.canonical_alias;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomCreate) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var cevt = (Matrix.Event.RoomCreate)evt;
|
|
|
|
|
|
|
|
|
|
room.creator = cevt.creator;
|
|
|
|
|
room.federate = cevt.federate;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomGuestAccess) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var gevt = (Matrix.Event.RoomGuestAccess)evt;
|
|
|
|
|
|
|
|
|
|
room.guest_access = gevt.guest_access;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomHistoryVisibility) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var hevt = (Matrix.Event.RoomHistoryVisibility)evt;
|
|
|
|
|
|
|
|
|
|
room.history_visibility = hevt.visibility;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomJoinRules) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var jevt = (Matrix.Event.RoomJoinRules)evt;
|
|
|
|
|
|
|
|
|
|
room.join_rules = jevt.join_rules;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomName) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var nevt = (Matrix.Event.RoomName)evt;
|
|
|
|
|
|
|
|
|
|
room.name = nevt.name;
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomPowerLevels) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var levt = (Matrix.Event.RoomPowerLevels)evt;
|
|
|
|
|
|
|
|
|
|
room.default_power_level = levt.users_default;
|
|
|
|
|
room.default_event_level = levt.events_default;
|
|
|
|
|
room.default_state_level = levt.state_default;
|
|
|
|
|
room.ban_level = levt.ban;
|
|
|
|
|
room.kick_level = levt.kick;
|
|
|
|
|
room.redact_level = levt.redact;
|
|
|
|
|
room.invite_level = levt.invite;
|
2016-03-18 15:58:49 +00:00
|
|
|
|
room.clear_user_levels();
|
|
|
|
|
room.clear_event_levels();
|
|
|
|
|
|
2017-11-02 06:33:14 +00:00
|
|
|
|
levt.user_levels.foreach(
|
|
|
|
|
(key, value) => {
|
|
|
|
|
room.set_user_level(key, value);
|
|
|
|
|
});
|
2016-03-18 15:58:49 +00:00
|
|
|
|
|
2017-11-02 06:33:14 +00:00
|
|
|
|
levt.event_levels.foreach(
|
|
|
|
|
(key, value) => {
|
|
|
|
|
room.set_event_level(key, value);
|
|
|
|
|
});
|
2016-03-18 10:59:32 +00:00
|
|
|
|
} else if (evt is Matrix.Event.RoomTopic) {
|
2016-03-17 10:04:48 +00:00
|
|
|
|
var tevt = (Matrix.Event.RoomTopic)evt;
|
|
|
|
|
|
|
|
|
|
room.topic = tevt.topic;
|
2016-03-14 23:04:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
incoming_event(room_id, event_node, evt);
|
2016-01-27 18:57:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-17 10:04:48 +00:00
|
|
|
|
private Room
|
|
|
|
|
_get_or_create_room(string room_id)
|
|
|
|
|
{
|
|
|
|
|
Room? room = null;
|
|
|
|
|
|
|
|
|
|
if ((room = _rooms[room_id]) == null) {
|
|
|
|
|
room = new Room(room_id);
|
|
|
|
|
_rooms[room_id] = room;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return room;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
private void
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_process_event_list_obj(Json.Node node, string? room_id)
|
|
|
|
|
requires(node.get_node_type() == Json.NodeType.OBJECT)
|
2016-02-15 15:30:06 +00:00
|
|
|
|
{
|
2016-02-15 15:30:06 +00:00
|
|
|
|
Json.Object node_obj;
|
|
|
|
|
Json.Node events_node;
|
|
|
|
|
|
|
|
|
|
node_obj = node.get_object();
|
|
|
|
|
|
|
|
|
|
if ((events_node = node_obj.get_member("events")) != null) {
|
|
|
|
|
if (events_node.get_node_type() == Json.NodeType.ARRAY) {
|
|
|
|
|
events_node.get_array().foreach_element(
|
|
|
|
|
(array, idx, event_node) => {
|
|
|
|
|
process_event(event_node, room_id);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
private void
|
|
|
|
|
cb_sync(string content_type,
|
|
|
|
|
Json.Node? json_content,
|
|
|
|
|
ByteArray? raw_content,
|
|
|
|
|
Matrix.Error? error)
|
|
|
|
|
{
|
2016-02-15 15:30:06 +00:00
|
|
|
|
if (error == null) {
|
|
|
|
|
var root_obj = json_content.get_object();
|
|
|
|
|
Json.Node? node;
|
|
|
|
|
|
2016-03-05 07:02:39 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Processing account data");
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_process_event_list_obj(root_obj.get_member("account_data"),
|
|
|
|
|
null);
|
|
|
|
|
|
2016-03-05 07:02:39 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Processing presence");
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_process_event_list_obj(root_obj.get_member("presence"),
|
|
|
|
|
null);
|
|
|
|
|
|
|
|
|
|
if ((node = root_obj.get_member("rooms")) != null) {
|
|
|
|
|
if (node.get_node_type() == Json.NodeType.OBJECT) {
|
|
|
|
|
Json.Object rooms_object = node.get_object();
|
|
|
|
|
Json.Node rooms_node;
|
|
|
|
|
|
2016-03-05 07:02:39 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Processing rooms");
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
|
|
|
|
if ((rooms_node = rooms_object.get_member(
|
|
|
|
|
"invite")) != null) {
|
|
|
|
|
rooms_node.get_object().foreach_member(
|
|
|
|
|
(obj, room_id, room_node) => {
|
|
|
|
|
if (room_node.get_node_type() != Json.NodeType.OBJECT) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("invite_state"),
|
|
|
|
|
room_id);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((rooms_node = rooms_object.get_member(
|
|
|
|
|
"join")) != null) {
|
|
|
|
|
rooms_node.get_object().foreach_member(
|
|
|
|
|
(obj, room_id, room_node) => {
|
|
|
|
|
if (room_node.get_node_type() != Json.NodeType.OBJECT) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("timeline"),
|
|
|
|
|
room_id);
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("state"),
|
|
|
|
|
room_id);
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("account_data"),
|
|
|
|
|
room_id);
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("ephemeral"),
|
|
|
|
|
room_id);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((rooms_node = rooms_object.get_member(
|
|
|
|
|
"leave")) != null) {
|
|
|
|
|
rooms_node.get_object().foreach_member(
|
|
|
|
|
(obj, room_id, room_node) => {
|
|
|
|
|
if (room_node.get_node_type() != Json.NodeType.OBJECT) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("timeline"),
|
|
|
|
|
room_id);
|
|
|
|
|
_process_event_list_obj(
|
|
|
|
|
room_node
|
|
|
|
|
.get_object()
|
|
|
|
|
.get_member("state"),
|
|
|
|
|
room_id);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
if ((node = root_obj.get_member("next_batch")) != null) {
|
|
|
|
|
_last_sync_token = node.get_string();
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
2016-03-21 10:45:27 +00:00
|
|
|
|
} else if ((error is Matrix.Error.M_FORBIDDEN)
|
|
|
|
|
|| (error is Matrix.Error.M_UNKNOWN_TOKEN)
|
|
|
|
|
|| (error is Matrix.Error.M_UNAUTHORIZED)) {
|
|
|
|
|
try {
|
2016-03-21 14:48:31 +00:00
|
|
|
|
token = null;
|
|
|
|
|
|
2016-03-21 10:45:27 +00:00
|
|
|
|
token_refresh((i, ct, jc, rc, err) => {
|
|
|
|
|
login_finished((error == null)
|
|
|
|
|
|| (error is Matrix.Error.NONE));
|
2016-03-21 14:48:31 +00:00
|
|
|
|
|
|
|
|
|
if (token == null) {
|
|
|
|
|
refresh_token = null;
|
|
|
|
|
polling_stopped(err);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
stop_polling(false);
|
|
|
|
|
} catch (GLib.Error e) {}
|
|
|
|
|
}
|
2016-03-21 10:45:27 +00:00
|
|
|
|
} , null);
|
|
|
|
|
} catch (Matrix.Error e) {}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
// It is possible that polling has been disabled while we were
|
|
|
|
|
// processing events. Don’t continue polling if that is the
|
|
|
|
|
// case.
|
|
|
|
|
if (_polling) {
|
|
|
|
|
// This `500` should be changed to M_MISSING_TOKEN somehow
|
2016-02-15 15:30:06 +00:00
|
|
|
|
try {
|
2016-02-15 15:30:06 +00:00
|
|
|
|
if ((error == null) || (error.code < 500)) {
|
|
|
|
|
begin_polling();
|
|
|
|
|
} else if ((error != null) && error.code >= 500) {
|
2016-03-21 14:48:31 +00:00
|
|
|
|
polling_stopped(error);
|
2016-02-15 15:30:06 +00:00
|
|
|
|
stop_polling(false);
|
|
|
|
|
}
|
2016-01-27 18:57:50 +00:00
|
|
|
|
} catch (Matrix.Error e) {}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
begin_polling()
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
try {
|
2016-02-15 15:30:06 +00:00
|
|
|
|
sync((API.Callback)cb_sync,
|
|
|
|
|
null, null, _last_sync_token,
|
|
|
|
|
false, false, _event_timeout);
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_polling = true;
|
|
|
|
|
} catch (Matrix.Error e) {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
|
2016-03-21 14:48:31 +00:00
|
|
|
|
if (_polling == false) {
|
|
|
|
|
polling_started();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:30:06 +00:00
|
|
|
|
_polling = true;
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
stop_polling(bool cancel_ongoing)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
_polling = false;
|
|
|
|
|
|
|
|
|
|
if (cancel_ongoing) {
|
|
|
|
|
abort_pending();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-14 23:04:39 +00:00
|
|
|
|
|
|
|
|
|
public Profile?
|
|
|
|
|
get_user_profile(string user_id, string? room_id = null)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
if (room_id == null) {
|
|
|
|
|
var profile = _user_global_profiles[user_id];
|
|
|
|
|
|
|
|
|
|
if (profile == null) {
|
|
|
|
|
throw new Matrix.Error.UNAVAILABLE(
|
|
|
|
|
"Global profile for %s is not cached yet.",
|
|
|
|
|
user_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return profile;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-18 10:59:57 +00:00
|
|
|
|
var room = _rooms[room_id];
|
|
|
|
|
|
|
|
|
|
if (room == null) {
|
|
|
|
|
throw new Matrix.Error.UNAVAILABLE(
|
|
|
|
|
"Room data for %s is not cached yet.", room_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return room.get_member(user_id, null);
|
2016-03-14 23:04:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-17 16:03:03 +00:00
|
|
|
|
public Presence
|
2016-03-14 23:04:39 +00:00
|
|
|
|
get_user_presence(string user_id, string? room_id = null)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
if (room_id == null) {
|
|
|
|
|
Presence? presence = _user_global_presence[user_id];
|
|
|
|
|
|
|
|
|
|
if (presence == null) {
|
|
|
|
|
throw new Matrix.Error.UNAVAILABLE(
|
|
|
|
|
"Global presence for %s is not cached yet.",
|
|
|
|
|
user_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return presence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Matrix.Error.UNSUPPORTED(
|
|
|
|
|
"Per-room presences are not supported yet.");
|
|
|
|
|
}
|
2016-03-16 16:38:50 +00:00
|
|
|
|
|
|
|
|
|
public Room
|
|
|
|
|
get_room_by_id(string room_id)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
Room? room;
|
|
|
|
|
|
|
|
|
|
if ((room = _rooms[room_id]) == null) {
|
|
|
|
|
throw new Matrix.Error.UNAVAILABLE(
|
|
|
|
|
"Room data for %s is not cached yet.", room_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return room;
|
|
|
|
|
}
|
2016-03-18 10:17:04 +00:00
|
|
|
|
|
|
|
|
|
public Room
|
|
|
|
|
get_room_by_alias(string room_alias)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
2017-11-02 06:33:14 +00:00
|
|
|
|
Room? room_found = _rooms.find(
|
|
|
|
|
(key, room) => {
|
|
|
|
|
if (room.canonical_alias == room_alias) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-03-18 10:17:04 +00:00
|
|
|
|
|
2017-11-02 06:33:14 +00:00
|
|
|
|
if (room_alias in room.aliases) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-03-18 10:17:04 +00:00
|
|
|
|
|
2017-11-02 06:33:14 +00:00
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (room_found != null) {
|
|
|
|
|
return room_found;
|
2016-03-18 10:17:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Matrix.Error.UNAVAILABLE(
|
|
|
|
|
"Noo room data found for alias %s", room_alias);
|
|
|
|
|
}
|
2016-03-19 07:12:13 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the next transaction ID to use. It increments the
|
|
|
|
|
* internally stored value and returns that, so it is guaranteed
|
|
|
|
|
* to be unique until we run out of ulong boundaries.
|
|
|
|
|
*
|
|
|
|
|
* It is called internally by send().
|
|
|
|
|
*/
|
|
|
|
|
public ulong
|
|
|
|
|
next_txn_id()
|
|
|
|
|
{
|
|
|
|
|
return ++_last_txn_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void
|
|
|
|
|
send_callback(Json.Node? json_content,
|
|
|
|
|
GLib.Error? err,
|
|
|
|
|
Matrix.Client.SendCallback? cb)
|
|
|
|
|
{
|
|
|
|
|
string? event_id = null;
|
|
|
|
|
GLib.Error? new_err = err;
|
|
|
|
|
|
|
|
|
|
// If there is no callback, there is no point to continue
|
|
|
|
|
if (cb == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err == null) {
|
|
|
|
|
var root = json_content.get_object();
|
|
|
|
|
|
|
|
|
|
if (root.has_member("event_id")) {
|
|
|
|
|
event_id = root.get_string_member("event_id");
|
|
|
|
|
} else {
|
|
|
|
|
new_err = new Matrix.Error.BAD_RESPONSE(
|
|
|
|
|
"event_id is missing from an event response");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cb(event_id, new_err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
send(string room_id,
|
|
|
|
|
Matrix.Event.Base evt,
|
|
|
|
|
Matrix.Client.SendCallback? cb,
|
|
|
|
|
out ulong txn_id)
|
|
|
|
|
throws Matrix.Error
|
|
|
|
|
{
|
|
|
|
|
var evt_node = evt.json;
|
|
|
|
|
var evt_root = evt_node.get_object();
|
|
|
|
|
string? state_key = null;
|
|
|
|
|
|
|
|
|
|
if (evt_root.has_member("state_key")) {
|
|
|
|
|
state_key = evt_root.get_string_member("state_key");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state_key != null) {
|
|
|
|
|
txn_id = 0;
|
|
|
|
|
send_state_event(
|
|
|
|
|
(i, ct, json_node, rc, err) =>
|
|
|
|
|
send_callback(json_node, err, cb),
|
|
|
|
|
room_id,
|
|
|
|
|
evt.event_type,
|
|
|
|
|
(state_key == "") ? null : state_key,
|
|
|
|
|
evt_root.get_member("content"));
|
|
|
|
|
} else {
|
|
|
|
|
txn_id = next_txn_id();
|
|
|
|
|
send_event(
|
|
|
|
|
(i, ct, json_node, rc, err) =>
|
|
|
|
|
send_callback(json_node, err, cb),
|
|
|
|
|
room_id,
|
|
|
|
|
evt.event_type,
|
|
|
|
|
"%lu".printf(txn_id),
|
|
|
|
|
evt_root.get_member("content"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-20 12:36:57 +00:00
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
save_state(string filename)
|
|
|
|
|
throws Matrix.Error, GLib.Error
|
|
|
|
|
{
|
|
|
|
|
var root = new Json.Object();
|
|
|
|
|
|
|
|
|
|
root.set_string_member("base_url", base_url);
|
|
|
|
|
|
|
|
|
|
root.set_boolean_member("validate_certificate", validate_certificate);
|
|
|
|
|
|
|
|
|
|
if (user_id != null) {
|
|
|
|
|
root.set_string_member("user_id", user_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (homeserver != null) {
|
|
|
|
|
root.set_string_member("homeserver_name", homeserver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (token != null) {
|
|
|
|
|
root.set_string_member("access_token", token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (refresh_token != null) {
|
|
|
|
|
root.set_string_member("refresh_token", refresh_token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var node = new Json.Node(Json.NodeType.OBJECT);
|
|
|
|
|
node.set_object(root);
|
|
|
|
|
|
2016-03-20 18:46:59 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Saving state to %s\n", filename);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 12:36:57 +00:00
|
|
|
|
var generator = new Json.Generator();
|
|
|
|
|
generator.set_root(node);
|
|
|
|
|
generator.to_file(filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void
|
|
|
|
|
load_state(string filename)
|
|
|
|
|
throws Matrix.Error, GLib.Error
|
|
|
|
|
{
|
|
|
|
|
var parser = new Json.Parser();
|
|
|
|
|
|
2016-03-20 18:46:59 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loading state from %s\n", filename);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 12:36:57 +00:00
|
|
|
|
parser.load_from_file(filename);
|
|
|
|
|
Json.Node? node = parser.get_root();
|
|
|
|
|
|
|
|
|
|
if (node.get_node_type() != Json.NodeType.OBJECT) {
|
|
|
|
|
throw new Matrix.Error.INVALID_FORMAT(
|
|
|
|
|
"Save data must be a JSON object.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var root = node.get_object();
|
|
|
|
|
|
|
|
|
|
if ((node = root.get_member("base_url")) == null) {
|
|
|
|
|
throw new Matrix.Error.INVALID_FORMAT(
|
|
|
|
|
"Save data has no base_url key");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base_url = node.get_string();
|
|
|
|
|
|
2016-03-20 18:46:59 +00:00
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loaded base URL %s", base_url);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 12:36:57 +00:00
|
|
|
|
if ((node = root.get_member("validate_certificate")) == null) {
|
|
|
|
|
throw new Matrix.Error.INVALID_FORMAT(
|
|
|
|
|
"Save data has no validate_certificate key");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_certificate = node.get_boolean();
|
|
|
|
|
|
|
|
|
|
if ((node = root.get_member("user_id")) != null) {
|
|
|
|
|
_user_id = node.get_string();
|
2016-03-20 18:46:59 +00:00
|
|
|
|
|
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loaded user ID %s", user_id);
|
|
|
|
|
}
|
2016-03-20 12:36:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((node = root.get_member("homeserver_name")) != null) {
|
|
|
|
|
_homeserver = node.get_string();
|
2016-03-20 18:46:59 +00:00
|
|
|
|
|
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loaded homeserver name %s", homeserver);
|
|
|
|
|
}
|
2016-03-20 12:36:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((node = root.get_member("access_token")) != null) {
|
|
|
|
|
token = node.get_string();
|
2016-03-20 18:46:59 +00:00
|
|
|
|
|
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loaded access token %s", token);
|
|
|
|
|
}
|
2016-03-20 12:36:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((node = root.get_member("refresh_token")) != null) {
|
|
|
|
|
refresh_token = node.get_string();
|
2016-03-20 18:46:59 +00:00
|
|
|
|
|
|
|
|
|
if (Config.DEBUG) {
|
|
|
|
|
debug("Loaded refresh token %s", refresh_token);
|
|
|
|
|
}
|
2016-03-20 12:36:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-15 15:30:06 +00:00
|
|
|
|
}
|