From ab39f741f3c5e583002bfd1a9ef780b1a0c63143 Mon Sep 17 00:00:00 2001 From: Niko Date: Mon, 21 Feb 2022 23:54:59 +0100 Subject: [PATCH] Intercept AP Create.Note and check if contains #fedilovequiz to parse * FediLove Quiz is a standar text-based (parsed) protocol wrapped inside the object "Note" that can create a Quiz inside FediLove instance so there can be an interaction between non-FediLove instances and FediLove instances. * TODO: implement the response from this side, so it goes parsed as a regular response to the original "Note" but parsed so if the target instance is FediLove, it parses it as a quiz result and can be interpreted on any AP software --- api/src/activity/federation.js | 10 +++++ api/src/api-utils.js | 5 +++ api/src/api/auth.js | 4 +- api/src/api/me.js | 76 ++++++++++++++++++++++++++++++++++ api/src/auth.js | 1 - api/src/db.js | 1 + api/src/random.js | 14 +++++++ api/src/utils.js | 6 --- 8 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 api/src/random.js delete mode 100644 api/src/utils.js diff --git a/api/src/activity/federation.js b/api/src/activity/federation.js index daef13b..7301a5e 100644 --- a/api/src/activity/federation.js +++ b/api/src/activity/federation.js @@ -37,6 +37,16 @@ module.exports = { return true } } + if (msg.activity.type === 'Create') + { + if (msg.object.type === 'Note') { + var content = utils.firstIfArray(msg.object.content) + if (api.me.isFediloveQuiz(content)) + await api.me.quizCreate(content, msg.actor.id, + msg.recipient.id, msg.object.id) + return true + } + } if (msg.activity.type === 'Delete') { if (msg.object.type === 'Note') { diff --git a/api/src/api-utils.js b/api/src/api-utils.js index 36d7611..927fabe 100644 --- a/api/src/api-utils.js +++ b/api/src/api-utils.js @@ -14,6 +14,11 @@ module.exports = { return false } }, + md5sum: (str, size) => { + size = size || 32 + const hasher = crypto.createHmac('md5','FediLove') + return hasher.update(str).digest('hex').slice(0, size) + }, firstIfArray: (o) => { if (o === undefined) return null diff --git a/api/src/api/auth.js b/api/src/api/auth.js index 22357f7..9bb6a16 100644 --- a/api/src/api/auth.js +++ b/api/src/api/auth.js @@ -1,6 +1,6 @@ const api = require('../api-utils.js') const pwd = require('../passwd.js') -const utils = require('../utils.js') +const random = require('../random.js') module.exports = { login: { @@ -16,7 +16,7 @@ module.exports = { if (!pwd.comparePassword(req.body.password, user.password)) return res.status(403).json({ error: 'invalid_password' }) - const session_id = utils.randomString(128) + const session_id = random.base64(128) const session = await db.table.sessions().insertOne({ id_user: user._id, session: session_id diff --git a/api/src/api/me.js b/api/src/api/me.js index 7eae5c3..e74d5aa 100644 --- a/api/src/api/me.js +++ b/api/src/api/me.js @@ -1,6 +1,82 @@ const auth = require('../auth.js') +const utils = require('../api-utils.js') +const random = require('../random.js') + +function quizParse(html, check) { + check = check === undefined ? false : check + if (html === undefined || html === null) + return false + html = decodeURIComponent(html) + html = html.replaceAll('>','>').replaceAll('<','<') + .replaceAll('"','"').replaceAll('&','&') + html = html.replaceAll(//g, '
') + html = html.replaceAll('

', '\n') + html = html.replaceAll('
', '\n') + html = html.replaceAll(/<[^<>]+?>/g, '') + if (!html.toLowerCase().includes('#fedilovequiz')) + return false + if (check) + return true + var it = {} + var sq = false + var items = [] + html = html+'\n999: end' + const lines = html.split('\n') + for (var i in lines) { + var ln = lines[i].trim() + if (ln === '') + continue + if (ln.toLowerCase().includes('#fedilovequiz')) { + sq = true + continue + } + if (sq) { + if (ln.match(/^\d+\:.*$/)) { + if (it.question !== undefined) + items.push(it) + ln = ln.substr(ln.indexOf(':')+1) + it = { question: ln.trim(), options: [] } + } else { + ln = ln.replaceAll(/\s?\|\s?/g,'|') + .replaceAll(/\|+/g, '|') + if (!ln.includes('|')) + continue + ps = ln.split('|') + for (var j in ps) + ps[j] = ps[j].trim() + it.options = ps + } + } + } + return items +} + +async function quizCreate(content, from, to, replyTo) { + var quiz = quizParse(content) + if (quiz === false) + return false + if (from === undefined || from === null) + return false + if (to === undefined || to === null) + return false + if (replyTo === undefined || replyTo === null) + return false + const payload = { + id: utils.md5sum(replyTo, 16).toUpperCase(), + replyTo, from, to, content: quiz, + } + await db.table.quizs().insertOne(payload) + return payload +} + +function isFediloveQuiz(html) { + return quizParse(html, true) !== false +} module.exports = { + quizParse, + quizCreate, + isFediloveQuiz, pending_follows: { get: [auth.enforceSession, async (req, res) => { let follows diff --git a/api/src/auth.js b/api/src/auth.js index 9b1e314..3d20c8b 100644 --- a/api/src/auth.js +++ b/api/src/auth.js @@ -1,5 +1,4 @@ const pwd = require('./passwd.js') -const utils = require('./utils.js') module.exports = { enforceSession: async (req, res, next) => { diff --git a/api/src/db.js b/api/src/db.js index c752f10..1afe9a7 100644 --- a/api/src/db.js +++ b/api/src/db.js @@ -21,6 +21,7 @@ module.exports = { objects: () => { return mdb.collection('objects') }, users: () => { return mdb.collection('u__users') }, sessions: () => { return mdb.collection('u__sessions') }, + quizs: () => { return mdb.collection('u__quizs') }, }, getAPObject: async (objID, checkType) => { const object = await module.exports.table diff --git a/api/src/random.js b/api/src/random.js new file mode 100644 index 0000000..c3ea6a4 --- /dev/null +++ b/api/src/random.js @@ -0,0 +1,14 @@ +const Crypto = require('crypto') + +module.exports = { + base64: (size) => { + return Crypto.randomBytes(size) + .toString('base64').slice(0, size) + }, + md5: (size) => { + size = size || 32 + const rnd = module.exports.base64(32) + const hasher = Crypto.createHmac("md5", rnd.substr(16)) + return hasher.update(rnd.substr(0,16)).digest("hex").slice(0, size) + }, +} diff --git a/api/src/utils.js b/api/src/utils.js deleted file mode 100644 index d7db5f1..0000000 --- a/api/src/utils.js +++ /dev/null @@ -1,6 +0,0 @@ -const Crypto = require('crypto') - -exports.randomString = (size) => { - return Crypto.randomBytes(size) - .toString('base64').slice(0, size) -}