fluffy – Rev 9

Subversion Repositories:
Rev:
#!/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, 'services/' + 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;
                        }
                    });
                    
                var templatePath = path.join(root, 'templates/service.html');
                fs.readFile(templatePath, '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;
                    }
        
                    data = data.replace(/FLUFFY_SERVICE_NAME/g, fields['service-name']);
                    data = data.replace(/FLUFFY_SERVICE_URL/g, fields['service-url'])

                    var htmlPath = path.join(root, 'services/' + fields['service-name'] + '.html');
                    fs.writeFile(htmlPath, data, (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':
                [ properfields['remove-services'] ].forEach((service) => {
                    var service_assets = [
                        path.join(root, 'services/' + service + '.html'),
                        path.join(root, 'services/' + service + '.png')
                    ]
                    service_assets.forEach((asset) => {
                        fs.realpath(asset, (error, dataPath) => {
                            // If the path does not exist, then return early.
                            if (error) {
                              self.emit('log', {
                                  message: 'Unable to access data path: ' + dataPath + ' error: ' + error,
                                  severity: 'error'
                              });
                              self.emit('data', {
                                  status: 404,
                                  data: new stream.Readable({
                                      read(size) {
                                          this.push(null);
                                      }
                                  }),
                                  type: 'text/plain'
                              });
                              return;
                            }
                        
                            fs.unlink(dataPath, (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'
                                    });
                                }
                            });
                        });
                    });
                });
                
                // 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;

Generated by GNU Enscript 1.6.5.90.