From 8c573ff6d829221f00d2204642803d6a4421e8b2 Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Sat, 18 Nov 2017 10:34:21 +0100 Subject: [PATCH] Unrar/tar and unzip on server side --- cps/static/js/archive.js | 364 -------------- cps/static/js/io.js | 484 ------------------ cps/static/js/kthoom.js | 215 ++++---- cps/static/js/unrar.js | 891 --------------------------------- cps/static/js/untar.js | 168 ------- cps/static/js/unzip.js | 621 ----------------------- cps/templates/config_edit.html | 9 +- cps/templates/readcbr.html | 4 +- cps/ub.py | 9 + cps/web.py | 225 ++++++--- optional-requirements.txt | 1 + 11 files changed, 267 insertions(+), 2724 deletions(-) delete mode 100644 cps/static/js/archive.js delete mode 100644 cps/static/js/io.js delete mode 100644 cps/static/js/unrar.js delete mode 100644 cps/static/js/untar.js delete mode 100644 cps/static/js/unzip.js diff --git a/cps/static/js/archive.js b/cps/static/js/archive.js deleted file mode 100644 index 28aae182..00000000 --- a/cps/static/js/archive.js +++ /dev/null @@ -1,364 +0,0 @@ -/** - * archive.js - * - * Provides base functionality for unarchiving. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - */ - -/* global bitjs */ - -var bitjs = bitjs || {}; -bitjs.archive = bitjs.archive || {}; - -(function() { - - // =========================================================================== - // Stolen from Closure because it's the best way to do Java-like inheritance. - bitjs.base = function(me, optMethodName, varArgs) { - var caller = arguments.callee.caller; - if (caller.superClass_) { - // This is a constructor. Call the superclass constructor. - return caller.superClass_.constructor.apply( - me, Array.prototype.slice.call(arguments, 1)); - } - - var args = Array.prototype.slice.call(arguments, 2); - var foundCaller = false; - for (var ctor = me.constructor; - ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { - if (ctor.prototype[optMethodName] === caller) { - foundCaller = true; - } else if (foundCaller) { - return ctor.prototype[optMethodName].apply(me, args); - } - } - - // If we did not find the caller in the prototype chain, - // then one of two things happened: - // 1) The caller is an instance method. - // 2) This method was not called by the right caller. - if (me[optMethodName] === caller) { - return me.constructor.prototype[optMethodName].apply(me, args); - } else { - throw Error( - "goog.base called from a method of one name " + - "to a method of a different name"); - } - }; - bitjs.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function TempCtor() {} - TempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new TempCtor(); - childCtor.prototype.constructor = childCtor; - }; - // =========================================================================== - - /** - * An unarchive event. - * - * @param {string} type The event type. - * @constructor - */ - bitjs.archive.UnarchiveEvent = function(type) { - /** - * The event type. - * - * @type {string} - */ - this.type = type; - }; - - /** - * The UnarchiveEvent types. - */ - bitjs.archive.UnarchiveEvent.Type = { - START: "start", - PROGRESS: "progress", - EXTRACT: "extract", - FINISH: "finish", - INFO: "info", - ERROR: "error" - }; - - /** - * Useful for passing info up to the client (for debugging). - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveInfoEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; - }; - bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); - - /** - * An unrecoverable error has occured. - * - * @param {string} msg The error message. - */ - bitjs.archive.UnarchiveErrorEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; - }; - bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); - - /** - * Start event. - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveStartEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); - }; - bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); - - /** - * Finish event. - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveFinishEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); - }; - bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); - - /** - * Progress event. - */ - bitjs.archive.UnarchiveProgressEvent = function( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive) - { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); - - this.currentFilename = currentFilename; - this.currentFileNumber = currentFileNumber; - this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; - this.totalFilesInArchive = totalFilesInArchive; - this.currentBytesUnarchived = currentBytesUnarchived; - this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; - }; - bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); - - /** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * - * interface UnarchivedFile { - * string filename - * TypedArray fileData - * } - * - */ - - /** - * Extract event. - */ - bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); - - /** - * @type {UnarchivedFile} - */ - this.unarchivedFile = unarchivedFile; - }; - bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); - - - /** - * Base class for all Unarchivers. - * - * @param {ArrayBuffer} arrayBuffer The Array Buffer. - * @param {string} optPathToBitJS Optional string for where the BitJS files are located. - * @constructor - */ - bitjs.archive.Unarchiver = function(arrayBuffer, optPathToBitJS) { - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - this.ab = arrayBuffer; - - /** - * The path to the BitJS files. - * @type {string} - * @private - */ - this.pathToBitJS_ = optPathToBitJS || ""; - - /** - * A map from event type to an array of listeners. - * @type {Map.} - */ - this.listeners_ = {}; - for (var type in bitjs.archive.UnarchiveEvent.Type) { - this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; - } - }; - - /** - * Private web worker initialized during start(). - * @type {Worker} - * @private - */ - bitjs.archive.Unarchiver.prototype.worker_ = null; - - /** - * This method must be overridden by the subclass to return the script filename. - * @return {string} The script filename. - * @protected. - */ - bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { - throw "Subclasses of AbstractUnarchiver must overload getScriptFileName()"; - }; - - /** - * Adds an event listener for UnarchiveEvents. - * - * @param {string} Event type. - * @param {function} An event handler function. - */ - bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { - if (type in this.listeners_) { - if (this.listeners_[type].indexOf(listener) === -1) { - this.listeners_[type].push(listener); - } - } - }; - - /** - * Removes an event listener. - * - * @param {string} Event type. - * @param {EventListener|function} An event listener or handler function. - */ - bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { - if (type in this.listeners_) { - var index = this.listeners_[type].indexOf(listener); - if (index !== -1) { - this.listeners_[type].splice(index, 1); - } - } - }; - - /** - * Receive an event and pass it to the listener functions. - * - * @param {bitjs.archive.UnarchiveEvent} e - * @private - */ - bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { - if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && - this.listeners_[e.type] instanceof Array) { - this.listeners_[e.type].forEach(function (listener) { - listener(e); - }); - if (e.type === bitjs.archive.UnarchiveEvent.Type.FINISH) { - this.worker_.terminate(); - } - } else { - console.log(e); - } - }; - - /** - * Starts the unarchive in a separate Web Worker thread and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.start = function() { - var me = this; - var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); - if (scriptFileName) { - this.worker_ = new Worker(scriptFileName); - - this.worker_.onerror = function(e) { - console.log("Worker error: message = " + e.message); - throw e; - }; - - this.worker_.onmessage = function(e) { - if (typeof e.data === "string") { - // Just log any strings the workers pump our way. - console.log(e.data); - } else { - // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' - // so that instanceof UnarchiveEvent returns true, but others do not. - me.handleWorkerEvent_(e.data); - } - }; - - this.worker_.postMessage({file: this.ab}); - } - }; - - /** - * Terminates the Web Worker for this Unarchiver and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.stop = function() { - if (this.worker_) { - this.worker_.terminate(); - } - }; - - - /** - * Unzipper - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Unzipper = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); - bitjs.archive.Unzipper.prototype.getScriptFileName = function() { - return "unzip.js"; - }; - - /** - * Unrarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Unrarrer = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); - bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { - return "unrar.js"; - }; - - /** - * Untarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Untarrer = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); - bitjs.archive.Untarrer.prototype.getScriptFileName = function() { - return "untar.js"; - }; - -})(); diff --git a/cps/static/js/io.js b/cps/static/js/io.js deleted file mode 100644 index 6cc4d81c..00000000 --- a/cps/static/js/io.js +++ /dev/null @@ -1,484 +0,0 @@ -/* - * io.js - * - * Provides readers for bit/byte streams (reading) and a byte buffer (writing). - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -/* global bitjs, Uint8Array */ - -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; - -(function() { - - // mask for getting the Nth bit (zero-based) - bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, - 0x1000, 0x2000, 0x4000, 0x8000]; - - // mask for getting N number of bits (0-8) - var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; - - - /** - * This bit stream peeks and consumes bits out of a binary stream. - * - * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * @param {boolean} rtl Whether the stream reads bits from the byte starting - * from bit 7 to 0 (true) or bit 0 to 7 (false). - * @param {Number} optOffset The offset into the ArrayBuffer - * @param {Number} optLength The length of this BitStream - */ - bitjs.io.BitStream = function(ab, rtl, optOffset, optLength) { - if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { - throw "Error! BitArray constructed with an invalid ArrayBuffer object"; - } - - var offset = optOffset || 0; - var length = optLength || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.bytePtr = 0; // tracks which byte we are on - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - this.peekBits = rtl ? this.peekBitsRtl : this.peekBitsLtr; - }; - - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit0 of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {number} The peeked bits, as an unsigned number. - */ - bitjs.io.BitStream.prototype.peekBitsLtr = function(n, movePointers) { - if (n <= 0 || typeof n !== typeof 1) { - return 0; - } - - var movePointers = movePointers || false; - var bytePtr = this.bytePtr; - var bitPtr = this.bitPtr; - var result = 0; - var bitsIn = 0; - var bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - var mask; - if (n >= numBitsLeftInThisByte) { - mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - n -= numBitsLeftInThisByte; - } else { - mask = (BITMASK[n] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += n; - bitsIn += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; - }; - - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {number} The peeked bits, as an unsigned number. - */ - bitjs.io.BitStream.prototype.peekBitsRtl = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var movePointers = movePointers || false; - var bytePtr = this.bytePtr; - var bitPtr = this.bitPtr; - var result = 0; - var bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - // return -1; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - if (n >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - n -= numBitsLeftInThisByte; - } - else { - result <<= n; - result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr)); - - bitPtr += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; - }; - - - /** - * Some voodoo magic. - */ - bitjs.io.BitStream.prototype.getBits = function() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + - ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); - }; - - - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. - * @return {number} The read bits, as an unsigned number. - */ - bitjs.io.BitStream.prototype.readBits = function(n) { - return this.peekBits(n, true); - }; - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {Uint8Array} The subarray. - */ - bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr !== 0) { - this.readBits(1); - } - - movePointers = movePointers || false; - var bytePtr = this.bytePtr; - // var bitPtr = this.bitPtr; - - var result = this.bytes.subarray(bytePtr, bytePtr + n); - - if (movePointers) { - this.bytePtr += n; - } - - return result; - }; - - - /** - * @param {number} n The number of bytes to read. - * @return {Uint8Array} The subarray. - */ - bitjs.io.BitStream.prototype.readBytes = function(n) { - return this.peekBytes(n, true); - }; - - - /** - * This object allows you to peek and consume bytes as numbers and strings - * out of an ArrayBuffer. In this buffer, everything must be byte-aligned. - * - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} optOffset The offset into the ArrayBuffer - * @param {number=} optLength The length of this BitStream - * @constructor - */ - bitjs.io.ByteStream = function(ab, optOffset, optLength) { - var offset = optOffset || 0; - var length = optLength || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.ptr = 0; - }; - - - /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer - * TODO: This apparently cannot read more than 4 bytes as a number? - * @param {number} n The number of bytes to peek at. - * @return {number} The n bytes interpreted as an unsigned number. - */ - bitjs.io.ByteStream.prototype.peekNumber = function(n) { - // TODO: return error if n would go past the end of the stream? - if (n <= 0 || typeof n !== typeof 1) { - return -1; - } - - var result = 0; - // read from last byte to first byte and roll them in - var curByte = this.ptr + n - 1; - while (curByte >= this.ptr) { - result <<= 8; - result |= this.bytes[curByte]; - --curByte; - } - return result; - }; - - - /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. - * @return {number} The n bytes interpreted as an unsigned number. - */ - bitjs.io.ByteStream.prototype.readNumber = function(n) { - var num = this.peekNumber( n ); - this.ptr += n; - return num; - }; - - - /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. - * @return {number} The bytes interpreted as a signed number. - */ - bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) { - var num = this.peekNumber(n); - var HALF = Math.pow(2, (n * 8) - 1); - var FULL = HALF * 2; - - if (num >= HALF) num -= FULL; - - return num; - }; - - - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. - * @return {number} The bytes interpreted as a signed number. - */ - bitjs.io.ByteStream.prototype.readSignedNumber = function(n) { - var num = this.peekSignedNumber(n); - this.ptr += n; - return num; - }; - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. - * @param {boolean} movePointers Whether to move the pointers. - * @return {Uint8Array} The subarray. - */ - bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return null; - } - - var result = this.bytes.subarray(this.ptr, this.ptr + n); - - if (movePointers) { - this.ptr += n; - } - - return result; - }; - - - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. - * @return {Uint8Array} The subarray. - */ - bitjs.io.ByteStream.prototype.readBytes = function(n) { - return this.peekBytes(n, true); - }; - - - /** - * Peeks at the next n bytes as a string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. - * @return {string} The next n bytes as a string. - */ - bitjs.io.ByteStream.prototype.peekString = function(n) { - if (n <= 0 || typeof n != typeof 1) { - return ""; - } - - var result = ""; - for (var p = this.ptr, end = this.ptr + n; p < end; ++p) { - result += String.fromCharCode(this.bytes[p]); - } - return result; - }; - - - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. - * @return {string} The next n bytes as a string. - */ - bitjs.io.ByteStream.prototype.readString = function(n) { - var strToReturn = this.peekString(n); - this.ptr += n; - return strToReturn; - }; - - - /** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - * @param {number} numBytes The number of bytes to allocate. - * @constructor - */ - bitjs.io.ByteBuffer = function(numBytes) { - if (typeof numBytes !== typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - this.data = new Uint8Array(numBytes); - this.ptr = 0; - }; - - - /** - * @param {number} b The byte to insert. - */ - bitjs.io.ByteBuffer.prototype.insertByte = function(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; - }; - - - /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ - bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; - }; - - - /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { - if (numBytes < 1) { - throw "Trying to write into too few bytes: " + numBytes; - } - if (num < 0) { - throw "Trying to write a negative number (" + num + - ") as an unsigned number to an ArrayBuffer"; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw "Trying to write " + num + " into only " + numBytes + " bytes"; - } - - // Roll 8-bits at a time into an array of bytes. - var bytes = []; - while (numBytes-- > 0) { - var eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - }; - - - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { - if (numBytes < 1) { - throw "Trying to write into too few bytes: " + numBytes; - } - - var HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw "Trying to write " + num + " into only " + numBytes + " bytes"; - } - - // Roll 8-bits at a time into an array of bytes. - var bytes = []; - while (numBytes-- > 0) { - var eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - }; - - - /** - * @param {string} str The ASCII string to write. - */ - bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) { - for (var i = 0; i < str.length; ++i) { - var curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 255) { - throw "Trying to write a non-ASCII string!"; - } - this.insertByte(curByte); - } - }; -})(); diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index 17ee8097..0dd41322 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -17,6 +17,8 @@ */ /* global bitjs */ +var start=0; + if (window.opera) { window.console.log = function(str) { opera.postError(str); @@ -127,11 +129,11 @@ var createURLFromArray = function(array, mimeType) { // This would save 25% on memory since base64-encoded strings are 4/3 the size of the binary kthoom.ImageFile = function(file) { this.filename = file.filename; - var fileExtension = file.filename.split(".").pop().toLowerCase(); + /*var fileExtension = file.filename.split(".").pop().toLowerCase(); var mimeType = fileExtension === "png" ? "image/png" : (fileExtension === "jpg" || fileExtension === "jpeg") ? "image/jpeg" : - fileExtension === "gif" ? "image/gif" : null; - this.dataURI = createURLFromArray(file.fileData, mimeType); + fileExtension === "gif" ? "image/gif" : null;*/ + this.dataURI = file.fileData; // createURLFromArray(file.fileData, mimeType); this.data = file; }; @@ -270,50 +272,22 @@ kthoom.setProgressMeter = function(pct, optLabel) { } function loadFromArrayBuffer(ab) { - var start = (new Date).getTime(); - var h = new Uint8Array(ab, 0, 10); - var pathToBitJS = "../../static/js/"; - if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar! - unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); - } else if (h[0] === 80 && h[1] === 75) { //PK (Zip) - unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); - } else { // Try with tar - unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); + var f=[]; + f.fileData=ab.content; + f.filename=ab.name; + // add any new pages based on the filename + if (imageFilenames.indexOf(f.filename) === -1) { + imageFilenames.push(f.filename); + imageFiles.push(new kthoom.ImageFile(f)); } - // Listen for UnarchiveEvents. - if (unarchiver) { - unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS, - function(e) { - var percentage = e.currentBytesUnarchived / e.totalUncompressedBytesInArchive; - totalImages = e.totalFilesInArchive; - kthoom.setProgressMeter(percentage, "Unzipping"); - // display nav - lastCompletion = percentage * 100; - }); - unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT, - function(e) { - // convert DecompressedFile into a bunch of ImageFiles - if (e.unarchivedFile) { - var f = e.unarchivedFile; - // add any new pages based on the filename - if (imageFilenames.indexOf(f.filename) === -1) { - imageFilenames.push(f.filename); - imageFiles.push(new kthoom.ImageFile(f)); - } - } - // display first page if we haven't yet - if (imageFiles.length === currentImage + 1) { - updatePage(); - } - }); - unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH, - function() { - var diff = ((new Date).getTime() - start) / 1000; - console.log("Unarchiving done in " + diff + "s"); - }); - unarchiver.start(); - } else { - alert("Some error"); + var percentage = (ab.page+1) / (ab.last+1); + totalImages = ab.last+1; + kthoom.setProgressMeter(percentage, "Unzipping"); + lastCompletion = percentage * 100; + + // display first page if we haven't yet + if (imageFiles.length === currentImage + 1) { + updatePage(); } } @@ -524,69 +498,90 @@ function keyHandler(evt) { } } -function init(filename) { - if (!window.FileReader) { - alert("Sorry, kthoom will not work with your browser because it does not support the File API. Please try kthoom with Chrome 12+ or Firefox 7+"); - } else { - var request = new XMLHttpRequest(); - request.open("GET", filename); - request.responseType = "arraybuffer"; - request.setRequestHeader("X-Test", "test1"); - request.setRequestHeader("X-Test", "test2"); - request.addEventListener("load", function(event) { - if (request.status >= 200 && request.status < 300) { - loadFromArrayBuffer(request.response); - } else { - console.warn(request.statusText, request.responseText); - } - }); - request.send(); - kthoom.initProgressMeter(); - document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : ""; - updateScale(true); - kthoom.loadSettings(); - $(document).keydown(keyHandler); - - $(window).resize(function() { - var f = (screen.width - innerWidth < 4 && screen.height - innerHeight < 4); - getElem("titlebar").className = f ? "main" : ""; - updateScale(); - }); - - $("#mainImage").click(function(evt) { - // Firefox does not support offsetX/Y so we have to manually calculate - // where the user clicked in the image. - var mainContentWidth = $("#mainContent").width(); - var mainContentHeight = $("#mainContent").height(); - var comicWidth = evt.target.clientWidth; - var comicHeight = evt.target.clientHeight; - var offsetX = (mainContentWidth - comicWidth) / 2; - var offsetY = (mainContentHeight - comicHeight) / 2; - var clickX = !!evt.offsetX ? evt.offsetX : (evt.clientX - offsetX); - var clickY = !!evt.offsetY ? evt.offsetY : (evt.clientY - offsetY); - - // Determine if the user clicked/tapped the left side or the - // right side of the page. - var clickedPrev = false; - switch (kthoom.rotateTimes) { - case 0: - clickedPrev = clickX < (comicWidth / 2); - break; - case 1: - clickedPrev = clickY < (comicHeight / 2); - break; - case 2: - clickedPrev = clickX > (comicWidth / 2); - break; - case 3: - clickedPrev = clickY > (comicHeight / 2); - break; - } - if (clickedPrev) { - showPrevPage(); - } else { - showNextPage(); - } - }); +function ImageLoadCallback(event) { + var jso=this.response; + if (jso.page !== jso.last) + { + // var secRequest = new XMLHttpRequest(); + this.open("GET", this.fileid + "/"+(jso.page+1)); + this.addEventListener("load",ImageLoadCallback); + this.send(); + } + else + { + var diff = ((new Date).getTime() - start)/1000; + console.log('Transfer done in ' + diff + 's'); } + loadFromArrayBuffer(jso); +} +function init(fileid) { + start = (new Date).getTime(); + var request = new XMLHttpRequest(); + request.open("GET", fileid); + request.responseType = "json"; + request.fileid=fileid.substring(0,fileid.length - 2); + request.addEventListener("load",ImageLoadCallback);/* function(event) { + var jso=request.response; + if (jso.page!=jso.length) + { + // var secRequest = new XMLHttpRequest(); + request.open("GET", fileid + "/../"+(jso.page+1)); + request.send(); + //secRequest.responseType = "json"; + //finished; + } + loadFromArrayBuffer(jso); + + // var byteArray = new Uint8Array(request.response); + // if you want to access the bytes: + });*/ + request.send(); + + kthoom.initProgressMeter(); + document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : ""; + updateScale(true); + kthoom.loadSettings(); + $(document).keydown(keyHandler); + + $(window).resize(function() { + var f = (screen.width - innerWidth < 4 && screen.height - innerHeight < 4); + getElem("titlebar").className = f ? "main" : ""; + updateScale(); + }); + + $("#mainImage").click(function(evt) { + // Firefox does not support offsetX/Y so we have to manually calculate + // where the user clicked in the image. + var mainContentWidth = $("#mainContent").width(); + var mainContentHeight = $("#mainContent").height(); + var comicWidth = evt.target.clientWidth; + var comicHeight = evt.target.clientHeight; + var offsetX = (mainContentWidth - comicWidth) / 2; + var offsetY = (mainContentHeight - comicHeight) / 2; + var clickX = !!evt.offsetX ? evt.offsetX : (evt.clientX - offsetX); + var clickY = !!evt.offsetY ? evt.offsetY : (evt.clientY - offsetY); + + // Determine if the user clicked/tapped the left side or the + // right side of the page. + var clickedPrev = false; + switch (kthoom.rotateTimes) { + case 0: + clickedPrev = clickX < (comicWidth / 2); + break; + case 1: + clickedPrev = clickY < (comicHeight / 2); + break; + case 2: + clickedPrev = clickX > (comicWidth / 2); + break; + case 3: + clickedPrev = clickY > (comicHeight / 2); + break; + } + if (clickedPrev) { + showPrevPage(); + } else { + showNextPage(); + } + }); } diff --git a/cps/static/js/unrar.js b/cps/static/js/unrar.js deleted file mode 100644 index f32fd6fa..00000000 --- a/cps/static/js/unrar.js +++ /dev/null @@ -1,891 +0,0 @@ -/** - * unrar.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * http://kthoom.googlecode.com/hg/docs/unrar.html - */ -/* global bitjs, importScripts */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts("io.js"); -importScripts("archive.js"); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -// shows a byte value as its hex representation -var nibble = "0123456789ABCDEF"; -var byteValueToHexString = function(num) { - return nibble[num>>4] + nibble[num & 0xF]; -}; -var twoByteValueToHexString = function(num) { - return nibble[(num>>12) & 0xF] + nibble[(num>>8) & 0xF] + nibble[(num>>4) & 0xF] + nibble[num & 0xF]; -}; - - -// Volume Types -// MARK_HEAD = 0x72; -var MAIN_HEAD = 0x73, - FILE_HEAD = 0x74, - // COMM_HEAD = 0x75, - // AV_HEAD = 0x76, - // SUB_HEAD = 0x77, - // PROTECT_HEAD = 0x78, - // SIGN_HEAD = 0x79, - // NEWSUB_HEAD = 0x7a, - ENDARC_HEAD = 0x7b; - -// bstream is a bit stream -var RarVolumeHeader = function(bstream) { - - var headPos = bstream.bytePtr; - // byte 1,2 - info("Rar Volume Header @" + bstream.bytePtr); - - this.crc = bstream.readBits(16); - info(" crc=" + this.crc); - - // byte 3 - this.headType = bstream.readBits(8); - info(" headType=" + this.headType); - - // Get flags - // bytes 4,5 - this.flags = {}; - this.flags.value = bstream.peekBits(16); - - info(" flags=" + twoByteValueToHexString(this.flags.value)); - switch (this.headType) { - case MAIN_HEAD: - this.flags.MHD_VOLUME = !!bstream.readBits(1); - this.flags.MHD_COMMENT = !!bstream.readBits(1); - this.flags.MHD_LOCK = !!bstream.readBits(1); - this.flags.MHD_SOLID = !!bstream.readBits(1); - this.flags.MHD_PACK_COMMENT = !!bstream.readBits(1); - this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT; - this.flags.MHD_AV = !!bstream.readBits(1); - this.flags.MHD_PROTECT = !!bstream.readBits(1); - this.flags.MHD_PASSWORD = !!bstream.readBits(1); - this.flags.MHD_FIRSTVOLUME = !!bstream.readBits(1); - this.flags.MHD_ENCRYPTVER = !!bstream.readBits(1); - bstream.readBits(6); // unused - break; - case FILE_HEAD: - this.flags.LHD_SPLIT_BEFORE = !!bstream.readBits(1); // 0x0001 - this.flags.LHD_SPLIT_AFTER = !!bstream.readBits(1); // 0x0002 - this.flags.LHD_PASSWORD = !!bstream.readBits(1); // 0x0004 - this.flags.LHD_COMMENT = !!bstream.readBits(1); // 0x0008 - this.flags.LHD_SOLID = !!bstream.readBits(1); // 0x0010 - bstream.readBits(3); // unused - this.flags.LHD_LARGE = !!bstream.readBits(1); // 0x0100 - this.flags.LHD_UNICODE = !!bstream.readBits(1); // 0x0200 - this.flags.LHD_SALT = !!bstream.readBits(1); // 0x0400 - this.flags.LHD_VERSION = !!bstream.readBits(1); // 0x0800 - this.flags.LHD_EXTTIME = !!bstream.readBits(1); // 0x1000 - this.flags.LHD_EXTFLAGS = !!bstream.readBits(1); // 0x2000 - bstream.readBits(2); // unused - info(" LHD_SPLIT_BEFORE = " + this.flags.LHD_SPLIT_BEFORE); - break; - default: - bstream.readBits(16); - } - - // byte 6,7 - this.headSize = bstream.readBits(16); - info(" headSize=" + this.headSize); - switch (this.headType) { - case MAIN_HEAD: - this.highPosAv = bstream.readBits(16); - this.posAv = bstream.readBits(32); - if (this.flags.MHD_ENCRYPTVER) { - this.encryptVer = bstream.readBits(8); - } - info("Found MAIN_HEAD with highPosAv=" + this.highPosAv + ", posAv=" + this.posAv); - break; - case FILE_HEAD: - this.packSize = bstream.readBits(32); - this.unpackedSize = bstream.readBits(32); - this.hostOS = bstream.readBits(8); - this.fileCRC = bstream.readBits(32); - this.fileTime = bstream.readBits(32); - this.unpVer = bstream.readBits(8); - this.method = bstream.readBits(8); - this.nameSize = bstream.readBits(16); - this.fileAttr = bstream.readBits(32); - - if (this.flags.LHD_LARGE) { - info("Warning: Reading in LHD_LARGE 64-bit size values"); - this.HighPackSize = bstream.readBits(32); - this.HighUnpSize = bstream.readBits(32); - } else { - this.HighPackSize = 0; - this.HighUnpSize = 0; - if (this.unpackedSize == 0xffffffff) { - this.HighUnpSize = 0x7fffffff; - this.unpackedSize = 0xffffffff; - } - } - this.fullPackSize = 0; - this.fullUnpackSize = 0; - this.fullPackSize |= this.HighPackSize; - this.fullPackSize <<= 32; - this.fullPackSize |= this.packSize; - - // read in filename - - this.filename = bstream.readBytes(this.nameSize); - for (var _i = 0, _s = ""; _i < this.filename.length ; _i++) { - _s += String.fromCharCode(this.filename[_i]); - } - - this.filename = _s; - - if (this.flags.LHD_SALT) { - info("Warning: Reading in 64-bit salt value"); - this.salt = bstream.readBits(64); // 8 bytes - } - - if (this.flags.LHD_EXTTIME) { - // 16-bit flags - var extTimeFlags = bstream.readBits(16); - - // this is adapted straight out of arcread.cpp, Archive::ReadHeader() - for (var I = 0; I < 4; ++I) { - var rmode = extTimeFlags >> ((3 - I) * 4); - if ((rmode & 8)==0) - continue; - if (I!=0) { - bstream.readBits(16); - } - var count = (rmode & 3); - for (var J = 0; J < count; ++J) { - bstream.readBits(8); - } - } - } - - if (this.flags.LHD_COMMENT) { - info("Found a LHD_COMMENT"); - } - - - while (headPos + this.headSize > bstream.bytePtr) bstream.readBits(1); - - info("Found FILE_HEAD with packSize=" + this.packSize + ", unpackedSize= " + this.unpackedSize + ", hostOS=" + this.hostOS + ", unpVer=" + this.unpVer + ", method=" + this.method + ", filename=" + this.filename); - - break; - default: - info("Found a header of type 0x" + byteValueToHexString(this.headType)); - // skip the rest of the header bytes (for now) - bstream.readBytes( this.headSize - 7 ); - break; - } -}; - -var BLOCK_LZ = 0; - // BLOCK_PPM = 1; - -var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224], - rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], - rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12], - rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192], - rSDBits = [2,2,3, 4, 5, 6, 6, 6]; - -var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, - 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, - 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, - 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, - 655360, 720896, 786432, 851968, 917504, 983040]; - -var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, - 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, - 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]; - -var rLOW_DIST_REP_COUNT = 16; - -var rNC = 299, - rDC = 60, - rLDC = 17, - rRC = 28, - rBC = 20, - rHUFF_TABLE_SIZE = (rNC + rDC + rRC + rLDC); - -var UnpBlockType = BLOCK_LZ; -var UnpOldTable = new Array(rHUFF_TABLE_SIZE); - -var BD = { //bitdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rBC) -}; -var LD = { //litdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rNC) -}; -var DD = { //distdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rDC) -}; -var LDD = { //low dist decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rLDC) -}; -var RD = { //rep decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rRC) -}; - -var rBuffer; - -// read in Huffman tables for RAR -function RarReadTables(bstream) { - var BitLength = new Array(rBC), - Table = new Array(rHUFF_TABLE_SIZE); - - // before we start anything we need to get byte-aligned - bstream.readBits( (8 - bstream.bitPtr) & 0x7 ); - - if (bstream.readBits(1)) { - info("Error! PPM not implemented yet"); - return; - } - - if (!bstream.readBits(1)) { //discard old table - for (var i = UnpOldTable.length; i--;) UnpOldTable[i] = 0; - } - - // read in bit lengths - for (var I = 0; I < rBC; ++I) { - - var Length = bstream.readBits(4); - if (Length == 15) { - var ZeroCount = bstream.readBits(4); - if (ZeroCount == 0) { - BitLength[I] = 15; - } - else { - ZeroCount += 2; - while (ZeroCount-- > 0 && I < rBC) - BitLength[I++] = 0; - --I; - } - } - else { - BitLength[I] = Length; - } - } - - // now all 20 bit lengths are obtained, we construct the Huffman Table: - - RarMakeDecodeTables(BitLength, 0, BD, rBC); - - var TableSize = rHUFF_TABLE_SIZE; - //console.log(DecodeLen, DecodePos, DecodeNum); - for (var i = 0; i < TableSize;) { - var num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[i] = (num + UnpOldTable[i]) & 0xf; - i++; - } else if(num < 18) { - var N = (num == 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i] = Table[i - 1]; - i++; - } - } else { - var N = (num == 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i++] = 0; - } - } - } - - RarMakeDecodeTables(Table, 0, LD, rNC); - RarMakeDecodeTables(Table, rNC, DD, rDC); - RarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC); - RarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC); - - for (var i = UnpOldTable.length; i--;) { - UnpOldTable[i] = Table[i]; - } - return true; -} - - -function RarDecodeNumber(bstream, dec) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var bitField = bstream.getBits() & 0xfffe; - //some sort of rolled out binary search - var bits = ((bitField < DecodeLen[8])? - ((bitField < DecodeLen[4])? - ((bitField < DecodeLen[2])? - ((bitField < DecodeLen[1])?1:2) - :((bitField < DecodeLen[3])?3:4)) - :(bitField < DecodeLen[6])? - ((bitField < DecodeLen[5])?5:6) - :((bitField < DecodeLen[7])?7:8)) - :((bitField < DecodeLen[12])? - ((bitField < DecodeLen[10])? - ((bitField < DecodeLen[9])?9:10) - :((bitField < DecodeLen[11])?11:12)) - :(bitField < DecodeLen[14])? - ((bitField < DecodeLen[13])?13:14) - :15)); - bstream.readBits(bits); - var N = DecodePos[bits] + ((bitField - DecodeLen[bits -1]) >>> (16 - bits)); - - return DecodeNum[N]; -} - - -function RarMakeDecodeTables(BitLength, offset, dec, size) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var LenCount = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - TmpPos = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - N = 0, M = 0; - for (var i = DecodeNum.length; i--;) DecodeNum[i] = 0; - for (var i = 0; i < size; i++) { - LenCount[BitLength[i + offset] & 0xF]++; - } - LenCount[0] = 0; - TmpPos[0] = 0; - DecodePos[0] = 0; - DecodeLen[0] = 0; - - for (var I = 1; I < 16; ++I) { - N = 2 * (N+LenCount[I]); - M = (N << (15-I)); - if (M > 0xFFFF) - M = 0xFFFF; - DecodeLen[I] = M; - DecodePos[I] = DecodePos[I-1] + LenCount[I-1]; - TmpPos[I] = DecodePos[I]; - } - for (I = 0; I < size; ++I) - if (BitLength[I + offset] != 0) - DecodeNum[ TmpPos[ BitLength[offset + I] & 0xF ]++] = I; -} - -// TODO: implement -function Unpack15(bstream, Solid) { - info("ERROR! RAR 1.5 compression not supported"); -} - -function Unpack20(bstream, Solid) { - var destUnpSize = rBuffer.data.length; - var oldDistPtr = 0; - - RarReadTables20(bstream); - while (destUnpSize > rBuffer.ptr) { - var num = RarDecodeNumber(bstream, LD); - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num > 269) { - var Length = rLDecode[num -= 270] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = RarDecodeNumber(bstream, DD); - var Distance = rDDecode[DistNumber] + 1; - if ((Bits = rDBits[DistNumber]) > 0) { - Distance += bstream.readBits(Bits); - } - if (Distance >= 0x2000) { - Length++; - if(Distance >= 0x40000) Length++; - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num == 269) { - RarReadTables20(bstream); - - RarUpdateProgress() - - continue; - } - if (num == 256) { - lastDist = rOldDist[oldDistPtr++ & 3] = lastDist; - RarCopyString(lastLength, lastDist); - continue; - } - if (num < 261) { - var Distance = rOldDist[(oldDistPtr - (num - 256)) & 3]; - var LengthNumber = RarDecodeNumber(bstream, RD); - var Length = rLDecode[LengthNumber] +2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - if (Distance >= 0x101) { - Length++; - if (Distance >= 0x2000) { - Length++ - if (Distance >= 0x40000) Length++; - } - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num < 270) { - var Distance = rSDDecode[num -= 261] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - lastLength = 2; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(2, Distance); - continue; - } - } - RarUpdateProgress() -} - -function RarUpdateProgress() { - var change = rBuffer.ptr - currentBytesUnarchivedInFile; - currentBytesUnarchivedInFile = rBuffer.ptr; - currentBytesUnarchived += change; - postProgress(); -} - - -var rNC20 = 298, - rDC20 = 48, - rRC20 = 28, - rBC20 = 19, - rMC20 = 257; - -var UnpOldTable20 = new Array(rMC20 * 4); - -function RarReadTables20(bstream) { - var BitLength = new Array(rBC20); - var Table = new Array(rMC20 * 4); - var TableSize, N, I; - var AudioBlock = bstream.readBits(1); - if (!bstream.readBits(1)) - for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = 0; - TableSize = rNC20 + rDC20 + rRC20; - for (var I = 0; I < rBC20; I++) - BitLength[I] = bstream.readBits(4); - RarMakeDecodeTables(BitLength, 0, BD, rBC20); - I = 0; - while (I < TableSize) { - var num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[I] = num + UnpOldTable20[I] & 0xf; - I++; - } else if(num == 16) { - N = bstream.readBits(2) + 3; - while (N-- > 0 && I < TableSize) { - Table[I] = Table[I - 1]; - I++; - } - } else { - if (num == 17) { - N = bstream.readBits(3) + 3; - } else { - N = bstream.readBits(7) + 11; - } - while (N-- > 0 && I < TableSize) { - Table[I++] = 0; - } - } - } - RarMakeDecodeTables(Table, 0, LD, rNC20); - RarMakeDecodeTables(Table, rNC20, DD, rDC20); - RarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20); - for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = Table[i]; -} - -var lowDistRepCount = 0, prevLowDist = 0; - -var rOldDist = [0,0,0,0]; -var lastDist; -var lastLength; - - -function Unpack29(bstream, Solid) { - // lazy initialize rDDecode and rDBits - - var DDecode = new Array(rDC); - var DBits = new Array(rDC); - - var Dist=0,BitLength=0,Slot=0; - - for (var I = 0; I < rDBitLengthCounts.length; I++,BitLength++) { - for (var J = 0; J < rDBitLengthCounts[I]; J++,Slot++,Dist+=(1<= 271) { - var Length = rLDecode[num -= 271] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = RarDecodeNumber(bstream, DD); - var Distance = DDecode[DistNumber]+1; - if ((Bits = DBits[DistNumber]) > 0) { - if (DistNumber > 9) { - if (Bits > 4) { - Distance += ((bstream.getBits() >>> (20 - Bits)) << 4); - bstream.readBits(Bits - 4); - //todo: check this - } - if (lowDistRepCount > 0) { - lowDistRepCount--; - Distance += prevLowDist; - } else { - var LowDist = RarDecodeNumber(bstream, LDD); - if (LowDist == 16) { - lowDistRepCount = rLOW_DIST_REP_COUNT - 1; - Distance += prevLowDist; - } else { - Distance += LowDist; - prevLowDist = LowDist; - } - } - } else { - Distance += bstream.readBits(Bits); - } - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) { - Length++; - } - } - RarInsertOldDist(Distance); - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num == 256) { - if (!RarReadEndOfBlock(bstream)) break; - continue; - } - if (num == 257) { - //console.log("READVMCODE"); - if (!RarReadVMCode(bstream)) break; - continue; - } - if (num == 258) { - if (lastLength != 0) { - RarCopyString(lastLength, lastDist); - } - continue; - } - if (num < 263) { - var DistNum = num - 259; - var Distance = rOldDist[DistNum]; - - for (var I = DistNum; I > 0; I--) { - rOldDist[I] = rOldDist[I-1]; - } - rOldDist[0] = Distance; - - var LengthNumber = RarDecodeNumber(bstream, RD); - var Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num < 272) { - var Distance = rSDDecode[num -= 263] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - RarInsertOldDist(Distance); - RarInsertLastMatch(2, Distance); - RarCopyString(2, Distance); - continue; - } - } - RarUpdateProgress() -} - -function RarReadEndOfBlock(bstream) { - - RarUpdateProgress() - - var NewTable = false, NewFile = false; - if (bstream.readBits(1)) { - NewTable = true; - } else { - NewFile = true; - NewTable = !!bstream.readBits(1); - } - //tablesRead = !NewTable; - return !(NewFile || NewTable && !RarReadTables(bstream)); -} - - -function RarReadVMCode(bstream) { - var FirstByte = bstream.readBits(8); - var Length = (FirstByte & 7) + 1; - if (Length == 7) { - Length = bstream.readBits(8) + 7; - } else if(Length == 8) { - Length = bstream.readBits(16); - } - var vmCode = []; - for(var I = 0; I < Length; I++) { - //do something here with cheking readbuf - vmCode.push(bstream.readBits(8)); - } - return RarAddVMCode(FirstByte, vmCode, Length); -} - -function RarAddVMCode(firstByte, vmCode, length) { - //console.log(vmCode); - if (vmCode.length > 0) { - info("Error! RarVM not supported yet!"); - } - return true; -} - -function RarInsertLastMatch(length, distance) { - lastDist = distance; - lastLength = length; -} - -function RarInsertOldDist(distance) { - rOldDist.splice(3,1); - rOldDist.splice(0,0,distance); -} - -//this is the real function, the other one is for debugging -function RarCopyString(length, distance) { - var destPtr = rBuffer.ptr - distance; - if(destPtr < 0){ - var l = rOldBuffers.length; - while(destPtr < 0){ - destPtr = rOldBuffers[--l].data.length + destPtr; - } - //TODO: lets hope that it never needs to read beyond file boundaries - while(length--) rBuffer.insertByte(rOldBuffers[l].data[destPtr++]); - } - if (length > distance) { - while(length--) rBuffer.insertByte(rBuffer.data[destPtr++]); - } else { - rBuffer.insertBytes(rBuffer.data.subarray(destPtr, destPtr + length)); - } -} - -var rOldBuffers = [] -// v must be a valid RarVolume -function unpack(v) { - - // TODO: implement what happens when unpVer is < 15 - var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer, - Solid = v.header.LHD_SOLID, - bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength ); - - rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize); - - info("Unpacking " + v.filename+" RAR v" + Ver); - - switch(Ver) { - case 15: // rar 1.5 compression - Unpack15(bstream, Solid); - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - Unpack20(bstream, Solid); - break; - case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(bstream, Solid); - break; - } // switch(method) - - rOldBuffers.push(rBuffer); - //TODO: clear these old buffers when there's over 4MB of history - return rBuffer.data; -} - -// bstream is a bit stream -var RarLocalFile = function(bstream) { - - this.header = new RarVolumeHeader(bstream); - this.filename = this.header.filename; - - if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) { - this.isValid = false; - info("Error! RAR Volume did not include a FILE_HEAD header "); - } - else { - // read in the compressed data - this.fileData = null; - if (this.header.packSize > 0) { - this.fileData = bstream.readBytes(this.header.packSize); - this.isValid = true; - } - } -}; - -RarLocalFile.prototype.unrar = function() { - - if (!this.header.flags.LHD_SPLIT_BEFORE) { - // unstore file - if (this.header.method == 0x30) { - info("Unstore "+this.filename); - this.isValid = true; - - currentBytesUnarchivedInFile += this.fileData.length; - currentBytesUnarchived += this.fileData.length; - - // Create a new buffer and copy it over. - var len = this.header.packSize; - var newBuffer = new bitjs.io.ByteBuffer(len); - newBuffer.insertBytes(this.fileData); - this.fileData = newBuffer.data; - } else { - this.isValid = true; - this.fileData = unpack(this); - } - } -} - -var unrar = function(arrayBuffer) { - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - - postMessage(new bitjs.archive.UnarchiveStartEvent()); - var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */); - - var header = new RarVolumeHeader(bstream); - if (header.crc == 0x6152 && - header.headType == 0x72 && - header.flags.value == 0x1A21 && - header.headSize == 7) - { - info("Found RAR signature"); - - var mhead = new RarVolumeHeader(bstream); - if (mhead.headType != MAIN_HEAD) { - info("Error! RAR did not include a MAIN_HEAD header"); - } else { - var localFiles = [], - localFile = null; - do { - try { - localFile = new RarLocalFile(bstream); - info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize); - if (localFile && localFile.isValid && localFile.header.packSize > 0) { - totalUncompressedBytesInArchive += localFile.header.unpackedSize; - localFiles.push(localFile); - } else if (localFile.header.packSize == 0 && localFile.header.unpackedSize == 0) { - localFile.isValid = true; - } - } catch(err) { - break; - } - //info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length); - } while( localFile.isValid ); - totalFilesInArchive = localFiles.length; - - // now we have all information but things are unpacked - // TODO: unpack - localFiles = localFiles.sort(function(a,b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - info(localFiles.map(function(a){return a.filename}).join(', ')); - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.header.filename; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unrar(); - - if (localfile.isValid) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - - postProgress(); - } - } - else { - err("Invalid RAR file"); - } - postMessage(new bitjs.archive.UnarchiveFinishEvent()); -}; - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - var ab = event.data.file; - unrar(ab, true); -}; diff --git a/cps/static/js/untar.js b/cps/static/js/untar.js deleted file mode 100644 index defed7d7..00000000 --- a/cps/static/js/untar.js +++ /dev/null @@ -1,168 +0,0 @@ -/** - * untar.js - * - * Copyright(c) 2011 Google Inc. - * - * Reference Documentation: - * - * TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html - */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts('io.js'); -importScripts('archive.js'); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -// Removes all characters from the first zero-byte in the string onwards. -var readCleanString = function(bstr, numBytes) { - var str = bstr.readString(numBytes); - var zIndex = str.indexOf(String.fromCharCode(0)); - return zIndex != -1 ? str.substr(0, zIndex) : str; -}; - -// takes a ByteStream and parses out the local file information -var TarLocalFile = function(bstream) { - this.isValid = false; - - // Read in the header block - this.name = readCleanString(bstream, 100); - this.mode = readCleanString(bstream, 8); - this.uid = readCleanString(bstream, 8); - this.gid = readCleanString(bstream, 8); - this.size = parseInt(readCleanString(bstream, 12), 8); - this.mtime = readCleanString(bstream, 12); - this.chksum = readCleanString(bstream, 8); - this.typeflag = readCleanString(bstream, 1); - this.linkname = readCleanString(bstream, 100); - this.maybeMagic = readCleanString(bstream, 6); - - if (this.maybeMagic == "ustar") { - this.version = readCleanString(bstream, 2); - this.uname = readCleanString(bstream, 32); - this.gname = readCleanString(bstream, 32); - this.devmajor = readCleanString(bstream, 8); - this.devminor = readCleanString(bstream, 8); - this.prefix = readCleanString(bstream, 155); - - if (this.prefix.length) { - this.name = this.prefix + this.name; - } - bstream.readBytes(12); // 512 - 500 - } else { - bstream.readBytes(255); // 512 - 257 - } - - // Done header, now rest of blocks are the file contents. - this.filename = this.name; - this.fileData = null; - - info("Untarring file '" + this.filename + "'"); - info(" size = " + this.size); - info(" typeflag = " + this.typeflag); - - // A regular file. - if (this.typeflag == 0) { - info(" This is a regular file."); - var sizeInBytes = parseInt(this.size); - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.size); - if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { - this.isValid = true; - } - - bstream.readBytes(this.size); - - // Round up to 512-byte blocks. - var remaining = 512 - this.size % 512; - if (remaining > 0 && remaining < 512) { - bstream.readBytes(remaining); - } - } else if (this.typeflag == 5) { - info(" This is a directory.") - } -}; - -// Takes an ArrayBuffer of a tar file in -// returns null on error -// returns an array of DecompressedFile objects on success -var untar = function(arrayBuffer) { - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - - postMessage(new bitjs.archive.UnarchiveStartEvent()); - var bstream = new bitjs.io.ByteStream(arrayBuffer); - var localFiles = []; - - // While we don't encounter an empty block, keep making TarLocalFiles. - while (bstream.peekNumber(4) != 0) { - var oneLocalFile = new TarLocalFile(bstream); - if (oneLocalFile && oneLocalFile.isValid) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.size; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a,b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the shipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - info("Sending file '" + localfile.filename + "' up"); - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = localfile.size; - currentBytesUnarchived += localfile.size; - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - - postProgress(); - - postMessage(new bitjs.archive.UnarchiveFinishEvent()); -}; - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - var ab = event.data.file; - untar(ab); -}; diff --git a/cps/static/js/unzip.js b/cps/static/js/unzip.js deleted file mode 100644 index 18b76443..00000000 --- a/cps/static/js/unzip.js +++ /dev/null @@ -1,621 +0,0 @@ -/** - * unzip.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ -/* global bitjs, importScripts, Uint8Array */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts("io.js"); -importScripts("archive.js"); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -var zLocalFileHeaderSignature = 0x04034b50; -var zArchiveExtraDataSignature = 0x08064b50; -var zCentralFileHeaderSignature = 0x02014b50; -var zDigitalSignatureSignature = 0x05054b50; -//var zEndOfCentralDirSignature = 0x06064b50; -//var zEndOfCentralDirLocatorSignature = 0x07064b50; - -// takes a ByteStream and parses out the local file information -var ZipLocalFile = function(bstream) { - if (typeof bstream !== typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function() {} ) { - return null; - } - - bstream.readNumber(4); // swallow signature - this.version = bstream.readNumber(2); - this.generalPurpose = bstream.readNumber(2); - this.compressionMethod = bstream.readNumber(2); - this.lastModFileTime = bstream.readNumber(2); - this.lastModFileDate = bstream.readNumber(2); - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - this.fileNameLength = bstream.readNumber(2); - this.extraFieldLength = bstream.readNumber(2); - - this.filename = null; - if (this.fileNameLength > 0) { - this.filename = bstream.readString(this.fileNameLength); - } - - info("Zip Local File Header:"); - info(" version=" + this.version); - info(" general purpose=" + this.generalPurpose); - info(" compression method=" + this.compressionMethod); - info(" last mod file time=" + this.lastModFileTime); - info(" last mod file date=" + this.lastModFileDate); - info(" crc32=" + this.crc32); - info(" compressed size=" + this.compressedSize); - info(" uncompressed size=" + this.uncompressedSize); - info(" file name length=" + this.fileNameLength); - info(" extra field length=" + this.extraFieldLength); - info(" filename = '" + this.filename + "'"); - - this.extraField = null; - if (this.extraFieldLength > 0) { - this.extraField = bstream.readString(this.extraFieldLength); - info(" extra field=" + this.extraField); - } - - // read in the compressed data - this.fileData = null; - if (this.compressedSize > 0) { - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); - bstream.ptr += this.compressedSize; - } - - // TODO: deal with data descriptor if present (we currently assume no data descriptor!) - // "This descriptor exists only if bit 3 of the general purpose bit flag is set" - // But how do you figure out how big the file data is if you don't know the compressedSize - // from the header?!? - if ((this.generalPurpose & bitjs.BIT[3]) !== 0) { - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - } -}; - -// determine what kind of compressed data we have and decompress -ZipLocalFile.prototype.unzip = function() { - - // Zip Version 1.0, no compression (store only) - if (this.compressionMethod === 0 ) { - info("ZIP v" + this.version + ", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); - currentBytesUnarchivedInFile = this.compressedSize; - currentBytesUnarchived += this.compressedSize; - } - // version == 20, compression method == 8 (DEFLATE) - else if (this.compressionMethod === 8) { - info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = inflate(this.fileData, this.uncompressedSize); - } - else { - err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = null; - } -}; - - -// Takes an ArrayBuffer of a zip file in -// returns null on error -// returns an array of DecompressedFile objects on success -var unzip = function(arrayBuffer) { - postMessage(new bitjs.archive.UnarchiveStartEvent()); - - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - currentBytesUnarchived = 0; - - var bstream = new bitjs.io.ByteStream(arrayBuffer); - // detect local file header signature or return null - if (bstream.peekNumber(4) === zLocalFileHeaderSignature) { - var localFiles = []; - // loop until we don't see any more local files - while (bstream.peekNumber(4) === zLocalFileHeaderSignature) { - var oneLocalFile = new ZipLocalFile(bstream); - // this should strip out directories/folders - if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a, b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - // archive extra data record - if (bstream.peekNumber(4) === zArchiveExtraDataSignature) { - info(" Found an Archive Extra Data Signature"); - - // skipping this record for now - bstream.readNumber(4); - var archiveExtraFieldLength = bstream.readNumber(4); - bstream.readString(archiveExtraFieldLength); - } - - // central directory structure - // TODO: handle the rest of the structures (Zip64 stuff) - if (bstream.peekNumber(4) === zCentralFileHeaderSignature) { - info(" Found a Central File Header"); - - // read all file headers - while (bstream.peekNumber(4) === zCentralFileHeaderSignature) { - bstream.readNumber(4); // signature - bstream.readNumber(2); // version made by - bstream.readNumber(2); // version needed to extract - bstream.readNumber(2); // general purpose bit flag - bstream.readNumber(2); // compression method - bstream.readNumber(2); // last mod file time - bstream.readNumber(2); // last mod file date - bstream.readNumber(4); // crc32 - bstream.readNumber(4); // compressed size - bstream.readNumber(4); // uncompressed size - var fileNameLength = bstream.readNumber(2); // file name length - var extraFieldLength = bstream.readNumber(2); // extra field length - var fileCommentLength = bstream.readNumber(2); // file comment length - bstream.readNumber(2); // disk number start - bstream.readNumber(2); // internal file attributes - bstream.readNumber(4); // external file attributes - bstream.readNumber(4); // relative offset of local header - - bstream.readString(fileNameLength); // file name - bstream.readString(extraFieldLength); // extra field - bstream.readString(fileCommentLength); // file comment - } - } - - // digital signature - if (bstream.peekNumber(4) === zDigitalSignatureSignature) { - info(" Found a Digital Signature"); - - bstream.readNumber(4); - var sizeOfSignature = bstream.readNumber(2); - bstream.readString(sizeOfSignature); // digital signature data - } - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the unzipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unzip(); - - if (localfile.fileData !== null) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - postProgress(); - postMessage(new bitjs.archive.UnarchiveFinishEvent()); - } -}; - -// returns a table of Huffman codes -// each entry's index is its code and its value is a JavaScript object -// containing {length: 6, symbol: X} -function getHuffmanCodes(bitLengths) { - // ensure bitLengths is an array containing at least one element - if (typeof bitLengths !== typeof [] || bitLengths.length < 1) { - err("Error! getHuffmanCodes() called with an invalid array"); - return null; - } - - // Reference: http://tools.ietf.org/html/rfc1951#page-8 - var numLengths = bitLengths.length, - blCount = [], - MAX_BITS = 1; - - // Step 1: count up how many codes of each length we have - for (var i = 0; i < numLengths; ++i) { - var len = bitLengths[i]; - // test to ensure each bit length is a positive, non-zero number - if (typeof len !== typeof 1 || len < 0) { - err("bitLengths contained an invalid number in getHuffmanCodes(): " + len + " of type " + (typeof len)); - return null; - } - // increment the appropriate bitlength count - if (blCount[len] === undefined) blCount[len] = 0; - // a length of zero means this symbol is not participating in the huffman coding - if (len > 0) blCount[len]++; - - if (len > MAX_BITS) MAX_BITS = len; - } - - // Step 2: Find the numerical value of the smallest code for each code length - var nextCode = [], - code = 0; - for (var bits = 1; bits <= MAX_BITS; ++bits) { - var len = bits-1; - // ensure undefined lengths are zero - if (blCount[len] == undefined) blCount[len] = 0; - code = (code + blCount[bits-1]) << 1; - nextCode[bits] = code; - } - - // Step 3: Assign numerical values to all codes - var table = {}, tableLength = 0; - for (var n = 0; n < numLengths; ++n) { - var len = bitLengths[n]; - if (len !== 0) { - table[nextCode[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(nextCode[len],len) }; - tableLength++; - nextCode[len]++; - } - } - table.maxLength = tableLength; - - return table; -} - -/* - The Huffman codes for the two alphabets are fixed, and are not - represented explicitly in the data. The Huffman code lengths - for the literal/length alphabet are: - - Lit Value Bits Codes - --------- ---- ----- - 0 - 143 8 00110000 through - 10111111 - 144 - 255 9 110010000 through - 111111111 - 256 - 279 7 0000000 through - 0010111 - 280 - 287 8 11000000 through - 11000111 -*/ -// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits -var fixedHCtoLiteral = null; -var fixedHCtoDistance = null; -function getFixedLiteralTable() { - // create once - if (!fixedHCtoLiteral) { - var bitlengths = new Array(288); - for (var i = 0; i <= 143; ++i) bitlengths[i] = 8; - for (var i = 144; i <= 255; ++i) bitlengths[i] = 9; - for (var i = 256; i <= 279; ++i) bitlengths[i] = 7; - for (var i = 280; i <= 287; ++i) bitlengths[i] = 8; - - // get huffman code table - fixedHCtoLiteral = getHuffmanCodes(bitlengths); - } - return fixedHCtoLiteral; -} -function getFixedDistanceTable() { - // create once - if (!fixedHCtoDistance) { - var bitlengths = new Array(32); - for (var i = 0; i < 32; ++i) { - bitlengths[i] = 5; - } - - // get huffman code table - fixedHCtoDistance = getHuffmanCodes(bitlengths); - } - return fixedHCtoDistance; -} - -// extract one bit at a time until we find a matching Huffman Code -// then return that symbol -function decodeSymbol(bstream, hcTable) { - var code = 0, len = 0; - // var match = false; - - // loop until we match - for (;;) { - // read in next bit - var bit = bstream.readBits(1); - code = (code<<1) | bit; - ++len; - - // check against Huffman Code table and break if found - if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { - - break; - } - if (len > hcTable.maxLength) { - err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + - " and table only max code length of " + hcTable.maxLength); - break; - } - } - return hcTable[code].symbol; -} - - -var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - /* - Extra Extra Extra - Code Bits Length(s) Code Bits Lengths Code Bits Length(s) - ---- ---- ------ ---- ---- ------- ---- ---- ------- - 257 0 3 267 1 15,16 277 4 67-82 - 258 0 4 268 1 17,18 278 4 83-98 - 259 0 5 269 2 19-22 279 4 99-114 - 260 0 6 270 2 23-26 280 4 115-130 - 261 0 7 271 2 27-30 281 5 131-162 - 262 0 8 272 2 31-34 282 5 163-194 - 263 0 9 273 3 35-42 283 5 195-226 - 264 0 10 274 3 43-50 284 5 227-257 - 265 1 11,12 275 3 51-58 285 0 258 - 266 1 13,14 276 3 59-66 - - */ -var LengthLookupTable = [ - [0, 3], [0, 4], [0, 5], [0, 6], - [0, 7], [0, 8], [0, 9], [0, 10], - [1, 11], [1, 13], [1, 15], [1, 17], - [2, 19], [2, 23], [2, 27], [2, 31], - [3, 35], [3, 43], [3, 51], [3, 59], - [4, 67], [4, 83], [4, 99], [4, 115], - [5, 131], [5, 163], [5, 195], [5, 227], - [0, 258] -]; - /* - Extra Extra Extra - Code Bits Dist Code Bits Dist Code Bits Distance - ---- ---- ---- ---- ---- ------ ---- ---- -------- - 0 0 1 10 4 33-48 20 9 1025-1536 - 1 0 2 11 4 49-64 21 9 1537-2048 - 2 0 3 12 5 65-96 22 10 2049-3072 - 3 0 4 13 5 97-128 23 10 3073-4096 - 4 1 5,6 14 6 129-192 24 11 4097-6144 - 5 1 7,8 15 6 193-256 25 11 6145-8192 - 6 2 9-12 16 7 257-384 26 12 8193-12288 - 7 2 13-16 17 7 385-512 27 12 12289-16384 - 8 3 17-24 18 8 513-768 28 13 16385-24576 - 9 3 25-32 19 8 769-1024 29 13 24577-32768 - */ -var DistLookupTable = [ - [0, 1], [0, 2], [0, 3], [0, 4], - [1, 5], [1, 7], - [2, 9], [2, 13], - [3, 17], [3, 25], - [4, 33], [4, 49], - [5, 65], [5, 97], - [6, 129], [6, 193], - [7, 257], [7, 385], - [8, 513], [8, 769], - [9, 1025], [9, 1537], - [10, 2049], [10, 3073], - [11, 4097], [11, 6145], - [12, 8193], [12, 12289], - [13, 16385], [13, 24577] -]; - -function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { - /* - loop (until end of block code recognized) - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream - - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ - var numSymbols = 0, blockSize = 0; - for (;;) { - var symbol = decodeSymbol(bstream, hcLiteralTable); - ++numSymbols; - if (symbol < 256) { - // copy literal byte to output - buffer.insertByte(symbol); - blockSize++; - } - else { - // end of block reached - if (symbol === 256) { - break; - } - else { - var lengthLookup = LengthLookupTable[symbol-257], - length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), - distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], - distance = distLookup[1] + bstream.readBits(distLookup[0]); - - // now apply length and distance appropriately and copy to output - - // TODO: check that backward distance < data.length? - - // http://tools.ietf.org/html/rfc1951#page-11 - // "Note also that the referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream." - // - // loop for each character - var ch = buffer.ptr - distance; - blockSize += length; - if(length > distance) { - var data = buffer.data; - while (length--) { - buffer.insertByte(data[ch++]); - } - } else { - buffer.insertBytes(buffer.data.subarray(ch, ch + length)) - } - - } // length-distance pair - } // length-distance pair or end-of-block - } // loop until we reach end of block - return blockSize; -} - -// {Uint8Array} compressedData A Uint8Array of the compressed file data. -// compression method 8 -// deflate: http://tools.ietf.org/html/rfc1951 -function inflate(compressedData, numDecompressedBytes) { - // Bit stream representing the compressed data. - var bstream = new bitjs.io.BitStream(compressedData.buffer, - false /* rtl */, - compressedData.byteOffset, - compressedData.byteLength); - var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); - var numBlocks = 0; - var blockSize = 0; - - // block format: http://tools.ietf.org/html/rfc1951#page-9 - do { - var bFinal = bstream.readBits(1); - var bType = bstream.readBits(2); - blockSize = 0; - ++numBlocks; - // no compression - if (bType == 0) { - // skip remaining bits in this byte - while (bstream.bitPtr != 0) bstream.readBits(1); - var len = bstream.readBits(16), - nlen = bstream.readBits(16); - // TODO: check if nlen is the ones-complement of len? - - if(len > 0) buffer.insertBytes(bstream.readBytes(len)); - blockSize = len; - } - // fixed Huffman codes - else if(bType === 1) { - blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); - } - // dynamic Huffman codes - else if(bType === 2) { - var numLiteralLengthCodes = bstream.readBits(5) + 257; - var numDistanceCodes = bstream.readBits(5) + 1, - numCodeLengthCodes = bstream.readBits(4) + 4; - - // populate the array of code length codes (first de-compaction) - var codeLengthsCodeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - for (var i = 0; i < numCodeLengthCodes; ++i) { - codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); - } - - // get the Huffman Codes for the code lengths - var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); - - // now follow this mapping - /* - 0 - 15: Represent code lengths of 0 - 15 - 16: Copy the previous code length 3 - 6 times. - The next 2 bits indicate repeat length - (0 = 3, ... , 3 = 6) - Example: Codes 8, 16 (+2 bits 11), - 16 (+2 bits 10) will expand to - 12 code lengths of 8 (1 + 6 + 5) - 17: Repeat a code length of 0 for 3 - 10 times. - (3 bits of length) - 18: Repeat a code length of 0 for 11 - 138 times - (7 bits of length) - */ - // to generate the true code lengths of the Huffman Codes for the literal - // and distance tables together - var literalCodeLengths = []; - var prevCodeLength = 0; - while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { - var symbol = decodeSymbol(bstream, codeLengthsCodes); - if (symbol <= 15) { - literalCodeLengths.push(symbol); - prevCodeLength = symbol; - } - else if (symbol === 16) { - var repeat = bstream.readBits(2) + 3; - while (repeat--) { - literalCodeLengths.push(prevCodeLength); - } - } - else if (symbol === 17) { - var repeat = bstream.readBits(3) + 3; - while (repeat--) { - literalCodeLengths.push(0); - } - } - else if (symbol === 18) { - var repeat = bstream.readBits(7) + 11; - while (repeat--) { - literalCodeLengths.push(0); - } - } - } - - // now split the distance code lengths out of the literal code array - var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); - - // now generate the true Huffman Code tables using these code lengths - var hcLiteralTable = getHuffmanCodes(literalCodeLengths), - hcDistanceTable = getHuffmanCodes(distanceCodeLengths); - blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); - } - // error - else { - err("Error! Encountered deflate block of type 3"); - return null; - } - - // update progress - currentBytesUnarchivedInFile += blockSize; - currentBytesUnarchived += blockSize; - postProgress(); - - } while (bFinal !== 1); - // we are done reading blocks if the bFinal bit was set for this block - - // return the buffer data bytes - return buffer.data; -} - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - unzip(event.data.file, true); -}; diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index cd8c321c..a8dddef5 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -76,8 +76,7 @@ + autocomplete="off">
@@ -88,6 +87,12 @@
+ {% if rarfile_support %} +
+ + +
+ {% endif %}
diff --git a/cps/templates/readcbr.html b/cps/templates/readcbr.html index 4893e038..cf2753d2 100644 --- a/cps/templates/readcbr.html +++ b/cps/templates/readcbr.html @@ -13,11 +13,11 @@ - + diff --git a/cps/ub.py b/cps/ub.py index 7b1f92ba..4c9159a4 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -296,6 +296,7 @@ class Settings(Base): config_goodreads_api_key = Column(String) config_goodreads_api_secret = Column(String) config_mature_content_tags = Column(String) # type: str + config_rarfile_location = Column(String) def __repr__(self): pass @@ -357,6 +358,7 @@ class Config: self.config_goodreads_api_key = data.config_goodreads_api_key self.config_goodreads_api_secret = data.config_goodreads_api_secret self.config_mature_content_tags = data.config_mature_content_tags + self.config_rarfile_location = data.config_rarfile_location @property def get_main_dir(self): @@ -487,6 +489,13 @@ def migrate_Database(): conn = engine.connect() conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1") session.commit() + try: + session.query(exists().where(Settings.config_rarfile_location)).scalar() + session.commit() + except exc.OperationalError: # Database is not compatible, some rows are missing + conn = engine.connect() + conn.execute("ALTER TABLE Settings ADD column `config_rarfile_location` String DEFAULT ''") + session.commit() try: create = False session.query(exists().where(User.sidebar_view)).scalar() diff --git a/cps/web.py b/cps/web.py index 8f6c9046..ec002fc8 100755 --- a/cps/web.py +++ b/cps/web.py @@ -24,6 +24,12 @@ try: except ImportError: pass # We're not using Python 3 +try: + import rarfile + rar_support=True +except ImportError: + rar_support=False + import mimetypes import logging from logging.handlers import RotatingFileHandler @@ -51,6 +57,7 @@ from flask_babel import Babel from flask_babel import gettext as _ import requests import zipfile +import tarfile from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.datastructures import Headers from babel import Locale as LC @@ -908,6 +915,122 @@ def get_metadata_calibre_companion(uuid): return "" +@app.route("/ajax/getcomic///") +@login_required +def get_comic_book(book_id, book_format, page): + book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + if not book: + return "" + else: + for bookformat in book.data: + if bookformat.format.lower() == book_format.lower(): + cbr_file = os.path.join(config.config_calibre_dir, book.path, bookformat.name) + "." + book_format + if book_format == "cbr": + if rar_support == True: + rarfile.UNRAR_TOOL = config.config_rarfile_location + try: + rf = rarfile.RarFile(cbr_file) + rarNames = rf.namelist() + extractedfile="data:image/png;base64," + (rf.read(rarNames[page])).encode('base64') + fileData={"name": rarNames[page],"page":page, "last":rarNames.__len__()-1, "content": extractedfile} + except: + return "" + # rarfile not valid + # ToDo: error handling + else: + # no support means return nothing + return "" + if book_format == "cbz": + zf = zipfile.ZipFile(cbr_file) + zipNames=zf.namelist() + extractedfile="data:image/png;base64," + (zf.read(zipNames[page])).encode('base64') + fileData={"name": zipNames[page],"page":page, "last":zipNames.__len__()-1, "content": extractedfile} + + if book_format == "cbt": + tf = tarfile.TarFile(u'D:\\zip\\test.cbt') + tarNames=tf.getnames() + extractedfile="data:image/png;base64," + (tf.extractfile(tarNames[page]).read()).encode('base64') + fileData={"name": tarNames[page],"page":page, "last":tarNames.__len__()-1, "content": extractedfile} + return make_response(json.dumps(fileData)) + +@app.route("/ajax/toggleread/", methods=['POST']) +@login_required +def toggle_read(book_id): + book = ub.session.query(ub.ReadBook).filter(ub.and_(ub.ReadBook.user_id == int(current_user.id), + ub.ReadBook.book_id == book_id)).first() + if book: + book.is_read = not book.is_read + else: + readBook = ub.ReadBook() + readBook.user_id = int(current_user.id) + readBook.book_id = book_id + readBook.is_read = True + book = readBook + ub.session.merge(book) + ub.session.commit() + return "" + + +@app.route('/ajax/verify_token', methods=['POST']) +@remote_login_required +def token_verified(): + token = request.form['token'] + auth_token = ub.session.query(ub.RemoteAuthToken).filter(ub.RemoteAuthToken.auth_token == token).first() + + data = {} + + # Token not found + if auth_token is None: + data['status'] = 'error' + data['message'] = _(u"Token not found") + + # Token expired + elif datetime.datetime.now() > auth_token.expiration: + ub.session.delete(auth_token) + ub.session.commit() + + data['status'] = 'error' + data['message'] = _(u"Token has expired") + + elif not auth_token.verified: + data['status'] = 'not_verified' + + else: + user = ub.session.query(ub.User).filter(ub.User.id == auth_token.user_id).first() + login_user(user) + + ub.session.delete(auth_token) + ub.session.commit() + + data['status'] = 'success' + flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") + + response = make_response(json.dumps(data, ensure_ascii=false)) + response.headers["Content-Type"] = "application/json; charset=utf-8" + + return response + + +@app.route("/ajax/bookmark//", methods=['POST']) +@login_required +def bookmark(book_id, book_format): + bookmark_key = request.form["bookmark"] + ub.session.query(ub.Bookmark).filter(ub.and_(ub.Bookmark.user_id == int(current_user.id), + ub.Bookmark.book_id == book_id, + ub.Bookmark.format == book_format)).delete() + if not bookmark_key: + ub.session.commit() + return "", 204 + + bookmark = ub.Bookmark(user_id=current_user.id, + book_id=book_id, + format=book_format, + bookmark_key=bookmark_key) + ub.session.merge(bookmark) + ub.session.commit() + return "", 201 + + @app.route("/get_authors_json", methods=['GET', 'POST']) @login_required_if_no_ano def get_authors_json(): @@ -1297,22 +1420,6 @@ def category(book_id, page): title=_(u"Category: %(name)s", name=name)) -@app.route("/ajax/toggleread/", methods=['POST']) -@login_required -def toggle_read(book_id): - book = ub.session.query(ub.ReadBook).filter(ub.and_(ub.ReadBook.user_id == int(current_user.id), - ub.ReadBook.book_id == book_id)).first() - if book: - book.is_read = not book.is_read - else: - readBook = ub.ReadBook() - readBook.user_id = int(current_user.id) - readBook.book_id = book_id - readBook.is_read = True - book = readBook - ub.session.merge(book) - ub.session.commit() - return "" @app.route("/book/") @@ -1356,24 +1463,6 @@ def show_book(book_id): return redirect(url_for("index")) -@app.route("/ajax/bookmark//", methods=['POST']) -@login_required -def bookmark(book_id, book_format): - bookmark_key = request.form["bookmark"] - ub.session.query(ub.Bookmark).filter(ub.and_(ub.Bookmark.user_id == int(current_user.id), - ub.Bookmark.book_id == book_id, - ub.Bookmark.format == book_format)).delete() - if not bookmark_key: - ub.session.commit() - return "", 204 - - bookmark = ub.Bookmark(user_id=current_user.id, - book_id=book_id, - format=book_format, - bookmark_key=bookmark_key) - ub.session.merge(bookmark) - ub.session.commit() - return "", 201 @app.route("/admin") @@ -1858,14 +1947,15 @@ def read_book(book_id, book_format): elif book_format.lower() == "txt": return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book")) else: - for fileext in ["cbr","cbt","cbz"]: + if rar_support == True: + extensionList = ["cbr","cbt","cbz"] + else: + extensionList = ["cbt","cbz"] + for fileext in extensionList: if book_format.lower() == fileext: - all_name = str(book_id) + "/" + book.data[0].name + "." + fileext - tmp_file = os.path.join(book_dir, book.data[0].name) + "." + fileext - if not os.path.exists(all_name): - cbr_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + "." + fileext - copyfile(cbr_file, tmp_file) - return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book")) + return render_title_template('readcbr.html', comicfile=book_id, extension=fileext, title=_(u"Read a Book")) + else: + flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") @app.route("/download//") @@ -2021,44 +2111,6 @@ def verify_token(token): return redirect(url_for('index')) -@app.route('/ajax/verify_token', methods=['POST']) -@remote_login_required -def token_verified(): - token = request.form['token'] - auth_token = ub.session.query(ub.RemoteAuthToken).filter(ub.RemoteAuthToken.auth_token == token).first() - - data = {} - - # Token not found - if auth_token is None: - data['status'] = 'error' - data['message'] = _(u"Token not found") - - # Token expired - elif datetime.datetime.now() > auth_token.expiration: - ub.session.delete(auth_token) - ub.session.commit() - - data['status'] = 'error' - data['message'] = _(u"Token has expired") - - elif not auth_token.verified: - data['status'] = 'not_verified' - - else: - user = ub.session.query(ub.User).filter(ub.User.id == auth_token.user_id).first() - login_user(user) - - ub.session.delete(auth_token) - ub.session.commit() - - data['status'] = 'success' - flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") - - response = make_response(json.dumps(data, ensure_ascii=false)) - response.headers["Content-Type"] = "application/json; charset=utf-8" - - return response @app.route('/send/') @@ -2480,6 +2532,12 @@ def configuration_helper(origin): if "config_mature_content_tags" in to_save: content.config_mature_content_tags = to_save["config_mature_content_tags"].strip() + # Rarfile Content configuration + # ToDo check: location valid + if "config_rarfile_location" in to_save: + content.config_rarfile_location = to_save["config_rarfile_location"].strip() + + content.config_default_role = 0 if "admin_role" in to_save: content.config_default_role = content.config_default_role + ub.ROLE_ADMIN @@ -2510,13 +2568,15 @@ def configuration_helper(origin): except e: flash(e, category="error") return render_title_template("config_edit.html", content=config, origin=origin, gdrive=gdrive_support, - goodreads=goodreads_support, title=_(u"Basic Configuration")) + goodreads=goodreads_support, rarfile_support=rar_support, + title=_(u"Basic Configuration")) if db_change: reload(db) if not db.setup_db(): flash(_(u'DB location is not valid, please enter correct path'), category="error") return render_title_template("config_edit.html", content=config, origin=origin, gdrive=gdrive_support, - goodreads=goodreads_support, title=_(u"Basic Configuration")) + goodreads=goodreads_support, rarfile_support=rar_support, + title=_(u"Basic Configuration")) if reboot_required: # db.engine.dispose() # ToDo verify correct ub.session.close() @@ -2530,7 +2590,8 @@ def configuration_helper(origin): success = True return render_title_template("config_edit.html", origin=origin, success=success, content=config, show_authenticate_google_drive=not is_gdrive_ready(), gdrive=gdrive_support, - goodreads=goodreads_support, title=_(u"Basic Configuration")) + goodreads=goodreads_support, rarfile_support=rar_support, + title=_(u"Basic Configuration")) @app.route("/admin/user/new", methods=["GET", "POST"]) diff --git a/optional-requirements.txt b/optional-requirements.txt index cf743dbb..fe37ed61 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -13,3 +13,4 @@ six==1.10.0 uritemplate==3.0.0 goodreads>=0.3.2 python-Levenshtein>=0.12.0 +rarfile>=2.7