child = require "child_process" logger = require "logger-sharelatex" metrics = require "metrics-sharelatex" fs = require "fs" Path = require "path" _ = require("underscore") ONE_MEG = 1024 * 1024 module.exports = ArchiveManager = _isZipTooLarge: (source, callback = (err, isTooLarge)->)-> callback = _.once callback unzip = child.spawn("unzip", ["-l", source]) output = "" unzip.stdout.on "data", (d)-> output += d error = null unzip.stderr.on "data", (chunk) -> error ||= "" error += chunk unzip.on "error", (err) -> logger.error {err, source}, "unzip failed" if err.code == "ENOENT" logger.error "unzip command not found. Please check the unzip command is installed" callback(err) unzip.on "close", (exitCode) -> if error? error = new Error(error) logger.error err:error, source: source, "error checking zip size" lines = output.split("\n") lastLine = lines[lines.length - 2]?.trim() totalSizeInBytes = lastLine?.split(" ")?[0] totalSizeInBytesAsInt = parseInt(totalSizeInBytes) if !totalSizeInBytesAsInt? or isNaN(totalSizeInBytesAsInt) logger.err source:source, totalSizeInBytes:totalSizeInBytes, totalSizeInBytesAsInt:totalSizeInBytesAsInt, lastLine:lastLine, exitCode:exitCode, "error getting bytes of zip" return callback(new Error("error getting bytes of zip")) isTooLarge = totalSizeInBytes > (ONE_MEG * 300) callback(error, isTooLarge) extractZipArchive: (source, destination, _callback = (err) ->) -> callback = (args...) -> _callback(args...) _callback = () -> ArchiveManager._isZipTooLarge source, (err, isTooLarge)-> if err? logger.err err:err, "error checking size of zip file" return callback(err) if isTooLarge return callback(new Error("zip_too_large")) timer = new metrics.Timer("unzipDirectory") logger.log source: source, destination: destination, "unzipping file" unzip = child.spawn("unzip", [source, "-d", destination]) # don't remove this line, some zips need # us to listen on this for some unknow reason unzip.stdout.on "data", (d)-> error = null unzip.stderr.on "data", (chunk) -> error ||= "" error += chunk unzip.on "error", (err) -> logger.error {err, source, destination}, "unzip failed" if err.code == "ENOENT" logger.error "unzip command not found. Please check the unzip command is installed" callback(err) unzip.on "close", () -> timer.done() if error? error = new Error(error) logger.error err:error, source: source, destination: destination, "error unzipping file" callback(error) findTopLevelDirectory: (directory, callback = (error, topLevelDir) ->) -> fs.readdir directory, (error, files) -> return callback(error) if error? if files.length == 1 childPath = Path.join(directory, files[0]) fs.stat childPath, (error, stat) -> return callback(error) if error? if stat.isDirectory() return callback(null, childPath) else return callback(null, directory) else return callback(null, directory)