'use strict'; const React = require('react'); const create = require('create-react-class'); const Promise = require('bluebird'); const urllib = require('url'); const createApiRequester = require("../lib/api-request"); let login = create({ displayName: "Login", getInitialState: function() { return { error: null, formState: { user: "", pass: "", hs: "" }, hs: { prompt: false, error: null, valid: false } }; }, login: function() { return Promise.try(() => { this.setState({error: ""}); if (this.state.hs.valid) { return this.doLogin(); } let parts = this.state.formState.user.split(':'); if (parts.length != 2) { return this.setState({error: "Please enter a full mxid, like username:homeserver.tld"}); } let hostname = urllib.parse("https://" + parts[1]); return Promise.try(() => { return getApiServer(hostname); }).then((homeserverUrl) => { console.log("Using API server", homeserverUrl); this.setState({ apiUrl: homeserverUrl, apiRequest: createApiRequester(homeserverUrl), formState: Object.assign(this.state.formState, { user: parts[0], hs: homeserverUrl }), hs: Object.assign(this.state.hs, { valid: true }) }); return this.doLogin(); }).catch((error) => { /* FIXME: Error filtering */ console.log("ERROR fetching homeserver url", error); this.setState({ hs: Object.assign(this.state.hs, { error: error, valid: false, prompt: true }) }); }); }); }, doLogin: function() { return Promise.try(() => { console.log("Logging in"); let user = this.state.formState.user.replace('@', ''); let password = this.state.formState.pass; let homeserverUrl = this.state.apiUrl; return Promise.try(() => { return this.state.apiRequest("/_matrix/client/r0/login", { user: user, password: password, type: "m.login.password", initial_device_display_name: "Neo v4", }); }).then((responseJson) => { console.log("got access token", responseJson); this.setState({ json: responseJson }); if(responseJson.access_token != undefined) { this.props.callback(responseJson.user_id, responseJson.access_token, homeserverUrl); } else { this.setState({ error: responseJson.error }); } }).catch((error) => { /* FIXME: Why are errors being swallowed here? */ console.error(error); }); }); }, handleUserChange: function(e) { let formState = this.state.formState; let user = e.target.value; formState.user = e.target.value; let parts = user.split(':'); if (parts.length == 2) { formState.hs = parts[1]; let hsState = Object.assign(this.state.hs, {error: null, valid: false}); this.setState({hs: hsState}); } this.setState({formState: formState}); }, handlePassChange: function(e) { let formState = this.state.formState; formState.pass = e.target.value; this.setState({formState: formState}); }, handleHomeserverChange: function(e) { let formState = this.state.formState; formState.hs = e.target.value; this.setState({formState: formState}); this.setState({hs: {error: null, valid: false, prompt: true, changed: true}}); }, render: function() { let hsState = "inactive"; if (this.state.hs.prompt) { hsState = "active"; } if (this.state.hs.error != null) { hsState = "error"; } if (this.state.hs.valid) { hsState = "validated"; } return (
{this.state.error}
{this.state.hs.prompt ? ( <> ) : ( {this.state.formState["hs"]} )}
); } }); function getApiServer(parsedUrl) { return Promise.try(() => { console.log("Checking for api server from mxid", urllib.format(parsedUrl)); return checkApi(parsedUrl); }).then(() => { // Hostname is a valid api server return buildUrl(parsedUrl, ""); }).catch(() => { /* FIXME: Error filtering */ console.log("trying .well-known"); return Promise.try(() => { return tryWellKnown(parsedUrl); }).then((hostname) => { console.log("got .well-known host", hostname); return hostname; }).catch((_err) => { /* FIXME: Error chaining */ throw new Error("Fatal error trying to get API host"); }); }); } function checkApi(host) { return Promise.try(() => { let versionUrl = buildUrl(host, "/_matrix/client/versions"); return Promise.try(() => { return fetch(versionUrl); }).then((response) => { if (response.status != 200) { console.log("Invalid homeserver url", versionUrl); /* FIXME: Error types */ throw new Error("Invalid homeserver URL"); } }); }); } function tryWellKnown(host) { let wellKnownUrl = urllib.format(Object.assign(host, { pathname: "/.well-known/matrix/client" })); console.log("Trying", wellKnownUrl, "for .well-known"); return Promise.try(() => { return fetch(wellKnownUrl); }).tap((response) => { if (response.status != 200) { console.log("no well-known in use"); /* FIXME: Error type */ throw new Error("No homeserver found"); } }).catch((_error) => { /* FIXME: Error chaining */ throw new Error("can't fetch .well-known"); }).then((response) => { return response.json(); }).then((json) => { console.log("Parsed json", json); if (json['m.homeserver'] != null && json['m.homeserver'].base_url != null) { return json['m.homeserver'].base_url; } else { /* FIXME: Error type */ throw new Error("No homeserver specified in .well-known"); } }).catch((err) => { /* FIXME: Error filtering? */ console.log("Error in json", err); /* FIXME: Error chaining */ throw new Error("Error while parsing .well-known"); }); } function buildUrl(parsedUrl, path) { return urllib.format(Object.assign(parsedUrl, { pathname: path })); } module.exports = login;