<commit_before>
var config = require('./config').config;

var fs = require('fs');

var dontPersist = process.argv.length > 1 && (process.argv.slice(-1)[0] == ('--no-persistence'));

if(! fs.existsSync) {
  fs.existsSync = function(path) {
    try {
      fs.statSync(path);
      return true;
    } catch(e) {
      return false;
    }
  }
}

exports.handler = (function() {
  var url=require('url'),
    crypto=require('crypto'),
    tokens, lastModified, contentType, content;

  function resetState() {
    tokens = {}, lastModified = {}, contentType = {}, content = {};
  }

  function saveState(name, value) {
    fs.writeFile("server-state/" + name + ".json", JSON.stringify(value), function() { });
  }

  function loadState(name) {
    if(fs.existsSync("server-state/" + name + ".json")) {
      return JSON.parse(fs.readFileSync("server-state/" + name + ".json"));
    } else {
      return {}
    }
  }

  function saveData() {
    if(dontPersist) {
      return;
    }
    if(! fs.existsSync("server-state")) {
      fs.mkdirSync("server-state");
    }
    saveState('tokens', tokens);
    saveState('lastModified', lastModified);
    saveState('contentType', contentType);
    saveState('content', content);
  }

  function loadData() {
    if(dontPersist) {
      return;
    }
    tokens = loadState('tokens');
    lastModified = loadState('lastModified');
    contentType = loadState('contentType');
    content = loadState('content');

    console.log("DATA LOADED", tokens, lastModified, contentType, content);
  }

  function makeScopePaths(userName, scopes) {
    var scopePaths=[];
    for(var i=0; i<scopes.length; i++) {
      var thisScopeParts = scopes[i].split(':');
      if(thisScopeParts[0]=='') {
        scopePaths.push(userName+'/:'+thisScopeParts[1]);
      } else {
        scopePaths.push(userName+'/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
        scopePaths.push(userName+'/public/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
      }
    }
    return scopePaths;
  }

  function createInitialTokens() {
    if(! config.initialTokens) {
      return;
    }
    for(var token in config.initialTokens) {
      var scopePaths = makeScopePaths(
        config.defaultUserName, config.initialTokens[token]
      );
      console.log('adding ',scopePaths,' for', token);
      tokens[token] = scopePaths;
    }
  }

  function createToken(userName, scopes, cb) {
    crypto.randomBytes(48, function(ex, buf) {
      var token = buf.toString('hex');
      var scopePaths = makeScopePaths(userName, scopes);
      console.log('createToken ',userName,scopes);
      console.log('adding ',scopePaths,' for',token);
      tokens[token] = scopePaths;
      cb(token);
    });
  }
  function mayRead(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          } else {
            console.log(path.substring(0, scopeParts[0].length)+' != '+ scopeParts[0]);
          }
        }
      }
    } else {
      var pathParts = path.split('/');
      return (pathParts[0]=='' && pathParts[2]=='public' && path.substr(-1) != '/');
    }
  }
  function mayWrite(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(scopeParts.length==2 && scopeParts[1]=='rw' && path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          }
        }
      }
    }
  }
  function writeHead(res, status, origin, timestamp, contentType) {
    var headers = {
      'Access-Control-Allow-Origin': (origin?origin:'*'),
      'Access-Control-Allow-Headers': 'Content-Type, Authorization, Origin',
      'Access-Control-Allow-Methods': 'GET, PUT, DELETE',
    };
    if(timestamp) {
      headers['last-modified']= new Date(timestamp).toString();
    }
    if(contentType) {
      headers['content-type']= contentType;
    }
    res.writeHead(status, headers);
  }
  function writeRaw(res, contentType, content, origin, timestamp) {
    console.log('access-control-allow-origin:'+ (origin?origin:'*'));
    console.log(contentType);
    console.log(content);
    writeHead(res, 200, origin, timestamp, contentType);
    res.write(content);
    res.end();
  }
  function writeJson(res, obj, origin, timestamp) {
    writeRaw(res, 'application/json', JSON.stringify(obj), origin, timestamp);
  }
  function writeHtml(res, html) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">'+html+'');
    res.end();
  }
  function give404(res, origin) {
    console.log('404');
    console.log(content);
    writeHead(res, 404, origin, 'now');
    res.end();
  }
  function computerSaysNo(res, origin) {
    console.log('COMPUTER_SAYS_NO');
    console.log(tokens);
    writeHead(res, 401, origin, 'now');
    res.end();
  }

  function toHtml(str) {
    return str
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
  }
  function portal(urlObj, res) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">
');
    var scopes = {
      'http://todomvc.michiel.5apps.com/': ['tasks:rw'],
      'http://litewrite.michiel.5apps.com/': ['documents:rw'],
      'http://docrastinate.michiel.5apps.com/': ['documents:rw'],
      'http://expenses.michiel.5apps.com/': ['money:rw'],
      'http://evanw-continuous-calendar.michiel.5apps.com/': ['calendar:rw'],
      'http://seven20.epic720.5apps.com/manage.htm': [':rw']
    };
    var outstanding = 0;
    for(var i in scopes) {
      outstanding++;
      (function(i) {
        createToken(config.defaultUserName, scopes[i], function(token) {
          res.write('
'+i+'
');
            +'&access_token='+token+'">'+i+'
');
          outstanding--;
          if(outstanding==0) {
            res.write('
');
            res.end();
          }
        });
      })(i);
    }
  }
  function webfinger(urlObj, res) {
    console.log('WEBFINGER');
    if(urlObj.query['resource']) {
      userAddress = urlObj.query['resource'].substring('acct:'.length);
      userName = userAddress.split('@')[0];
    }
    writeJson(res, {
      links:[{
        href: config.protocol+'://'+config.host+'/storage/'+userName,
        rel: "remoteStorage",
        type: "https://www.w3.org/community/rww/wiki/read-write-web-00#simple",
        properties: {
          'auth-method': "https://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2",
          'auth-endpoint': config.protocol+'://'+config.host+'/auth/'+userName
        }
      }]
    });
  }
  function oauth(urlObj, res) {
    console.log('OAUTH');
    var scopes = decodeURIComponent(urlObj.query['scope']).split(' '),
      clientId = decodeURIComponent(urlObj.query['client_id']),
      redirectUri = decodeURIComponent(urlObj.query['redirect_uri']),
      clientIdToMatch,
      userName;
    if(redirectUri.split('://').length<2) {
      clientIdToMatch=redirectUri;
    } else {
      clientIdToMatch = redirectUri.split('://')[1].split('/')[0];
    }
    if(clientId != clientIdToMatch) {
      writeHtml(res, 'we do not trust this combination of client_id and redirect_uri');
    } else {
      var userName = urlObj.pathname.substring('/auth/'.length);
      createToken(userName, scopes, function(token) {
        writeHtml(res, 'Allow');
      });
    }
  }
  function storage(req, urlObj, res) {
    var path=urlObj.pathname.substring('/storage/'.length);
    if(req.method=='OPTIONS') {
      console.log('OPTIONS ', req.headers);
      writeJson(res, null, req.headers.origin);
    } else if(req.method=='GET') {
      console.log('GET');
      if(mayRead(req.headers.authorization, path)) {
        if(content[path]) {
          if(path.substr(-1)=='/') {
            writeJson(res, content[path], req.headers.origin, 0);
          } else {
            writeRaw(res, contentType[path], content[path], req.headers.origin, lastModified[path]);
          }
        } else {
          if(path.substr(-1)=='/') {
            writeJson(res, {}, req.headers.origin, 0);
          } else {
            give404(res, req.headers.origin);
          }
        }
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='PUT') {
      console.log('PUT');
      if(mayWrite(req.headers.authorization, path) && path.substr(-1)!='/') {
        var dataStr = '';
        req.on('data', function(chunk) {
          dataStr+=chunk;
        });
        req.on('end', function(chunk) {
          var timestamp = new Date().getTime();
          content[path]=dataStr;
          contentType[path]=req.headers['content-type'];
          console.log('stored '+path, content[path], contentType[path]);
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var timestamp=new Date().getTime();
          console.log(pathParts);
          var fileItself=true;
          while(pathParts.length > 1) {
            var thisPart = pathParts.pop();
            if(fileItself) {
              fileItself=false;
            } else {
              thisPart += '/';
            }
            if(!content[pathParts.join('/')+'/']) {
              content[pathParts.join('/')+'/'] = {};
            }
            content[pathParts.join('/')+'/'][thisPart]=timestamp;
            console.log('stored parent '+pathParts.join('/')+'/ ['+thisPart+']='+timestamp, content[pathParts.join('/')+'/']);
          }
          console.log('content:', content);
          console.log('contentType:', contentType);
          console.log('lastModified:', lastModified);
          writeJson(res, null, req.headers.origin, timestamp);
        });
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='DELETE') {
      console.log('DELETE');
      if(mayWrite(req.headers.authorization, path)) {
          var timestamp = new Date().getTime();
          delete content[path];
          delete contentType[path];
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var thisPart = pathParts.pop();
          if(content[pathParts.join('/')+'/']) {
            console.log('delete content['+pathParts.join('/')+'/]['+thisPart+']');
            delete content[pathParts.join('/')+'/'][thisPart];
          }
          console.log(content);
          writeJson(res, null, req.headers.origin, timestamp);
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else {
      console.log('ILLEGAL '+req.method);
      computerSaysNo(res, req.headers.origin);
    }
  }
  function serve(req, res, staticsMap) {
    var urlObj = url.parse(req.url, true), userAddress, userName;
    console.log(urlObj);
    if(urlObj.pathname == '/') {
      console.log('PORTAL');
      portal(urlObj, res);
    } else if(urlObj.pathname == '/.well-known/host-meta.json') {//TODO: implement rest of webfinger
      console.log('HOST-META');
      webfinger(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/auth/'.length) == '/auth/') {
      console.log('OAUTH');
      oauth(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/storage/'.length) == '/storage/') {
      console.log('STORAGE');
      storage(req, urlObj, res);
    } else if(req.method == 'POST' && urlObj.pathname.substring(0, '/reset'.length) == '/reset') { // clear data; used in tests.
      resetState();
      writeJson(res, { forgot: 'everything' });
    } else {
      console.log('UNKNOWN');
      writeJson(res, urlObj.query);
    }
  }

  resetState();
  loadData();
  createInitialTokens();

  return {
    serve: serve
  };
})();

if(require.main==module) {//if this file is directly called from the CLI
  require('http').createServer(exports.handler.serve).listen(config.port);
  console.log("Example server started on 0.0.0.0:" + config.port);
}

<commit_msg>added helpers to nodejs-example server, so it can be used from tests<commit_after>
var config = require('./config').config;

var fs = require('fs');

var dontPersist = process.argv.length > 1 && (process.argv.slice(-1)[0] == ('--no-persistence'));

if(! fs.existsSync) {
  fs.existsSync = function(path) {
    try {
      fs.statSync(path);
      return true;
    } catch(e) {
      return false;
    }
  }
}

exports.server = (function() {
  var url=require('url'),
    crypto=require('crypto'),
    tokens, lastModified, contentType, content;

  function resetState() {
    tokens = {}, lastModified = {}, contentType = {}, content = {};
  }

  function getState() {
    return {
      tokens: tokens,
      lastModified: lastModified,
      contentType: contentType,
      content: content
    };
  }

  function saveState(name, value) {
    fs.writeFile("server-state/" + name + ".json", JSON.stringify(value), function() { });
  }

  function loadState(name) {
    if(fs.existsSync("server-state/" + name + ".json")) {
      return JSON.parse(fs.readFileSync("server-state/" + name + ".json"));
    } else {
      return {}
    }
  }

  function saveData() {
    if(dontPersist) {
      return;
    }
    if(! fs.existsSync("server-state")) {
      fs.mkdirSync("server-state");
    }
    saveState('tokens', tokens);
    saveState('lastModified', lastModified);
    saveState('contentType', contentType);
    saveState('content', content);
  }

  function loadData() {
    if(dontPersist) {
      return;
    }
    tokens = loadState('tokens');
    lastModified = loadState('lastModified');
    contentType = loadState('contentType');
    content = loadState('content');

    console.log("DATA LOADED", tokens, lastModified, contentType, content);
  }

  function makeScopePaths(userName, scopes) {
    var scopePaths=[];
    for(var i=0; i<scopes.length; i++) {
      var thisScopeParts = scopes[i].split(':');
      if(thisScopeParts[0]=='') {
        scopePaths.push(userName+'/:'+thisScopeParts[1]);
      } else {
        scopePaths.push(userName+'/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
        scopePaths.push(userName+'/public/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
      }
    }
    return scopePaths;
  }

  function addToken(token, scopes) {
    tokens[token] = makeScopePaths('me', scopes);
  }

  function createInitialTokens() {
    if(! config.initialTokens) {
      return;
    }
    for(var token in config.initialTokens) {
      var scopePaths = makeScopePaths(
        config.defaultUserName, config.initialTokens[token]
      );
      console.log('adding ',scopePaths,' for', token);
      tokens[token] = scopePaths;
    }
  }

  function createToken(userName, scopes, cb) {
    crypto.randomBytes(48, function(ex, buf) {
      var token = buf.toString('hex');
      var scopePaths = makeScopePaths(userName, scopes);
      console.log('createToken ',userName,scopes);
      console.log('adding ',scopePaths,' for',token);
      tokens[token] = scopePaths;
      cb(token);
    });
  }
  function mayRead(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          } else {
            console.log(path.substring(0, scopeParts[0].length)+' != '+ scopeParts[0]);
          }
        }
      }
    } else {
      var pathParts = path.split('/');
      return (pathParts[0]=='' && pathParts[2]=='public' && path.substr(-1) != '/');
    }
  }
  function mayWrite(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(scopeParts.length==2 && scopeParts[1]=='rw' && path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          }
        }
      }
    }
  }
  function writeHead(res, status, origin, timestamp, contentType) {
    var headers = {
      'Access-Control-Allow-Origin': (origin?origin:'*'),
      'Access-Control-Allow-Headers': 'Content-Type, Authorization, Origin',
      'Access-Control-Allow-Methods': 'GET, PUT, DELETE',
    };
    if(timestamp) {
      headers['last-modified']= new Date(timestamp).toString();
    }
    if(contentType) {
      headers['content-type']= contentType;
    }
    res.writeHead(status, headers);
  }
  function writeRaw(res, contentType, content, origin, timestamp) {
    console.log('access-control-allow-origin:'+ (origin?origin:'*'));
    console.log(contentType);
    console.log(content);
    writeHead(res, 200, origin, timestamp, contentType);
    res.write(content);
    res.end();
  }
  function writeJson(res, obj, origin, timestamp) {
    writeRaw(res, 'application/json', JSON.stringify(obj), origin, timestamp);
  }
  function writeHtml(res, html) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">'+html+'');
    res.end();
  }
  function give404(res, origin) {
    console.log('404');
    console.log(content);
    writeHead(res, 404, origin, 'now');
    res.end();
  }
  function computerSaysNo(res, origin) {
    console.log('COMPUTER_SAYS_NO');
    console.log(tokens);
    writeHead(res, 401, origin, 'now');
    res.end();
  }

  function toHtml(str) {
    return str
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
  }
  function portal(urlObj, res) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">
');
    var scopes = {
      'http://todomvc.michiel.5apps.com/': ['tasks:rw'],
      'http://litewrite.michiel.5apps.com/': ['documents:rw'],
      'http://docrastinate.michiel.5apps.com/': ['documents:rw'],
      'http://expenses.michiel.5apps.com/': ['money:rw'],
      'http://evanw-continuous-calendar.michiel.5apps.com/': ['calendar:rw'],
      'http://seven20.epic720.5apps.com/manage.htm': [':rw']
    };
    var outstanding = 0;
    for(var i in scopes) {
      outstanding++;
      (function(i) {
        createToken(config.defaultUserName, scopes[i], function(token) {
          res.write('
'+i+'
');
            +'&access_token='+token+'">'+i+'
');
          outstanding--;
          if(outstanding==0) {
            res.write('
');
            res.end();
          }
        });
      })(i);
    }
  }
  function webfinger(urlObj, res) {
    console.log('WEBFINGER');
    if(urlObj.query['resource']) {
      userAddress = urlObj.query['resource'].substring('acct:'.length);
      userName = userAddress.split('@')[0];
    }
    writeJson(res, {
      links:[{
        href: config.protocol+'://'+config.host+'/storage/'+userName,
        rel: "remoteStorage",
        type: "https://www.w3.org/community/rww/wiki/read-write-web-00#simple",
        properties: {
          'auth-method': "https://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2",
          'auth-endpoint': config.protocol+'://'+config.host+'/auth/'+userName
        }
      }]
    });
  }
  function oauth(urlObj, res) {
    console.log('OAUTH');
    var scopes = decodeURIComponent(urlObj.query['scope']).split(' '),
      clientId = decodeURIComponent(urlObj.query['client_id']),
      redirectUri = decodeURIComponent(urlObj.query['redirect_uri']),
      clientIdToMatch,
      userName;
    if(redirectUri.split('://').length<2) {
      clientIdToMatch=redirectUri;
    } else {
      clientIdToMatch = redirectUri.split('://')[1].split('/')[0];
    }
    if(clientId != clientIdToMatch) {
      writeHtml(res, 'we do not trust this combination of client_id and redirect_uri');
    } else {
      var userName = urlObj.pathname.substring('/auth/'.length);
      createToken(userName, scopes, function(token) {
        writeHtml(res, 'Allow');
      });
    }
  }
  function storage(req, urlObj, res) {
    var path=urlObj.pathname.substring('/storage/'.length);
    if(req.method=='OPTIONS') {
      console.log('OPTIONS ', req.headers);
      writeJson(res, null, req.headers.origin);
    } else if(req.method=='GET') {
      console.log('GET');
      if(mayRead(req.headers.authorization, path)) {
        if(content[path]) {
          if(path.substr(-1)=='/') {
            writeJson(res, content[path], req.headers.origin, 0);
          } else {
            writeRaw(res, contentType[path], content[path], req.headers.origin, lastModified[path]);
          }
        } else {
          if(path.substr(-1)=='/') {
            writeJson(res, {}, req.headers.origin, 0);
          } else {
            give404(res, req.headers.origin);
          }
        }
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='PUT') {
      console.log('PUT');
      if(mayWrite(req.headers.authorization, path) && path.substr(-1)!='/') {
        var dataStr = '';
        req.on('data', function(chunk) {
          dataStr+=chunk;
        });
        req.on('end', function(chunk) {
          var timestamp = new Date().getTime();
          content[path]=dataStr;
          contentType[path]=req.headers['content-type'];
          console.log('stored '+path, content[path], contentType[path]);
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var timestamp=new Date().getTime();
          console.log(pathParts);
          var fileItself=true;
          while(pathParts.length > 1) {
            var thisPart = pathParts.pop();
            if(fileItself) {
              fileItself=false;
            } else {
              thisPart += '/';
            }
            if(!content[pathParts.join('/')+'/']) {
              content[pathParts.join('/')+'/'] = {};
            }
            content[pathParts.join('/')+'/'][thisPart]=timestamp;
            console.log('stored parent '+pathParts.join('/')+'/ ['+thisPart+']='+timestamp, content[pathParts.join('/')+'/']);
          }
          console.log('content:', content);
          console.log('contentType:', contentType);
          console.log('lastModified:', lastModified);
          writeJson(res, null, req.headers.origin, timestamp);
        });
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='DELETE') {
      console.log('DELETE');
      if(mayWrite(req.headers.authorization, path)) {
          var timestamp = new Date().getTime();
          delete content[path];
          delete contentType[path];
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var thisPart = pathParts.pop();
          if(content[pathParts.join('/')+'/']) {
            console.log('delete content['+pathParts.join('/')+'/]['+thisPart+']');
            delete content[pathParts.join('/')+'/'][thisPart];
          }
          console.log(content);
          writeJson(res, null, req.headers.origin, timestamp);
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else {
      console.log('ILLEGAL '+req.method);
      computerSaysNo(res, req.headers.origin);
    }
  }
  function serve(req, res, staticsMap) {
    var urlObj = url.parse(req.url, true), userAddress, userName;
    console.log(urlObj);
    if(urlObj.pathname == '/') {
      console.log('PORTAL');
      portal(urlObj, res);
    } else if(urlObj.pathname == '/.well-known/host-meta.json') {//TODO: implement rest of webfinger
      console.log('HOST-META');
      webfinger(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/auth/'.length) == '/auth/') {
      console.log('OAUTH');
      oauth(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/storage/'.length) == '/storage/') {
      console.log('STORAGE');
      storage(req, urlObj, res);
    } else if(req.method == 'POST' && urlObj.pathname.substring(0, '/reset'.length) == '/reset') { // clear data; used in tests.
      resetState();
      writeJson(res, { forgot: 'everything' });
    } else {
      console.log('UNKNOWN');
      writeJson(res, urlObj.query);
    }
  }

  resetState();
  loadData();
  createInitialTokens();

  return {
    serve: serve,
    getState: getState,
    resetState: resetState,
    addToken: addToken
  };
})();

if(require.main==module) {//if this file is directly called from the CLI
  require('http').createServer(exports.server.serve).listen(config.port);
  console.log("Example server started on 0.0.0.0:" + config.port);
}

<|endoftext|>


<commit_before>// Required modules
var async = require('async');
var validator = require('validator');

// Variables passed from server.js
var client, model, app;

// Helper functions
function get_years()
{
    // Get current year
    var year = new Date().getFullYear();
    var years = [];

    for(var i = year, l = year - 150; i >= l; i--)
    {
        years.push({'value': i, 'text': i});
    }

    return years;
}

function get_months()
{
    var month_list = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'November', 'December'];
    var months = [];

    for(var i = 0, l = month_list.length; i < l; i++)
    {
        months.push({'value': i + 1, 'text': month_list[i]});
    }

    return months;
}

function get_days()
{
    var days = [];

    for(var i = 1; i <= 31; i++)
    {
        days.push({'value': i, 'text': i});
    }

    return days;
}

function check_url(url)
{
    // Trim whitespace
    url = validator.trim(url);

    if(validator.isURL(url))
    {
        if(url.indexOf('http') == 0)
            return url;

        return 'http://' + url;
    }

    return false;
}

function check_social(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        var url = check_url(value);

        if(url)
            valid.push([key, url]);
        else
            errors.push([key, 'This field must be a URL']);
    }

    return {valid: valid, errors: errors};
}

function check_personal(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        // Handlers for special fields
        if(key == "birth")
        {
            // Make sure only numbers are entered for birthdays
            var birth =
            {
                'year': parseInt(value.year),
                'month': parseInt(value.month),
                'day': parseInt(value.day)
            };

            valid.push([key, birth]);
        }
        else if(key == "address")
        {
            // Make sure a clever user didn't add extra address fields
            var address =
            {
                address: value.address,
                city: value.city,
                state: value.state,
                zip: value.zip
            };

            valid.push([key, address]);
        }
        else
        {
            valid.push([key, value]);
        }
    }

    return {valid: valid, errors: errors};
}

function check_nerdy(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        if(key == "github")
        {
            var url = check_url(value);

            if(url)
                valid.push([key, url]);
            else
                errors.push([key, 'This field must be a URL']);
        }

        // TODO: Check to ensure pubkeys are in PEM format
        else if(key == "pubkey")
        {
            valid.push([key, value]);
        }
    }

    return {valid: valid, errors: errors};
}

module.exports = function(required)
{
    client = required.client;
    model = required.model;
    app = required.app;

    app.get('/profile', function(req, res)
    {
        // Users shouldn't be here if they're not logged in
        if(typeof req.session.user == "undefined")
        {
            res.redirect('/');
            return;
        }

        console.log("GET: /profile");
        res.render('profile', {
            title: 'Profile',
            user: req.session.user,
            user_data: req.session.user_data,
            years: get_years(),
            months: get_months(),
            days: get_days(),
            partials: {
                head: 'partials/head',
                header: 'partials/header',
                foot: 'partials/foot'
            }
        });
    });

    app.post('/profile', function(req, res)
    {
        // Users shouldn't be here if they're not logged in
        if(typeof req.session.user == "undefined")
        {
            res.redirect('/');
            return;
        }

        console.log("POST: /profile");
        var user_data = JSON.parse(JSON.stringify(req.session.user_data));
        var errors = {};
        var error = false;

        var social = check_social(
        [
            ['website', req.body.website],
            ['twitter', req.body.twitter],
            ['facebook', req.body.facebook],
            ['soundcloud', req.body.soundcloud],
            ['bandcamp', req.body.bandcamp]
        ]);

        var personal = check_personal(
        [
            ['about', req.body.about],
            ['full_name', req.body.full_name],
            ['birth', req.body.birth],
            ['phone', req.body.phone],
            ['address', req.body.address],
        ]);

        var nerdy = check_nerdy(
        [
            ['github', req.body.github],
            ['pubkey', req.body.pubkey]
        ]);

        var error_list = social.errors.concat(personal.errors, nerdy.errors);
        var valid_list = social.valid.concat(personal.valid, nerdy.valid);

        for(var i = 0, l = error_list.length; i < l; i++)
        {
            var key = error_list[i][0];
            var value = error_list[i][1];

            errors[key] = value;
            error = true;
        }

        for(var i = 0, l = valid_list.length; i < l; i++)
        {
            var key = valid_list[i][0];
            var value = valid_list[i][1];

            user_data[key] = value;
        }

        if(error)
        {
            res.send(JSON.stringify({'status': 'error', 'errors': errors}));
            res.end();
        }
        else
        {
            user_data.profile_saved = new Date().getTime();

            // Save member information
            model.user.set({user_id: req.session.user_id}, {user_data: JSON.stringify(user_data)}, function(error, response)
            {
                if(error)
                {
                    res.send(JSON.stringify({'status': 'error', 'message': 'An error occured while saving your account information. Please try again.'}));
                    res.end();
                }
                else
                {
                    req.session.user_data = user_data;

                    res.send(JSON.stringify({'status': 'success', 'message': 'Profile updated.', 'redirect': {'url': '/profile', 'timeout': 2}}));
                    res.end();
                }
            });
        }
    });
}
<commit_msg>Apparently I forgot october :jack_o_lantern:<commit_after>// Required modules
var async = require('async');
var validator = require('validator');

// Variables passed from server.js
var client, model, app;

// Helper functions
function get_years()
{
    // Get current year
    var year = new Date().getFullYear();
    var years = [];

    for(var i = year, l = year - 150; i >= l; i--)
    {
        years.push({'value': i, 'text': i});
    }

    return years;
}

function get_months()
{
    var month_list = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
    var months = [];

    for(var i = 0, l = month_list.length; i < l; i++)
    {
        months.push({'value': i + 1, 'text': month_list[i]});
    }

    return months;
}

function get_days()
{
    var days = [];

    for(var i = 1; i <= 31; i++)
    {
        days.push({'value': i, 'text': i});
    }

    return days;
}

function check_url(url)
{
    // Trim whitespace
    url = validator.trim(url);

    if(validator.isURL(url))
    {
        if(url.indexOf('http') == 0)
            return url;

        return 'http://' + url;
    }

    return false;
}

function check_social(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        var url = check_url(value);

        if(url)
            valid.push([key, url]);
        else
            errors.push([key, 'This field must be a URL']);
    }

    return {valid: valid, errors: errors};
}

function check_personal(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        // Handlers for special fields
        if(key == "birth")
        {
            // Make sure only numbers are entered for birthdays
            var birth =
            {
                'year': parseInt(value.year),
                'month': parseInt(value.month),
                'day': parseInt(value.day)
            };

            valid.push([key, birth]);
        }
        else if(key == "address")
        {
            // Make sure a clever user didn't add extra address fields
            var address =
            {
                address: value.address,
                city: value.city,
                state: value.state,
                zip: value.zip
            };

            valid.push([key, address]);
        }
        else
        {
            valid.push([key, value]);
        }
    }

    return {valid: valid, errors: errors};
}

function check_nerdy(data)
{
    var valid = [];
    var errors = [];

    for(var i = 0, l = data.length; i < l; i++)
    {
        var key = data[i][0];
        var value = data[i][1];

        // Skip empty values
        if(typeof value == "undefined" || value.toString().length == 0)
            continue;

        if(key == "github")
        {
            var url = check_url(value);

            if(url)
                valid.push([key, url]);
            else
                errors.push([key, 'This field must be a URL']);
        }

        // TODO: Check to ensure pubkeys are in PEM format
        else if(key == "pubkey")
        {
            valid.push([key, value]);
        }
    }

    return {valid: valid, errors: errors};
}

module.exports = function(required)
{
    client = required.client;
    model = required.model;
    app = required.app;

    app.get('/profile', function(req, res)
    {
        // Users shouldn't be here if they're not logged in
        if(typeof req.session.user == "undefined")
        {
            res.redirect('/');
            return;
        }

        console.log("GET: /profile");
        res.render('profile', {
            title: 'Profile',
            user: req.session.user,
            user_data: req.session.user_data,
            years: get_years(),
            months: get_months(),
            days: get_days(),
            partials: {
                head: 'partials/head',
                header: 'partials/header',
                foot: 'partials/foot'
            }
        });
    });

    app.post('/profile', function(req, res)
    {
        // Users shouldn't be here if they're not logged in
        if(typeof req.session.user == "undefined")
        {
            res.redirect('/');
            return;
        }

        console.log("POST: /profile");
        var user_data = JSON.parse(JSON.stringify(req.session.user_data));
        var errors = {};
        var error = false;

        var social = check_social(
        [
            ['website', req.body.website],
            ['twitter', req.body.twitter],
            ['facebook', req.body.facebook],
            ['soundcloud', req.body.soundcloud],
            ['bandcamp', req.body.bandcamp]
        ]);

        var personal = check_personal(
        [
            ['about', req.body.about],
            ['full_name', req.body.full_name],
            ['birth', req.body.birth],
            ['phone', req.body.phone],
            ['address', req.body.address],
        ]);

        var nerdy = check_nerdy(
        [
            ['github', req.body.github],
            ['pubkey', req.body.pubkey]
        ]);

        var error_list = social.errors.concat(personal.errors, nerdy.errors);
        var valid_list = social.valid.concat(personal.valid, nerdy.valid);

        for(var i = 0, l = error_list.length; i < l; i++)
        {
            var key = error_list[i][0];
            var value = error_list[i][1];

            errors[key] = value;
            error = true;
        }

        for(var i = 0, l = valid_list.length; i < l; i++)
        {
            var key = valid_list[i][0];
            var value = valid_list[i][1];

            user_data[key] = value;
        }

        if(error)
        {
            res.send(JSON.stringify({'status': 'error', 'errors': errors}));
            res.end();
        }
        else
        {
            user_data.profile_saved = new Date().getTime();

            // Save member information
            model.user.set({user_id: req.session.user_id}, {user_data: JSON.stringify(user_data)}, function(error, response)
            {
                if(error)
                {
                    res.send(JSON.stringify({'status': 'error', 'message': 'An error occured while saving your account information. Please try again.'}));
                    res.end();
                }
                else
                {
                    req.session.user_data = user_data;

                    res.send(JSON.stringify({'status': 'success', 'message': 'Profile updated.', 'redirect': {'url': '/profile', 'timeout': 2}}));
                    res.end();
                }
            });
        }
    });
}
<|endoftext|>


<commit_before>var router = require('express').Router();
var log = require('../lib/logger');
var models = require('../models')(require('../lib/db'));

var Student = models.Student;

module.exports = function() {
  router.route('/students')
    /* GET '/students' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim

          GET '/students' returns:
            - A status code of success/failure
            - The entire student list via an array of JSON objects bearing every student stored in DB
    */
    .get(function(req, res, next) {
      Student.find({}, function(err, students) {
        if(err) {
          return next(err);
        }

        log.info({
          req: req,
          res: res
        });

        res.status(200).json(students);
      });
    })

    /* POST '/students' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - (1) new object student with properties in accordance with data model design in JSON format

          POST '/students' returns:
            - A status code of success/failure
            - If successful, the new student object data passed in by the request
    */
    .post(function(req, res, next) {
      if(!req.body) {
        log.warn({
          req: req,
          res: res
        });
        res.status(400).send('Invalid data!');
        return;
      }

      var newStud = new Student({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        gender: req.body.gender,
        rankId: req.body.rankId,
        healthInformation: req.body.healthInformation,
        guardianInformation: req.body.guardianInformation,
        email: req.body.email,
        membershipStatus: req.body.membershipStatus,
        membershipExpiry: req.body.membershipExpiry,
        phone: req.body.phone,
        birthDate: req.body.birthDate
      });

      newStud.save(function (err, newStud) {
        if (err) {
          log.error({
            err: err,
            req: req,
            res: res
          });
          if (err.name === "ValidationError") {
            res.status(400).send('Invalid data!');
            return;
          }
          return next(err);
        }
        res.status(201).json(newStud);
      });
  });

  router.route('/student/:id')
    /* GET '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The ID of the object being requested (in JSON format ie. {"_id": "6"})

          GET '/student/:id' returns:
            - A status code of success/failure
            - The student object bearing the given ID and all of its associated properties and values
    */
    .get(function(req, res, next) {
      var id = req.params.id;

      Student.findById(id, function(err, student) {
        if (err || !student) {
          log.error({
            err: err,
            req: req,
            res: res
          });
          return res.status(400).send('Invalid data!');
        }
        log.info({
          req: req,
          res: res
        });
        res.status(200).json(student);
      });
    })

    /* PUT '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The ID of the corresponding object to be altered (in JSON format)
            - The property being altered and its new value in the request body

          PUT '/student/:id' returns:
            - A status code of success/failure
            - If successful, the altered student data object passed in by the request
    */
    .put(function(req, res, next) { debugger;
      var validData = true;
      var studentProperties = [
        "_id", "firstName", "lastName", "gender",
        "rankId", "healthInformation", "guardianInformation",
        "email",  "membershipStatus", "membershipExpiry",
        "phone", "birthDate"
      ];

      if(!req.body) {
        res.status(400).send('Invalid data!');
        return;
      }

      var id = req.params.id;

      Object.keys(req.body).forEach(function(key) {
        if(studentProperties.indexOf(key) === -1) {
          validData = false;
        }
      });

      if (!validData) {
        res.status(400).send('Invalid data!');
        return;
      }

      Student.findOneAndUpdate({"_id": id}, req.body, {new: true}, function(err, student) {
        if (err && err.name === "CastError") {
          log.warn({
            req: req,
            res: res
          });
           return res.status(400).send('Invalid data!');
        }
        log.info({
          req: req,
          res: res
        });
        res.status(200).json(student);
      });
    })

    /* DELETE '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The student ID of the corresponding object to be deleted

          DELETE '/student/:id' returns:
            - A status code of success/failure
    */
    .delete(function(req, res, next) {
      var id = req.params.id;
      Student.remove({_id: id}, function(err) {
        if(err) {
          log.error({
            req: req,
            res: res,
            err: err
          });
          return res.status(204).send('Operation completed');
        }
        log.info({
          req: req,
          res: res
        });
        return res.status(204).send('Operation completed');
      });
    });

    return router;
}();
<commit_msg>removed debugger statement<commit_after>var router = require('express').Router();
var log = require('../lib/logger');
var models = require('../models')(require('../lib/db'));

var Student = models.Student;

module.exports = function() {
  router.route('/students')
    /* GET '/students' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim

          GET '/students' returns:
            - A status code of success/failure
            - The entire student list via an array of JSON objects bearing every student stored in DB
    */
    .get(function(req, res, next) {
      Student.find({}, function(err, students) {
        if(err) {
          return next(err);
        }

        log.info({
          req: req,
          res: res
        });

        res.status(200).json(students);
      });
    })

    /* POST '/students' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - (1) new object student with properties in accordance with data model design in JSON format

          POST '/students' returns:
            - A status code of success/failure
            - If successful, the new student object data passed in by the request
    */
    .post(function(req, res, next) {
      if(!req.body) {
        log.warn({
          req: req,
          res: res
        });
        res.status(400).send('Invalid data!');
        return;
      }

      var newStud = new Student({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        gender: req.body.gender,
        rankId: req.body.rankId,
        healthInformation: req.body.healthInformation,
        guardianInformation: req.body.guardianInformation,
        email: req.body.email,
        membershipStatus: req.body.membershipStatus,
        membershipExpiry: req.body.membershipExpiry,
        phone: req.body.phone,
        birthDate: req.body.birthDate
      });

      newStud.save(function (err, newStud) {
        if (err) {
          log.error({
            err: err,
            req: req,
            res: res
          });
          if (err.name === "ValidationError") {
            res.status(400).send('Invalid data!');
            return;
          }
          return next(err);
        }
        res.status(201).json(newStud);
      });
  });

  router.route('/student/:id')
    /* GET '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The ID of the object being requested (in JSON format ie. {"_id": "6"})

          GET '/student/:id' returns:
            - A status code of success/failure
            - The student object bearing the given ID and all of its associated properties and values
    */
    .get(function(req, res, next) {
      var id = req.params.id;

      Student.findById(id, function(err, student) {
        if (err || !student) {
          log.error({
            err: err,
            req: req,
            res: res
          });
          return res.status(400).send('Invalid data!');
        }
        log.info({
          req: req,
          res: res
        });
        res.status(200).json(student);
      });
    })

    /* PUT '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The ID of the corresponding object to be altered (in JSON format)
            - The property being altered and its new value in the request body

          PUT '/student/:id' returns:
            - A status code of success/failure
            - If successful, the altered student data object passed in by the request
    */
    .put(function(req, res, next) {
      var validData = true;
      var studentProperties = [
        "_id", "firstName", "lastName", "gender",
        "rankId", "healthInformation", "guardianInformation",
        "email",  "membershipStatus", "membershipExpiry",
        "phone", "birthDate"
      ];

      if(!req.body) {
        res.status(400).send('Invalid data!');
        return;
      }

      var id = req.params.id;

      Object.keys(req.body).forEach(function(key) {
        if(studentProperties.indexOf(key) === -1) {
          validData = false;
        }
      });

      if (!validData) {
        res.status(400).send('Invalid data!');
        return;
      }

      Student.findOneAndUpdate({"_id": id}, req.body, {new: true}, function(err, student) {
        if (err && err.name === "CastError") {
          log.warn({
            req: req,
            res: res
          });
           return res.status(400).send('Invalid data!');
        }
        log.info({
          req: req,
          res: res
        });
        res.status(200).json(student);
      });
    })

    /* DELETE '/student/:id' accepts:
            - Auth/Session tokens required from successful user sign-in to check for appropriate role/claim
            - The student ID of the corresponding object to be deleted

          DELETE '/student/:id' returns:
            - A status code of success/failure
    */
    .delete(function(req, res, next) {
      var id = req.params.id;
      Student.remove({_id: id}, function(err) {
        if(err) {
          log.error({
            req: req,
            res: res,
            err: err
          });
          return res.status(204).send('Operation completed');
        }
        log.info({
          req: req,
          res: res
        });
        return res.status(204).send('Operation completed');
      });
    });

    return router;
}();
<|endoftext|>


<commit_before>/* globals FileUpload */
Meteor.startup(function() {
	WebApp.connectHandlers.use('/avatar/', Meteor.bindEnvironment(function(req, res/*, next*/) {
		const params = {
			username: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))
		};

		if (_.isEmpty(params.username)) {
			res.writeHead(403);
			res.write('Forbidden');
			res.end();
			return;
		}

		const match = /^\/([^?]*)/.exec(req.url);

		if (match[1]) {
			let username = decodeURIComponent(match[1]);
			let file;

			if (username[0] !== '@') {
				if (Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm) {
					const user = RocketChat.models.Users.findOneByUsername(username);
					if (user && user.services && user.services.sandstorm && user.services.sandstorm.picture) {
						res.setHeader('Location', user.services.sandstorm.picture);
						res.writeHead(302);
						res.end();
						return;
					}
				}
				file = RocketChat.models.Avatars.findOneByName(username);
			}

			if (file) {
				res.setHeader('Content-Security-Policy', 'default-src \'none\'');

				return FileUpload.get(file, req, res);
			} else {
				res.setHeader('Content-Type', 'image/svg+xml');
				res.setHeader('Cache-Control', 'public, max-age=0');
				res.setHeader('Expires', '-1');
				res.setHeader('Last-Modified', 'Thu, 01 Jan 2015 00:00:00 GMT');

				const reqModifiedHeader = req.headers['if-modified-since'];

				if (reqModifiedHeader) {
					if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') {
						res.writeHead(304);
						res.end();
						return;
					}
				}

				const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];

				if (RocketChat.settings.get('UI_Use_Name_Avatar')) {
					const user = RocketChat.models.Users.findOneByUsername(username, {
						fields: {
							name: 1
						}
					});

					if (user && user.name) {
						username = user.name;
					}
				}

				let color = '';
				let initials = '';

				if (username === '?') {
					color = '#000';
					initials = username;
				} else {
					const position = username.length % colors.length;

					color = colors[position];
					username = username.replace(/[^A-Za-z0-9]/g, '.').replace(/\.+/g, '.').replace(/(^\.)|(\.$)/g, '');

					const usernameParts = username.split('.');

					initials = usernameParts.length > 1 ? _.first(usernameParts)[0] + _.last(usernameParts)[0] : username.replace(/[^A-Za-z0-9]/g, '').substr(0, 2);
					initials = initials.toUpperCase();
				}

				const svg = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" pointer-events=\"none\" width=\"50\" height=\"50\" style=\"width: 50px; height: 50px; background-color: ${ color };\">\n	<text text-anchor=\"middle\" y=\"50%\" x=\"50%\" dy=\"0.36em\" pointer-events=\"auto\" fill=\"#ffffff\" font-family=\"Helvetica, Arial, Lucida Grande, sans-serif\" style=\"font-weight: 400; font-size: 28px;\">\n		${ initials }\n	</text>\n</svg>`;
				res.write(svg);
				res.end();

				return;
			}
		}

		res.writeHead(404);
		res.end();
		return;
	}));
});
<commit_msg>Fix mobile avatars<commit_after>/* globals FileUpload */
Meteor.startup(function() {
	WebApp.connectHandlers.use('/avatar/', Meteor.bindEnvironment(function(req, res/*, next*/) {
		const params = {
			username: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))
		};

		if (_.isEmpty(params.username)) {
			res.writeHead(403);
			res.write('Forbidden');
			res.end();
			return;
		}

		const match = /^\/([^?]*)/.exec(req.url);

		if (match[1]) {
			let username = decodeURIComponent(match[1]);
			let file;

			username = username.replace(/\.jpg$/, '');

			if (username[0] !== '@') {
				if (Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm) {
					const user = RocketChat.models.Users.findOneByUsername(username);
					if (user && user.services && user.services.sandstorm && user.services.sandstorm.picture) {
						res.setHeader('Location', user.services.sandstorm.picture);
						res.writeHead(302);
						res.end();
						return;
					}
				}
				file = RocketChat.models.Avatars.findOneByName(username);
			}

			if (file) {
				res.setHeader('Content-Security-Policy', 'default-src \'none\'');

				return FileUpload.get(file, req, res);
			} else {
				res.setHeader('Content-Type', 'image/svg+xml');
				res.setHeader('Cache-Control', 'public, max-age=0');
				res.setHeader('Expires', '-1');
				res.setHeader('Last-Modified', 'Thu, 01 Jan 2015 00:00:00 GMT');

				const reqModifiedHeader = req.headers['if-modified-since'];

				if (reqModifiedHeader) {
					if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') {
						res.writeHead(304);
						res.end();
						return;
					}
				}

				const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];

				if (RocketChat.settings.get('UI_Use_Name_Avatar')) {
					const user = RocketChat.models.Users.findOneByUsername(username, {
						fields: {
							name: 1
						}
					});

					if (user && user.name) {
						username = user.name;
					}
				}

				let color = '';
				let initials = '';

				if (username === '?') {
					color = '#000';
					initials = username;
				} else {
					const position = username.length % colors.length;

					color = colors[position];
					username = username.replace(/[^A-Za-z0-9]/g, '.').replace(/\.+/g, '.').replace(/(^\.)|(\.$)/g, '');

					const usernameParts = username.split('.');

					initials = usernameParts.length > 1 ? _.first(usernameParts)[0] + _.last(usernameParts)[0] : username.replace(/[^A-Za-z0-9]/g, '').substr(0, 2);
					initials = initials.toUpperCase();
				}

				const svg = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" pointer-events=\"none\" width=\"50\" height=\"50\" style=\"width: 50px; height: 50px; background-color: ${ color };\">\n	<text text-anchor=\"middle\" y=\"50%\" x=\"50%\" dy=\"0.36em\" pointer-events=\"auto\" fill=\"#ffffff\" font-family=\"Helvetica, Arial, Lucida Grande, sans-serif\" style=\"font-weight: 400; font-size: 28px;\">\n		${ initials }\n	</text>\n</svg>`;
				res.write(svg);
				res.end();

				return;
			}
		}

		res.writeHead(404);
		res.end();
		return;
	}));
});
<|endoftext|>


<commit_before>const redis = require('./redis');
const config = require('../config');
const { generateRandomVehicles } = require('../simulation/vehicles');
const { createVehicle } = require('../client-thrift');

const parseVehiclesArray = vehicles =>
  vehicles
    // filter vehicles
    .filter(vehicle => !!vehicle)
    // format response objects
    .map(vehicle => ({
      id: vehicle.id,
      model: vehicle.model,
      icon: vehicle.icon,
      coords: { lat: parseFloat(vehicle.lat), long: parseFloat(vehicle.long) },
      rating: parseFloat(vehicle.rating),
      missions_completed: parseInt(vehicle.missions_completed),
      missions_completed_7_days: parseInt(vehicle.missions_completed_7_days),
    }));

const addNewVehicle = vehicle => {
  // Add to vehicles
  redis.hmsetAsync(`vehicles:${vehicle.id}`,
    'id', vehicle.id,
    'model', vehicle.model,
    'icon', vehicle.icon,
    'long', vehicle.coords.long,
    'lat', vehicle.coords.lat,
    'rating', vehicle.rating,
    'missions_completed', vehicle.missions_completed,
    'missions_completed_7_days', vehicle.missions_completed_7_days,
    'status', vehicle.status,
  );
  // Set TTL for vehicles
  redis.expire(`vehicles:${vehicle.id}`, config('vehicles_ttl'));
  // Send new vehicle to Captain
  createVehicle(vehicle);
  updateVehiclePosition(vehicle);
};

const getVehicle = async id => {
  redis.expire(`vehicles:${id}`, config('vehicles_ttl'));
  let vehicle = await redis.hgetallAsync(`vehicles:${id}`);
  vehicle.rating = parseInt(vehicle.rating);
  vehicle.coords = { long: vehicle.long, lat: vehicle.lat };
  return vehicle;
};

const getVehicles = async vehicleIds =>
  parseVehiclesArray(
    await Promise.all(vehicleIds.map(vehicleId => getVehicle(vehicleId))),
  );

const updateVehicleStatus = async (id, status) => {
  return await redis.hsetAsync(`vehicles:${id}`, 'status', status);
};

const updateVehiclePosition = async (vehicle, newLong = vehicle.coords.long, newLat = vehicle.coords.lat) => {
  const positionId = await redis.incrAsync('next_position_id');

  redis.geoaddAsync('vehicle_positions', newLong, newLat, vehicle.id);

  redis.hmsetAsync(`vehicle_position_history:${positionId}`,
    'long', newLong,
    'lat', newLat,
    'status', vehicle.status
  );

  redis.zaddAsync(`vehicles:${vehicle.id}:positions`,
    Date.now(), positionId
  )
}

const generateAndAddVehicles = (count, coords, radius) =>
  count > 0 && generateRandomVehicles(count, coords, radius)
    .forEach(vehicle => {
      addNewVehicle(vehicle);
    });

const getVehiclesInRange = async (coords, radius) => {
  const shortRangeRadius = radius / 7;
  const desiredVehicleCountInShortRange = 3;
  const desiredVehicleCountInLongRange = 100;

  // get list of known vehicles in short range
  const vehiclesInShortRange = await redis.georadiusAsync('vehicle_positions', coords.long, coords.lat, shortRangeRadius, 'm');
  // if not enough vehicles in short range generate new ones
  generateAndAddVehicles(desiredVehicleCountInShortRange - vehiclesInShortRange.length, coords, shortRangeRadius);

  // get list of known vehicles in long range
  const vehiclesInLongRange = await redis.georadiusAsync('vehicle_positions', coords.long, coords.lat, radius, 'm');
  // if not enough vehicles in long range generate new ones
  generateAndAddVehicles(desiredVehicleCountInLongRange - vehiclesInLongRange.length, coords, radius);

  // get details for vehicles in range
  return await getVehicles(vehiclesInLongRange);
};

module.exports = {
  getVehiclesInRange,
  getVehicle,
  getVehicles,
  updateVehicleStatus,
};
<commit_msg>chore: moved setting long and lat for vehicle into updateVehiclePosition<commit_after>const redis = require('./redis');
const config = require('../config');
const { generateRandomVehicles } = require('../simulation/vehicles');
const { createVehicle } = require('../client-thrift');

const parseVehiclesArray = vehicles =>
  vehicles
    // filter vehicles
    .filter(vehicle => !!vehicle)
    // format response objects
    .map(vehicle => ({
      id: vehicle.id,
      model: vehicle.model,
      icon: vehicle.icon,
      coords: { lat: parseFloat(vehicle.lat), long: parseFloat(vehicle.long) },
      rating: parseFloat(vehicle.rating),
      missions_completed: parseInt(vehicle.missions_completed),
      missions_completed_7_days: parseInt(vehicle.missions_completed_7_days),
    }));

const addNewVehicle = vehicle => {
  // Add to vehicles
  redis.hmsetAsync(`vehicles:${vehicle.id}`,
    'id', vehicle.id,
    'model', vehicle.model,
    'icon', vehicle.icon,
    'rating', vehicle.rating,
    'missions_completed', vehicle.missions_completed,
    'missions_completed_7_days', vehicle.missions_completed_7_days,
    'status', vehicle.status,
  );

  updateVehiclePosition(vehicle);

  // Set TTL for vehicles
  redis.expire(`vehicles:${vehicle.id}`, config('vehicles_ttl'));
  // Send new vehicle to Captain
  createVehicle(vehicle);
};

const getVehicle = async id => {
  redis.expire(`vehicles:${id}`, config('vehicles_ttl'));
  let vehicle = await redis.hgetallAsync(`vehicles:${id}`);
  vehicle.rating = parseInt(vehicle.rating);
  vehicle.coords = { long: vehicle.long, lat: vehicle.lat };
  return vehicle;
};

const getVehicles = async vehicleIds =>
  parseVehiclesArray(
    await Promise.all(vehicleIds.map(vehicleId => getVehicle(vehicleId))),
  );

const updateVehicleStatus = async (id, status) => {
  return await redis.hsetAsync(`vehicles:${id}`, 'status', status);
};

const updateVehiclePosition = async (vehicle, newLong = vehicle.coords.long, newLat = vehicle.coords.lat) => {
  const positionId = await redis.incrAsync('next_position_id');

  redis.geoaddAsync('vehicle_positions', newLong, newLat, vehicle.id);

  redis.hmsetAsync(`vehicles:${vehicle.id}`,
    'long', newLong,
    'lat', newLat,
  )

  redis.hmsetAsync(`vehicle_position_history:${positionId}`,
    'long', newLong,
    'lat', newLat,
    'status', vehicle.status
  );

  redis.zaddAsync(`vehicles:${vehicle.id}:positions`,
    Date.now(), positionId
  )
}

const generateAndAddVehicles = (count, coords, radius) =>
  count > 0 && generateRandomVehicles(count, coords, radius)
    .forEach(vehicle => {
      addNewVehicle(vehicle);
    });

const getVehiclesInRange = async (coords, radius) => {
  const shortRangeRadius = radius / 7;
  const desiredVehicleCountInShortRange = 3;
  const desiredVehicleCountInLongRange = 100;

  // get list of known vehicles in short range
  const vehiclesInShortRange = await redis.georadiusAsync('vehicle_positions', coords.long, coords.lat, shortRangeRadius, 'm');
  // if not enough vehicles in short range generate new ones
  generateAndAddVehicles(desiredVehicleCountInShortRange - vehiclesInShortRange.length, coords, shortRangeRadius);

  // get list of known vehicles in long range
  const vehiclesInLongRange = await redis.georadiusAsync('vehicle_positions', coords.long, coords.lat, radius, 'm');
  // if not enough vehicles in long range generate new ones
  generateAndAddVehicles(desiredVehicleCountInLongRange - vehiclesInLongRange.length, coords, radius);

  // get details for vehicles in range
  return await getVehicles(vehiclesInLongRange);
};

module.exports = {
  getVehiclesInRange,
  getVehicle,
  getVehicles,
  updateVehicleStatus,
};
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

var devFlagPlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: [
    'webpack-hot-middleware/client',
    './frontend/index'
  ],
  output: {
    path: path.join(__dirname, 'public'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    devFlagPlugin
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel', 'eslint-loader'],
      exclude: [/node_modules/,/scripts/],
      include: __dirname
    }]
  }
  eslint: {
    configFile: './.eslintrc'
  }
};

// When inside Redux repo, prefer src to compiled version.
// You can safely delete these lines in your project.
var reduxSrc = path.join(__dirname, '..', '..', 'src');
var reduxNodeModules = path.join(__dirname, '..', '..', 'node_modules');
var fs = require('fs');
if (fs.existsSync(reduxSrc) && fs.existsSync(reduxNodeModules)) {
  // Resolve Redux to source
  module.exports.resolve = { alias: { 'redux': reduxSrc } };
  // Compile Redux from source
  module.exports.module.loaders.push({
    test: /\.js$/,
    loaders: ['babel'],
    include: reduxSrc
  });
}
<commit_msg>Configure webpack to use custom eslint settings<commit_after>var path = require('path');
var webpack = require('webpack');

var devFlagPlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: [
    'webpack-hot-middleware/client',
    './frontend/index'
  ],
  output: {
    path: path.join(__dirname, 'public'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    devFlagPlugin
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel', 'eslint-loader'],
      exclude: [/node_modules/,/scripts/],
      include: __dirname
    }],
    eslint: {
      configFile: './.eslintrc'
    }
  }
};

// When inside Redux repo, prefer src to compiled version.
// You can safely delete these lines in your project.
var reduxSrc = path.join(__dirname, '..', '..', 'src');
var reduxNodeModules = path.join(__dirname, '..', '..', 'node_modules');
var fs = require('fs');
if (fs.existsSync(reduxSrc) && fs.existsSync(reduxNodeModules)) {
  // Resolve Redux to source
  module.exports.resolve = { alias: { 'redux': reduxSrc } };
  // Compile Redux from source
  module.exports.module.loaders.push({
    test: /\.js$/,
    loaders: ['babel'],
    include: reduxSrc
  });
}
<|endoftext|>


<commit_before>/*
  Beschrijving
*/

//De functie returnd een object ()
function graphService(){

  return {

    voorbeeldRoute(req, res){
      /*
      req is de request, https://expressjs.com/en/4x/api.html#req
      res is de response, https://expressjs.com/en/4x/api.html#res

      Als je naar 'https://localhost:3000/services/graphservice/voorbeeld?vraag=WhyHelloDidntSeeYouThere'
      Zou dit je response moeten zijn:
      > WhyHelloDidntSeeYouThere
      */

      const vraag = req.query.vraag;
      res.send(vraag);

    }
  };
}

module.exports = graphService();
<commit_msg>Added functionality for graph service, not working since there is no local way of checking the data<commit_after>/*
  Beschrijving
*/

//De functie returnd een object ()

function getData(){
      //use the find() API and pass an empty query object to retrieve all records
      dbObject.collection("collectioName").find({}).toArray(function(err, docs){
        if ( err ) throw err;
        var dateArray = [];
        var amountPerDayArray = [];
        var hardwareIDArray = [];

        for ( index in docs){
          var doc = docs[index];
          //category array
          var date = doc['date'];
          //series 1 values array
          var activated = doc['activated'];
          //series 2 values array
          var hardwareId = doc['hardwareId'];
          dateArray.push({"label": date});
          amountPerDayArray.push({"value" : activated});
          hardwareIDArray.push({"value" : hardwareId});
        }

        var dataset = [
          {
            "seriesname" : "AmountperDay",
            "data" : amountPerDayArray
          },
          {
            "seriesname" : "HardwareID",
            "data": dateArray
          }
        ];

        var response = {
          "dataset" : dataset,
          "categories" : dateArray
        };
      });
    }

// ajax method to get the data
var chartData;
$(function(){
  $.AJAX({
    url: 'http://localhost:3300/fuelPrices',
    type: 'GET',
    success : function(data) {
      chartData = data;
      console.log(data);
    }
  });
});

//BUILDING THE GRAPH ITSELF

var chartProperties = {
    "caption": "Variation of activations of the lamp",
    "numberprefix": "Rs",
    "xAxisName": "Dates",
    "yAxisName": "Amount of activations "
};

var categoriesArray = [{
      "category" : data["categories"]
}];

var lineChart = new FusionCharts({
    type: 'msline',
    renderAt: 'chart-location',
    width: '100Ω',
    height: '600',
    dataFormat: 'json',
    dataSource: {
        chart: chartProperties,
        categories : categoriesArray,
        dataset : data["dataset"]
    }
});

lineChart.render();

function graphService(){

  return {

    voorbeeldRoute(req, res){
      /*
      req is de request, https://expressjs.com/en/4x/api.html#req
      res is de response, https://expressjs.com/en/4x/api.html#res

      Als je naar 'https://localhost:3000/services/graphservice/voorbeeld?vraag=WhyHelloDidntSeeYouThere'
      Zou dit je response moeten zijn:
      > WhyHelloDidntSeeYouThere
      */

      const vraag = req.query.vraag;
      res.send(vraag);

    }
  };
}

module.exports = graphService();
<|endoftext|>


<commit_before>var contentType = require('node-lib').content_type.ext;
var VirusScan = require('../../utils/VirusScan');
var FSService = require('../local/FSService');

var async = require('async');
var AWS = require('aws-sdk');
var uuid = require('node-uuid');
var fs = require('fs');
var tmp = require('tmp');
var knox = require('knox');

var writeQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var readQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var removeQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var deleteQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

function S3Service(config) {
	this.getConfig = function () {
		return config;
	};
	AWS.config.update({
		region: config.region,
		accessKeyId: config.accessKeyId,
		secretAccessKey: config.secretAccessKey
	});
	var s3 = new AWS.S3();
	this.getS3 = function () {
		return s3;
	};
}

S3Service.prototype.addToWriteQueue = function (params, callback) {
	var self = this;
	writeQueue.push(function (callback) {
		self.getS3().client.putObject(params, callback);
	}, callback);
};

S3Service.prototype.addToReadQueue = function (params, callback) {
	var self = this;
	readQueue.push(function (callback) {
		self.getS3().client.getObject(params, callback);
	}, callback);
};

S3Service.prototype.addToRemoveQueue = function (params, callback) {
	var self = this;
	removeQueue.push(function (callback) {
		self.getS3().client.deleteObject(params, callback);
	}, callback);
};

S3Service.prototype.addToDeleteQueue = function (params, callback) {
	var self = this;
	deleteQueue.push(function (callback) {
		self.getS3().client.deleteObjects(params, callback);
	}, callback);
};

S3Service.prototype.bucketInitialization = function () {
	var self = this;
	var bucketNames = [];
	if (this.getConfig().s3BucketName) bucketNames.push(this.getConfig().s3BucketName);
	if (this.getConfig().s3SecuredBucketName) bucketNames.push(this.getConfig().s3SecuredBucketName);

	this.getS3().client.listBuckets(function (err, data) {
		if (err) {
			console.log(err);
		} else {
			bucketNames.forEach(function (bucketName) {
				if (data.Buckets.filter(function (bucket) {
					return bucket.Name == bucketName;
				}).length === 0) {
					self.getS3().client.createBucket({
						Bucket: bucketName
					}, function (err, data) {
						if (err) {
							console.log(err);
						} else console.log("Successfully created S3 " + bucketName + " bucket");
					});
				} else {
					console.log("S3 bucket " + bucketName + " connected...");
				}
			});
		}
	});
};

S3Service.prototype.writeFileToS3 = function (base64data, originalFilename, extension, secure, callback) {
	var name = uuid.v4();
	var filename = extension ? name + "." + extension : name;

	console.log("write file to S3: ", originalFilename);

	this.addToWriteQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Key: filename,
		Body: new Buffer(base64data, 'base64'),
		ContentType: contentType.getContentType(extension),
		ContentDisposition: 'attachment; filename="' + originalFilename || filename + '"'
	}, function (err) {
		callback(err, filename);
	});
};

S3Service.prototype.readFileFromS3 = function (filename, secureOrBucket, callback) {
	var bucket;
	if (typeof secureOrBucket == "string") {
		bucket = secureOrBucket;
	} else {
		bucket = secureOrBucket ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName;
	}

	this.addToReadQueue({
		Bucket: bucket,
		Key: filename
	}, callback);
};

S3Service.prototype.removeFileFromS3 = function (filename, secure, callback) {
	this.addToRemoveQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Key: filename
	}, callback);
};

S3Service.prototype.removeFilesFromS3 = function (filenames, secure, callback) {
	if (!filenames.length) {
		return callback(new Error('NO_FILENAMES'));
	}
	var objects = [];
	filenames.forEach(function (filename) {
		objects.push({
			Key: filename
		});
	});

	this.addToDeleteQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Delete: {
			Objects: objects
		}
	}, callback);
};

S3Service.prototype.uploadFileOnS3 = function (filepath, originalFilename, extension, secure, callback) {
	var self = this;
	//Scan for viruses
	VirusScan.launchFileScan(filepath, function (err, msg) {
		if (err) {
			//An error occured (might be a virus)
			console.log(msg);
			fs.unlink(filepath);
			return callback(err);
		}
		//No virus detected
		fs.readFile(filepath, function (err, data) {
			if (err) {
				return callback(err);
			}
			self.writeFileToS3(new Buffer(data, 'binary').toString('base64'), originalFilename, extension, secure, function (err, filename) {
				if (err) {
					return callback(err);
				}

				callback(err, self.getConfig().s3StaticURL + "/" + secure ? self.getConfig().s3SecuredBucketName : self.getConfig().s3BucketName + "/" + filename);
			});
		});
	});
};

S3Service.prototype.uploadThenDeleteLocalFile = function (filepath, originalFilename, extension, secure, callback) {
	var self = this;
	//Scan for viruses
	VirusScan.launchFileScan(filepath, function (err, msg) {
		if (err) {
			//An error occured (might be a virus)
			console.log(msg);
			fs.unlink(filepath);
			return callback(err);
		}
		//No virus detected
		FSService.readThenDeleteLocalFile(filepath, function (err, data) {
			if (err) {
				return callback(err);
			}
			self.writeFileToS3(new Buffer(data, 'binary').toString('base64'), originalFilename, extension, secure, function (err, filename) {
				if (err) {
					return callback(err);
				}

				callback(err, self.getConfig().s3StaticURL + "/" + (secure ? self.getConfig().s3SecuredBucketName : self.getConfig().s3BucketName) + "/" + filename);
			});
		});
	});
};

S3Service.prototype.getFileFromS3AndWriteItToFileSystem = function (filename, bucket, callback) {
	this.readFileFromS3(filename, bucket, function (err, data) {
		if (err) {
			return callback(err);
		}

		FSService.writeDataToFileSystem(filename, data.Body, function (err, filepath) {
			if (err) {
				return callback(err);
			}

			console.log("filepath", filepath);

			VirusScan.launchFileScan(filepath, function (err, msg) {
				if (err) {
					//An error occured (might be a virus)
					console.log(msg);
					// TODO delete file on S3
					fs.unlink(filepath);
					return callback(err);
				}

				callback(null, filepath);
			});
		});
	});
};

S3Service.prototype.getSecuredFilepath = function (key, filename) {
	// var params = {
	// 	Bucket: this.getConfig().s3SecuredBucketName,
	// 	Key: key,
	// 	Expires: 30
	// };
	// return this.getS3().client.getSignedUrl('getObject', params);

	var s3Client = knox.createClient({
		key: this.getConfig().accessKeyId,
		secret: this.getConfig().secretAccessKey,
		bucket: this.getConfig().s3SecuredBucketName
	});

	var expires = new Date();
	expires.setMinutes(expires.getMinutes() + 30);

	if (filename) {
		var headers = { 'response-content-disposition': 'attachment;filename="' + filename + '"'};
		return s3Client.signedUrl(key, expires, { qs: headers });
	}

	return s3Client.signedUrl(key, expires);
};

module.exports = S3Service;<commit_msg>bug syntax js<commit_after>var contentType = require('node-lib').content_type.ext;
var VirusScan = require('../../utils/VirusScan');
var FSService = require('../local/FSService');

var async = require('async');
var AWS = require('aws-sdk');
var uuid = require('node-uuid');
var fs = require('fs');
var tmp = require('tmp');
var knox = require('knox');

var writeQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var readQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var removeQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

var deleteQueue = async.queue(function (task, callback) {
	task(callback);
}, 3);

function S3Service(config) {
	this.getConfig = function () {
		return config;
	};
	AWS.config.update({
		region: config.region,
		accessKeyId: config.accessKeyId,
		secretAccessKey: config.secretAccessKey
	});
	var s3 = new AWS.S3();
	this.getS3 = function () {
		return s3;
	};
}

S3Service.prototype.addToWriteQueue = function (params, callback) {
	var self = this;
	writeQueue.push(function (callback) {
		self.getS3().client.putObject(params, callback);
	}, callback);
};

S3Service.prototype.addToReadQueue = function (params, callback) {
	var self = this;
	readQueue.push(function (callback) {
		self.getS3().client.getObject(params, callback);
	}, callback);
};

S3Service.prototype.addToRemoveQueue = function (params, callback) {
	var self = this;
	removeQueue.push(function (callback) {
		self.getS3().client.deleteObject(params, callback);
	}, callback);
};

S3Service.prototype.addToDeleteQueue = function (params, callback) {
	var self = this;
	deleteQueue.push(function (callback) {
		self.getS3().client.deleteObjects(params, callback);
	}, callback);
};

S3Service.prototype.bucketInitialization = function () {
	var self = this;
	var bucketNames = [];
	if (this.getConfig().s3BucketName) bucketNames.push(this.getConfig().s3BucketName);
	if (this.getConfig().s3SecuredBucketName) bucketNames.push(this.getConfig().s3SecuredBucketName);

	this.getS3().client.listBuckets(function (err, data) {
		if (err) {
			console.log(err);
		} else {
			bucketNames.forEach(function (bucketName) {
				if (data.Buckets.filter(function (bucket) {
					return bucket.Name == bucketName;
				}).length === 0) {
					self.getS3().client.createBucket({
						Bucket: bucketName
					}, function (err, data) {
						if (err) {
							console.log(err);
						} else console.log("Successfully created S3 " + bucketName + " bucket");
					});
				} else {
					console.log("S3 bucket " + bucketName + " connected...");
				}
			});
		}
	});
};

S3Service.prototype.writeFileToS3 = function (base64data, originalFilename, extension, secure, callback) {
	var name = uuid.v4();
	var filename = extension ? name + "." + extension : name;

	console.log("write file to S3: ", originalFilename);

	this.addToWriteQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Key: filename,
		Body: new Buffer(base64data, 'base64'),
		ContentType: contentType.getContentType(extension),
		ContentDisposition: 'attachment; filename="' + originalFilename || filename + '"'
	}, function (err) {
		callback(err, filename);
	});
};

S3Service.prototype.readFileFromS3 = function (filename, secureOrBucket, callback) {
	var bucket;
	if (typeof secureOrBucket == "string") {
		bucket = secureOrBucket;
	} else {
		bucket = secureOrBucket ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName;
	}

	this.addToReadQueue({
		Bucket: bucket,
		Key: filename
	}, callback);
};

S3Service.prototype.removeFileFromS3 = function (filename, secure, callback) {
	this.addToRemoveQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Key: filename
	}, callback);
};

S3Service.prototype.removeFilesFromS3 = function (filenames, secure, callback) {
	if (!filenames.length) {
		return callback(new Error('NO_FILENAMES'));
	}
	var objects = [];
	filenames.forEach(function (filename) {
		objects.push({
			Key: filename
		});
	});

	this.addToDeleteQueue({
		Bucket: secure ? this.getConfig().s3SecuredBucketName : this.getConfig().s3BucketName,
		Delete: {
			Objects: objects
		}
	}, callback);
};

S3Service.prototype.uploadFileOnS3 = function (filepath, originalFilename, extension, secure, callback) {
	var self = this;
	//Scan for viruses
	VirusScan.launchFileScan(filepath, function (err, msg) {
		if (err) {
			//An error occured (might be a virus)
			console.log(msg);
			fs.unlink(filepath);
			return callback(err);
		}
		//No virus detected
		fs.readFile(filepath, function (err, data) {
			if (err) {
				return callback(err);
			}
			self.writeFileToS3(new Buffer(data, 'binary').toString('base64'), originalFilename, extension, secure, function (err, filename) {
				if (err) {
					return callback(err);
				}

				callback(err, self.getConfig().s3StaticURL + "/" + (secure ? self.getConfig().s3SecuredBucketName : self.getConfig().s3BucketName) + "/" + filename);
			});
		});
	});
};

S3Service.prototype.uploadThenDeleteLocalFile = function (filepath, originalFilename, extension, secure, callback) {
	var self = this;
	//Scan for viruses
	VirusScan.launchFileScan(filepath, function (err, msg) {
		if (err) {
			//An error occured (might be a virus)
			console.log(msg);
			fs.unlink(filepath);
			return callback(err);
		}
		//No virus detected
		FSService.readThenDeleteLocalFile(filepath, function (err, data) {
			if (err) {
				return callback(err);
			}
			self.writeFileToS3(new Buffer(data, 'binary').toString('base64'), originalFilename, extension, secure, function (err, filename) {
				if (err) {
					return callback(err);
				}

				callback(err, self.getConfig().s3StaticURL + "/" + (secure ? self.getConfig().s3SecuredBucketName : self.getConfig().s3BucketName) + "/" + filename);
			});
		});
	});
};

S3Service.prototype.getFileFromS3AndWriteItToFileSystem = function (filename, bucket, callback) {
	this.readFileFromS3(filename, bucket, function (err, data) {
		if (err) {
			return callback(err);
		}

		FSService.writeDataToFileSystem(filename, data.Body, function (err, filepath) {
			if (err) {
				return callback(err);
			}

			console.log("filepath", filepath);

			VirusScan.launchFileScan(filepath, function (err, msg) {
				if (err) {
					//An error occured (might be a virus)
					console.log(msg);
					// TODO delete file on S3
					fs.unlink(filepath);
					return callback(err);
				}

				callback(null, filepath);
			});
		});
	});
};

S3Service.prototype.getSecuredFilepath = function (key, filename) {
	// var params = {
	// 	Bucket: this.getConfig().s3SecuredBucketName,
	// 	Key: key,
	// 	Expires: 30
	// };
	// return this.getS3().client.getSignedUrl('getObject', params);

	var s3Client = knox.createClient({
		key: this.getConfig().accessKeyId,
		secret: this.getConfig().secretAccessKey,
		bucket: this.getConfig().s3SecuredBucketName
	});

	var expires = new Date();
	expires.setMinutes(expires.getMinutes() + 30);

	if (filename) {
		var headers = { 'response-content-disposition': 'attachment;filename="' + filename + '"'};
		return s3Client.signedUrl(key, expires, { qs: headers });
	}

	return s3Client.signedUrl(key, expires);
};

module.exports = S3Service;<|endoftext|>


<commit_before>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// loggout.js
//
// function to log out the user
//
// N.B.: The extra "g" in "loggout" is just for kicks.
//
"use strict";

define([
    'jquery',
    'app/lib',
    'ajax',
    'cf',
    'current-user',
    'html',
    'lib',
], function (
    $,
    appLib,
    ajax,
    cf,
    currentUser,
    html,
    coreLib,
) {

    return function () {

        var logoutPostAJAX = function (msg) {
                currentUser('obj', null);
                currentUser('priv', null);
                $('#userbox').html(appLib.fillUserBox()); // reset userbox
                $('#mainarea').html(html.logout());
                // automated page reloads are a source of woe
                // if (! cf('testing')) {
                //     setTimeout(function() {
                //         location.reload();
                //     }, 2000);
                // }
            },
            rest = {
                method: 'LOGIN',
                path: 'logout',
                body: null
            },
            sc = function (st) {
                if (! msg) {
                    msg = st.text;
                }
                logoutPostAJAX(msg);
            },
            fc = function (st) {
                logoutPostAJAX(st.text);
            };
        ajax(rest, sc, fc);

    };

});
<commit_msg>js: core/loggout: propagate result line message<commit_after>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// loggout.js
//
// function to log out the user
//
// N.B.: The extra "g" in "loggout" is just for kicks.
//
"use strict";

define([
    'jquery',
    'app/lib',
    'ajax',
    'cf',
    'current-user',
    'html',
    'lib',
], function (
    $,
    appLib,
    ajax,
    cf,
    currentUser,
    html,
    coreLib,
) {

    return function (msg) {

        var logoutPostAJAX = function (msg) {
                currentUser('obj', null);
                currentUser('priv', null);
                $('#userbox').html(appLib.fillUserBox()); // reset userbox
                $('#mainarea').html(html.logout());
                // automated page reloads are a source of woe
                // if (! cf('testing')) {
                //     setTimeout(function() {
                //         location.reload();
                //     }, 2000);
                // }
            },
            rest = {
                method: 'LOGIN',
                path: 'logout',
                body: null
            },
            sc = function (st) {
                if (! msg) {
                    msg = st.text;
                }
                logoutPostAJAX(msg);
            },
            fc = function (st) {
                logoutPostAJAX(st.text);
            };
        ajax(rest, sc, fc);

    };

});
<|endoftext|>


<commit_before>/*global rendr*/
var format = require('util').format
  , cachedTemplates = {};

module.exports = function(Handlebars) {

  /**
   * Provide a way for apps to specify that different template name patterns
   * should use different compiled template files.
   *
   * The default pattern `/.+/` is very greedy; it matches anything, including nested paths.
   * To add rules that should match before this default rule, `unshift` them from this array.
   */
  var templatePatterns = [{
    pattern: /.+/,
    src: rendr.entryPath + '/app/templates/compiledTemplates'
  }];

  /**
   * Given a template name, return the compiled Handlebars template.
   */
  function getTemplate(templateName) {
    /**
     * Find the correct source file for this template.
     */
    var src = getSrcForTemplate(templateName);

    /**
     * Allow compiledTemplates to be created asynchronously.
     */
    cachedTemplates[src] = cachedTemplates[src] || require(src)(Handlebars);
    return cachedTemplates[src][templateName];
  }

  /**
   * For a given template name, find the correct compiled templates source file
   * based on pattern matching on the template name.
   */
  function getSrcForTemplate(templateName) {
    var currentPattern = templatePatterns.filter(function(obj) {
      return obj.pattern.test(templateName);
    })[0];

    if (currentPattern == null) {
      throw new Error(format('No pattern found to match template "%s".', templateName))
    }

    return currentPattern.src;
  }

  return {
    getTemplate: getTemplate,
    getSrcForTemplate: getSrcForTemplate,
    templatePatterns: templatePatterns
  }
};
<commit_msg>Can't use 'util' module in shared environment<commit_after>/*global rendr*/
var cachedTemplates = {};

module.exports = function(Handlebars) {

  /**
   * Provide a way for apps to specify that different template name patterns
   * should use different compiled template files.
   *
   * The default pattern `/.+/` is very greedy; it matches anything, including nested paths.
   * To add rules that should match before this default rule, `unshift` them from this array.
   */
  var templatePatterns = [{
    pattern: /.+/,
    src: rendr.entryPath + '/app/templates/compiledTemplates'
  }];

  /**
   * Given a template name, return the compiled Handlebars template.
   */
  function getTemplate(templateName) {
    /**
     * Find the correct source file for this template.
     */
    var src = getSrcForTemplate(templateName);

    /**
     * Allow compiledTemplates to be created asynchronously.
     */
    cachedTemplates[src] = cachedTemplates[src] || require(src)(Handlebars);
    return cachedTemplates[src][templateName];
  }

  /**
   * For a given template name, find the correct compiled templates source file
   * based on pattern matching on the template name.
   */
  function getSrcForTemplate(templateName) {
    var currentPattern = templatePatterns.filter(function(obj) {
      return obj.pattern.test(templateName);
    })[0];

    if (currentPattern == null) {
      throw new Error('No pattern found to match template "' + templateName + '".');
    }

    return currentPattern.src;
  }

  return {
    getTemplate: getTemplate,
    getSrcForTemplate: getSrcForTemplate,
    templatePatterns: templatePatterns
  }
};
<|endoftext|>