|
@@ -1,5 +1,6 @@
|
1
|
1
|
var pug = require('pug');
|
2
|
2
|
var nodemailer = require('nodemailer');
|
|
3
|
+var crypto = require('crypto');
|
3
|
4
|
var settings = require('./settings');
|
4
|
5
|
|
5
|
6
|
// Web server
|
|
@@ -19,6 +20,10 @@ var log = printit({
|
19
|
20
|
var transporter = nodemailer.createTransport(settings.mailserver);
|
20
|
21
|
|
21
|
22
|
|
|
23
|
+// Verification tokens
|
|
24
|
+var tokens = {};
|
|
25
|
+
|
|
26
|
+
|
22
|
27
|
// Serve static (JS + HTML) files
|
23
|
28
|
app.use(express.static('front'));
|
24
|
29
|
// Body parsing
|
|
@@ -26,8 +31,40 @@ app.use(bodyParser.urlencoded({ extended: true }));
|
26
|
31
|
app.use(bodyParser.json());
|
27
|
32
|
|
28
|
33
|
|
|
34
|
+// A request on /register generates a token and store it, along the user's
|
|
35
|
+// address, on the tokens object
|
|
36
|
+app.get('/register', function(req, res, next) {
|
|
37
|
+ // Get IP from express
|
|
38
|
+ let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
|
39
|
+ if(tokens[ip] === undefined) {
|
|
40
|
+ tokens[ip] = [];
|
|
41
|
+ }
|
|
42
|
+ // Generate token
|
|
43
|
+ crypto.randomBytes(10, (err, buf) => {
|
|
44
|
+ let token = buf.toString('hex');
|
|
45
|
+ // Store and send the token
|
|
46
|
+ tokens[ip].push({
|
|
47
|
+ token: token,
|
|
48
|
+ // A token expires after 12h
|
|
49
|
+ expire: new Date().getTime() + 12 * 3600 * 1000
|
|
50
|
+ });
|
|
51
|
+ res.status(200).send(token);
|
|
52
|
+ });
|
|
53
|
+});
|
|
54
|
+
|
|
55
|
+
|
29
|
56
|
// A request on /send with user input = mail to be sent
|
30
|
57
|
app.post('/send', function(req, res, next) {
|
|
58
|
+ if(!checkBody(req.body)) {
|
|
59
|
+ return res.status(400).send();
|
|
60
|
+ }
|
|
61
|
+
|
|
62
|
+ let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
|
63
|
+
|
|
64
|
+ if(!checkToken(ip, req.body.token)) {
|
|
65
|
+ return res.status(403).send();
|
|
66
|
+ }
|
|
67
|
+
|
31
|
68
|
// Count the failures
|
32
|
69
|
let status = {
|
33
|
70
|
failed: 0,
|
|
@@ -53,13 +90,12 @@ app.post('/send', function(req, res, next) {
|
53
|
90
|
// Send the email to all users
|
54
|
91
|
sendMails(params, function(err, infos) {
|
55
|
92
|
if(err) {
|
56
|
|
- log.error(err)
|
|
93
|
+ log.error(err);
|
57
|
94
|
}
|
58
|
95
|
logStatus(infos);
|
59
|
96
|
}, function() {
|
60
|
|
- res.header('Access-Control-Allow-Origin', '*');
|
61
|
97
|
if(status.failed === status.total) {
|
62
|
|
- res.status(500).send()
|
|
98
|
+ res.status(500).send();
|
63
|
99
|
} else {
|
64
|
100
|
res.status(200).send();
|
65
|
101
|
}
|
|
@@ -75,6 +111,10 @@ app.listen(port, function() {
|
75
|
111
|
});
|
76
|
112
|
|
77
|
113
|
|
|
114
|
+// Run the clean every hour
|
|
115
|
+var tokensChecks = setTimeout(cleanTokens, 3600 * 1000);
|
|
116
|
+
|
|
117
|
+
|
78
|
118
|
// Send mails to the recipients specified in the JSON settings file
|
79
|
119
|
// content: object containing mail params
|
80
|
120
|
// {
|
|
@@ -117,4 +157,72 @@ function logStatus(infos) {
|
117
|
157
|
status.failed++;
|
118
|
158
|
log.info('Message failed to send to ' + infos.rejected[0]);
|
119
|
159
|
}
|
|
160
|
+}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+// Checks if the request's sender has been registered (and unregister it if not)
|
|
164
|
+// ip: sender's IP address
|
|
165
|
+// token: token used by the sender
|
|
166
|
+// return: true if the user was registered, false else
|
|
167
|
+function checkToken(ip, token) {
|
|
168
|
+ let verified = false;
|
|
169
|
+
|
|
170
|
+ // Check if there's at least one token for this IP
|
|
171
|
+ if(tokens[ip] !== undefined) {
|
|
172
|
+ if(tokens[ip].length !== 0) {
|
|
173
|
+ // There's at least one element for this IP, let's check the tokens
|
|
174
|
+ for(var i = 0; i < tokens[ip].length; i++) {
|
|
175
|
+ if(!tokens[ip][i].token.localeCompare(token)) {
|
|
176
|
+ // We found the right token
|
|
177
|
+ verified = true;
|
|
178
|
+ // Removing the token
|
|
179
|
+ tokens[ip].pop(tokens[ip][i]);
|
|
180
|
+ break;
|
|
181
|
+ }
|
|
182
|
+ }
|
|
183
|
+ }
|
|
184
|
+ }
|
|
185
|
+
|
|
186
|
+ if(!verified) {
|
|
187
|
+ log.warn(ip + ' just tried to send a message with an invalid token');
|
|
188
|
+ }
|
|
189
|
+
|
|
190
|
+ return verified;
|
|
191
|
+}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+// Checks if all the required fields are in the request body
|
|
195
|
+// body: body taken from express's request object
|
|
196
|
+// return: true if the body is valid, false else
|
|
197
|
+function checkBody(body) {
|
|
198
|
+ let valid = false;
|
|
199
|
+
|
|
200
|
+ if(body.token !== undefined && body.subj !== undefined
|
|
201
|
+ && body.name !== undefined && body.addr !== undefined
|
|
202
|
+ && body.text !== undefined) {
|
|
203
|
+ valid = true;
|
|
204
|
+ }
|
|
205
|
+
|
|
206
|
+ return valid;
|
|
207
|
+}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+// Checks the tokens object to see if no token has expired
|
|
211
|
+// return: nothing
|
|
212
|
+function cleanTokens() {
|
|
213
|
+ // Get current time for comparison
|
|
214
|
+ let now = new Date().getTime();
|
|
215
|
+
|
|
216
|
+ for(let ip in tokens) { // Check for each IP in the object
|
|
217
|
+ for(let token of tokens[ip]) { // Check for each token of an IP
|
|
218
|
+ if(token.expire < now) { // Token has expired
|
|
219
|
+ tokens[ip].pop(token);
|
|
220
|
+ }
|
|
221
|
+ }
|
|
222
|
+ if(tokens[ip].length === 0) { // No more element for this IP
|
|
223
|
+ delete tokens[ip];
|
|
224
|
+ }
|
|
225
|
+ }
|
|
226
|
+
|
|
227
|
+ log.info('Cleared expired tokens');
|
120
|
228
|
}
|