define(function(require, exports, module) { "use strict"; main.consumes = ["Plugin", "vfs.cache"]; main.provides = ["vfs.download"]; return main; function main(options, imports, register) { var Plugin = imports.Plugin; var cache = imports["vfs.cache"]; var error = require("http-error"); var Path = require("path"); /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); cache.registerExtension(function(vfs, callback) { var restful = vfs.restful.workspace; vfs.restful.workspace = function(req, res, next) { if (req.method == "GET" && "download" in req.uri.query) download(vfs.vfs, vfs.workspaceDir, req, res, next); else restful(req, res, next); }; callback(); }); function download(vfs, root, req, res, next) { var paths = req.uri.pathname.split(",").map(function(path) { return Path.join(root, Path.normalize(unescape(path).replace(/^(\/?\.\.)?\/?/, ""))); }); var path = paths[0]; var name = Path.basename(path); var filename = req.uri.query.download; if (!filename) { filename = name; if (!req.uri.query.isfile) { filename += (paths.length > 1 ? "[+" + (paths.length - 1) + "]" : "") + ".tar.gz"; } } var filenameHeader = "attachment; filename*=utf-8''" + escape(filename); var proc; req.on("close", function() { if (proc) proc.kill(); }); if (req.uri.query.isfile) { vfs.readfile(path, {}, function(err, meta){ if (err) { if (err.code == "ENOENT") return next(new error.NotFound("File '" + path + "' could not be found!")); else return next(err); } // once we receive data on stdout pipe it to the response meta.stream.once("data", function(data) { if (res.headerSent) return; res.writeHead(200, { "Content-Type": "octet/stream", "Content-Disposition": filenameHeader }); res.write(data); meta.stream.pipe(res); }); meta.stream.once("close", function() { if (res.headerSent) return; res.writeHead(200, { "Content-Type": "octet/stream", "Content-Disposition": filenameHeader }); res.end(); }); meta.stream.on("error", function(err){ res.writeHead(500); res.end(err.message); }); }); } else { var executable, args, contentType; if (/\.zip$/.test(filename)) { executable = "zip"; args = ["-r", "-", "--"]; contentType = "application/zip" } else { executable = "tar"; args = ["-zcf", "-", "--"]; contentType = "application/x-gzip" } // Find the longest common parent directory of all the paths. var cwd = null; paths.forEach(function (path) { if (!path) return; var dir = Path.dirname(path); if (!cwd) { cwd = dir; } else { var relative = Path.relative(cwd, dir).split(Path.sep); var i = 0; while (relative[i] === '..') { cwd = Path.resolve(cwd, '..'); i++; } } }); paths.forEach(function(path) { if (!path) return; path = Path.relative(cwd, path); if (process.platform == "win32") { // Quote the path to escape unusual characters and spaces. // NB: Double quotes are illegal within the actual path on Windows. path = '"' + path.replace(/"/g, "") + '"'; } args.push(path); }); vfs.spawn(executable, { args: args, cwd: cwd, windowsVerbatimArguments: true // Prevents Node from escaping the double quotes added above. }, function (err, meta) { if (err) return next(err); proc = meta.process; // once we receive data on stdout pipe it to the response proc.stdout.once("data", function (data) { if (res.headerSent) return; res.writeHead(200, { "Content-Type": contentType, "Content-Disposition": filenameHeader }); res.write(data); proc.stdout.pipe(res); }); var stderr = ""; proc.stderr.on("data", function (data) { stderr += data; }); proc.on("exit", function(code, signal) { if (res.headerSent) return; var err; if (code == 127) { err = new error.PreconditionFailed( "Your instance seems to be missing the '" + executable + "' utility\n" + "If you are using an SSH workspace, please do:\n" + " 'sudo apt-get install " + executable + "'"); } else if (code) { err = new error.InternalServerError( "'" + executable + "' utility failed with exit code " + code + " and stderr:/n'" + stderr + "'"); } else if (signal) { err = new error.InternalServerError( "'" + executable + "' utility was terminated by signal " + signal ); } if (err) { next(err); } }); }); } } plugin.freezePublicAPI({ // for testing only download: download }); register(null, { "vfs.download" : plugin }); } });