summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2019-02-20 16:14:39 +0900
committersyuilo <syuilotan@yahoo.co.jp>2019-02-20 16:14:39 +0900
commite0e4bdbdbca79760a3984370ca4024994f8dfbe4 (patch)
treed15af9669bce59b8c43323b80fc5a4f0ce509584 /src
parentUpdate CHANGELOG.md (diff)
parentFix #4329 (#4330) (diff)
downloadmisskey-e0e4bdbdbca79760a3984370ca4024994f8dfbe4.tar.gz
misskey-e0e4bdbdbca79760a3984370ca4024994f8dfbe4.tar.bz2
misskey-e0e4bdbdbca79760a3984370ca4024994f8dfbe4.zip
Merge branch 'develop' of https://github.com/syuilo/misskey into develop
Diffstat (limited to 'src')
-rw-r--r--src/prelude/xml.ts41
-rw-r--r--src/server/well-known.ts70
2 files changed, 89 insertions, 22 deletions
diff --git a/src/prelude/xml.ts b/src/prelude/xml.ts
new file mode 100644
index 0000000000..0773f75d47
--- /dev/null
+++ b/src/prelude/xml.ts
@@ -0,0 +1,41 @@
+const map: Record<string, string> = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ '\'': '&apos;'
+};
+
+const beginingOfCDATA = '<![CDATA[';
+const endOfCDATA = ']]>';
+
+export function escapeValue(x: string): string {
+ let insideOfCDATA = false;
+ let builder = '';
+ for (
+ let i = 0;
+ i < x.length;
+ ) {
+ if (insideOfCDATA) {
+ if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
+ insideOfCDATA = true;
+ i += beginingOfCDATA.length;
+ } else {
+ builder += x[i++];
+ }
+ } else {
+ if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
+ insideOfCDATA = false;
+ i += endOfCDATA.length;
+ } else {
+ const b = x[i++];
+ builder += map[b] || b;
+ }
+ }
+ }
+ return builder;
+}
+
+export function escapeAttribute(x: string): string {
+ return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
+}
diff --git a/src/server/well-known.ts b/src/server/well-known.ts
index 3c994793e1..18c080acc7 100644
--- a/src/server/well-known.ts
+++ b/src/server/well-known.ts
@@ -6,27 +6,37 @@ import parseAcct from '../misc/acct/parse';
import User from '../models/user';
import Acct from '../misc/acct/type';
import { links } from './nodeinfo';
+import { escapeAttribute, escapeValue } from '../prelude/xml';
// Init router
const router = new Router();
+const XRD = (...x: { element: string, value?: string, attributes?: Record<string, string> }[]) =>
+ `<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">${x.map(({ element, value, attributes }) =>
+ `<${
+ Object.entries(typeof attributes === 'object' && attributes || {}).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element)
+ }${
+ typeof value === 'string' ? `>${escapeValue(value)}</${element}` : '/'
+ }>`).reduce((a, c) => a + c, '')}</XRD>`;
+
const webFingerPath = '/.well-known/webfinger';
+const jrd = 'application/jrd+json';
+const xrd = 'application/xrd+xml';
router.get('/.well-known/host-meta', async ctx => {
- ctx.set('Content-Type', 'application/xrd+xml');
- ctx.body = `<?xml version="1.0" encoding="UTF-8"?>
-<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
- <Link rel="lrdd" type="application/xrd+xml" template="${config.url}${webFingerPath}?resource={uri}"/>
-</XRD>
-`;
+ ctx.set('Content-Type', xrd);
+ ctx.body = XRD({ element: 'Link', attributes: {
+ type: xrd,
+ template: `${config.url}${webFingerPath}?resource={uri}`
+ }});
});
router.get('/.well-known/host-meta.json', async ctx => {
- ctx.set('Content-Type', 'application/jrd+json');
+ ctx.set('Content-Type', jrd);
ctx.body = {
links: [{
rel: 'lrdd',
- type: 'application/xrd+xml',
+ type: jrd,
template: `${config.url}${webFingerPath}?resource={uri}`
}]
};
@@ -75,22 +85,38 @@ router.get(webFingerPath, async ctx => {
return;
}
- ctx.body = {
- subject: `acct:${user.username}@${config.host}`,
- links: [{
- rel: 'self',
- type: 'application/activity+json',
- href: `${config.url}/users/${user._id}`
- }, {
- rel: 'http://webfinger.net/rel/profile-page',
- type: 'text/html',
- href: `${config.url}/@${user.username}`
- }, {
- rel: 'http://ostatus.org/schema/1.0/subscribe',
- template: `${config.url}/authorize-follow?acct={uri}`
- }]
+ const subject = `acct:${user.username}@${config.host}`;
+ const self = {
+ rel: 'self',
+ type: 'application/activity+json',
+ href: `${config.url}/users/${user._id}`
};
+ const profilePage = {
+ rel: 'http://webfinger.net/rel/profile-page',
+ type: 'text/html',
+ href: `${config.url}/@${user.username}`
+ };
+ const subscribe = {
+ rel: 'http://ostatus.org/schema/1.0/subscribe',
+ template: `${config.url}/authorize-follow?acct={uri}`
+ };
+
+ if (ctx.accepts(jrd, xrd) === xrd) {
+ ctx.body = XRD(
+ { element: 'Subject', value: subject },
+ { element: 'Link', attributes: self },
+ { element: 'Link', attributes: profilePage },
+ { element: 'Link', attributes: subscribe });
+ ctx.type = xrd;
+ } else {
+ ctx.body = {
+ subject,
+ links: [self, profilePage, subscribe]
+ };
+ ctx.type = jrd;
+ }
+ ctx.vary('Accept');
ctx.set('Cache-Control', 'public, max-age=180');
});