fluffy

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 5  →  ?path2? @ 6
/src/methods/post.js
@@ -0,0 +1,413 @@
#!/usr/bin/env node
 
/*************************************************************************/
/* Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 */
/*************************************************************************/
 
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const auth = require("http-auth");
const stream = require('stream');
const util = require('util');
var formidable = require('formidable');
const EventEmitter = require('events').EventEmitter;
 
// Local imports.
const Cache = require(
path
.resolve(
path.dirname(require.main.filename),
'src/server',
'cache'
)
);
const was = require(
path
.resolve(
path.dirname(require.main.filename),
'src/server',
'was'
)
);
 
// Constructor.
function POST() {
// Create events emitters for logging and data.
EventEmitter.call(this);
};
 
// Process a request.
POST.prototype.process = function(config, request, response, root) {
EventEmitter.call(this);
var self = this;
// Get client details.
const address = request.socket.address();
// Get requested URL.
const requestURL = url.parse(
request.url, true
);
 
// Perform URL re-writes.
Object.keys(config.site.rewrite).forEach((key, index) => {
if (config.site.rewrite[key].test(requestURL.path)) {
const originalPath = requestURL.path;
requestURL.path = requestURL
.path
.replace(
config.site.rewrite[key], key
);
requestURL.pathname = url.parse(
requestURL
.pathname
.replace(
config.site.rewrite[key], key
),
true
)
.pathname;
self.emit('log', {
message: 'Rewrite path: ' +
originalPath +
' to: ' +
requestURL.path,
severity: 'info'
});
}
});
 
const trimmedPath = requestURL
.pathname
.split('/')
.filter(Boolean)
.join('/');
const requestPath = trimmedPath === '/' ?
path.join(root, trimmedPath) :
path.resolve(root, trimmedPath);
var form = new formidable.IncomingForm(),
properfields = {};
 
// Workaround for formidable not parsing multiple filed options.
form.on('field', function(name, value) {
if (!properfields[name]) {
properfields[name] = value;
} else {
if (properfields[name].constructor.toString().indexOf("Array") > -1) { // is array
properfields[name].push(value);
} else { // not array
var tmp = properfields[name];
properfields[name] = [];
properfields[name].push(tmp);
properfields[name].push(value);
}
}
});
form.parse(request, function(error, fields, files) {
// If the form data could not be parsed.
if (error) {
self.emit('log', {
message: 'Could not parse form data from: ' +
address.address + ':' +
address.port +
' requesting: ' +
requestURL.pathname,
severity: 'warning'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
switch(trimmedPath) {
case 'add':
// Write the icon file.
var iconPath = path.join(root, 'data/' + fields['service-name'] + '.png');
var formFile = fs.createReadStream(files['service-icon'].path)
.on('open', () => {
formFile.pipe(
fs.createWriteStream(iconPath)
.on('error', (error) => {
if (error) {
self.emit('log', {
message: 'Unable to create file at: ' + iconPath,
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
})
);
})
.on('error', (error) => {
if (error) {
self.emit('log', {
message: 'Unable to create file at: ' + iconPath,
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
});
// Update the data file.
fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => {
// If the path does not exist, then return early.
if (error) {
self.emit('log', {
message: 'Unable to access data path: ' + dataPath,
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
fs.readFile(dataPath, 'utf8', function(error, data) {
// Could not read data file.
if (error) {
self.emit('log', {
message: 'Unable to read data file.',
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
// Remove previous service if it exists.
var services = JSON.parse(data).filter((service) => {
return service.tooltip != fields['service-name'];
});
services.push({
image: '/data/' + fields['service-name'] + '.png',
width: '50',
height: '50',
url: fields['service-url'],
target: '_top',
tooltip: fields['service-name']
});
fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => {
// Could not write data file.
if (error) {
self.emit('log', {
message: 'Unable to write data file.',
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
// Send the main site index.
self.emit('log', {
message: 'Added new service.',
severity: 'info'
});
self.emit('data', {
status: 200,
data: fs.createReadStream(path.join(root, config.site.index)),
type: 'text/html'
});
});
});
 
});
break;
case 'remove':
fs.realpath(path.join(root, 'data/data.json'), (error, dataPath) => {
// If the path does not exist, then return early.
if (error) {
self.emit('log', {
message: 'Unable to access data path: ' + dataPath,
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
fs.readFile(dataPath, 'utf8', function(error, data) {
// Could not read data file.
if (error) {
self.emit('log', {
message: 'Unable to read data file.',
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
// Remove all specified services and their corresponding icon files.
var currentServices = JSON.parse(data);
var services = [];
currentServices.forEach(currentService => {
if([ properfields['remove-services'] ].some((service) => service === currentService.tooltip)) {
var imagePath = path.join(root, currentService.image);
fs.stat(imagePath, (error, stats) => {
// Image file does not exist.
if (error) {
self.emit('log', {
message: 'Image file does not exist.',
severity: 'warning'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
fs.unlink(imagePath, (error) => {
if(error) {
self.emit('log', {
message: 'Could not remove image file.',
severity: 'warning'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
}
});
});
return;
}
services.push(currentService);
});
// Write the data file back.
fs.writeFile(dataPath, JSON.stringify(services, null, 4), (error) => {
// Could not write data file.
if (error) {
self.emit('log', {
message: 'Unable to write data file.',
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
// Send the main site index.
self.emit('log', {
message: 'Removed services.',
severity: 'info'
});
self.emit('data', {
status: 200,
data: fs.createReadStream(path.join(root, config.site.index)),
type: 'text/html'
});
});
});
});
break;
default:
self.emit('log', {
message: 'No such path.',
severity: 'error'
});
self.emit('data', {
status: 404,
data: new stream.Readable({
read(size) {
this.push(null);
}
}),
type: 'text/plain'
});
return;
}
});
 
return this;
};
 
util.inherits(POST, EventEmitter);
util.inherits(Cache, EventEmitter);
module.exports = POST;