2013-01-31 01:57:12 +00:00
|
|
|
var db = require('../../src/node/db/DB').db,
|
|
|
|
API = require('../../src/node/db/API.js'),
|
|
|
|
async = require('../../src/node_modules/async'),
|
2013-04-02 03:14:01 +00:00
|
|
|
email = require('emailjs'),
|
|
|
|
randomString = require('../../src/static/js/pad_utils').randomString;
|
2013-01-31 01:57:12 +00:00
|
|
|
settings = require('../../src/node/utils/Settings');
|
|
|
|
|
|
|
|
var pluginSettings = settings.ep_email_notifications;
|
2013-04-04 18:39:43 +00:00
|
|
|
var areParamsOk = (pluginSettings)?true:false;
|
2013-04-04 12:57:54 +00:00
|
|
|
var fromName = (pluginSettings && pluginSettings.fromName)?pluginSettings.fromName:"Etherpad";
|
|
|
|
var fromEmail = (pluginSettings && pluginSettings.fromEmail)?pluginSettings.fromEmail:"pad@etherpad.org";
|
|
|
|
var urlToPads = (pluginSettings && pluginSettings.urlToPads)?pluginSettings.urlToPads:"http://beta.etherpad.org/p/";
|
|
|
|
var emailServer = (pluginSettings && pluginSettings.emailServer)?pluginSettings.emailServer:{host:"127.0.0.1"};
|
2013-04-02 03:14:01 +00:00
|
|
|
|
2013-04-04 18:39:43 +00:00
|
|
|
if (areParamsOk == false) console.warn("Settings for ep_email_notifications plugin are missing in settings.json file");
|
|
|
|
|
2013-04-02 03:14:01 +00:00
|
|
|
// Connect to the email server -- This might not be the ideal place to connect but it stops us having lots of connections
|
|
|
|
var server = email.server.connect(emailServer);
|
2013-01-31 01:57:12 +00:00
|
|
|
|
|
|
|
// When a new message comes in from the client - FML this is ugly
|
|
|
|
exports.handleMessage = function(hook_name, context, callback){
|
|
|
|
if (context.message && context.message.data){
|
|
|
|
if (context.message.data.type == 'USERINFO_UPDATE' ) { // if it's a request to update an authors email
|
2013-04-04 18:39:43 +00:00
|
|
|
if (areParamsOk == false) {
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailNotificationMissingParams",
|
|
|
|
payload: true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
console.warn("Settings for ep_email_notifications plugin are missing in settings.json file");
|
2013-04-04 19:09:57 +00:00
|
|
|
|
|
|
|
callback([null]); // don't run onto passing colorId or anything else to the message handler
|
|
|
|
|
2013-04-04 18:39:43 +00:00
|
|
|
} else if (context.message.data.userInfo){
|
2013-01-31 01:57:12 +00:00
|
|
|
if(context.message.data.userInfo.email){ // it contains email
|
|
|
|
console.debug(context.message);
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
// does email (Un)Subscription already exist for this email address?
|
2013-01-31 01:57:12 +00:00
|
|
|
db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){
|
|
|
|
|
|
|
|
var alreadyExists = false;
|
2013-03-30 00:34:19 +00:00
|
|
|
|
2013-01-31 01:57:12 +00:00
|
|
|
if(userIds){
|
|
|
|
async.forEach(Object.keys(userIds), function(user, cb){
|
2013-03-26 20:40:02 +00:00
|
|
|
console.debug("UserIds subscribed by email to this pad:", userIds);
|
2013-03-26 19:44:34 +00:00
|
|
|
if(user == context.message.data.userInfo.email){ // If we already have this email registered for this pad
|
2013-01-31 01:57:12 +00:00
|
|
|
// This user ID is already assigned to this padId so don't do anything except tell the user they are already subscribed somehow..
|
|
|
|
alreadyExists = true;
|
2013-03-30 00:34:19 +00:00
|
|
|
|
|
|
|
if(context.message.data.userInfo.email_option == 'subscribe') {
|
|
|
|
// Subscription process
|
|
|
|
exports.subscriptionEmail(
|
|
|
|
context,
|
|
|
|
context.message.data.userInfo.email,
|
|
|
|
alreadyExists,
|
|
|
|
context.message.data.userInfo,
|
|
|
|
context.message.data.padId,
|
|
|
|
callback
|
|
|
|
);
|
|
|
|
} else if (context.message.data.userInfo.email_option == 'unsubscribe') {
|
|
|
|
// Unsubscription process
|
|
|
|
exports.unsubscriptionEmail(
|
|
|
|
context,
|
|
|
|
alreadyExists,
|
|
|
|
context.message.data.userInfo,
|
|
|
|
context.message.data.padId
|
|
|
|
);
|
|
|
|
}
|
2013-01-31 01:57:12 +00:00
|
|
|
}
|
|
|
|
cb();
|
|
|
|
},
|
|
|
|
|
|
|
|
function(err){
|
|
|
|
// There should be something in here!
|
|
|
|
}); // end async for each
|
|
|
|
}
|
2013-03-26 19:44:34 +00:00
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
// In case we didn't find it in the Db
|
|
|
|
if (alreadyExists == false) {
|
|
|
|
if(context.message.data.userInfo.email_option == 'subscribe') {
|
|
|
|
// Subscription process
|
|
|
|
exports.subscriptionEmail(
|
|
|
|
context,
|
|
|
|
context.message.data.userInfo.email,
|
|
|
|
alreadyExists,
|
2013-03-26 19:44:34 +00:00
|
|
|
context.message.data.userInfo,
|
2013-03-30 00:34:19 +00:00
|
|
|
context.message.data.padId,
|
2013-03-27 01:19:27 +00:00
|
|
|
callback
|
2013-03-26 19:44:34 +00:00
|
|
|
);
|
2013-03-30 00:34:19 +00:00
|
|
|
} else if (context.message.data.userInfo.email_option == 'unsubscribe') {
|
|
|
|
// Unsubscription process
|
|
|
|
exports.unsubscriptionEmail(
|
|
|
|
context,
|
|
|
|
alreadyExists,
|
2013-03-26 19:44:34 +00:00
|
|
|
context.message.data.userInfo,
|
|
|
|
context.message.data.padId
|
|
|
|
);
|
2013-03-27 01:19:27 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-30 00:34:19 +00:00
|
|
|
|
2013-01-31 01:57:12 +00:00
|
|
|
}); // close db get
|
|
|
|
|
2013-03-19 22:29:34 +00:00
|
|
|
callback([null]); // don't run onto passing colorId or anything else to the message handler
|
2013-01-31 01:57:12 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2013-03-30 00:34:19 +00:00
|
|
|
|
|
|
|
} else if (context.message.data.type == 'USERINFO_GET' ) { // A request to find datas for a userId
|
2013-03-26 19:44:34 +00:00
|
|
|
if (context.message.data.userInfo){
|
|
|
|
if(context.message.data.userInfo.userId){ // it contains the userId
|
|
|
|
console.debug(context.message);
|
|
|
|
|
2013-03-27 01:19:27 +00:00
|
|
|
// does email Subscription already exist for this UserId?
|
2013-03-26 19:44:34 +00:00
|
|
|
db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){
|
2013-03-30 00:34:19 +00:00
|
|
|
var userIdFound = false;
|
|
|
|
|
2013-03-26 19:44:34 +00:00
|
|
|
if(userIds){
|
|
|
|
async.forEach(Object.keys(userIds), function(user, cb){
|
|
|
|
if(userIds[user].authorId == context.message.data.userInfo.userId){ // if we find the same Id in the Db as the one used by the user
|
2013-03-26 20:40:02 +00:00
|
|
|
console.debug("Options for this pad ", userIds[user].authorId, " found in the Db");
|
2013-03-26 19:44:34 +00:00
|
|
|
userIdFound = true;
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
// Request user subscription info process
|
|
|
|
exports.sendUserInfo (
|
|
|
|
context,
|
|
|
|
userIdFound,
|
|
|
|
user,
|
|
|
|
userIds[user]
|
|
|
|
);
|
2013-03-26 19:44:34 +00:00
|
|
|
}
|
|
|
|
cb();
|
|
|
|
},
|
|
|
|
|
|
|
|
function(err){
|
|
|
|
// There should be something in here!
|
|
|
|
}); // end async for each
|
|
|
|
}
|
2013-03-30 00:34:19 +00:00
|
|
|
|
|
|
|
if (userIdFound == false) {
|
|
|
|
// Request user subscription info process
|
|
|
|
exports.sendUserInfo (
|
|
|
|
context,
|
|
|
|
userIdFound,
|
2013-03-30 00:51:31 +00:00
|
|
|
"",
|
2013-03-30 00:34:19 +00:00
|
|
|
""
|
|
|
|
);
|
|
|
|
}
|
2013-03-26 19:44:34 +00:00
|
|
|
});
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
callback([null]);
|
2013-03-26 19:44:34 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-31 01:57:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
/**
|
|
|
|
* Subscription process
|
|
|
|
*/
|
|
|
|
exports.subscriptionEmail = function (context, email, emailFound, userInfo, padId, callback) {
|
|
|
|
var validatesAsEmail = exports.checkEmailValidation(email);
|
2013-04-02 03:14:01 +00:00
|
|
|
var subscribeId = randomString(25);
|
2013-03-30 00:34:19 +00:00
|
|
|
if(emailFound == false && validatesAsEmail){
|
|
|
|
// Subscription -> Go for it
|
|
|
|
console.debug ("Subscription: Wrote to the database and sent client a positive response ",context.message.data.userInfo.email);
|
|
|
|
|
|
|
|
exports.setAuthorEmailRegistered(
|
|
|
|
userInfo,
|
|
|
|
userInfo.userId,
|
2013-04-02 03:14:01 +00:00
|
|
|
subscribeId,
|
2013-03-30 00:34:19 +00:00
|
|
|
padId
|
|
|
|
);
|
|
|
|
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailSubscriptionSuccess",
|
|
|
|
payload: {
|
|
|
|
formName: userInfo.formName,
|
|
|
|
success: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-04-02 03:14:01 +00:00
|
|
|
|
|
|
|
// Send mail to user with the link for validation
|
|
|
|
server.send(
|
|
|
|
{
|
|
|
|
text: "Please click on this link in order to validate your subscription to the pad " + padId + "\n" + urlToPads+padId + "/subscribe=" + subscribeId,
|
|
|
|
from: fromName+ "<"+fromEmail+">",
|
|
|
|
to: userInfo.email,
|
|
|
|
subject: "Email subscription confirmation for pad "+padId
|
|
|
|
},
|
|
|
|
function(err, message) {
|
|
|
|
console.log(err || message);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
} else if (!validatesAsEmail) {
|
|
|
|
// Subscription -> failed coz mail malformed.. y'know in general fuck em!
|
|
|
|
console.warn("Dropped email subscription due to malformed email address");
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailSubscriptionSuccess",
|
|
|
|
payload: {
|
|
|
|
type: "malformedEmail",
|
|
|
|
formName: userInfo.formName,
|
|
|
|
success: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Subscription -> failed coz email already subscribed for this pad
|
|
|
|
console.debug("email ", context.message.data.userInfo.email, "already subscribed to ", context.message.data.padId, " so sending message to client");
|
|
|
|
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailSubscriptionSuccess",
|
|
|
|
payload: {
|
|
|
|
type: "alreadyRegistered",
|
|
|
|
formName: userInfo.formName,
|
|
|
|
success: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* UnsUbscription process
|
|
|
|
*/
|
|
|
|
exports.unsubscriptionEmail = function (context, emailFound, userInfo, padId) {
|
2013-04-02 03:14:01 +00:00
|
|
|
var unsubscribeId = randomString(25);
|
|
|
|
|
2013-03-30 00:34:19 +00:00
|
|
|
if(emailFound == true) {
|
|
|
|
// Unsubscription -> Go for it
|
|
|
|
console.debug ("Unsubscription: Remove from the database and sent client a positive response ",context.message.data.userInfo.email);
|
|
|
|
|
|
|
|
exports.unsetAuthorEmailRegistered(
|
|
|
|
userInfo,
|
|
|
|
userInfo.userId,
|
2013-04-02 03:14:01 +00:00
|
|
|
unsubscribeId,
|
2013-03-30 00:34:19 +00:00
|
|
|
padId
|
|
|
|
);
|
|
|
|
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailUnsubscriptionSuccess",
|
|
|
|
payload: {
|
|
|
|
formName: userInfo.formName,
|
|
|
|
success: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-04-02 03:14:01 +00:00
|
|
|
|
|
|
|
// Send mail to user with the link for validation
|
|
|
|
server.send(
|
|
|
|
{
|
|
|
|
text: "Please click on this link in order to validate your unsubscription to the pad " + padId + "\n" + urlToPads+padId + "/unsubscribe=" + unsubscribeId,
|
|
|
|
from: fromName+ "<"+fromEmail+">",
|
|
|
|
to: userInfo.email,
|
|
|
|
subject: "Email unsubscription confirmation for pad "+padId
|
|
|
|
},
|
|
|
|
function(err, message) {
|
|
|
|
console.log(err || message);
|
|
|
|
}
|
|
|
|
);
|
2013-03-30 00:34:19 +00:00
|
|
|
} else {
|
|
|
|
// Unsubscription -> Send failed as email not found
|
|
|
|
console.debug ("Unsubscription: Send client a negative response ",context.message.data.userInfo.email);
|
|
|
|
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailUnsubscriptionSuccess",
|
|
|
|
payload: {
|
|
|
|
formName: userInfo.formName,
|
|
|
|
success: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request user subscription info process
|
|
|
|
*/
|
|
|
|
exports.sendUserInfo = function (context, emailFound, email, userInfo) {
|
|
|
|
var defaultOnStartOption = true;
|
|
|
|
var defaultOnEndOption = false;
|
|
|
|
|
|
|
|
if (typeof userInfo.onStart == 'boolean' && typeof userInfo.onEnd == 'boolean') {
|
|
|
|
var onStart = userInfo.onStart;
|
|
|
|
var onEnd = userInfo.onEnd;
|
|
|
|
} else { // In case these options are not yet defined for this userId
|
|
|
|
var onStart = defaultOnStartOption;
|
|
|
|
var onEnd = defaultOnEndOption;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emailFound == true) {
|
|
|
|
// We send back the options associated to this userId
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailNotificationGetUserInfo",
|
|
|
|
payload: {
|
|
|
|
email: email,
|
|
|
|
onStart: onStart,
|
|
|
|
onEnd: onEnd,
|
|
|
|
formName: context.message.data.userInfo.formName,
|
|
|
|
success:true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// No options set for this userId
|
|
|
|
context.client.json.send({ type: "COLLABROOM",
|
|
|
|
data:{
|
|
|
|
type: "emailNotificationGetUserInfo",
|
|
|
|
payload: {
|
|
|
|
formName: context.message.data.userInfo.formName,
|
|
|
|
success:false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to check if an email is valid
|
|
|
|
*/
|
|
|
|
exports.checkEmailValidation = function (email) {
|
2014-03-17 19:01:03 +00:00
|
|
|
var validator = require('validator');
|
|
|
|
if(validator.isEmail(email)){
|
|
|
|
return true;
|
|
|
|
}else{
|
2013-03-30 00:34:19 +00:00
|
|
|
return false;
|
2014-03-17 19:01:03 +00:00
|
|
|
}
|
2013-03-30 00:34:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Database manipulation
|
|
|
|
*/
|
|
|
|
|
2013-04-02 03:14:01 +00:00
|
|
|
// Write email, options, authorId and pendingId to the database
|
|
|
|
exports.setAuthorEmailRegistered = function(userInfo, authorId, subscribeId, padId){
|
2013-01-31 01:57:12 +00:00
|
|
|
var timestamp = new Date().getTime();
|
|
|
|
var registered = {
|
|
|
|
authorId: authorId,
|
2013-03-30 00:34:19 +00:00
|
|
|
onStart: userInfo.email_onStart,
|
|
|
|
onEnd: userInfo.email_onEnd,
|
2013-04-02 03:14:01 +00:00
|
|
|
subscribeId: subscribeId,
|
2013-01-31 01:57:12 +00:00
|
|
|
timestamp: timestamp
|
|
|
|
};
|
|
|
|
console.debug("registered", registered, " to ", padId);
|
2013-04-02 03:14:01 +00:00
|
|
|
|
2013-01-31 01:57:12 +00:00
|
|
|
// Here we have to basically hack a new value into the database, this isn't clean or polite.
|
|
|
|
db.get("emailSubscription:" + padId, function(err, value){ // get the current value
|
2013-04-02 03:14:01 +00:00
|
|
|
if(!value){
|
|
|
|
// if an emailSubscription doesnt exist yet for this padId don't panic
|
|
|
|
value = {"pending":{}};
|
|
|
|
} else if (!value['pending']) {
|
|
|
|
// if the pending section doesn't exist yet for this padId, we create it
|
|
|
|
value['pending'] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the registered values to the pending section of the object
|
|
|
|
value['pending'][userInfo.email] = registered;
|
|
|
|
|
2013-04-04 21:24:24 +00:00
|
|
|
// Write the modified datas back in the Db
|
2013-01-31 01:57:12 +00:00
|
|
|
db.set("emailSubscription:" + padId, value); // stick it in the database
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-04-02 03:14:01 +00:00
|
|
|
// Write email, authorId and pendingId to the database
|
|
|
|
exports.unsetAuthorEmailRegistered = function(userInfo, authorId, unsubscribeId, padId){
|
|
|
|
var timestamp = new Date().getTime();
|
|
|
|
var registered = {
|
|
|
|
authorId: authorId,
|
|
|
|
unsubscribeId: unsubscribeId,
|
|
|
|
timestamp: timestamp
|
|
|
|
};
|
2013-03-30 00:34:19 +00:00
|
|
|
console.debug("unregistered", userInfo.email, " to ", padId);
|
2013-03-27 01:19:27 +00:00
|
|
|
|
2013-03-26 19:44:34 +00:00
|
|
|
db.get("emailSubscription:" + padId, function(err, value){ // get the current value
|
2013-04-02 03:14:01 +00:00
|
|
|
// if the pending section doesn't exist yet for this padId, we create it (this shouldn't happen)
|
|
|
|
if (!value['pending']) {value['pending'] = {};}
|
2013-03-27 01:19:27 +00:00
|
|
|
|
2013-04-02 03:14:01 +00:00
|
|
|
// add the registered values to the pending section of the object
|
|
|
|
value['pending'][userInfo.email] = registered;
|
2013-03-27 01:19:27 +00:00
|
|
|
|
|
|
|
// Write the modified datas back in the Db
|
|
|
|
db.set("emailSubscription:" + padId, value);
|
2013-03-26 19:44:34 +00:00
|
|
|
});
|
|
|
|
}
|