2016-04-04 12:36:30 +00:00
'use strict' ;
let log = require ( 'npmlog' ) ;
2017-04-03 17:53:01 +00:00
let config = require ( 'config' ) ;
2016-04-04 12:36:30 +00:00
let tools = require ( '../lib/tools' ) ;
2017-03-19 12:36:57 +00:00
let helpers = require ( '../lib/helpers' ) ;
2016-04-04 12:36:30 +00:00
let passport = require ( '../lib/passport' ) ;
let express = require ( 'express' ) ;
let router = new express . Router ( ) ;
let lists = require ( '../lib/models/lists' ) ;
let fields = require ( '../lib/models/fields' ) ;
let subscriptions = require ( '../lib/models/subscriptions' ) ;
let settings = require ( '../lib/models/settings' ) ;
2016-04-16 21:09:23 +00:00
let openpgp = require ( 'openpgp' ) ;
2017-03-07 14:30:56 +00:00
let _ = require ( '../lib/translate' ) . _ ;
let util = require ( 'util' ) ;
2017-04-03 17:53:01 +00:00
let cors = require ( 'cors' ) ;
let cache = require ( 'memory-cache' ) ;
2017-05-03 19:46:49 +00:00
let geoip = require ( 'geoip-ultralight' ) ;
let confirmations = require ( '../lib/models/confirmations' ) ;
let mailHelpers = require ( '../lib/subscription-mail-helpers' ) ;
2017-04-03 17:53:01 +00:00
let originWhitelist = config . cors && config . cors . origins || [ ] ;
let corsOptions = {
allowedHeaders : [ 'Content-Type' , 'Origin' , 'Accept' , 'X-Requested-With' ] ,
methods : [ 'GET' , 'POST' ] ,
optionsSuccessStatus : 200 , // IE11 chokes on 204
origin : ( origin , callback ) => {
if ( originWhitelist . includes ( origin ) ) {
callback ( null , true ) ;
} else {
let err = new Error ( _ ( 'Not allowed by CORS' ) ) ;
err . status = 403 ;
callback ( err ) ;
}
}
} ;
let corsOrCsrfProtection = ( req , res , next ) => {
if ( req . get ( 'X-Requested-With' ) === 'XMLHttpRequest' ) {
cors ( corsOptions ) ( req , res , next ) ;
} else {
passport . csrfProtection ( req , res , next ) ;
}
} ;
2016-04-04 12:36:30 +00:00
2017-05-06 10:35:32 +00:00
function checkAndExecuteConfirmation ( req , action , errorMsg , next , exec ) {
2017-05-03 19:46:49 +00:00
confirmations . takeConfirmation ( req . params . cid , ( err , confirmation ) => {
2017-05-06 10:35:32 +00:00
if ( ! err && ( ! confirmation || confirmation . action !== action ) ) {
err = new Error ( _ ( errorMsg ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
lists . get ( confirmation . listId , ( err , list ) => {
2016-04-04 12:36:30 +00:00
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-06 10:35:32 +00:00
exec ( confirmation , list ) ;
} ) ;
} ) ;
}
router . get ( '/confirm/subscribe/:cid' , ( req , res , next ) => {
checkAndExecuteConfirmation ( req , 'subscribe' , 'Request invalid or already completed. If your subscription request is still pending, please subscribe again.' , next , ( confirmation , list ) => {
const data = confirmation . data ;
let optInCountry = geoip . lookupCountry ( confirmation . ip ) || null ;
const meta = {
2017-06-10 23:26:15 +00:00
cid : req . params . cid ,
2017-05-06 10:35:32 +00:00
email : data . email ,
optInIp : confirmation . ip ,
optInCountry ,
status : subscriptions . Status . SUBSCRIBED
} ;
subscriptions . insert ( list . id , meta , data . subscriptionData , ( err , result ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! result . entryId ) {
return next ( new Error ( _ ( 'Could not save subscription' ) ) ) ;
}
2016-04-21 17:17:19 +00:00
2017-05-06 10:35:32 +00:00
subscriptions . getById ( list . id , result . entryId , ( err , subscription ) => {
if ( err ) {
return next ( err ) ;
2016-06-30 21:06:46 +00:00
}
2017-05-06 10:35:32 +00:00
mailHelpers . sendSubscriptionConfirmed ( list , data . email , subscription , err => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-06 10:35:32 +00:00
res . redirect ( '/subscription/' + list . cid + '/subscribed-notice' ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
router . get ( '/confirm/change-address/:cid' , ( req , res , next ) => {
checkAndExecuteConfirmation ( req , 'change-address' , 'Request invalid or already completed. If your address change request is still pending, please change the address again.' , next , ( confirmation , list ) => {
const data = confirmation . data ;
2017-04-30 17:01:22 +00:00
2017-05-06 10:35:32 +00:00
if ( ! data . subscriptionId ) { // Something went terribly wrong and we don't have data that we have originally provided
return next ( new Error ( _ ( 'Subscriber info corrupted or missing' ) ) ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
subscriptions . updateAddress ( list . id , data . subscriptionId , data . emailNew , err => {
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
subscriptions . getById ( list . id , data . subscriptionId , ( err , subscription ) => {
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
mailHelpers . sendSubscriptionConfirmed ( list , data . emailNew , subscription , err => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-06 10:35:32 +00:00
req . flash ( 'info' , _ ( 'Email address changed' ) ) ;
res . redirect ( '/subscription/' + list . cid + '/manage/' + subscription . cid ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
router . get ( '/confirm/unsubscribe/:cid' , ( req , res , next ) => {
checkAndExecuteConfirmation ( req , 'unsubscribe' , 'Request invalid or already completed. If your unsubscription request is still pending, please unsubscribe again.' , next , ( confirmation , list ) => {
const data = confirmation . data ;
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
subscriptions . changeStatus ( list . id , confirmation . data . subscriptionId , confirmation . data . campaignId , subscriptions . Status . UNSUBSCRIBED , ( err , found ) => {
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
// TODO: Shall we do anything with "found"?
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
subscriptions . getById ( list . id , confirmation . data . subscriptionId , ( err , subscription ) => {
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-06 10:35:32 +00:00
mailHelpers . sendUnsubscriptionConfirmed ( list , subscription . email , subscription , err => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-06 10:35:32 +00:00
res . redirect ( '/subscription/' + list . cid + '/unsubscribed-notice' ) ;
2017-05-03 19:46:49 +00:00
} ) ;
2017-05-06 10:35:32 +00:00
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
} ) ;
router . get ( '/:cid' , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . cid , ( err , list ) => {
2017-04-14 12:48:49 +00:00
if ( ! err ) {
if ( ! list ) {
err = new Error ( _ ( 'Selected list not found' ) ) ;
err . status = 404 ;
} else if ( ! list . publicSubscribe ) {
err = new Error ( _ ( 'The list does not allow public subscriptions.' ) ) ;
err . status = 403 ;
}
2016-04-04 12:36:30 +00:00
}
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
// TODO: process subscriber cid param for resubscription requests
2017-04-30 17:01:22 +00:00
2016-04-04 12:36:30 +00:00
let data = tools . convertKeys ( req . query , {
skip : [ 'layout' ]
} ) ;
data . layout = 'subscription/layout' ;
data . title = list . name ;
data . cid = list . cid ;
data . csrfToken = req . csrfToken ( ) ;
2017-05-04 21:42:46 +00:00
function nextStep ( ) {
fields . list ( list . id , ( err , fieldList ) => {
if ( err && ! fieldList ) {
fieldList = [ ] ;
2016-04-16 21:09:23 +00:00
}
2017-03-19 12:36:57 +00:00
2017-05-04 21:42:46 +00:00
data . customFields = fields . getRow ( fieldList , data ) ;
data . useEditor = true ;
2017-03-19 12:36:57 +00:00
2017-05-04 21:42:46 +00:00
settings . list ( [ 'pgpPrivateKey' , 'defaultAddress' , 'defaultPostaddress' ] , ( err , configItems ) => {
2017-03-19 12:36:57 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
data . hasPubkey = ! ! configItems . pgpPrivateKey ;
data . defaultAddress = configItems . defaultAddress ;
data . defaultPostaddress = configItems . defaultPostaddress ;
2017-03-19 12:36:57 +00:00
2017-05-04 21:42:46 +00:00
data . template = {
template : 'subscription/web-subscribe.mjml.hbs' ,
layout : 'subscription/layout.mjml.hbs'
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-subscribe' , data , ( err , data ) => {
2017-03-19 12:36:57 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
helpers . getMjmlTemplate ( data . template , ( err , htmlRenderer ) => {
2017-03-19 12:36:57 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
helpers . captureFlashMessages ( req , res , ( err , flash ) => {
if ( err ) {
return next ( err ) ;
}
data . isWeb = true ;
data . needsJsWarning = true ;
data . flashMessages = flash ;
res . send ( htmlRenderer ( data ) ) ;
} ) ;
2017-03-19 12:36:57 +00:00
} ) ;
} ) ;
} ) ;
2016-04-16 21:09:23 +00:00
} ) ;
2017-05-04 21:42:46 +00:00
}
const ucid = req . query . cid ;
if ( ucid ) {
subscriptions . get ( list . id , ucid , ( err , subscription ) => {
if ( err ) {
return next ( err ) ;
}
for ( let key in subscription ) {
if ( ! ( key in data ) ) {
data [ key ] = subscription [ key ] ;
}
}
nextStep ( ) ;
} ) ;
} else {
nextStep ( ) ;
}
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
2017-04-03 17:53:01 +00:00
router . options ( '/:cid/widget' , cors ( corsOptions ) ) ;
router . get ( '/:cid/widget' , cors ( corsOptions ) , ( req , res , next ) => {
let cached = cache . get ( req . path ) ;
if ( cached ) {
return res . status ( 200 ) . json ( cached ) ;
}
let sendError = err => {
res . status ( err . status || 500 ) ;
res . json ( {
error : err . message || err
} ) ;
} ;
lists . getByCid ( req . params . cid , ( err , list ) => {
if ( ! err && ! list ) {
err = new Error ( _ ( 'Selected list not found' ) ) ;
err . status = 404 ;
}
if ( err ) {
return sendError ( err ) ;
}
fields . list ( list . id , ( err , fieldList ) => {
if ( err && ! fieldList ) {
fieldList = [ ] ;
}
settings . list ( [ 'serviceUrl' , 'pgpPrivateKey' ] , ( err , configItems ) => {
if ( err ) {
return sendError ( err ) ;
}
let data = {
title : list . name ,
cid : list . cid ,
serviceUrl : configItems . serviceUrl ,
hasPubkey : ! ! configItems . pgpPrivateKey ,
customFields : fields . getRow ( fieldList ) ,
2017-04-10 06:44:33 +00:00
template : { } ,
2017-04-03 17:53:01 +00:00
layout : null ,
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-subscribe' , data , ( err , data ) => {
if ( err ) {
return sendError ( err ) ;
}
res . render ( 'subscription/widget-subscribe' , data , ( err , html ) => {
if ( err ) {
return sendError ( err ) ;
}
let response = {
data : {
title : data . title ,
cid : data . cid ,
html
}
} ;
cache . put ( req . path , response , 30000 ) ; // ms
res . status ( 200 ) . json ( response ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
router . options ( '/:cid/subscribe' , cors ( corsOptions ) ) ;
router . post ( '/:cid/subscribe' , passport . parseForm , corsOrCsrfProtection , ( req , res , next ) => {
let sendJsonError = ( err , status ) => {
res . status ( status || err . status || 500 ) ;
res . json ( {
error : err . message || err
} ) ;
} ;
2016-04-04 12:36:30 +00:00
let email = ( req . body . email || '' ) . toString ( ) . trim ( ) ;
if ( ! email ) {
2017-04-03 17:53:01 +00:00
if ( req . xhr ) {
return sendJsonError ( _ ( 'Email address not set' ) , 400 ) ;
}
2017-03-07 14:30:56 +00:00
req . flash ( 'danger' , _ ( 'Email address not set' ) ) ;
2016-04-04 12:36:30 +00:00
return res . redirect ( '/subscription/' + encodeURIComponent ( req . params . cid ) + '?' + tools . queryParams ( req . body ) ) ;
}
2017-04-30 14:51:47 +00:00
tools . validateEmail ( email , false , err => {
if ( err ) {
if ( req . xhr ) {
return sendJsonError ( err . message , 400 ) ;
}
req . flash ( 'danger' , err . message ) ;
return res . redirect ( '/subscription/' + encodeURIComponent ( req . params . cid ) + '?' + tools . queryParams ( req . body ) ) ;
}
// Check if the subscriber seems legit. This is a really simple check, the only requirement is that
2017-05-03 19:46:49 +00:00
// the subscriber has JavaScript turned on and thats it. If Mailtrain gets more targeted then this
2017-04-30 14:51:47 +00:00
// simple check should be replaced with an actual captcha
let subTime = Number ( req . body . sub ) || 0 ;
// allow clock skew 24h in the past and 24h to the future
let subTimeTest = ! ! ( subTime > Date . now ( ) - 24 * 3600 * 1000 && subTime < Date . now ( ) + 24 * 3600 * 1000 ) ;
let addressTest = ! req . body . address ;
let testsPass = subTimeTest && addressTest ;
lists . getByCid ( req . params . cid , ( err , list ) => {
if ( ! err ) {
if ( ! list ) {
err = new Error ( _ ( 'Selected list not found' ) ) ;
err . status = 404 ;
} else if ( ! list . publicSubscribe ) {
err = new Error ( _ ( 'The list does not allow public subscriptions.' ) ) ;
err . status = 403 ;
}
}
2016-09-08 14:49:01 +00:00
2017-04-30 14:51:47 +00:00
if ( err ) {
return req . xhr ? sendJsonError ( err ) : next ( err ) ;
2017-04-14 12:48:49 +00:00
}
2016-04-04 12:36:30 +00:00
2017-05-03 19:46:49 +00:00
let subscriptionData = { } ;
2017-04-30 14:51:47 +00:00
Object . keys ( req . body ) . forEach ( key => {
if ( key !== 'email' && key . charAt ( 0 ) !== '_' ) {
2017-05-03 19:46:49 +00:00
subscriptionData [ key ] = ( req . body [ key ] || '' ) . toString ( ) . trim ( ) ;
2017-04-30 14:51:47 +00:00
}
} ) ;
2017-05-04 21:42:46 +00:00
subscriptionData = tools . convertKeys ( subscriptionData ) ;
2017-05-03 19:46:49 +00:00
subscriptions . getByEmail ( list . id , email , ( err , subscription ) => {
if ( err ) {
return req . xhr ? sendJsonError ( err ) : next ( err ) ;
}
2016-04-04 12:36:30 +00:00
2017-05-04 21:42:46 +00:00
if ( subscription && subscription . status === subscriptions . Status . SUBSCRIBED ) {
2017-05-03 19:46:49 +00:00
mailHelpers . sendAlreadySubscribed ( list , email , subscription , ( err ) => {
if ( err ) {
return req . xhr ? sendJsonError ( err ) : next ( err ) ;
}
res . redirect ( '/subscription/' + req . params . cid + '/confirm-subscription-notice' ) ;
} ) ;
} else {
const data = {
email ,
subscriptionData
} ;
2016-09-08 14:50:25 +00:00
2017-05-03 19:46:49 +00:00
confirmations . addConfirmation ( list . id , 'subscribe' , req . ip , data , ( err , confirmCid ) => {
if ( err ) {
if ( req . xhr ) {
return sendJsonError ( err ) ;
}
req . flash ( 'danger' , err . message || err ) ;
return res . redirect ( '/subscription/' + encodeURIComponent ( req . params . cid ) + '?' + tools . queryParams ( req . body ) ) ;
}
2016-04-04 12:36:30 +00:00
2017-05-03 19:46:49 +00:00
function sendWebResponse ( ) {
if ( req . xhr ) {
return res . status ( 200 ) . json ( {
msg : _ ( 'Please Confirm Subscription' )
} ) ;
}
res . redirect ( '/subscription/' + req . params . cid + '/confirm-subscription-notice' ) ;
}
2017-04-03 17:53:01 +00:00
2017-05-03 19:46:49 +00:00
if ( ! testsPass ) {
log . info ( 'Subscription' , 'Confirmation message for %s marked to be skipped (%s)' , email , JSON . stringify ( data ) ) ;
sendWebResponse ( ) ;
} else {
2017-06-10 23:26:15 +00:00
mailHelpers . sendConfirmSubscription ( list , email , confirmCid , subscriptionData , ( err ) => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return req . xhr ? sendJsonError ( err ) : sendWebResponse ( err ) ;
}
sendWebResponse ( ) ;
} )
}
2017-04-30 14:51:47 +00:00
} ) ;
2017-04-03 17:53:01 +00:00
}
2017-04-30 14:51:47 +00:00
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
} ) ;
router . get ( '/:lcid/manage/:ucid' , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
fields . list ( list . id , ( err , fieldList ) => {
if ( err ) {
return next ( err ) ;
}
subscriptions . get ( list . id , req . params . ucid , ( err , subscription ) => {
2017-05-04 21:42:46 +00:00
if ( ! err && ( ! subscription || subscription . status !== subscriptions . Status . SUBSCRIBED ) ) {
err = new Error ( _ ( 'Subscription not found in this list' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
subscription . lcid = req . params . lcid ;
subscription . title = list . name ;
subscription . csrfToken = req . csrfToken ( ) ;
subscription . layout = 'subscription/layout' ;
subscription . customFields = fields . getRow ( fieldList , subscription ) ;
subscription . useEditor = true ;
2017-03-19 12:36:57 +00:00
settings . list ( [ 'pgpPrivateKey' , 'defaultAddress' , 'defaultPostaddress' ] , ( err , configItems ) => {
2016-04-16 21:09:23 +00:00
if ( err ) {
return next ( err ) ;
}
subscription . hasPubkey = ! ! configItems . pgpPrivateKey ;
2017-03-19 12:36:57 +00:00
subscription . defaultAddress = configItems . defaultAddress ;
subscription . defaultPostaddress = configItems . defaultPostaddress ;
subscription . template = {
template : 'subscription/web-manage.mjml.hbs' ,
layout : 'subscription/layout.mjml.hbs'
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-manage' , subscription , ( err , data ) => {
if ( err ) {
return next ( err ) ;
}
2016-04-16 21:09:23 +00:00
2017-03-19 12:36:57 +00:00
helpers . getMjmlTemplate ( data . template , ( err , htmlRenderer ) => {
if ( err ) {
return next ( err ) ;
}
helpers . captureFlashMessages ( req , res , ( err , flash ) => {
if ( err ) {
return next ( err ) ;
}
data . isWeb = true ;
data . needsJsWarning = true ;
data . isManagePreferences = true ;
data . flashMessages = flash ;
res . send ( htmlRenderer ( data ) ) ;
} ) ;
} ) ;
} ) ;
2016-04-16 21:09:23 +00:00
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
} ) ;
} ) ;
router . post ( '/:lcid/manage' , passport . parseForm , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
subscriptions . get ( list . id , req . body . cid , ( err , subscription ) => {
if ( ! err && ( ! subscription || subscription . status !== subscriptions . Status . SUBSCRIBED ) ) {
err = new Error ( _ ( 'Subscription not found in this list' ) ) ;
err . status = 404 ;
}
2016-04-04 12:36:30 +00:00
if ( err ) {
2017-05-04 21:42:46 +00:00
return next ( err ) ;
2016-04-04 12:36:30 +00:00
}
2017-05-04 21:42:46 +00:00
subscriptions . update ( list . id , subscription . cid , req . body , false , err => {
if ( err ) {
return next ( err ) ;
}
res . redirect ( '/subscription/' + req . params . lcid + '/updated-notice' ) ;
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
} ) ;
2016-12-07 14:12:26 +00:00
router . get ( '/:lcid/manage-address/:ucid' , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-12-07 14:12:26 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-03-19 12:36:57 +00:00
settings . list ( [ 'defaultAddress' , 'defaultPostaddress' ] , ( err , configItems ) => {
if ( err ) {
return next ( err ) ;
2016-12-07 14:12:26 +00:00
}
2017-03-19 12:36:57 +00:00
subscriptions . get ( list . id , req . params . ucid , ( err , subscription ) => {
2017-05-04 21:42:46 +00:00
if ( ! err && ( ! subscription || subscription . status !== subscriptions . Status . SUBSCRIBED ) ) {
err = new Error ( _ ( 'Subscription not found in this list' ) ) ;
2017-03-19 12:36:57 +00:00
err . status = 404 ;
}
subscription . lcid = req . params . lcid ;
subscription . title = list . name ;
subscription . csrfToken = req . csrfToken ( ) ;
subscription . defaultAddress = configItems . defaultAddress ;
subscription . defaultPostaddress = configItems . defaultPostaddress ;
subscription . template = {
template : 'subscription/web-manage-address.mjml.hbs' ,
layout : 'subscription/layout.mjml.hbs'
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-manage-address' , subscription , ( err , data ) => {
if ( err ) {
return next ( err ) ;
}
helpers . getMjmlTemplate ( data . template , ( err , htmlRenderer ) => {
if ( err ) {
return next ( err ) ;
}
helpers . captureFlashMessages ( req , res , ( err , flash ) => {
if ( err ) {
return next ( err ) ;
}
2016-12-07 14:12:26 +00:00
2017-03-19 12:36:57 +00:00
data . isWeb = true ;
data . needsJsWarning = true ;
data . flashMessages = flash ;
res . send ( htmlRenderer ( data ) ) ;
} ) ;
} ) ;
} ) ;
} ) ;
2016-12-07 14:12:26 +00:00
} ) ;
} ) ;
} ) ;
router . post ( '/:lcid/manage-address' , passport . parseForm , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-12-07 14:12:26 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
let bodyData = tools . convertKeys ( req . body ) ; // This is here to convert "email-new" to "emailNew"
const emailOld = ( bodyData . email || '' ) . toString ( ) . trim ( ) ;
const emailNew = ( bodyData . emailNew || '' ) . toString ( ) . trim ( ) ;
2017-05-03 19:46:49 +00:00
2017-05-04 21:42:46 +00:00
if ( emailOld === emailNew ) {
req . flash ( 'info' , _ ( 'Nothing seems to be changed' ) ) ;
res . redirect ( '/subscription/' + req . params . lcid + '/manage/' + req . body . cid ) ;
2016-12-07 14:12:26 +00:00
2017-05-04 21:42:46 +00:00
} else {
subscriptions . updateAddressCheck ( list , req . body . cid , emailNew , req . ip , ( err , subscription , newEmailAvailable ) => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
function sendWebResponse ( err ) {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-04 21:42:46 +00:00
req . flash ( 'info' , _ ( 'An email with further instructions has been sent to the provided address' ) ) ;
res . redirect ( '/subscription/' + req . params . lcid + '/manage/' + req . body . cid ) ;
}
2017-05-03 19:46:49 +00:00
2017-05-04 21:42:46 +00:00
if ( newEmailAvailable ) {
const data = {
subscriptionId : subscription . id ,
emailNew
} ;
confirmations . addConfirmation ( list . id , 'change-address' , req . ip , data , ( err , confirmCid ) => {
if ( err ) {
return next ( err ) ;
}
mailHelpers . sendConfirmAddressChange ( list , emailNew , confirmCid , subscription , sendWebResponse ) ;
} ) ;
} else {
mailHelpers . sendAlreadySubscribed ( list , emailNew , subscription , sendWebResponse ) ;
}
} ) ;
}
2016-12-07 14:12:26 +00:00
} ) ;
} ) ;
2016-04-04 12:36:30 +00:00
router . get ( '/:lcid/unsubscribe/:ucid' , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-03-19 12:36:57 +00:00
settings . list ( [ 'defaultAddress' , 'defaultPostaddress' ] , ( err , configItems ) => {
2016-04-04 12:36:30 +00:00
if ( err ) {
return next ( err ) ;
}
2017-03-19 12:36:57 +00:00
subscriptions . get ( list . id , req . params . ucid , ( err , subscription ) => {
2017-05-04 21:42:46 +00:00
if ( ! err && ( ! subscription || subscription . status !== subscriptions . Status . SUBSCRIBED ) ) {
2017-05-03 19:46:49 +00:00
err = new Error ( _ ( 'Subscription not found in this list' ) ) ;
2017-03-19 12:36:57 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-26 22:43:56 +00:00
const autoUnsubscribe = req . query . auto === 'yes' ;
if ( autoUnsubscribe ) {
handleUnsubscribe ( list , subscription , autoUnsubscribe , req . query . c , req . ip , res , next ) ;
} else if ( req . query . formTest ||
2017-05-04 21:42:46 +00:00
list . unsubscriptionMode === lists . UnsubscriptionMode . ONE _STEP _WITH _FORM ||
2017-05-03 19:46:49 +00:00
list . unsubscriptionMode === lists . UnsubscriptionMode . TWO _STEP _WITH _FORM ) {
2017-03-19 12:36:57 +00:00
2017-05-03 19:46:49 +00:00
subscription . lcid = req . params . lcid ;
subscription . ucid = req . params . ucid ;
subscription . title = list . name ;
subscription . csrfToken = req . csrfToken ( ) ;
subscription . campaign = req . query . c ;
subscription . defaultAddress = configItems . defaultAddress ;
subscription . defaultPostaddress = configItems . defaultPostaddress ;
2017-03-19 12:36:57 +00:00
2017-05-03 19:46:49 +00:00
subscription . template = {
template : 'subscription/web-unsubscribe.mjml.hbs' ,
layout : 'subscription/layout.mjml.hbs'
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-unsubscribe' , subscription , ( err , data ) => {
2017-03-19 12:36:57 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
helpers . getMjmlTemplate ( data . template , ( err , htmlRenderer ) => {
2017-03-19 12:36:57 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
helpers . captureFlashMessages ( req , res , ( err , flash ) => {
if ( err ) {
return next ( err ) ;
}
data . isWeb = true ;
data . flashMessages = flash ;
res . send ( htmlRenderer ( data ) ) ;
} ) ;
2017-03-19 12:36:57 +00:00
} ) ;
} ) ;
2017-05-03 19:46:49 +00:00
} else { // UnsubscriptionMode.ONE_STEP || UnsubscriptionMode.TWO_STEP || UnsubscriptionMode.MANUAL
2017-05-26 22:43:56 +00:00
handleUnsubscribe ( list , subscription , autoUnsubscribe , req . query . c , req . ip , res , next ) ;
2017-05-03 19:46:49 +00:00
}
2017-03-19 12:36:57 +00:00
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
} ) ;
} ) ;
router . post ( '/:lcid/unsubscribe' , passport . parseForm , passport . csrfProtection , ( req , res , next ) => {
lists . getByCid ( req . params . lcid , ( err , list ) => {
if ( ! err && ! list ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Selected list not found' ) ) ;
2016-04-04 12:36:30 +00:00
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
const campaignId = ( req . body . campaign || '' ) . toString ( ) . trim ( ) || false ;
subscriptions . get ( list . id , req . body . ucid , ( err , subscription ) => {
2017-05-04 21:42:46 +00:00
if ( ! err && ( ! subscription || subscription . status !== subscriptions . Status . SUBSCRIBED ) ) {
2017-05-03 19:46:49 +00:00
err = new Error ( _ ( 'Subscription not found in this list' ) ) ;
err . status = 404 ;
}
2016-04-04 12:36:30 +00:00
if ( err ) {
2017-05-03 19:46:49 +00:00
return next ( err ) ;
2016-04-04 12:36:30 +00:00
}
2016-04-21 17:17:19 +00:00
2017-05-26 22:43:56 +00:00
handleUnsubscribe ( list , subscription , false , campaignId , req . ip , res , next ) ;
2017-05-03 19:46:49 +00:00
} ) ;
} ) ;
} ) ;
2017-05-26 22:43:56 +00:00
function handleUnsubscribe ( list , subscription , autoUnsubscribe , campaignId , ip , res , next ) {
if ( ( list . unsubscriptionMode === lists . UnsubscriptionMode . ONE _STEP || list . unsubscriptionMode === lists . UnsubscriptionMode . ONE _STEP _WITH _FORM ) ||
( autoUnsubscribe && ( list . unsubscriptionMode === lists . UnsubscriptionMode . TWO _STEP || list . unsubscriptionMode === lists . UnsubscriptionMode . TWO _STEP _WITH _FORM ) ) ) {
2017-05-03 19:46:49 +00:00
2017-05-26 22:43:56 +00:00
subscriptions . changeStatus ( list . id , subscription . id , campaignId , subscriptions . Status . UNSUBSCRIBED , ( err , found ) => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-06-10 23:26:15 +00:00
2017-05-26 22:43:56 +00:00
// TODO: Shall we do anything with "found"?
2017-05-03 19:46:49 +00:00
2017-05-26 22:43:56 +00:00
mailHelpers . sendUnsubscriptionConfirmed ( list , subscription . email , subscription , err => {
2016-04-21 17:17:19 +00:00
if ( err ) {
2017-05-03 19:46:49 +00:00
return next ( err ) ;
2016-04-21 17:17:19 +00:00
}
2017-05-26 22:43:56 +00:00
res . redirect ( '/subscription/' + list . cid + '/unsubscribed-notice' ) ;
2016-04-21 17:17:19 +00:00
} ) ;
2016-04-04 12:36:30 +00:00
} ) ;
2017-05-03 19:46:49 +00:00
2017-05-26 22:43:56 +00:00
} else if ( list . unsubscriptionMode === lists . UnsubscriptionMode . TWO _STEP || list . unsubscriptionMode === lists . UnsubscriptionMode . TWO _STEP _WITH _FORM ) {
2017-05-03 19:46:49 +00:00
2017-05-26 22:43:56 +00:00
const data = {
subscriptionId : subscription . id ,
campaignId
} ;
confirmations . addConfirmation ( list . id , 'unsubscribe' , ip , data , ( err , confirmCid ) => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-26 22:43:56 +00:00
mailHelpers . sendConfirmUnsubscription ( list , subscription . email , confirmCid , subscription , err => {
2017-05-03 19:46:49 +00:00
if ( err ) {
return next ( err ) ;
}
2017-05-26 22:43:56 +00:00
res . redirect ( '/subscription/' + list . cid + '/confirm-unsubscription-notice' ) ;
2017-05-03 19:46:49 +00:00
} ) ;
} ) ;
2017-05-26 22:43:56 +00:00
2017-05-03 19:46:49 +00:00
} else { // UnsubscriptionMode.MANUAL
res . redirect ( '/subscription/' + list . cid + '/manual-unsubscribe-notice' ) ;
}
}
2016-04-04 12:36:30 +00:00
2017-04-30 14:51:47 +00:00
router . get ( '/:cid/confirm-subscription-notice' , ( req , res , next ) => {
2017-05-03 19:46:49 +00:00
webNotice ( 'confirm-subscription' , req , res , next ) ;
2017-04-30 14:51:47 +00:00
} ) ;
router . get ( '/:cid/confirm-unsubscription-notice' , ( req , res , next ) => {
2017-05-03 19:46:49 +00:00
webNotice ( 'confirm-unsubscription' , req , res , next ) ;
2017-04-30 14:51:47 +00:00
} ) ;
router . get ( '/:cid/subscribed-notice' , ( req , res , next ) => {
2017-05-03 19:46:49 +00:00
webNotice ( 'subscribed' , req , res , next ) ;
2017-04-30 14:51:47 +00:00
} ) ;
router . get ( '/:cid/updated-notice' , ( req , res , next ) => {
2017-05-03 19:46:49 +00:00
webNotice ( 'updated' , req , res , next ) ;
2017-04-30 14:51:47 +00:00
} ) ;
router . get ( '/:cid/unsubscribed-notice' , ( req , res , next ) => {
2017-05-03 19:46:49 +00:00
webNotice ( 'unsubscribed' , req , res , next ) ;
} ) ;
router . get ( '/:cid/manual-unsubscribe-notice' , ( req , res , next ) => {
webNotice ( 'manual-unsubscribe' , req , res , next ) ;
2017-04-30 14:51:47 +00:00
} ) ;
2017-04-03 17:53:01 +00:00
router . post ( '/publickey' , passport . parseForm , ( req , res , next ) => {
2016-04-16 21:09:23 +00:00
settings . list ( [ 'pgpPassphrase' , 'pgpPrivateKey' ] , ( err , configItems ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! configItems . pgpPrivateKey ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Public key is not set' ) ) ;
2016-04-16 21:09:23 +00:00
err . status = 404 ;
return next ( err ) ;
}
let privKey ;
try {
privKey = openpgp . key . readArmored ( configItems . pgpPrivateKey ) . keys [ 0 ] ;
if ( configItems . pgpPassphrase && ! privKey . decrypt ( configItems . pgpPassphrase ) ) {
privKey = false ;
}
} catch ( E ) {
// just ignore if failed
}
if ( ! privKey ) {
2017-03-07 14:30:56 +00:00
err = new Error ( _ ( 'Public key is not set' ) ) ;
2016-04-16 21:09:23 +00:00
err . status = 404 ;
return next ( err ) ;
}
let pubkey = privKey . toPublic ( ) . armor ( ) ;
res . writeHead ( 200 , {
'Content-Type' : 'application/octet-stream' ,
'Content-Disposition' : 'attachment; filename=public.asc'
} ) ;
res . end ( pubkey ) ;
} ) ;
} ) ;
2017-04-30 14:51:47 +00:00
2017-05-03 19:46:49 +00:00
function webNotice ( type , req , res , next ) {
2017-04-30 14:51:47 +00:00
lists . getByCid ( req . params . cid , ( err , list ) => {
if ( ! err && ! list ) {
err = new Error ( _ ( 'Selected list not found' ) ) ;
err . status = 404 ;
}
if ( err ) {
return next ( err ) ;
}
2017-05-03 19:46:49 +00:00
settings . list ( [ 'defaultHomepage' , 'serviceUrl' , 'defaultAddress' , 'defaultPostaddress' , 'adminEmail' ] , ( err , configItems ) => {
2017-04-30 14:51:47 +00:00
if ( err ) {
return next ( err ) ;
}
let data = {
title : list . name ,
homepage : configItems . defaultHomepage || configItems . serviceUrl ,
defaultAddress : configItems . defaultAddress ,
defaultPostaddress : configItems . defaultPostaddress ,
2017-05-03 19:46:49 +00:00
contactAddress : configItems . defaultAddress ,
2017-04-30 14:51:47 +00:00
template : {
template : 'subscription/web-' + type + '-notice.mjml.hbs' ,
layout : 'subscription/layout.mjml.hbs'
}
} ;
helpers . injectCustomFormData ( req . query . fid || list . defaultForm , 'subscription/web-' + type + '-notice' , data , ( err , data ) => {
if ( err ) {
return next ( err ) ;
}
helpers . getMjmlTemplate ( data . template , ( err , htmlRenderer ) => {
if ( err ) {
return next ( err ) ;
}
helpers . captureFlashMessages ( req , res , ( err , flash ) => {
if ( err ) {
return next ( err ) ;
}
data . isWeb = true ;
data . isConfirmNotice = true ;
data . flashMessages = flash ;
res . send ( htmlRenderer ( data ) ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
}
2016-04-04 12:36:30 +00:00
module . exports = router ;