<commit_before>'use strict';

const {parse, format} = require('url');
const querystring = require('querystring');
const crypto = require('crypto');

const uuid = require('uuid');
const httpx = require('httpx');

const ua = require('./ua');
const Base = require('./base');

/**
 * API Gateway Client
 */
class Client extends Base {
  constructor(key, secret, stage) {
    super();
    this.appKey = key;
    this.appSecret = Buffer.from(secret, 'utf8');
    this.stage = stage || 'RELEASE';
  }

  buildSignToString(method, headers, signedHeadersStr, url) {
    // accept, contentMD5, contentType,
    const lf = '\n';
    var list = [method, lf];

    var accept = headers['accept'];
    if (accept) {
      list.push(accept);
    }
    list.push(lf);

    var contentMD5 = headers['content-md5'];
    if (contentMD5) {
      list.push(contentMD5);
    }
    list.push(lf);

    var contentType = headers['content-type'];
    if (contentType) {
      list.push(contentType);
    }
    list.push(lf);

    var date = headers['date'];
    if (date) {
      list.push(date);
    }
    list.push(lf);

    if (signedHeadersStr) {
      list.push(signedHeadersStr);
      list.push(lf);
    }

    list.push(this.buildUrl(url));

    return list.join('');
  }

  sign(stringToSign) {
    return crypto.createHmac('sha256', this.appSecret)
      .update(stringToSign, 'utf8').digest('base64');
  }

  md5(content) {
    return crypto.createHash('md5')
      .update(content, 'utf8')
      .digest('base64');
  }

  getSignHeaders(headers) {
    var keys = Object.keys(headers).sort();
    var signKeys = [];
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      if (key.toLowerCase().startsWith('x-ca-')) {
        signKeys.push(key);
      }
    }

    return signKeys;
  }

  buildUrl(parsedUrl, query) {
    var toStringify = Object.assign({}, parsedUrl.query, query);
    var result = parsedUrl.pathname;
    if (Object.keys(toStringify).length) {
      result += '?' + querystring.stringify(toStringify);
    }
    return result;
  }

  buildHeaders(headers) {
    var now = new Date();

    var keys = Object.keys(headers || {});
    var lowered = {};
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      lowered[key.toLowerCase()] = headers[key];
    }

    return Object.assign({
      'x-ca-timestamp': now.getTime(),
      'x-ca-key': this.appKey,
      'x-ca-nonce': uuid.v4(),
      'x-ca-stage': this.stage,
      'date': now.toUTCString(),
      'content-type': 'application/json',
      'accept': 'application/json'
    }, lowered);
  }

  getSignedHeadersString(signHeaders, headers) {
    var list = [];
    for (var i = 0; i < signHeaders.length; i++) {
      var key = signHeaders[i];
      list.push(key + ':' + headers[key]);
    }

    return list.join('\n');
  }

  * request(method, url, opts) {
    var headers = this.buildHeaders(opts.headers);

    if (method === 'POST' && headers['content-type'] === 'application/octet-stream') {
      headers['content-md5'] = this.md5(opts.data);
    }

    var signHeaders = this.getSignHeaders(headers);
    headers['x-ca-signature-headers'] = signHeaders.join(',');
    var signedHeadersStr = this.getSignedHeadersString(signHeaders, headers);

    var parsedUrl = parse(url, true);
    var signToString = this.buildSignToString(method, headers, signedHeadersStr, parsedUrl);
    headers['x-ca-signature'] = this.sign(signToString);
    headers['user-agent'] = ua;

    var response = yield httpx.request(parsedUrl, {
      method: method,
      headers: headers,
      data: opts.data,
      beforeRequest: function (opts) {
        // FIXME: 证书有问题
        opts.rejectUnauthorized = false;
        return opts;
      }
    });

    var code = response.statusCode;
    if (code !== 200) {
      var err = new Error(`${method} ${format(url)} failed width code(${code}).` +
        ` request id: ${response.headers['x-ca-request-id']},` +
        ` error message: ${response.headers['x-ca-error-message']}`);
      throw err;
    }

    var result = yield httpx.read(response, 'utf8');
    var contentType = response.headers['content-type'] || '';
    if (contentType.includes('application/json')) {
      result = JSON.parse(result);
    }
    return result;
  }
}

module.exports = Client;
<commit_msg>Dont set Date header, so browserify can run it<commit_after>'use strict';

const {parse, format} = require('url');
const querystring = require('querystring');
const crypto = require('crypto');

const uuid = require('uuid');
const httpx = require('httpx');

const ua = require('./ua');
const Base = require('./base');

/**
 * API Gateway Client
 */
class Client extends Base {
  constructor(key, secret, stage) {
    super();
    this.appKey = key;
    this.appSecret = Buffer.from(secret, 'utf8');
    this.stage = stage || 'RELEASE';
  }

  buildSignToString(method, headers, signedHeadersStr, url) {
    // accept, contentMD5, contentType,
    const lf = '\n';
    var list = [method, lf];

    var accept = headers['accept'];
    if (accept) {
      list.push(accept);
    }
    list.push(lf);

    var contentMD5 = headers['content-md5'];
    if (contentMD5) {
      list.push(contentMD5);
    }
    list.push(lf);

    var contentType = headers['content-type'];
    if (contentType) {
      list.push(contentType);
    }
    list.push(lf);

    var date = headers['date'];
    if (date) {
      list.push(date);
    }
    list.push(lf);

    if (signedHeadersStr) {
      list.push(signedHeadersStr);
      list.push(lf);
    }

    list.push(this.buildUrl(url));

    return list.join('');
  }

  sign(stringToSign) {
    return crypto.createHmac('sha256', this.appSecret)
      .update(stringToSign, 'utf8').digest('base64');
  }

  md5(content) {
    return crypto.createHash('md5')
      .update(content, 'utf8')
      .digest('base64');
  }

  getSignHeaders(headers) {
    var keys = Object.keys(headers).sort();
    var signKeys = [];
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      if (key.toLowerCase().startsWith('x-ca-')) {
        signKeys.push(key);
      }
    }

    return signKeys;
  }

  buildUrl(parsedUrl, query) {
    var toStringify = Object.assign({}, parsedUrl.query, query);
    var result = parsedUrl.pathname;
    if (Object.keys(toStringify).length) {
      result += '?' + querystring.stringify(toStringify);
    }
    return result;
  }

  buildHeaders(headers) {
    var now = new Date();

    var keys = Object.keys(headers || {});
    var lowered = {};
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      lowered[key.toLowerCase()] = headers[key];
    }

    return Object.assign({
      'x-ca-timestamp': now.getTime(),
      'x-ca-key': this.appKey,
      'x-ca-nonce': uuid.v4(),
      'x-ca-stage': this.stage,
      'content-type': 'application/json',
      'accept': 'application/json'
    }, lowered);
  }

  getSignedHeadersString(signHeaders, headers) {
    var list = [];
    for (var i = 0; i < signHeaders.length; i++) {
      var key = signHeaders[i];
      list.push(key + ':' + headers[key]);
    }

    return list.join('\n');
  }

  * request(method, url, opts) {
    var headers = this.buildHeaders(opts.headers);

    if (method === 'POST' && headers['content-type'] === 'application/octet-stream') {
      headers['content-md5'] = this.md5(opts.data);
    }

    var signHeaders = this.getSignHeaders(headers);
    headers['x-ca-signature-headers'] = signHeaders.join(',');
    var signedHeadersStr = this.getSignedHeadersString(signHeaders, headers);

    var parsedUrl = parse(url, true);
    var signToString = this.buildSignToString(method, headers, signedHeadersStr, parsedUrl);
    headers['x-ca-signature'] = this.sign(signToString);
    headers['user-agent'] = ua;

    var response = yield httpx.request(parsedUrl, {
      method: method,
      headers: headers,
      data: opts.data,
      beforeRequest: function (opts) {
        // FIXME: 证书有问题
        opts.rejectUnauthorized = false;
        return opts;
      }
    });

    var code = response.statusCode;
    if (code !== 200) {
      var err = new Error(`${method} ${format(url)} failed width code(${code}).` +
        ` request id: ${response.headers['x-ca-request-id']},` +
        ` error message: ${response.headers['x-ca-error-message']}`);
      throw err;
    }

    var result = yield httpx.read(response, 'utf8');
    var contentType = response.headers['content-type'] || '';
    if (contentType.includes('application/json')) {
      result = JSON.parse(result);
    }
    return result;
  }
}

module.exports = Client;
<|endoftext|>


<commit_before>var Util, DOM, CloudFunc;
/* Функция которая возвратит обьект CloudCmd
 * @CloudFunc - обьект содержащий общий функционал
 *  клиентский и серверный
 */
(function(scope, Util, DOM, CloudFunc) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key, Config, Modules, Extensions,
            FileTemplate, PathTemplate, LinkTemplate, Listeners,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage;

        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.JSONDIR                = '/json/';
        this.HTMLDIR                = '/html/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.OLD_BROWSER            = false;
        this.HOST                   =  (function() {
            var lLocation = document.location;
            return lLocation.protocol + '//' + lLocation.host;
        })();

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param pLink - ссылка
         * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера
         */
        this.loadDir                = function(pLink, pNeedRefresh) {
            return function(pEvent) {
                /* показываем гиф загрузки возле пути папки сверху
                 * ctrl+r нажата? */

                var lCurrentLink    = DOM.getCurrentLink(),
                    lHref           = lCurrentLink.href,
                    lLink           = pLink || Util.removeStr(lHref, CloudCmd.HOST);

                lLink += '?json';

                if (lLink || lCurrentLink.target !== '_blank') {
                    Images.showLoad(pNeedRefresh ? {top:true} : null);

                    /* загружаем содержимое каталога */
                    CloudCmd.ajaxLoad(lLink, { refresh: pNeedRefresh });
                }

                DOM.preventDefault(pEvent);
            };
        };

        /**
         * функция устанавливает курсор на каталог
         * с которого мы пришли, если мы поднялись
         * в верх по файловой структуре
         * @param pDirName - имя каталога с которого мы пришли
         */
        function currentToParent(pDirName) {
            var lRootDir;
            /* убираем слэш с имени каталога */
            pDirName    = Util.removeStr(pDirName, '/');
            lRootDir    = DOM.getCurrentFileByName(pDirName);

            if (lRootDir) {
                DOM.setCurrentFile(lRootDir);
                DOM.scrollIntoViewIfNeeded(lRootDir, true);
            }
        }

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(pParams) {
            var lName, lPath, lFunc, lDoBefore, lSlash, lAfterSlash,
                isContain;

            if (pParams) {
                lName       = pParams.name,
                lPath       = pParams.path,
                lFunc       = pParams.func,
                lDoBefore   = pParams.dobefore;

                if (Util.isString(pParams))
                    lPath = pParams;

                if (lPath && !lName) {
                    lName = Util.getStrBigFirst(lPath);
                    lName = Util.removeStr(lName, '.js');

                    lSlash = lName.indexOf('/');
                    if (lSlash > 0) {
                        lAfterSlash = lName.substr(lSlash);
                        lName = Util.removeStr(lName, lAfterSlash);
                    }
                }

                isContain = Util.isContainStr(lPath, '.js');
                if (!isContain)
                    lPath += '.js';

                if (!CloudCmd[lName]) {
                    CloudCmd[lName] = function(pArg) {
                        var path = CloudCmd.LIBDIRCLIENT + lPath;

                        Util.exec(lDoBefore);

                        return DOM.jsload(path, lFunc ||
                            function() {
                                var Proto = CloudCmd[lName];

                                if (Util.isFunction(Proto))
                                    CloudCmd[lName] = new Proto(pArg);
                            });
                    };

                    CloudCmd[lName].show = CloudCmd[lName];
                }
            }
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function() {
            var lCallBack, lFunc;

            lCallBack = function() {
                Util.loadOnLoad([
                    initModules,
                    baseInit,
                    Util.bind(CloudCmd.route, location.hash)
                ]);
            },
            lFunc = function(pCallBack) {
                CloudCmd.OLD_BROWSER = true;
                var lSrc = CloudCmd.LIBDIRCLIENT + 'polyfill.js';

                DOM.jqueryLoad(
                    DOM.retJSLoad(lSrc, pCallBack)
                );
            };

            Util.ifExec(document.body.scrollIntoViewIfNeeded, lCallBack, lFunc);
        };

        this.route                   = function(pPath) {
            var lQuery, lModule, lFile, lCurrent, lMsg;

            if (pPath.length > 0) {
                lQuery  = pPath.split('/');

                if (lQuery.length > 0) {
                    lModule     = Util.getStrBigFirst(lQuery[1]);
                    lFile       = lQuery[2];
                    lCurrent    = DOM.getCurrentFileByName(lFile);
                    if (lFile && !lCurrent) {
                        lMsg = CloudFunc.formatMsg('set current file', lFile, 'error');
                        Util.log(lMsg);
                    } else {
                        DOM.setCurrentFile(lCurrent);
                        CloudCmd.execFromModule(lModule, 'show');
                    }
                }
            }
        };

        function initModules(pCallBack) {
            Util.ifExec(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path  : 'key.js',
                    func : callback
                 });
             });

            CloudCmd.getModules(function(pModules) {
                pModules            = pModules || [];

                var i, n, module, storageObj, mod, name, path,
                    STORAGE  = 'storage',
                    showLoadFunc    = Util.bind(Images.showLoad, {
                        top:true
                    }),

                    doBefore       = {
                        'edit'  : showLoadFunc,
                        'view'  : showLoadFunc,
                        'menu'  : showLoadFunc
                    },

                    load = function(name, path, func) {
                        loadModule({
                            name        : name,
                            path        : path,
                            dobefore    : func
                        });
                    };

                for (i = 0, n = pModules.length; i < n ; i++) {
                    module = pModules[i];

                    if (Util.isString(module))
                        load(null, module, doBefore[module]);
                }

                storageObj = Util.findObjByNameInArr(pModules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                for (i = 0, n = mod.length; i < n; i++) {
                    name = mod[i],
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path);
                }

                Util.exec(pCallBack);

            });
        }

        function baseInit(pCallBack) {
            var LIB         = CloudCmd.LIBDIR,
                LIBCLIENT   = CloudCmd.LIBDIRCLIENT,
                files       = DOM.getFiles(),
                LEFT        = CloudFunc.PANEL_LEFT,
                RIGHT       = CloudFunc.PANEL_RIGHT;

            /* выделяем строку с первым файлом                                  */
            if (files)
                DOM.setCurrentFile(files[0]);

            Listeners = CloudCmd.Listeners;
            Listeners.init();
            /* загружаем Google Analytics */
            Listeners.analytics();
            Listeners.changeLinks(LEFT);
            Listeners.changeLinks(RIGHT);

            Listeners.initKeysPanel();

            CloudCmd.getConfig(function(config) {
                var localStorage    = config.localStorage,
                    dirPath         = DOM.getCurrentDirPath();

                /* устанавливаем переменную доступности кэша                    */
                Storage.setAllowed(localStorage);
                /* Устанавливаем кэш корневого каталога                         */

                dirPath     = CloudFunc.rmLastSlash(dirPath) || '/';

                if (!Storage.get(dirPath))
                    Storage.set(dirPath, getJSONfromFileTable());
                });

            Util.exec(CloudCmd.Key);
            Util.exec(pCallBack);
        }

        function getSystemFile(pGlobal, pURL) {

            function lGetSysFile(pCallBack) {
                Util.ifExec(pGlobal, pCallBack, function(pCallBack) {
                    DOM.ajax({
                        url     : pURL,
                        success : function(pLocal) {
                            pGlobal = pLocal;
                            Util.exec(pCallBack, pLocal);
                        }
                    });
                });
            }

            return lGetSysFile;
        }

        this.setConfig              = function(config) { Config = config; };
        this.getConfig              = getSystemFile(Config,         CloudCmd.JSONDIR + 'config.json');
        this.getModules             = getSystemFile(Modules,        CloudCmd.JSONDIR + 'modules.json');
        this.getExt                 = getSystemFile(Extensions,        CloudCmd.JSONDIR + 'ext.json');
        this.getFileTemplate        = getSystemFile(FileTemplate,   CloudCmd.HTMLDIR + 'file.html');
        this.getPathTemplate        = getSystemFile(PathTemplate,   CloudCmd.HTMLDIR + 'path.html');
        this.getLinkTemplate        = getSystemFile(PathTemplate,   CloudCmd.HTMLDIR + 'link.html');

        this.execFromModule         = function(pModuleName, pFuncName, pParams) {
            var lObject     = CloudCmd[pModuleName];
            Util.ifExec(Util.isObject(lObject),
                function() {
                    var lObj = CloudCmd[pModuleName];
                    Util.exec( lObj[pFuncName], pParams);
                },

                function(pCallBack) {
                    Util.exec(lObject, pCallBack);
                });
        };

        this.refresh                =  function(pCurrent) {
            var lNEEDREFRESH    = true,
                lPanel          = pCurrent && pCurrent.parentElement,
                lPath           = DOM.getCurrentDirPath(lPanel),
                lLink           = CloudFunc.FS + lPath,
                lNotSlashlLink  = CloudFunc.rmLastSlash(lLink),
                lLoad           = CloudCmd.loadDir(lNotSlashlLink, lNEEDREFRESH);

            lLoad();
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param pOptions
         * { refresh, nohistory } - необходимость обновить данные о каталоге
         */
        this.ajaxLoad               = function(path, options) {
            var json, str,
                ret         = options && options.refresh,
                SLASH       = '/',
                fsPath      = decodeURI(path),
                noJSONPath  = Util.removeStr(fsPath, '?json' ),
                cleanPath   = Util.removeStrOneTime(noJSONPath, CloudFunc.FS) || SLASH,
                title       = CloudFunc.getTitle(cleanPath),
                oldURL      = window.location.pathname;

            if (!options)
                options    = {};

            if (cleanPath === SLASH)
                noJSONPath = SLASH;

            Util.log ('reading dir: "' + cleanPath + '";');

            if (!options.nohistory)
                DOM.setHistory(noJSONPath, null, noJSONPath);

            DOM.setTitle(title);

             /* если доступен localStorage и
              * в нём есть нужная нам директория -
              * читаем данные с него и
              * выходим
              * если стоит поле обязательной перезагрузки -
              * перезагружаемся
              */
            if (!ret) {
                str = Storage.get(cleanPath);

                if (!str)
                    ret = true;
                else {
                    json = Util.parseJSON(str);
                    CloudCmd.createFileTable(json);
                }
            }

            if (ret)
                DOM.getCurrentFileContent({
                    url         : fsPath,
                    dataType    : 'json',
                    error       : function() {
                        DOM.setHistory(oldURL, null, oldURL);
                    },
                    success     : function(data) {
                        var str         = Util.stringifyJSON(data),
                            MAX_SIZE    = 50000;

                        CloudCmd.createFileTable(data);
                        Util.log(str.length);

                        /* если размер данных не очень бошьой       *
                         * сохраняем их в кэше                      */
                        if (str.length < MAX_SIZE)
                            Storage.set(cleanPath, str);
                    }
                });
        };

        /**
         * Функция строит файловую таблицу
         * @param pJSON  - данные о файлах
         */
        this.createFileTable        = function(pJSON) {
            var files,
                panel           = DOM.getPanel(),
                /* getting current element if was refresh */
                lPath           = DOM.getCurrentDirPath(panel),

                lCurrent        = DOM.getCurrentFile(),
                lDir            = DOM.getCurrentDirName(),

                lName           = DOM.getCurrentName(lCurrent),
                wasRefresh      = lPath === pJSON.path,
                lFuncs          = [
                    CloudCmd.getFileTemplate,
                    CloudCmd.getPathTemplate,
                    CloudCmd.getLinkTemplate
                ];

            Util.asyncCall(lFuncs, function(pTemplate, pPathTemplate, pLinkTemplate) {
                /* очищаем панель */
                var n, found,
                    i = panel.childNodes.length;

                while(i--)
                    panel.removeChild(panel.lastChild);

                panel.innerHTML = CloudFunc.buildFromJSON(pJSON, pTemplate, pPathTemplate, pLinkTemplate);

                files = DOM.getFiles(panel);

                /* searching current file */
                if (wasRefresh) {
                    n = files.length;

                    for (i = 0; i < n ; i++) {
                        var lVarCurrent = files[i],
                            lVarName    = DOM.getCurrentName(lVarCurrent);

                        found = lVarName === lName;

                        if (found) {
                            lCurrent    = files[i];
                            break;
                        }
                    }
                }
                if (!found) /* .. */
                    lCurrent = files[0];

                DOM.setCurrentFile(lCurrent);

                Listeners.changeLinks(panel.id);

                if (lName === '..' && lDir !== '/')
                    currentToParent(lDir);
            });
        };

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var current, name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files,

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files,

                i, n        = infoFiles.length;

            for (i = 0; i < n; i++) {
                current     = infoFiles[i];
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                mode        = CloudFunc.getNumericPermissions(mode);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            }

            ret = Util.stringifyJSON(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.FS + CloudFunc.rmLastSlash(path);

                Util.exec(CloudCmd.loadDir(path));
            }
        };

    }
})(this, Util, DOM, CloudFunc);
<commit_msg>fix(client) ajaxLoad: do not save path to history if content not load<commit_after>var Util, DOM, CloudFunc;
/* Функция которая возвратит обьект CloudCmd
 * @CloudFunc - обьект содержащий общий функционал
 *  клиентский и серверный
 */
(function(scope, Util, DOM, CloudFunc) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key, Config, Modules, Extensions,
            FileTemplate, PathTemplate, LinkTemplate, Listeners,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage;

        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.JSONDIR                = '/json/';
        this.HTMLDIR                = '/html/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.OLD_BROWSER            = false;
        this.HOST                   =  (function() {
            var lLocation = document.location;
            return lLocation.protocol + '//' + lLocation.host;
        })();

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param pLink - ссылка
         * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера
         */
        this.loadDir                = function(pLink, pNeedRefresh) {
            return function(pEvent) {
                /* показываем гиф загрузки возле пути папки сверху
                 * ctrl+r нажата? */

                var lCurrentLink    = DOM.getCurrentLink(),
                    lHref           = lCurrentLink.href,
                    lLink           = pLink || Util.removeStr(lHref, CloudCmd.HOST);

                lLink += '?json';

                if (lLink || lCurrentLink.target !== '_blank') {
                    Images.showLoad(pNeedRefresh ? {top:true} : null);

                    /* загружаем содержимое каталога */
                    CloudCmd.ajaxLoad(lLink, { refresh: pNeedRefresh });
                }

                DOM.preventDefault(pEvent);
            };
        };

        /**
         * функция устанавливает курсор на каталог
         * с которого мы пришли, если мы поднялись
         * в верх по файловой структуре
         * @param pDirName - имя каталога с которого мы пришли
         */
        function currentToParent(pDirName) {
            var lRootDir;
            /* убираем слэш с имени каталога */
            pDirName    = Util.removeStr(pDirName, '/');
            lRootDir    = DOM.getCurrentFileByName(pDirName);

            if (lRootDir) {
                DOM.setCurrentFile(lRootDir);
                DOM.scrollIntoViewIfNeeded(lRootDir, true);
            }
        }

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(pParams) {
            var lName, lPath, lFunc, lDoBefore, lSlash, lAfterSlash,
                isContain;

            if (pParams) {
                lName       = pParams.name,
                lPath       = pParams.path,
                lFunc       = pParams.func,
                lDoBefore   = pParams.dobefore;

                if (Util.isString(pParams))
                    lPath = pParams;

                if (lPath && !lName) {
                    lName = Util.getStrBigFirst(lPath);
                    lName = Util.removeStr(lName, '.js');

                    lSlash = lName.indexOf('/');
                    if (lSlash > 0) {
                        lAfterSlash = lName.substr(lSlash);
                        lName = Util.removeStr(lName, lAfterSlash);
                    }
                }

                isContain = Util.isContainStr(lPath, '.js');
                if (!isContain)
                    lPath += '.js';

                if (!CloudCmd[lName]) {
                    CloudCmd[lName] = function(pArg) {
                        var path = CloudCmd.LIBDIRCLIENT + lPath;

                        Util.exec(lDoBefore);

                        return DOM.jsload(path, lFunc ||
                            function() {
                                var Proto = CloudCmd[lName];

                                if (Util.isFunction(Proto))
                                    CloudCmd[lName] = new Proto(pArg);
                            });
                    };

                    CloudCmd[lName].show = CloudCmd[lName];
                }
            }
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function() {
            var lCallBack, lFunc;

            lCallBack = function() {
                Util.loadOnLoad([
                    initModules,
                    baseInit,
                    Util.bind(CloudCmd.route, location.hash)
                ]);
            },
            lFunc = function(pCallBack) {
                CloudCmd.OLD_BROWSER = true;
                var lSrc = CloudCmd.LIBDIRCLIENT + 'polyfill.js';

                DOM.jqueryLoad(
                    DOM.retJSLoad(lSrc, pCallBack)
                );
            };

            Util.ifExec(document.body.scrollIntoViewIfNeeded, lCallBack, lFunc);
        };

        this.route                   = function(pPath) {
            var lQuery, lModule, lFile, lCurrent, lMsg;

            if (pPath.length > 0) {
                lQuery  = pPath.split('/');

                if (lQuery.length > 0) {
                    lModule     = Util.getStrBigFirst(lQuery[1]);
                    lFile       = lQuery[2];
                    lCurrent    = DOM.getCurrentFileByName(lFile);
                    if (lFile && !lCurrent) {
                        lMsg = CloudFunc.formatMsg('set current file', lFile, 'error');
                        Util.log(lMsg);
                    } else {
                        DOM.setCurrentFile(lCurrent);
                        CloudCmd.execFromModule(lModule, 'show');
                    }
                }
            }
        };

        function initModules(pCallBack) {
            Util.ifExec(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path  : 'key.js',
                    func : callback
                 });
             });

            CloudCmd.getModules(function(pModules) {
                pModules            = pModules || [];

                var i, n, module, storageObj, mod, name, path,
                    STORAGE  = 'storage',
                    showLoadFunc    = Util.bind(Images.showLoad, {
                        top:true
                    }),

                    doBefore       = {
                        'edit'  : showLoadFunc,
                        'view'  : showLoadFunc,
                        'menu'  : showLoadFunc
                    },

                    load = function(name, path, func) {
                        loadModule({
                            name        : name,
                            path        : path,
                            dobefore    : func
                        });
                    };

                for (i = 0, n = pModules.length; i < n ; i++) {
                    module = pModules[i];

                    if (Util.isString(module))
                        load(null, module, doBefore[module]);
                }

                storageObj = Util.findObjByNameInArr(pModules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                for (i = 0, n = mod.length; i < n; i++) {
                    name = mod[i],
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path);
                }

                Util.exec(pCallBack);

            });
        }

        function baseInit(pCallBack) {
            var LIB         = CloudCmd.LIBDIR,
                LIBCLIENT   = CloudCmd.LIBDIRCLIENT,
                files       = DOM.getFiles(),
                LEFT        = CloudFunc.PANEL_LEFT,
                RIGHT       = CloudFunc.PANEL_RIGHT;

            /* выделяем строку с первым файлом                                  */
            if (files)
                DOM.setCurrentFile(files[0]);

            Listeners = CloudCmd.Listeners;
            Listeners.init();
            /* загружаем Google Analytics */
            Listeners.analytics();
            Listeners.changeLinks(LEFT);
            Listeners.changeLinks(RIGHT);

            Listeners.initKeysPanel();

            CloudCmd.getConfig(function(config) {
                var localStorage    = config.localStorage,
                    dirPath         = DOM.getCurrentDirPath();

                /* устанавливаем переменную доступности кэша                    */
                Storage.setAllowed(localStorage);
                /* Устанавливаем кэш корневого каталога                         */

                dirPath     = CloudFunc.rmLastSlash(dirPath) || '/';

                if (!Storage.get(dirPath))
                    Storage.set(dirPath, getJSONfromFileTable());
                });

            Util.exec(CloudCmd.Key);
            Util.exec(pCallBack);
        }

        function getSystemFile(pGlobal, pURL) {

            function lGetSysFile(pCallBack) {
                Util.ifExec(pGlobal, pCallBack, function(pCallBack) {
                    DOM.ajax({
                        url     : pURL,
                        success : function(pLocal) {
                            pGlobal = pLocal;
                            Util.exec(pCallBack, pLocal);
                        }
                    });
                });
            }

            return lGetSysFile;
        }

        this.setConfig              = function(config) { Config = config; };
        this.getConfig              = getSystemFile(Config,         CloudCmd.JSONDIR + 'config.json');
        this.getModules             = getSystemFile(Modules,        CloudCmd.JSONDIR + 'modules.json');
        this.getExt                 = getSystemFile(Extensions,        CloudCmd.JSONDIR + 'ext.json');
        this.getFileTemplate        = getSystemFile(FileTemplate,   CloudCmd.HTMLDIR + 'file.html');
        this.getPathTemplate        = getSystemFile(PathTemplate,   CloudCmd.HTMLDIR + 'path.html');
        this.getLinkTemplate        = getSystemFile(PathTemplate,   CloudCmd.HTMLDIR + 'link.html');

        this.execFromModule         = function(pModuleName, pFuncName, pParams) {
            var lObject     = CloudCmd[pModuleName];
            Util.ifExec(Util.isObject(lObject),
                function() {
                    var lObj = CloudCmd[pModuleName];
                    Util.exec( lObj[pFuncName], pParams);
                },

                function(pCallBack) {
                    Util.exec(lObject, pCallBack);
                });
        };

        this.refresh                =  function(pCurrent) {
            var lNEEDREFRESH    = true,
                lPanel          = pCurrent && pCurrent.parentElement,
                lPath           = DOM.getCurrentDirPath(lPanel),
                lLink           = CloudFunc.FS + lPath,
                lNotSlashlLink  = CloudFunc.rmLastSlash(lLink),
                lLoad           = CloudCmd.loadDir(lNotSlashlLink, lNEEDREFRESH);

            lLoad();
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param pOptions
         * { refresh, nohistory } - необходимость обновить данные о каталоге
         */
        this.ajaxLoad               = function(path, options) {
            var json, str,
                ret         = options && options.refresh,
                SLASH       = '/',
                dirPath     = DOM.getCurrentDirPath(),
                fsPath      = decodeURI(path),
                noJSONPath  = Util.removeStr(fsPath, '?json' ),
                cleanPath   = Util.removeStrOneTime(noJSONPath, CloudFunc.FS) || SLASH,
                setTitle    = function() {
                    var title;

                    dirPath = CloudFunc.rmLastSlash(dirPath) || '/';

                    if (dirPath !== cleanPath) {
                        if (!options.nohistory)
                            DOM.setHistory(noJSONPath, null, noJSONPath);

                        title   = CloudFunc.getTitle(cleanPath);
                        DOM.setTitle(title);
                    }
                };

            if (!options)
                options    = {};

            if (cleanPath === SLASH)
                noJSONPath = SLASH;

            Util.log ('reading dir: "' + cleanPath + '";');

             /* если доступен localStorage и
              * в нём есть нужная нам директория -
              * читаем данные с него и
              * выходим
              * если стоит поле обязательной перезагрузки -
              * перезагружаемся
              */
            if (!ret) {
                str = Storage.get(cleanPath);

                if (!str)
                    ret = true;
                else {
                    json = Util.parseJSON(str);
                    CloudCmd.createFileTable(json);
                    setTitle();
                }
            }

            if (ret)
                DOM.getCurrentFileContent({
                    url         : fsPath,
                    dataType    : 'json',
                    success     : function(data) {
                        var str         = Util.stringifyJSON(data),
                            MAX_SIZE    = 50000;

                        setTitle();

                        CloudCmd.createFileTable(data);
                        Util.log(str.length);

                        /* если размер данных не очень бошьой       *
                         * сохраняем их в кэше                      */
                        if (str.length < MAX_SIZE)
                            Storage.set(cleanPath, str);
                    }
                });
        };

        /**
         * Функция строит файловую таблицу
         * @param pJSON  - данные о файлах
         */
        this.createFileTable        = function(pJSON) {
            var files,
                panel           = DOM.getPanel(),
                /* getting current element if was refresh */
                lPath           = DOM.getCurrentDirPath(panel),

                lCurrent        = DOM.getCurrentFile(),
                lDir            = DOM.getCurrentDirName(),

                lName           = DOM.getCurrentName(lCurrent),
                wasRefresh      = lPath === pJSON.path,
                lFuncs          = [
                    CloudCmd.getFileTemplate,
                    CloudCmd.getPathTemplate,
                    CloudCmd.getLinkTemplate
                ];

            Util.asyncCall(lFuncs, function(pTemplate, pPathTemplate, pLinkTemplate) {
                /* очищаем панель */
                var n, found,
                    i = panel.childNodes.length;

                while(i--)
                    panel.removeChild(panel.lastChild);

                panel.innerHTML = CloudFunc.buildFromJSON(pJSON, pTemplate, pPathTemplate, pLinkTemplate);

                files = DOM.getFiles(panel);

                /* searching current file */
                if (wasRefresh) {
                    n = files.length;

                    for (i = 0; i < n ; i++) {
                        var lVarCurrent = files[i],
                            lVarName    = DOM.getCurrentName(lVarCurrent);

                        found = lVarName === lName;

                        if (found) {
                            lCurrent    = files[i];
                            break;
                        }
                    }
                }
                if (!found) /* .. */
                    lCurrent = files[0];

                DOM.setCurrentFile(lCurrent);

                Listeners.changeLinks(panel.id);

                if (lName === '..' && lDir !== '/')
                    currentToParent(lDir);
            });
        };

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var current, name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files,

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files,

                i, n        = infoFiles.length;

            for (i = 0; i < n; i++) {
                current     = infoFiles[i];
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                mode        = CloudFunc.getNumericPermissions(mode);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            }

            ret = Util.stringifyJSON(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.FS + CloudFunc.rmLastSlash(path);

                Util.exec(CloudCmd.loadDir(path));
            }
        };

    }
})(this, Util, DOM, CloudFunc);
<|endoftext|>


<commit_before>
/*!
 * knox - Client
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter
  , utils = require('./utils')
  , auth = require('./auth')
  , http = require('http')
  , https = require('https')
  , url = require('url')
  , mime = require('mime')
  , fs = require('fs')
  , crypto = require('crypto');

// The max for multi-object delete, bucket listings, etc.
var BUCKET_OPS_MAX = 1000;

/**
 * Register event listeners on a request object to convert standard http
 * request events into appropriate call backs.
 * @param {Request} req The http request
 * @param {Function} fn(err, res) The callback function.
 * err - The exception if an exception occurred while sending the http
 * request (for example if internet connection was lost).
 * res - The http response if no exception occurred.
 * @api private
 */
function registerReqListeners(req, fn){
  req.on('response', function(res){ fn(null, res); });
  req.on('error', fn);
}

function ensureLeadingSlash(filename) {
  return filename[0] !== '/' ? '/' + filename : filename;
}

/**
 * Initialize a `Client` with the given `options`.
 *
 * Required:
 *
 *  - `key`     amazon api key
 *  - `secret`  amazon secret
 *  - `bucket`  bucket name string, ex: "learnboost"
 *
 * @param {Object} options
 * @api public
 */

var Client = module.exports = exports = function Client(options) {
  if (!options.key) throw new Error('aws "key" required');
  if (!options.secret) throw new Error('aws "secret" required');
  if (!options.bucket) throw new Error('aws "bucket" required');

  if (options.bucket !== options.bucket.toLowerCase()) {
    throw new Error('AWS bucket names must be all lower case. ' +
      'See https://github.com/LearnBoost/knox/issues/44#issuecomment-7074177 ' +
      'for details.');
  }

  this.endpoint = options.bucket + '.s3.amazonaws.com';
  this.secure = true;
  utils.merge(this, options);

  this.url = this.secure ? this.https : this.http;
};

/**
 * Request with `filename` the given `method`, and optional `headers`.
 *
 * @param {String} method
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api private
 */

Client.prototype.request = function(method, filename, headers){
  var options = { host: this.endpoint }
    , date = new Date
    , headers = headers || {};

  if ('undefined' != typeof this.port) {
    options.port = this.port;
  }

  filename = ensureLeadingSlash(filename);

  // Default headers
  utils.merge(headers, {
      Date: date.toUTCString()
    , Host: this.endpoint
  });

  // Authorization header
  headers.Authorization = auth.authorization({
      key: this.key
    , secret: this.secret
    , verb: method
    , date: date
    , resource: auth.canonicalizeResource('/' + this.bucket + filename)
    , contentType: headers['Content-Type']
    , md5: headers['Content-MD5'] || ''
    , amazonHeaders: auth.canonicalizeHeaders(headers)
  });

  // Issue request
  options.method = method;
  options.path = filename;
  options.headers = headers;
  var req = (this.secure ? https : http).request(options);
  req.url = this.url(filename);

  return req;
};

/**
 * PUT data to `filename` with optional `headers`.
 *
 * Example:
 *
 *     // Fetch the size
 *     fs.stat('Readme.md', function(err, stat){
 *      // Create our request
 *      var req = client.put('/test/Readme.md', {
 *          'Content-Length': stat.size
 *        , 'Content-Type': 'text/plain'
 *      });
 *      fs.readFile('Readme.md', function(err, buf){
 *        // Output response
 *        req.on('response', function(res){
 *          console.log(res.statusCode);
 *          console.log(res.headers);
 *          res.on('data', function(chunk){
 *            console.log(chunk.toString());
 *          });
 *        });
 *        // Send the request with the file's Buffer obj
 *        req.end(buf);
 *      });
 *     });
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.put = function(filename, headers){
  headers = utils.merge({
      Expect: '100-continue'
    , 'x-amz-acl': 'public-read'
  }, headers || {});
  return this.request('PUT', filename, headers);
};

/**
 * PUT the file at `src` to `filename`, with callback `fn`
 * receiving a possible exception, and the response object.
 *
 * Example:
 *
 *    client
 *     .putFile('package.json', '/test/package.json', function(err, res){
 *       if (err) throw err;
 *       console.log(res.statusCode);
 *       console.log(res.headers);
 *     });
 *
 * @param {String} src
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @return {EventEmitter}
 * @api public
 */

Client.prototype.putFile = function(src, filename, headers, fn){
  var self = this;
  var emitter = new Emitter;

  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  fs.stat(src, function (err, stat) {
    if (err) return fn(err);

    var contentType = mime.lookup(src);

    // Add charset if it's known.
    var charset = mime.charsets.lookup(contentType);
    if (charset) {
      contentType += '; charset=' + charset;
    }

    headers = utils.merge({
        'Content-Length': stat.size
      , 'Content-Type': contentType
    }, headers);

    var stream = fs.createReadStream(src);
    var req = self.putStream(stream, filename, headers, fn);

    req.on('progress', emitter.emit.bind(emitter, 'progress'));
  });

  return emitter;
};

/**
 * PUT the given `stream` as `filename` with `headers`.
 * `headers` must contain `'Content-Length'` at least.
 *
 * @param {Stream} stream
 * @param {String} filename
 * @param {Object} headers
 * @param {Function} fn
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.putStream = function(stream, filename, headers, fn){
  var self = this;
  var req = self.put(filename, headers);

  registerReqListeners(req, fn);
  stream.on('error', fn);

  var written = 0;
  var total = headers['Content-Length'];
  stream.on('data', function(chunk){
    written += chunk.length;
    req.emit('progress', {
        percent: written / total * 100 | 0
      , written: written
      , total: total
    });
  });

  stream.pipe(req);
  return req;
};

/**
 * PUT the given `buffer` as `filename` with optional `headers`.
 * Callback `fn` receives a possible exception and the response object.
 *
 * @param {Buffer} buffer
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.putBuffer = function(buffer, filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  headers['Content-Length'] = buffer.length;

  var req = this.put(filename, headers);
  registerReqListeners(req, fn);
  req.end(buffer);
  return req;
};

/**
 * Copy files from `sourceFilename` to `destFilename` with optional `headers`.
 *
 * @param {String} sourceFilename
 * @param {String} destFilename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.copy = function(sourceFilename, destFilename, headers){
  sourceFilename = ensureLeadingSlash(sourceFilename);
  headers = utils.merge({
      Expect: '100-continue'
    , 'x-amz-acl': 'public-read'
  }, headers || {});
  headers['x-amz-copy-source'] = '/' + this.bucket + sourceFilename;
  headers['Content-Length'] = 0; // to avoid Node's automatic chunking if omitted

  return this.put(destFilename, headers);
};

/**
 * Copy files from `sourceFilename` to `destFilename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} sourceFilename
 * @param {String} destFilename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.copyFile = function(sourceFilename, destFilename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var req = this.copy(sourceFilename, destFilename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * GET `filename` with optional `headers`.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.get = function(filename, headers){
  return this.request('GET', filename, headers);
};

/**
 * GET `filename` with optional `headers` and callback `fn`
 * with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.getFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var req = this.get(filename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * Issue a HEAD request on `filename` with optional `headers.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.head = function(filename, headers){
  return this.request('HEAD', filename, headers);
};

/**
 * Issue a HEAD request on `filename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.headFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }
  var req = this.head(filename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * DELETE `filename` with optional `headers.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.del = function(filename, headers){
  return this.request('DELETE', filename, headers);
};

/**
 * DELETE `filename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.deleteFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }
  var req = this.del(filename, headers)
  registerReqListeners(req, fn);
  req.end();
};

function xmlEscape(string) {
    return string.replace(/&/g, "&")
                 .replace(/</g, "<")
                 .replace(/>/g, ">")
                 .replace(/"/g, """);
}

function makeDeleteXmlString(keys) {
    var tags = keys.map(function (key) {
        return "<Object><Key>" + xmlEscape(key) + "</Key></Object>";
    });
    return "<Delete>" + tags.join("") + "</Delete>";
}

/**
 * Delete up to 1000 files at a time, with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {Array[String]} filenames
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.deleteMultiple = function(filenames, headers, fn){
  if (filenames.length > BUCKET_OPS_MAX) {
    throw new Error('Can only delete up to ' + BUCKET_OPS_MAX + ' files ' +
                    'at a time. You\'ll need to batch them.');
  }

  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var xml = makeDeleteXmlString(filenames);

  headers['Content-Length'] = xml.length;
  headers['Content-MD5'] = crypto.createHash('md5').update(xml).digest('base64');

  return this.request('POST', '/?delete', headers)
  .on('response', function(res){
    fn(null, res);
  })
  .on('error', function(err){
    fn(err);
  })
  .end(xml);
};

/**
 * Return a url to the given `filename`.
 *
 * @param {String} filename
 * @return {String}
 * @api public
 */

Client.prototype.http = function(filename){
  return 'http://' + this.endpoint + filename;
};

/**
 * Return an HTTPS url to the given `filename`.
 *
 * @param {String} filename
 * @return {String}
 * @api public
 */

Client.prototype.https = function(filename){
  return 'https://' + this.endpoint + filename;
};

/**
 * Return an S3 presigned url to the given `filename`.
 *
 * @param {String} filename
 * @param {Date} expiration
 * @return {String}
 * @api public
 */

Client.prototype.signedUrl = function(filename, expiration){
  var epoch = Math.floor(expiration.getTime()/1000)
    , pathname = url.parse(filename).pathname
    , resource = '/' + this.bucket + ensureLeadingSlash(pathname);

  var signature = auth.signQuery({
      secret: this.secret
    , date: epoch
    , resource: resource
  });

  return this.url(filename) +
    '?Expires=' + epoch +
    '&AWSAccessKeyId=' + this.key +
    '&Signature=' + encodeURIComponent(signature);
};

/**
 * Shortcut for `new Client()`.
 *
 * @param {Object} options
 * @see Client()
 * @api public
 */

exports.createClient = function(options){
  return new Client(options);
};
<commit_msg>Default to insecure if a custom port is specified.<commit_after>/*!
 * knox - Client
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter
  , utils = require('./utils')
  , auth = require('./auth')
  , http = require('http')
  , https = require('https')
  , url = require('url')
  , mime = require('mime')
  , fs = require('fs')
  , crypto = require('crypto');

// The max for multi-object delete, bucket listings, etc.
var BUCKET_OPS_MAX = 1000;

/**
 * Register event listeners on a request object to convert standard http
 * request events into appropriate call backs.
 * @param {Request} req The http request
 * @param {Function} fn(err, res) The callback function.
 * err - The exception if an exception occurred while sending the http
 * request (for example if internet connection was lost).
 * res - The http response if no exception occurred.
 * @api private
 */
function registerReqListeners(req, fn){
  req.on('response', function(res){ fn(null, res); });
  req.on('error', fn);
}

function ensureLeadingSlash(filename) {
  return filename[0] !== '/' ? '/' + filename : filename;
}

/**
 * Initialize a `Client` with the given `options`.
 *
 * Required:
 *
 *  - `key`     amazon api key
 *  - `secret`  amazon secret
 *  - `bucket`  bucket name string, ex: "learnboost"
 *
 * @param {Object} options
 * @api public
 */

var Client = module.exports = exports = function Client(options) {
  if (!options.key) throw new Error('aws "key" required');
  if (!options.secret) throw new Error('aws "secret" required');
  if (!options.bucket) throw new Error('aws "bucket" required');

  if (options.bucket !== options.bucket.toLowerCase()) {
    throw new Error('AWS bucket names must be all lower case. ' +
      'See https://github.com/LearnBoost/knox/issues/44#issuecomment-7074177 ' +
      'for details.');
  }

  this.endpoint = options.bucket + '.s3.amazonaws.com';
  this.secure = 'undefined' == typeof options.port;
  utils.merge(this, options);

  this.url = this.secure ? this.https : this.http;
};

/**
 * Request with `filename` the given `method`, and optional `headers`.
 *
 * @param {String} method
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api private
 */

Client.prototype.request = function(method, filename, headers){
  var options = { host: this.endpoint }
    , date = new Date
    , headers = headers || {};

  if ('undefined' != typeof this.port) {
    options.port = this.port;
  }

  filename = ensureLeadingSlash(filename);

  // Default headers
  utils.merge(headers, {
      Date: date.toUTCString()
    , Host: this.endpoint
  });

  // Authorization header
  headers.Authorization = auth.authorization({
      key: this.key
    , secret: this.secret
    , verb: method
    , date: date
    , resource: auth.canonicalizeResource('/' + this.bucket + filename)
    , contentType: headers['Content-Type']
    , md5: headers['Content-MD5'] || ''
    , amazonHeaders: auth.canonicalizeHeaders(headers)
  });

  // Issue request
  options.method = method;
  options.path = filename;
  options.headers = headers;
  var req = (this.secure ? https : http).request(options);
  req.url = this.url(filename);

  return req;
};

/**
 * PUT data to `filename` with optional `headers`.
 *
 * Example:
 *
 *     // Fetch the size
 *     fs.stat('Readme.md', function(err, stat){
 *      // Create our request
 *      var req = client.put('/test/Readme.md', {
 *          'Content-Length': stat.size
 *        , 'Content-Type': 'text/plain'
 *      });
 *      fs.readFile('Readme.md', function(err, buf){
 *        // Output response
 *        req.on('response', function(res){
 *          console.log(res.statusCode);
 *          console.log(res.headers);
 *          res.on('data', function(chunk){
 *            console.log(chunk.toString());
 *          });
 *        });
 *        // Send the request with the file's Buffer obj
 *        req.end(buf);
 *      });
 *     });
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.put = function(filename, headers){
  headers = utils.merge({
      Expect: '100-continue'
    , 'x-amz-acl': 'public-read'
  }, headers || {});
  return this.request('PUT', filename, headers);
};

/**
 * PUT the file at `src` to `filename`, with callback `fn`
 * receiving a possible exception, and the response object.
 *
 * Example:
 *
 *    client
 *     .putFile('package.json', '/test/package.json', function(err, res){
 *       if (err) throw err;
 *       console.log(res.statusCode);
 *       console.log(res.headers);
 *     });
 *
 * @param {String} src
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @return {EventEmitter}
 * @api public
 */

Client.prototype.putFile = function(src, filename, headers, fn){
  var self = this;
  var emitter = new Emitter;

  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  fs.stat(src, function (err, stat) {
    if (err) return fn(err);

    var contentType = mime.lookup(src);

    // Add charset if it's known.
    var charset = mime.charsets.lookup(contentType);
    if (charset) {
      contentType += '; charset=' + charset;
    }

    headers = utils.merge({
        'Content-Length': stat.size
      , 'Content-Type': contentType
    }, headers);

    var stream = fs.createReadStream(src);
    var req = self.putStream(stream, filename, headers, fn);

    req.on('progress', emitter.emit.bind(emitter, 'progress'));
  });

  return emitter;
};

/**
 * PUT the given `stream` as `filename` with `headers`.
 * `headers` must contain `'Content-Length'` at least.
 *
 * @param {Stream} stream
 * @param {String} filename
 * @param {Object} headers
 * @param {Function} fn
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.putStream = function(stream, filename, headers, fn){
  var self = this;
  var req = self.put(filename, headers);

  registerReqListeners(req, fn);
  stream.on('error', fn);

  var written = 0;
  var total = headers['Content-Length'];
  stream.on('data', function(chunk){
    written += chunk.length;
    req.emit('progress', {
        percent: written / total * 100 | 0
      , written: written
      , total: total
    });
  });

  stream.pipe(req);
  return req;
};

/**
 * PUT the given `buffer` as `filename` with optional `headers`.
 * Callback `fn` receives a possible exception and the response object.
 *
 * @param {Buffer} buffer
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.putBuffer = function(buffer, filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  headers['Content-Length'] = buffer.length;

  var req = this.put(filename, headers);
  registerReqListeners(req, fn);
  req.end(buffer);
  return req;
};

/**
 * Copy files from `sourceFilename` to `destFilename` with optional `headers`.
 *
 * @param {String} sourceFilename
 * @param {String} destFilename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.copy = function(sourceFilename, destFilename, headers){
  sourceFilename = ensureLeadingSlash(sourceFilename);
  headers = utils.merge({
      Expect: '100-continue'
    , 'x-amz-acl': 'public-read'
  }, headers || {});
  headers['x-amz-copy-source'] = '/' + this.bucket + sourceFilename;
  headers['Content-Length'] = 0; // to avoid Node's automatic chunking if omitted

  return this.put(destFilename, headers);
};

/**
 * Copy files from `sourceFilename` to `destFilename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} sourceFilename
 * @param {String} destFilename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.copyFile = function(sourceFilename, destFilename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var req = this.copy(sourceFilename, destFilename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * GET `filename` with optional `headers`.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.get = function(filename, headers){
  return this.request('GET', filename, headers);
};

/**
 * GET `filename` with optional `headers` and callback `fn`
 * with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.getFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var req = this.get(filename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * Issue a HEAD request on `filename` with optional `headers.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.head = function(filename, headers){
  return this.request('HEAD', filename, headers);
};

/**
 * Issue a HEAD request on `filename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.headFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }
  var req = this.head(filename, headers);
  registerReqListeners(req, fn);
  req.end();
  return req;
};

/**
 * DELETE `filename` with optional `headers.
 *
 * @param {String} filename
 * @param {Object} headers
 * @return {ClientRequest}
 * @api public
 */

Client.prototype.del = function(filename, headers){
  return this.request('DELETE', filename, headers);
};

/**
 * DELETE `filename` with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {String} filename
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.deleteFile = function(filename, headers, fn){
  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }
  var req = this.del(filename, headers)
  registerReqListeners(req, fn);
  req.end();
};

function xmlEscape(string) {
    return string.replace(/&/g, "&")
                 .replace(/</g, "<")
                 .replace(/>/g, ">")
                 .replace(/"/g, """);
}

function makeDeleteXmlString(keys) {
    var tags = keys.map(function (key) {
        return "<Object><Key>" + xmlEscape(key) + "</Key></Object>";
    });
    return "<Delete>" + tags.join("") + "</Delete>";
}

/**
 * Delete up to 1000 files at a time, with optional `headers`
 * and callback `fn` with a possible exception and the response.
 *
 * @param {Array[String]} filenames
 * @param {Object|Function} headers
 * @param {Function} fn
 * @api public
 */

Client.prototype.deleteMultiple = function(filenames, headers, fn){
  if (filenames.length > BUCKET_OPS_MAX) {
    throw new Error('Can only delete up to ' + BUCKET_OPS_MAX + ' files ' +
                    'at a time. You\'ll need to batch them.');
  }

  if ('function' == typeof headers) {
    fn = headers;
    headers = {};
  }

  var xml = makeDeleteXmlString(filenames);

  headers['Content-Length'] = xml.length;
  headers['Content-MD5'] = crypto.createHash('md5').update(xml).digest('base64');

  return this.request('POST', '/?delete', headers)
  .on('response', function(res){
    fn(null, res);
  })
  .on('error', function(err){
    fn(err);
  })
  .end(xml);
};

/**
 * Return a url to the given `filename`.
 *
 * @param {String} filename
 * @return {String}
 * @api public
 */

Client.prototype.http = function(filename){
  return 'http://' + this.endpoint + filename;
};

/**
 * Return an HTTPS url to the given `filename`.
 *
 * @param {String} filename
 * @return {String}
 * @api public
 */

Client.prototype.https = function(filename){
  return 'https://' + this.endpoint + filename;
};

/**
 * Return an S3 presigned url to the given `filename`.
 *
 * @param {String} filename
 * @param {Date} expiration
 * @return {String}
 * @api public
 */

Client.prototype.signedUrl = function(filename, expiration){
  var epoch = Math.floor(expiration.getTime()/1000)
    , pathname = url.parse(filename).pathname
    , resource = '/' + this.bucket + ensureLeadingSlash(pathname);

  var signature = auth.signQuery({
      secret: this.secret
    , date: epoch
    , resource: resource
  });

  return this.url(filename) +
    '?Expires=' + epoch +
    '&AWSAccessKeyId=' + this.key +
    '&Signature=' + encodeURIComponent(signature);
};

/**
 * Shortcut for `new Client()`.
 *
 * @param {Object} options
 * @see Client()
 * @api public
 */

exports.createClient = function(options){
  return new Client(options);
};
<|endoftext|>


<commit_before>var net = require('net');
var parse = require('url').parse;
var HttpRequestStack = require('http-stack').HttpRequestStack;
var IcecastReadStack = require('./icecast-stack').IcecastReadStack;

/**
 * A full-blown Icecast/SHOUTcast compliant Client class. Conveniently takes
 * care of creating a low-level TCP connection, sending an HTTP request, and
 * parsing the 'metadata' events out of the resulting audio stream.
 */
function Client(url, headers, retainMetadata) {

  // parse the URL into a usable object
  var parsedUrl = parse(url);
  // ensure an HTTP based path and port
  parsedUrl.pathname = parsedUrl.pathname || '/';
  parsedUrl.port = parsedUrl.port || (parsedUrl.protocol == "https:" ? 443 : 80);

  // Create the low-level TCP connection to the remote server.
  var stream = net.createConnection(parsedUrl.port, parsedUrl.hostname);
  stream.allowHalfOpen = true;
  stream.remoteAddress = parsedUrl.hostname;
  stream.remotePort = parsedUrl.port;

  // Stack an 'HttpRequstStack' instance on it, and send an HTTP request
  // to the specified URL.

  stream = new HttpRequestStack(stream);
  stream.get(parsedUrl.pathname, {
    'Host': parsedUrl.host,
    'Icy-MetaData': 1
  });
  stream.end();

  // Next stack "this" 'IcecastReadStack' instance on top of that, setting the
  // inital 'metaint' value to Infinity. Setting to Infinity is just a hack
  // that allows us to have the 'createClient' function return immediately,
  // instead of invoking a callback with the stream instance.
  IcecastReadStack.call(this, stream, Infinity, retainMetadata);

  // Keep a reference to the URL used to connect to the Icecast server.
  this.url = url;

  // When the 'response' event is fired (bubbled down from the
  // HttpRequestStack instance), check for the 'icy-metaint' response header.
  this.on('response', function(res) {
    this.headers = res.headers;
    if (res.headers['icy-metaint']) {
      this.metaint = res.headers['icy-metaint'];
    }
  });
}
exports.Client = Client;
require('util').inherits(Client, IcecastReadStack);

/**
 * Convenience function to create a new Client instance.
 */
function createClient(url, retainMetadata) {
  return new Client(url, retainMetadata);
}
exports.createClient = createClient;

<commit_msg>Add support for sending custom HTTP request headers with the Icecast 'Client'.<commit_after>var net = require('net');
var parse = require('url').parse;
var HttpRequestStack = require('http-stack').HttpRequestStack;
var IcecastReadStack = require('./icecast-stack').IcecastReadStack;

/**
 * A full-blown Icecast/SHOUTcast compliant Client class. Conveniently takes
 * care of creating a low-level TCP connection, sending an HTTP request, and
 * parsing the 'metadata' events out of the resulting audio stream.
 */
function Client(url, headers, retainMetadata) {

  // parse the URL into a usable object
  var parsedUrl = parse(url);
  // ensure an HTTP based path and port
  parsedUrl.pathname = parsedUrl.pathname || '/';
  parsedUrl.port = parsedUrl.port || (parsedUrl.protocol == "https:" ? 443 : 80);

  // Create the low-level TCP connection to the remote server.
  var stream = net.createConnection(parsedUrl.port, parsedUrl.hostname);
  stream.allowHalfOpen = true;
  stream.remoteAddress = parsedUrl.hostname;
  stream.remotePort = parsedUrl.port;

  // Stack an 'HttpRequstStack' instance on it, and send an HTTP request
  // to the specified URL.
  stream = new HttpRequestStack(stream);
  headers.push('Host: ' + parsedUrl.host);
  headers.push('Icy-MetaData: 1');
  stream.get(parsedUrl.pathname, headers);
  stream.end();

  // Next stack "this" 'IcecastReadStack' instance on top of that, setting the
  // inital 'metaint' value to Infinity. Setting to Infinity is just a hack
  // that allows us to have the 'createClient' function return immediately,
  // instead of invoking a callback with the stream instance.
  IcecastReadStack.call(this, stream, Infinity, retainMetadata);

  // Keep a reference to the URL used to connect to the Icecast server.
  this.url = url;

  // When the 'response' event is fired (bubbled down from the
  // HttpRequestStack instance), check for the 'icy-metaint' response header.
  this.on('response', function(res) {
    this.headers = res.headers;
    if (res.headers['icy-metaint']) {
      this.metaint = Number(res.headers['icy-metaint']);
    }
  });
}
exports.Client = Client;
require('util').inherits(Client, IcecastReadStack);

/**
 * Convenience function to create a new Icecast 'Client' instance.
 */
function createClient(url, headers, retainMetadata) {
  if (headers === true || headers === false) {
    retainMetadata = headers;
    headers = [];
  } else if (!Array.isArray(headers)) {
    headers = [];
  }
  return new Client(url, headers, retainMetadata);
}
exports.createClient = createClient;

<|endoftext|>


<commit_before>// HTTP/HTTPS
var https = require('https');
var http = require('http');
// Express
var express = require('express');
var inspect = require('util').inspect;
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var compress = require('compression');
var authentication = require('./middleware/authentication');
var crypto = require('crypto');
// File System
var fs = require('fs');

var ACCESS = require('./routes/access');
var API = require('./routes/api');

// Make Database connection
// & Start the server
module.exports = function(environment, port, connection, instance) {
    var app = express();

    if (!port) port = 3000; // Default port;

    app.set("models", connection.collections);
    app.set("connections", connection.connections);
    app.set("galleon", instance);
    app.set("environment", environment);
    app.set("secret", environment.secret);

    // SSL Detection, Automatically switches between HTTP and HTTPS on start
    if (environment.ssl.use) {
        var SSL_CONFIG;
        try {
            SSL_CONFIG = {
                key: fs.readFileSync(environment.ssl.api.key, 'utf8'),
                cert: fs.readFileSync(environment.ssl.api.cert, 'utf8')
            }
            https.createServer(SSL_CONFIG, app).listen(port);
        }catch(e) {
            http.createServer(app).listen(port);
        }
    }else {
        http.createServer(app).listen(port);
    }

    // Allow API access outside origin (This is an API after all)
    app.use(function(req, res, next) {
		// Allow for Webmail interface
        res.header("Access-Control-Allow-Origin", req.protocol + '://' + environment.domain + ":2095");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
		res.header("Access-Control-Allow-Credentials", "true")
        next();
    });

    // uncomment after placing your favicon in /public
    //app.use(favicon(__dirname + '/public/favicon.ico'));
    app.use(compress());
    if(environment.verbose) app.use(logger('dev'));

    // If Environment secret is not set assign a random secret on every restart
    app.use(cookieParser(app.get('secret') || crypto.randomBytes(20).toString('hex')));

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
        extended: false
    }));

    // Database middleware
    app.use(function(req, res, next) {
        req.galleon = app.get("galleon");
        req.database = {
            models: app.get("models"),
            connections: app.get("connections")
        }
        req.environment = app.get("environment");
        next();
    });

    app.use(authentication({
        login: '/access/login',
        logout: '/access/logout'
    }));
    app.use('//access', ACCESS);
    app.use('//', API);

    // catch 404 and forward to error handler
    app.use(function(req, res, next) {
        var err = new Error('Not Found');
        err.status = 404;
        next(err);
    });

    // error handlers

    // development error handler
    // will print stacktrace
    if (app.get('env') === 'development') {
        app.use(function(err, req, res, next) {
            console.trace(err);
            res.status(err.status || 500);
            res.json({
                message: err.message,
                error: inspect(err, {
                    showHidden: true,
                    depth: 5
                })
            })
        });
    }

    // production error handler
    // no stacktraces leaked to user
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.json({
            message: err.message,
            error: JSON.stringify(err)
        })
    });
}
<commit_msg>Fixed CORS Control-Allow-Origin Header<commit_after>// HTTP/HTTPS
var https = require('https');
var http = require('http');
// Express
var express = require('express');
var inspect = require('util').inspect;
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var compress = require('compression');
var authentication = require('./middleware/authentication');
var crypto = require('crypto');
// File System
var fs = require('fs');

var ACCESS = require('./routes/access');
var API = require('./routes/api');

// Make Database connection
// & Start the server
module.exports = function(environment, port, connection, instance) {
    var app = express();

    if (!port) port = 3000; // Default port;

    app.set("models", connection.collections);
    app.set("connections", connection.connections);
    app.set("galleon", instance);
    app.set("environment", environment);
    app.set("secret", environment.secret);

    // SSL Detection, Automatically switches between HTTP and HTTPS on start
    if (environment.ssl.use) {
        var SSL_CONFIG;
        try {
            SSL_CONFIG = {
                key: fs.readFileSync(environment.ssl.api.key, 'utf8'),
                cert: fs.readFileSync(environment.ssl.api.cert, 'utf8')
            }
            https.createServer(SSL_CONFIG, app).listen(port);
        }catch(e) {
            http.createServer(app).listen(port);
        }
    }else {
        http.createServer(app).listen(port);
    }

    // Allow API access outside origin (This is an API after all)
    app.use(function(req, res, next) {
		// Allow for Webmail interface
        // Echo Back Origin if header is provided (Equivalent to * but allows Credentials)
        if(req.get('origin') && (typeof req.get('origin') === 'string')) {
            res.header("Access-Control-Allow-Origin", req.get('origin'));
        }else res.header("Access-Control-Allow-Origin", req.protocol + '://' + environment.domain + ":2095");

        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
		res.header("Access-Control-Allow-Credentials", "true");
        next();
    });

    // uncomment after placing your favicon in /public
    //app.use(favicon(__dirname + '/public/favicon.ico'));
    app.use(compress());
    if(environment.verbose) app.use(logger('dev'));

    // If Environment secret is not set assign a random secret on every restart
    app.use(cookieParser(app.get('secret') || crypto.randomBytes(20).toString('hex')));

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
        extended: false
    }));

    // Database middleware
    app.use(function(req, res, next) {
        req.galleon = app.get("galleon");
        req.database = {
            models: app.get("models"),
            connections: app.get("connections")
        }
        req.environment = app.get("environment");
        next();
    });

    app.use(authentication({
        login: '/access/login',
        logout: '/access/logout'
    }));
    app.use('//access', ACCESS);
    app.use('//', API);

    // catch 404 and forward to error handler
    app.use(function(req, res, next) {
        var err = new Error('Not Found');
        err.status = 404;
        next(err);
    });

    // error handlers

    // development error handler
    // will print stacktrace
    if (app.get('env') === 'development') {
        app.use(function(err, req, res, next) {
            console.trace(err);
            res.status(err.status || 500);
            res.json({
                message: err.message,
                error: inspect(err, {
                    showHidden: true,
                    depth: 5
                })
            })
        });
    }

    // production error handler
    // no stacktraces leaked to user
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.json({
            message: err.message,
            error: JSON.stringify(err)
        })
    });
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
import express from 'express';
import compression from 'compression';
import session from 'express-session';
import bodyParser from 'body-parser';
import logger from 'morgan';
import chalk from 'chalk';
import errorHandler from 'errorhandler';
import lusca from 'lusca';
import path from 'path';
import mongoose from 'mongoose';
import passport from 'passport';
import expressValidator from 'express-validator';
import expressStatusMonitor from 'express-status-monitor';
import multer from 'multer';
import dotenv from 'dotenv/config';

/**
* Controllers (route handlers).
*/
import userController from './controllers/user';

/**
* API keys and Passport configuration.
*/
import passportConfig from './config/passport';

// stores sessions in the "sessions" collection by default. See if user is loggedin (passport).
const MongoStore = require('connect-mongo')(session);

/**
 * multer configuration
 */
const upload = multer({ dest: path.join(__dirname, 'uploads/tmp') });

/**
 * Create Express server.
 */
const app = express();

/**
 * Connect to MongoDB.
 */
mongoose.Promise = global.Promise; // Use native promises (vs bluebird...) (?)
mongoose.connect(process.env.MONGODB_URI);
mongoose.connection.on('error', (err) => {
  console.error(err);
  console.log('%s MongoDB connection error. Please make sure MongoDB is running.', chalk.red('✗'));
  process.exit();
});

/**
 * Express configuration.
 */
app.set('host', process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0');
app.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8000);
app.use(expressStatusMonitor()); // report realtime server metrics for Express-based node servers ?
app.use(compression()); // reduce page loads time to the order of 15-20%
app.use(logger('dev')); // morgan
app.use('/static', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator()); // validate form inputs. cf req.assert in controllers files
// express-session: sends a session ID over cookies to the client
app.use(session({
  resave: true, // automatically write to the session store
  saveUninitialized: true, // saved new sessions
  secret: process.env.SESSION_SECRET,
  store: new MongoStore({
    url: process.env.MONGODB_URI,
    autoReconnect: true,
    clear_interval: 3600
  })
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(lusca.xframe('SAMEORIGIN')); // lusca = security middleware
app.use(lusca.xssProtection(true));

/**
 * Primary app routes.
 */
app.post('/api/signin', userController.postSignin);
app.post('/api/signup/info', userController.postSignup);
app.post('/api/signup/upload', upload.single('imageUploaded'), userController.postSignupPicture);
app.post('/api/forgot', userController.postForgot);
app.post('/api/reset/:token', userController.postReset);

app.get('/api/islogged', userController.getIslogged);

// Logged part  ====================
app.use(passportConfig.isAuthenticated);

app.get('/api/signout', userController.signout);
app.get('/api/me', userController.getMyAccount);
app.post('/api/me');
app.delete('/api/me');
app.get('/api/profile/:login');

app.get('/api/search');
app.get('/api/movie/:id');
app.post('/api/movie/:id');

/**
 * OAuth authentication routes. (Sign in)
 */
app.get('/api/auth/google', passport.authenticate('google', { scope: 'profile email' }));
app.get('/api/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
  res.redirect(req.session.returnTo || '/');
});

/**
 * Error Handler. only use in development
 */
app.use(errorHandler());

/**
 * Start Express server.
 */
app.listen(app.get('port'), () => {
  console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
  console.log('  Press CTRL-C to stop\n');
});

module.exports = app;
<commit_msg>fix upload destination file<commit_after>/**
 * Module dependencies.
 */
import express from 'express';
import compression from 'compression';
import session from 'express-session';
import bodyParser from 'body-parser';
import logger from 'morgan';
import chalk from 'chalk';
import errorHandler from 'errorhandler';
import lusca from 'lusca';
import path from 'path';
import mongoose from 'mongoose';
import passport from 'passport';
import expressValidator from 'express-validator';
import expressStatusMonitor from 'express-status-monitor';
import multer from 'multer';
import dotenv from 'dotenv/config';

/**
* Controllers (route handlers).
*/
import userController from './controllers/user';

/**
* API keys and Passport configuration.
*/
import passportConfig from './config/passport';

// stores sessions in the "sessions" collection by default. See if user is loggedin (passport).
const MongoStore = require('connect-mongo')(session);

/**
 * multer configuration
 */
const upload = multer({ dest: path.join(__dirname, 'public/uploads/tmp') });

/**
 * Create Express server.
 */
const app = express();

/**
 * Connect to MongoDB.
 */
mongoose.Promise = global.Promise; // Use native promises (vs bluebird...) (?)
mongoose.connect(process.env.MONGODB_URI);
mongoose.connection.on('error', (err) => {
  console.error(err);
  console.log('%s MongoDB connection error. Please make sure MongoDB is running.', chalk.red('✗'));
  process.exit();
});

/**
 * Express configuration.
 */
app.set('host', process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0');
app.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8000);
app.use(expressStatusMonitor()); // report realtime server metrics for Express-based node servers ?
app.use(compression()); // reduce page loads time to the order of 15-20%
app.use(logger('dev')); // morgan
app.use('/static', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator()); // validate form inputs. cf req.assert in controllers files
// express-session: sends a session ID over cookies to the client
app.use(session({
  resave: true, // automatically write to the session store
  saveUninitialized: true, // saved new sessions
  secret: process.env.SESSION_SECRET,
  store: new MongoStore({
    url: process.env.MONGODB_URI,
    autoReconnect: true,
    clear_interval: 3600
  })
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(lusca.xframe('SAMEORIGIN')); // lusca = security middleware
app.use(lusca.xssProtection(true));

/**
 * Primary app routes.
 */
app.post('/api/signin', userController.postSignin);
app.post('/api/signup/info', userController.postSignup);
app.post('/api/signup/upload', upload.single('imageUploaded'), userController.postSignupPicture);
app.post('/api/forgot', userController.postForgot);
app.post('/api/reset/:token', userController.postReset);

app.get('/api/islogged', userController.getIslogged);

// Logged part  ====================
app.use(passportConfig.isAuthenticated);

app.get('/api/signout', userController.signout);
app.get('/api/me', userController.getMyAccount);
app.post('/api/me');
app.delete('/api/me');
app.get('/api/profile/:login');

app.get('/api/search');
app.get('/api/movie/:id');
app.post('/api/movie/:id');

/**
 * OAuth authentication routes. (Sign in)
 */
app.get('/api/auth/google', passport.authenticate('google', { scope: 'profile email' }));
app.get('/api/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
  res.redirect(req.session.returnTo || '/');
});

/**
 * Error Handler. only use in development
 */
app.use(errorHandler());

/**
 * Start Express server.
 */
app.listen(app.get('port'), () => {
  console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
  console.log('  Press CTRL-C to stop\n');
});

module.exports = app;
<|endoftext|>


<commit_before>"use strict";

const global_JSON_stringify = global.JSON.stringify;
const global_Reflect_apply = global.Reflect.apply;
const global_Object_prototype_toString = global.Object.prototype.toString;
const global_String = global.String;
const global_Error = global.Error;
const global_SyntaxError = global.SyntaxError;

exports.SyntaxError = global_SyntaxError;

exports.AsynchronousClosureAranError = class AsynchronousClosureAranError extends global_Error {};

exports.GeneratorClosureAranError = class GeneratorClosureAranError extends global_Error {};

exports.EnclaveLimitationAranError = class EnclaveLimitationAranError extends global_Error {};

exports.InvalidOptionsAranError = class InvalidOptionsAranError extends global_Error {};

exports.inspect = (value) => (
  typeof value === "string" ?
  global_JSON_stringify(value) :
  (
    (
      typeof value === "function" ||
      (
        typeof value === "object" &&
        value !== null)) ?
    global_Reflect_apply(
      global_Object_prototype_toString,
      value,
      []) :
    global_String(value)));

exports.deadcode = () => { throw new global_Error(`Supposedly deadcode has been reached`) };

exports.abort = (constructor, message, optional) => { throw new (constructor || global_Error)(message, optional) }

exports.assert = (check, constructor, message, optional) => check || exports.abort(constructor, message, optional);
<commit_msg>custom error name<commit_after>"use strict";

const global_JSON_stringify = global.JSON.stringify;
const global_Reflect_apply = global.Reflect.apply;
const global_Object_prototype_toString = global.Object.prototype.toString;
const global_String = global.String;
const global_Error = global.Error;
const global_SyntaxError = global.SyntaxError;

const ArrayLite = require("array-lite");

exports.SyntaxError = global_SyntaxError;

ArrayLite.forEach(
  [
    "AsynchronousClosureAranError",
    "GeneratorClosureAranError",
    "EnclaveLimitationAranError",
    "InvalidOptionsAranError"],
  (name) => exports[name] = class extends global_Error {
    constructor (message) {
      super(message);
      this.name = name; } });

exports.inspect = (value) => (
  typeof value === "string" ?
  global_JSON_stringify(value) :
  (
    (
      typeof value === "function" ||
      (
        typeof value === "object" &&
        value !== null)) ?
    global_Reflect_apply(
      global_Object_prototype_toString,
      value,
      []) :
    global_String(value)));

exports.deadcode = () => { throw new global_Error(`Supposedly deadcode has been reached`) };

exports.abort = (constructor, message, optional) => { throw new (constructor === null ? global_Error : constructor)(message, optional) }

exports.assert = (check, constructor, message, optional) => check || exports.abort(constructor, message, optional);
<|endoftext|>


<commit_before>
var api = require('@request/api')

module.exports = (client, provider, methods, config, transform) => {
  return api(methods, {
    api: function (options, name) {
      options.api = name
      return this
    },
    auth: function (options, arg1, arg2) {
      var alias = (options.api || provider.api || '__default')
      config.endpoint.auth(alias, options, arg1, arg2)
      return this
    },
    submit: function (options) {
      var alias = (options.api || provider.api || '__default')

      if (!config.aliases[alias]) {
        throw new Error('Purest: non existing alias!')
      }

      transform.oauth(options)
      transform.parse(options)
      options = config.endpoint.options(alias, options)

      if (!/^http/.test(options.url)) {
        options.url = config.url(alias, options)
      }

      return client(options)
    }
  })
}
<commit_msg>Fix custom method options + Allow callback to be passed to the submit method<commit_after>
var api = require('@request/api')

module.exports = (client, provider, methods, config, transform) => {
  return api(methods, {
    api: function (name) {
      this._options.api = name
      return this
    },
    auth: function (arg1, arg2) {
      var alias = (this._options.api || provider.api || '__default')
      config.endpoint.auth(alias, this._options, arg1, arg2)
      return this
    },
    submit: function (callback) {
      var options = this._options

      if (callback) {
        options.callback = callback
      }

      var alias = (options.api || provider.api || '__default')

      if (!config.aliases[alias]) {
        throw new Error('Purest: non existing alias!')
      }

      transform.oauth(options)
      transform.parse(options)
      options = config.endpoint.options(alias, options)

      if (!/^http/.test(options.url)) {
        options.url = config.url(alias, options)
      }

      return client(options)
    }
  })
}
<|endoftext|>