<commit_before>'use strict'; // eslint-disable-line strict

const proc = require('child_process');
const process = require('process');
const assert = require('assert');

const program = 's3cmd';
const upload = 'test1MB';
const download = 'tmpfile';
const MPUpload = 'test16MB';
const MPDownload = 'MPtmpfile';
const bucket = 'universe';
const nonexist = 'VOID';
const emailAccount = 'sampleAccount1@sampling.com';

const isIronman = process.env.IP ? ['-c', `${__dirname}/s3cfg`, ] : null;

function diff(putFile, receivedFile, done) {
    process.stdout.write(`diff ${putFile} ${receivedFile}\n`);
    proc.spawn('diff', [ putFile, receivedFile, ]).on('exit', code => {
        assert.strictEqual(code, 0);
        done();
    });
}

function createFile(name, bytes, callback) {
    process.stdout.write(`dd if=/dev/urandom of=${name} bs=${bytes} count=1\n`);
    proc.spawn('dd', [ 'if=/dev/urandom', `of=${name}`,
        `bs=${bytes}`, 'count=1' ], { stdio: 'inherit' }).on('exit', code => {
            assert.strictEqual(code, 0);
            callback();
        });
}

function deleteFile(file, callback) {
    process.stdout.write(`rm ${file}\n`);
    proc.spawn('rm', [ `${file}`, ]).on('exit', () => {
        callback();
    });
}

function exec(args, done, exitCode) {
    let exit = exitCode;
    if (exit === undefined) {
        exit = 0;
    }
    let av = args;
    if (isIronman) {
        av = args.concat(isIronman);
    }
    process.stdout.write(`${program} ${av}\n`);
    proc.spawn(program, av, { stdio: 'inherit' })
        .on('exit', code => {
            assert.strictEqual(code, exit);
            done();
        });
}

// Test stdout against expected output
function provideRawOutput(args, lineFinder, testString, cb) {
    let av = args;
    if (isIronman) {
        av = args.concat(isIronman);
    }
    process.stdout.write(`${program} ${av}\n`);
    const allData = [];
    const child = proc.spawn(program, av);
    child.stdout.on('data', (data) => {
        allData.push(data.toString().trim());
        process.stdout.write(data.toString());
    });
    child.on('close', () => {
        const lineOfInterest = allData.find((item) => {
            return item.indexOf(lineFinder) > -1;
        });
        const foundIt = lineOfInterest.indexOf(testString) > -1;
        return cb(foundIt);
    });
}

describe('s3cmd putBucket', () => {
    it('should put a valid bucket', (done) => {
        exec(['mb', `s3://${bucket}`, ], done);
    });

    it('put the same bucket, should fail', (done) => {
        exec(['mb', `s3://${bucket}`, ], done, 13);
    });

    it('put an invalid bucket, should fail', (done) => {
        exec(['mb', `s3://${nonexist}`, ], done, 11);
    });
});

describe(`s3cmd put and get bucket ACL's`, function aclBuck() {
    this.timeout(60000);
    // Note that s3cmd first gets the current ACL and then
    // sets the new one so by running setacl you are running a
    // get and a put
    it('should set a canned ACL', (done) => {
        exec(['setacl', `s3://${bucket}`, '--acl-public'], done);
    });

    it('should get canned ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}`], 'ACL', `*anon*: READ`,
        (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should set a specific ACL', (done) => {
        exec(['setacl', `s3://${bucket}`,
        `--acl-grant=write:${emailAccount}`], done);
    });

    it('should get specific ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}`], 'ACL',
        `${emailAccount}: WRITE`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });
});

describe('s3cmd getBucket', () => {
    it('should list existing bucket', (done) => {
        exec(['ls', `s3://${bucket}`, ], done);
    });

    it('list non existing bucket, should fail', (done) => {
        exec(['ls', `s3://${nonexist}`, ], done, 12);
    });
});

describe('s3cmd putObject', () => {
    before('create file to put', (done) => {
        createFile(upload, 1048576, done);
    });

    it('should put file in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('should put file with the same name in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('put file in non existing bucket, should fail', (done) => {
        exec(['put', upload, `s3://${nonexist}`, ], done, 12);
    });
});

describe('s3cmd getObject', function toto() {
    this.timeout(0);
    after('delete downloaded file', done => {
        deleteFile(download, done);
    });

    it('should get existing file in existing bucket', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download ], done);
    });

    it('downloaded file should equal uploaded file', (done) => {
        diff(upload, download, done);
    });

    it('get non existing file in existing bucket, should fail', (done) => {
        exec(['get', `s3://${bucket}/${nonexist}`, 'fail', ], done, 12);
    });

    it('get file in non existing bucket, should fail', (done) => {
        exec(['get', `s3://${nonexist}/${nonexist}`, 'fail2', ], done, 12);
    });
});

describe(`s3cmd put and get object ACL's`, function aclObj() {
    this.timeout(60000);
    // Note that s3cmd first gets the current ACL and then
    // sets the new one so by running setacl you are running a
    // get and a put
    it('should set a canned ACL', (done) => {
        exec(['setacl', `s3://${bucket}/${upload}`, '--acl-public'], done);
    });

    it('should get canned ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}/${upload}`], 'ACL',
        `*anon*: READ`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should set a specific ACL', (done) => {
        exec(['setacl', `s3://${bucket}/${upload}`,
            `--acl-grant=read:${emailAccount}`], done);
    });

    it('should get specific ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}/${upload}`], 'ACL',
        `${emailAccount}: READ`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should return error if set acl for ' +
        'nonexistent object', (done) => {
        exec(['setacl', `s3://${bucket}/${nonexist}`,
            '--acl-public'], done, 12);
    });
});

describe('s3cmd delObject', () => {
    it('should delete existing object', (done) => {
        exec(['rm', `s3://${bucket}/${upload}`, ], done);
    });

    it('delete non existing object, should fail', (done) => {
        exec(['rm', `s3://${bucket}/${nonexist}`, ], done, 12);
    });

    it('try to get the deleted object, should fail', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download, ], done, 12);
    });
});

describe('connector edge cases', function tata() {
    this.timeout(0);
    after('delete uploaded and downloaded file', done => {
        deleteFile(upload, () => {
            deleteFile(download, done);
        });
    });

    it('should put previous file in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('should get existing file in existing bucket', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download ], done);
    });
});

describe('s3cmd multipart upload', function titi() {
    this.timeout(0);
    before('create the multipart file', function createMPUFile(done) {
        this.timeout(60000);
        createFile(MPUpload, 16777216, done);
    });

    after('delete the multipart and the downloaded file', done => {
        deleteFile(MPUpload, () => {
            deleteFile(MPDownload, done);
        });
    });

    it('should put an object via a multipart upload', (done) => {
        exec(['put', MPUpload, `s3://${bucket}`, ], done);
    });

    it('should get an object that was put via multipart upload', (done) => {
        exec(['get', `s3://${bucket}/${MPUpload}`, MPDownload ], done);
    });

    it('downloaded file should equal uploaded file', (done) => {
        diff(MPUpload, MPDownload, done);
    });

    it('should delete multipart uploaded object', (done) => {
        exec(['rm', `s3://${bucket}/${MPUpload}`, ], done);
    });

    it('should not be able to get deleted object', (done) => {
        exec(['get', `s3://${bucket}/${MPUpload}`, download, ], done, 12);
    });
});

describe('s3cmd delBucket', () => {
    it('delete non-empty bucket, should fail', (done) => {
        exec(['rb', `s3://${bucket}`, ], done, 13);
    });

    it('should delete remaining object', (done) => {
        exec(['rm', `s3://${bucket}/${upload}`, ], done);
    });

    it('should delete empty bucket', (done) => {
        exec(['rb', `s3://${bucket}`, ], done);
    });

    it('try to get the deleted bucket, should fail', (done) => {
        exec(['ls', `s3://${bucket}`, ], done, 12);
    });
});
<commit_msg>Test: PUT, GET and DEL 0B objects<commit_after>'use strict'; // eslint-disable-line strict

const proc = require('child_process');
const process = require('process');
const assert = require('assert');

const program = 's3cmd';
const upload = 'test1MB';
const emptyUpload = 'Utest0B';
const emptyDownload = 'Dtest0B';
const download = 'tmpfile';
const MPUpload = 'test16MB';
const MPDownload = 'MPtmpfile';
const bucket = 'universe';
const nonexist = 'VOID';
const emailAccount = 'sampleAccount1@sampling.com';

const isIronman = process.env.IP ? ['-c', `${__dirname}/s3cfg`, ] : null;

function diff(putFile, receivedFile, done) {
    process.stdout.write(`diff ${putFile} ${receivedFile}\n`);
    proc.spawn('diff', [ putFile, receivedFile, ]).on('exit', code => {
        assert.strictEqual(code, 0);
        done();
    });
}

function createFile(name, bytes, callback) {
    process.stdout.write(`dd if=/dev/urandom of=${name} bs=${bytes} count=1\n`);
    proc.spawn('dd', [ 'if=/dev/urandom', `of=${name}`,
        `bs=${bytes}`, 'count=1' ], { stdio: 'inherit' }).on('exit', code => {
            assert.strictEqual(code, 0);
            callback();
        });
}

function createEmptyFile(name, callback) {
    process.stdout.write(`touch ${name}\n`);
    proc.spawn('touch', [ name ], { stdio: 'inherit' }).on('exit', code => {
        assert.strictEqual(code, 0);
        callback();
    });
}

function deleteFile(file, callback) {
    process.stdout.write(`rm ${file}\n`);
    proc.spawn('rm', [ `${file}`, ]).on('exit', () => {
        callback();
    });
}

function exec(args, done, exitCode) {
    let exit = exitCode;
    if (exit === undefined) {
        exit = 0;
    }
    let av = args;
    if (isIronman) {
        av = args.concat(isIronman);
    }
    process.stdout.write(`${program} ${av}\n`);
    proc.spawn(program, av, { stdio: 'inherit' })
        .on('exit', code => {
            assert.strictEqual(code, exit);
            done();
        });
}

// Test stdout against expected output
function provideRawOutput(args, lineFinder, testString, cb) {
    let av = args;
    if (isIronman) {
        av = args.concat(isIronman);
    }
    process.stdout.write(`${program} ${av}\n`);
    const allData = [];
    const child = proc.spawn(program, av);
    child.stdout.on('data', (data) => {
        allData.push(data.toString().trim());
        process.stdout.write(data.toString());
    });
    child.on('close', () => {
        const lineOfInterest = allData.find((item) => {
            return item.indexOf(lineFinder) > -1;
        });
        const foundIt = lineOfInterest.indexOf(testString) > -1;
        return cb(foundIt);
    });
}

describe('s3cmd putBucket', () => {
    it('should put a valid bucket', (done) => {
        exec(['mb', `s3://${bucket}`, ], done);
    });

    it('put the same bucket, should fail', (done) => {
        exec(['mb', `s3://${bucket}`, ], done, 13);
    });

    it('put an invalid bucket, should fail', (done) => {
        exec(['mb', `s3://${nonexist}`, ], done, 11);
    });
});

describe(`s3cmd put and get bucket ACL's`, function aclBuck() {
    this.timeout(60000);
    // Note that s3cmd first gets the current ACL and then
    // sets the new one so by running setacl you are running a
    // get and a put
    it('should set a canned ACL', (done) => {
        exec(['setacl', `s3://${bucket}`, '--acl-public'], done);
    });

    it('should get canned ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}`], 'ACL', `*anon*: READ`,
        (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should set a specific ACL', (done) => {
        exec(['setacl', `s3://${bucket}`,
        `--acl-grant=write:${emailAccount}`], done);
    });

    it('should get specific ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}`], 'ACL',
        `${emailAccount}: WRITE`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });
});

describe('s3cmd getBucket', () => {
    it('should list existing bucket', (done) => {
        exec(['ls', `s3://${bucket}`, ], done);
    });

    it('list non existing bucket, should fail', (done) => {
        exec(['ls', `s3://${nonexist}`, ], done, 12);
    });
});

describe('s3cmd putObject', () => {
    before('create file to put', (done) => {
        createFile(upload, 1048576, done);
    });

    it('should put file in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('should put file with the same name in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('put file in non existing bucket, should fail', (done) => {
        exec(['put', upload, `s3://${nonexist}`, ], done, 12);
    });
});

describe('s3cmd getObject', function toto() {
    this.timeout(0);
    after('delete downloaded file', done => {
        deleteFile(download, done);
    });

    it('should get existing file in existing bucket', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download ], done);
    });

    it('downloaded file should equal uploaded file', (done) => {
        diff(upload, download, done);
    });

    it('get non existing file in existing bucket, should fail', (done) => {
        exec(['get', `s3://${bucket}/${nonexist}`, 'fail', ], done, 12);
    });

    it('get file in non existing bucket, should fail', (done) => {
        exec(['get', `s3://${nonexist}/${nonexist}`, 'fail2', ], done, 12);
    });
});

describe(`s3cmd put and get object ACL's`, function aclObj() {
    this.timeout(60000);
    // Note that s3cmd first gets the current ACL and then
    // sets the new one so by running setacl you are running a
    // get and a put
    it('should set a canned ACL', (done) => {
        exec(['setacl', `s3://${bucket}/${upload}`, '--acl-public'], done);
    });

    it('should get canned ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}/${upload}`], 'ACL',
        `*anon*: READ`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should set a specific ACL', (done) => {
        exec(['setacl', `s3://${bucket}/${upload}`,
            `--acl-grant=read:${emailAccount}`], done);
    });

    it('should get specific ACL that was set', (done) => {
        provideRawOutput(['info', `s3://${bucket}/${upload}`], 'ACL',
        `${emailAccount}: READ`, (foundIt) => {
            assert(foundIt);
            done();
        });
    });

    it('should return error if set acl for ' +
        'nonexistent object', (done) => {
        exec(['setacl', `s3://${bucket}/${nonexist}`,
            '--acl-public'], done, 12);
    });
});

describe('s3cmd delObject', () => {
    it('should delete existing object', (done) => {
        exec(['rm', `s3://${bucket}/${upload}`, ], done);
    });

    it('delete non existing object, should fail', (done) => {
        exec(['rm', `s3://${bucket}/${nonexist}`, ], done, 12);
    });

    it('try to get the deleted object, should fail', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download, ], done, 12);
    });
});

describe('connector edge cases', function tata() {
    this.timeout(0);
    before('create file to put', (done) => {
        createEmptyFile(emptyUpload, done);
    });
    after('delete uploaded and downloaded file', (done) => {
        deleteFile(upload, () => {
            deleteFile(download, () => {
                deleteFile(emptyUpload, () => {
                    deleteFile(emptyDownload, done);
                });
            });
        });
    });

    it('should put previous file in existing bucket', (done) => {
        exec(['put', upload, `s3://${bucket}`, ], done);
    });

    it('should get existing file in existing bucket', (done) => {
        exec(['get', `s3://${bucket}/${upload}`, download ], done);
    });

    it('should put a 0 Bytes file', (done) => {
        exec(['put', emptyUpload, `s3://${bucket}`, ], done);
    });

    it('should get a 0 Bytes file', (done) => {
        exec(['get', `s3://${bucket}/${emptyUpload}`, emptyDownload, ], done);
    });

    it('should delete a 0 Bytes file', (done) => {
        exec(['del', `s3://${bucket}/${emptyUpload}`, ], done);
    });
});

describe('s3cmd multipart upload', function titi() {
    this.timeout(0);
    before('create the multipart file', function createMPUFile(done) {
        this.timeout(60000);
        createFile(MPUpload, 16777216, done);
    });

    after('delete the multipart and the downloaded file', done => {
        deleteFile(MPUpload, () => {
            deleteFile(MPDownload, done);
        });
    });

    it('should put an object via a multipart upload', (done) => {
        exec(['put', MPUpload, `s3://${bucket}`, ], done);
    });

    it('should get an object that was put via multipart upload', (done) => {
        exec(['get', `s3://${bucket}/${MPUpload}`, MPDownload ], done);
    });

    it('downloaded file should equal uploaded file', (done) => {
        diff(MPUpload, MPDownload, done);
    });

    it('should delete multipart uploaded object', (done) => {
        exec(['rm', `s3://${bucket}/${MPUpload}`, ], done);
    });

    it('should not be able to get deleted object', (done) => {
        exec(['get', `s3://${bucket}/${MPUpload}`, download, ], done, 12);
    });
});

describe('s3cmd delBucket', () => {
    it('delete non-empty bucket, should fail', (done) => {
        exec(['rb', `s3://${bucket}`, ], done, 13);
    });

    it('should delete remaining object', (done) => {
        exec(['rm', `s3://${bucket}/${upload}`, ], done);
    });

    it('should delete empty bucket', (done) => {
        exec(['rb', `s3://${bucket}`, ], done);
    });

    it('try to get the deleted bucket, should fail', (done) => {
        exec(['ls', `s3://${bucket}`, ], done, 12);
    });
});
<|endoftext|>


<commit_before>function Cart(config) {
  var $ = jQuery;
  Cart.instance = this; // this may be a list on the future;
  this.cartElem = $("#cart1")[0];
  this.cartElem.cartObj = this;
  this.contentBox = $("#cart1 .cart-content");
  this.itemsBox = $("#cart1 .cart-items");
  this.items = {};
  this.empty = !config.hasProducts;
  this.visible = false;
  $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} });
  if (!this.empty) {
    $(this.cartElem).show();
    me = this;
    $.ajax({
      url: '/plugin/shopping_cart/visibility',
      dataType: 'json',
      success: function(data, status, ajax){
        me.visible = /^true$/i.test(data);
        me.listProducts();
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Visibility - HTTP '+status, errorThrown);
      }
    });
    $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' });
  }
}

(function($){

  Cart.prototype.listProducts = function() {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/list',
      dataType: 'json',
      success: function(data, ststus, ajax){
        if ( !data.ok ) log.error(data.error);
        else me.addToList(data, true);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('List cart items - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.prototype.addToList = function(data, clear) {
    if( clear ) this.itemsBox.empty();
    var me = this;
    this.productsLength = data.products.length;
    for( var item,i=0; item=data.products[i]; i++ ) {
      this.items[item.id] = { price:item.price, quantity:item.quantity };
      this.updateTotal();
      var liId = "cart-item-"+item.id;
      var li = $("#"+liId);
      if( !li[0] ) li = $('

\n').appendTo(this.itemsBox);
      li.empty();
      $('

' +
        ''+ item.name +'' +
        '
' +
        '<input size="1" value="'+item.quantity+'" />'+ (item.price ? '× '+ item.price : '') +'
' +
        ' remove'
       ).appendTo(li);
      var input = $("input", li)[0];
      input.lastValue = input.value;
      input.productId = item.id;
      input.ajustSize = function() {
        var len = this.value.toString().length;
        if(len > 2) len--;
        this.style.width = len+"em";
      };
      input.ajustSize();
      input.onchange = function() {
        me.updateQuantity(this, this.productId, this.value);
      };
      // TODO: Scroll to newest item
      var liBg = li.css("background-color");
      li[0].style.backgroundColor = "#FF0";
      li.animate({ backgroundColor: liBg }, 1000);
    }

    if (!clear && this.empty) $(this.cartElem).show();
    if((!clear && this.empty) || (this.visible && clear)) {
      this.contentBox.hide();
      this.show();
    }
    this.empty = false;
  }

  Cart.prototype.updateQuantity = function(input, itemId, quantity) {
    quantity = parseInt(quantity);
    input.disabled = true;
    var originalBg = input.style.backgroundImage;
    input.style.backgroundImage = "url(/images/loading-small.gif)";
    var me = this;
    if( quantity == NaN ) return input.value = input.lastValue;
    $.ajax({
      url: '/plugin/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) {
          log.error(data.error);
          input.value = input.lastValue;
        }
        else {
          input.lastValue = quantity;
          me.items[itemId].quantity = quantity;
          me.updateTotal();
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Add item - HTTP '+status, errorThrown);
        input.value = input.lastValue;
      },
      complete: function(){
        input.disabled = false;
        input.style.backgroundImage = originalBg;
        input.ajustSize();
      }
    });
  }

  Cart.prototype.removeFromList = function(itemId) {
    $("#cart-item-"+itemId).slideUp(500, function() {$(this).remove()});
    delete this.items[itemId];
    this.updateTotal();
  }

  Cart.addItem = function(itemId, link) {
    if ( this.productsLength > 100 ) {
      // This limit protect the user from losing data on cookie limit.
      // This is NOT limiting to 100 products, is limiting to 100 kinds of products.
      alert(shoppingCartPluginL10n.maxNumberOfItens);
      return false;
    }
    link.intervalId = setInterval(function() {
      $(link).addClass('loading');
      steps = ['w', 'n', 'e', 's'];
      if( !link.step || link.step==3 ) link.step = 0;
      link.step++;
      $(link).button({ icons: { primary: 'ui-icon-arrowrefresh-1-'+steps[link.step]}, disable: true })
    }, 100);
    var stopBtLoading = function() {
      clearInterval(link.intervalId);
      $(link).removeClass('loading');
      $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false });
    };
    this.instance.addItem(itemId, stopBtLoading);
  }

  Cart.prototype.addItem = function(itemId, callback) {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/add/'+ itemId,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error('Shopping cart data failure', data.error);
        else me.addToList(data);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Add item - HTTP '+status, errorThrown);
      },
      complete: callback
    });
  }

  Cart.removeItem = function(itemId) {
    var message = this.instance.cartElem.getAttribute('data-l10nRemoveItem');
    if( confirm(message) ) this.instance.removeItem(itemId);
  }

  Cart.prototype.removeItem = function(itemId) {
    if ($("li", this.itemsBox).size() < 2) return this.clean();
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/remove/'+ itemId,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error(data.error);
        else me.removeFromList(data.product_id);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Remove item - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.toggle = function(link) {
    link.parentNode.parentNode.cartObj.toggle();
  }
  Cart.prototype.toggle = function() {
    this.visible ? this.hide() : this.show();
  }

  Cart.prototype.show = function() {
    $.ajax({
      url: '/plugin/shopping_cart/show',
      dataType: 'json',
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Show - HTTP '+status, errorThrown);
      }
    });
    this.visible = true;
    this.contentBox.slideDown(500);
    $(".cart-toggle .str-show", this.cartElem).hide();
    $(".cart-toggle .str-hide", this.cartElem).show();

  }
  Cart.prototype.hide = function() {
    $.ajax({
      url: '/plugin/shopping_cart/hide',
      dataType: 'json',
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Hide - HTTP '+status, errorThrown);
      }
    });
    this.visible = false;
    this.contentBox.slideUp(500);
    $(".cart-toggle .str-show", this.cartElem).show();
    $(".cart-toggle .str-hide", this.cartElem).hide();
  }

  Cart.prototype.updateTotal = function() {
    var total = 0;
    var currency, sep = "";
    for( var itemId in this.items ) {
      var item = this.items[itemId];
      if( item.price ) {
        currency = item.price.replace(/^([^0-9]+).*$/, "$1");
        sep = item.price.charAt(item.price.length-3);
        var price = item.price.replace(/[^0-9]/g,"");
        total += item.quantity * parseFloat(price);
      }
    }
    total = Math.round(total).toString().replace(/(..)$/, sep+"$1")
    $(".cart-total b", this.cartElem).text( ( (total!=0) ? currency+" "+total : "---" ) );
  }

  Cart.clean = function(link) {
    var message = this.instance.cartElem.getAttribute('data-l10nCleanCart');
    if( confirm(message) ) link.parentNode.parentNode.parentNode.cartObj.clean();
  }

  Cart.prototype.clean = function() {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/clean',
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error(data.error);
        else{
          me.items = {};
          $(me.cartElem).slideUp(500, function() {
            $(me.itemsBox).empty();
            me.hide();
            me.updateTotal();
            me.empty = true;
          });
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Remove item - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.send_request = function(form) {
    if($(form).valid())
      Cart.instance.send_request($(form).serialize());
    return false;
  }

  Cart.prototype.send_request = function(params) {
    var me = this;
    $.ajax({
      type: 'POST',
      url: '/plugin/shopping_cart/send_request',
      data: params,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) display_notice(data.error.message);
        else {
          me.clean();
          display_notice(data.message);
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Send request - HTTP '+status, errorThrown);
      },
      complete: function() {
        $.colorbox.close();
      }
    });
  }

  Cart.colorbox_close = function() {
    $.colorbox.close();
  }

  $(function(){

    $.ajax({
      url: "/plugin/shopping_cart/get",
      dataType: 'json',
      success: function(data) {
        new Cart(data);
        $('.cart-add-item').button({ icons: { primary: 'ui-icon-cart'} })
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Error getting shopping cart - HTTP '+status, errorThrown);
        if ( confirm(shoppingCartPluginL10n.getProblemConfirmReload) ) {
          document.location.reload();
        }
      }
    });
  });

})(jQuery);
<commit_msg>ShoppingCartPlugin do not worry about page unload<commit_after>function Cart(config) {
  var $ = jQuery;
  Cart.instance = this; // this may be a list on the future;
  this.cartElem = $("#cart1")[0];
  this.cartElem.cartObj = this;
  this.contentBox = $("#cart1 .cart-content");
  this.itemsBox = $("#cart1 .cart-items");
  this.items = {};
  this.empty = !config.hasProducts;
  this.visible = false;
  $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} });
  if (!this.empty) {
    $(this.cartElem).show();
    me = this;
    $.ajax({
      url: '/plugin/shopping_cart/visibility',
      dataType: 'json',
      success: function(data, status, ajax){
        me.visible = /^true$/i.test(data);
        me.listProducts();
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Visibility - HTTP '+status, errorThrown);
      }
    });
    $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' });
  }
}

(function($){

  Cart.prototype.listProducts = function() {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/list',
      dataType: 'json',
      success: function(data, ststus, ajax){
        if ( !data.ok ) log.error(data.error);
        else me.addToList(data, true);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('List cart items - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.prototype.addToList = function(data, clear) {
    if( clear ) this.itemsBox.empty();
    var me = this;
    this.productsLength = data.products.length;
    for( var item,i=0; item=data.products[i]; i++ ) {
      this.items[item.id] = { price:item.price, quantity:item.quantity };
      this.updateTotal();
      var liId = "cart-item-"+item.id;
      var li = $("#"+liId);
      if( !li[0] ) li = $('

\n').appendTo(this.itemsBox);
      li.empty();
      $('

' +
        ''+ item.name +'' +
        '
' +
        '<input size="1" value="'+item.quantity+'" />'+ (item.price ? '× '+ item.price : '') +'
' +
        ' remove'
       ).appendTo(li);
      var input = $("input", li)[0];
      input.lastValue = input.value;
      input.productId = item.id;
      input.ajustSize = function() {
        var len = this.value.toString().length;
        if(len > 2) len--;
        this.style.width = len+"em";
      };
      input.ajustSize();
      input.onchange = function() {
        me.updateQuantity(this, this.productId, this.value);
      };
      // TODO: Scroll to newest item
      var liBg = li.css("background-color");
      li[0].style.backgroundColor = "#FF0";
      li.animate({ backgroundColor: liBg }, 1000);
    }

    if (!clear && this.empty) $(this.cartElem).show();
    if((!clear && this.empty) || (this.visible && clear)) {
      this.contentBox.hide();
      this.show();
    }
    this.empty = false;
  }

  Cart.prototype.updateQuantity = function(input, itemId, quantity) {
    quantity = parseInt(quantity);
    input.disabled = true;
    var originalBg = input.style.backgroundImage;
    input.style.backgroundImage = "url(/images/loading-small.gif)";
    var me = this;
    if( quantity == NaN ) return input.value = input.lastValue;
    $.ajax({
      url: '/plugin/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) {
          log.error(data.error);
          input.value = input.lastValue;
        }
        else {
          input.lastValue = quantity;
          me.items[itemId].quantity = quantity;
          me.updateTotal();
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Add item - HTTP '+status, errorThrown);
        input.value = input.lastValue;
      },
      complete: function(){
        input.disabled = false;
        input.style.backgroundImage = originalBg;
        input.ajustSize();
      }
    });
  }

  Cart.prototype.removeFromList = function(itemId) {
    $("#cart-item-"+itemId).slideUp(500, function() {$(this).remove()});
    delete this.items[itemId];
    this.updateTotal();
  }

  Cart.addItem = function(itemId, link) {
    if ( this.productsLength > 100 ) {
      // This limit protect the user from losing data on cookie limit.
      // This is NOT limiting to 100 products, is limiting to 100 kinds of products.
      alert(shoppingCartPluginL10n.maxNumberOfItens);
      return false;
    }
    link.intervalId = setInterval(function() {
      $(link).addClass('loading');
      steps = ['w', 'n', 'e', 's'];
      if( !link.step || link.step==3 ) link.step = 0;
      link.step++;
      $(link).button({ icons: { primary: 'ui-icon-arrowrefresh-1-'+steps[link.step]}, disable: true })
    }, 100);
    var stopBtLoading = function() {
      clearInterval(link.intervalId);
      $(link).removeClass('loading');
      $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false });
    };
    this.instance.addItem(itemId, stopBtLoading);
  }

  Cart.prototype.addItem = function(itemId, callback) {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/add/'+ itemId,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error('Shopping cart data failure', data.error);
        else me.addToList(data);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Add item - HTTP '+status, errorThrown);
      },
      complete: callback
    });
  }

  Cart.removeItem = function(itemId) {
    var message = this.instance.cartElem.getAttribute('data-l10nRemoveItem');
    if( confirm(message) ) this.instance.removeItem(itemId);
  }

  Cart.prototype.removeItem = function(itemId) {
    if ($("li", this.itemsBox).size() < 2) return this.clean();
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/remove/'+ itemId,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error(data.error);
        else me.removeFromList(data.product_id);
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Remove item - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.toggle = function(link) {
    link.parentNode.parentNode.cartObj.toggle();
  }
  Cart.prototype.toggle = function() {
    this.visible ? this.hide() : this.show();
  }

  Cart.prototype.show = function() {
    $.ajax({
      url: '/plugin/shopping_cart/show',
      dataType: 'json',
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Show - HTTP '+status, errorThrown);
      }
    });
    this.visible = true;
    this.contentBox.slideDown(500);
    $(".cart-toggle .str-show", this.cartElem).hide();
    $(".cart-toggle .str-hide", this.cartElem).show();

  }
  Cart.prototype.hide = function() {
    $.ajax({
      url: '/plugin/shopping_cart/hide',
      dataType: 'json',
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Hide - HTTP '+status, errorThrown);
      }
    });
    this.visible = false;
    this.contentBox.slideUp(500);
    $(".cart-toggle .str-show", this.cartElem).show();
    $(".cart-toggle .str-hide", this.cartElem).hide();
  }

  Cart.prototype.updateTotal = function() {
    var total = 0;
    var currency, sep = "";
    for( var itemId in this.items ) {
      var item = this.items[itemId];
      if( item.price ) {
        currency = item.price.replace(/^([^0-9]+).*$/, "$1");
        sep = item.price.charAt(item.price.length-3);
        var price = item.price.replace(/[^0-9]/g,"");
        total += item.quantity * parseFloat(price);
      }
    }
    total = Math.round(total).toString().replace(/(..)$/, sep+"$1")
    $(".cart-total b", this.cartElem).text( ( (total!=0) ? currency+" "+total : "---" ) );
  }

  Cart.clean = function(link) {
    var message = this.instance.cartElem.getAttribute('data-l10nCleanCart');
    if( confirm(message) ) link.parentNode.parentNode.parentNode.cartObj.clean();
  }

  Cart.prototype.clean = function() {
    var me = this;
    $.ajax({
      url: '/plugin/shopping_cart/clean',
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) log.error(data.error);
        else{
          me.items = {};
          $(me.cartElem).slideUp(500, function() {
            $(me.itemsBox).empty();
            me.hide();
            me.updateTotal();
            me.empty = true;
          });
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Remove item - HTTP '+status, errorThrown);
      }
    });
  }

  Cart.send_request = function(form) {
    if($(form).valid())
      Cart.instance.send_request($(form).serialize());
    return false;
  }

  Cart.prototype.send_request = function(params) {
    var me = this;
    $.ajax({
      type: 'POST',
      url: '/plugin/shopping_cart/send_request',
      data: params,
      dataType: 'json',
      success: function(data, status, ajax){
        if ( !data.ok ) display_notice(data.error.message);
        else {
          me.clean();
          display_notice(data.message);
        }
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        log.error('Send request - HTTP '+status, errorThrown);
      },
      complete: function() {
        $.colorbox.close();
      }
    });
  }

  Cart.colorbox_close = function() {
    $.colorbox.close();
  }

  $(window).bind('beforeunload', function(){
    log('Page unload.');
    Cart.unloadingPage = true;
  });

  $(function(){

    $.ajax({
      url: "/plugin/shopping_cart/get",
      dataType: 'json',
      success: function(data) {
        new Cart(data);
        $('.cart-add-item').button({ icons: { primary: 'ui-icon-cart'} })
      },
      cache: false,
      error: function(ajax, status, errorThrown) {
        // Give some time to register page unload.
        setTimeout(function() {
          // page unload is not our problem.
          if (Cart.unloadingPage) {
            log('Page unload before cart load.');
          } else {
            log.error('Error getting shopping cart - HTTP '+status, errorThrown);
            if ( confirm(shoppingCartPluginL10n.getProblemConfirmReload) ) {
              document.location.reload();
            }
          }
        }, 100);
      }
    });
  });

})(jQuery);
<|endoftext|>


<commit_before>// Copyright 2016, EMC, Inc.

'use strict';

var di = require('di'),
    jwt = require('jsonwebtoken'),
    passport = require('passport'),
    JwtStrategy = require('passport-jwt').Strategy,
    LocalStrategy = require('passport-local').Strategy,
    BasicStrategy = require('passport-http').BasicStrategy,
    ExtractJwt = require('passport-jwt').ExtractJwt,
    AnonStrategy = require('passport-anonymous').Strategy;

var BAD_REQUEST_STATUS = 400;
var UNAUTHORIZED_STATUS = 401;
var ERROR_STATUS = 500;

module.exports = authServiceFactory;
di.annotate(authServiceFactory, new di.Provide('Auth.Services'));
di.annotate(authServiceFactory,
    new di.Inject(
        'Assert',
        'Services.Configuration',
        '_',
        'Services.Waterline'
    )
);

function authServiceFactory(
    assert,
    configuration,
    _,
    waterline
) {
    var redfishSessions = [];

    function AuthService() {
    }

    AuthService.prototype.init = function(){
        var self = this;

        self.algorithm = 'HS256';
        self.secretOrKey = '';
        self.ignoreTokenExpiration = false;
        self.encoder = 'base64';

        self.loadFromConf();

        self.jwtSignOptions = {
            algorithm : self.algorithm,
            secretOrKey : self.secretOrKey,
            expiresIn : self.expiresIn
        };

        self.jwtVerifyOptions = {
            algorithm : self.algorithm,
            secretOrKey : self.secretOrKey,
            ignoreExpiration : self.ignoreTokenExpiration
        };

        // We do not use passport.session middleware and run our strategies
        // with session: false, but this is a required function for req.logIn
        // to succeed.  The 'user' value below is the value passed to req.logIn
        // and it is returned to indicate success.
        passport.serializeUser(function(user, done) {
            done(null, user);
        });
    };

    AuthService.prototype.getPassportMidware = function () {
        var self = this;
        var passportMidware = passport.initialize();

        passport.use(new LocalStrategy(self.localStrategyAuth.bind(self)));
        passport.use(new JwtStrategy(
            _.merge(self.jwtVerifyOptions,
                    { jwtFromRequest: ExtractJwt.versionOneCompatibility({
                        authScheme: 'JWT',
                        tokenQueryParameterName: 'auth_token' })
                    }),
            self.jwtStrategyAuth.bind(self)));
        passport.use('redfish', new JwtStrategy(
            _.merge(self.jwtVerifyOptions,
                    { jwtFromRequest: ExtractJwt.fromHeader('x-auth-token') }),
            self.jwtStrategyRedfish.bind(self)));
        passport.use(new BasicStrategy(self.localStrategyAuth.bind(self)));
        passport.use(new AnonStrategy());

        return passportMidware;
    };

    AuthService.prototype.loadFromConf = function () {
        var secret = configuration.get('authTokenSecret', 'RackHDRocks!');
        assert.string(secret, 'Invalid token secret in configure file');
        this.secretOrKey = new Buffer(secret, this.encoder);

        this.expiresIn = configuration.get('authTokenExpireIn', 24 * 60 * 60 /* 24 hours */);
        assert.number(this.expiresIn, 'Invalid token expire value in configure file');

        if (this.expiresIn === 0){
            this.ignoreTokenExpiration = true;
        }
    };

    AuthService.prototype.localStrategyAuth = function (username, password, done) {
        waterline.localusers.findOne({username: username}).then(function(user) {
            if(user && user.comparePassword(password)) {
                return done(null, username);
            }
            return done(null, false, {message: 'Unauthorized'});
        }).catch(function(err) {
            return done(err);
        });
    };

    AuthService.prototype.createJwtToken = function (user) {
        var self = this;
        return jwt.sign({
            user: user
        },
        self.secretOrKey,
        self.jwtSignOptions
        );
    };

    AuthService.prototype.jwtStrategyAuth = function(jwtPayload, done) {
        waterline.localusers.findOne({username: jwtPayload.user}).then(function(user) {
            if(user) {
                return done(null, user.username);
            }
            return done(null, false, {message: 'Unauthorized'});
        }).catch(function(err) {
            return done(err);
        });
    };

    AuthService.prototype.jwtStrategyRedfish = function(jwtPayload, done) {
        var found = _.find(redfishSessions, function(entry) {
            return (entry.user === jwtPayload.user) &&
                   (entry.id === jwtPayload.id);
        });
        if( found ) {
            return done(null, found.user);
        } else {
            return done(null, false);
        }
    };

    AuthService.prototype.authMiddlewareJwt = function(req, res, next) {
        passport.authenticate(      // middleware for token authentication
            'jwt',
            {session: false},
            function errorHandler(err, user, challenges){
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    res.status(UNAUTHORIZED_STATUS).send({message: challenges.message});
                }
                else{   // nothing wrong, move on to next middleware
                    // remove the authToken query string if there are any.
                    delete req.query.auth_token; /* jshint ignore: line */
                    next();
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authMiddlewareLogin = function(req, res, next) {
        var self = this;

        passport.authenticate('local', {
                session: false
            },
            function errorHandler(err, user, challenges){ // Callback to handle auth error
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    if(challenges.message === 'Missing credentials'){
                        res.status(BAD_REQUEST_STATUS).send(challenges);
                    }
                    else if(challenges.message === 'Invalid username or password') {
                        res.status(UNAUTHORIZED_STATUS).send(challenges);
                    }
                    else {
                        res.status(UNAUTHORIZED_STATUS).send(challenges);
                    }
                }
                else {
                    var token = {
                        'token': self.createJwtToken(user)
                    };

                    res.send(token);
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authMiddlewareRedfish = function(req, res, next) {
        passport.authenticate(
            ['basic', 'redfish'],
            {session: false},
            function(err, user, challenges) {
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    res.status(UNAUTHORIZED_STATUS).send({message: challenges.message});
                }
                else {
                    next();
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authenticateWithMethod = function(req, res, next, method) {
        passport.authenticate(method, {session: false}, function(err, user, challenges) {
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    var res_message=[]
                    for(var i=0; i<challenges.length; i++){
                        if (challenges[i].message)
                            res_message.push({"method":method[i], "message": challenges[i].message})
                    }
                    res.status(UNAUTHORIZED_STATUS).send(res_message);
                }
                else {
                    req.login(user, function(err) {
                        if(err) {
                            return next(err);
                        }
                        next();
                    });
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.addRedfishSession = function(user, id) {
        var self = this;
        redfishSessions.push( {user: user, id: id} );
        return jwt.sign( {user: user, id: id}, self.secretOrKey, self.jwtSignOptions );
    };

    AuthService.prototype.getRedfishSession = function(id) {
        if(!id) {
            return redfishSessions;
        }
        return _.find(redfishSessions, function(entry) {
            return entry.id === id;
        });
    };

    AuthService.prototype.delRedfishSession = function(id) {
        _.remove(redfishSessions, function(entry) {
            return entry.id === id;
        });
    };

    return new AuthService();
}
<commit_msg>fix houndci issues<commit_after>// Copyright 2016, EMC, Inc.

'use strict';

var di = require('di'),
    jwt = require('jsonwebtoken'),
    passport = require('passport'),
    JwtStrategy = require('passport-jwt').Strategy,
    LocalStrategy = require('passport-local').Strategy,
    BasicStrategy = require('passport-http').BasicStrategy,
    ExtractJwt = require('passport-jwt').ExtractJwt,
    AnonStrategy = require('passport-anonymous').Strategy;

var BAD_REQUEST_STATUS = 400;
var UNAUTHORIZED_STATUS = 401;
var ERROR_STATUS = 500;

module.exports = authServiceFactory;
di.annotate(authServiceFactory, new di.Provide('Auth.Services'));
di.annotate(authServiceFactory,
    new di.Inject(
        'Assert',
        'Services.Configuration',
        '_',
        'Services.Waterline'
    )
);

function authServiceFactory(
    assert,
    configuration,
    _,
    waterline
) {
    var redfishSessions = [];

    function AuthService() {
    }

    AuthService.prototype.init = function(){
        var self = this;

        self.algorithm = 'HS256';
        self.secretOrKey = '';
        self.ignoreTokenExpiration = false;
        self.encoder = 'base64';

        self.loadFromConf();

        self.jwtSignOptions = {
            algorithm : self.algorithm,
            secretOrKey : self.secretOrKey,
            expiresIn : self.expiresIn
        };

        self.jwtVerifyOptions = {
            algorithm : self.algorithm,
            secretOrKey : self.secretOrKey,
            ignoreExpiration : self.ignoreTokenExpiration
        };

        // We do not use passport.session middleware and run our strategies
        // with session: false, but this is a required function for req.logIn
        // to succeed.  The 'user' value below is the value passed to req.logIn
        // and it is returned to indicate success.
        passport.serializeUser(function(user, done) {
            done(null, user);
        });
    };

    AuthService.prototype.getPassportMidware = function () {
        var self = this;
        var passportMidware = passport.initialize();

        passport.use(new LocalStrategy(self.localStrategyAuth.bind(self)));
        passport.use(new JwtStrategy(
            _.merge(self.jwtVerifyOptions,
                    { jwtFromRequest: ExtractJwt.versionOneCompatibility({
                        authScheme: 'JWT',
                        tokenQueryParameterName: 'auth_token' })
                    }),
            self.jwtStrategyAuth.bind(self)));
        passport.use('redfish', new JwtStrategy(
            _.merge(self.jwtVerifyOptions,
                    { jwtFromRequest: ExtractJwt.fromHeader('x-auth-token') }),
            self.jwtStrategyRedfish.bind(self)));
        passport.use(new BasicStrategy(self.localStrategyAuth.bind(self)));
        passport.use(new AnonStrategy());

        return passportMidware;
    };

    AuthService.prototype.loadFromConf = function () {
        var secret = configuration.get('authTokenSecret', 'RackHDRocks!');
        assert.string(secret, 'Invalid token secret in configure file');
        this.secretOrKey = new Buffer(secret, this.encoder);

        this.expiresIn = configuration.get('authTokenExpireIn', 24 * 60 * 60 /* 24 hours */);
        assert.number(this.expiresIn, 'Invalid token expire value in configure file');

        if (this.expiresIn === 0){
            this.ignoreTokenExpiration = true;
        }
    };

    AuthService.prototype.localStrategyAuth = function (username, password, done) {
        waterline.localusers.findOne({username: username}).then(function(user) {
            if(user && user.comparePassword(password)) {
                return done(null, username);
            }
            return done(null, false, {message: 'Unauthorized'});
        }).catch(function(err) {
            return done(err);
        });
    };

    AuthService.prototype.createJwtToken = function (user) {
        var self = this;
        return jwt.sign({
            user: user
        },
        self.secretOrKey,
        self.jwtSignOptions
        );
    };

    AuthService.prototype.jwtStrategyAuth = function(jwtPayload, done) {
        waterline.localusers.findOne({username: jwtPayload.user}).then(function(user) {
            if(user) {
                return done(null, user.username);
            }
            return done(null, false, {message: 'Unauthorized'});
        }).catch(function(err) {
            return done(err);
        });
    };

    AuthService.prototype.jwtStrategyRedfish = function(jwtPayload, done) {
        var found = _.find(redfishSessions, function(entry) {
            return (entry.user === jwtPayload.user) &&
                   (entry.id === jwtPayload.id);
        });
        if( found ) {
            return done(null, found.user);
        } else {
            return done(null, false);
        }
    };

    AuthService.prototype.authMiddlewareJwt = function(req, res, next) {
        passport.authenticate(      // middleware for token authentication
            'jwt',
            {session: false},
            function errorHandler(err, user, challenges){
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    res.status(UNAUTHORIZED_STATUS).send({message: challenges.message});
                }
                else{   // nothing wrong, move on to next middleware
                    // remove the authToken query string if there are any.
                    delete req.query.auth_token; /* jshint ignore: line */
                    next();
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authMiddlewareLogin = function(req, res, next) {
        var self = this;

        passport.authenticate('local', {
                session: false
            },
            function errorHandler(err, user, challenges){ // Callback to handle auth error
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    if(challenges.message === 'Missing credentials'){
                        res.status(BAD_REQUEST_STATUS).send(challenges);
                    }
                    else if(challenges.message === 'Invalid username or password') {
                        res.status(UNAUTHORIZED_STATUS).send(challenges);
                    }
                    else {
                        res.status(UNAUTHORIZED_STATUS).send(challenges);
                    }
                }
                else {
                    var token = {
                        'token': self.createJwtToken(user)
                    };

                    res.send(token);
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authMiddlewareRedfish = function(req, res, next) {
        passport.authenticate(
            ['basic', 'redfish'],
            {session: false},
            function(err, user, challenges) {
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    res.status(UNAUTHORIZED_STATUS).send({message: challenges.message});
                }
                else {
                    next();
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.authenticateWithMethod = function(req, res, next, method) {
        passport.authenticate(method, {session: false}, function(err, user, challenges) {
                if(err){
                    res.status(ERROR_STATUS).send({message: 'Internal server error'});
                }
                else if (!user){
                    var messages = [];
                    for(var i = 0; i < challenges.length; i += 1){
                        if (challenges[i].message){
                            messages.push({"method": method[i],
                                           "message": challenges[i].message});
                        }
                    }
                    res.status(UNAUTHORIZED_STATUS).send(messages);
                }
                else {
                    req.login(user, function(err) {
                        if(err) {
                            return next(err);
                        }
                        next();
                    });
                }
            }
        )(req, res, next);
    };

    AuthService.prototype.addRedfishSession = function(user, id) {
        var self = this;
        redfishSessions.push( {user: user, id: id} );
        return jwt.sign( {user: user, id: id}, self.secretOrKey, self.jwtSignOptions );
    };

    AuthService.prototype.getRedfishSession = function(id) {
        if(!id) {
            return redfishSessions;
        }
        return _.find(redfishSessions, function(entry) {
            return entry.id === id;
        });
    };

    AuthService.prototype.delRedfishSession = function(id) {
        _.remove(redfishSessions, function(entry) {
            return entry.id === id;
        });
    };

    return new AuthService();
}
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var clutch = require('extern/clutch');
var log = require('util/log');
var fs = require('fs');
var sys = require('sys');
var path = require('path');
var config = require('util/config');
var misc = require('util/misc');
var http = require('util/http');
var async = require('extern/async');

/**
 * Get the path to a bundle and optional file within the data root. Normalizes
 * the path and verifies that it is within the data root.
 *
 * @param {String} bundle The name of the bundle
 * @param {String} file   The (optional) name of the file
 * @returns The normalized path to the bundle (or file if specified) or null if
 *          the path is not within the data root
 */
function toPath(bundle, file) {
  /* TODO: move the core of this to utils */
  var p;
  var root = config.get().data_root;
  if (bundle && file) {
    p = path.join(root, 'bundles', bundle, file);
  }
  else {
    p = path.join(root, 'bundles', bundle);
  }

  p = path.normalize(p);

  if (p.indexOf(root) !== 0 ) {
    return null;
  }

  return p;
}

/**
 * Get a temporary file name within the same directory as the provided filename
 *
 * @param {String} filename The name of the file to create a temporary name for
 * @returns A path to a temporary file in the same directory as filename.
 */
function tempfileInDirectory(filename) {
  dname = path.dirname(filename);
  randname = ".cast_tmp_" + misc.randstr(8) + path.extname(filename);
  return path.join(dname, randname);
}

/**
 * Stat a path and verify that it exists. Return a 404 if it doesn't. If it
 * does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function path_or_404(p, res, cb) {
  if (p === null) {
    return http.return_error(res, 404, "File not found");
  }

  fs.stat(p, function(err, stats) {
    if (err) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Stat a path and verify that it is a file which exists. Return a 404 if it
 * doesn't. If it does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function file_or_404(p, res, cb) {
  path_or_404(p, res, function(stats) {
    if (!stats.isFile()) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Stat a path and verify that it is a directory which exists. Return a 404 if
 * it doesn't. If it does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function directory_or_404(p, res, cb) {
  path_or_404(p, res, function(stats) {
    if (!stats.isDirectory()) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Write a file from an HTTP stream and fire a callback on success or error.
 *
 * @param {String} dest The path to store the file to
 * @param {http.ServerRequest} istream The HTTP stream to read from
 * @param {Function} cb A callback that takes a possible error
 */
function pumpfilein(dest, istream, cb) {
  var fstream = fs.createWriteStream(dest, {'flags': 'w', 'encoding': 'binary', 'mode': 0644});

  /* We don't want to actually end the HTTP request (the client is supposed to
   * retry the request if that happens), rather we want to simply stop
   * accepting data while we send an error to make the client stop.
   */
  function close_fstream() {
    try {
      fstream.end();
    }
    catch (err) {
      log.err("Unable to close file: " + err.message);
    }
  }

  istream.on('error', function(err) {
    log.err("Error on HTTP stream: " + err.message);
    close_fstream();
    return cb(err);
  });

  fstream.on('error', function(err) {
    log.err("Error on file stream: " + err.message);
    close_fstream();
    return cb(err);
  });

  istream.on('end', function() {
    /* This is not ideal, but sys.pump and the HTTP request don't interact that
     * well together, so this emits a spurious and technically incorrect
     * 'close' event for when the HTTP request body has ended.
     */
    istream.emit('close');
  });

  sys.pump(istream, fstream, function() {
    cb();
  });
}

/**
 * Write a file from an HTTP stream via a temporary file.
 *
 * @param {String} dest The final path to store the file to
 * @param {http.ServerRequest} istream  The HTTP stream to read from
 * @param {Function} cb A callback which takes a possible error
 */
function pumpfilein_via_tempfile(dest, istream, cb) {
  var temp = tempfileInDirectory(dest);
  pumpfilein(temp, istream, function(err) {
    if (err) {
      return cb(err);
    }
    fs.rename(temp, dest, cb);
  });
}

/**
 * Write a file to an HTTP stream. Does not write the headers.
 *
 * @param {String} source The path to read the file from
 * @param {http.ServerResponse} ostream The HTTP Stream to write the file to
 * @param {Function} cb A callback which takes a possible error
 */
function pumpfileout(source, ostream, cb) {
  /* TODO: global configuration of the bufferSize setting */
  var fstream = fs.createReadStream(source, {'bufferSize': 1024 * 64});

  function close_both() {
    (function() {
      try {
        ostream.end();
      }
      catch (err) {
        log.err("Unable to close outgoing HTTP stream: " + err.message);
      }
    })();

    (function() {
      try {
        fstream.end();
      }
      catch (err) {
        log.err("Unable to close file: " + err.message);
      }
    })();
  }

  ostream.on('error', function(err) {
    close_both();
    return cb(err);
  });

  fstream.on('error', function(err) {
    close_both();
    return cb(err);
  });

  sys.pump(fstream, ostream, function() {
    cb();
  });
}

/**
 * Receive an uploaded bundle file and store it.
 *
 * @param {http.ServerRequest} req  The HTTP request to read from
 * @param {http.ServerResponse} res The HTTP response to respond on
 * @param {String} bundle The name of the bundle to store
 * @param {String} file   The name of the file to store
 */
function upload(req, res, bundle, file) {
  var p = toPath(bundle, file);
  pumpfilein_via_tempfile(p, req, function(err) {
    if (err) {
      return http.return_error(res, 500, "Error storing file: " + err.message);
    }
    res.writeHead(204, {});
    res.end();
  });
}

/**
 * Send a requested bundle file
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to write to
 * @param {String} bundle The name of the bundle to read
 * @param {String} file   The name of the file to read
 */
function download(req, res, bundle, file) {
  var p = toPath(bundle, file);
  return file_or_404(p, res, function(stats) {
    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-Length': stats.size
    });
    pumpfileout(p, res, function(err) {
      if (err) {
        // The header is already written, there isn't much we can do
        log.err("Error streaming file: " + err);
      }
      // End the response in any case
      res.end();
    });
  });
}

/**
 * Remove a bundle file
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 * @param {String} bundle The name of the bundle to delete from
 * @param {String} file   The name of the file to delete
 */
function remove(req, res, bundle, file) {
  var p = toPath(bundle, file);
  return file_or_404(p, res, function() {
    fs.unlink(p, function(err) {
      if (err) {
        return http.return_error(res, 500, "Error removing bundle: " + err.message);
      }
      res.writeHead(204, {});
      res.end();
    });
  });
}

/**
 * List files within a bundle
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 * @param {String} bundle The name of the bundle to delete from
 */
function list_files(req, res, bundle) {
  var p = toPath(bundle);
  directory_or_404(p, res, function() {
    fs.readdir(p, function(err, files) {
      if (err) {
        // This could happen in the course of normal operations, so 404
        log.err("Error reading directory: " + err.message);
        return http.return_error(res, 404, "File not found");
      }

      var filedata = [];

      // Build a list of files in this directory
      async.forEach(files, function(file, callback) {
        var fpath = path.join(p, file);

        fs.stat(fpath, function(err, stats) {
          if (err) {
            log.warning("Error stat-ing file: " + err.message);
          }

          else if (stats.isFile()) {
            // TODO: Do we want anything besides the name of the file?
            filedata.push(file);
          }
          return callback();
        });
      },
      function(err) {
        return http.return_json(res, 200, filedata);
      });
    });
  });
}

/**
 * List available bundles
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 */
function list_bundles(req, res) {
  var p = path.join(config.get().data_root, "bundles");
  directory_or_404(p, res, function() {
    fs.readdir(p, function(err, files) {
      if (err) {
        // This should NOT happen in the course of normal operations
        log.err("Error reading directory: " + err.message);
        return http.return_error(res, 500, "Error reading directory");
      }

      var dirs = [];

      // Build a list of directories in this directory
      async.forEach(files, function(file, callback) {
        var fpath = path.join(p, file);

        fs.stat(fpath, function(err, stats) {
          if (err) {
            log.warning("Error stat-ing file: " + err.message);
          }

          else if (stats.isDirectory()) {
            // TODO: Do we want anything besides the name of the bundle?
            dirs.push(file);
          }
          return callback();
        });
      },
      function(err) {
        return http.return_json(res, 200, dirs);
      });
    });
  });
}

exports.urls = clutch.route([
                              ['PUT /(.+)/(.+)$', upload],
                              ['GET /(.+)/(.+)$', download],
                              ['DELETE /(.+)/(.+)$', remove],
                              ['GET /(.+)/$', list_files],
                              ['GET /$', list_bundles]
                              ]);
<commit_msg>Improved bundle uploading validation<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var clutch = require('extern/clutch');
var log = require('util/log');
var fs = require('fs');
var sys = require('sys');
var path = require('path');
var config = require('util/config');
var misc = require('util/misc');
var http = require('util/http');
var async = require('extern/async');

/**
 * Given a bundle name and a bundle string, return a validated and normalized
 * path to a (not necessarily existing) file. Return false on invalid paths.
 *
 * @param {String} bundle The name of the bundle
 * @param {String} file The name of the file
 *
 * @returns The normalized file path, or false on invalid paths
 */
function file_path(bundle, file) {
  var bundleroot = path.join(config.get().data_root, 'bundles');
  var bpath = path.normalize(path.join(bundleroot, bundle));
  var fpath = path.normalize(path.join(bundleroot, bundle, file));
  if ((path.dirname(bpath) === bundleroot) && (path.dirname(fpath) === bpath)) {
    return fpath;
  };
  return false;
}

/**
 * Get the path to a bundle and optional file within the data root. Normalizes
 * the path and verifies that it is within the data root.
 *
 * @param {String} bundle The name of the bundle
 * @param {String} file   The (optional) name of the file
 * @returns The normalized path to the bundle (or file if specified) or null if
 *          the path is not within the data root
 */
function toPath(bundle, file) {
  /* TODO: move the core of this to utils */
  var p;
  var root = config.get().data_root;
  if (bundle && file) {
    p = path.join(root, 'bundles', bundle, file);
  }
  else {
    p = path.join(root, 'bundles', bundle);
  }

  p = path.normalize(p);

  if (p.indexOf(root) !== 0 ) {
    return null;
  }

  return p;
}

/**
 * Get a temporary file name within the same directory as the provided filename
 *
 * @param {String} filename The name of the file to create a temporary name for
 * @returns A path to a temporary file in the same directory as filename.
 */
function tempfileInDirectory(filename) {
  dname = path.dirname(filename);
  randname = ".cast_tmp_" + misc.randstr(8) + path.extname(filename);
  return path.join(dname, randname);
}

/**
 * Stat a path and verify that it exists. Return a 404 if it doesn't. If it
 * does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function path_or_404(p, res, cb) {
  if (p === null) {
    return http.return_error(res, 404, "File not found");
  }

  fs.stat(p, function(err, stats) {
    if (err) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Stat a path and verify that it is a file which exists. Return a 404 if it
 * doesn't. If it does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function file_or_404(p, res, cb) {
  path_or_404(p, res, function(stats) {
    if (!stats.isFile()) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Stat a path and verify that it is a directory which exists. Return a 404 if
 * it doesn't. If it does, fire the given callback with the stat object.
 *
 * @param {String} p  The path to verify
 * @param {http.ServerResponse} res The response to potentially send the 404 to
 * @param {Function} cb An optional callback which takes the stat object as
 *                      its only argument.
 */
function directory_or_404(p, res, cb) {
  path_or_404(p, res, function(stats) {
    if (!stats.isDirectory()) {
      return http.return_error(res, 404, "File not found");
    }
    return cb(stats);
  });
}

/**
 * Write a file from an HTTP stream and fire a callback on success or error.
 *
 * @param {String} dest The path to store the file to
 * @param {http.ServerRequest} istream The HTTP stream to read from
 * @param {Function} cb A callback that takes a possible error
 */
function pumpfilein(dest, istream, received, completed, cb) {
  var fstream = fs.createWriteStream(dest, {'flags': 'w', 'encoding': 'binary', 'mode': 0644});

  function close_fstream() {
    try {
      fstream.end();
    }
    catch (err) {
      log.err("Unable to close file: " + err.message);
    }
  }

  fstream.on('error', function(err) {
    log.err("Error on file stream: " + err.message);
    close_fstream();
    return cb(err);
  });

  /* If the request has already been completed, we don't need to actually use
   * the pump. Instead we simply write all of the received buffer chunks to the
   * file stream, and return on success. On error we mooch off of the error
   * listener registered above.
   */
  if (completed) {
    fstream.on('close', function() {
      return cb();
    });

    for (var i = 0; i < received.length; i++) {
      fstream.write(received[i]);
    }

    fstream.end();
    return;
  }

  istream.on('error', function(err) {
    log.err("Error on HTTP stream: " + err.message);
    close_fstream();
    return cb(err);
  });

  istream.on('end', function() {
    /* This is not ideal, but sys.pump and the HTTP request don't interact that
     * well together, so this emits a spurious and technically incorrect
     * 'close' event for when the HTTP request body has ended.
     */
    istream.emit('close');
  });

  for (var i = 0; i < received.length; i++) {
    fstream.write(received[i]);
  }

  sys.pump(istream, fstream, function() {
    cb();
  });
}

/**
 * Write a file from an HTTP stream via a temporary file.
 *
 * @param {String} dest The final path to store the file to
 * @param {http.ServerRequest} istream  The HTTP stream to read from
 * @param {Function} cb A callback which takes a possible error
 */
function pumpfilein_via_tempfile(dest, istream, received, completed, cb) {
  var temp = tempfileInDirectory(dest);
  pumpfilein(temp, istream, received, completed, function(err) {
    if (err) {
      return cb(err);
    }
    fs.rename(temp, dest, cb);
  });
}

/**
 * Write a file to an HTTP stream. Does not write the headers.
 *
 * @param {String} source The path to read the file from
 * @param {http.ServerResponse} ostream The HTTP Stream to write the file to
 * @param {Function} cb A callback which takes a possible error
 */
function pumpfileout(source, ostream, cb) {
  /* TODO: global configuration of the bufferSize setting */
  var fstream = fs.createReadStream(source, {'bufferSize': 1024 * 64});

  function close_both() {
    (function() {
      try {
        ostream.end();
      }
      catch (err) {
        log.err("Unable to close outgoing HTTP stream: " + err.message);
      }
    })();

    (function() {
      try {
        fstream.end();
      }
      catch (err) {
        log.err("Unable to close file: " + err.message);
      }
    })();
  }

  ostream.on('error', function(err) {
    close_both();
    return cb(err);
  });

  fstream.on('error', function(err) {
    close_both();
    return cb(err);
  });

  sys.pump(fstream, ostream, function() {
    cb();
  });
}

/**
 * Receive an uploaded bundle file and store it.
 *
 * @param {http.ServerRequest} req  The HTTP request to read from
 * @param {http.ServerResponse} res The HTTP response to respond on
 * @param {String} bundle The name of the bundle to store
 * @param {String} file   The name of the file to store
 */
function upload(req, res, bundle, file) {
  // We must pause the request until we decide what to do with it
  req.pause();

  // Some data will still arrive, so buffer it
  var received = [];
  var completed = false;

  function on_data(data) {
    received.push(data);
  }
  req.on('data', on_data);

  function on_end() {
    completed = true;
  }
  req.on('end', on_end);

  // Validate the path
  var p = file_path(bundle, file);
  if (!p) {
    return http.return_erro(res, "404", "File not found");
  }

  // Make sure the bundle exists
  directory_or_404(path.dirname(p), res, function() {
    // Disable the buffering and resume the request
    req.removeListener('data', on_data);
    req.removeListener('end', on_end);
    req.resume();
    pumpfilein_via_tempfile(p, req, received, completed, function(err) {
      if (err) {
        return http.return_error(res, 500, "Error storing file: " + err.message);
      }
      res.writeHead(204, {});
      res.end();
    });
  });
}

/**
 * Send a requested bundle file
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to write to
 * @param {String} bundle The name of the bundle to read
 * @param {String} file   The name of the file to read
 */
function download(req, res, bundle, file) {
  var p = toPath(bundle, file);
  return file_or_404(p, res, function(stats) {
    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-Length': stats.size
    });
    pumpfileout(p, res, function(err) {
      if (err) {
        // The header is already written, there isn't much we can do
        log.err("Error streaming file: " + err);
      }
      // End the response in any case
      res.end();
    });
  });
}

/**
 * Remove a bundle file
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 * @param {String} bundle The name of the bundle to delete from
 * @param {String} file   The name of the file to delete
 */
function remove(req, res, bundle, file) {
  var p = toPath(bundle, file);
  return file_or_404(p, res, function() {
    fs.unlink(p, function(err) {
      if (err) {
        return http.return_error(res, 500, "Error removing bundle: " + err.message);
      }
      res.writeHead(204, {});
      res.end();
    });
  });
}

/**
 * List files within a bundle
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 * @param {String} bundle The name of the bundle to delete from
 */
function list_files(req, res, bundle) {
  var p = toPath(bundle);
  directory_or_404(p, res, function() {
    fs.readdir(p, function(err, files) {
      if (err) {
        // This could happen in the course of normal operations, so 404
        log.err("Error reading directory: " + err.message);
        return http.return_error(res, 404, "File not found");
      }

      var filedata = [];

      // Build a list of files in this directory
      async.forEach(files, function(file, callback) {
        var fpath = path.join(p, file);

        fs.stat(fpath, function(err, stats) {
          if (err) {
            log.warning("Error stat-ing file: " + err.message);
          }

          else if (stats.isFile()) {
            // TODO: Do we want anything besides the name of the file?
            filedata.push(file);
          }
          return callback();
        });
      },
      function(err) {
        return http.return_json(res, 200, filedata);
      });
    });
  });
}

/**
 * List available bundles
 *
 * @param {http.ServerRequest} req  The HTTP request to respond to
 * @param {http.ServerResponse} res The HTTP response to respond to
 */
function list_bundles(req, res) {
  var p = path.join(config.get().data_root, "bundles");
  directory_or_404(p, res, function() {
    fs.readdir(p, function(err, files) {
      if (err) {
        // This should NOT happen in the course of normal operations
        log.err("Error reading directory: " + err.message);
        return http.return_error(res, 500, "Error reading directory");
      }

      var dirs = [];

      // Build a list of directories in this directory
      async.forEach(files, function(file, callback) {
        var fpath = path.join(p, file);

        fs.stat(fpath, function(err, stats) {
          if (err) {
            log.warning("Error stat-ing file: " + err.message);
          }

          else if (stats.isDirectory()) {
            // TODO: Do we want anything besides the name of the bundle?
            dirs.push(file);
          }
          return callback();
        });
      },
      function(err) {
        return http.return_json(res, 200, dirs);
      });
    });
  });
}

exports.urls = clutch.route([
                              ['PUT /(.+)/(.+)$', upload],
                              ['GET /(.+)/(.+)$', download],
                              ['DELETE /(.+)/(.+)$', remove],
                              ['GET /(.+)/$', list_files],
                              ['GET /$', list_bundles]
                              ]);
<|endoftext|>


<commit_before>wwt.app.factory(
  'Community',
  [
    '$http',
    '$q',
    '$timeout',
    'Util',

    function ($http, $q, $timeout, util) {
      var api = {
	getRoot: getRoot,
	getChildren: getChildren

      };

      var root,
	  rootFolders,
	  openCollectionsFolder;

      function getRoot() {
	var deferred = $q.defer();
	initPromise.then(function (folders) {
	  rootFolders = folders;
	  $.each(folders, function(i,item) {
	    if (item.get_thumbnailUrl) {
	      item.thumb = item.get_thumbnailUrl();
	    }
	  });

	  deferred.resolve(folders);
	});

	return deferred.promise;
      }

      function getChildren(obj) {
	var deferred = $q.defer();

	obj.childLoadCallback(function () {
	  var children = obj.get_children();
	  $.each(children, function (i, item) {
	    item.guid = obj.guid + '.' + (item.get_isFolder() ? item.get_name() : i);
	  });
	  deferred.resolve(transformData(children));
	});

	return deferred.promise;
      }

      var init = function () {
	var deferred = $q.defer();

	function tryInit() {
	  if (!wwt.wc) {
	    setTimeout(tryInit, 333);
	    return;
	  }
	  root = wwt.wc.createFolder();

	  root.loadFromUrl('//worldwidetelescope.org/Resource/Service/Payload', function () {
	    deferred.resolve(root.get_children());
	  });
	}

	tryInit();

	return deferred.promise;
      };

      var initPromise = init();

      return api;
    }
  ]
);
<commit_msg>dataproxy/Community.js: use rewritePlaceUrls here too<commit_after>wwt.app.factory(
  'Community',
  [
    '$http',
    '$q',
    '$timeout',
    'Util',

    function ($http, $q, $timeout, util) {
      var api = {
	getRoot: getRoot,
	getChildren: getChildren

      };

      var root,
	  rootFolders,
	  openCollectionsFolder;

      function getRoot() {
	var deferred = $q.defer();
	initPromise.then(function (folders) {
	  rootFolders = folders;
	  $.each(folders, function(i,item) {
            util.rewritePlaceUrls(item);
	  });

	  deferred.resolve(folders);
	});

	return deferred.promise;
      }

      function getChildren(obj) {
	var deferred = $q.defer();

	obj.childLoadCallback(function () {
	  var children = obj.get_children();

	  $.each(children, function (i, item) {
	    item.guid = obj.guid + '.' + (item.get_isFolder() ? item.get_name() : i);
            util.rewritePlaceUrls(item);
	  });

	  deferred.resolve(transformData(children));
	});

	return deferred.promise;
      }

      var init = function () {
	var deferred = $q.defer();

	function tryInit() {
	  if (!wwt.wc) {
	    setTimeout(tryInit, 333);
	    return;
	  }
	  root = wwt.wc.createFolder();

	  root.loadFromUrl('//worldwidetelescope.org/Resource/Service/Payload', function () {
	    deferred.resolve(root.get_children());
	  });
	}

	tryInit();

	return deferred.promise;
      };

      var initPromise = init();

      return api;
    }
  ]
);
<|endoftext|>


<commit_before>import bus from 'bus'
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import topicStore from 'lib/stores/topic-store/topic-store'

export default function TopicConnectorFactory (WrappedComponent) {
  return class TopicConnector extends Component {
    static displayName = `TopicConnector(${getDisplayName(WrappedComponent)})`

    static WrappedComponent = WrappedComponent

    constructor (props) {
      super(props)

      if (!props.params.id) {
        throw new Error('TopicConnector intented to be used on Topic View')
      }

      this.state = {
        topic: null
      }
    }

    componentDidMount () {
      const id = this.props.params.id
      bus.on(`topic-store:update:${id}`, this.handleTopicUpdate)
      bus.on(`topic-store:remove:${id}`, this.handleTopicRemove)
      this.fetchTopic()
    }

    componentWillUnmount () {
      const id = this.props.params.id
      bus.off(`topic-store:update:${id}`, this.handleTopicUpdate)
      bus.off(`topic-store:remove:${id}`, this.handleTopicRemove)
    }

    handleTopicUpdate = (topic) => {
      this.setState({ topic })
    }

    handleTopicRemove = () => {
      this.setState({ topic: null })
      this.fetchTopic()
    }

    fetchTopic = () => {
      topicStore.findOne(this.props.params.id)
        .then(this.handleTopicUpdate)
        .catch((err) => {
          if (err.status === 404) return browserHistory.push('/404')
          if (err.status === 401) return browserHistory.push('/401')
          throw err
        })
    }

    render () {
      return <WrappedComponent topic={this.state.topic} {...this.props} />
    }
  }
}

function getDisplayName (WrappedComponent) {
  return WrappedComponent.displayName ||
    WrappedComponent.name ||
    'Component'
}
<commit_msg>fix topic connector<commit_after>import bus from 'bus'
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import topicStore from 'lib/stores/topic-store/topic-store'

export default function TopicConnectorFactory (WrappedComponent) {
  return class TopicConnector extends Component {
    static displayName = `TopicConnector(${getDisplayName(WrappedComponent)})`

    static WrappedComponent = WrappedComponent

    state = {
      topic: null
    }

    constructor (props) {
      super(props)

      if (!props.params.id) {
        throw new Error('TopicConnector intented to be used on Topic View')
      }
    }

    componentWillReceiveProps (nextProps) {
      const { id } = nextProps.params
      this.fetchTopic(id)
    }

    componentDidMount () {
      const { id } = this.props.params
      bus.on(`topic-store:update:${id}`, this.handleTopicUpdate)
      bus.on(`topic-store:remove:${id}`, this.handleTopicRemove)
      this.fetchTopic(id)
    }

    componentWillUnmount () {
      const id = this.props.params.id
      bus.off(`topic-store:update:${id}`, this.handleTopicUpdate)
      bus.off(`topic-store:remove:${id}`, this.handleTopicRemove)
    }

    handleTopicUpdate = (topic) => {
      this.setState({ topic })
    }

    handleTopicRemove = () => {
      this.setState({ topic: null })
      this.fetchTopic(this.props.params.id)
    }

    fetchTopic = (id) => {
      topicStore.findOne(id)
        .then(this.handleTopicUpdate)
        .catch((err) => {
          if (err.status === 404) return browserHistory.push('/404')
          if (err.status === 401) return browserHistory.push('/401')
          throw err
        })
    }

    render () {
      return <WrappedComponent topic={this.state.topic} {...this.props} />
    }
  }
}

function getDisplayName (WrappedComponent) {
  return WrappedComponent.displayName ||
    WrappedComponent.name ||
    'Component'
}
<|endoftext|>