Brendan Abolivier vor 7 Jahren
Ursprung
Commit
08558fdd2d
Signiert von: Brendan Abolivier <contact@brendanabolivier.com> GPG Schlüssel ID: 8EF1500759F70623
2 geänderte Dateien mit 419 neuen und 419 gelöschten Zeilen
  1. 225
    225
      front/form.js
  2. 194
    194
      server.js

+ 225
- 225
front/form.js Datei anzeigen

@@ -1,15 +1,15 @@
1 1
 // Consts for readability
2
-const REQUIRED      = true;
2
+const REQUIRED		= true;
3 3
 const NOT_REQUIRED  = false;
4 4
 
5 5
 
6 6
 var prefix = 'form'
7 7
 
8 8
 var items = {
9
-    name: 'name',
10
-    addr: 'addr',
11
-    subj: 'subj',
12
-    text: 'text',
9
+	name: 'name',
10
+	addr: 'addr',
11
+	subj: 'subj',
12
+	text: 'text',
13 13
 };
14 14
 
15 15
 var DOMFields = {};
@@ -17,73 +17,73 @@ var DOMFields = {};
17 17
 var server  		= getServer();
18 18
 var token   		= "";
19 19
 var labels  		= true;
20
-var lang    		= [];
20
+var lang			= [];
21 21
 var customFields 	= {};
22 22
 
23 23
 var xhr = {
24 24
 	customFields: new XMLHttpRequest(),
25
-    lang: new XMLHttpRequest(),
26
-    token: new XMLHttpRequest(),
27
-    send: new XMLHttpRequest()
25
+	lang: new XMLHttpRequest(),
26
+	token: new XMLHttpRequest(),
27
+	send: new XMLHttpRequest()
28 28
 }
29 29
 
30 30
 // XHR callbacks
31 31
 
32 32
 xhr.customFields.onreadystatechange = function() {
33 33
 	if(xhr.customFields.readyState == XMLHttpRequest.DONE) {
34
-        customFields = JSON.parse(xhr.customFields.responseText);
35
-        for(let field in customFields) {
36
-            customFields[field].name = field;
37
-        }
34
+		customFields = JSON.parse(xhr.customFields.responseText);
35
+		for(let field in customFields) {
36
+			customFields[field].name = field;
37
+		}
38 38
 	}
39 39
 };
40 40
 
41 41
 xhr.token.onreadystatechange = function() {
42
-    if(xhr.token.readyState == XMLHttpRequest.DONE) {
43
-        token = xhr.token.responseText;
44
-    }
42
+	if(xhr.token.readyState == XMLHttpRequest.DONE) {
43
+		token = xhr.token.responseText;
44
+	}
45 45
 };
46 46
 
47 47
 xhr.lang.onreadystatechange = function() {
48
-    if(xhr.lang.readyState == XMLHttpRequest.DONE) {
49
-        let response = JSON.parse(xhr.lang.responseText);
50
-        lang = response.translations;
51
-        labels = response.labels;
52
-    }
48
+	if(xhr.lang.readyState == XMLHttpRequest.DONE) {
49
+		let response = JSON.parse(xhr.lang.responseText);
50
+		lang = response.translations;
51
+		labels = response.labels;
52
+	}
53 53
 };
54 54
 
55 55
 xhr.send.onreadystatechange = function() {
56
-    if(xhr.send.readyState == XMLHttpRequest.DONE) {
57
-        let status = document.getElementById('form_status');
58
-        status.setAttribute('class', '');
59
-        if(xhr.send.status === 200) {
60
-            cleanForm();
61
-            status.setAttribute('class', 'success');
62
-            status.innerHTML = lang.send_status_success;
63
-        } else {
64
-            status.setAttribute('class', 'failure');
65
-            status.innerHTML = lang.send_status_failure;
66
-        }
67
-    }
56
+	if(xhr.send.readyState == XMLHttpRequest.DONE) {
57
+		let status = document.getElementById('form_status');
58
+		status.setAttribute('class', '');
59
+		if(xhr.send.status === 200) {
60
+			cleanForm();
61
+			status.setAttribute('class', 'success');
62
+			status.innerHTML = lang.send_status_success;
63
+		} else {
64
+			status.setAttribute('class', 'failure');
65
+			status.innerHTML = lang.send_status_failure;
66
+		}
67
+	}
68 68
 };
69 69
 
70 70
 
71 71
 // Returns the server's base URI based on the user's script tag
72 72
 // return: the SMAM server's base URI
73 73
 function getServer() {
74
-    var scripts = document.getElementsByTagName('script');
75
-    // Parsing all the <script> tags to find the URL to our file
76
-    for(var i = 0; i < scripts.length; i++) {
77
-        let script = scripts[i];
78
-        if(script.src) {
79
-            let url = script.src;
80
-            // This should be our script
81
-            if(url.match(/form\.js$/)) {
82
-                // Port has been found
83
-                return url.match(/^(https?:\/\/[^\/]+)/)[1];
84
-            }
85
-        }
86
-    }    
74
+	var scripts = document.getElementsByTagName('script');
75
+	// Parsing all the <script> tags to find the URL to our file
76
+	for(var i = 0; i < scripts.length; i++) {
77
+		let script = scripts[i];
78
+		if(script.src) {
79
+			let url = script.src;
80
+			// This should be our script
81
+			if(url.match(/form\.js$/)) {
82
+				// Port has been found
83
+				return url.match(/^(https?:\/\/[^\/]+)/)[1];
84
+			}
85
+		}
86
+	}	
87 87
 }
88 88
 
89 89
 
@@ -91,61 +91,61 @@ function getServer() {
91 91
 // id: HTML identifier of the document's block to create the form into
92 92
 // return: nothing
93 93
 function generateForm(id) {
94
-    // Get translated strings
95
-    getLangSync();
94
+	// Get translated strings
95
+	getLangSync();
96 96
 	// Get custom fields if defined in the configuration
97 97
 	getCustomFieldsSync();
98
-    
99
-    var el = document.getElementById(id);
100
-    
101
-    // Set the form's behaviour
102
-    el.setAttribute('onsubmit', 'sendForm(); return false;');
103
-    
104
-    // Add an empty paragraph for status
105
-    var status = document.createElement('p');
106
-    status.setAttribute('id', 'form_status');
107
-    el.appendChild(status);
108
-
109
-    // Default fields
110
-    DOMFields = {
111
-        name: getField({
112
-            name: items.name,
113
-            label: lang.form_name_label,
114
-            type: 'text'
115
-        }, REQUIRED),
116
-        addr: getField({
117
-            name: items.addr,
118
-            label: lang.form_addr_label,
119
-            type: 'email'
120
-        }, REQUIRED),
121
-        subj: getField({
122
-            name: items.subj,
123
-            label: lang.form_subj_label,
124
-            type: 'text'
125
-        }, REQUIRED),
126
-        text: getField({
127
-            name: items.text,
128
-            label: lang.form_mesg_label,
129
-            type: 'textarea'
130
-        }, REQUIRED)
131
-    };
132
-    
133
-    // Adding custom fields
134
-    for(let fieldName in customFields) {
135
-        let field = customFields[fieldName];
136
-        DOMFields[fieldName] = getField(field, NOT_REQUIRED);
137
-    }
138
-    
139
-    // Adding all nodes to document
140
-    for(let field in DOMFields) {
141
-        el.appendChild(DOMFields[field]);
142
-    }
143
-
144
-    // Adding submit button
145
-    el.appendChild(getSubmitButton('form_subm', lang.form_subm_label));
146
-    
147
-    // Retrieve the token from the server
148
-    getToken();
98
+	
99
+	var el = document.getElementById(id);
100
+	
101
+	// Set the form's behaviour
102
+	el.setAttribute('onsubmit', 'sendForm(); return false;');
103
+	
104
+	// Add an empty paragraph for status
105
+	var status = document.createElement('p');
106
+	status.setAttribute('id', 'form_status');
107
+	el.appendChild(status);
108
+
109
+	// Default fields
110
+	DOMFields = {
111
+		name: getField({
112
+			name: items.name,
113
+			label: lang.form_name_label,
114
+			type: 'text'
115
+		}, REQUIRED),
116
+		addr: getField({
117
+			name: items.addr,
118
+			label: lang.form_addr_label,
119
+			type: 'email'
120
+		}, REQUIRED),
121
+		subj: getField({
122
+			name: items.subj,
123
+			label: lang.form_subj_label,
124
+			type: 'text'
125
+		}, REQUIRED),
126
+		text: getField({
127
+			name: items.text,
128
+			label: lang.form_mesg_label,
129
+			type: 'textarea'
130
+		}, REQUIRED)
131
+	};
132
+	
133
+	// Adding custom fields
134
+	for(let fieldName in customFields) {
135
+		let field = customFields[fieldName];
136
+		DOMFields[fieldName] = getField(field, NOT_REQUIRED);
137
+	}
138
+	
139
+	// Adding all nodes to document
140
+	for(let field in DOMFields) {
141
+		el.appendChild(DOMFields[field]);
142
+	}
143
+
144
+	// Adding submit button
145
+	el.appendChild(getSubmitButton('form_subm', lang.form_subm_label));
146
+	
147
+	// Retrieve the token from the server
148
+	getToken();
149 149
 }
150 150
 
151 151
 
@@ -154,33 +154,33 @@ function generateForm(id) {
154 154
 // required: boolean on whether the field is required or optional
155 155
 // return: a block containing the field and a label describing it (if enabled)
156 156
 function getField(fieldInfos, required) {
157
-    var block = document.createElement('div');
158
-    block.setAttribute('id', fieldInfos.name);
159
-
160
-    // Declare the variable first
161
-    let field = {};
162
-
163
-    // Easily add new supported input types
164
-    switch(fieldInfos.type) {
165
-        case 'text':        field = getTextField(fieldInfos, required);
166
-                            break;
167
-        case 'textarea':    field = getTextarea(fieldInfos, required);
168
-                            break;
169
-        case 'email':       field = getEmailField(fieldInfos, required);
170
-                            break;
171
-        case 'select':      field = getSelectField(fieldInfos, required);
172
-                            break;
173
-    }
174
-
175
-    // We need the input field's ID to bind it to the label, so we generate the
176
-    // field first
177
-    if(labels) {
178
-        block.appendChild(getLabel(fieldInfos.label, field.id));
179
-    }
180
-    
181
-    // Assemble the block and return it
182
-    block.appendChild(field);
183
-    return block;
157
+	var block = document.createElement('div');
158
+	block.setAttribute('id', fieldInfos.name);
159
+
160
+	// Declare the variable first
161
+	let field = {};
162
+
163
+	// Easily add new supported input types
164
+	switch(fieldInfos.type) {
165
+		case 'text':		field = getTextField(fieldInfos, required);
166
+							break;
167
+		case 'textarea':	field = getTextarea(fieldInfos, required);
168
+							break;
169
+		case 'email':	   field = getEmailField(fieldInfos, required);
170
+							break;
171
+		case 'select':	  field = getSelectField(fieldInfos, required);
172
+							break;
173
+	}
174
+
175
+	// We need the input field's ID to bind it to the label, so we generate the
176
+	// field first
177
+	if(labels) {
178
+		block.appendChild(getLabel(fieldInfos.label, field.id));
179
+	}
180
+	
181
+	// Assemble the block and return it
182
+	block.appendChild(field);
183
+	return block;
184 184
 }
185 185
 
186 186
 
@@ -189,12 +189,12 @@ function getField(fieldInfos, required) {
189 189
 // id: field HTML identifier
190 190
 // return: a label node the field's description
191 191
 function getLabel(content, id) {
192
-    var label = document.createElement('label');
193
-    
194
-    label.setAttribute('for', id);
195
-    label.innerHTML = content;
196
-    
197
-    return label;
192
+	var label = document.createElement('label');
193
+	
194
+	label.setAttribute('for', id);
195
+	label.innerHTML = content;
196
+	
197
+	return label;
198 198
 }
199 199
 
200 200
 
@@ -203,29 +203,29 @@ function getLabel(content, id) {
203 203
 // required: boolean on whether the field is required or optional
204 204
 // return: a <select> element corresponding to the info passed as input
205 205
 function getSelectField(fieldInfos, required) {
206
-    let field = document.createElement('select');
207
-
208
-    // Set attributes when necessary
209
-    if(required) {
210
-        field.setAttribute('required', 'required');
211
-    }
212
-    field.setAttribute('id', prefix + '_' + fieldInfos.name + '_select');
213
-
214
-    let index = 0;
215
-    
216
-    // Add all options to select
217
-    for(let choice of fieldInfos.options) {
218
-        let option = document.createElement('option');
219
-        // Options' values are incremental numeric indexes
220
-        option.setAttribute('value', index);
221
-        // Set the value defined by the user
222
-        option.innerHTML = choice;
223
-        field.appendChild(option);
224
-        // Increment the index
225
-        index++;
226
-    }
227
-
228
-    return field
206
+	let field = document.createElement('select');
207
+
208
+	// Set attributes when necessary
209
+	if(required) {
210
+		field.setAttribute('required', 'required');
211
+	}
212
+	field.setAttribute('id', prefix + '_' + fieldInfos.name + '_select');
213
+
214
+	let index = 0;
215
+	
216
+	// Add all options to select
217
+	for(let choice of fieldInfos.options) {
218
+		let option = document.createElement('option');
219
+		// Options' values are incremental numeric indexes
220
+		option.setAttribute('value', index);
221
+		// Set the value defined by the user
222
+		option.innerHTML = choice;
223
+		field.appendChild(option);
224
+		// Increment the index
225
+		index++;
226
+	}
227
+
228
+	return field
229 229
 }
230 230
 
231 231
 
@@ -234,7 +234,7 @@ function getSelectField(fieldInfos, required) {
234 234
 // required: boolean on whether the field is required or optional
235 235
 // return: a <input> HTML element corresponding to the info passed as input
236 236
 function getTextField(fieldInfos, required) {
237
-    return getBaseInputField(fieldInfos, required, 'text');
237
+	return getBaseInputField(fieldInfos, required, 'text');
238 238
 }
239 239
 
240 240
 
@@ -243,7 +243,7 @@ function getTextField(fieldInfos, required) {
243 243
 // required: boolean on whether the field is required or optional
244 244
 // return: a <input> HTML element corresponding to the info passed as input
245 245
 function getEmailField(fieldInfos, required) {
246
-    return getBaseInputField(fieldInfos, required, 'email');
246
+	return getBaseInputField(fieldInfos, required, 'email');
247 247
 }
248 248
 
249 249
 
@@ -253,9 +253,9 @@ function getEmailField(fieldInfos, required) {
253 253
 // required: boolean on whether the field is required or optional
254 254
 // return: a basic <input> HTML element with generic info
255 255
 function getBaseInputField(fieldInfos, required, type) {
256
-    let field = getBaseField(fieldInfos, required, 'input')
257
-    field.setAttribute('type', type);
258
-    return field;
256
+	let field = getBaseField(fieldInfos, required, 'input')
257
+	field.setAttribute('type', type);
258
+	return field;
259 259
 }
260 260
 
261 261
 
@@ -264,7 +264,7 @@ function getBaseInputField(fieldInfos, required, type) {
264 264
 // required: boolean on whether the field is required or optional
265 265
 // return: a <textarea> element corresponding to the info passed as input
266 266
 function getTextarea(fieldInfos, required) {
267
-    return getBaseField(fieldInfos, required, 'textarea');
267
+	return getBaseField(fieldInfos, required, 'textarea');
268 268
 }
269 269
 
270 270
 
@@ -275,15 +275,15 @@ function getTextarea(fieldInfos, required) {
275 275
 // tag: the HTML tag the field element must have
276 276
 // return: a HTML element of the given tag with basic info given as input
277 277
 function getBaseField(fieldInfos, required, tag) {
278
-    let field = document.createElement(tag);
279
-    
280
-    if(required) {
281
-        field.setAttribute('required', 'required');
282
-    }
283
-    field.setAttribute('placeholder', fieldInfos.label);
284
-    field.setAttribute('id', prefix + '_' + fieldInfos.name + '_' + tag);
285
-    
286
-    return field;
278
+	let field = document.createElement(tag);
279
+	
280
+	if(required) {
281
+		field.setAttribute('required', 'required');
282
+	}
283
+	field.setAttribute('placeholder', fieldInfos.label);
284
+	field.setAttribute('id', prefix + '_' + fieldInfos.name + '_' + tag);
285
+	
286
+	return field;
287 287
 }
288 288
 
289 289
 
@@ -292,91 +292,91 @@ function getBaseField(fieldInfos, required, tag) {
292 292
 // text: button text
293 293
 // return: a div node containing the button
294 294
 function getSubmitButton(id, text) {
295
-    var submit = document.createElement('div');
296
-    
297
-    submit.setAttribute('id', id);
298
-    
299
-    var button = document.createElement('button');
300
-    
301
-    button.setAttribute('type', 'submit');
302
-    button.setAttribute('id', id + '_btn');
303
-    
304
-    button.innerHTML = text;
305
-    
306
-    submit.appendChild(button);
307
-    
308
-    return submit;
295
+	var submit = document.createElement('div');
296
+	
297
+	submit.setAttribute('id', id);
298
+	
299
+	var button = document.createElement('button');
300
+	
301
+	button.setAttribute('type', 'submit');
302
+	button.setAttribute('id', id + '_btn');
303
+	
304
+	button.innerHTML = text;
305
+	
306
+	submit.appendChild(button);
307
+	
308
+	return submit;
309 309
 }
310 310
 
311 311
 
312 312
 // Send form data through the XHR object
313 313
 // return: nothing
314 314
 function sendForm() {
315
-    // Clear status
316
-    let status = document.getElementById('form_status');
317
-    status.setAttribute('class', 'sending');
318
-    status.innerHTML = lang.send_status_progress;
319
-    
320
-    xhr.send.open('POST', server + '/send');
321
-    xhr.send.setRequestHeader('Content-Type', 'application/json');
322
-    xhr.send.send(JSON.stringify(getFormData()));
323
-    
324
-    // Get a new token
325
-    getToken();
315
+	// Clear status
316
+	let status = document.getElementById('form_status');
317
+	status.setAttribute('class', 'sending');
318
+	status.innerHTML = lang.send_status_progress;
319
+	
320
+	xhr.send.open('POST', server + '/send');
321
+	xhr.send.setRequestHeader('Content-Type', 'application/json');
322
+	xhr.send.send(JSON.stringify(getFormData()));
323
+	
324
+	// Get a new token
325
+	getToken();
326 326
 }
327 327
 
328 328
 
329 329
 // Fetch form inputs from HTML elements
330 330
 // return: an object containing all the user's input
331 331
 function getFormData() {
332
-    let data = {};
333
-    data.token = token;
334
-    data.custom = {};
335
-
336
-    // Custom fields
337
-    // Select the field
338
-    let index = 0;
339
-
340
-    if(labels) {
341
-        index = 1;
342
-    }
343
-    
344
-    for(let field in DOMFields) {
345
-        let el = DOMFields[field].children[index];
346
-        if(field in customFields) {
347
-            data.custom[field] = el.value;
348
-        } else {
349
-            data[field] = el.value;
350
-        }
351
-    }
352
-
353
-    return data;
332
+	let data = {};
333
+	data.token = token;
334
+	data.custom = {};
335
+
336
+	// Custom fields
337
+	// Select the field
338
+	let index = 0;
339
+
340
+	if(labels) {
341
+		index = 1;
342
+	}
343
+	
344
+	for(let field in DOMFields) {
345
+		let el = DOMFields[field].children[index];
346
+		if(field in customFields) {
347
+			data.custom[field] = el.value;
348
+		} else {
349
+			data[field] = el.value;
350
+		}
351
+	}
352
+
353
+	return data;
354 354
 }
355 355
 
356 356
 
357 357
 // Empties the form fields
358 358
 // return: nothing
359 359
 function cleanForm() {
360
-    document.getElementById(prefix + '_' + items.name + '_input').value = '';
361
-    document.getElementById(prefix + '_' + items.addr + '_input').value = '';
362
-    document.getElementById(prefix + '_' + items.subj + '_input').value = '';
363
-    document.getElementById(prefix + '_' + items.text + '_textarea').value = '';
360
+	document.getElementById(prefix + '_' + items.name + '_input').value = '';
361
+	document.getElementById(prefix + '_' + items.addr + '_input').value = '';
362
+	document.getElementById(prefix + '_' + items.subj + '_input').value = '';
363
+	document.getElementById(prefix + '_' + items.text + '_textarea').value = '';
364 364
 }
365 365
 
366 366
 
367 367
 // Asks the server for a token
368 368
 // return: nothing
369 369
 function getToken() {
370
-    xhr.token.open('GET', server + '/register');
371
-    xhr.token.send();
370
+	xhr.token.open('GET', server + '/register');
371
+	xhr.token.send();
372 372
 }
373 373
 
374 374
 
375 375
 // Asks the server for translated strings to display
376 376
 // return: notghing
377 377
 function getLangSync() {
378
-    xhr.lang.open('GET', server + '/lang', false);
379
-    xhr.lang.send();
378
+	xhr.lang.open('GET', server + '/lang', false);
379
+	xhr.lang.send();
380 380
 }
381 381
 
382 382
 

+ 194
- 194
server.js Datei anzeigen

@@ -1,23 +1,23 @@
1
-var pug         = require('pug');
1
+var pug			= require('pug');
2 2
 var nodemailer  = require('nodemailer');
3
-var crypto      = require('crypto');
4
-var settings    = require('./settings');
3
+var crypto		= require('crypto');
4
+var settings	= require('./settings');
5 5
 
6 6
 // Translation
7
-var locale  = require('./locales/' + settings.language);
8
-var lang    = locale.server;
7
+var locale		= require('./locales/' + settings.language);
8
+var language	= locale.server;
9 9
 
10 10
 // Web server
11
-var bodyParser  = require('body-parser');
12
-var cors        = require('cors');
13
-var express     = require('express');
14
-var app = express();
11
+var bodyParser	= require('body-parser');
12
+var cors		= require('cors');
13
+var express		= require('express');
14
+var app			= express();
15 15
 
16 16
 // Logging
17 17
 var printit = require('printit');
18 18
 var log = printit({
19
-    prefix: 'SMAM',
20
-    date: true
19
+	prefix: 'SMAM',
20
+	date: true
21 21
 });
22 22
 
23 23
 
@@ -47,80 +47,80 @@ app.options('*', cors(corsOptions));
47 47
 // A request on /register generates a token and store it, along the user's
48 48
 // address, on the tokens object
49 49
 app.get('/register', function(req, res, next) {
50
-    // Get IP from express
51
-    let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
52
-    if(tokens[ip] === undefined) {
53
-        tokens[ip] = [];
54
-    }
55
-    // Generate token
56
-    crypto.randomBytes(10, (err, buf) => { 
57
-        let token = buf.toString('hex');
58
-        // Store and send the token
59
-        tokens[ip].push({
60
-            token: token,
61
-            // A token expires after 12h
62
-            expire: new Date().getTime() + 12 * 3600 * 1000
63
-        });
64
-        res.status(200).send(token);
65
-    });
50
+	// Get IP from express
51
+	let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
52
+	if(tokens[ip] === undefined) {
53
+		tokens[ip] = [];
54
+	}
55
+	// Generate token
56
+	crypto.randomBytes(10, (err, buf) => { 
57
+		let token = buf.toString('hex');
58
+		// Store and send the token
59
+		tokens[ip].push({
60
+			token: token,
61
+			// A token expires after 12h
62
+			expire: new Date().getTime() + 12 * 3600 * 1000
63
+		});
64
+		res.status(200).send(token);
65
+	});
66 66
 });
67 67
 
68 68
 
69 69
 // A request on /send with user input = mail to be sent
70 70
 app.post('/send', function(req, res, next) {
71
-    // Response will be JSON
72
-    res.header('Access-Control-Allow-Headers', 'Content-Type');
73
-    
74
-    if(!checkBody(req.body)) {
75
-        return res.status(400).send();
76
-    }
77
-    
78
-    let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
79
-    
80
-    // Token verification
81
-    if(!checkToken(ip, req.body.token)) {
82
-        return res.status(403).send();
83
-    }
84
-    
85
-    // Count the failures
86
-    let status = {
87
-        failed: 0,
88
-        total: settings.recipients.length
89
-    };
90
-    
91
-    // params will be used as:
92
-    // - values for html generation from the pug template
93
-    // - parameters for sending the mail(s)
94
-    let params = {
95
-        subject: req.body.subj,
96
-        from: req.body.name + '<' + settings.mailserver.auth.user + '>',
97
-        replyTo: req.body.name + ' <' + req.body.addr + '>',
98
-        html: req.body.text
99
-    };
100
-
101
-    // Process custom fields to get data we can use in the HTML generation
102
-    params.custom = processCustom(req.body.custom);
103
-    
104
-    // Replacing the mail's content with HTML from the pug template
105
-    // Commenting the line below will bypass the generation and only user the
106
-    // text entered by the user
107
-    params.html = pug.renderFile('template.pug', params);
108
-    
109
-    log.info(lang.log_sending, params.replyTo);
110
-    
111
-    // Send the email to all users
112
-    sendMails(params, function(err, infos) {
113
-        if(err) {
114
-            log.error(err);
115
-        }
116
-        logStatus(infos);
117
-    }, function() {
118
-        if(status.failed === status.total) {
119
-            res.status(500).send();
120
-        } else {
121
-            res.status(200).send();
122
-        }
123
-    })
71
+	// Response will be JSON
72
+	res.header('Access-Control-Allow-Headers', 'Content-Type');
73
+	
74
+	if(!checkBody(req.body)) {
75
+		return res.status(400).send();
76
+	}
77
+	
78
+	let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
79
+	
80
+	// Token verification
81
+	if(!checkToken(ip, req.body.token)) {
82
+		return res.status(403).send();
83
+	}
84
+	
85
+	// Count the failures
86
+	let status = {
87
+		failed: 0,
88
+		total: settings.recipients.length
89
+	};
90
+	
91
+	// params will be used as:
92
+	// - values for html generation from the pug template
93
+	// - parameters for sending the mail(s)
94
+	let params = {
95
+		subject: req.body.subj,
96
+		from: req.body.name + '<' + settings.mailserver.auth.user + '>',
97
+		replyTo: req.body.name + ' <' + req.body.addr + '>',
98
+		html: req.body.text
99
+	};
100
+
101
+	// Process custom fields to get data we can use in the HTML generation
102
+	params.custom = processCustom(req.body.custom);
103
+	
104
+	// Replacing the mail's content with HTML from the pug template
105
+	// Commenting the line below will bypass the generation and only user the
106
+	// text entered by the user
107
+	params.html = pug.renderFile('template.pug', params);
108
+	
109
+	log.info(lang.log_sending, params.replyTo);
110
+	
111
+	// Send the email to all users
112
+	sendMails(params, function(err, infos) {
113
+		if(err) {
114
+			log.error(err);
115
+		}
116
+		logStatus(infos);
117
+	}, function() {
118
+		if(status.failed === status.total) {
119
+			res.status(500).send();
120
+		} else {
121
+			res.status(200).send();
122
+		}
123
+	})
124 124
 });
125 125
 
126 126
 
@@ -128,27 +128,27 @@ app.post('/send', function(req, res, next) {
128 128
 // the app settings), alongside the boolean for the display of labels in the
129 129
 // form block.
130 130
 app.get('/lang', function(req, res, next) {
131
-    // Response will be JSON
132
-    res.header('Access-Control-Allow-Headers', 'Content-Type');
133
-
134
-    // Preventing un-updated settings files
135
-    let labels = true;
136
-    if(settings.labels !== undefined) {
137
-        labels = settings.labels;
138
-    }
139
-    
140
-    // Send the infos
141
-    res.status(200).send({
142
-        'labels': labels,
143
-        'translations': locale.client
144
-    });
131
+	// Response will be JSON
132
+	res.header('Access-Control-Allow-Headers', 'Content-Type');
133
+
134
+	// Preventing un-updated settings files
135
+	let labels = true;
136
+	if(settings.labels !== undefined) {
137
+		labels = settings.labels;
138
+	}
139
+	
140
+	// Send the infos
141
+	res.status(200).send({
142
+		'labels': labels,
143
+		'translations': locale.client
144
+	});
145 145
 });
146 146
 
147 147
 
148 148
 // A request on /fields sends data on custom fields.
149 149
 app.get('/fields', function(req, res, next) {
150 150
 	// Response will be JSON
151
-    res.header('Access-Control-Allow-Headers', 'Content-Type');
151
+	res.header('Access-Control-Allow-Headers', 'Content-Type');
152 152
 
153 153
 	// Send an array anyway, its length will determine if we need to display any
154 154
 	let customFields = settings.customFields || [];
@@ -164,7 +164,7 @@ var port = process.env.PORT || 1970;
164 164
 var host = process.env.HOST || '0.0.0.0';
165 165
 // Start the server
166 166
 app.listen(port, host, function() {
167
-    log.info(lang.log_server_start, host + ':' + port);
167
+	log.info(lang.log_server_start, host + ':' + port);
168 168
 });
169 169
 
170 170
 
@@ -175,31 +175,31 @@ var tokensChecks = setTimeout(cleanTokens, 3600 * 1000);
175 175
 // Send mails to the recipients specified in the JSON settings file
176 176
 // content: object containing mail params
177 177
 //  {
178
-//      subject: String
179
-//      from: String (following RFC 1036 (https://tools.ietf.org/html/rfc1036#section-2.1.1))
180
-//      html: String
178
+//	  subject: String
179
+//	  from: String (following RFC 1036 (https://tools.ietf.org/html/rfc1036#section-2.1.1))
180
+//	  html: String
181 181
 //  }
182 182
 // update(next, infos): Called each time a mail is sent with the infos provided
183
-//                      by nodemailer
183
+//					  by nodemailer
184 184
 // done(): Called once each mail has been sent
185 185
 function sendMails(params, update, done) {
186
-    let mails = settings.recipients.map((recipient) => {
187
-        // Promise for each recipient to send each mail asynchronously
188
-        return new Promise((sent) => {
189
-            params.to = recipient;
190
-            // Send the email
191
-            transporter.sendMail(params, (err, infos) => {
192
-                sent();
193
-                if(err) {
194
-                    return update(err, recipient);
195
-                }
196
-                update(null, infos);
197
-                // Promise callback
198
-            });
199
-        });
200
-    });
201
-    // Run all the promises (= send all the mails)
202
-    Promise.all(mails).then(done);
186
+	let mails = settings.recipients.map((recipient) => {
187
+		// Promise for each recipient to send each mail asynchronously
188
+		return new Promise((sent) => {
189
+			params.to = recipient;
190
+			// Send the email
191
+			transporter.sendMail(params, (err, infos) => {
192
+				sent();
193
+				if(err) {
194
+					return update(err, recipient);
195
+				}
196
+				update(null, infos);
197
+				// Promise callback
198
+			});
199
+		});
200
+	});
201
+	// Run all the promises (= send all the mails)
202
+	Promise.all(mails).then(done);
203 203
 }
204 204
 
205 205
 
@@ -207,13 +207,13 @@ function sendMails(params, update, done) {
207 207
 // infos: infos provided by nodemailer
208 208
 // return: nothing
209 209
 function logStatus(infos) {
210
-    if(infos.accepted.length !== 0) {
211
-        log.info(lang.log_send_success, infos.accepted[0]);
212
-    }
213
-    if(infos.rejected.length !== 0) {
214
-        status.failed++;
215
-        log.info(lang.log_send_failure, infos.rejected[0]);
216
-    }
210
+	if(infos.accepted.length !== 0) {
211
+		log.info(lang.log_send_success, infos.accepted[0]);
212
+	}
213
+	if(infos.rejected.length !== 0) {
214
+		status.failed++;
215
+		log.info(lang.log_send_failure, infos.rejected[0]);
216
+	}
217 217
 }
218 218
 
219 219
 
@@ -222,29 +222,29 @@ function logStatus(infos) {
222 222
 // token: token used by the sender
223 223
 // return: true if the user was registered, false else
224 224
 function checkToken(ip, token) {
225
-    let verified = false;
226
-    
227
-    // Check if there's at least one token for this IP
228
-    if(tokens[ip] !== undefined) {
229
-        if(tokens[ip].length !== 0) {
230
-            // There's at least one element for this IP, let's check the tokens
231
-            for(var i = 0; i < tokens[ip].length; i++) {
232
-                if(!tokens[ip][i].token.localeCompare(token)) {
233
-                    // We found the right token
234
-                    verified = true;
235
-                    // Removing the token
236
-                    tokens[ip].pop(tokens[ip][i]);
237
-                    break;
238
-                }
239
-            }
240
-        } 
241
-    }
242
-    
243
-    if(!verified) {
244
-        log.warn(ip, lang.log_invalid_token);
245
-    }
246
-    
247
-    return verified;
225
+	let verified = false;
226
+	
227
+	// Check if there's at least one token for this IP
228
+	if(tokens[ip] !== undefined) {
229
+		if(tokens[ip].length !== 0) {
230
+			// There's at least one element for this IP, let's check the tokens
231
+			for(var i = 0; i < tokens[ip].length; i++) {
232
+				if(!tokens[ip][i].token.localeCompare(token)) {
233
+					// We found the right token
234
+					verified = true;
235
+					// Removing the token
236
+					tokens[ip].pop(tokens[ip][i]);
237
+					break;
238
+				}
239
+			}
240
+		} 
241
+	}
242
+	
243
+	if(!verified) {
244
+		log.warn(ip, lang.log_invalid_token);
245
+	}
246
+	
247
+	return verified;
248 248
 }
249 249
 
250 250
 
@@ -252,36 +252,36 @@ function checkToken(ip, token) {
252 252
 // body: body taken from express's request object
253 253
 // return: true if the body is valid, false else
254 254
 function checkBody(body) {
255
-    let valid = false;
256
-    
257
-    if(body.token !== undefined && body.subj !== undefined 
258
-        && body.name !== undefined && body.addr !== undefined 
259
-        && body.text !== undefined) {
260
-        valid = true;
261
-    }
262
-    
263
-    return valid;
255
+	let valid = false;
256
+	
257
+	if(body.token !== undefined && body.subj !== undefined 
258
+		&& body.name !== undefined && body.addr !== undefined 
259
+		&& body.text !== undefined) {
260
+		valid = true;
261
+	}
262
+	
263
+	return valid;
264 264
 }
265 265
 
266 266
 
267 267
 // Checks the tokens object to see if no token has expired
268 268
 // return: nothing
269 269
 function cleanTokens() {
270
-    // Get current time for comparison
271
-    let now = new Date().getTime();
272
-    
273
-    for(let ip in tokens) { // Check for each IP in the object
274
-        for(let token of tokens[ip]) { // Check for each token of an IP
275
-            if(token.expire < now) { // Token has expired
276
-                tokens[ip].pop(token);
277
-            }
278
-        }
279
-        if(tokens[ip].length === 0) { // No more element for this IP
280
-            delete tokens[ip];
281
-        }
282
-    }
283
-    
284
-    log.info(lang.log_cleared_token);
270
+	// Get current time for comparison
271
+	let now = new Date().getTime();
272
+	
273
+	for(let ip in tokens) { // Check for each IP in the object
274
+		for(let token of tokens[ip]) { // Check for each token of an IP
275
+			if(token.expire < now) { // Token has expired
276
+				tokens[ip].pop(token);
277
+			}
278
+		}
279
+		if(tokens[ip].length === 0) { // No more element for this IP
280
+			delete tokens[ip];
281
+		}
282
+	}
283
+	
284
+	log.info(lang.log_cleared_token);
285 285
 }
286 286
 
287 287
 
@@ -290,29 +290,29 @@ function cleanTokens() {
290 290
 // custom: object describing data from custom fields
291 291
 // return: an object with user-input data from each field:
292 292
 // {
293
-//      field name: {
294
-//          value: String,
295
-//          label: String
296
-//      }
293
+//	  field name: {
294
+//		  value: String,
295
+//		  label: String
296
+//	  }
297 297
 // }
298 298
 function processCustom(custom) {
299
-    let fields = {};
300
-
301
-    // Process each field
302
-    for(let field in custom) {
303
-        let type = settings.customFields[field].type;
304
-        // Match indexes with data when needed
305
-        switch(type) {
306
-            case 'select':  custom[field] = settings.customFields[field]
307
-                                            .options[custom[field]];
308
-                            break;
309
-        }
310
-        // Insert data into the finale object
311
-        fields[field] = {
312
-            value: custom[field],
313
-            label: settings.customFields[field].label
314
-        }
315
-    }
316
-    
317
-    return fields;
299
+	let fields = {};
300
+
301
+	// Process each field
302
+	for(let field in custom) {
303
+		let type = settings.customFields[field].type;
304
+		// Match indexes with data when needed
305
+		switch(type) {
306
+			case 'select':  custom[field] = settings.customFields[field]
307
+											.options[custom[field]];
308
+							break;
309
+		}
310
+		// Insert data into the finale object
311
+		fields[field] = {
312
+			value: custom[field],
313
+			label: settings.customFields[field].label
314
+		}
315
+	}
316
+	
317
+	return fields;
318 318
 }