'use strict'; module.exports = function (myDeps, conf, overrideHttp) { var express = require('express'); //var finalhandler = require('finalhandler'); var serveStatic = require('serve-static'); var serveIndex = require('serve-index'); //var assetServer = serveStatic(opts.assetsPath); var path = require('path'); //var wellKnownServer = serveStatic(path.join(opts.assetsPath, 'well-known')); var serveStaticMap = {}; var serveIndexMap = {}; var content = conf.content; //var server; var serveInit; var app; var tun; var request; /* function _reloadWrite(data, enc, cb) { // /*jshint validthis: true */ /* if (this.headersSent) { this.__write(data, enc, cb); return; } if (!/html/i.test(this.getHeader('Content-Type'))) { this.__write(data, enc, cb); return; } if (this.getHeader('Content-Length')) { this.setHeader('Content-Length', this.getHeader('Content-Length') + this.__my_addLen); } this.__write(this.__my_livereload); this.__write(data, enc, cb); } */ function createServeInit() { var PromiseA = require('bluebird'); var stunnel = require('stunnel'); var OAUTH3 = require('../packages/assets/org.oauth3'); require('../packages/assets/org.oauth3/oauth3.domains.js'); require('../packages/assets/org.oauth3/oauth3.dns.js'); require('../packages/assets/org.oauth3/oauth3.tunnel.js'); OAUTH3._hooks = require('../packages/assets/org.oauth3/oauth3.node.storage.js'); var fs = PromiseA.promisifyAll(require('fs')); var ownersPath = path.join(__dirname, '..', 'var', 'owners.json'); var scmp = require('scmp'); request = request || PromiseA.promisify(require('request')); var owners = { all: function () { var owners; try { owners = require(ownersPath); } catch(e) { owners = {}; } return PromiseA.resolve(Object.keys(owners).map(function (key) { var owner = owners[key]; owner.id = key; return owner; })); } , get: function (id) { var me = this; return me.all().then(function (owners) { return owners.filter(function (owner) { return scmp(id, owner.id); })[0]; }); } , exists: function (id) { var me = this; return me.get(id).then(function (owner) { return !!owner; }); } , set: function (id, obj) { var owners; try { owners = require(ownersPath); } catch(e) { owners = {}; } obj.id = id; owners[id] = obj; return fs.writeFileAsync(ownersPath, JSON.stringify(owners), 'utf8'); } }; myDeps.PromiseA = PromiseA; myDeps.OAUTH3 = OAUTH3; myDeps.storage = { owners: owners }; myDeps.recase = require('recase').create({}); myDeps.request = request; myDeps.api = { // TODO move loopback to oauth3.api('tunnel:loopback') loopback: function (deps, session, opts2) { var crypto = require('crypto'); var token = crypto.randomBytes(16).toString('hex'); var keyAuthorization = crypto.randomBytes(16).toString('hex'); var nonce = crypto.randomBytes(16).toString('hex'); // TODO set token and keyAuthorization to /.well-known/cloud-challenge/:token return request({ method: 'POST' , url: 'https://oauth3.org/api/org.oauth3.tunnel/loopback' , json: { address: opts2.address , port: opts2.port , token: token , keyAuthorization: keyAuthorization , servername: opts2.servername , nonce: nonce , scheme: 'https' , iat: Date.now() } }).then(function (result) { // TODO this will always fail at the moment console.log('loopback result:'); return result; }); } , tunnel: function (deps, session) { // TODO save session to config and turn tunnel on var OAUTH3 = deps.OAUTH3; var url = require('url'); var providerUri = session.token.aud; var urlObj = url.parse(OAUTH3.url.normalize(session.token.azp)); var oauth3 = OAUTH3.create(urlObj, { providerUri: providerUri , session: session }); //var crypto = require('crypto'); //var id = crypto.createHash('sha256').update(session.token.sub).digest('hex'); return oauth3.setProvider(providerUri).then(function () { /* return oauth3.api('domains.list').then(function (domains) { var domainsMap = {}; domains.forEach(function (d) { if (!d.device) { return; } if (d.device !== conf.device.hostname) { return; } domainsMap[d.name] = true; }); */ //console.log('domains matching hostname', Object.keys(domainsMap)); //console.log('device', conf.device); return oauth3.api('tunnel.token', { data: { // filter to all domains that are on this device //domains: Object.keys(domainsMap) device: { hostname: conf.device.hostname , id: conf.device.uid || conf.device.id } } }).then(function (result) { console.log('got a token from the tunnel server?'); console.log(result); if (!result.tunnelUrl) { result.tunnelUrl = ('wss://' + (new Buffer(result.jwt.split('.')[1], 'base64').toString('ascii')).aud + '/'); } var services = { https: { '*': 443 }, http: { '*': 80 }, smtp: { '*': 25}, smtps: { '*': 587 /*also 465/starttls*/ } /*, ssh: { '*': 22 }*/ }; /* console.log('blah'); console.log(result.jwt); console.log(result.tunnelUrl); console.log(services); console.log('deps.tunnel'); console.log(deps.tunnel); console.log('deps.tunnel.net'); console.log(deps.tunnel.net.toString()); console.log('deps.net'); console.log(deps.net); */ var opts3 = { token: result.jwt , stunneld: result.tunnelUrl // we'll provide faux networking and pipe as we please , services: services , net: myDeps.tunnel.net }; console.log('blah 2'); if (tun) { console.log('balh 3'); if (tun.append) { tun.append(result.jwt); } else if (tun.end) { tun.end(); tun = null; } } console.log('might have tunnel?'); if (!tun) { console.log('connecting to the tunnel'); tun = stunnel.connect(opts3); conf.tun = true; } }); /* }); */ }); //, { token: token, refresh: refresh }); } }; return require('../packages/apis/com.daplie.goldilocks').create(myDeps, conf); } app = express(); var Sites = { add: function (sitesMap, site) { if (!sitesMap[site.$id]) { sitesMap[site.$id] = site; } if (!site.paths) { site.paths = []; } if (!site.paths._map) { site.paths._map = {}; } site.paths.forEach(function (path) { site.paths._map[path.$id] = path; if (!path.modules) { path.modules = []; } if (!path.modules._map) { path.modules._map = {}; } path.modules.forEach(function (module) { path.modules._map[module.$id] = module; }); }); } }; var opts = overrideHttp || conf.http; if (!opts.defaults) { opts.defaults = {}; } if (!opts.global) { opts.global = {}; } if (!opts.sites) { opts.sites = []; } opts.sites._map = {}; opts.sites.forEach(function (site) { Sites.add(opts.sites._map, site); }); function mapMap(el, i, arr) { arr._map[el.$id] = el; } opts.global.modules._map = {}; opts.global.modules.forEach(mapMap); opts.global.paths._map = {}; opts.global.paths.forEach(function (path, i, arr) { mapMap(path, i, arr); //opts.global.paths._map[path.$id] = path; path.modules._map = {}; path.modules.forEach(mapMap); }); opts.sites.forEach(function (site) { site.paths._map = {}; site.paths.forEach(function (path, i, arr) { mapMap(path, i, arr); //site.paths._map[path.$id] = path; path.modules._map = {}; path.modules.forEach(mapMap); }); }); opts.defaults.modules._map = {}; opts.defaults.modules.forEach(mapMap); opts.defaults.paths._map = {}; opts.defaults.paths.forEach(function (path, i, arr) { mapMap(path, i, arr); //opts.global.paths._map[path.$id] = path; path.modules._map = {}; path.modules.forEach(mapMap); }); return app.use('/', function (req, res, next) { if (!req.headers.host) { next(new Error('missing HTTP Host header')); return; } if (0 === req.url.indexOf('/api/com.daplie.goldilocks/')) { if (!serveInit) { serveInit = createServeInit(); } } if ('/api/com.daplie.goldilocks/init' === req.url) { serveInit.init(req, res); return; } if ('/api/com.daplie.goldilocks/tunnel' === req.url) { serveInit.tunnel(req, res); return; } if ('/api/com.daplie.goldilocks/config' === req.url) { serveInit.config(req, res); return; } if ('/api/com.daplie.goldilocks/request' === req.url) { serveInit.request(req, res); return; } if (content && '/' === req.url) { // res.setHeader('Content-Type', 'application/octet-stream'); res.end(content); return; } //var done = finalhandler(req, res); var host = req.headers.host; var hostname = (host||'').split(':')[0].toLowerCase(); console.log('opts.global', opts.global); var sites = [ opts.global || null, opts.sites._map[hostname] || null, opts.defaults || null ]; var loadables = { serve: function (config, hostname, pathname, req, res, next) { var originalUrl = req.url; var dirpaths = config.paths.slice(0); function nextServe() { var dirname = dirpaths.pop(); if (!dirname) { req.url = originalUrl; next(); return; } console.log('[serve]', req.url, hostname, pathname, dirname); dirname = path.resolve(conf.cwd, dirname.replace(/:hostname/, hostname)); if (!serveStaticMap[dirname]) { serveStaticMap[dirname] = serveStatic(dirname); } serveStaticMap[dirname](req, res, nextServe); } req.url = req.url.substr(pathname.length - 1); nextServe(); } , indexes: function (config, hostname, pathname, req, res, next) { var originalUrl = req.url; var dirpaths = config.paths.slice(0); function nextIndex() { var dirname = dirpaths.pop(); if (!dirname) { req.url = originalUrl; next(); return; } console.log('[indexes]', req.url, hostname, pathname, dirname); dirname = path.resolve(conf.cwd, dirname.replace(/:hostname/, hostname)); if (!serveStaticMap[dirname]) { serveIndexMap[dirname] = serveIndex(dirname); } serveIndexMap[dirname](req, res, nextIndex); } req.url = req.url.substr(pathname.length - 1); nextIndex(); } , app: function (config, hostname, pathname, req, res, next) { //var appfile = path.resolve(/*process.cwd(), */config.path.replace(/:hostname/, hostname)); var appfile = config.path.replace(/:hostname/, hostname); var app = require(appfile); app(req, res, next); } }; function runModule(module, hostname, pathname, modulename, req, res, next) { if (!loadables[modulename]) { next(new Error("no module '" + modulename + "' found")); return; } loadables[modulename](module, hostname, pathname, req, res, next); } function iterModules(modules, hostname, pathname, req, res, next) { console.log('modules'); console.log(modules); var modulenames = Object.keys(modules._map); function nextModule() { var modulename = modulenames.pop(); if (!modulename) { next(); return; } console.log('modules', modules); runModule(modules._map[modulename], hostname, pathname, modulename, req, res, nextModule); } nextModule(); } function iterPaths(site, hostname, req, res, next) { console.log('site', hostname); console.log(site); var pathnames = Object.keys(site.paths._map); console.log('pathnames', pathnames); pathnames = pathnames.filter(function (pathname) { // TODO ensure that pathname has trailing / return (0 === req.url.indexOf(pathname)); //return req.url.match(pathname); }); pathnames.sort(function (a, b) { return b.length - a.length; }); console.log('pathnames', pathnames); function nextPath() { var pathname = pathnames.shift(); if (!pathname) { next(); return; } console.log('iterPaths', hostname, pathname, req.url); iterModules(site.paths._map[pathname].modules, hostname, pathname, req, res, nextPath); } nextPath(); } function nextSite() { console.log('hostname', hostname, sites); var site; if (!sites.length) { next(); // 404 return; } site = sites.shift(); if (!site) { nextSite(); return; } iterPaths(site, hostname, req, res, nextSite); } nextSite(); /* function serveStaticly(server) { function serveTheStatic() { server.serve(req, res, function (err) { if (err) { return done(err); } server.index(req, res, function (err) { if (err) { return done(err); } req.url = req.url.replace(/\/assets/, ''); assetServer(req, res, function () { if (err) { return done(err); } req.url = req.url.replace(/\/\.well-known/, ''); wellKnownServer(req, res, done); }); }); }); } if (server.expressApp) { server.expressApp(req, res, serveTheStatic); return; } serveTheStatic(); } if (opts.livereload) { res.__my_livereload = ''; res.__my_addLen = res.__my_livereload.length; // TODO modify prototype instead of each instance? res.__write = res.write; res.write = _reloadWrite; } console.log('hostname:', hostname, opts.sites[0].paths); addServer(hostname); server = hostsMap[hostname] || hostsMap[opts.sites[0].name]; serveStaticly(server); */ }); };