2014-08-12 20:32:18 -06:00
|
|
|
/*
|
2014-09-03 10:29:13 -06:00
|
|
|
Copyright 2014 OpenMarket Ltd
|
2014-08-12 20:32:18 -06:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
'use strict';
|
|
|
|
|
2014-08-15 07:06:48 -06:00
|
|
|
/*
|
|
|
|
This service wraps up Matrix API calls.
|
|
|
|
|
|
|
|
This serves to isolate the caller from changes to the underlying url paths, as
|
|
|
|
well as attach common params (e.g. access_token) to requests.
|
|
|
|
*/
|
2014-08-12 08:10:52 -06:00
|
|
|
angular.module('matrixService', [])
|
2014-08-14 06:59:33 -06:00
|
|
|
.factory('matrixService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {
|
2014-08-12 08:10:52 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Permanent storage of user information
|
|
|
|
* The config contains:
|
|
|
|
* - homeserver url
|
|
|
|
* - Identity server url
|
|
|
|
* - user_id
|
|
|
|
* - access_token
|
|
|
|
* - version: the version of this cache
|
|
|
|
*/
|
|
|
|
var config;
|
|
|
|
|
2014-09-06 01:31:57 -06:00
|
|
|
var roomIdToAlias = {};
|
|
|
|
var aliasToRoomId = {};
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Current version of permanent storage
|
|
|
|
var configVersion = 0;
|
2014-08-31 07:51:37 -06:00
|
|
|
var prefixPath = "/_matrix/client/api/v1";
|
2014-08-12 08:10:52 -06:00
|
|
|
var MAPPING_PREFIX = "alias_for_";
|
|
|
|
|
2014-08-29 03:31:03 -06:00
|
|
|
var doRequest = function(method, path, params, data, $httpParams) {
|
2014-08-15 07:06:48 -06:00
|
|
|
if (!config) {
|
|
|
|
console.warn("No config exists. Cannot perform request to "+path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Inject the access token
|
|
|
|
if (!params) {
|
|
|
|
params = {};
|
|
|
|
}
|
2014-08-15 07:06:48 -06:00
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
params.access_token = config.access_token;
|
|
|
|
|
2014-08-18 08:50:55 -06:00
|
|
|
if (path.indexOf(prefixPath) !== 0) {
|
|
|
|
path = prefixPath + path;
|
|
|
|
}
|
|
|
|
|
2014-08-29 03:31:03 -06:00
|
|
|
return doBaseRequest(config.homeserver, method, path, params, data, undefined, $httpParams);
|
2014-08-12 08:10:52 -06:00
|
|
|
};
|
|
|
|
|
2014-08-21 05:36:14 -06:00
|
|
|
var doBaseRequest = function(baseUrl, method, path, params, data, headers, $httpParams) {
|
|
|
|
|
|
|
|
var request = {
|
2014-08-12 08:10:52 -06:00
|
|
|
method: method,
|
|
|
|
url: baseUrl + path,
|
|
|
|
params: params,
|
|
|
|
data: data,
|
|
|
|
headers: headers
|
2014-08-21 05:36:14 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Add additional $http parameters
|
|
|
|
if ($httpParams) {
|
|
|
|
angular.extend(request, $httpParams);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $http(request);
|
2014-08-12 08:10:52 -06:00
|
|
|
};
|
2014-09-15 07:31:53 -06:00
|
|
|
|
|
|
|
var doRegisterLogin = function(path, loginType, sessionId, userName, password, threepidCreds) {
|
|
|
|
var data = {};
|
|
|
|
if (loginType === "m.login.recaptcha") {
|
|
|
|
var challengeToken = Recaptcha.get_challenge();
|
|
|
|
var captchaEntry = Recaptcha.get_response();
|
|
|
|
data = {
|
|
|
|
type: "m.login.recaptcha",
|
|
|
|
challenge: challengeToken,
|
|
|
|
response: captchaEntry
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else if (loginType === "m.login.email.identity") {
|
|
|
|
data = {
|
|
|
|
threepidCreds: threepidCreds
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else if (loginType === "m.login.password") {
|
|
|
|
data = {
|
2014-09-15 08:38:29 -06:00
|
|
|
user: userName,
|
2014-09-15 07:31:53 -06:00
|
|
|
password: password
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sessionId) {
|
|
|
|
data.session = sessionId;
|
|
|
|
}
|
|
|
|
data.type = loginType;
|
|
|
|
console.log("doRegisterLogin >>> " + loginType);
|
|
|
|
return doRequest("POST", path, undefined, data);
|
|
|
|
};
|
2014-08-12 08:10:52 -06:00
|
|
|
|
|
|
|
return {
|
|
|
|
/****** Home server API ******/
|
|
|
|
prefix: prefixPath,
|
|
|
|
|
|
|
|
// Register an user
|
2014-09-15 07:31:53 -06:00
|
|
|
register: function(user_name, password, threepidCreds, useCaptcha) {
|
|
|
|
// registration is composed of multiple requests, to check you can
|
|
|
|
// register, then to actually register. This deferred will fire when
|
|
|
|
// all the requests are done, along with the final response.
|
|
|
|
var deferred = $q.defer();
|
2014-08-12 08:10:52 -06:00
|
|
|
var path = "/register";
|
2014-09-05 14:56:36 -06:00
|
|
|
|
2014-09-15 07:31:53 -06:00
|
|
|
// check we can actually register with this HS.
|
|
|
|
doRequest("GET", path, undefined, undefined).then(
|
|
|
|
function(response) {
|
|
|
|
console.log("/register [1] : "+JSON.stringify(response));
|
|
|
|
var flows = response.data.flows;
|
|
|
|
var knownTypes = [
|
|
|
|
"m.login.password",
|
|
|
|
"m.login.recaptcha",
|
|
|
|
"m.login.email.identity"
|
|
|
|
];
|
|
|
|
// if they entered 3pid creds, we want to use a flow which uses it.
|
|
|
|
var useThreePidFlow = threepidCreds != undefined;
|
|
|
|
var flowIndex = 0;
|
|
|
|
var firstRegType = undefined;
|
|
|
|
|
|
|
|
for (var i=0; i<flows.length; i++) {
|
|
|
|
var isThreePidFlow = false;
|
|
|
|
if (flows[i].stages) {
|
|
|
|
for (var j=0; j<flows[i].stages.length; j++) {
|
|
|
|
var regType = flows[i].stages[j];
|
|
|
|
if (knownTypes.indexOf(regType) === -1) {
|
|
|
|
deferred.reject("Unknown type: "+regType);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (regType == "m.login.email.identity") {
|
|
|
|
isThreePidFlow = true;
|
|
|
|
}
|
|
|
|
if (!useCaptcha && regType == "m.login.recaptcha") {
|
|
|
|
console.error("Web client setup to not use captcha, but HS demands a captcha.");
|
2014-09-15 07:52:39 -06:00
|
|
|
deferred.reject({
|
|
|
|
data: {
|
|
|
|
errcode: "M_CAPTCHA_NEEDED",
|
|
|
|
error: "Home server requires a captcha."
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return;
|
2014-09-15 07:31:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (isThreePidFlow && useThreePidFlow) || (!isThreePidFlow && !useThreePidFlow) ) {
|
|
|
|
flowIndex = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (knownTypes.indexOf(flows[i].type) == -1) {
|
|
|
|
deferred.reject("Unknown type: "+flows[i].type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// looks like we can register fine, go ahead and do it.
|
|
|
|
console.log("Using flow " + JSON.stringify(flows[flowIndex]));
|
|
|
|
firstRegType = flows[flowIndex].type;
|
|
|
|
var sessionId = undefined;
|
|
|
|
|
|
|
|
// generic response processor so it can loop as many times as required
|
|
|
|
var loginResponseFunc = function(response) {
|
|
|
|
if (response.data.session) {
|
|
|
|
sessionId = response.data.session;
|
|
|
|
}
|
|
|
|
console.log("login response: " + JSON.stringify(response.data));
|
|
|
|
if (response.data.access_token) {
|
|
|
|
deferred.resolve(response);
|
|
|
|
}
|
|
|
|
else if (response.data.next) {
|
2014-09-15 07:52:39 -06:00
|
|
|
var nextType = response.data.next;
|
|
|
|
if (response.data.next instanceof Array) {
|
|
|
|
for (var i=0; i<response.data.next.length; i++) {
|
|
|
|
if (useThreePidFlow && response.data.next[i] == "m.login.email.identity") {
|
|
|
|
nextType = response.data.next[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (!useThreePidFlow && response.data.next[i] != "m.login.email.identity") {
|
|
|
|
nextType = response.data.next[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return doRegisterLogin(path, nextType, sessionId, user_name, password, threepidCreds).then(
|
2014-09-15 07:31:53 -06:00
|
|
|
loginResponseFunc,
|
|
|
|
function(err) {
|
|
|
|
deferred.reject(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
deferred.reject("Unknown continuation: "+JSON.stringify(response));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// set the ball rolling
|
|
|
|
doRegisterLogin(path, firstRegType, undefined, user_name, password, threepidCreds).then(
|
|
|
|
loginResponseFunc,
|
|
|
|
function(err) {
|
|
|
|
deferred.reject(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
},
|
|
|
|
function(err) {
|
|
|
|
deferred.reject(err);
|
|
|
|
}
|
|
|
|
);
|
2014-09-05 14:56:36 -06:00
|
|
|
|
2014-09-15 07:31:53 -06:00
|
|
|
return deferred.promise;
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Create a room
|
2014-08-30 17:40:42 -06:00
|
|
|
create: function(room_alias, visibility) {
|
2014-08-12 08:10:52 -06:00
|
|
|
// The REST path spec
|
2014-08-27 04:33:56 -06:00
|
|
|
var path = "/createRoom";
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-30 17:40:42 -06:00
|
|
|
var req = {
|
|
|
|
"visibility": visibility
|
|
|
|
};
|
|
|
|
if (room_alias) {
|
|
|
|
req.room_alias_name = room_alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
return doRequest("POST", path, undefined, req);
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
2014-09-08 10:21:41 -06:00
|
|
|
// Get the user's current state: his presence, the list of his rooms with
|
|
|
|
// the last {limit} events
|
|
|
|
initialSync: function(limit, feedback) {
|
2014-08-12 08:10:52 -06:00
|
|
|
// The REST path spec
|
2014-08-27 02:24:01 -06:00
|
|
|
|
2014-08-26 09:19:17 -06:00
|
|
|
var path = "/initialSync";
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-27 02:24:01 -06:00
|
|
|
var params = {};
|
|
|
|
if (limit) {
|
|
|
|
params.limit = limit;
|
|
|
|
}
|
|
|
|
if (feedback) {
|
|
|
|
params.feedback = feedback;
|
|
|
|
}
|
|
|
|
|
|
|
|
return doRequest("GET", path, params);
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
2014-09-23 11:50:39 -06:00
|
|
|
|
|
|
|
// get room state for a specific room
|
|
|
|
roomState: function(room_id) {
|
|
|
|
var path = "/rooms/" + room_id + "/state";
|
|
|
|
return doRequest("GET", path);
|
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Joins a room
|
|
|
|
join: function(room_id) {
|
2014-08-26 07:59:31 -06:00
|
|
|
return this.membershipChange(room_id, undefined, "join");
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
2014-08-12 10:17:10 -06:00
|
|
|
joinAlias: function(room_alias) {
|
|
|
|
var path = "/join/$room_alias";
|
|
|
|
room_alias = encodeURIComponent(room_alias);
|
|
|
|
|
|
|
|
path = path.replace("$room_alias", room_alias);
|
|
|
|
|
2014-08-27 06:59:14 -06:00
|
|
|
// TODO: PUT with txn ID
|
|
|
|
return doRequest("POST", path, undefined, {});
|
2014-08-12 10:17:10 -06:00
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Invite a user to a room
|
|
|
|
invite: function(room_id, user_id) {
|
2014-08-26 03:24:47 -06:00
|
|
|
return this.membershipChange(room_id, user_id, "invite");
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Leaves a room
|
|
|
|
leave: function(room_id) {
|
2014-08-26 07:59:31 -06:00
|
|
|
return this.membershipChange(room_id, undefined, "leave");
|
2014-08-26 03:24:47 -06:00
|
|
|
},
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-26 03:24:47 -06:00
|
|
|
membershipChange: function(room_id, user_id, membershipValue) {
|
|
|
|
// The REST path spec
|
2014-08-26 07:59:31 -06:00
|
|
|
var path = "/rooms/$room_id/$membership";
|
2014-08-26 03:24:47 -06:00
|
|
|
path = path.replace("$room_id", encodeURIComponent(room_id));
|
2014-08-26 07:59:31 -06:00
|
|
|
path = path.replace("$membership", encodeURIComponent(membershipValue));
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-26 07:59:31 -06:00
|
|
|
var data = {};
|
|
|
|
if (user_id !== undefined) {
|
|
|
|
data = { user_id: user_id };
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Use PUT with transaction IDs
|
|
|
|
return doRequest("POST", path, undefined, data);
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
2014-09-03 10:33:36 -06:00
|
|
|
// Change the membership of an another user
|
2014-09-05 09:30:50 -06:00
|
|
|
setMembership: function(room_id, user_id, membershipValue, reason) {
|
|
|
|
|
2014-09-03 10:33:36 -06:00
|
|
|
// The REST path spec
|
|
|
|
var path = "/rooms/$room_id/state/m.room.member/$user_id";
|
|
|
|
path = path.replace("$room_id", encodeURIComponent(room_id));
|
|
|
|
path = path.replace("$user_id", user_id);
|
|
|
|
|
2014-09-05 09:30:50 -06:00
|
|
|
return doRequest("PUT", path, undefined, {
|
|
|
|
membership : membershipValue,
|
|
|
|
reason: reason
|
|
|
|
});
|
2014-09-03 10:33:36 -06:00
|
|
|
},
|
2014-09-03 03:17:58 -06:00
|
|
|
|
2014-09-05 09:30:50 -06:00
|
|
|
// Bans a user from a room
|
2014-09-03 03:17:58 -06:00
|
|
|
ban: function(room_id, user_id, reason) {
|
2014-09-03 11:19:26 -06:00
|
|
|
var path = "/rooms/$room_id/ban";
|
2014-09-03 03:17:58 -06:00
|
|
|
path = path.replace("$room_id", encodeURIComponent(room_id));
|
|
|
|
|
2014-09-03 10:33:36 -06:00
|
|
|
return doRequest("POST", path, undefined, {
|
2014-09-03 03:17:58 -06:00
|
|
|
user_id: user_id,
|
|
|
|
reason: reason
|
|
|
|
});
|
|
|
|
},
|
2014-09-05 09:30:50 -06:00
|
|
|
|
|
|
|
// Unbans a user in a room
|
|
|
|
unban: function(room_id, user_id) {
|
|
|
|
// FIXME: To update when there will be homeserver API for unban
|
|
|
|
// For now, do an unban by resetting the user membership to "leave"
|
|
|
|
return this.setMembership(room_id, user_id, "leave");
|
|
|
|
},
|
|
|
|
|
|
|
|
// Kicks a user from a room
|
|
|
|
kick: function(room_id, user_id, reason) {
|
|
|
|
// Set the user membership to "leave" to kick him
|
|
|
|
return this.setMembership(room_id, user_id, "leave", reason);
|
|
|
|
},
|
|
|
|
|
2014-08-18 09:11:08 -06:00
|
|
|
// Retrieves the room ID corresponding to a room alias
|
|
|
|
resolveRoomAlias:function(room_alias) {
|
2014-08-31 07:51:37 -06:00
|
|
|
var path = "/_matrix/client/api/v1/directory/room/$room_alias";
|
2014-08-18 09:11:08 -06:00
|
|
|
room_alias = encodeURIComponent(room_alias);
|
|
|
|
|
|
|
|
path = path.replace("$room_alias", room_alias);
|
|
|
|
|
|
|
|
return doRequest("GET", path, undefined, {});
|
|
|
|
},
|
2014-09-08 19:40:34 -06:00
|
|
|
|
2014-09-12 02:48:06 -06:00
|
|
|
setName: function(room_id, name) {
|
|
|
|
var data = {
|
|
|
|
name: name
|
|
|
|
};
|
|
|
|
return this.sendStateEvent(room_id, "m.room.name", data);
|
|
|
|
},
|
|
|
|
|
2014-09-08 19:40:34 -06:00
|
|
|
setTopic: function(room_id, topic) {
|
|
|
|
var data = {
|
|
|
|
topic: topic
|
|
|
|
};
|
|
|
|
return this.sendStateEvent(room_id, "m.room.topic", data);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
sendStateEvent: function(room_id, eventType, content, state_key) {
|
|
|
|
var path = "/rooms/$room_id/state/"+eventType;
|
|
|
|
if (state_key !== undefined) {
|
|
|
|
path += "/" + state_key;
|
|
|
|
}
|
|
|
|
room_id = encodeURIComponent(room_id);
|
|
|
|
path = path.replace("$room_id", room_id);
|
|
|
|
|
|
|
|
return doRequest("PUT", path, undefined, content);
|
|
|
|
},
|
2014-08-18 09:11:08 -06:00
|
|
|
|
2014-08-29 06:23:01 -06:00
|
|
|
sendEvent: function(room_id, eventType, txn_id, content) {
|
2014-08-12 08:10:52 -06:00
|
|
|
// The REST path spec
|
2014-08-29 06:23:01 -06:00
|
|
|
var path = "/rooms/$room_id/send/"+eventType+"/$txn_id";
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-26 10:21:48 -06:00
|
|
|
if (!txn_id) {
|
|
|
|
txn_id = "m" + new Date().getTime();
|
2014-08-12 08:10:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Like the cmd client, escape room ids
|
|
|
|
room_id = encodeURIComponent(room_id);
|
|
|
|
|
|
|
|
// Customize it
|
|
|
|
path = path.replace("$room_id", room_id);
|
2014-08-26 10:21:48 -06:00
|
|
|
path = path.replace("$txn_id", txn_id);
|
2014-08-12 08:10:52 -06:00
|
|
|
|
|
|
|
return doRequest("PUT", path, undefined, content);
|
|
|
|
},
|
|
|
|
|
2014-08-29 06:23:01 -06:00
|
|
|
sendMessage: function(room_id, txn_id, content) {
|
2014-08-29 06:45:15 -06:00
|
|
|
return this.sendEvent(room_id, 'm.room.message', txn_id, content);
|
2014-08-29 06:23:01 -06:00
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Send a text message
|
|
|
|
sendTextMessage: function(room_id, body, msg_id) {
|
|
|
|
var content = {
|
|
|
|
msgtype: "m.text",
|
|
|
|
body: body
|
|
|
|
};
|
|
|
|
|
|
|
|
return this.sendMessage(room_id, msg_id, content);
|
|
|
|
},
|
|
|
|
|
2014-08-13 03:42:28 -06:00
|
|
|
// Send an image message
|
2014-08-20 08:18:50 -06:00
|
|
|
sendImageMessage: function(room_id, image_url, image_body, msg_id) {
|
2014-08-13 03:42:28 -06:00
|
|
|
var content = {
|
|
|
|
msgtype: "m.image",
|
|
|
|
url: image_url,
|
2014-08-20 08:18:50 -06:00
|
|
|
body: image_body
|
2014-08-13 03:42:28 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
return this.sendMessage(room_id, msg_id, content);
|
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// Send an emote message
|
|
|
|
sendEmoteMessage: function(room_id, body, msg_id) {
|
|
|
|
var content = {
|
|
|
|
msgtype: "m.emote",
|
|
|
|
body: body
|
|
|
|
};
|
|
|
|
|
|
|
|
return this.sendMessage(room_id, msg_id, content);
|
|
|
|
},
|
|
|
|
|
2014-10-29 09:31:50 -06:00
|
|
|
redactEvent: function(room_id, event_id) {
|
|
|
|
var path = "/rooms/$room_id/redact/$event_id";
|
|
|
|
path = path.replace("$room_id", room_id);
|
|
|
|
path = path.replace("$event_id", event_id);
|
|
|
|
var content = {};
|
|
|
|
return doRequest("POST", path, undefined, content);
|
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// get a snapshot of the members in a room.
|
|
|
|
getMemberList: function(room_id) {
|
|
|
|
// Like the cmd client, escape room ids
|
|
|
|
room_id = encodeURIComponent(room_id);
|
|
|
|
|
2014-08-26 09:19:17 -06:00
|
|
|
var path = "/rooms/$room_id/members";
|
2014-08-12 08:10:52 -06:00
|
|
|
path = path.replace("$room_id", room_id);
|
|
|
|
return doRequest("GET", path);
|
|
|
|
},
|
2014-08-14 10:23:47 -06:00
|
|
|
|
|
|
|
paginateBackMessages: function(room_id, from_token, limit) {
|
2014-08-26 09:19:17 -06:00
|
|
|
var path = "/rooms/$room_id/messages";
|
2014-08-14 10:23:47 -06:00
|
|
|
path = path.replace("$room_id", room_id);
|
|
|
|
var params = {
|
|
|
|
from: from_token,
|
2014-08-19 07:19:48 -06:00
|
|
|
limit: limit,
|
|
|
|
dir: 'b'
|
2014-08-14 10:23:47 -06:00
|
|
|
};
|
|
|
|
return doRequest("GET", path, params);
|
|
|
|
},
|
2014-08-12 08:10:52 -06:00
|
|
|
|
|
|
|
// get a list of public rooms on your home server
|
|
|
|
publicRooms: function() {
|
2014-09-03 06:12:56 -06:00
|
|
|
var path = "/publicRooms";
|
2014-08-12 08:10:52 -06:00
|
|
|
return doRequest("GET", path);
|
|
|
|
},
|
|
|
|
|
2014-09-05 17:14:02 -06:00
|
|
|
// get a user's profile
|
|
|
|
getProfile: function(userId) {
|
|
|
|
return this.getProfileInfo(userId);
|
|
|
|
},
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
// get a display name for this user ID
|
|
|
|
getDisplayName: function(userId) {
|
|
|
|
return this.getProfileInfo(userId, "displayname");
|
|
|
|
},
|
|
|
|
|
|
|
|
// get the profile picture url for this user ID
|
|
|
|
getProfilePictureUrl: function(userId) {
|
|
|
|
return this.getProfileInfo(userId, "avatar_url");
|
|
|
|
},
|
|
|
|
|
|
|
|
// update your display name
|
|
|
|
setDisplayName: function(newName) {
|
|
|
|
var content = {
|
|
|
|
displayname: newName
|
|
|
|
};
|
|
|
|
return this.setProfileInfo(content, "displayname");
|
|
|
|
},
|
|
|
|
|
|
|
|
// update your profile picture url
|
|
|
|
setProfilePictureUrl: function(newUrl) {
|
|
|
|
var content = {
|
|
|
|
avatar_url: newUrl
|
|
|
|
};
|
|
|
|
return this.setProfileInfo(content, "avatar_url");
|
|
|
|
},
|
|
|
|
|
|
|
|
setProfileInfo: function(data, info_segment) {
|
|
|
|
var path = "/profile/$user/" + info_segment;
|
|
|
|
path = path.replace("$user", config.user_id);
|
|
|
|
return doRequest("PUT", path, undefined, data);
|
|
|
|
},
|
|
|
|
|
|
|
|
getProfileInfo: function(userId, info_segment) {
|
2014-09-05 17:14:02 -06:00
|
|
|
var path = "/profile/"+userId
|
|
|
|
if (info_segment) path += '/' + info_segment;
|
2014-08-12 08:10:52 -06:00
|
|
|
return doRequest("GET", path);
|
|
|
|
},
|
|
|
|
|
|
|
|
login: function(userId, password) {
|
|
|
|
// TODO We should be checking to make sure the client can support
|
|
|
|
// logging in to this HS, else use the fallback.
|
|
|
|
var path = "/login";
|
|
|
|
var data = {
|
|
|
|
"type": "m.login.password",
|
|
|
|
"user": userId,
|
|
|
|
"password": password
|
|
|
|
};
|
|
|
|
return doRequest("POST", path, undefined, data);
|
|
|
|
},
|
|
|
|
|
|
|
|
// hit the Identity Server for a 3PID request.
|
2014-08-22 03:34:27 -06:00
|
|
|
linkEmail: function(email, clientSecret, sendAttempt) {
|
2014-09-03 06:12:56 -06:00
|
|
|
var path = "/_matrix/identity/api/v1/validate/email/requestToken";
|
2014-08-22 03:34:27 -06:00
|
|
|
var data = "clientSecret="+clientSecret+"&email=" + encodeURIComponent(email)+"&sendAttempt="+sendAttempt;
|
2014-08-12 08:10:52 -06:00
|
|
|
var headers = {};
|
|
|
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
|
return doBaseRequest(config.identityServer, "POST", path, {}, data, headers);
|
|
|
|
},
|
|
|
|
|
2014-09-03 11:23:56 -06:00
|
|
|
authEmail: function(clientSecret, sid, code) {
|
2014-08-31 07:51:37 -06:00
|
|
|
var path = "/_matrix/identity/api/v1/validate/email/submitToken";
|
2014-09-03 11:23:56 -06:00
|
|
|
var data = "token="+code+"&sid="+sid+"&clientSecret="+clientSecret;
|
2014-08-22 03:34:27 -06:00
|
|
|
var headers = {};
|
|
|
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
|
return doBaseRequest(config.identityServer, "POST", path, {}, data, headers);
|
|
|
|
},
|
|
|
|
|
|
|
|
bindEmail: function(userId, tokenId, clientSecret) {
|
2014-08-31 07:51:37 -06:00
|
|
|
var path = "/_matrix/identity/api/v1/3pid/bind";
|
2014-08-22 03:34:27 -06:00
|
|
|
var data = "mxid="+encodeURIComponent(userId)+"&sid="+tokenId+"&clientSecret="+clientSecret;
|
2014-08-12 08:10:52 -06:00
|
|
|
var headers = {};
|
|
|
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
|
return doBaseRequest(config.identityServer, "POST", path, {}, data, headers);
|
|
|
|
},
|
2014-09-03 11:23:56 -06:00
|
|
|
|
|
|
|
lookup3pid: function(medium, address) {
|
|
|
|
var path = "/_matrix/identity/api/v1/lookup?medium="+encodeURIComponent(medium)+"&address="+encodeURIComponent(address);
|
|
|
|
return doBaseRequest(config.identityServer, "GET", path, {}, undefined, {});
|
|
|
|
},
|
2014-08-12 08:10:52 -06:00
|
|
|
|
2014-08-18 08:50:55 -06:00
|
|
|
uploadContent: function(file) {
|
2014-08-31 07:51:37 -06:00
|
|
|
var path = "/_matrix/content";
|
2014-08-18 08:50:55 -06:00
|
|
|
var headers = {
|
|
|
|
"Content-Type": undefined // undefined means angular will figure it out
|
|
|
|
};
|
|
|
|
var params = {
|
|
|
|
access_token: config.access_token
|
|
|
|
};
|
2014-08-21 05:36:14 -06:00
|
|
|
|
|
|
|
// If the file is actually a Blob object, prevent $http from JSON-stringified it before sending
|
|
|
|
// (Equivalent to jQuery ajax processData = false)
|
|
|
|
var $httpParams;
|
|
|
|
if (file instanceof Blob) {
|
|
|
|
$httpParams = {
|
|
|
|
transformRequest: angular.identity
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return doBaseRequest(config.homeserver, "POST", path, params, file, headers, $httpParams);
|
2014-08-18 08:50:55 -06:00
|
|
|
},
|
2014-08-29 03:31:03 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Start listening on /events
|
|
|
|
* @param {String} from the token from which to listen events to
|
|
|
|
* @param {Integer} serverTimeout the time in ms the server will hold open the connection
|
|
|
|
* @param {Integer} clientTimeout the timeout in ms used at the client HTTP request level
|
|
|
|
* @returns a promise
|
|
|
|
*/
|
|
|
|
getEventStream: function(from, serverTimeout, clientTimeout) {
|
2014-08-15 03:20:14 -06:00
|
|
|
var path = "/events";
|
|
|
|
var params = {
|
|
|
|
from: from,
|
2014-08-29 03:31:03 -06:00
|
|
|
timeout: serverTimeout
|
2014-08-15 03:20:14 -06:00
|
|
|
};
|
2014-08-29 03:31:03 -06:00
|
|
|
|
|
|
|
var $httpParams;
|
|
|
|
if (clientTimeout) {
|
|
|
|
// If the Internet connection is lost, this timeout is used to be able to
|
|
|
|
// cancel the current request and notify the client so that it can retry with a new request.
|
|
|
|
$httpParams = {
|
|
|
|
timeout: clientTimeout
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return doRequest("GET", path, params, undefined, $httpParams);
|
2014-08-15 03:20:14 -06:00
|
|
|
},
|
2014-08-18 02:44:29 -06:00
|
|
|
|
|
|
|
// Indicates if user authentications details are stored in cache
|
|
|
|
isUserLoggedIn: function() {
|
|
|
|
var config = this.config();
|
|
|
|
|
|
|
|
// User is considered logged in if his cache is not empty and contains
|
|
|
|
// an access token
|
|
|
|
if (config && config.access_token) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-14 06:59:33 -06:00
|
|
|
},
|
2014-08-26 08:25:27 -06:00
|
|
|
|
|
|
|
// Enum of presence state
|
|
|
|
presence: {
|
|
|
|
offline: "offline",
|
|
|
|
unavailable: "unavailable",
|
|
|
|
online: "online",
|
|
|
|
free_for_chat: "free_for_chat"
|
|
|
|
},
|
|
|
|
|
|
|
|
// Set the logged in user presence state
|
|
|
|
setUserPresence: function(presence) {
|
|
|
|
var path = "/presence/$user_id/status";
|
|
|
|
path = path.replace("$user_id", config.user_id);
|
|
|
|
return doRequest("PUT", path, undefined, {
|
2014-09-04 07:10:43 -06:00
|
|
|
presence: presence
|
2014-08-26 08:25:27 -06:00
|
|
|
});
|
|
|
|
},
|
2014-09-03 03:17:58 -06:00
|
|
|
|
|
|
|
|
2014-08-12 08:10:52 -06:00
|
|
|
/****** Permanent storage of user information ******/
|
|
|
|
|
|
|
|
// Returns the current config
|
|
|
|
config: function() {
|
|
|
|
if (!config) {
|
|
|
|
config = localStorage.getItem("config");
|
|
|
|
if (config) {
|
|
|
|
config = JSON.parse(config);
|
|
|
|
|
|
|
|
// Reset the cache if the version loaded is not the expected one
|
|
|
|
if (configVersion !== config.version) {
|
|
|
|
config = undefined;
|
|
|
|
this.saveConfig();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return config;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Set a new config (Use saveConfig to actually store it permanently)
|
|
|
|
setConfig: function(newConfig) {
|
|
|
|
config = newConfig;
|
2014-08-22 03:34:27 -06:00
|
|
|
console.log("new IS: "+config.identityServer);
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Commits config into permanent storage
|
|
|
|
saveConfig: function() {
|
|
|
|
config.version = configVersion;
|
|
|
|
localStorage.setItem("config", JSON.stringify(config));
|
|
|
|
},
|
2014-08-27 07:07:51 -06:00
|
|
|
|
|
|
|
|
|
|
|
/****** Room aliases management ******/
|
|
|
|
|
|
|
|
/**
|
2014-08-28 09:48:55 -06:00
|
|
|
* Get the room_alias & room_display_name which are computed from data
|
|
|
|
* already retrieved from the server.
|
|
|
|
* @param {Room object} room one element of the array returned by the response
|
|
|
|
* of rooms() and publicRooms()
|
|
|
|
* @returns {Object} {room_alias: "...", room_display_name: "..."}
|
2014-08-27 07:07:51 -06:00
|
|
|
*/
|
2014-08-28 09:48:55 -06:00
|
|
|
getRoomAliasAndDisplayName: function(room) {
|
|
|
|
var result = {
|
|
|
|
room_alias: undefined,
|
|
|
|
room_display_name: undefined
|
|
|
|
};
|
|
|
|
var alias = this.getRoomIdToAliasMapping(room.room_id);
|
|
|
|
if (alias) {
|
|
|
|
// use the existing alias from storage
|
|
|
|
result.room_alias = alias;
|
|
|
|
result.room_display_name = alias;
|
|
|
|
}
|
2014-09-06 01:31:57 -06:00
|
|
|
// XXX: this only lets us learn aliases from our local HS - we should
|
|
|
|
// make the client stop returning this if we can trust m.room.aliases state events
|
2014-08-28 09:48:55 -06:00
|
|
|
else if (room.aliases && room.aliases[0]) {
|
|
|
|
// save the mapping
|
|
|
|
// TODO: select the smarter alias from the array
|
|
|
|
this.createRoomIdToAliasMapping(room.room_id, room.aliases[0]);
|
|
|
|
result.room_display_name = room.aliases[0];
|
2014-09-06 10:57:13 -06:00
|
|
|
result.room_alias = room.aliases[0];
|
2014-08-28 09:48:55 -06:00
|
|
|
}
|
|
|
|
else if (room.membership === "invite" && "inviter" in room) {
|
|
|
|
result.room_display_name = room.inviter + "'s room";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// last resort use the room id
|
|
|
|
result.room_display_name = room.room_id;
|
2014-08-27 07:07:51 -06:00
|
|
|
}
|
2014-08-28 09:48:55 -06:00
|
|
|
return result;
|
2014-08-27 07:07:51 -06:00
|
|
|
},
|
2014-08-12 08:10:52 -06:00
|
|
|
|
|
|
|
createRoomIdToAliasMapping: function(roomId, alias) {
|
2014-09-06 01:31:57 -06:00
|
|
|
roomIdToAlias[roomId] = alias;
|
|
|
|
aliasToRoomId[alias] = roomId;
|
2014-08-12 08:10:52 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
getRoomIdToAliasMapping: function(roomId) {
|
2014-09-23 09:12:06 -06:00
|
|
|
var alias = roomIdToAlias[roomId];
|
2014-09-06 01:31:57 -06:00
|
|
|
//console.log("looking for alias for " + roomId + "; found: " + alias);
|
|
|
|
return alias;
|
2014-09-02 03:54:11 -06:00
|
|
|
},
|
|
|
|
|
2014-09-06 01:31:57 -06:00
|
|
|
getAliasToRoomIdMapping: function(alias) {
|
|
|
|
var roomId = aliasToRoomId[alias];
|
|
|
|
//console.log("looking for roomId for " + alias + "; found: " + roomId);
|
|
|
|
return roomId;
|
|
|
|
},
|
2014-09-02 03:54:11 -06:00
|
|
|
|
|
|
|
/****** Power levels management ******/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the power level of an user in a particular room
|
|
|
|
* @param {String} room_id the room id
|
|
|
|
* @param {String} user_id the user id
|
|
|
|
* @returns {Number} a value between 0 and 10
|
|
|
|
*/
|
|
|
|
getUserPowerLevel: function(room_id, user_id) {
|
|
|
|
var powerLevel = 0;
|
|
|
|
var room = $rootScope.events.rooms[room_id];
|
|
|
|
if (room && room["m.room.power_levels"]) {
|
|
|
|
if (user_id in room["m.room.power_levels"].content) {
|
|
|
|
powerLevel = room["m.room.power_levels"].content[user_id];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Use the room default user power
|
|
|
|
powerLevel = room["m.room.power_levels"].content["default"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return powerLevel;
|
2014-09-03 06:12:56 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-09-03 07:18:57 -06:00
|
|
|
* Change or reset the power level of a user
|
2014-09-03 06:12:56 -06:00
|
|
|
* @param {String} room_id the room id
|
|
|
|
* @param {String} user_id the user id
|
|
|
|
* @param {Number} powerLevel a value between 0 and 10
|
2014-09-03 07:18:57 -06:00
|
|
|
* If undefined, the user power level will be reset, ie he will use the default room user power level
|
2014-09-03 06:12:56 -06:00
|
|
|
* @returns {promise} an $http promise
|
|
|
|
*/
|
|
|
|
setUserPowerLevel: function(room_id, user_id, powerLevel) {
|
|
|
|
|
|
|
|
// Hack: currently, there is no home server API so do it by hand by updating
|
|
|
|
// the current m.room.power_levels of the room and send it to the server
|
|
|
|
var room = $rootScope.events.rooms[room_id];
|
|
|
|
if (room && room["m.room.power_levels"]) {
|
|
|
|
var content = angular.copy(room["m.room.power_levels"].content);
|
|
|
|
content[user_id] = powerLevel;
|
|
|
|
|
|
|
|
var path = "/rooms/$room_id/state/m.room.power_levels";
|
|
|
|
path = path.replace("$room_id", encodeURIComponent(room_id));
|
|
|
|
|
|
|
|
return doRequest("PUT", path, undefined, content);
|
|
|
|
}
|
|
|
|
|
2014-09-03 07:14:13 -06:00
|
|
|
// The room does not exist or does not contain power_levels data
|
|
|
|
var deferred = $q.defer();
|
|
|
|
deferred.reject({data:{error: "Invalid room: " + room_id}});
|
2014-09-03 06:12:56 -06:00
|
|
|
return deferred.promise;
|
2014-09-24 09:07:33 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
getTurnServer: function() {
|
2014-09-24 09:28:47 -06:00
|
|
|
return doRequest("GET", "/voip/turnServer");
|
2014-08-12 08:10:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
}]);
|