diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf1185f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.json +out/ diff --git a/gen.js b/gen.js deleted file mode 100644 index db84ea9..0000000 --- a/gen.js +++ /dev/null @@ -1,190 +0,0 @@ -#!node -const fs = require("fs"); -const p = require("path"); -const qrimage = require("./qr/qr"); -const crypto = require("crypto"); - - -const deleteRec = (path) => { - path = p.resolve(path); - if (fs.existsSync(path)) { - if (fs.statSync(path).isDirectory()) { - fs.readdirSync(path).forEach(entry => deleteRec(p.join(path, entry))); - fs.rmdirSync(path); - } else { - fs.unlinkSync(path); - } - } -} - -const config = fs.existsSync("config.json") ? JSON.parse(fs.readFileSync("config.json").toString()) : {}; - -const child = require("child_process"); -const getPrivate = () => { - return child.execSync("wg genkey").toString().replace("\n", "").replace("\r", "").trim(); -} - -const getPublic = (private) => { - return child.execSync(`echo ${private} | wg pubkey`).toString().replace("\n", "").replace("\r", "").trim(); -} - -const keyPair = () => { - const private = getPrivate(); - const public = getPublic(private); - return { private, public }; -} - -const clientArray = (clients) => Object.keys(clients).map(key => ({ ...clients[key], name: key })) - -const ipTablesPostUP = [ - "iptables -A FORWARD -i %i -j ACCEPT", - "iptables -A FORWARD -o %i -j ACCEPT", - "iptables -t nat -A POSTROUTING -o %i -j MASQUERADE", - "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE", - "iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE" -].join("; "); - -const ipTablesPostDOWN = [ - "iptables -D FORWARD -i %i -j ACCEPT", - "iptables -D FORWARD -o %i -j ACCEPT", - "iptables -t nat -D POSTROUTING -o %i -j MASQUERADE", - "iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE", - "iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE" -].join("; "); - -function makeServer({ port, subnet, subnet6, server, clients }) { - const base = `[Interface] #Server -ListenPort = ${port} -Address = ${subnet}1/32,${subnet6}1/48 -PrivateKey = ${server.private} -PostUp = ${ipTablesPostUP} -PostDown = ${ipTablesPostDOWN} -# --------------------------- - -` - - let peers = clientArray(clients).map(client => { - return `[Peer] #${client.name} -PublicKey = ${client.public} -AllowedIPs = ${subnet}${client.ip}/32,${subnet6}${client.ip}/128 -` + (client.endpoint ? "\nEndpoint = " + client.endpoint + ":" + port : ""); - }).join("\n\n"); - - fs.writeFileSync("out/server/server.conf", base + peers); - fs.writeFileSync("out/server/server.public", server.public); -} - -function makeClients({ port, subnet, subnet6, server, clients }) { - clientArray(clients).map(client => { - const config = `[Interface] -ListenPort = ${port} -Address = ${subnet}${client.ip}/32, ${subnet6}${client.ip}/128 -PrivateKey = ${client.private} -DNS = 1.1.1.1 - -[Peer] #Server -PublicKey = ${server.public} -AllowedIPs = 0.0.0.0/0, ::0/0 -Endpoint = ${server.endpoint}:${port} -` - - fs.writeFileSync(`out/clients/${client.name}.conf`, config); - fs.writeFileSync(`out/clients/${client.name}.public`, client.public); - var qr_svg = qrimage.image(config, { type: 'png' }); - qr_svg.pipe(require('fs').createWriteStream(`out/clients/${client.name}.png`)); - }) -} - -try { - if (!config.subnet6) { - let sn = crypto.randomBytes(5).toString("hex"); - let s1 = sn.substr(0, 2); - let s23 = sn.substr(2, 4); - let s45 = sn.substr(6, 4); - config.subnet6 = `fd${s1}:${s23}:${s45}::`; - } - - switch (process.argv[2]) { - case "init": { - const hostname = process.argv[3]; - let subnet = process.argv[4]; - const port = Number(process.argv[5]) | 51823; - - if (!hostname) - throw new Error("Hostname required"); - - if (!subnet) { - let randomIP1 = Math.floor(Math.random() * 256); - let randomIP2 = Math.floor(Math.random() * 256); - subnet = `10.${randomIP1}.${randomIP2}.`; - } else { - if (!subnet.endsWith(".")) - subnet += "."; - } - - - - config.subnet = subnet; - config.port = port; - config.server = { - endpoint: hostname, - ...keyPair() - } - config.clients = {}; - console.log("Created new configuration"); - console.log("Server Hostname:", hostname); - console.log(" Port:", port); - console.log(" Subnet:", subnet + "0/24"); - - break; - } - case "generate": { - deleteRec("./out"); - fs.mkdirSync("./out") - fs.mkdirSync("./out/server") - fs.mkdirSync("./out/clients") - makeServer(config) - makeClients(config) - break; - } - case "add": { - const name = process.argv[3]; - const endpoint = process.argv[4]; - if (!name) - throw new Error("No name!"); - if (config.clients[name]) { - throw new Error("A device with this name exists already!\n Remove with 'remove '"); - } - - let freeIP = 2; - clientArray(config.clients).forEach(c => c.ip >= freeIP ? freeIP = c.ip + 1 : undefined); - config.clients[name] = { - ip: freeIP, - endpoint, - ...keyPair() - } - console.log("Peer added:") - console.log(`IP: ${config.subnet}${freeIP}`) - break; - } - - case "remove": { - const name = process.argv[3]; - if (!name) - throw new Error("No name!"); - delete config.clients[name]; - break; - } - - default: { - console.log("Wireguard VPN Config generator v0.1"); - console.log("Usage:") - console.log("generate, add, remove") - } - } -} catch (err) { - console.error(err); - console.error(err.message); -} - -fs.writeFileSync("config.json", JSON.stringify(config, undefined, " ")); \ No newline at end of file diff --git a/qr/LICENSE b/qr/LICENSE deleted file mode 100644 index 3884cac..0000000 --- a/qr/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013 Yandex LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/qr/crc32buffer.js b/qr/crc32buffer.js deleted file mode 100644 index e4bd213..0000000 --- a/qr/crc32buffer.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -var crc_table = []; - -for (var n = 0; n < 256; n++) { - var c = crc_table[n] = Buffer.alloc(4); - c.writeUInt32BE(n, 0); - - for (var k = 0; k < 8; k++) { - var b0 = c[0] & 1; - var b1 = c[1] & 1; - var b2 = c[2] & 1; - var b3 = c[3] & 1; - - c[0] = (c[0] >> 1) ^ (b3 ? 0xed : 0); - c[1] = (c[1] >> 1) ^ (b3 ? 0xb8 : 0) ^ (b0 ? 0x80 : 0); - c[2] = (c[2] >> 1) ^ (b3 ? 0x83 : 0) ^ (b1 ? 0x80 : 0); - c[3] = (c[3] >> 1) ^ (b3 ? 0x20 : 0) ^ (b2 ? 0x80 : 0); - } -} - -function update(c, buf) { - var l = buf.length; - for (var n = 0; n < l; n++) { - var e = crc_table[c[3] ^ buf[n]]; - c[3] = e[3] ^ c[2]; - c[2] = e[2] ^ c[1]; - c[1] = e[1] ^ c[0]; - c[0] = e[0]; - } -} - -function crc32(/* arguments */) { - var l = arguments.length; - var c = Buffer.alloc(4); - c.fill(0xff); - - for (var i = 0; i < l; i++) { - update(c, Buffer.from(arguments[i])); - } - - c[0] = c[0] ^ 0xff; - c[1] = c[1] ^ 0xff; - c[2] = c[2] ^ 0xff; - c[3] = c[3] ^ 0xff; - - return c.readUInt32BE(0); -} - -module.exports = crc32; diff --git a/qr/encode.js b/qr/encode.js deleted file mode 100644 index fc56dbd..0000000 --- a/qr/encode.js +++ /dev/null @@ -1,175 +0,0 @@ -"use strict"; - -function pushBits(arr, n, value) { - for (var bit = 1 << (n - 1); bit; bit = bit >>> 1) { - arr.push(bit & value ? 1 : 0); - } -} - -// {{{1 8bit encode -function encode_8bit(data) { - var len = data.length; - var bits = []; - - for (var i = 0; i < len; i++) { - pushBits(bits, 8, data[i]); - } - - var res = {}; - - var d = [0, 1, 0, 0]; - pushBits(d, 16, len); - res.data10 = res.data27 = d.concat(bits); - - if (len < 256) { - var d = [0, 1, 0, 0]; - pushBits(d, 8, len); - res.data1 = d.concat(bits); - } - - return res; -} - -// {{{1 alphanumeric encode -var ALPHANUM = (function (s) { - var res = {}; - for (var i = 0; i < s.length; i++) { - res[s[i]] = i; - } - return res; -})('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'); - -function encode_alphanum(str) { - var len = str.length; - var bits = []; - - for (var i = 0; i < len; i += 2) { - var b = 6; - var n = ALPHANUM[str[i]]; - if (str[i + 1]) { - b = 11; - n = n * 45 + ALPHANUM[str[i + 1]]; - } - pushBits(bits, b, n); - } - - var res = {}; - - var d = [0, 0, 1, 0]; - pushBits(d, 13, len); - res.data27 = d.concat(bits); - - if (len < 2048) { - var d = [0, 0, 1, 0]; - pushBits(d, 11, len); - res.data10 = d.concat(bits); - } - - if (len < 512) { - var d = [0, 0, 1, 0]; - pushBits(d, 9, len); - res.data1 = d.concat(bits); - } - - return res; -} - -// {{{1 numeric encode -function encode_numeric(str) { - var len = str.length; - var bits = []; - - for (var i = 0; i < len; i += 3) { - var s = str.substr(i, 3); - var b = Math.ceil(s.length * 10 / 3); - pushBits(bits, b, parseInt(s, 10)); - } - - var res = {}; - - var d = [0, 0, 0, 1]; - pushBits(d, 14, len); - res.data27 = d.concat(bits); - - if (len < 4096) { - var d = [0, 0, 0, 1]; - pushBits(d, 12, len); - res.data10 = d.concat(bits); - } - - if (len < 1024) { - var d = [0, 0, 0, 1]; - pushBits(d, 10, len); - res.data1 = d.concat(bits); - } - - return res; -} - -// {{{1 url encode -function encode_url(str) { - var slash = str.indexOf('/', 8) + 1 || str.length; - var res = encode(str.slice(0, slash).toUpperCase(), false); - - if (slash >= str.length) { - return res; - } - - var path_res = encode(str.slice(slash), false); - - res.data27 = res.data27.concat(path_res.data27); - - if (res.data10 && path_res.data10) { - res.data10 = res.data10.concat(path_res.data10); - } - - if (res.data1 && path_res.data1) { - res.data1 = res.data1.concat(path_res.data1); - } - - return res; -} - -// {{{1 Choose encode mode and generates struct with data for different version -function encode(data, parse_url) { - var str; - var t = typeof data; - - if (t == 'string' || t == 'number') { - str = '' + data; - data = Buffer.from(str); - } else if (Buffer.isBuffer(data)) { - str = data.toString(); - } else if (Array.isArray(data)) { - data = Buffer.from(data); - str = data.toString(); - } else { - throw new Error("Bad data"); - } - - if (/^[0-9]+$/.test(str)) { - if (data.length > 7089) { - throw new Error("Too much data"); - } - return encode_numeric(str); - } - - if (/^[0-9A-Z \$%\*\+\.\/\:\-]+$/.test(str)) { - if (data.length > 4296) { - throw new Error("Too much data"); - } - return encode_alphanum(str); - } - - if (parse_url && /^https?:/i.test(str)) { - return encode_url(str); - } - - if (data.length > 2953) { - throw new Error("Too much data"); - } - return encode_8bit(data); -} - -// {{{1 export functions -module.exports = encode; diff --git a/qr/errorcode.js b/qr/errorcode.js deleted file mode 100644 index afb99c4..0000000 --- a/qr/errorcode.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -// {{{1 Galois Field Math -var GF256_BASE = 285; - -var EXP_TABLE = [1]; -var LOG_TABLE = []; - -for (var i = 1; i < 256; i++) { - var n = EXP_TABLE[i - 1] << 1; - if (n > 255) n = n ^ GF256_BASE; - EXP_TABLE[i] = n; -} - -for (var i = 0; i < 255; i++) { - LOG_TABLE[EXP_TABLE[i]] = i; -} - -function exp(k) { - while (k < 0) k += 255; - while (k > 255) k -= 255; - return EXP_TABLE[k]; -} - -function log(k) { - if (k < 1 || k > 255) { - throw Error('Bad log(' + k + ')'); - } - return LOG_TABLE[k]; -} - -// {{{1 Generator Polynomials -var POLYNOMIALS = [ - [0], // a^0 x^0 - [0, 0], // a^0 x^1 + a^0 x^0 - [0, 25, 1], // a^0 x^2 + a^25 x^1 + a^1 x^0 - // and so on... -]; - -function generatorPolynomial(num) { - if (POLYNOMIALS[num]) { - return POLYNOMIALS[num]; - } - var prev = generatorPolynomial(num - 1); - var res = []; - - res[0] = prev[0]; - for (var i = 1; i <= num; i++) { - res[i] = log(exp(prev[i]) ^ exp(prev[i - 1] + num - 1)); - } - POLYNOMIALS[num] = res; - return res; -} - -// {{{1 export functions -module.exports = function calculate_ec(msg, ec_len) { - // `msg` could be array or buffer - // convert `msg` to array - msg = [].slice.call(msg); - - // Generator Polynomial - var poly = generatorPolynomial(ec_len); - - for (var i = 0; i < ec_len; i++) msg.push(0); - while (msg.length > ec_len) { - if (!msg[0]) { - msg.shift(); - continue; - } - var log_k = log(msg[0]); - for (var i = 0; i <= ec_len; i++) { - msg[i] = msg[i] ^ exp(poly[i] + log_k); - } - msg.shift(); - } - return Buffer.from(msg); -} diff --git a/qr/matrix.js b/qr/matrix.js deleted file mode 100644 index aea0fd7..0000000 --- a/qr/matrix.js +++ /dev/null @@ -1,352 +0,0 @@ -"use strict"; - -// {{{1 Initialize matrix with zeros -function init(version) { - var N = version * 4 + 17; - var matrix = []; - var zeros = new Buffer(N); - zeros.fill(0); - zeros = [].slice.call(zeros); - for (var i = 0; i < N; i++) { - matrix[i] = zeros.slice(); - } - return matrix; -} - -// {{{1 Put finders into matrix -function fillFinders(matrix) { - var N = matrix.length; - for (var i = -3; i <= 3; i++) { - for (var j = -3; j <= 3; j++) { - var max = Math.max(i, j); - var min = Math.min(i, j); - var pixel = (max == 2 && min >= -2) || (min == -2 && max <= 2) ? 0x80 : 0x81; - matrix[3 + i][3 + j] = pixel; - matrix[3 + i][N - 4 + j] = pixel; - matrix[N - 4 + i][3 + j] = pixel; - } - } - for (var i = 0; i < 8; i++) { - matrix[7][i] = matrix[i][7] = - matrix[7][N - i - 1] = matrix[i][N - 8] = - matrix[N - 8][i] = matrix[N - 1 - i][7] = 0x80; - } -} - -// {{{1 Put align and timinig -function fillAlignAndTiming(matrix) { - var N = matrix.length; - if (N > 21) { - var len = N - 13; - var delta = Math.round(len / Math.ceil(len / 28)); - if (delta % 2) delta++; - var res = []; - for (var p = len + 6; p > 10; p -= delta) { - res.unshift(p); - } - res.unshift(6); - for (var i = 0; i < res.length; i++) { - for (var j = 0; j < res.length; j++) { - var x = res[i], y = res[j]; - if (matrix[x][y]) continue; - for (var r = -2; r <=2 ; r++) { - for (var c = -2; c <=2 ; c++) { - var max = Math.max(r, c); - var min = Math.min(r, c); - var pixel = (max == 1 && min >= -1) || (min == -1 && max <= 1) ? 0x80 : 0x81; - matrix[x + r][y + c] = pixel; - } - } - } - } - } - for (var i = 8; i < N - 8; i++) { - matrix[6][i] = matrix[i][6] = i % 2 ? 0x80 : 0x81; - } -} - -// {{{1 Fill reserved areas with zeroes -function fillStub(matrix) { - var N = matrix.length; - for (var i = 0; i < 8; i++) { - if (i != 6) { - matrix[8][i] = matrix[i][8] = 0x80; - } - matrix[8][N - 1 - i] = 0x80; - matrix[N - 1 - i][8] = 0x80; - } - matrix[8][8] = 0x80; - matrix[N - 8][8] = 0x81; - - if (N < 45) return; - - for (var i = N - 11; i < N - 8; i++) { - for (var j = 0; j < 6; j++) { - matrix[i][j] = matrix[j][i] = 0x80; - } - } -} - -// {{{1 Fill reserved areas -var fillReserved = (function() { - var FORMATS = Array(32); - var VERSIONS = Array(40); - - var gf15 = 0x0537; - var gf18 = 0x1f25; - var formats_mask = 0x5412; - - for (var format = 0; format < 32; format++) { - var res = format << 10; - for (var i = 5; i > 0; i--) { - if (res >>> (9 + i)) { - res = res ^ (gf15 << (i - 1)); - } - } - FORMATS[format] = (res | (format << 10)) ^ formats_mask; - } - - for (var version = 7; version <= 40; version++) { - var res = version << 12; - for (var i = 6; i > 0; i--) { - if (res >>> (11 + i)) { - res = res ^ (gf18 << (i - 1)); - } - } - VERSIONS[version] = (res | (version << 12)); - } - - var EC_LEVELS = { L: 1, M: 0, Q: 3, H: 2 }; - - return function fillReserved(matrix, ec_level, mask) { - var N = matrix.length; - var format = FORMATS[EC_LEVELS[ec_level] << 3 | mask]; - function F(k) { return format >> k & 1 ? 0x81 : 0x80 }; - for (var i = 0; i < 8; i++) { - matrix[8][N - 1 - i] = F(i); - if (i < 6) matrix[i][8] = F(i); - } - for (var i = 8; i < 15; i++) { - matrix[N - 15 + i][8] = F(i); - if (i > 8) matrix[8][14 - i] = F(i); - } - matrix[7][8] = F(6); - matrix[8][8] = F(7); - matrix[8][7] = F(8); - - var version = VERSIONS[(N - 17)/4]; - if (!version) return; - function V(k) { return version >> k & 1 ? 0x81 : 0x80 }; - for (var i = 0; i < 6; i++) { - for (var j = 0; j < 3; j++) { - matrix[N - 11 + j][i] = matrix[i][N - 11 + j] = V(i * 3 + j); - } - } - } -})(); - -// {{{1 Fill data -var fillData = (function() { - var MASK_FUNCTIONS = [ - function(i, j) { return (i + j) % 2 == 0 }, - function(i, j) { return i % 2 == 0 }, - function(i, j) { return j % 3 == 0 }, - function(i, j) { return (i + j) % 3 == 0 }, - function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0 }, - function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0 }, - function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0 }, - function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0 } - ]; - - return function fillData(matrix, data, mask) { - var N = matrix.length; - var row, col, dir = -1; - row = col = N - 1; - var mask_fn = MASK_FUNCTIONS[mask]; - var len = data.blocks[data.blocks.length - 1].length; - - for (var i = 0; i < len; i++) { - for (var b = 0; b < data.blocks.length; b++) { - if (data.blocks[b].length <= i) continue; - put(data.blocks[b][i]); - } - } - - len = data.ec_len; - for (var i = 0; i < len; i++) { - for (var b = 0; b < data.ec.length; b++) { - put(data.ec[b][i]); - } - } - - if (col > -1) { - do { - matrix[row][col] = mask_fn(row, col) ? 1 : 0; - } while (next()); - } - - function put(byte) { - for (var mask = 0x80; mask; mask = mask >> 1) { - var pixel = !!(mask & byte); - if (mask_fn(row, col)) pixel = !pixel; - matrix[row][col] = pixel ? 1 : 0; - next(); - } - } - - function next() { - do { - if ((col % 2) ^ (col < 6)) { - if (dir < 0 && row == 0 || dir > 0 && row == N - 1) { - col--; - dir = -dir; - } else { - col++; - row += dir; - } - } else { - col--; - } - if (col == 6) { - col--; - } - if (col < 0) { - return false; - } - } while (matrix[row][col] & 0xf0); - return true; - } - } -})(); - -// {{{1 Calculate penalty -function calculatePenalty(matrix) { - var N = matrix.length; - var penalty = 0; - // Rule 1 - for (var i = 0; i < N; i++) { - var pixel = matrix[i][0] & 1; - var len = 1; - for (var j = 1; j < N; j++) { - var p = matrix[i][j] & 1; - if (p == pixel) { - len++; - continue; - } - if (len >= 5) { - penalty += len - 2; - } - pixel = p; - len = 1; - } - if (len >= 5) { - penalty += len - 2; - } - } - for (var j = 0; j < N; j++) { - var pixel = matrix[0][j] & 1; - var len = 1; - for (var i = 1; i < N; i++) { - var p = matrix[i][j] & 1; - if (p == pixel) { - len++; - continue; - } - if (len >= 5) { - penalty += len - 2; - } - pixel = p; - len = 1; - } - if (len >= 5) { - penalty += len - 2; - } - } - - // Rule 2 - for (var i = 0; i < N - 1; i++) { - for (var j = 0; j < N - 1; j++) { - var s = matrix[i][j] + matrix[i][j + 1] + matrix[i + 1][j] + matrix[i + 1][j + 1] & 7; - if (s == 0 || s == 4) { - penalty += 3; - } - } - } - - // Rule 3 - function I(k) { return matrix[i][j + k] & 1 }; - function J(k) { return matrix[i + k][j] & 1 }; - for (var i = 0; i < N; i++) { - for (var j = 0; j < N; j++) { - if (j < N - 6 && I(0) && !I(1) && I(2) && I(3) && I(4) && !I(5) && I(6)) { - if (j >= 4 && !(I(-4) || I(-3) || I(-2) || I(-1))) { - penalty += 40; - } - if (j < N - 10 && !(I(7) || I(8) || I(9) || I(10))) { - penalty += 40; - } - } - - if (i < N - 6 && J(0) && !J(1) && J(2) && J(3) && J(4) && !J(5) && J(6)) { - if (i >= 4 && !(J(-4) || J(-3) || J(-2) || J(-1))) { - penalty += 40; - } - if (i < N - 10 && !(J(7) || J(8) || J(9) || J(10))) { - penalty += 40; - } - } - } - } - - // Rule 4 - var numDark = 0; - for (var i = 0; i < N; i++) { - for (var j = 0; j < N; j++) { - if (matrix[i][j] & 1) numDark++; - } - } - penalty += 10 * Math.floor(Math.abs(10 - 20 * numDark/(N * N))); - - return penalty; -} - -// {{{1 All-in-one function -function getMatrix(data) { - var matrix = init(data.version); - fillFinders(matrix); - fillAlignAndTiming(matrix); - fillStub(matrix); - - var penalty = Infinity; - var bestMask = 0; - for (var mask = 0; mask < 8; mask++) { - fillData(matrix, data, mask); - fillReserved(matrix, data.ec_level, mask); - var p = calculatePenalty(matrix); - if (p < penalty) { - penalty = p; - bestMask = mask; - } - } - - fillData(matrix, data, bestMask); - fillReserved(matrix, data.ec_level, bestMask); - - return matrix.map(function(row) { - return row.map(function(cell) { - return cell & 1; - }); - }); -} - -// {{{1 export functions -module.exports = { - getMatrix: getMatrix, - init: init, - fillFinders: fillFinders, - fillAlignAndTiming: fillAlignAndTiming, - fillStub: fillStub, - fillReserved: fillReserved, - fillData: fillData, - calculatePenalty: calculatePenalty, -} diff --git a/qr/png.js b/qr/png.js deleted file mode 100644 index 20ed92a..0000000 --- a/qr/png.js +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; - -var zlib = require('zlib'); - -var crc32 = require('./crc32buffer'); - -var PNG_HEAD = new Buffer([137, 80, 78, 71, 13, 10, 26, 10]); -var PNG_IHDR = new Buffer([0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0]); -var PNG_IDAT = new Buffer([0, 0, 0, 0, 73, 68, 65, 84]); -var PNG_IEND = new Buffer([0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]); - -function png(bitmap, stream) { - stream.push(PNG_HEAD); - - var IHDR = Buffer.concat([PNG_IHDR]); - IHDR.writeUInt32BE(bitmap.size, 8); - IHDR.writeUInt32BE(bitmap.size, 12); - IHDR.writeUInt32BE(crc32(IHDR.slice(4, -4)), 21); - stream.push(IHDR); - - var IDAT = Buffer.concat([ - PNG_IDAT, - zlib.deflateSync(bitmap.data, { level: 9 }), - new Buffer(4) - ]); - IDAT.writeUInt32BE(IDAT.length - 12, 0); - IDAT.writeUInt32BE(crc32(IDAT.slice(4, -4)), IDAT.length - 4); - stream.push(IDAT); - - stream.push(PNG_IEND); - stream.push(null); -} - -function bitmap(matrix, size, margin) { - var N = matrix.length; - var X = (N + 2 * margin) * size; - var data = new Buffer((X + 1) * X); - data.fill(255); - for (var i = 0; i < X; i++) { - data[i * (X + 1)] = 0; - } - - for (var i = 0; i < N; i++) { - for (var j = 0; j < N; j++) { - if (matrix[i][j]) { - var offset = ((margin + i) * (X + 1) + (margin + j)) * size + 1; - data.fill(0, offset, offset + size); - for (var c = 1; c < size; c++) { - data.copy(data, offset + c * (X + 1), offset, offset + size); - } - } - } - } - - return { - data: data, - size: X - } -} - -module.exports = { - bitmap: bitmap, - png: png -} diff --git a/qr/qr-base.js b/qr/qr-base.js deleted file mode 100644 index 808eb39..0000000 --- a/qr/qr-base.js +++ /dev/null @@ -1,180 +0,0 @@ -"use strict"; - -var encode = require('./encode'); -var calculateEC = require('./errorcode'); -var matrix = require('./matrix'); - -function _deepCopy(obj) { - return JSON.parse(JSON.stringify(obj)); -} - -var EC_LEVELS = ['L', 'M', 'Q', 'H']; - -// {{{1 Versions -var versions = [ - [], // there is no version 0 - // total number of codewords, (number of ec codewords, number of blocks) * ( L, M, Q, H ) - [26, 7, 1, 10, 1, 13, 1, 17, 1], - [44, 10, 1, 16, 1, 22, 1, 28, 1], - [70, 15, 1, 26, 1, 36, 2, 44, 2], - [100, 20, 1, 36, 2, 52, 2, 64, 4], - [134, 26, 1, 48, 2, 72, 4, 88, 4], // 5 - [172, 36, 2, 64, 4, 96, 4, 112, 4], - [196, 40, 2, 72, 4, 108, 6, 130, 5], - [242, 48, 2, 88, 4, 132, 6, 156, 6], - [292, 60, 2, 110, 5, 160, 8, 192, 8], - [346, 72, 4, 130, 5, 192, 8, 224, 8], // 10 - [404, 80, 4, 150, 5, 224, 8, 264, 11], - [466, 96, 4, 176, 8, 260, 10, 308, 11], - [532, 104, 4, 198, 9, 288, 12, 352, 16], - [581, 120, 4, 216, 9, 320, 16, 384, 16], - [655, 132, 6, 240, 10, 360, 12, 432, 18], // 15 - [733, 144, 6, 280, 10, 408, 17, 480, 16], - [815, 168, 6, 308, 11, 448, 16, 532, 19], - [901, 180, 6, 338, 13, 504, 18, 588, 21], - [991, 196, 7, 364, 14, 546, 21, 650, 25], - [1085, 224, 8, 416, 16, 600, 20, 700, 25], // 20 - [1156, 224, 8, 442, 17, 644, 23, 750, 25], - [1258, 252, 9, 476, 17, 690, 23, 816, 34], - [1364, 270, 9, 504, 18, 750, 25, 900, 30], - [1474, 300, 10, 560, 20, 810, 27, 960, 32], - [1588, 312, 12, 588, 21, 870, 29, 1050, 35], // 25 - [1706, 336, 12, 644, 23, 952, 34, 1110, 37], - [1828, 360, 12, 700, 25, 1020, 34, 1200, 40], - [1921, 390, 13, 728, 26, 1050, 35, 1260, 42], - [2051, 420, 14, 784, 28, 1140, 38, 1350, 45], - [2185, 450, 15, 812, 29, 1200, 40, 1440, 48], // 30 - [2323, 480, 16, 868, 31, 1290, 43, 1530, 51], - [2465, 510, 17, 924, 33, 1350, 45, 1620, 54], - [2611, 540, 18, 980, 35, 1440, 48, 1710, 57], - [2761, 570, 19, 1036, 37, 1530, 51, 1800, 60], - [2876, 570, 19, 1064, 38, 1590, 53, 1890, 63], // 35 - [3034, 600, 20, 1120, 40, 1680, 56, 1980, 66], - [3196, 630, 21, 1204, 43, 1770, 59, 2100, 70], - [3362, 660, 22, 1260, 45, 1860, 62, 2220, 74], - [3532, 720, 24, 1316, 47, 1950, 65, 2310, 77], - [3706, 750, 25, 1372, 49, 2040, 68, 2430, 81] // 40 -]; - -versions = versions.map(function(v, index) { - if (!index) return {}; - - var res = { - } - for (var i = 1; i < 8; i += 2) { - var length = v[0] - v[i]; - var num_template = v[i+1]; - var ec_level = EC_LEVELS[(i/2)|0]; - var level = { - version: index, - ec_level: ec_level, - data_len: length, - ec_len: v[i] / num_template, - blocks: [], - ec: [] - } - - for (var k = num_template, n = length; k > 0; k--) { - var block = (n / k)|0; - level.blocks.push(block); - n -= block; - - } - res[ec_level] = level; - } - return res; -}); - -// {{{1 Get version template -function getTemplate(message, ec_level) { - var i = 1; - var len; - - if (message.data1) { - len = Math.ceil(message.data1.length / 8); - } else { - i = 10; - } - for (/* i */; i < 10; i++) { - var version = versions[i][ec_level]; - if (version.data_len >= len) { - return _deepCopy(version); - } - } - - if (message.data10) { - len = Math.ceil(message.data10.length / 8); - } else { - i = 27; - } - for (/* i */; i < 27; i++) { - var version = versions[i][ec_level]; - if (version.data_len >= len) { - return _deepCopy(version); - } - } - - len = Math.ceil(message.data27.length / 8); - for (/* i */; i < 41; i++) { - var version = versions[i][ec_level]; - if (version.data_len >= len) { - return _deepCopy(version); - } - } - throw new Error("Too much data"); -} - -// {{{1 Fill template -function fillTemplate(message, template) { - var blocks = new Buffer(template.data_len); - blocks.fill(0); - - if (template.version < 10) { - message = message.data1; - } else if (template.version < 27) { - message = message.data10; - } else { - message = message.data27; - } - - var len = message.length; - - for (var i = 0; i < len; i += 8) { - var b = 0; - for (var j = 0; j < 8; j++) { - b = (b << 1) | (message[i + j] ? 1 : 0); - } - blocks[i / 8] = b; - } - - var pad = 236; - for (var i = Math.ceil((len + 4) / 8); i < blocks.length; i++) { - blocks[i] = pad; - pad = (pad == 236) ? 17 : 236; - } - - var offset = 0; - template.blocks = template.blocks.map(function(n) { - var b = blocks.slice(offset, offset + n); - offset += n; - template.ec.push(calculateEC(b, template.ec_len)); - return b; - }); - - return template; -} - -// {{{1 All-in-one -function QR(text, ec_level, parse_url) { - ec_level = EC_LEVELS.indexOf(ec_level) > -1 ? ec_level : 'M'; - var message = encode(text, parse_url); - var data = fillTemplate(message, getTemplate(message, ec_level)); - return matrix.getMatrix(data); -} - -// {{{1 export functions -module.exports = { - QR: QR, - getTemplate: getTemplate, - fillTemplate: fillTemplate, -} diff --git a/qr/qr.js b/qr/qr.js deleted file mode 100644 index 9bd792b..0000000 --- a/qr/qr.js +++ /dev/null @@ -1,121 +0,0 @@ -"use strict"; - -var Readable = require('stream').Readable; - -var QR = require('./qr-base').QR; -var png = require('./png'); -var vector = require('./vector'); - -var fn_noop = function() {}; - -var BITMAP_OPTIONS = { - parse_url: false, - ec_level: 'M', - size: 5, - margin: 4, - customize: null -}; - -var VECTOR_OPTIONS = { - parse_url: false, - ec_level: 'M', - margin: 1, - size: 0 -}; - -function get_options(options, force_type) { - if (typeof options === 'string') { - options = { 'ec_level': options } - } else { - options = options || {}; - } - var _options = { - type: String(force_type || options.type || 'png').toLowerCase() - }; - - var defaults = _options.type == 'png' ? BITMAP_OPTIONS : VECTOR_OPTIONS; - - for (var k in defaults) { - _options[k] = k in options ? options[k] : defaults[k]; - } - - return _options; -} - -function qr_image(text, options) { - options = get_options(options); - - var matrix = QR(text, options.ec_level, options.parse_url); - var stream = new Readable(); - stream._read = fn_noop; - - switch (options.type) { - case 'svg': - case 'pdf': - case 'eps': - process.nextTick(function() { - vector[options.type](matrix, stream, options.margin, options.size); - }); - break; - case 'svgpath': - // deprecated, use svg_object method - process.nextTick(function() { - var obj = vector.svg_object(matrix, options.margin, options.size); - stream.push(obj.path); - stream.push(null); - }); - break; - case 'png': - default: - process.nextTick(function() { - var bitmap = png.bitmap(matrix, options.size, options.margin); - if (options.customize) { - options.customize(bitmap); - } - png.png(bitmap, stream); - }); - } - - return stream; -} - -function qr_image_sync(text, options) { - options = get_options(options); - - var matrix = QR(text, options.ec_level, options.parse_url); - var stream = []; - var result; - - switch (options.type) { - case 'svg': - case 'pdf': - case 'eps': - vector[options.type](matrix, stream, options.margin, options.size); - result = stream.filter(Boolean).join(''); - break; - case 'png': - default: - var bitmap = png.bitmap(matrix, options.size, options.margin); - if (options.customize) { - options.customize(bitmap); - } - png.png(bitmap, stream); - result = Buffer.concat(stream.filter(Boolean)); - } - - return result; -} - -function svg_object(text, options) { - options = get_options(options, 'svg'); - - var matrix = QR(text, options.ec_level); - return vector.svg_object(matrix, options.margin); -} - -module.exports = { - matrix: QR, - image: qr_image, - imageSync: qr_image_sync, - svgObject: svg_object -}; diff --git a/qr/vector.js b/qr/vector.js deleted file mode 100644 index 56211ae..0000000 --- a/qr/vector.js +++ /dev/null @@ -1,260 +0,0 @@ -"use strict"; - -function matrix2path(matrix) { - var N = matrix.length; - var filled = []; - for (var row = -1; row <= N; row++) { - filled[row] = []; - } - - var path = []; - for (var row = 0; row < N; row++) { - for (var col = 0; col < N; col++) { - if (filled[row][col]) continue; - filled[row][col] = 1; - if (isDark(row, col)) { - if (!isDark(row - 1, col)) { - path.push(plot(row, col, 'right')); - } - } else { - if (isDark(row, col - 1)) { - path.push(plot(row, col, 'down')); - } - } - } - } - return path; - - function isDark(row, col) { - if (row < 0 || col < 0 || row >= N || col >= N) return false; - return !!matrix[row][col]; - } - - function plot(row0, col0, dir) { - filled[row0][col0] = 1; - var res = []; - res.push(['M', col0, row0 ]); - var row = row0; - var col = col0; - var len = 0; - do { - switch (dir) { - case 'right': - filled[row][col] = 1; - if (isDark(row, col)) { - filled[row - 1][col] = 1; - if (isDark(row - 1, col)) { - res.push(['h', len]); - len = 0; - dir = 'up'; - } else { - len++; - col++; - } - } else { - res.push(['h', len]); - len = 0; - dir = 'down'; - } - break; - case 'left': - filled[row - 1][col - 1] = 1; - if (isDark(row - 1, col - 1)) { - filled[row][col - 1] = 1; - if (isDark(row, col - 1)) { - res.push(['h', -len]); - len = 0; - dir = 'down'; - } else { - len++; - col--; - } - } else { - res.push(['h', -len]); - len = 0; - dir = 'up'; - } - break; - case 'down': - filled[row][col - 1] = 1; - if (isDark(row, col - 1)) { - filled[row][col] = 1; - if (isDark(row, col)) { - res.push(['v', len]); - len = 0; - dir = 'right'; - } else { - len++; - row++; - } - } else { - res.push(['v', len]); - len = 0; - dir = 'left'; - } - break; - case 'up': - filled[row - 1][col] = 1; - if (isDark(row - 1, col)) { - filled[row - 1][col - 1] = 1; - if (isDark(row - 1, col - 1)) { - res.push(['v', -len]); - len = 0; - dir = 'left'; - } else { - len++; - row--; - } - } else { - res.push(['v', -len]); - len = 0; - dir = 'right'; - } - break; - } - } while (row != row0 || col != col0); - return res; - } -} - -function pushSVGPath(matrix, stream, margin) { - matrix2path(matrix).forEach(function(subpath) { - var res = ''; - for (var k = 0; k < subpath.length; k++) { - var item = subpath[k]; - switch (item[0]) { - case 'M': - res += 'M' + (item[1] + margin) + ' ' + (item[2] + margin); - break; - default: - res += item.join(''); - } - } - res += 'z'; - stream.push(res); - }); -} - -function SVG_object(matrix, margin) { - var stream = []; - pushSVGPath(matrix, stream, margin); - - var result = { - size: matrix.length + 2 * margin, - path: stream.filter(Boolean).join('') - } - - return result; -} - -function SVG(matrix, stream, margin, size) { - var X = matrix.length + 2 * margin; - stream.push(' 0) { - var XY = X * size; - stream.push('width="' + XY + '" height="' + XY + '" '); - } - stream.push('viewBox="0 0 ' + X + ' ' + X + '">'); - stream.push(''); - stream.push(null); -} - -function EPS(matrix, stream, margin) { - var N = matrix.length; - var scale = 9; - var X = (N + 2 * margin) * scale; - stream.push([ - '%!PS-Adobe-3.0 EPSF-3.0', - '%%BoundingBox: 0 0 ' + X + ' ' + X, - '/h { 0 rlineto } bind def', - '/v { 0 exch neg rlineto } bind def', - '/M { neg ' + (N + margin) + ' add moveto } bind def', - '/z { closepath } bind def', - scale + ' ' + scale + ' scale', - '' - ].join('\n')); - - matrix2path(matrix).forEach(function(subpath) { - var res = ''; - for (var k = 0; k < subpath.length; k++) { - var item = subpath[k]; - switch (item[0]) { - case 'M': - res += (item[1] + margin) + ' ' + item[2] + ' M '; - break; - default: - res += item[1] + ' ' + item[0] + ' '; - } - } - res += 'z\n'; - stream.push(res); - }); - - stream.push('fill\n%%EOF\n'); - stream.push(null); -} - -function PDF(matrix, stream, margin) { - // TODO deflate - var N = matrix.length; - var scale = 9; - var X = (N + 2 * margin) * scale; - var data = [ - '%PDF-1.0\n\n', - '1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n', - '2 0 obj << /Type /Pages /Count 1 /Kids [ 3 0 R ] >> endobj\n', - ]; - data.push('3 0 obj << /Type /Page /Parent 2 0 R /Resources <<>> ' + - '/Contents 4 0 R /MediaBox [ 0 0 ' + X + ' ' + X + ' ] >> endobj\n'); - - var path = scale + ' 0 0 ' + scale + ' 0 0 cm\n'; - path += matrix2path(matrix).map(function(subpath) { - var res = ''; - var x, y; - for (var k = 0; k < subpath.length; k++) { - var item = subpath[k]; - switch (item[0]) { - case 'M': - x = item[1] + margin; - y = N - item[2] + margin; - res += x + ' ' + y + ' m '; - break; - case 'h': - x += item[1]; - res += x + ' ' + y + ' l '; - break; - case 'v': - y -= item[1]; - res += x + ' ' + y + ' l '; - break; - } - } - res += 'h'; - return res; - }).join('\n'); - path += '\nf\n'; - data.push('4 0 obj << /Length ' + path.length + ' >> stream\n' + - path + 'endstream\nendobj\n'); - - var xref = 'xref\n0 5\n0000000000 65535 f \n'; - for (var i = 1, l = data[0].length; i < 5; i++) { - xref += ('0000000000' + l).substr(-10) + ' 00000 n \n'; - l += data[i].length; - } - data.push( - xref, - 'trailer << /Root 1 0 R /Size 5 >>\n', - 'startxref\n' + l + '\n%%EOF\n' - ); - stream.push(data.join('')); - stream.push(null); -} - -module.exports = { - svg: SVG, - eps: EPS, - pdf: PDF, - svg_object: SVG_object -}