Skip to content

Commit

Permalink
now it works with latest encryption versions > 5 (6 till 10).
Browse files Browse the repository at this point in the history
  • Loading branch information
abdumu committed Sep 16, 2024
1 parent 759f590 commit 551053d
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ deploy/\*\*
out/
jarir_dump/
flows_jarir
test
jarir_dump.zip
jarir.har
code.c
Expand Down
100 changes: 77 additions & 23 deletions cli/backend/api-calls.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { resolve: pathResolve, resolve } = require("path");
const { createHash } = require("crypto");
const { getSettings, uuid, randomCompany, setSettings, clearResidue, blank, xAccess } = require("./helpers");
const { getAppDataPath } = require("./cross-platform");
const { unzipBook } = require("./decrypt");
const { unzipBook, appendFiles } = require("./decrypt");
const bookGenerator = require("./book-generator");
const https = require("https");

Expand Down Expand Up @@ -63,7 +63,6 @@ const getInitialAuth = () => {
resolve([settings.initialToken, settings.expires]);
}


http.post(
"/v7/login/token",
querystring.stringify({
Expand Down Expand Up @@ -143,7 +142,6 @@ const auth = (email, password) => {
email: settings.email,
});


const deviceUID = settings.deviceUID || uuid().replace("-", "");
const deviceName = settings.deviceName || randomCompany();
const x_access = xAccess();
Expand Down Expand Up @@ -292,9 +290,54 @@ const getUserBooks = () => {
});
};

const getDownloadInfo = (book) => {
return new Promise((resolve, reject) => {
return auth()
.then((authResult) => {
http.post(
"/v7/books/file/download",
querystring.stringify({
access_token: authResult.auth,
file_id: book.file_id,
Platform: "Android",
}),
{
timeout: 10000,
headers: {
"X-Request-Check": getRequestCheck(),
},
}
)
.then((response) => {
// console.log(response);
// console.log(response.data);
if (!blank(response.data) && response.data.hasOwnProperty("result")) {
if (response.data.result.hasOwnProperty("body") && response.data.result.hasOwnProperty("header") && response.data.result.body !== "") {
book.url = response.data.result.body;
book.header = response.data.result.header;
}
resolve(book);
return;
}

const err = new Error("(800) Could not get download info! check your info!");
err.code = 800;
reject(err);
})
.catch((error) => {
console.log(error);
const err = new Error("(801) Could not get download info! check your info! \n error:" + error.message);
err.code = 801;
reject(err);
});
})
.catch((error) => reject(error));
});
};

const downloadBook = (book) => {
return new Promise((resolve, reject) => {
if (!(book instanceof Book) && !(typeof book === "object" && "title" in book)) {
if (!(book instanceof Book) && !(typeof book === "object" && "id" in book)) {
const err = new Error("(700) Can not download book, has no valid book info!");
err.code = 700;
reject(err);
Expand All @@ -318,29 +361,38 @@ const downloadBook = (book) => {
} catch (e) {
bInfo.end();
reject(e);
return;
}

const path = pathResolve(getAppDataPath("jarir-cli"), "books", book.id + ".zip");

if (existsSync(path)) {
bInfo.end();
resolve(path);
resolve(book);
return;
}

const writer = createWriteStream(path);
writer.on("finish", () => {
const isBody = book.url.endsWith(".body") && book.header !== "";
const writer = createWriteStream(isBody ? path + '.body' : path);

writer.on("finish", async () => {
bInfo.end();
resolve(path);

if (isBody) {
const headerKey = await appendFiles(path + ".body", book.header, getSettings("auth"), path);
book.key = headerKey;
resolve(book);
} else {
resolve(book);
}
});
writer.on("error", (err) => {
bInfo.end();
reject(err);
});

//todo 403.
axios({
url: book.url.replace("_sample", ""),
url: book.url,
method: "GET",
responseType: "stream",
headers: {
Expand All @@ -361,15 +413,19 @@ const downloadBook = (book) => {

const downloadAndGenerateBook = async (book) => {
return new Promise((resolve, reject) => {
downloadBook(book)
.then((r1) => {
unzipBook(book)
.then((r2) => {
bookGenerator(book)
.then((r3) => {
resolve(r3);
clearResidue(book);
console.log(`💕️ Your book is ready: "${r3}"\n`);
getDownloadInfo(book)
.then((xbook) => {
downloadBook(xbook)
.then((ybook) => {
unzipBook(ybook)
.then((r2) => {
bookGenerator(book)
.then((r3) => {
resolve(r3);
clearResidue(book);
console.log(`💕️ Your book is ready: "${r3}"\n`);
})
.catch((error) => reject(error));
})
.catch((error) => reject(error));
})
Expand All @@ -389,7 +445,6 @@ const logout = () => {
return;
}


http.post(
"/v7/logout",
querystring.stringify({
Expand Down Expand Up @@ -421,14 +476,13 @@ const logout = () => {
err.code = 504;
reject(err);
});
});
});
};


module.exports = {
auth,
getUserBooks,
downloadBook,
downloadAndGenerateBook,
logout
logout,
};
1 change: 1 addition & 0 deletions cli/backend/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { cleanFilename, getBookIndex } = require("./helpers");
const { renameSync, writeFileSync, existsSync, mkdirSync } = require("fs");
const { getAppDataPath } = require("./cross-platform");

//TODO: fix for new version
const bookAudioGenerator = (book, info) => {
return new Promise((resolve, reject) => {
//create directory
Expand Down
13 changes: 7 additions & 6 deletions cli/backend/book.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const arabicTrans = require("./transliteration");

function Book(id, name, url, index, publisher, authors, cover, type, thumb, access, file_md5, file_id, latest_file_id, size, bookPath) {
function Book(id, name, url, index, publisher, authors, cover, type, thumb, access, file_md5, file_id, latest_file_id, size, header, key, bookPath) {
if (typeof id === "object") {
Object.assign(this, id);
return this;
Expand All @@ -10,7 +10,7 @@ function Book(id, name, url, index, publisher, authors, cover, type, thumb, acce
if (typeof id === "object") {
return new Book(id);
} else {
return new Book(id, name, url, index, publisher, authors, cover, type, thumb, access, file_md5, file_id, latest_file_id, size, bookPath);
return new Book(id, name, url, index, publisher, authors, cover, type, thumb, access, file_md5, file_id, latest_file_id, size, header, key, bookPath);
}
}

Expand All @@ -30,13 +30,14 @@ function Book(id, name, url, index, publisher, authors, cover, type, thumb, acce
this.thumb = thumb;
this.bookPath = bookPath;
this.name = this.id + { pdf: "🟥", epub: "📗", mp3: "🔉 mp3", "-": "" }[this.type] + ": " + arabicTrans(this.title) + " (" + this.title + ")";
//new items
//new items for future use
this.access = access || 0;
this.file_md5 = file_md5 || "";
this.file_id = file_id || 0;
this.latest_file_id = latest_file_id || 0;
this.header = header || "";
this.key = key || [115, -36, 110, -93, 78, -22, 63, -71, 97, -126, 86, 66, -36, 46, 13, -96];
this.file_id = file_id || id || 0;
this.latest_file_id = latest_file_id || file_id || id || 0;
this.size = size || 0;

}

Book.prototype.urlFilename = function urlFilename() {
Expand Down
108 changes: 87 additions & 21 deletions cli/backend/decrypt.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
const { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, unlinkSync } = require("fs");
const { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, unlinkSync, createWriteStream } = require("fs");
const bottomInfo = require("./bottom-info");
const StreamZip = require("node-stream-zip");

const { resolve: pathResolve } = require("path");
// const Crypto = require("crypto");
const { resolve: pathResolve, join: pathJoin } = require("path");
const CryptoJS = require("crypto-js");
const crypto = require('crypto');
const { promisify } = require('util');
const { inflateSync } = require("zlib");
const { getAppDataPath } = require("./cross-platform");

const readBookInfo = async (bookFile) => {
const zip = new StreamZip.async({ file: bookFile });
let info = {
formatVersion: 5
};

const entries = await zip.entries();
for (const entry of Object.values(entries)) {
if (entry.name === 'info.json') {
info = await zip.entryData(entry);
break;
}
}

await zip.close();
return info;
}

const unzipBook = (book) => {
const bInfo = new bottomInfo("Decrypting", "", ["🔒", "🔑", "🔏", "🔓"]);

return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {

if (typeof book.id === "undefined" || Number(book.id) === NaN) {
reject(new Error("book.id is not valid!"));
}
Expand All @@ -31,6 +50,7 @@ const unzipBook = (book) => {
}

bInfo.start();
const bookInfo = await readBookInfo(outputFolder + ".zip");

try {
if (!existsSync(outputFolder)) {
Expand All @@ -54,35 +74,49 @@ const unzipBook = (book) => {
});

zip.on("ready", () => {
zip.extract(null, outputFolder, (err, count) => {
if (err) {
try {
zip.extract(null, outputFolder, (err, count) => {
if (err) {
bInfo.end();
zip.close();
reject(err);
}

bInfo.end();
zip.close();
reject(err);
}

resolve(count);
});
} catch (err) {
bInfo.end();
zip.close();
resolve(count);
});
reject(err);
}
});

zip.on("extract", async (entry, file) => {
const formatVersion = Number(bookInfo.formatVersion) || 5;
// console.log("Format version: " + formatVersion);

if (entry.isFile && /DATA\.DATA$/.test(file)) {
decryptBinary(file);
decryptBinary(file, book.key);
}
if (entry.isFile && /chapter-[0-9]+.dat$/.test(file)) {
decryptBinary(file);
decryptBinary(file, book.key);
}
if (entry.isFile && /\.html$/.test(file)) {
decryptText(file);
decryptText(file, book.key);
}
//if format >= 10 decrypt .json and .spans files except info.json
if (formatVersion >= 10 && entry.isFile && /\.json$/.test(file) && /\.spans$/.test(file) && file !== 'info.json') {
decryptText(file, book.key);
}
});
});
};

const decryptBinary = (file) => {
const key = new Int8Array(Buffer.from([115, -36, 110, -93, 78, -22, 63, -71, 97, -126, 86, 66, -36, 46, 13, -96]));
const decryptBinary = (file, xkey) => {
//[115, -36, 110, -93, 78, -22, 63, -71, 97, -126, 86, 66, -36, 46, 13, -96]
const key = new Int8Array(Buffer.from(xkey));
// const cipher = Crypto.createDecipheriv("rc4", key, "");
// let outputBuffer = cipher.update(readFileSync(file), null, "binary") + cipher.final("binary");

Expand All @@ -106,8 +140,8 @@ const decryptBinary = (file) => {
});
};

const decryptText = async (file) => {
const key = new Int8Array(Buffer.from([115, -36, 110, -93, 78, -22, 63, -71, 97, -126, 86, 66, -36, 46, 13, -96]));
const decryptText = async (file, xkey) => {
const key = new Int8Array(Buffer.from(xkey));
// const cipher = Crypto.createDecipheriv("rc4", key, "");
// let outputBuffer = cipher.update(readFileSync(file), null, "binary") + cipher.final("binary");
const keyHex = Array.from(key).map(byte => {
Expand Down Expand Up @@ -140,10 +174,42 @@ const decryptText = async (file) => {
});
};

const jsonFileData = (file) => {
return {};
//for new version
const appendFiles = async (bookFile, headerHash, userAccessToken, filePath) => {
const sha1HashValue = crypto.createHash('sha1').update(userAccessToken + "platform").digest('hex');
const key = sha1HashValue.padEnd(32, '0').slice(0, 32);
const iv = '1234567812345678'; // IV should be 16 bytes long
const decodedBytes = Buffer.from(headerHash, 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
const headerBytes = Buffer.concat([decipher.update(decodedBytes), decipher.final()]);
const bodyBytes = readFileSync(bookFile);
const combinedBuffer = Buffer.concat([headerBytes, bodyBytes]);

const tempFile = pathJoin(require('os').tmpdir(), 'combined_data.zip');
writeFileSync(tempFile, combinedBuffer);

const zip = new StreamZip.async({ file: tempFile });
let headerKey = null;

const entries = await zip.entries();
for (const entry of Object.values(entries)) {
if (entry.name === 'header') {
const headerBuffer = await zip.entryData(entry);
headerKey = Array.from(headerBuffer);
} else if (entry.name === 'body') {
const outputStream = createWriteStream(filePath);
const stream = await zip.stream(entry.name);
stream.pipe(outputStream);
await promisify(stream.on.bind(stream))('end');
}
}

await zip.close();
unlinkSync(tempFile);
return headerKey;
};

module.exports = {
unzipBook,
appendFiles
};
Loading

0 comments on commit 551053d

Please sign in to comment.