1 | 'use strict';
|
2 |
|
3 |
|
4 | var winston = module.parent.require('winston'),
|
5 | async = module.parent.require('async'),
|
6 | nconf = module.parent.require('nconf'),
|
7 |
|
8 | Meta = module.parent.require('./meta'),
|
9 | User = module.parent.require('./user'),
|
10 | Posts = module.parent.require('./posts'),
|
11 | Topics = module.parent.require('./topics'),
|
12 | hostEmailer = module.parent.require('./emailer'),
|
13 | Privileges = module.parent.require('./privileges'),
|
14 | SocketHelpers = module.parent.require('./socket.io/helpers'),
|
15 |
|
16 | mandrill,
|
17 | Emailer = {
|
18 | settings: {}
|
19 | };
|
20 |
|
21 | Emailer.init = function(data, callback) {
|
22 |
|
23 | var render = function(req, res) {
|
24 | res.render('admin/plugins/emailer-mandrill', {
|
25 | url: nconf.get('url')
|
26 | });
|
27 | };
|
28 |
|
29 | Meta.settings.get('mandrill', function(err, settings) {
|
30 | Emailer.settings = Object.freeze(settings);
|
31 | Emailer.receiptRegex = new RegExp('^reply-([\\d]+)@' + Emailer.settings.receive_domain + '$');
|
32 |
|
33 | if (!err && settings && settings.apiKey) {
|
34 | mandrill = require('node-mandrill')(settings.apiKey || 'Replace Me');
|
35 | } else {
|
36 | winston.error('[plugins/emailer-mandrill] API key not set!');
|
37 | }
|
38 |
|
39 | data.router.get('/admin/plugins/emailer-mandrill', data.middleware.admin.buildHeader, render);
|
40 | data.router.get('/api/admin/plugins/emailer-mandrill', render);
|
41 | data.router.head('/emailer-mandrill/reply', function(req, res) {
|
42 | res.sendStatus(200);
|
43 | });
|
44 | data.router.post('/emailer-mandrill/reply', Emailer.receive);
|
45 |
|
46 | if (typeof callback === 'function') {
|
47 | callback();
|
48 | }
|
49 | });
|
50 | };
|
51 |
|
52 | Emailer.send = function(data, callback) {
|
53 | if (mandrill) {
|
54 | var headers = {};
|
55 |
|
56 | if (data.pid && Emailer.settings.hasOwnProperty('receive_domain')) {
|
57 | headers['Reply-To'] = 'reply-' + data.pid + '@' + Emailer.settings.receive_domain;
|
58 | }
|
59 | async.waterfall([
|
60 | function(next) {
|
61 | if (data.fromUid) {
|
62 | next(null, data.fromUid);
|
63 | } else if (data.pid) {
|
64 | Posts.getPostField(data.pid, 'uid', next);
|
65 | } else {
|
66 | next(null, false);
|
67 | }
|
68 | },
|
69 | function(uid, next) {
|
70 | if (uid === false) { return next(null, {}); }
|
71 | User.getSettings(uid, function(err, settings) {
|
72 | if (err) {
|
73 | return next(err);
|
74 | }
|
75 |
|
76 | if (settings.showemail) {
|
77 | User.getUserFields(parseInt(uid, 10), ['email', 'username'], function(err, userData) {
|
78 | next(null, userData);
|
79 | });
|
80 | } else {
|
81 | User.getUserFields(parseInt(uid, 10), ['username'], function(err, userData) {
|
82 | next(null, userData);
|
83 | });
|
84 | }
|
85 | })
|
86 | },
|
87 | function(userData, next) {
|
88 | mandrill('/messages/send', {
|
89 | message: {
|
90 | to: [{email: data.to, name: data.toName}],
|
91 | subject: data.subject,
|
92 | from_email: userData.email || data.from,
|
93 | from_name: data.from_name || userData.username || undefined,
|
94 | html: data.html,
|
95 | text: data.plaintext,
|
96 | auto_text: !data.plaintext,
|
97 | headers: headers
|
98 | }
|
99 | }, next);
|
100 | }
|
101 | ], function (err) {
|
102 | if (!err) {
|
103 | winston.verbose('[emailer.mandrill] Sent `' + data.template + '` email to uid ' + data.uid);
|
104 | } else {
|
105 | winston.warn('[emailer.mandrill] Unable to send `' + data.template + '` email to uid ' + data.uid + '!!');
|
106 | winston.warn('[emailer.mandrill] Error Stringified:' + JSON.stringify(err));
|
107 | }
|
108 | callback(err, data);
|
109 | });
|
110 | } else {
|
111 | winston.warn('[plugins/emailer-mandrill] API key not set, not sending email as Mandrill object is not instantiated.');
|
112 | callback(new Error('[[error:mandril-api-key-not-set]]'));
|
113 | }
|
114 | };
|
115 |
|
116 | Emailer.receive = function(req, res) {
|
117 | var events;
|
118 |
|
119 | try {
|
120 | events = JSON.parse(req.body.mandrill_events);
|
121 | } catch (err) {
|
122 | winston.error('[emailer.mandrill] Error parsing response JSON from Mandrill API "Receive" webhook');
|
123 | return res.sendStatus(400);
|
124 | }
|
125 |
|
126 | winston.debug('POST from Mandrill contained ' + events.length + ' items');
|
127 |
|
128 | async.eachLimit(events, 5, function(eventObj, next) {
|
129 | async.waterfall([
|
130 | async.apply(Emailer.verifyEvent, eventObj),
|
131 | Emailer.resolveUserOrGuest,
|
132 | Emailer.processEvent,
|
133 | Emailer.notifyUsers
|
134 | ], function(err) {
|
135 | Emailer.handleError(err, eventObj);
|
136 | next();
|
137 | });
|
138 | }, function() {
|
139 | res.sendStatus(200);
|
140 | });
|
141 | };
|
142 |
|
143 | Emailer.verifyEvent = function(eventObj, next) {
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | var pid, match;
|
150 | eventObj.msg.to.forEach(function(recipient) {
|
151 | match = recipient[0].match(Emailer.receiptRegex);
|
152 | if (match && match[1]) { pid = match[1]; }
|
153 | });
|
154 |
|
155 | if (pid) {
|
156 | eventObj.pid = pid;
|
157 |
|
158 | Posts.getPostField(pid, 'tid', function(err, tid) {
|
159 | if (!err && tid) {
|
160 | eventObj.tid = tid;
|
161 | next(null, eventObj);
|
162 | } else {
|
163 | if (!tid) { winston.warn('[emailer.mandrill.verifyEvent] Could not retrieve tid'); }
|
164 | next(new Error('invalid-data'));
|
165 | }
|
166 | });
|
167 | } else {
|
168 | winston.warn('[emailer.mandrill.verifyEvent] Could not locate post id');
|
169 | next(new Error('invalid-data'));
|
170 | }
|
171 | };
|
172 |
|
173 | Emailer.resolveUserOrGuest = function(eventObj, callback) {
|
174 |
|
175 |
|
176 |
|
177 | User.getUidByEmail(eventObj.msg.from_email, function(err, uid) {
|
178 | if (uid) {
|
179 | eventObj.uid = uid;
|
180 | callback(null, eventObj);
|
181 | } else {
|
182 |
|
183 | async.waterfall([
|
184 | async.apply(Topics.getTopicField, eventObj.tid, 'cid'),
|
185 | function(cid, next) {
|
186 | Privileges.categories.groupPrivileges(cid, 'guests', next);
|
187 | }
|
188 | ], function(err, privileges) {
|
189 | if (privileges['groups:topics:reply']) {
|
190 | eventObj.uid = 0;
|
191 |
|
192 | if (parseInt(Meta.config.allowGuestHandles, 10) === 1) {
|
193 | if (eventObj.msg.from_name && eventObj.msg.from_name.length) {
|
194 | eventObj.handle = eventObj.msg.from_name;
|
195 | } else {
|
196 | eventObj.handle = eventObj.msg.from_email;
|
197 | }
|
198 | }
|
199 |
|
200 | callback(null, eventObj);
|
201 | } else {
|
202 |
|
203 | winston.verbose('[emailer.mandrill] Received reply by guest to pid ' + eventObj.pid + ', but guests are not allowed to post here.');
|
204 | callback(new Error('[[error:no-privileges]]'));
|
205 | }
|
206 | });
|
207 | }
|
208 | });
|
209 | };
|
210 |
|
211 | Emailer.processEvent = function(eventObj, callback) {
|
212 | winston.verbose('[emailer.mandrill] Processing incoming email reply by uid ' + eventObj.uid + ' to pid ' + eventObj.pid);
|
213 | Topics.reply({
|
214 | uid: eventObj.uid,
|
215 | toPid: eventObj.pid,
|
216 | tid: eventObj.tid,
|
217 | content: eventObj.msg.text,
|
218 | handle: (eventObj.uid === 0 && eventObj.hasOwnProperty('handle') ? eventObj.handle : undefined)
|
219 | }, callback);
|
220 | };
|
221 |
|
222 | Emailer.notifyUsers = function(postData, next) {
|
223 | var result = {
|
224 | posts: [postData],
|
225 | privileges: {
|
226 | 'topics:reply': true
|
227 | },
|
228 | 'reputation:disabled': parseInt(Meta.config['reputation:disabled'], 10) === 1,
|
229 | 'downvote:disabled': parseInt(Meta.config['downvote:disabled'], 10) === 1,
|
230 | };
|
231 |
|
232 | SocketHelpers.notifyOnlineUsers(parseInt(postData.uid, 10), result);
|
233 | next();
|
234 | };
|
235 |
|
236 | Emailer.handleError = function(err, eventObj) {
|
237 | if (err) {
|
238 | switch(err.message) {
|
239 | case '[[error:no-privileges]]':
|
240 | case 'invalid-data':
|
241 |
|
242 | hostEmailer.sendToEmail('bounce', eventObj.msg.from_email, Meta.config.defaultLang || 'en_GB', {
|
243 | site_title: Meta.config.title || 'NodeBB',
|
244 | subject: 'Re: ' + eventObj.msg.subject,
|
245 | messageBody: eventObj.msg.html
|
246 | }, function(err) {
|
247 | if (err) {
|
248 | winston.error('[emailer.mandrill] Unable to bounce email back to sender! ' + err.message);
|
249 | } else {
|
250 | winston.verbose('[emailer.mandrill] Bounced email back to sender (' + eventObj.msg.from_email + ')');
|
251 | }
|
252 | });
|
253 | break;
|
254 | }
|
255 | }
|
256 | };
|
257 |
|
258 | Emailer.admin = {
|
259 | menu: function(custom_header, callback) {
|
260 | custom_header.plugins.push({
|
261 | 'route': '/plugins/emailer-mandrill',
|
262 | 'icon': 'fa-envelope-o',
|
263 | 'name': 'Emailer (Mandrill)'
|
264 | });
|
265 |
|
266 | callback(null, custom_header);
|
267 | }
|
268 | };
|
269 |
|
270 | module.exports = Emailer;
|