UNPKG

82.1 kBJavaScriptView Raw
1/*
2Copyright 2015, 2016 OpenMarket Ltd
3Copyright 2017 Vector Creations Ltd
4Copyright 2019 The Matrix.org Foundation C.I.C.
5Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
6
7Licensed under the Apache License, Version 2.0 (the "License");
8you may not use this file except in compliance with the License.
9You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13Unless required by applicable law or agreed to in writing, software
14distributed under the License is distributed on an "AS IS" BASIS,
15WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16See the License for the specific language governing permissions and
17limitations under the License.
18*/
19
20/**
21 * This is an internal module. MatrixBaseApis is currently only meant to be used
22 * by {@link client~MatrixClient}.
23 *
24 * @module base-apis
25 */
26
27import {SERVICE_TYPES} from './service-types';
28import {logger} from './logger';
29import {PushProcessor} from "./pushprocessor";
30import * as utils from "./utils";
31import {MatrixHttpApi, PREFIX_IDENTITY_V2, PREFIX_R0, PREFIX_UNSTABLE} from "./http-api";
32
33function termsUrlForService(serviceType, baseUrl) {
34 switch (serviceType) {
35 case SERVICE_TYPES.IS:
36 return baseUrl + PREFIX_IDENTITY_V2 + '/terms';
37 case SERVICE_TYPES.IM:
38 return baseUrl + '/_matrix/integrations/v1/terms';
39 default:
40 throw new Error('Unsupported service type');
41 }
42}
43
44/**
45 * Low-level wrappers for the Matrix APIs
46 *
47 * @constructor
48 *
49 * @param {Object} opts Configuration options
50 *
51 * @param {string} opts.baseUrl Required. The base URL to the client-server
52 * HTTP API.
53 *
54 * @param {string} opts.idBaseUrl Optional. The base identity server URL for
55 * identity server requests.
56 *
57 * @param {Function} opts.request Required. The function to invoke for HTTP
58 * requests. The value of this property is typically <code>require("request")
59 * </code> as it returns a function which meets the required interface. See
60 * {@link requestFunction} for more information.
61 *
62 * @param {string} opts.accessToken The access_token for this user.
63 *
64 * @param {IdentityServerProvider} [opts.identityServer]
65 * Optional. A provider object with one function `getAccessToken`, which is a
66 * callback that returns a Promise<String> of an identity access token to supply
67 * with identity requests. If the object is unset, no access token will be
68 * supplied.
69 * See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
70 * replace the previous approach of manual access tokens params with this
71 * callback throughout the SDK.
72 *
73 * @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
74 * time to wait before timing out HTTP requests. If not specified, there is no
75 * timeout.
76 *
77 * @param {Object} opts.queryParams Optional. Extra query parameters to append
78 * to all requests with this client. Useful for application services which require
79 * <code>?user_id=</code>.
80 *
81 * @param {boolean} [opts.useAuthorizationHeader = false] Set to true to use
82 * Authorization header instead of query param to send the access token to the server.
83 */
84export function MatrixBaseApis(opts) {
85 utils.checkObjectHasKeys(opts, ["baseUrl", "request"]);
86
87 this.baseUrl = opts.baseUrl;
88 this.idBaseUrl = opts.idBaseUrl;
89 this.identityServer = opts.identityServer;
90
91 const httpOpts = {
92 baseUrl: opts.baseUrl,
93 idBaseUrl: opts.idBaseUrl,
94 accessToken: opts.accessToken,
95 request: opts.request,
96 prefix: PREFIX_R0,
97 onlyData: true,
98 extraParams: opts.queryParams,
99 localTimeoutMs: opts.localTimeoutMs,
100 useAuthorizationHeader: opts.useAuthorizationHeader,
101 };
102 this._http = new MatrixHttpApi(this, httpOpts);
103
104 this._txnCtr = 0;
105}
106
107/**
108 * Get the Homeserver URL of this client
109 * @return {string} Homeserver URL of this client
110 */
111MatrixBaseApis.prototype.getHomeserverUrl = function() {
112 return this.baseUrl;
113};
114
115/**
116 * Get the Identity Server URL of this client
117 * @param {boolean} stripProto whether or not to strip the protocol from the URL
118 * @return {string} Identity Server URL of this client
119 */
120MatrixBaseApis.prototype.getIdentityServerUrl = function(stripProto=false) {
121 if (stripProto && (this.idBaseUrl.startsWith("http://") ||
122 this.idBaseUrl.startsWith("https://"))) {
123 return this.idBaseUrl.split("://")[1];
124 }
125 return this.idBaseUrl;
126};
127
128/**
129 * Set the Identity Server URL of this client
130 * @param {string} url New Identity Server URL
131 */
132MatrixBaseApis.prototype.setIdentityServerUrl = function(url) {
133 this.idBaseUrl = utils.ensureNoTrailingSlash(url);
134 this._http.setIdBaseUrl(this.idBaseUrl);
135};
136
137/**
138 * Get the access token associated with this account.
139 * @return {?String} The access_token or null
140 */
141MatrixBaseApis.prototype.getAccessToken = function() {
142 return this._http.opts.accessToken || null;
143};
144
145/**
146 * @return {boolean} true if there is a valid access_token for this client.
147 */
148MatrixBaseApis.prototype.isLoggedIn = function() {
149 return this._http.opts.accessToken !== undefined;
150};
151
152/**
153 * Make up a new transaction id
154 *
155 * @return {string} a new, unique, transaction id
156 */
157MatrixBaseApis.prototype.makeTxnId = function() {
158 return "m" + new Date().getTime() + "." + (this._txnCtr++);
159};
160
161
162// Registration/Login operations
163// =============================
164
165/**
166 * Check whether a username is available prior to registration. An error response
167 * indicates an invalid/unavailable username.
168 * @param {string} username The username to check the availability of.
169 * @return {Promise} Resolves: to `true`.
170 */
171MatrixBaseApis.prototype.isUsernameAvailable = function(username) {
172 return this._http.authedRequest(
173 undefined, "GET", '/register/available', { username: username },
174 ).then((response) => {
175 return response.available;
176 });
177};
178
179/**
180 * @param {string} username
181 * @param {string} password
182 * @param {string} sessionId
183 * @param {Object} auth
184 * @param {Object} bindThreepids Set key 'email' to true to bind any email
185 * threepid uses during registration in the ID server. Set 'msisdn' to
186 * true to bind msisdn.
187 * @param {string} guestAccessToken
188 * @param {string} inhibitLogin
189 * @param {module:client.callback} callback Optional.
190 * @return {Promise} Resolves: TODO
191 * @return {module:http-api.MatrixError} Rejects: with an error response.
192 */
193MatrixBaseApis.prototype.register = function(
194 username, password,
195 sessionId, auth, bindThreepids, guestAccessToken, inhibitLogin,
196 callback,
197) {
198 // backwards compat
199 if (bindThreepids === true) {
200 bindThreepids = {email: true};
201 } else if (bindThreepids === null || bindThreepids === undefined) {
202 bindThreepids = {};
203 }
204 if (typeof inhibitLogin === 'function') {
205 callback = inhibitLogin;
206 inhibitLogin = undefined;
207 }
208
209 if (auth === undefined || auth === null) {
210 auth = {};
211 }
212 if (sessionId) {
213 auth.session = sessionId;
214 }
215
216 const params = {
217 auth: auth,
218 };
219 if (username !== undefined && username !== null) {
220 params.username = username;
221 }
222 if (password !== undefined && password !== null) {
223 params.password = password;
224 }
225 if (bindThreepids.email) {
226 params.bind_email = true;
227 }
228 if (bindThreepids.msisdn) {
229 params.bind_msisdn = true;
230 }
231 if (guestAccessToken !== undefined && guestAccessToken !== null) {
232 params.guest_access_token = guestAccessToken;
233 }
234 if (inhibitLogin !== undefined && inhibitLogin !== null) {
235 params.inhibit_login = inhibitLogin;
236 }
237 // Temporary parameter added to make the register endpoint advertise
238 // msisdn flows. This exists because there are clients that break
239 // when given stages they don't recognise. This parameter will cease
240 // to be necessary once these old clients are gone.
241 // Only send it if we send any params at all (the password param is
242 // mandatory, so if we send any params, we'll send the password param)
243 if (password !== undefined && password !== null) {
244 params.x_show_msisdn = true;
245 }
246
247 return this.registerRequest(params, undefined, callback);
248};
249
250/**
251 * Register a guest account.
252 * @param {Object=} opts Registration options
253 * @param {Object} opts.body JSON HTTP body to provide.
254 * @param {module:client.callback} callback Optional.
255 * @return {Promise} Resolves: TODO
256 * @return {module:http-api.MatrixError} Rejects: with an error response.
257 */
258MatrixBaseApis.prototype.registerGuest = function(opts, callback) {
259 opts = opts || {};
260 opts.body = opts.body || {};
261 return this.registerRequest(opts.body, "guest", callback);
262};
263
264/**
265 * @param {Object} data parameters for registration request
266 * @param {string=} kind type of user to register. may be "guest"
267 * @param {module:client.callback=} callback
268 * @return {Promise} Resolves: to the /register response
269 * @return {module:http-api.MatrixError} Rejects: with an error response.
270 */
271MatrixBaseApis.prototype.registerRequest = function(data, kind, callback) {
272 const params = {};
273 if (kind) {
274 params.kind = kind;
275 }
276
277 return this._http.request(
278 callback, "POST", "/register", params, data,
279 );
280};
281
282/**
283 * @param {module:client.callback} callback Optional.
284 * @return {Promise} Resolves: TODO
285 * @return {module:http-api.MatrixError} Rejects: with an error response.
286 */
287MatrixBaseApis.prototype.loginFlows = function(callback) {
288 return this._http.request(callback, "GET", "/login");
289};
290
291/**
292 * @param {string} loginType
293 * @param {Object} data
294 * @param {module:client.callback} callback Optional.
295 * @return {Promise} Resolves: TODO
296 * @return {module:http-api.MatrixError} Rejects: with an error response.
297 */
298MatrixBaseApis.prototype.login = function(loginType, data, callback) {
299 const login_data = {
300 type: loginType,
301 };
302
303 // merge data into login_data
304 utils.extend(login_data, data);
305
306 return this._http.authedRequest(
307 (error, response) => {
308 if (response && response.access_token && response.user_id) {
309 this._http.opts.accessToken = response.access_token;
310 this.credentials = {
311 userId: response.user_id,
312 };
313 }
314
315 if (callback) {
316 callback(error, response);
317 }
318 }, "POST", "/login", undefined, login_data,
319 );
320};
321
322/**
323 * @param {string} user
324 * @param {string} password
325 * @param {module:client.callback} callback Optional.
326 * @return {Promise} Resolves: TODO
327 * @return {module:http-api.MatrixError} Rejects: with an error response.
328 */
329MatrixBaseApis.prototype.loginWithPassword = function(user, password, callback) {
330 return this.login("m.login.password", {
331 user: user,
332 password: password,
333 }, callback);
334};
335
336/**
337 * @param {string} relayState URL Callback after SAML2 Authentication
338 * @param {module:client.callback} callback Optional.
339 * @return {Promise} Resolves: TODO
340 * @return {module:http-api.MatrixError} Rejects: with an error response.
341 */
342MatrixBaseApis.prototype.loginWithSAML2 = function(relayState, callback) {
343 return this.login("m.login.saml2", {
344 relay_state: relayState,
345 }, callback);
346};
347
348/**
349 * @param {string} redirectUrl The URL to redirect to after the HS
350 * authenticates with CAS.
351 * @return {string} The HS URL to hit to begin the CAS login process.
352 */
353MatrixBaseApis.prototype.getCasLoginUrl = function(redirectUrl) {
354 return this.getSsoLoginUrl(redirectUrl, "cas");
355};
356
357/**
358 * @param {string} redirectUrl The URL to redirect to after the HS
359 * authenticates with the SSO.
360 * @param {string} loginType The type of SSO login we are doing (sso or cas).
361 * Defaults to 'sso'.
362 * @return {string} The HS URL to hit to begin the SSO login process.
363 */
364MatrixBaseApis.prototype.getSsoLoginUrl = function(redirectUrl, loginType) {
365 if (loginType === undefined) {
366 loginType = "sso";
367 }
368 return this._http.getUrl("/login/"+loginType+"/redirect", {
369 "redirectUrl": redirectUrl,
370 }, PREFIX_R0);
371};
372
373/**
374 * @param {string} token Login token previously received from homeserver
375 * @param {module:client.callback} callback Optional.
376 * @return {Promise} Resolves: TODO
377 * @return {module:http-api.MatrixError} Rejects: with an error response.
378 */
379MatrixBaseApis.prototype.loginWithToken = function(token, callback) {
380 return this.login("m.login.token", {
381 token: token,
382 }, callback);
383};
384
385
386/**
387 * Logs out the current session.
388 * Obviously, further calls that require authorisation should fail after this
389 * method is called. The state of the MatrixClient object is not affected:
390 * it is up to the caller to either reset or destroy the MatrixClient after
391 * this method succeeds.
392 * @param {module:client.callback} callback Optional.
393 * @return {Promise} Resolves: On success, the empty object
394 */
395MatrixBaseApis.prototype.logout = function(callback) {
396 return this._http.authedRequest(
397 callback, "POST", '/logout',
398 );
399};
400
401/**
402 * Deactivates the logged-in account.
403 * Obviously, further calls that require authorisation should fail after this
404 * method is called. The state of the MatrixClient object is not affected:
405 * it is up to the caller to either reset or destroy the MatrixClient after
406 * this method succeeds.
407 * @param {object} auth Optional. Auth data to supply for User-Interactive auth.
408 * @param {boolean} erase Optional. If set, send as `erase` attribute in the
409 * JSON request body, indicating whether the account should be erased. Defaults
410 * to false.
411 * @return {Promise} Resolves: On success, the empty object
412 */
413MatrixBaseApis.prototype.deactivateAccount = function(auth, erase) {
414 if (typeof(erase) === 'function') {
415 throw new Error(
416 'deactivateAccount no longer accepts a callback parameter',
417 );
418 }
419
420 const body = {};
421 if (auth) {
422 body.auth = auth;
423 }
424 if (erase !== undefined) {
425 body.erase = erase;
426 }
427
428 return this._http.authedRequest(
429 undefined, "POST", '/account/deactivate', undefined, body,
430 );
431};
432
433/**
434 * Get the fallback URL to use for unknown interactive-auth stages.
435 *
436 * @param {string} loginType the type of stage being attempted
437 * @param {string} authSessionId the auth session ID provided by the homeserver
438 *
439 * @return {string} HS URL to hit to for the fallback interface
440 */
441MatrixBaseApis.prototype.getFallbackAuthUrl = function(loginType, authSessionId) {
442 const path = utils.encodeUri("/auth/$loginType/fallback/web", {
443 $loginType: loginType,
444 });
445
446 return this._http.getUrl(path, {
447 session: authSessionId,
448 }, PREFIX_R0);
449};
450
451// Room operations
452// ===============
453
454/**
455 * Create a new room.
456 * @param {Object} options a list of options to pass to the /createRoom API.
457 * @param {string} options.room_alias_name The alias localpart to assign to
458 * this room.
459 * @param {string} options.visibility Either 'public' or 'private'.
460 * @param {string[]} options.invite A list of user IDs to invite to this room.
461 * @param {string} options.name The name to give this room.
462 * @param {string} options.topic The topic to give this room.
463 * @param {module:client.callback} callback Optional.
464 * @return {Promise} Resolves: <code>{room_id: {string},
465 * room_alias: {string(opt)}}</code>
466 * @return {module:http-api.MatrixError} Rejects: with an error response.
467 */
468MatrixBaseApis.prototype.createRoom = function(options, callback) {
469 // valid options include: room_alias_name, visibility, invite
470 return this._http.authedRequest(
471 callback, "POST", "/createRoom", undefined, options,
472 );
473};
474/**
475 * Fetches relations for a given event
476 * @param {string} roomId the room of the event
477 * @param {string} eventId the id of the event
478 * @param {string} relationType the rel_type of the relations requested
479 * @param {string} eventType the event type of the relations requested
480 * @param {Object} opts options with optional values for the request.
481 * @param {Object} opts.from the pagination token returned from a previous request as `next_batch` to return following relations.
482 * @return {Object} the response, with chunk and next_batch.
483 */
484MatrixBaseApis.prototype.fetchRelations =
485 async function(roomId, eventId, relationType, eventType, opts) {
486 const queryParams = {};
487 if (opts.from) {
488 queryParams.from = opts.from;
489 }
490 const queryString = utils.encodeParams(queryParams);
491 const path = utils.encodeUri(
492 "/rooms/$roomId/relations/$eventId/$relationType/$eventType?" + queryString, {
493 $roomId: roomId,
494 $eventId: eventId,
495 $relationType: relationType,
496 $eventType: eventType,
497 });
498 const response = await this._http.authedRequest(
499 undefined, "GET", path, null, null, {
500 prefix: PREFIX_UNSTABLE,
501 },
502 );
503 return response;
504};
505
506/**
507 * @param {string} roomId
508 * @param {module:client.callback} callback Optional.
509 * @return {Promise} Resolves: TODO
510 * @return {module:http-api.MatrixError} Rejects: with an error response.
511 */
512MatrixBaseApis.prototype.roomState = function(roomId, callback) {
513 const path = utils.encodeUri("/rooms/$roomId/state", {$roomId: roomId});
514 return this._http.authedRequest(callback, "GET", path);
515};
516
517/**
518 * Get an event in a room by its event id.
519 * @param {string} roomId
520 * @param {string} eventId
521 * @param {module:client.callback} callback Optional.
522 *
523 * @return {Promise} Resolves to an object containing the event.
524 * @return {module:http-api.MatrixError} Rejects: with an error response.
525 */
526MatrixBaseApis.prototype.fetchRoomEvent = function(roomId, eventId, callback) {
527 const path = utils.encodeUri(
528 "/rooms/$roomId/event/$eventId", {
529 $roomId: roomId,
530 $eventId: eventId,
531 },
532 );
533 return this._http.authedRequest(callback, "GET", path);
534};
535
536/**
537 * @param {string} roomId
538 * @param {string} includeMembership the membership type to include in the response
539 * @param {string} excludeMembership the membership type to exclude from the response
540 * @param {string} atEventId the id of the event for which moment in the timeline the members should be returned for
541 * @param {module:client.callback} callback Optional.
542 * @return {Promise} Resolves: dictionary of userid to profile information
543 * @return {module:http-api.MatrixError} Rejects: with an error response.
544 */
545MatrixBaseApis.prototype.members =
546function(roomId, includeMembership, excludeMembership, atEventId, callback) {
547 const queryParams = {};
548 if (includeMembership) {
549 queryParams.membership = includeMembership;
550 }
551 if (excludeMembership) {
552 queryParams.not_membership = excludeMembership;
553 }
554 if (atEventId) {
555 queryParams.at = atEventId;
556 }
557
558 const queryString = utils.encodeParams(queryParams);
559
560 const path = utils.encodeUri("/rooms/$roomId/members?" + queryString,
561 {$roomId: roomId});
562 return this._http.authedRequest(callback, "GET", path);
563};
564
565/**
566 * Upgrades a room to a new protocol version
567 * @param {string} roomId
568 * @param {string} newVersion The target version to upgrade to
569 * @return {Promise} Resolves: Object with key 'replacement_room'
570 * @return {module:http-api.MatrixError} Rejects: with an error response.
571 */
572MatrixBaseApis.prototype.upgradeRoom = function(roomId, newVersion) {
573 const path = utils.encodeUri("/rooms/$roomId/upgrade", {$roomId: roomId});
574 return this._http.authedRequest(
575 undefined, "POST", path, undefined, {new_version: newVersion},
576 );
577};
578
579
580/**
581 * @param {string} groupId
582 * @return {Promise} Resolves: Group summary object
583 * @return {module:http-api.MatrixError} Rejects: with an error response.
584 */
585MatrixBaseApis.prototype.getGroupSummary = function(groupId) {
586 const path = utils.encodeUri("/groups/$groupId/summary", {$groupId: groupId});
587 return this._http.authedRequest(undefined, "GET", path);
588};
589
590/**
591 * @param {string} groupId
592 * @return {Promise} Resolves: Group profile object
593 * @return {module:http-api.MatrixError} Rejects: with an error response.
594 */
595MatrixBaseApis.prototype.getGroupProfile = function(groupId) {
596 const path = utils.encodeUri("/groups/$groupId/profile", {$groupId: groupId});
597 return this._http.authedRequest(undefined, "GET", path);
598};
599
600/**
601 * @param {string} groupId
602 * @param {Object} profile The group profile object
603 * @param {string=} profile.name Name of the group
604 * @param {string=} profile.avatar_url MXC avatar URL
605 * @param {string=} profile.short_description A short description of the room
606 * @param {string=} profile.long_description A longer HTML description of the room
607 * @return {Promise} Resolves: Empty object
608 * @return {module:http-api.MatrixError} Rejects: with an error response.
609 */
610MatrixBaseApis.prototype.setGroupProfile = function(groupId, profile) {
611 const path = utils.encodeUri("/groups/$groupId/profile", {$groupId: groupId});
612 return this._http.authedRequest(
613 undefined, "POST", path, undefined, profile,
614 );
615};
616
617/**
618 * @param {string} groupId
619 * @param {object} policy The join policy for the group. Must include at
620 * least a 'type' field which is 'open' if anyone can join the group
621 * the group without prior approval, or 'invite' if an invite is
622 * required to join.
623 * @return {Promise} Resolves: Empty object
624 * @return {module:http-api.MatrixError} Rejects: with an error response.
625 */
626MatrixBaseApis.prototype.setGroupJoinPolicy = function(groupId, policy) {
627 const path = utils.encodeUri(
628 "/groups/$groupId/settings/m.join_policy",
629 {$groupId: groupId},
630 );
631 return this._http.authedRequest(
632 undefined, "PUT", path, undefined, {
633 'm.join_policy': policy,
634 },
635 );
636};
637
638/**
639 * @param {string} groupId
640 * @return {Promise} Resolves: Group users list object
641 * @return {module:http-api.MatrixError} Rejects: with an error response.
642 */
643MatrixBaseApis.prototype.getGroupUsers = function(groupId) {
644 const path = utils.encodeUri("/groups/$groupId/users", {$groupId: groupId});
645 return this._http.authedRequest(undefined, "GET", path);
646};
647
648/**
649 * @param {string} groupId
650 * @return {Promise} Resolves: Group users list object
651 * @return {module:http-api.MatrixError} Rejects: with an error response.
652 */
653MatrixBaseApis.prototype.getGroupInvitedUsers = function(groupId) {
654 const path = utils.encodeUri("/groups/$groupId/invited_users", {$groupId: groupId});
655 return this._http.authedRequest(undefined, "GET", path);
656};
657
658/**
659 * @param {string} groupId
660 * @return {Promise} Resolves: Group rooms list object
661 * @return {module:http-api.MatrixError} Rejects: with an error response.
662 */
663MatrixBaseApis.prototype.getGroupRooms = function(groupId) {
664 const path = utils.encodeUri("/groups/$groupId/rooms", {$groupId: groupId});
665 return this._http.authedRequest(undefined, "GET", path);
666};
667
668/**
669 * @param {string} groupId
670 * @param {string} userId
671 * @return {Promise} Resolves: Empty object
672 * @return {module:http-api.MatrixError} Rejects: with an error response.
673 */
674MatrixBaseApis.prototype.inviteUserToGroup = function(groupId, userId) {
675 const path = utils.encodeUri(
676 "/groups/$groupId/admin/users/invite/$userId",
677 {$groupId: groupId, $userId: userId},
678 );
679 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
680};
681
682/**
683 * @param {string} groupId
684 * @param {string} userId
685 * @return {Promise} Resolves: Empty object
686 * @return {module:http-api.MatrixError} Rejects: with an error response.
687 */
688MatrixBaseApis.prototype.removeUserFromGroup = function(groupId, userId) {
689 const path = utils.encodeUri(
690 "/groups/$groupId/admin/users/remove/$userId",
691 {$groupId: groupId, $userId: userId},
692 );
693 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
694};
695
696/**
697 * @param {string} groupId
698 * @param {string} userId
699 * @param {string} roleId Optional.
700 * @return {Promise} Resolves: Empty object
701 * @return {module:http-api.MatrixError} Rejects: with an error response.
702 */
703MatrixBaseApis.prototype.addUserToGroupSummary = function(groupId, userId, roleId) {
704 const path = utils.encodeUri(
705 roleId ?
706 "/groups/$groupId/summary/$roleId/users/$userId" :
707 "/groups/$groupId/summary/users/$userId",
708 {$groupId: groupId, $roleId: roleId, $userId: userId},
709 );
710 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
711};
712
713/**
714 * @param {string} groupId
715 * @param {string} userId
716 * @return {Promise} Resolves: Empty object
717 * @return {module:http-api.MatrixError} Rejects: with an error response.
718 */
719MatrixBaseApis.prototype.removeUserFromGroupSummary = function(groupId, userId) {
720 const path = utils.encodeUri(
721 "/groups/$groupId/summary/users/$userId",
722 {$groupId: groupId, $userId: userId},
723 );
724 return this._http.authedRequest(undefined, "DELETE", path, undefined, {});
725};
726
727/**
728 * @param {string} groupId
729 * @param {string} roomId
730 * @param {string} categoryId Optional.
731 * @return {Promise} Resolves: Empty object
732 * @return {module:http-api.MatrixError} Rejects: with an error response.
733 */
734MatrixBaseApis.prototype.addRoomToGroupSummary = function(groupId, roomId, categoryId) {
735 const path = utils.encodeUri(
736 categoryId ?
737 "/groups/$groupId/summary/$categoryId/rooms/$roomId" :
738 "/groups/$groupId/summary/rooms/$roomId",
739 {$groupId: groupId, $categoryId: categoryId, $roomId: roomId},
740 );
741 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
742};
743
744/**
745 * @param {string} groupId
746 * @param {string} roomId
747 * @return {Promise} Resolves: Empty object
748 * @return {module:http-api.MatrixError} Rejects: with an error response.
749 */
750MatrixBaseApis.prototype.removeRoomFromGroupSummary = function(groupId, roomId) {
751 const path = utils.encodeUri(
752 "/groups/$groupId/summary/rooms/$roomId",
753 {$groupId: groupId, $roomId: roomId},
754 );
755 return this._http.authedRequest(undefined, "DELETE", path, undefined, {});
756};
757
758/**
759 * @param {string} groupId
760 * @param {string} roomId
761 * @param {bool} isPublic Whether the room-group association is visible to non-members
762 * @return {Promise} Resolves: Empty object
763 * @return {module:http-api.MatrixError} Rejects: with an error response.
764 */
765MatrixBaseApis.prototype.addRoomToGroup = function(groupId, roomId, isPublic) {
766 if (isPublic === undefined) {
767 isPublic = true;
768 }
769 const path = utils.encodeUri(
770 "/groups/$groupId/admin/rooms/$roomId",
771 {$groupId: groupId, $roomId: roomId},
772 );
773 return this._http.authedRequest(undefined, "PUT", path, undefined,
774 { "m.visibility": { type: isPublic ? "public" : "private" } },
775 );
776};
777
778/**
779 * Configure the visibility of a room-group association.
780 * @param {string} groupId
781 * @param {string} roomId
782 * @param {bool} isPublic Whether the room-group association is visible to non-members
783 * @return {Promise} Resolves: Empty object
784 * @return {module:http-api.MatrixError} Rejects: with an error response.
785 */
786MatrixBaseApis.prototype.updateGroupRoomVisibility = function(groupId, roomId, isPublic) {
787 // NB: The /config API is generic but there's not much point in exposing this yet as synapse
788 // is the only server to implement this. In future we should consider an API that allows
789 // arbitrary configuration, i.e. "config/$configKey".
790
791 const path = utils.encodeUri(
792 "/groups/$groupId/admin/rooms/$roomId/config/m.visibility",
793 {$groupId: groupId, $roomId: roomId},
794 );
795 return this._http.authedRequest(undefined, "PUT", path, undefined,
796 { type: isPublic ? "public" : "private" },
797 );
798};
799
800/**
801 * @param {string} groupId
802 * @param {string} roomId
803 * @return {Promise} Resolves: Empty object
804 * @return {module:http-api.MatrixError} Rejects: with an error response.
805 */
806MatrixBaseApis.prototype.removeRoomFromGroup = function(groupId, roomId) {
807 const path = utils.encodeUri(
808 "/groups/$groupId/admin/rooms/$roomId",
809 {$groupId: groupId, $roomId: roomId},
810 );
811 return this._http.authedRequest(undefined, "DELETE", path, undefined, {});
812};
813
814/**
815 * @param {string} groupId
816 * @param {Object} opts Additional options to send alongside the acceptance.
817 * @return {Promise} Resolves: Empty object
818 * @return {module:http-api.MatrixError} Rejects: with an error response.
819 */
820MatrixBaseApis.prototype.acceptGroupInvite = function(groupId, opts = null) {
821 const path = utils.encodeUri(
822 "/groups/$groupId/self/accept_invite",
823 {$groupId: groupId},
824 );
825 return this._http.authedRequest(undefined, "PUT", path, undefined, opts || {});
826};
827
828/**
829 * @param {string} groupId
830 * @return {Promise} Resolves: Empty object
831 * @return {module:http-api.MatrixError} Rejects: with an error response.
832 */
833MatrixBaseApis.prototype.joinGroup = function(groupId) {
834 const path = utils.encodeUri(
835 "/groups/$groupId/self/join",
836 {$groupId: groupId},
837 );
838 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
839};
840
841/**
842 * @param {string} groupId
843 * @return {Promise} Resolves: Empty object
844 * @return {module:http-api.MatrixError} Rejects: with an error response.
845 */
846MatrixBaseApis.prototype.leaveGroup = function(groupId) {
847 const path = utils.encodeUri(
848 "/groups/$groupId/self/leave",
849 {$groupId: groupId},
850 );
851 return this._http.authedRequest(undefined, "PUT", path, undefined, {});
852};
853
854/**
855 * @return {Promise} Resolves: The groups to which the user is joined
856 * @return {module:http-api.MatrixError} Rejects: with an error response.
857 */
858MatrixBaseApis.prototype.getJoinedGroups = function() {
859 const path = utils.encodeUri("/joined_groups");
860 return this._http.authedRequest(undefined, "GET", path);
861};
862
863/**
864 * @param {Object} content Request content
865 * @param {string} content.localpart The local part of the desired group ID
866 * @param {Object} content.profile Group profile object
867 * @return {Promise} Resolves: Object with key group_id: id of the created group
868 * @return {module:http-api.MatrixError} Rejects: with an error response.
869 */
870MatrixBaseApis.prototype.createGroup = function(content) {
871 const path = utils.encodeUri("/create_group");
872 return this._http.authedRequest(
873 undefined, "POST", path, undefined, content,
874 );
875};
876
877/**
878 * @param {string[]} userIds List of user IDs
879 * @return {Promise} Resolves: Object as exmaple below
880 *
881 * {
882 * "users": {
883 * "@bob:example.com": {
884 * "+example:example.com"
885 * }
886 * }
887 * }
888 * @return {module:http-api.MatrixError} Rejects: with an error response.
889 */
890MatrixBaseApis.prototype.getPublicisedGroups = function(userIds) {
891 const path = utils.encodeUri("/publicised_groups");
892 return this._http.authedRequest(
893 undefined, "POST", path, undefined, { user_ids: userIds },
894 );
895};
896
897/**
898 * @param {string} groupId
899 * @param {bool} isPublic Whether the user's membership of this group is made public
900 * @return {Promise} Resolves: Empty object
901 * @return {module:http-api.MatrixError} Rejects: with an error response.
902 */
903MatrixBaseApis.prototype.setGroupPublicity = function(groupId, isPublic) {
904 const path = utils.encodeUri(
905 "/groups/$groupId/self/update_publicity",
906 {$groupId: groupId},
907 );
908 return this._http.authedRequest(undefined, "PUT", path, undefined, {
909 publicise: isPublic,
910 });
911};
912
913/**
914 * Retrieve a state event.
915 * @param {string} roomId
916 * @param {string} eventType
917 * @param {string} stateKey
918 * @param {module:client.callback} callback Optional.
919 * @return {Promise} Resolves: TODO
920 * @return {module:http-api.MatrixError} Rejects: with an error response.
921 */
922MatrixBaseApis.prototype.getStateEvent = function(roomId, eventType, stateKey, callback) {
923 const pathParams = {
924 $roomId: roomId,
925 $eventType: eventType,
926 $stateKey: stateKey,
927 };
928 let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
929 if (stateKey !== undefined) {
930 path = utils.encodeUri(path + "/$stateKey", pathParams);
931 }
932 return this._http.authedRequest(
933 callback, "GET", path,
934 );
935};
936
937/**
938 * @param {string} roomId
939 * @param {string} eventType
940 * @param {Object} content
941 * @param {string} stateKey
942 * @param {module:client.callback} callback Optional.
943 * @return {Promise} Resolves: TODO
944 * @return {module:http-api.MatrixError} Rejects: with an error response.
945 */
946MatrixBaseApis.prototype.sendStateEvent = function(roomId, eventType, content, stateKey,
947 callback) {
948 const pathParams = {
949 $roomId: roomId,
950 $eventType: eventType,
951 $stateKey: stateKey,
952 };
953 let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
954 if (stateKey !== undefined) {
955 path = utils.encodeUri(path + "/$stateKey", pathParams);
956 }
957 return this._http.authedRequest(
958 callback, "PUT", path, undefined, content,
959 );
960};
961
962/**
963 * @param {string} roomId
964 * @param {Number} limit
965 * @param {module:client.callback} callback Optional.
966 * @return {Promise} Resolves: TODO
967 * @return {module:http-api.MatrixError} Rejects: with an error response.
968 */
969MatrixBaseApis.prototype.roomInitialSync = function(roomId, limit, callback) {
970 if (utils.isFunction(limit)) {
971 callback = limit; limit = undefined;
972 }
973 const path = utils.encodeUri("/rooms/$roomId/initialSync",
974 {$roomId: roomId},
975 );
976 if (!limit) {
977 limit = 30;
978 }
979 return this._http.authedRequest(
980 callback, "GET", path, { limit: limit },
981 );
982};
983
984/**
985 * Set a marker to indicate the point in a room before which the user has read every
986 * event. This can be retrieved from room account data (the event type is `m.fully_read`)
987 * and displayed as a horizontal line in the timeline that is visually distinct to the
988 * position of the user's own read receipt.
989 * @param {string} roomId ID of the room that has been read
990 * @param {string} rmEventId ID of the event that has been read
991 * @param {string} rrEventId ID of the event tracked by the read receipt. This is here
992 * for convenience because the RR and the RM are commonly updated at the same time as
993 * each other. Optional.
994 * @param {object} opts Options for the read markers.
995 * @param {object} opts.hidden True to hide the read receipt from other users. <b>This
996 * property is currently unstable and may change in the future.</b>
997 * @return {Promise} Resolves: the empty object, {}.
998 */
999MatrixBaseApis.prototype.setRoomReadMarkersHttpRequest =
1000 function(roomId, rmEventId, rrEventId, opts) {
1001 const path = utils.encodeUri("/rooms/$roomId/read_markers", {
1002 $roomId: roomId,
1003 });
1004
1005 const content = {
1006 "m.fully_read": rmEventId,
1007 "m.read": rrEventId,
1008 "m.hidden": Boolean(opts ? opts.hidden : false),
1009 };
1010
1011 return this._http.authedRequest(
1012 undefined, "POST", path, undefined, content,
1013 );
1014};
1015
1016/**
1017 * @return {Promise} Resolves: A list of the user's current rooms
1018 * @return {module:http-api.MatrixError} Rejects: with an error response.
1019 */
1020MatrixBaseApis.prototype.getJoinedRooms = function() {
1021 const path = utils.encodeUri("/joined_rooms");
1022 return this._http.authedRequest(undefined, "GET", path);
1023};
1024
1025/**
1026 * Retrieve membership info. for a room.
1027 * @param {string} roomId ID of the room to get membership for
1028 * @return {Promise} Resolves: A list of currently joined users
1029 * and their profile data.
1030 * @return {module:http-api.MatrixError} Rejects: with an error response.
1031 */
1032MatrixBaseApis.prototype.getJoinedRoomMembers = function(roomId) {
1033 const path = utils.encodeUri("/rooms/$roomId/joined_members", {
1034 $roomId: roomId,
1035 });
1036 return this._http.authedRequest(undefined, "GET", path);
1037};
1038
1039// Room Directory operations
1040// =========================
1041
1042/**
1043 * @param {Object} options Options for this request
1044 * @param {string} options.server The remote server to query for the room list.
1045 * Optional. If unspecified, get the local home
1046 * server's public room list.
1047 * @param {number} options.limit Maximum number of entries to return
1048 * @param {string} options.since Token to paginate from
1049 * @param {object} options.filter Filter parameters
1050 * @param {string} options.filter.generic_search_term String to search for
1051 * @param {module:client.callback} callback Optional.
1052 * @return {Promise} Resolves: TODO
1053 * @return {module:http-api.MatrixError} Rejects: with an error response.
1054 */
1055MatrixBaseApis.prototype.publicRooms = function(options, callback) {
1056 if (typeof(options) == 'function') {
1057 callback = options;
1058 options = {};
1059 }
1060 if (options === undefined) {
1061 options = {};
1062 }
1063
1064 const query_params = {};
1065 if (options.server) {
1066 query_params.server = options.server;
1067 delete options.server;
1068 }
1069
1070 if (Object.keys(options).length === 0 && Object.keys(query_params).length === 0) {
1071 return this._http.authedRequest(callback, "GET", "/publicRooms");
1072 } else {
1073 return this._http.authedRequest(
1074 callback, "POST", "/publicRooms", query_params, options,
1075 );
1076 }
1077};
1078
1079/**
1080 * Create an alias to room ID mapping.
1081 * @param {string} alias The room alias to create.
1082 * @param {string} roomId The room ID to link the alias to.
1083 * @param {module:client.callback} callback Optional.
1084 * @return {Promise} Resolves: TODO.
1085 * @return {module:http-api.MatrixError} Rejects: with an error response.
1086 */
1087MatrixBaseApis.prototype.createAlias = function(alias, roomId, callback) {
1088 const path = utils.encodeUri("/directory/room/$alias", {
1089 $alias: alias,
1090 });
1091 const data = {
1092 room_id: roomId,
1093 };
1094 return this._http.authedRequest(
1095 callback, "PUT", path, undefined, data,
1096 );
1097};
1098
1099/**
1100 * Delete an alias to room ID mapping. This alias must be on your local server
1101 * and you must have sufficient access to do this operation.
1102 * @param {string} alias The room alias to delete.
1103 * @param {module:client.callback} callback Optional.
1104 * @return {Promise} Resolves: TODO.
1105 * @return {module:http-api.MatrixError} Rejects: with an error response.
1106 */
1107MatrixBaseApis.prototype.deleteAlias = function(alias, callback) {
1108 const path = utils.encodeUri("/directory/room/$alias", {
1109 $alias: alias,
1110 });
1111 return this._http.authedRequest(
1112 callback, "DELETE", path, undefined, undefined,
1113 );
1114};
1115
1116/**
1117 * @param {string} roomId
1118 * @param {module:client.callback} callback Optional.
1119 * @return {Promise} Resolves: an object with an `aliases` property, containing an array of local aliases
1120 * @return {module:http-api.MatrixError} Rejects: with an error response.
1121 */
1122MatrixBaseApis.prototype.unstableGetLocalAliases =
1123function(roomId, callback) {
1124 const path = utils.encodeUri("/rooms/$roomId/aliases",
1125 {$roomId: roomId});
1126 const prefix = PREFIX_UNSTABLE + "/org.matrix.msc2432";
1127 return this._http.authedRequest(callback, "GET", path,
1128 null, null, { prefix });
1129};
1130
1131/**
1132 * Get room info for the given alias.
1133 * @param {string} alias The room alias to resolve.
1134 * @param {module:client.callback} callback Optional.
1135 * @return {Promise} Resolves: Object with room_id and servers.
1136 * @return {module:http-api.MatrixError} Rejects: with an error response.
1137 */
1138MatrixBaseApis.prototype.getRoomIdForAlias = function(alias, callback) {
1139 // TODO: deprecate this or resolveRoomAlias
1140 const path = utils.encodeUri("/directory/room/$alias", {
1141 $alias: alias,
1142 });
1143 return this._http.authedRequest(
1144 callback, "GET", path,
1145 );
1146};
1147
1148/**
1149 * @param {string} roomAlias
1150 * @param {module:client.callback} callback Optional.
1151 * @return {Promise} Resolves: TODO
1152 * @return {module:http-api.MatrixError} Rejects: with an error response.
1153 */
1154MatrixBaseApis.prototype.resolveRoomAlias = function(roomAlias, callback) {
1155 // TODO: deprecate this or getRoomIdForAlias
1156 const path = utils.encodeUri("/directory/room/$alias", {$alias: roomAlias});
1157 return this._http.request(callback, "GET", path);
1158};
1159
1160/**
1161 * Get the visibility of a room in the current HS's room directory
1162 * @param {string} roomId
1163 * @param {module:client.callback} callback Optional.
1164 * @return {Promise} Resolves: TODO
1165 * @return {module:http-api.MatrixError} Rejects: with an error response.
1166 */
1167MatrixBaseApis.prototype.getRoomDirectoryVisibility =
1168 function(roomId, callback) {
1169 const path = utils.encodeUri("/directory/list/room/$roomId", {
1170 $roomId: roomId,
1171 });
1172 return this._http.authedRequest(callback, "GET", path);
1173};
1174
1175/**
1176 * Set the visbility of a room in the current HS's room directory
1177 * @param {string} roomId
1178 * @param {string} visibility "public" to make the room visible
1179 * in the public directory, or "private" to make
1180 * it invisible.
1181 * @param {module:client.callback} callback Optional.
1182 * @return {Promise} Resolves: result object
1183 * @return {module:http-api.MatrixError} Rejects: with an error response.
1184 */
1185MatrixBaseApis.prototype.setRoomDirectoryVisibility =
1186 function(roomId, visibility, callback) {
1187 const path = utils.encodeUri("/directory/list/room/$roomId", {
1188 $roomId: roomId,
1189 });
1190 return this._http.authedRequest(
1191 callback, "PUT", path, undefined, { "visibility": visibility },
1192 );
1193};
1194
1195/**
1196 * Set the visbility of a room bridged to a 3rd party network in
1197 * the current HS's room directory.
1198 * @param {string} networkId the network ID of the 3rd party
1199 * instance under which this room is published under.
1200 * @param {string} roomId
1201 * @param {string} visibility "public" to make the room visible
1202 * in the public directory, or "private" to make
1203 * it invisible.
1204 * @param {module:client.callback} callback Optional.
1205 * @return {Promise} Resolves: result object
1206 * @return {module:http-api.MatrixError} Rejects: with an error response.
1207 */
1208MatrixBaseApis.prototype.setRoomDirectoryVisibilityAppService =
1209 function(networkId, roomId, visibility, callback) {
1210 const path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", {
1211 $networkId: networkId,
1212 $roomId: roomId,
1213 });
1214 return this._http.authedRequest(
1215 callback, "PUT", path, undefined, { "visibility": visibility },
1216 );
1217};
1218
1219// User Directory Operations
1220// =========================
1221
1222/**
1223 * Query the user directory with a term matching user IDs, display names and domains.
1224 * @param {object} opts options
1225 * @param {string} opts.term the term with which to search.
1226 * @param {number} opts.limit the maximum number of results to return. The server will
1227 * apply a limit if unspecified.
1228 * @return {Promise} Resolves: an array of results.
1229 */
1230MatrixBaseApis.prototype.searchUserDirectory = function(opts) {
1231 const body = {
1232 search_term: opts.term,
1233 };
1234
1235 if (opts.limit !== undefined) {
1236 body.limit = opts.limit;
1237 }
1238
1239 return this._http.authedRequest(
1240 undefined, "POST", "/user_directory/search", undefined, body,
1241 );
1242};
1243
1244
1245// Media operations
1246// ================
1247
1248/**
1249 * Upload a file to the media repository on the home server.
1250 *
1251 * @param {object} file The object to upload. On a browser, something that
1252 * can be sent to XMLHttpRequest.send (typically a File). Under node.js,
1253 * a a Buffer, String or ReadStream.
1254 *
1255 * @param {object} opts options object
1256 *
1257 * @param {string=} opts.name Name to give the file on the server. Defaults
1258 * to <tt>file.name</tt>.
1259 *
1260 * @param {boolean=} opts.includeFilename if false will not send the filename,
1261 * e.g for encrypted file uploads where filename leaks are undesirable.
1262 * Defaults to true.
1263 *
1264 * @param {string=} opts.type Content-type for the upload. Defaults to
1265 * <tt>file.type</tt>, or <tt>applicaton/octet-stream</tt>.
1266 *
1267 * @param {boolean=} opts.rawResponse Return the raw body, rather than
1268 * parsing the JSON. Defaults to false (except on node.js, where it
1269 * defaults to true for backwards compatibility).
1270 *
1271 * @param {boolean=} opts.onlyContentUri Just return the content URI,
1272 * rather than the whole body. Defaults to false (except on browsers,
1273 * where it defaults to true for backwards compatibility). Ignored if
1274 * opts.rawResponse is true.
1275 *
1276 * @param {Function=} opts.callback Deprecated. Optional. The callback to
1277 * invoke on success/failure. See the promise return values for more
1278 * information.
1279 *
1280 * @param {Function=} opts.progressHandler Optional. Called when a chunk of
1281 * data has been uploaded, with an object containing the fields `loaded`
1282 * (number of bytes transferred) and `total` (total size, if known).
1283 *
1284 * @return {Promise} Resolves to response object, as
1285 * determined by this.opts.onlyData, opts.rawResponse, and
1286 * opts.onlyContentUri. Rejects with an error (usually a MatrixError).
1287 */
1288MatrixBaseApis.prototype.uploadContent = function(file, opts) {
1289 return this._http.uploadContent(file, opts);
1290};
1291
1292/**
1293 * Cancel a file upload in progress
1294 * @param {Promise} promise The promise returned from uploadContent
1295 * @return {boolean} true if canceled, otherwise false
1296 */
1297MatrixBaseApis.prototype.cancelUpload = function(promise) {
1298 return this._http.cancelUpload(promise);
1299};
1300
1301/**
1302 * Get a list of all file uploads in progress
1303 * @return {array} Array of objects representing current uploads.
1304 * Currently in progress is element 0. Keys:
1305 * - promise: The promise associated with the upload
1306 * - loaded: Number of bytes uploaded
1307 * - total: Total number of bytes to upload
1308 */
1309MatrixBaseApis.prototype.getCurrentUploads = function() {
1310 return this._http.getCurrentUploads();
1311};
1312
1313
1314// Profile operations
1315// ==================
1316
1317/**
1318 * @param {string} userId
1319 * @param {string} info The kind of info to retrieve (e.g. 'displayname',
1320 * 'avatar_url').
1321 * @param {module:client.callback} callback Optional.
1322 * @return {Promise} Resolves: TODO
1323 * @return {module:http-api.MatrixError} Rejects: with an error response.
1324 */
1325MatrixBaseApis.prototype.getProfileInfo = function(userId, info, callback) {
1326 if (utils.isFunction(info)) {
1327 callback = info; info = undefined;
1328 }
1329
1330 const path = info ?
1331 utils.encodeUri("/profile/$userId/$info",
1332 { $userId: userId, $info: info }) :
1333 utils.encodeUri("/profile/$userId",
1334 { $userId: userId });
1335 return this._http.authedRequest(callback, "GET", path);
1336};
1337
1338
1339// Account operations
1340// ==================
1341
1342/**
1343 * @param {module:client.callback} callback Optional.
1344 * @return {Promise} Resolves: TODO
1345 * @return {module:http-api.MatrixError} Rejects: with an error response.
1346 */
1347MatrixBaseApis.prototype.getThreePids = function(callback) {
1348 const path = "/account/3pid";
1349 return this._http.authedRequest(
1350 callback, "GET", path, undefined, undefined,
1351 );
1352};
1353
1354/**
1355 * Add a 3PID to your homeserver account and optionally bind it to an identity
1356 * server as well. An identity server is required as part of the `creds` object.
1357 *
1358 * This API is deprecated, and you should instead use `addThreePidOnly`
1359 * for homeservers that support it.
1360 *
1361 * @param {Object} creds
1362 * @param {boolean} bind
1363 * @param {module:client.callback} callback Optional.
1364 * @return {Promise} Resolves: on success
1365 * @return {module:http-api.MatrixError} Rejects: with an error response.
1366 */
1367MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
1368 const path = "/account/3pid";
1369 const data = {
1370 'threePidCreds': creds,
1371 'bind': bind,
1372 };
1373 return this._http.authedRequest(
1374 callback, "POST", path, null, data,
1375 );
1376};
1377
1378/**
1379 * Add a 3PID to your homeserver account. This API does not use an identity
1380 * server, as the homeserver is expected to handle 3PID ownership validation.
1381 *
1382 * You can check whether a homeserver supports this API via
1383 * `doesServerSupportSeparateAddAndBind`.
1384 *
1385 * @param {Object} data A object with 3PID validation data from having called
1386 * `account/3pid/<medium>/requestToken` on the homeserver.
1387 * @return {Promise} Resolves: on success
1388 * @return {module:http-api.MatrixError} Rejects: with an error response.
1389 */
1390MatrixBaseApis.prototype.addThreePidOnly = async function(data) {
1391 const path = "/account/3pid/add";
1392 const prefix = await this.isVersionSupported("r0.6.0") ?
1393 PREFIX_R0 : PREFIX_UNSTABLE;
1394 return this._http.authedRequest(
1395 undefined, "POST", path, null, data, { prefix },
1396 );
1397};
1398
1399/**
1400 * Bind a 3PID for discovery onto an identity server via the homeserver. The
1401 * identity server handles 3PID ownership validation and the homeserver records
1402 * the new binding to track where all 3PIDs for the account are bound.
1403 *
1404 * You can check whether a homeserver supports this API via
1405 * `doesServerSupportSeparateAddAndBind`.
1406 *
1407 * @param {Object} data A object with 3PID validation data from having called
1408 * `validate/<medium>/requestToken` on the identity server. It should also
1409 * contain `id_server` and `id_access_token` fields as well.
1410 * @return {Promise} Resolves: on success
1411 * @return {module:http-api.MatrixError} Rejects: with an error response.
1412 */
1413MatrixBaseApis.prototype.bindThreePid = async function(data) {
1414 const path = "/account/3pid/bind";
1415 const prefix = await this.isVersionSupported("r0.6.0") ?
1416 PREFIX_R0 : PREFIX_UNSTABLE;
1417 return this._http.authedRequest(
1418 undefined, "POST", path, null, data, { prefix },
1419 );
1420};
1421
1422/**
1423 * Unbind a 3PID for discovery on an identity server via the homeserver. The
1424 * homeserver removes its record of the binding to keep an updated record of
1425 * where all 3PIDs for the account are bound.
1426 *
1427 * @param {string} medium The threepid medium (eg. 'email')
1428 * @param {string} address The threepid address (eg. 'bob@example.com')
1429 * this must be as returned by getThreePids.
1430 * @return {Promise} Resolves: on success
1431 * @return {module:http-api.MatrixError} Rejects: with an error response.
1432 */
1433MatrixBaseApis.prototype.unbindThreePid = async function(medium, address) {
1434 const path = "/account/3pid/unbind";
1435 const data = {
1436 medium,
1437 address,
1438 id_server: this.getIdentityServerUrl(true),
1439 };
1440 const prefix = await this.isVersionSupported("r0.6.0") ?
1441 PREFIX_R0 : PREFIX_UNSTABLE;
1442 return this._http.authedRequest(
1443 undefined, "POST", path, null, data, { prefix },
1444 );
1445};
1446
1447/**
1448 * @param {string} medium The threepid medium (eg. 'email')
1449 * @param {string} address The threepid address (eg. 'bob@example.com')
1450 * this must be as returned by getThreePids.
1451 * @return {Promise} Resolves: The server response on success
1452 * (generally the empty JSON object)
1453 * @return {module:http-api.MatrixError} Rejects: with an error response.
1454 */
1455MatrixBaseApis.prototype.deleteThreePid = function(medium, address) {
1456 const path = "/account/3pid/delete";
1457 const data = {
1458 'medium': medium,
1459 'address': address,
1460 };
1461 return this._http.authedRequest(undefined, "POST", path, null, data);
1462};
1463
1464/**
1465 * Make a request to change your password.
1466 * @param {Object} authDict
1467 * @param {string} newPassword The new desired password.
1468 * @param {module:client.callback} callback Optional.
1469 * @return {Promise} Resolves: TODO
1470 * @return {module:http-api.MatrixError} Rejects: with an error response.
1471 */
1472MatrixBaseApis.prototype.setPassword = function(authDict, newPassword, callback) {
1473 const path = "/account/password";
1474 const data = {
1475 'auth': authDict,
1476 'new_password': newPassword,
1477 };
1478
1479 return this._http.authedRequest(
1480 callback, "POST", path, null, data,
1481 );
1482};
1483
1484
1485// Device operations
1486// =================
1487
1488/**
1489 * Gets all devices recorded for the logged-in user
1490 * @return {Promise} Resolves: result object
1491 * @return {module:http-api.MatrixError} Rejects: with an error response.
1492 */
1493MatrixBaseApis.prototype.getDevices = function() {
1494 return this._http.authedRequest(
1495 undefined, 'GET', "/devices", undefined, undefined,
1496 );
1497};
1498
1499/**
1500 * Update the given device
1501 *
1502 * @param {string} device_id device to update
1503 * @param {Object} body body of request
1504 * @return {Promise} Resolves: result object
1505 * @return {module:http-api.MatrixError} Rejects: with an error response.
1506 */
1507MatrixBaseApis.prototype.setDeviceDetails = function(device_id, body) {
1508 const path = utils.encodeUri("/devices/$device_id", {
1509 $device_id: device_id,
1510 });
1511
1512 return this._http.authedRequest(undefined, "PUT", path, undefined, body);
1513};
1514
1515/**
1516 * Delete the given device
1517 *
1518 * @param {string} device_id device to delete
1519 * @param {object} auth Optional. Auth data to supply for User-Interactive auth.
1520 * @return {Promise} Resolves: result object
1521 * @return {module:http-api.MatrixError} Rejects: with an error response.
1522 */
1523MatrixBaseApis.prototype.deleteDevice = function(device_id, auth) {
1524 const path = utils.encodeUri("/devices/$device_id", {
1525 $device_id: device_id,
1526 });
1527
1528 const body = {};
1529
1530 if (auth) {
1531 body.auth = auth;
1532 }
1533
1534 return this._http.authedRequest(undefined, "DELETE", path, undefined, body);
1535};
1536
1537/**
1538 * Delete multiple device
1539 *
1540 * @param {string[]} devices IDs of the devices to delete
1541 * @param {object} auth Optional. Auth data to supply for User-Interactive auth.
1542 * @return {Promise} Resolves: result object
1543 * @return {module:http-api.MatrixError} Rejects: with an error response.
1544 */
1545MatrixBaseApis.prototype.deleteMultipleDevices = function(devices, auth) {
1546 const body = {devices};
1547
1548 if (auth) {
1549 body.auth = auth;
1550 }
1551
1552 const path = "/delete_devices";
1553 return this._http.authedRequest(undefined, "POST", path, undefined, body);
1554};
1555
1556
1557// Push operations
1558// ===============
1559
1560/**
1561 * Gets all pushers registered for the logged-in user
1562 *
1563 * @param {module:client.callback} callback Optional.
1564 * @return {Promise} Resolves: Array of objects representing pushers
1565 * @return {module:http-api.MatrixError} Rejects: with an error response.
1566 */
1567MatrixBaseApis.prototype.getPushers = function(callback) {
1568 const path = "/pushers";
1569 return this._http.authedRequest(
1570 callback, "GET", path, undefined, undefined,
1571 );
1572};
1573
1574/**
1575 * Adds a new pusher or updates an existing pusher
1576 *
1577 * @param {Object} pusher Object representing a pusher
1578 * @param {module:client.callback} callback Optional.
1579 * @return {Promise} Resolves: Empty json object on success
1580 * @return {module:http-api.MatrixError} Rejects: with an error response.
1581 */
1582MatrixBaseApis.prototype.setPusher = function(pusher, callback) {
1583 const path = "/pushers/set";
1584 return this._http.authedRequest(
1585 callback, "POST", path, null, pusher,
1586 );
1587};
1588
1589/**
1590 * @param {module:client.callback} callback Optional.
1591 * @return {Promise} Resolves: TODO
1592 * @return {module:http-api.MatrixError} Rejects: with an error response.
1593 */
1594MatrixBaseApis.prototype.getPushRules = function(callback) {
1595 return this._http.authedRequest(callback, "GET", "/pushrules/").then(rules => {
1596 return PushProcessor.rewriteDefaultRules(rules);
1597 });
1598};
1599
1600/**
1601 * @param {string} scope
1602 * @param {string} kind
1603 * @param {string} ruleId
1604 * @param {Object} body
1605 * @param {module:client.callback} callback Optional.
1606 * @return {Promise} Resolves: TODO
1607 * @return {module:http-api.MatrixError} Rejects: with an error response.
1608 */
1609MatrixBaseApis.prototype.addPushRule = function(scope, kind, ruleId, body, callback) {
1610 // NB. Scope not uri encoded because devices need the '/'
1611 const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
1612 $kind: kind,
1613 $ruleId: ruleId,
1614 });
1615 return this._http.authedRequest(
1616 callback, "PUT", path, undefined, body,
1617 );
1618};
1619
1620/**
1621 * @param {string} scope
1622 * @param {string} kind
1623 * @param {string} ruleId
1624 * @param {module:client.callback} callback Optional.
1625 * @return {Promise} Resolves: TODO
1626 * @return {module:http-api.MatrixError} Rejects: with an error response.
1627 */
1628MatrixBaseApis.prototype.deletePushRule = function(scope, kind, ruleId, callback) {
1629 // NB. Scope not uri encoded because devices need the '/'
1630 const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
1631 $kind: kind,
1632 $ruleId: ruleId,
1633 });
1634 return this._http.authedRequest(callback, "DELETE", path);
1635};
1636
1637/**
1638 * Enable or disable a push notification rule.
1639 * @param {string} scope
1640 * @param {string} kind
1641 * @param {string} ruleId
1642 * @param {boolean} enabled
1643 * @param {module:client.callback} callback Optional.
1644 * @return {Promise} Resolves: result object
1645 * @return {module:http-api.MatrixError} Rejects: with an error response.
1646 */
1647MatrixBaseApis.prototype.setPushRuleEnabled = function(scope, kind,
1648 ruleId, enabled, callback) {
1649 const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", {
1650 $kind: kind,
1651 $ruleId: ruleId,
1652 });
1653 return this._http.authedRequest(
1654 callback, "PUT", path, undefined, {"enabled": enabled},
1655 );
1656};
1657
1658/**
1659 * Set the actions for a push notification rule.
1660 * @param {string} scope
1661 * @param {string} kind
1662 * @param {string} ruleId
1663 * @param {array} actions
1664 * @param {module:client.callback} callback Optional.
1665 * @return {Promise} Resolves: result object
1666 * @return {module:http-api.MatrixError} Rejects: with an error response.
1667 */
1668MatrixBaseApis.prototype.setPushRuleActions = function(scope, kind,
1669 ruleId, actions, callback) {
1670 const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/actions", {
1671 $kind: kind,
1672 $ruleId: ruleId,
1673 });
1674 return this._http.authedRequest(
1675 callback, "PUT", path, undefined, {"actions": actions},
1676 );
1677};
1678
1679
1680// Search
1681// ======
1682
1683/**
1684 * Perform a server-side search.
1685 * @param {Object} opts
1686 * @param {string} opts.next_batch the batch token to pass in the query string
1687 * @param {Object} opts.body the JSON object to pass to the request body.
1688 * @param {module:client.callback} callback Optional.
1689 * @return {Promise} Resolves: TODO
1690 * @return {module:http-api.MatrixError} Rejects: with an error response.
1691 */
1692MatrixBaseApis.prototype.search = function(opts, callback) {
1693 const queryparams = {};
1694 if (opts.next_batch) {
1695 queryparams.next_batch = opts.next_batch;
1696 }
1697 return this._http.authedRequest(
1698 callback, "POST", "/search", queryparams, opts.body,
1699 );
1700};
1701
1702// Crypto
1703// ======
1704
1705/**
1706 * Upload keys
1707 *
1708 * @param {Object} content body of upload request
1709 *
1710 * @param {Object=} opts this method no longer takes any opts,
1711 * used to take opts.device_id but this was not removed from the spec as a redundant parameter
1712 *
1713 * @param {module:client.callback=} callback
1714 *
1715 * @return {Promise} Resolves: result object. Rejects: with
1716 * an error response ({@link module:http-api.MatrixError}).
1717 */
1718MatrixBaseApis.prototype.uploadKeysRequest = function(content, opts, callback) {
1719 return this._http.authedRequest(callback, "POST", "/keys/upload", undefined, content);
1720};
1721
1722MatrixBaseApis.prototype.uploadKeySignatures = function(content) {
1723 return this._http.authedRequest(
1724 undefined, "POST", '/keys/signatures/upload', undefined,
1725 content, {
1726 prefix: PREFIX_UNSTABLE,
1727 },
1728 );
1729};
1730
1731/**
1732 * Download device keys
1733 *
1734 * @param {string[]} userIds list of users to get keys for
1735 *
1736 * @param {Object=} opts
1737 *
1738 * @param {string=} opts.token sync token to pass in the query request, to help
1739 * the HS give the most recent results
1740 *
1741 * @return {Promise} Resolves: result object. Rejects: with
1742 * an error response ({@link module:http-api.MatrixError}).
1743 */
1744MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, opts) {
1745 if (utils.isFunction(opts)) {
1746 // opts used to be 'callback'.
1747 throw new Error(
1748 'downloadKeysForUsers no longer accepts a callback parameter',
1749 );
1750 }
1751 opts = opts || {};
1752
1753 const content = {
1754 device_keys: {},
1755 };
1756 if ('token' in opts) {
1757 content.token = opts.token;
1758 }
1759 userIds.forEach((u) => {
1760 content.device_keys[u] = {};
1761 });
1762
1763 return this._http.authedRequest(undefined, "POST", "/keys/query", undefined, content);
1764};
1765
1766/**
1767 * Claim one-time keys
1768 *
1769 * @param {string[]} devices a list of [userId, deviceId] pairs
1770 *
1771 * @param {string} [key_algorithm = signed_curve25519] desired key type
1772 *
1773 * @param {number} [timeout] the time (in milliseconds) to wait for keys from remote
1774 * servers
1775 *
1776 * @return {Promise} Resolves: result object. Rejects: with
1777 * an error response ({@link module:http-api.MatrixError}).
1778 */
1779MatrixBaseApis.prototype.claimOneTimeKeys = function(devices, key_algorithm, timeout) {
1780 const queries = {};
1781
1782 if (key_algorithm === undefined) {
1783 key_algorithm = "signed_curve25519";
1784 }
1785
1786 for (let i = 0; i < devices.length; ++i) {
1787 const userId = devices[i][0];
1788 const deviceId = devices[i][1];
1789 const query = queries[userId] || {};
1790 queries[userId] = query;
1791 query[deviceId] = key_algorithm;
1792 }
1793 const content = {one_time_keys: queries};
1794 if (timeout) {
1795 content.timeout = timeout;
1796 }
1797 const path = "/keys/claim";
1798 return this._http.authedRequest(undefined, "POST", path, undefined, content);
1799};
1800
1801/**
1802 * Ask the server for a list of users who have changed their device lists
1803 * between a pair of sync tokens
1804 *
1805 * @param {string} oldToken
1806 * @param {string} newToken
1807 *
1808 * @return {Promise} Resolves: result object. Rejects: with
1809 * an error response ({@link module:http-api.MatrixError}).
1810 */
1811MatrixBaseApis.prototype.getKeyChanges = function(oldToken, newToken) {
1812 const qps = {
1813 from: oldToken,
1814 to: newToken,
1815 };
1816
1817 const path = "/keys/changes";
1818 return this._http.authedRequest(undefined, "GET", path, qps, undefined);
1819};
1820
1821MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
1822 const data = Object.assign({}, keys);
1823 if (auth) Object.assign(data, {auth});
1824 return this._http.authedRequest(
1825 undefined, "POST", "/keys/device_signing/upload", undefined, data, {
1826 prefix: PREFIX_UNSTABLE,
1827 },
1828 );
1829};
1830
1831// Identity Server Operations
1832// ==========================
1833
1834/**
1835 * Register with an Identity Server using the OpenID token from the user's
1836 * Homeserver, which can be retrieved via
1837 * {@link module:client~MatrixClient#getOpenIdToken}.
1838 *
1839 * Note that the `/account/register` endpoint (as well as IS authentication in
1840 * general) was added as part of the v2 API version.
1841 *
1842 * @param {object} hsOpenIdToken
1843 * @return {Promise} Resolves: with object containing an Identity
1844 * Server access token.
1845 * @return {module:http-api.MatrixError} Rejects: with an error response.
1846 */
1847MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) {
1848 if (!this.idBaseUrl) {
1849 throw new Error("No Identity Server base URL set");
1850 }
1851
1852 const uri = this.idBaseUrl + PREFIX_IDENTITY_V2 + "/account/register";
1853 return this._http.requestOtherUrl(
1854 undefined, "POST", uri,
1855 null, hsOpenIdToken,
1856 );
1857};
1858
1859/**
1860 * Requests an email verification token directly from an identity server.
1861 *
1862 * This API is used as part of binding an email for discovery on an identity
1863 * server. The validation data that results should be passed to the
1864 * `bindThreePid` method to complete the binding process.
1865 *
1866 * @param {string} email The email address to request a token for
1867 * @param {string} clientSecret A secret binary string generated by the client.
1868 * It is recommended this be around 16 ASCII characters.
1869 * @param {number} sendAttempt If an identity server sees a duplicate request
1870 * with the same sendAttempt, it will not send another email.
1871 * To request another email to be sent, use a larger value for
1872 * the sendAttempt param as was used in the previous request.
1873 * @param {string} nextLink Optional If specified, the client will be redirected
1874 * to this link after validation.
1875 * @param {module:client.callback} callback Optional.
1876 * @param {string} identityAccessToken The `access_token` field of the identity
1877 * server `/account/register` response (see {@link registerWithIdentityServer}).
1878 *
1879 * @return {Promise} Resolves: TODO
1880 * @return {module:http-api.MatrixError} Rejects: with an error response.
1881 * @throws Error if no identity server is set
1882 */
1883MatrixBaseApis.prototype.requestEmailToken = async function(
1884 email,
1885 clientSecret,
1886 sendAttempt,
1887 nextLink,
1888 callback,
1889 identityAccessToken,
1890) {
1891 const params = {
1892 client_secret: clientSecret,
1893 email: email,
1894 send_attempt: sendAttempt,
1895 next_link: nextLink,
1896 };
1897
1898 return await this._http.idServerRequest(
1899 callback, "POST", "/validate/email/requestToken",
1900 params, PREFIX_IDENTITY_V2, identityAccessToken,
1901 );
1902};
1903
1904/**
1905 * Requests a MSISDN verification token directly from an identity server.
1906 *
1907 * This API is used as part of binding a MSISDN for discovery on an identity
1908 * server. The validation data that results should be passed to the
1909 * `bindThreePid` method to complete the binding process.
1910 *
1911 * @param {string} phoneCountry The ISO 3166-1 alpha-2 code for the country in
1912 * which phoneNumber should be parsed relative to.
1913 * @param {string} phoneNumber The phone number, in national or international
1914 * format
1915 * @param {string} clientSecret A secret binary string generated by the client.
1916 * It is recommended this be around 16 ASCII characters.
1917 * @param {number} sendAttempt If an identity server sees a duplicate request
1918 * with the same sendAttempt, it will not send another SMS.
1919 * To request another SMS to be sent, use a larger value for
1920 * the sendAttempt param as was used in the previous request.
1921 * @param {string} nextLink Optional If specified, the client will be redirected
1922 * to this link after validation.
1923 * @param {module:client.callback} callback Optional.
1924 * @param {string} identityAccessToken The `access_token` field of the Identity
1925 * Server `/account/register` response (see {@link registerWithIdentityServer}).
1926 *
1927 * @return {Promise} Resolves: TODO
1928 * @return {module:http-api.MatrixError} Rejects: with an error response.
1929 * @throws Error if no identity server is set
1930 */
1931MatrixBaseApis.prototype.requestMsisdnToken = async function(
1932 phoneCountry,
1933 phoneNumber,
1934 clientSecret,
1935 sendAttempt,
1936 nextLink,
1937 callback,
1938 identityAccessToken,
1939) {
1940 const params = {
1941 client_secret: clientSecret,
1942 country: phoneCountry,
1943 phone_number: phoneNumber,
1944 send_attempt: sendAttempt,
1945 next_link: nextLink,
1946 };
1947
1948 return await this._http.idServerRequest(
1949 callback, "POST", "/validate/msisdn/requestToken",
1950 params, PREFIX_IDENTITY_V2, identityAccessToken,
1951 );
1952};
1953
1954/**
1955 * Submits a MSISDN token to the identity server
1956 *
1957 * This is used when submitting the code sent by SMS to a phone number.
1958 * The ID server has an equivalent API for email but the js-sdk does
1959 * not expose this, since email is normally validated by the user clicking
1960 * a link rather than entering a code.
1961 *
1962 * @param {string} sid The sid given in the response to requestToken
1963 * @param {string} clientSecret A secret binary string generated by the client.
1964 * This must be the same value submitted in the requestToken call.
1965 * @param {string} msisdnToken The MSISDN token, as enetered by the user.
1966 * @param {string} identityAccessToken The `access_token` field of the Identity
1967 * Server `/account/register` response (see {@link registerWithIdentityServer}).
1968 *
1969 * @return {Promise} Resolves: Object, currently with no parameters.
1970 * @return {module:http-api.MatrixError} Rejects: with an error response.
1971 * @throws Error if No ID server is set
1972 */
1973MatrixBaseApis.prototype.submitMsisdnToken = async function(
1974 sid,
1975 clientSecret,
1976 msisdnToken,
1977 identityAccessToken,
1978) {
1979 const params = {
1980 sid: sid,
1981 client_secret: clientSecret,
1982 token: msisdnToken,
1983 };
1984
1985 return await this._http.idServerRequest(
1986 undefined, "POST", "/validate/msisdn/submitToken",
1987 params, PREFIX_IDENTITY_V2, identityAccessToken,
1988 );
1989};
1990
1991/**
1992 * Submits a MSISDN token to an arbitrary URL.
1993 *
1994 * This is used when submitting the code sent by SMS to a phone number in the
1995 * newer 3PID flow where the homeserver validates 3PID ownership (as part of
1996 * `requestAdd3pidMsisdnToken`). The homeserver response may include a
1997 * `submit_url` to specify where the token should be sent, and this helper can
1998 * be used to pass the token to this URL.
1999 *
2000 * @param {string} url The URL to submit the token to
2001 * @param {string} sid The sid given in the response to requestToken
2002 * @param {string} clientSecret A secret binary string generated by the client.
2003 * This must be the same value submitted in the requestToken call.
2004 * @param {string} msisdnToken The MSISDN token, as enetered by the user.
2005 *
2006 * @return {Promise} Resolves: Object, currently with no parameters.
2007 * @return {module:http-api.MatrixError} Rejects: with an error response.
2008 */
2009MatrixBaseApis.prototype.submitMsisdnTokenOtherUrl = function(
2010 url,
2011 sid,
2012 clientSecret,
2013 msisdnToken,
2014) {
2015 const params = {
2016 sid: sid,
2017 client_secret: clientSecret,
2018 token: msisdnToken,
2019 };
2020
2021 return this._http.requestOtherUrl(
2022 undefined, "POST", url, undefined, params,
2023 );
2024};
2025
2026/**
2027 * Gets the V2 hashing information from the identity server. Primarily useful for
2028 * lookups.
2029 * @param {string} identityAccessToken The access token for the identity server.
2030 * @returns {Promise<object>} The hashing information for the identity server.
2031 */
2032MatrixBaseApis.prototype.getIdentityHashDetails = function(identityAccessToken) {
2033 return this._http.idServerRequest(
2034 undefined, "GET", "/hash_details",
2035 null, PREFIX_IDENTITY_V2, identityAccessToken,
2036 );
2037};
2038
2039/**
2040 * Performs a hashed lookup of addresses against the identity server. This is
2041 * only supported on identity servers which have at least the version 2 API.
2042 * @param {Array<Array<string,string>>} addressPairs An array of 2 element arrays.
2043 * The first element of each pair is the address, the second is the 3PID medium.
2044 * Eg: ["email@example.org", "email"]
2045 * @param {string} identityAccessToken The access token for the identity server.
2046 * @returns {Promise<Array<{address, mxid}>>} A collection of address mappings to
2047 * found MXIDs. Results where no user could be found will not be listed.
2048 */
2049MatrixBaseApis.prototype.identityHashedLookup = async function(
2050 addressPairs, // [["email@example.org", "email"], ["10005550000", "msisdn"]]
2051 identityAccessToken,
2052) {
2053 const params = {
2054 // addresses: ["email@example.org", "10005550000"],
2055 // algorithm: "sha256",
2056 // pepper: "abc123"
2057 };
2058
2059 // Get hash information first before trying to do a lookup
2060 const hashes = await this.getIdentityHashDetails(identityAccessToken);
2061 if (!hashes || !hashes['lookup_pepper'] || !hashes['algorithms']) {
2062 throw new Error("Unsupported identity server: bad response");
2063 }
2064
2065 params['pepper'] = hashes['lookup_pepper'];
2066
2067 const localMapping = {
2068 // hashed identifier => plain text address
2069 // For use in this function's return format
2070 };
2071
2072 // When picking an algorithm, we pick the hashed over no hashes
2073 if (hashes['algorithms'].includes('sha256')) {
2074 // Abuse the olm hashing
2075 const olmutil = new global.Olm.Utility();
2076 params["addresses"] = addressPairs.map(p => {
2077 const addr = p[0].toLowerCase(); // lowercase to get consistent hashes
2078 const med = p[1].toLowerCase();
2079 const hashed = olmutil.sha256(`${addr} ${med} ${params['pepper']}`)
2080 .replace(/\+/g, '-').replace(/\//g, '_'); // URL-safe base64
2081 // Map the hash to a known (case-sensitive) address. We use the case
2082 // sensitive version because the caller might be expecting that.
2083 localMapping[hashed] = p[0];
2084 return hashed;
2085 });
2086 params["algorithm"] = "sha256";
2087 } else if (hashes['algorithms'].includes('none')) {
2088 params["addresses"] = addressPairs.map(p => {
2089 const addr = p[0].toLowerCase(); // lowercase to get consistent hashes
2090 const med = p[1].toLowerCase();
2091 const unhashed = `${addr} ${med}`;
2092 // Map the unhashed values to a known (case-sensitive) address. We use
2093 // the case sensitive version because the caller might be expecting that.
2094 localMapping[unhashed] = p[0];
2095 return unhashed;
2096 });
2097 params["algorithm"] = "none";
2098 } else {
2099 throw new Error("Unsupported identity server: unknown hash algorithm");
2100 }
2101
2102 const response = await this._http.idServerRequest(
2103 undefined, "POST", "/lookup",
2104 params, PREFIX_IDENTITY_V2, identityAccessToken,
2105 );
2106
2107 if (!response || !response['mappings']) return []; // no results
2108
2109 const foundAddresses = [/* {address: "plain@example.org", mxid} */];
2110 for (const hashed of Object.keys(response['mappings'])) {
2111 const mxid = response['mappings'][hashed];
2112 const plainAddress = localMapping[hashed];
2113 if (!plainAddress) {
2114 throw new Error("Identity server returned more results than expected");
2115 }
2116
2117 foundAddresses.push({address: plainAddress, mxid});
2118 }
2119 return foundAddresses;
2120};
2121
2122/**
2123 * Looks up the public Matrix ID mapping for a given 3rd party
2124 * identifier from the Identity Server
2125 *
2126 * @param {string} medium The medium of the threepid, eg. 'email'
2127 * @param {string} address The textual address of the threepid
2128 * @param {module:client.callback} callback Optional.
2129 * @param {string} identityAccessToken The `access_token` field of the Identity
2130 * Server `/account/register` response (see {@link registerWithIdentityServer}).
2131 *
2132 * @return {Promise} Resolves: A threepid mapping
2133 * object or the empty object if no mapping
2134 * exists
2135 * @return {module:http-api.MatrixError} Rejects: with an error response.
2136 */
2137MatrixBaseApis.prototype.lookupThreePid = async function(
2138 medium,
2139 address,
2140 callback,
2141 identityAccessToken,
2142) {
2143 // Note: we're using the V2 API by calling this function, but our
2144 // function contract requires a V1 response. We therefore have to
2145 // convert it manually.
2146 const response = await this.identityHashedLookup(
2147 [[address, medium]], identityAccessToken,
2148 );
2149 const result = response.find(p => p.address === address);
2150 if (!result) {
2151 if (callback) callback(null, {});
2152 return {};
2153 }
2154
2155 const mapping = {
2156 address,
2157 medium,
2158 mxid: result.mxid,
2159
2160 // We can't reasonably fill these parameters:
2161 // not_before
2162 // not_after
2163 // ts
2164 // signatures
2165 };
2166
2167 if (callback) callback(null, mapping);
2168 return mapping;
2169};
2170
2171/**
2172 * Looks up the public Matrix ID mappings for multiple 3PIDs.
2173 *
2174 * @param {Array.<Array.<string>>} query Array of arrays containing
2175 * [medium, address]
2176 * @param {string} identityAccessToken The `access_token` field of the Identity
2177 * Server `/account/register` response (see {@link registerWithIdentityServer}).
2178 *
2179 * @return {Promise} Resolves: Lookup results from IS.
2180 * @return {module:http-api.MatrixError} Rejects: with an error response.
2181 */
2182MatrixBaseApis.prototype.bulkLookupThreePids = async function(
2183 query,
2184 identityAccessToken,
2185) {
2186 // Note: we're using the V2 API by calling this function, but our
2187 // function contract requires a V1 response. We therefore have to
2188 // convert it manually.
2189 const response = await this.identityHashedLookup(
2190 // We have to reverse the query order to get [address, medium] pairs
2191 query.map(p => [p[1], p[0]]), identityAccessToken,
2192 );
2193
2194 const v1results = [];
2195 for (const mapping of response) {
2196 const originalQuery = query.find(p => p[1] === mapping.address);
2197 if (!originalQuery) {
2198 throw new Error("Identity sever returned unexpected results");
2199 }
2200
2201 v1results.push([
2202 originalQuery[0], // medium
2203 mapping.address,
2204 mapping.mxid,
2205 ]);
2206 }
2207
2208 return {threepids: v1results};
2209};
2210
2211/**
2212 * Get account info from the Identity Server. This is useful as a neutral check
2213 * to verify that other APIs are likely to approve access by testing that the
2214 * token is valid, terms have been agreed, etc.
2215 *
2216 * @param {string} identityAccessToken The `access_token` field of the Identity
2217 * Server `/account/register` response (see {@link registerWithIdentityServer}).
2218 *
2219 * @return {Promise} Resolves: an object with account info.
2220 * @return {module:http-api.MatrixError} Rejects: with an error response.
2221 */
2222MatrixBaseApis.prototype.getIdentityAccount = function(
2223 identityAccessToken,
2224) {
2225 return this._http.idServerRequest(
2226 undefined, "GET", "/account",
2227 undefined, PREFIX_IDENTITY_V2, identityAccessToken,
2228 );
2229};
2230
2231// Direct-to-device messaging
2232// ==========================
2233
2234/**
2235 * Send an event to a specific list of devices
2236 *
2237 * @param {string} eventType type of event to send
2238 * @param {Object.<string, Object<string, Object>>} contentMap
2239 * content to send. Map from user_id to device_id to content object.
2240 * @param {string=} txnId transaction id. One will be made up if not
2241 * supplied.
2242 * @return {Promise} Resolves to the result object
2243 */
2244MatrixBaseApis.prototype.sendToDevice = function(
2245 eventType, contentMap, txnId,
2246) {
2247 const path = utils.encodeUri("/sendToDevice/$eventType/$txnId", {
2248 $eventType: eventType,
2249 $txnId: txnId ? txnId : this.makeTxnId(),
2250 });
2251
2252 const body = {
2253 messages: contentMap,
2254 };
2255
2256 const targets = Object.keys(contentMap).reduce((obj, key) => {
2257 obj[key] = Object.keys(contentMap[key]);
2258 return obj;
2259 }, {});
2260 logger.log(`PUT ${path}`, targets);
2261
2262 return this._http.authedRequest(undefined, "PUT", path, undefined, body);
2263};
2264
2265// Third party Lookup API
2266// ======================
2267
2268/**
2269 * Get the third party protocols that can be reached using
2270 * this HS
2271 * @return {Promise} Resolves to the result object
2272 */
2273MatrixBaseApis.prototype.getThirdpartyProtocols = function() {
2274 return this._http.authedRequest(
2275 undefined, "GET", "/thirdparty/protocols", undefined, undefined,
2276 ).then((response) => {
2277 // sanity check
2278 if (!response || typeof(response) !== 'object') {
2279 throw new Error(
2280 `/thirdparty/protocols did not return an object: ${response}`,
2281 );
2282 }
2283 return response;
2284 });
2285};
2286
2287/**
2288 * Get information on how a specific place on a third party protocol
2289 * may be reached.
2290 * @param {string} protocol The protocol given in getThirdpartyProtocols()
2291 * @param {object} params Protocol-specific parameters, as given in the
2292 * response to getThirdpartyProtocols()
2293 * @return {Promise} Resolves to the result object
2294 */
2295MatrixBaseApis.prototype.getThirdpartyLocation = function(protocol, params) {
2296 const path = utils.encodeUri("/thirdparty/location/$protocol", {
2297 $protocol: protocol,
2298 });
2299
2300 return this._http.authedRequest(undefined, "GET", path, params, undefined);
2301};
2302
2303/**
2304 * Get information on how a specific user on a third party protocol
2305 * may be reached.
2306 * @param {string} protocol The protocol given in getThirdpartyProtocols()
2307 * @param {object} params Protocol-specific parameters, as given in the
2308 * response to getThirdpartyProtocols()
2309 * @return {Promise} Resolves to the result object
2310 */
2311MatrixBaseApis.prototype.getThirdpartyUser = function(protocol, params) {
2312 const path = utils.encodeUri("/thirdparty/user/$protocol", {
2313 $protocol: protocol,
2314 });
2315
2316 return this._http.authedRequest(undefined, "GET", path, params, undefined);
2317};
2318
2319MatrixBaseApis.prototype.getTerms = function(serviceType, baseUrl) {
2320 const url = termsUrlForService(serviceType, baseUrl);
2321 return this._http.requestOtherUrl(
2322 undefined, 'GET', url,
2323 );
2324};
2325
2326MatrixBaseApis.prototype.agreeToTerms = function(
2327 serviceType, baseUrl, accessToken, termsUrls,
2328) {
2329 const url = termsUrlForService(serviceType, baseUrl);
2330 const headers = {
2331 Authorization: "Bearer " + accessToken,
2332 };
2333 return this._http.requestOtherUrl(
2334 undefined, 'POST', url, null, { user_accepts: termsUrls }, { headers },
2335 );
2336};
2337
2338/**
2339 * Reports an event as inappropriate to the server, which may then notify the appropriate people.
2340 * @param {string} roomId The room in which the event being reported is located.
2341 * @param {string} eventId The event to report.
2342 * @param {number} score The score to rate this content as where -100 is most offensive and 0 is inoffensive.
2343 * @param {string} reason The reason the content is being reported. May be blank.
2344 * @returns {Promise} Resolves to an empty object if successful
2345 */
2346MatrixBaseApis.prototype.reportEvent = function(roomId, eventId, score, reason) {
2347 const path = utils.encodeUri("/rooms/$roomId/report/$eventId", {
2348 $roomId: roomId,
2349 $eventId: eventId,
2350 });
2351
2352 return this._http.authedRequest(undefined, "POST", path, null, {score, reason});
2353};
2354