Brendan Abolivier 8 anni fa
parent
commit
08558fdd2d
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
2 ha cambiato i file con 419 aggiunte e 419 eliminazioni
  1. 225
    225
      front/form.js
  2. 194
    194
      server.js

+ 225
- 225
front/form.js Vedi File

1
 // Consts for readability
1
 // Consts for readability
2
-const REQUIRED      = true;
2
+const REQUIRED		= true;
3
 const NOT_REQUIRED  = false;
3
 const NOT_REQUIRED  = false;
4
 
4
 
5
 
5
 
6
 var prefix = 'form'
6
 var prefix = 'form'
7
 
7
 
8
 var items = {
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
 var DOMFields = {};
15
 var DOMFields = {};
17
 var server  		= getServer();
17
 var server  		= getServer();
18
 var token   		= "";
18
 var token   		= "";
19
 var labels  		= true;
19
 var labels  		= true;
20
-var lang    		= [];
20
+var lang			= [];
21
 var customFields 	= {};
21
 var customFields 	= {};
22
 
22
 
23
 var xhr = {
23
 var xhr = {
24
 	customFields: new XMLHttpRequest(),
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
 // XHR callbacks
30
 // XHR callbacks
31
 
31
 
32
 xhr.customFields.onreadystatechange = function() {
32
 xhr.customFields.onreadystatechange = function() {
33
 	if(xhr.customFields.readyState == XMLHttpRequest.DONE) {
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
 xhr.token.onreadystatechange = function() {
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
 xhr.lang.onreadystatechange = function() {
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
 xhr.send.onreadystatechange = function() {
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
 // Returns the server's base URI based on the user's script tag
71
 // Returns the server's base URI based on the user's script tag
72
 // return: the SMAM server's base URI
72
 // return: the SMAM server's base URI
73
 function getServer() {
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
 // id: HTML identifier of the document's block to create the form into
91
 // id: HTML identifier of the document's block to create the form into
92
 // return: nothing
92
 // return: nothing
93
 function generateForm(id) {
93
 function generateForm(id) {
94
-    // Get translated strings
95
-    getLangSync();
94
+	// Get translated strings
95
+	getLangSync();
96
 	// Get custom fields if defined in the configuration
96
 	// Get custom fields if defined in the configuration
97
 	getCustomFieldsSync();
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
 // required: boolean on whether the field is required or optional
154
 // required: boolean on whether the field is required or optional
155
 // return: a block containing the field and a label describing it (if enabled)
155
 // return: a block containing the field and a label describing it (if enabled)
156
 function getField(fieldInfos, required) {
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
 // id: field HTML identifier
189
 // id: field HTML identifier
190
 // return: a label node the field's description
190
 // return: a label node the field's description
191
 function getLabel(content, id) {
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
 // required: boolean on whether the field is required or optional
203
 // required: boolean on whether the field is required or optional
204
 // return: a <select> element corresponding to the info passed as input
204
 // return: a <select> element corresponding to the info passed as input
205
 function getSelectField(fieldInfos, required) {
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
 // required: boolean on whether the field is required or optional
234
 // required: boolean on whether the field is required or optional
235
 // return: a <input> HTML element corresponding to the info passed as input
235
 // return: a <input> HTML element corresponding to the info passed as input
236
 function getTextField(fieldInfos, required) {
236
 function getTextField(fieldInfos, required) {
237
-    return getBaseInputField(fieldInfos, required, 'text');
237
+	return getBaseInputField(fieldInfos, required, 'text');
238
 }
238
 }
239
 
239
 
240
 
240
 
243
 // required: boolean on whether the field is required or optional
243
 // required: boolean on whether the field is required or optional
244
 // return: a <input> HTML element corresponding to the info passed as input
244
 // return: a <input> HTML element corresponding to the info passed as input
245
 function getEmailField(fieldInfos, required) {
245
 function getEmailField(fieldInfos, required) {
246
-    return getBaseInputField(fieldInfos, required, 'email');
246
+	return getBaseInputField(fieldInfos, required, 'email');
247
 }
247
 }
248
 
248
 
249
 
249
 
253
 // required: boolean on whether the field is required or optional
253
 // required: boolean on whether the field is required or optional
254
 // return: a basic <input> HTML element with generic info
254
 // return: a basic <input> HTML element with generic info
255
 function getBaseInputField(fieldInfos, required, type) {
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
 // required: boolean on whether the field is required or optional
264
 // required: boolean on whether the field is required or optional
265
 // return: a <textarea> element corresponding to the info passed as input
265
 // return: a <textarea> element corresponding to the info passed as input
266
 function getTextarea(fieldInfos, required) {
266
 function getTextarea(fieldInfos, required) {
267
-    return getBaseField(fieldInfos, required, 'textarea');
267
+	return getBaseField(fieldInfos, required, 'textarea');
268
 }
268
 }
269
 
269
 
270
 
270
 
275
 // tag: the HTML tag the field element must have
275
 // tag: the HTML tag the field element must have
276
 // return: a HTML element of the given tag with basic info given as input
276
 // return: a HTML element of the given tag with basic info given as input
277
 function getBaseField(fieldInfos, required, tag) {
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
 // text: button text
292
 // text: button text
293
 // return: a div node containing the button
293
 // return: a div node containing the button
294
 function getSubmitButton(id, text) {
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
 // Send form data through the XHR object
312
 // Send form data through the XHR object
313
 // return: nothing
313
 // return: nothing
314
 function sendForm() {
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
 // Fetch form inputs from HTML elements
329
 // Fetch form inputs from HTML elements
330
 // return: an object containing all the user's input
330
 // return: an object containing all the user's input
331
 function getFormData() {
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
 // Empties the form fields
357
 // Empties the form fields
358
 // return: nothing
358
 // return: nothing
359
 function cleanForm() {
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
 // Asks the server for a token
367
 // Asks the server for a token
368
 // return: nothing
368
 // return: nothing
369
 function getToken() {
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
 // Asks the server for translated strings to display
375
 // Asks the server for translated strings to display
376
 // return: notghing
376
 // return: notghing
377
 function getLangSync() {
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 Vedi File

1
-var pug         = require('pug');
1
+var pug			= require('pug');
2
 var nodemailer  = require('nodemailer');
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
 // Translation
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
 // Web server
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
 // Logging
16
 // Logging
17
 var printit = require('printit');
17
 var printit = require('printit');
18
 var log = printit({
18
 var log = printit({
19
-    prefix: 'SMAM',
20
-    date: true
19
+	prefix: 'SMAM',
20
+	date: true
21
 });
21
 });
22
 
22
 
23
 
23
 
47
 // A request on /register generates a token and store it, along the user's
47
 // A request on /register generates a token and store it, along the user's
48
 // address, on the tokens object
48
 // address, on the tokens object
49
 app.get('/register', function(req, res, next) {
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
 // A request on /send with user input = mail to be sent
69
 // A request on /send with user input = mail to be sent
70
 app.post('/send', function(req, res, next) {
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
 // the app settings), alongside the boolean for the display of labels in the
128
 // the app settings), alongside the boolean for the display of labels in the
129
 // form block.
129
 // form block.
130
 app.get('/lang', function(req, res, next) {
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
 // A request on /fields sends data on custom fields.
148
 // A request on /fields sends data on custom fields.
149
 app.get('/fields', function(req, res, next) {
149
 app.get('/fields', function(req, res, next) {
150
 	// Response will be JSON
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
 	// Send an array anyway, its length will determine if we need to display any
153
 	// Send an array anyway, its length will determine if we need to display any
154
 	let customFields = settings.customFields || [];
154
 	let customFields = settings.customFields || [];
164
 var host = process.env.HOST || '0.0.0.0';
164
 var host = process.env.HOST || '0.0.0.0';
165
 // Start the server
165
 // Start the server
166
 app.listen(port, host, function() {
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
 // Send mails to the recipients specified in the JSON settings file
175
 // Send mails to the recipients specified in the JSON settings file
176
 // content: object containing mail params
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
 // update(next, infos): Called each time a mail is sent with the infos provided
182
 // update(next, infos): Called each time a mail is sent with the infos provided
183
-//                      by nodemailer
183
+//					  by nodemailer
184
 // done(): Called once each mail has been sent
184
 // done(): Called once each mail has been sent
185
 function sendMails(params, update, done) {
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
 // infos: infos provided by nodemailer
207
 // infos: infos provided by nodemailer
208
 // return: nothing
208
 // return: nothing
209
 function logStatus(infos) {
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
 // token: token used by the sender
222
 // token: token used by the sender
223
 // return: true if the user was registered, false else
223
 // return: true if the user was registered, false else
224
 function checkToken(ip, token) {
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
 // body: body taken from express's request object
252
 // body: body taken from express's request object
253
 // return: true if the body is valid, false else
253
 // return: true if the body is valid, false else
254
 function checkBody(body) {
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
 // Checks the tokens object to see if no token has expired
267
 // Checks the tokens object to see if no token has expired
268
 // return: nothing
268
 // return: nothing
269
 function cleanTokens() {
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
 // custom: object describing data from custom fields
290
 // custom: object describing data from custom fields
291
 // return: an object with user-input data from each field:
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
 function processCustom(custom) {
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
 }