enigma-bbs/core/webfinger.js

75 lines
2.3 KiB
JavaScript

const { Errors } = require('./enig_error');
const { getAddressedToInfo } = require('./mail_util');
const Message = require('./message');
const { getJson } = require('./http_util');
// deps
exports.queryWebFinger = queryWebFinger;
function queryWebFinger(query, cb) {
//
// Accept a variety of formats to query via WebFinger
// 1) @Username@foo.bar -> query with acct:Username resource
// 2) http/https URL -> query with resource = URL
// 3) If not one of the above and a '/' is present in the query,
// assume https:// and try #2
//
// ex: @NuSkooler@toot.community -> https://toot.community/.well-known/webfinger with acct:NuSkooler resource
const addrInfo = getAddressedToInfo(query);
let resource;
let host;
if (
addrInfo.flavor === Message.AddressFlavor.ActivityPub ||
addrInfo.flavor === Message.AddressFlavor.Email
) {
host = addrInfo.remote.slice(addrInfo.remote.lastIndexOf('@') + 1);
if (!host) {
return cb(Errors.Invalid(`Unsure how to WebFinger "${query}"`));
}
resource = `acct:${addrInfo.name}@${host}`;
} else {
if (!/^https?:\/\/.+$/.test(query)) {
resource = `https://${query}`;
} else {
resource = query;
}
try {
const url = new URL(resource);
host = url.host;
} catch (e) {
return cb(Errors.Invalid(`Cannot WebFinger "${query}": ${e.message}`));
}
}
resource = encodeURIComponent(resource);
const webFingerUrl = `https://${host}/.well-known/webfinger?resource=${resource}`;
getJson(webFingerUrl, {}, (err, json, res) => {
if (err) {
return cb(err);
}
if (res.statusCode !== 200) {
// only accept 200
return cb(Errors.DoesNotExist(`Failed to WebFinger URL ${webFingerUrl}`));
}
const contentType = res.headers['content-type'] || '';
if (
!['application/jrd+json', 'application/json'].some(ct =>
contentType.startsWith(ct)
)
) {
return cb(
Errors.Invalid(
`Invalid Content-Type for WebFinger URL ${webFingerUrl}: ${contentType}`
)
);
}
return cb(null, json);
});
}