cryptpad/www/common/outer/mailbox-handlers.js

853 lines
29 KiB
JavaScript
Raw Normal View History

2019-05-22 16:03:52 +00:00
define([
'/common/common-messaging.js',
'/common/common-hash.js',
2019-09-20 13:27:20 +00:00
'/common/common-util.js',
'/bower_components/chainpad-crypto/crypto.js',
], function (Messaging, Hash, Util, Crypto) {
2019-05-22 16:03:52 +00:00
2020-02-05 12:33:32 +00:00
// Random timeout between 10 and 30 times your sync time (lag + chainpad sync)
var getRandomTimeout = function (ctx) {
var lag = ctx.store.realtime.getLag().lag || 0;
return (Math.max(0, lag) + 300) * 20 * (0.5 + Math.random());
};
2019-05-22 16:03:52 +00:00
var handlers = {};
2019-05-27 12:03:15 +00:00
var removeHandlers = {};
2019-05-22 16:03:52 +00:00
var isMuted = function (ctx, data) {
var muted = ctx.store.proxy.mutedUsers || {};
var curvePublic = Util.find(data, ['msg', 'author']);
if (!curvePublic) { return false; }
return Boolean(muted[curvePublic]);
};
2022-11-30 17:41:16 +00:00
var isChannelMuted = function (ctx, channel) {
var muted = ctx.store.proxy.mutedChannels || [];
return muted.includes(channel);
};
2019-05-27 12:03:15 +00:00
// Store the friend request displayed to avoid duplicates
var friendRequest = {};
handlers['FRIEND_REQUEST'] = function (ctx, box, data, cb) {
2020-02-05 12:33:32 +00:00
// Old format: data was stored directly in "content"
var userData = data.msg.content.user || data.msg.content;
if (isMuted(ctx, data)) { return void cb(true); }
2019-05-27 12:03:15 +00:00
// Don't show duplicate friend request: if we already have a friend request
// in memory from the same user, dismiss the new one
if (friendRequest[data.msg.author]) { return void cb(true); }
2020-12-09 13:54:22 +00:00
friendRequest[data.msg.author] = {
type: box.type,
hash: data.hash
};
2019-05-27 12:03:15 +00:00
// If the user is already in our friend list, automatically accept the request
if (Messaging.getFriend(ctx.store.proxy, data.msg.author) ||
ctx.store.proxy.friends_pending[data.msg.author]) {
2020-12-09 13:54:22 +00:00
delete ctx.store.proxy.friends_pending[data.msg.author];
2020-02-05 12:33:32 +00:00
Messaging.acceptFriendRequest(ctx.store, userData, function (obj) {
2019-05-27 12:03:15 +00:00
if (obj && obj.error) {
return void cb();
}
Messaging.addToFriendList({
proxy: ctx.store.proxy,
realtime: ctx.store.realtime,
pinPads: ctx.pinPads
2020-02-05 12:33:32 +00:00
}, userData, function (err) {
2020-12-09 13:54:22 +00:00
if (err) {
console.error(err);
return void cb(true);
}
if (ctx.store.messenger) {
2020-02-05 12:33:32 +00:00
ctx.store.messenger.onFriendAdded(userData);
}
2020-12-09 13:54:22 +00:00
ctx.updateMetadata();
cb(true);
});
2019-05-22 16:03:52 +00:00
});
return;
}
2019-05-27 12:03:15 +00:00
2019-05-22 16:03:52 +00:00
cb();
};
2019-05-27 12:03:15 +00:00
removeHandlers['FRIEND_REQUEST'] = function (ctx, box, data) {
2020-02-05 12:33:32 +00:00
var userData = data.content.user || data.content;
if (friendRequest[userData.curvePublic]) {
delete friendRequest[userData.curvePublic];
2019-05-27 12:03:15 +00:00
}
};
2020-02-05 12:33:32 +00:00
// The DECLINE and ACCEPT messages act on the contacts data
// They are processed with a random timeout to avoid having
// multiple workers trying to add or remove the contacts at
// the same time. Once processed, they are dismissed.
// We must dismiss them and send another message to our own
// mailbox for the UI part otherwise it would automatically
// accept or decline future requests from the same user
// until the message is manually dismissed.
2019-05-27 12:03:15 +00:00
var friendRequestDeclined = {};
2020-12-09 14:36:30 +00:00
var friendRequestAccepted = {};
2019-05-23 13:25:05 +00:00
handlers['DECLINE_FRIEND_REQUEST'] = function (ctx, box, data, cb) {
2020-02-05 12:33:32 +00:00
// Old format: data was stored directly in "content"
var userData = data.msg.content.user || data.msg.content;
if (!userData.curvePublic) { userData.curvePublic = data.msg.author; }
// Our friend request was declined.
setTimeout(function () {
2020-02-05 12:33:32 +00:00
// Only dismissed once in the timeout to make sure we won't lose
// the data if we close the worker before adding the friend
cb(true);
2020-02-05 12:33:32 +00:00
// Make sure we really sent it
if (!ctx.store.proxy.friends_pending[data.msg.author]) { return; }
// Remove the pending message and display the "declined" state in the UI
delete ctx.store.proxy.friends_pending[data.msg.author];
2020-02-05 12:33:32 +00:00
ctx.updateMetadata();
2019-05-27 12:03:15 +00:00
if (friendRequestDeclined[data.msg.author]) { return; }
box.sendMessage({
type: 'FRIEND_REQUEST_DECLINED',
2020-02-05 12:33:32 +00:00
content: { user: userData }
2020-12-09 13:54:22 +00:00
}, function (hash) {
friendRequestDeclined[data.msg.author] = {
type: box.type,
hash: hash
};
});
}, getRandomTimeout(ctx));
2019-05-23 13:25:05 +00:00
};
2020-02-05 12:33:32 +00:00
// UI for declined friend request
2019-05-27 12:03:15 +00:00
handlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data, cb) {
ctx.updateMetadata();
2020-02-05 12:33:32 +00:00
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
2020-12-09 13:54:22 +00:00
var toRemove = friendRequestAccepted[curve];
delete friendRequestAccepted[curve];
if (friendRequestDeclined[curve]) { return void cb(true, toRemove); }
friendRequestDeclined[curve] = {
type: box.type,
hash: data.hash
};
cb(false, toRemove);
2019-05-27 12:03:15 +00:00
};
removeHandlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data) {
2020-02-05 12:33:32 +00:00
var curve = data.content.user.curvePublic || data.content.user;
if (friendRequestDeclined[curve]) { delete friendRequestDeclined[curve]; }
2019-05-27 12:03:15 +00:00
};
2019-05-22 16:03:52 +00:00
handlers['ACCEPT_FRIEND_REQUEST'] = function (ctx, box, data, cb) {
2020-02-05 12:33:32 +00:00
// Old format: data was stored directly in "content"
var userData = data.msg.content.user || data.msg.content;
2019-05-22 16:03:52 +00:00
// Our friend request was accepted.
setTimeout(function () {
2020-02-05 12:33:32 +00:00
// Only dismissed once in the timeout to make sure we won't lose
// the data if we close the worker before adding the friend
cb(true);
// Make sure we really sent it
if (!ctx.store.proxy.friends_pending[data.msg.author]) { return; }
2020-02-05 12:33:32 +00:00
// Remove the pending state. It will also us to send a new request in case of error
delete ctx.store.proxy.friends_pending[data.msg.author];
// And add the friend
Messaging.addToFriendList({
proxy: ctx.store.proxy,
realtime: ctx.store.realtime,
pinPads: ctx.pinPads
2020-02-05 12:33:32 +00:00
}, userData, function (err) {
if (err) { return void console.error(err); }
// Load the chat if contacts app loaded
if (ctx.store.messenger) { ctx.store.messenger.onFriendAdded(userData); }
// Update the userlist
ctx.updateMetadata();
// If you have a profile page open, update it
if (ctx.store.modules['profile']) { ctx.store.modules['profile'].update(); }
// Display the "accepted" state in the UI
2020-02-05 12:33:32 +00:00
if (friendRequestAccepted[data.msg.author]) { return; }
box.sendMessage({
type: 'FRIEND_REQUEST_ACCEPTED',
2020-02-05 12:33:32 +00:00
content: { user: userData }
2020-12-09 13:54:22 +00:00
}, function (hash) {
friendRequestAccepted[data.msg.author] = {
type: box.type,
hash: hash
};
});
});
}, getRandomTimeout(ctx));
2019-05-22 16:03:52 +00:00
};
2020-02-05 12:33:32 +00:00
// UI for accepted friend request
2019-05-27 12:03:15 +00:00
handlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data, cb) {
ctx.updateMetadata();
2020-02-05 12:33:32 +00:00
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
2020-12-09 13:54:22 +00:00
var toRemove = friendRequestDeclined[curve];
delete friendRequestDeclined[curve];
if (friendRequestAccepted[curve]) { return void cb(true, toRemove); }
friendRequestAccepted[curve] = {
type: box.type,
hash: data.hash
};
cb(false, toRemove);
2019-05-27 12:03:15 +00:00
};
removeHandlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data) {
2020-02-05 12:33:32 +00:00
var curve = data.content.user.curvePublic || data.content.user;
if (friendRequestAccepted[curve]) { delete friendRequestAccepted[curve]; }
2019-05-27 12:03:15 +00:00
};
2019-05-22 16:03:52 +00:00
2020-12-09 13:54:22 +00:00
handlers['CANCEL_FRIEND_REQUEST'] = function (ctx, box, data, cb) {
var f = friendRequest[data.msg.author];
if (!f) { return void cb(true); }
cb(true, f);
};
2019-05-27 16:58:35 +00:00
handlers['UNFRIEND'] = function (ctx, box, data, cb) {
var curve = data.msg.author;
2019-05-27 16:58:35 +00:00
var friend = Messaging.getFriend(ctx.store.proxy, curve);
if (!friend) { return void cb(true); }
delete ctx.store.proxy.friends[curve];
2020-12-09 13:54:22 +00:00
delete ctx.store.proxy.friends_pending[curve];
2019-05-27 16:58:35 +00:00
if (ctx.store.messenger) {
ctx.store.messenger.onFriendRemoved(curve, friend.channel);
}
ctx.updateMetadata();
2019-05-27 16:58:35 +00:00
cb(true);
};
2019-05-22 16:03:52 +00:00
handlers['UPDATE_DATA'] = function (ctx, box, data, cb) {
var msg = data.msg;
var curve = msg.author;
var friend = ctx.store.proxy.friends && ctx.store.proxy.friends[curve];
if (!friend || typeof msg.content !== "object") { return void cb(true); }
Object.keys(msg.content).forEach(function (key) {
friend[key] = msg.content[key];
});
if (ctx.store.messenger) {
2019-09-16 13:56:50 +00:00
ctx.store.messenger.onFriendUpdate(curve);
}
ctx.updateMetadata();
cb(true);
};
// Hide duplicates when receiving a SHARE_PAD notification:
// Keep only one notification per channel: the stronger and more recent one
var channels = {};
handlers['SHARE_PAD'] = function (ctx, box, data, cb) {
var msg = data.msg;
var hash = data.hash;
var content = msg.content;
// content.name, content.title, content.href, content.password
if (isMuted(ctx, data)) { return void cb(true); }
// if the shared content is a 'link' then we can't use the channel to deduplicate notifications
// use href instead.
2021-07-20 08:40:40 +00:00
var channel = content.isStatic ? content.href : Hash.hrefToHexChannelId(content.href, content.password);
var parsed = Hash.parsePadUrl(content.href);
var mode = parsed.hashData && parsed.hashData.mode || 'n/a';
var old = channels[channel];
var toRemove;
if (old) {
// New hash is weaker, ignore
if (old.mode === 'edit' && mode === 'view') {
return void cb(true);
}
// New hash is not weaker, clear the old one
toRemove = old.data;
}
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
}
// Update the data
channels[channel] = {
mode: mode,
data: {
type: box.type,
hash: hash
}
2019-07-01 16:19:54 +00:00
};
cb(false, toRemove);
};
removeHandlers['SHARE_PAD'] = function (ctx, box, data, hash) {
var content = data.content;
var channel = Hash.hrefToHexChannelId(content.href, content.password);
var old = channels[channel];
if (old && old.data && old.data.hash === hash) {
delete channels[channel];
}
};
// Hide duplicates when receiving a SUPPORT_MESSAGE notification
var supportMessage = false;
handlers['SUPPORT_MESSAGE'] = function (ctx, box, data, cb) {
if (supportMessage) { return void cb(true); }
supportMessage = true;
cb();
};
removeHandlers['SUPPORT_MESSAGE'] = function () {
supportMessage = false;
};
2019-07-11 12:16:04 +00:00
// Incoming edit rights request: add data before sending it to inner
2019-07-13 09:47:58 +00:00
handlers['REQUEST_PAD_ACCESS'] = function (ctx, box, data, cb) {
2019-07-11 12:16:04 +00:00
var msg = data.msg;
var content = msg.content;
if (isMuted(ctx, data)) { return void cb(true); }
2019-07-11 12:16:04 +00:00
var channel = content.channel;
var res = ctx.store.manager.findChannel(channel);
if (!res.length) { return void cb(true); }
2019-07-13 09:47:58 +00:00
var edPublic = ctx.store.proxy.edPublic;
2019-07-16 15:29:55 +00:00
var title, href;
2019-07-13 09:47:58 +00:00
if (!res.some(function (obj) {
2019-07-11 12:16:04 +00:00
if (obj.data &&
Array.isArray(obj.data.owners) && obj.data.owners.indexOf(edPublic) !== -1 &&
obj.data.href) {
2019-07-16 15:29:55 +00:00
href = obj.data.href;
2019-07-11 12:16:04 +00:00
title = obj.data.filename || obj.data.title;
return true;
}
})) { return void cb(true); }
content.title = title;
2019-07-16 15:29:55 +00:00
content.href = href;
2019-07-13 09:47:58 +00:00
cb(false);
};
handlers['GIVE_PAD_ACCESS'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
var channel = content.channel;
2019-10-07 16:30:46 +00:00
var res = ctx.store.manager.findChannel(channel, true);
2019-07-13 09:47:58 +00:00
var title;
res.forEach(function (obj) {
if (obj.data && !obj.data.href) {
if (!title) { title = obj.data.filename || obj.data.title; }
2019-10-07 16:30:46 +00:00
obj.userObject.setHref(channel, null, content.href);
2019-07-13 09:47:58 +00:00
}
});
content.title = title || content.title;
cb(false);
};
2019-07-11 12:16:04 +00:00
// Hide duplicates when receiving an ADD_OWNER notification:
var addOwners = {};
handlers['ADD_OWNER'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (isMuted(ctx, data)) { return void cb(true); }
2019-09-30 13:17:26 +00:00
if (!content.teamChannel && !(content.href && content.title && content.channel)) {
console.log('Remove invalid notification');
return void cb(true);
}
2019-09-30 13:17:26 +00:00
var channel = content.channel || content.teamChannel;
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
}
if (addOwners[channel]) { return void cb(true); }
addOwners[channel] = {
type: box.type,
hash: data.hash
};
cb(false);
};
removeHandlers['ADD_OWNER'] = function (ctx, box, data) {
2019-09-30 13:17:26 +00:00
var channel = data.content.channel || data.content.teamChannel;
if (addOwners[channel]) {
delete addOwners[channel];
}
};
handlers['RM_OWNER'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
2019-09-30 13:17:26 +00:00
if (!content.channel && !content.teamChannel) {
console.log('Remove invalid notification');
return void cb(true);
}
2019-09-30 13:17:26 +00:00
var channel = content.channel || content.teamChannel;
// If our ownership rights for a team have been removed, update the owner flag
if (content.teamChannel) {
var teams = ctx.store.proxy.teams || {};
Object.keys(teams).some(function (id) {
if (teams[id].channel === channel) {
teams[id].owner = false;
return true;
}
});
}
if (addOwners[channel] && content.pending) {
return void cb(false, addOwners[channel]);
}
cb(false);
};
2019-09-20 13:27:20 +00:00
var invitedTo = {};
handlers['INVITE_TO_TEAM'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (isMuted(ctx, data)) { return void cb(true); }
2019-09-20 13:27:20 +00:00
if (!content.team) {
console.log('Remove invalid notification');
return void cb(true);
}
2019-09-25 16:21:45 +00:00
var invited = invitedTo[content.team.channel];
if (invited) {
console.log('removing old invitation');
cb(false, invited);
invitedTo[content.team.channel] = {
type: box.type,
hash: data.hash
};
return;
}
2019-09-20 13:27:20 +00:00
var myTeams = Util.find(ctx, ['store', 'proxy', 'teams']) || {};
2019-09-20 13:27:20 +00:00
var alreadyMember = Object.keys(myTeams).some(function (k) {
var team = myTeams[k];
return team.channel === content.team.channel;
});
if (alreadyMember) { return void cb(true); }
2019-09-25 16:21:45 +00:00
invitedTo[content.team.channel] = {
type: box.type,
hash: data.hash
};
2019-09-20 13:27:20 +00:00
cb(false);
};
removeHandlers['INVITE_TO_TEAM'] = function (ctx, box, data) {
var channel = Util.find(data, ['content', 'team', 'channel']);
delete invitedTo[channel];
};
handlers['KICKED_FROM_TEAM'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (!content.teamChannel) {
console.log('Remove invalid notification');
return void cb(true);
}
2019-09-25 16:21:45 +00:00
if (invitedTo[content.teamChannel] && content.pending) {
return void cb(true, invitedTo[content.teamChannel]);
}
cb(false);
};
2019-09-20 13:27:20 +00:00
handlers['INVITE_TO_TEAM_ANSWER'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (!content.teamChannel) {
console.log('Remove invalid notification');
return void cb(true);
}
var myTeams = Util.find(ctx, ['store', 'proxy', 'teams']) || {};
2019-09-20 13:27:20 +00:00
var teamId;
var team;
Object.keys(myTeams).some(function (k) {
var _team = myTeams[k];
if (_team.channel === content.teamChannel) {
teamId = k;
team = _team;
return true;
}
});
if (!teamId) { return void cb(true); }
content.team = team;
if (!content.answer) {
// If they declined the invitation, remove them from the roster (as a pending member)
try {
var module = ctx.store.modules['team'];
2020-08-31 08:48:15 +00:00
module.removeFromTeam(teamId, msg.author, true);
2019-09-20 13:27:20 +00:00
} catch (e) { console.error(e); }
}
2020-08-31 09:00:46 +00:00
var userData = content.user || content;
2020-08-31 08:48:15 +00:00
box.sendMessage({
type: 'INVITE_TO_TEAM_ANSWERED',
content: {
user: userData,
team: team,
answer: content.answer
}
}, function () {});
cb(true);
2019-09-20 13:27:20 +00:00
};
handlers['TEAM_EDIT_RIGHTS'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (!content.teamData) {
console.log('Remove invalid notification');
return void cb(true);
}
// Make sure we are a member of this team
var myTeams = Util.find(ctx, ['store', 'proxy', 'teams']) || {};
var teamId;
var team;
Object.keys(myTeams).some(function (k) {
var _team = myTeams[k];
2019-10-14 10:01:44 +00:00
if (_team.channel === content.teamData.channel) {
teamId = k;
team = _team;
return true;
}
});
if (!teamId) { return void cb(true); }
try {
var module = ctx.store.modules['team'];
// changeMyRights returns true if we can't change our rights
module.changeMyRights(teamId, content.state, content.teamData, function (done) {
if (!done) { console.error("Can't update team rights"); }
cb(true);
});
} catch (e) { console.error(e); }
};
2020-01-13 14:58:46 +00:00
handlers['OWNED_PAD_REMOVED'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
if (!content.channel) {
console.log('Remove invalid notification');
return void cb(true);
}
var channel = content.channel;
var res = ctx.store.manager.findChannel(channel);
res.forEach(function (obj) {
var paths = ctx.store.manager.findFile(obj.id);
ctx.store.manager.delete({
paths: paths
}, function () {
ctx.updateDrive();
});
});
cb(true);
};
2020-05-15 13:27:55 +00:00
// Make sure "todo migration" notifications are from yourself
handlers['MOVE_TODO'] = function (ctx, box, data, cb) {
var curve = ctx.store.proxy.curvePublic;
if (data.msg.author !== curve) { return void cb(true); }
cb();
};
handlers["SAFE_LINKS_DEFAULT"] = function (ctx, box, data, cb) {
2020-06-08 19:34:20 +00:00
var curve = ctx.store.proxy.curvePublic;
if (data.msg.author !== curve) { return void cb(true); }
cb();
};
2022-10-25 14:40:15 +00:00
// Hide duplicates when receiving a form notification:
// Keep only one notification per channel
var formNotifs = {};
handlers['FORM_RESPONSE'] = function (ctx, box, data, cb) {
var msg = data.msg;
var hash = data.hash;
var content = msg.content;
var channel = content.channel;
if (!channel) { return void cb(true); }
2022-11-30 17:41:16 +00:00
if (isChannelMuted(ctx, channel)) { return void cb(true); }
2022-10-25 14:40:15 +00:00
var title, href;
ctx.Store.getAllStores().some(function (s) {
var res = s.manager.findChannel(channel);
// Check if the pad is in our drive
return res.some(function (obj) {
if (!obj.data) { return; }
if (href && !obj.data.href) { return; } // We already have the VIEW url, we need EDIT
href = obj.data.href || obj.data.roHref;
title = obj.data.filename || obj.data.title;
if (obj.data.href) { return true; } // Abort only if we have the EDIT url
});
});
// If we don't have the edit url, ignore this notification
if (!href) { return void cb(true); }
// Add the title
content.href = href;
content.title = title;
// Remove duplicates
var old = formNotifs[channel];
var toRemove = old ? old.data : undefined;
// Update the data
formNotifs[channel] = {
data: {
type: box.type,
hash: hash
}
};
cb(false, toRemove);
};
removeHandlers['FORM_RESPONSE'] = function (ctx, box, data, hash) {
var content = data.content;
var channel = content.channel;
var old = formNotifs[channel];
if (old && old.data && old.data.hash === hash) {
delete formNotifs[channel];
}
};
// Hide duplicates when receiving a SHARE_PAD notification:
// Keep only one notification per channel: the stronger and more recent one
var comments = {};
handlers['COMMENT_REPLY'] = function (ctx, box, data, cb) {
var msg = data.msg;
var hash = data.hash;
var content = msg.content;
if (Util.find(ctx.store.proxy, ['settings', 'pad', 'disableNotif'])) {
return void cb(true);
}
var channel = content.channel;
if (!channel) { return void cb(true); }
var title, href;
ctx.Store.getAllStores().some(function (s) {
var res = s.manager.findChannel(channel);
// Check if the pad is in our drive
return res.some(function (obj) {
if (!obj.data) { return; }
if (href && !obj.data.href) { return; } // We already have the VIEW url, we need EDIT
href = obj.data.href || obj.data.roHref;
title = obj.data.filename || obj.data.title;
if (obj.data.href) { return true; } // Abort only if we have the EDIT url
});
});
// If we don't have the edit url, ignore this notification
if (!href) { return void cb(true); }
// Add the title
content.href = href;
content.title = title;
// Remove duplicates
var old = comments[channel];
var toRemove = old ? old.data : undefined;
// Update the data
comments[channel] = {
data: {
type: box.type,
hash: hash
}
};
cb(false, toRemove);
};
removeHandlers['COMMENT_REPLY'] = function (ctx, box, data, hash) {
var content = data.content;
var channel = content.channel;
var old = comments[channel];
if (old && old.data && old.data.hash === hash) {
delete comments[channel];
}
};
2020-04-28 13:54:12 +00:00
// Hide duplicates when receiving a SHARE_PAD notification:
// Keep only one notification per channel: the stronger and more recent one
var mentions = {};
handlers['MENTION'] = function (ctx, box, data, cb) {
var msg = data.msg;
var hash = data.hash;
var content = msg.content;
if (isMuted(ctx, data)) { return void cb(true); }
var channel = content.channel;
if (!channel) { return void cb(true); }
var title, href;
ctx.Store.getAllStores().some(function (s) {
var res = s.manager.findChannel(channel);
// Check if the pad is in our drive
return res.some(function (obj) {
if (!obj.data) { return; }
if (href && !obj.data.href) { return; } // We already have the VIEW url, we need EDIT
href = obj.data.href || obj.data.roHref;
title = obj.data.filename || obj.data.title;
if (obj.data.href) { return true; } // Abort only if we have the EDIT url
});
});
2020-04-28 13:54:12 +00:00
// Add the title
content.href = href;
content.title = title;
// Remove duplicates
var old = mentions[channel];
var toRemove = old ? old.data : undefined;
// Update the data
mentions[channel] = {
data: {
type: box.type,
hash: hash
}
};
cb(false, toRemove);
};
removeHandlers['MENTION'] = function (ctx, box, data, hash) {
var content = data.content;
var channel = content.channel;
var old = mentions[channel];
if (old && old.data && old.data.hash === hash) {
delete mentions[channel];
}
};
2019-09-20 13:27:20 +00:00
// Broadcast
handlers['BROADCAST_MAINTENANCE'] = function (ctx, box, data, cb) {
var msg = data.msg;
var uid = msg.uid;
2021-03-30 15:41:12 +00:00
ctx.Store.onMaintenanceUpdate(uid);
cb(true);
};
2021-03-30 15:41:12 +00:00
var activeSurvey;
handlers['BROADCAST_SURVEY'] = function (ctx, box, data, cb) {
var msg = data.msg;
2021-03-30 15:59:54 +00:00
var content = msg.content;
var uid = msg.uid;
2021-03-30 15:41:12 +00:00
var old = activeSurvey;
activeSurvey = {
type: box.type,
hash: data.hash
};
2021-03-30 15:41:12 +00:00
ctx.Store.onSurveyUpdate(uid);
2021-03-30 15:59:54 +00:00
var dismiss = !content.url;
cb(dismiss, old);
};
2021-04-01 11:14:57 +00:00
var activeCustom;
handlers['BROADCAST_CUSTOM'] = function (ctx, box, data, cb) {
var msg = data.msg;
var uid = msg.uid;
2021-03-30 15:41:12 +00:00
var old = activeCustom;
activeCustom = {
uid: uid,
type: box.type,
hash: data.hash
};
2021-03-30 15:41:12 +00:00
cb(false, old);
};
handlers['BROADCAST_DELETE'] = function (ctx, box, data, cb) {
var msg = data.msg;
var content = msg.content;
var uid = content.uid; // uid of the message to delete
2021-03-30 15:41:12 +00:00
if (activeCustom && activeCustom.uid === uid) {
// We have the message in memory, remove it and don't keep the DELETE msg
cb(true, activeCustom);
activeCustom = undefined;
return;
}
2021-03-30 15:41:12 +00:00
// We don't have this message in memory, nothing to delete
cb(true);
};
2019-05-27 12:03:15 +00:00
return {
add: function (ctx, box, data, cb) {
/**
* data = {
msg: {
type: 'STRING',
author: 'curvePublicString',
content: {} (depend on the "type")
},
hash: 'string'
}
*/
if (!data.msg) { return void cb(true); }
// Check if the request is valid (sent by the correct user)
2020-08-31 09:00:46 +00:00
var myCurve = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
var curve = Util.find(data, ['msg', 'content', 'user', 'curvePublic']) ||
Util.find(data, ['msg', 'content', 'curvePublic']);
2020-08-31 09:00:46 +00:00
// Block messages that are not coming from the user described in the message
// except if the author is ourselves.
if (curve && data.msg.author !== curve && data.msg.author !== myCurve) {
console.error('blocked');
return void cb(true);
}
2019-05-27 12:03:15 +00:00
var type = data.msg.type;
if (handlers[type]) {
try {
handlers[type](ctx, box, data, cb);
} catch (e) {
console.error(e);
cb();
}
} else {
2019-05-22 16:03:52 +00:00
cb();
}
2019-05-27 12:03:15 +00:00
},
remove: function (ctx, box, data, h) {
// We sometimes try to delete non-existant data (with "delete box.content[h]")
// In this case, we don't have the data in memory so we don't need to call
// any "remove" handler
if (!data) { return; }
var type = data.type;
if (removeHandlers[type]) {
try {
removeHandlers[type](ctx, box, data, h);
} catch (e) {
console.error(e);
}
}
2019-05-22 16:03:52 +00:00
}
};
});