commit 640b240570709b2a42df00a7b2ce455acb9becbb Author: Sergiusz 'q3k' Bazański Date: Thu Sep 5 22:48:54 2013 +0200 Initial commit of test/ugly/prototype code. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..674aaad --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +openwrt +sdk diff --git a/README b/README new file mode 100644 index 0000000..6ababf4 --- /dev/null +++ b/README @@ -0,0 +1,19 @@ +This is the Hackerspace Warsaw electronic lock mk2 project. + +Work in progress! This is a prototype for a TP-Link WR703n based hardware +solution - with a I2C bus on which there is a MAX7300 IO expander and a PN532 +NFC tag reader. The I2C is software bit-banged by the kernel, drivers for both +the IO expander and the PN532 are written in Lua. + +Files: + hslockmk2/ - this directory + /code.lua - main Lua source code + /lua-libs/ - necessary Lua libraries + /mips-bin/ - C libraries compiled for MIPS/OpenWRT + luai2c.tar.gz - C i2c library + luasha2.tar.gz - C sha2 library + JSON.lua - JSON library + bit.lua - bitwise operations for Lua 5.1 + +You will additonally need luasec, luaposix and luasocket. But you can easily +find these in your favourite distribution, or even the OpenWRT source tree. diff --git a/code.lua b/code.lua new file mode 100644 index 0000000..818e3c1 --- /dev/null +++ b/code.lua @@ -0,0 +1,234 @@ +-- The MIT License (MIT) +-- +-- Copyright (c) 2013 Sergiusz 'q3k' Bazański +-- +-- 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. + +-- This is the HSWAW lock mk2. source code. WIP. refucktor badly needed. + +local i2c = require("libluai2c") +require('bit') +require('socket') +require('posix') +local sha2 = require('libsha2') +local https = require('ssl.https') + +q3k = {} +q3k.Config = {} + +q3k.Config.I2CBus = 0 + +q3k.Config.I2CGPIO = 0x40 +q3k.Config.I2CNFC = 0x24 + +q3k.I2CWrite = function(Address, ...) + local Data = {...} + local Bytes = string.format("%02x ", Address) + for _, Byte in pairs(Data) do + Bytes = Bytes .. string.format("%02x ", Byte) + end + local DataString = string.char(unpack(Data)) + return i2c.write(q3k.Config.I2CBus, Address, DataString) +end + +q3k.I2CRead = function(Address, BytesOut, ...) + local Data = {...} + local DataString = string.char(unpack(Data)) + local Status, Data = i2c.read(q3k.Config.I2CBus, Address, BytesOut, DataString) + local Return = {} + + if Status == 0 then + Data:gsub(".", function(c) + Return[#Return+1] = string.byte(c) + end) + return Return + end +end + +-- sigh. luaposix on openwrt doesn't have nanosleep() +q3k.Sleep = function(Seconds) + socket.select(nil, nil, Seconds) +end + +-- GPIO Functions +q3k.SetupGPIO = function() + -- Set up pins 28, 29, 30, 31 as output + q3k.I2CWrite(q3k.Config.I2CGPIO, 0x0F, 0x55) + q3k.DoorClose() + -- Set up pins 12, 13, 14, 15 as input without pullups + q3k.I2CWrite(q3k.Config.I2CGPIO, 0x0B, 0xAA) + + -- Turn on GPIO Multiplexer + q3k.I2CWrite(q3k.Config.I2CGPIO, 0x04, 0x01) +end + +q3k.DoorOpen = function() + -- Turn on pin 31 (door) + q3k.I2CWrite(q3k.Config.I2CGPIO, 0x3F, 0x01) +end + +q3k.DoorClose = function() + -- Turn off pin 31 (door) + q3k.I2CWrite(q3k.Config.I2CGPIO, 0x3F, 0x00) +end + +-- NFC (PN532) functions +local PN532_PREAMBLE = 0x00 +local PN532_STARTCODE1 = 0x00 +local PN532_STARTCODE2 = 0xFF +local PN532_POSTAMBLE = 0x00 + +local PN532_HOSTTOPN532 = 0xD4 +local PN532_PN532TOHOST = 0xD5 + +q3k.NFCIRQRead = function() + local Data = q3k.I2CRead(q3k.Config.I2CGPIO, 1, 0x2C) + if Data then + return Data[1] + else + return 1 + end +end + +q3k.NFCWaitForIRQ = function(Timeout) + local Timeout = Timeout or 5 + local WaitStart = os.time() + while os.time() < WaitStart + Timeout do + local IRQStatus = q3k.NFCIRQRead() + if IRQStatus == 0 then + break + end + q3k.Sleep(0.2) + end + local IRQStatus = q3k.NFCIRQRead() + if IRQStatus ~= 0 then + return false + end + return true +end + +q3k.NFCWriteCommand = function(...) + local Command = {...} + local Checksum = PN532_PREAMBLE + PN532_STARTCODE1 + PN532_STARTCODE2 + local WireCommand = { PN532_PREAMBLE, PN532_STARTCODE1, PN532_STARTCODE2 } + + WireCommand[#WireCommand+1] = (#Command + 1) + WireCommand[#WireCommand+1] = bit.band(bit.bnot(#Command +1) + 1, 0xFF) + + Checksum = Checksum + PN532_HOSTTOPN532 + WireCommand[#WireCommand+1] = PN532_HOSTTOPN532 + + for _, Byte in pairs(Command) do + Checksum = Checksum + Byte + WireCommand[#WireCommand+1] = Byte + end + + WireCommand[#WireCommand+1] = bit.band(bit.bnot(Checksum), 0xFF) + WireCommand[#WireCommand+1] = PN532_POSTAMBLE + + q3k.I2CWrite(q3k.Config.I2CNFC, unpack(WireCommand)) +end + +q3k.NFCSendAndAck = function(...) + q3k.NFCWriteCommand(...) + local IRQArrived = q3k.NFCWaitForIRQ() + if not IRQArrived then + return false + end + local ACK = q3k.I2CRead(q3k.Config.I2CNFC, 8) + if ACK[1] == 1 and ACK[2] == 0 and ACK[3] == 0 and ACK[4] == 255 + and ACK[5] == 0 and ACK[6] == 255 and ACK[7] == 0 then + return true + else + return false + end +end + +q3k.NFCReadFrame = function(Count) + local Bytes = q3k.I2CRead(q3k.Config.I2CNFC, Count+2) + table.remove(Bytes, 1) + table.remove(Bytes, #Bytes) + return Bytes +end + +-- Mock until we have a keypad +q3k.ReadPIN = function() + print "[mock] Entering PIN..." + q3k.Sleep(2) + print "[mock] PIN entered." + return { 0, 0, 0, 0 } +end + +-- API stuff + +-- the PIN is a table like { 1, 2, 3, 4 } +-- the NFC ID is a table like { 0xde, 0xad, 0xbe, 0xef } +q3k.CalculateHash = function(PIN, NFCID) + local PINString = string.format("%i%i%i%i", PIN[1], PIN[2], PIN[3], PIN[4]) + local PINNumber = tonumber(PINString) + local IDString = string.format("%02x%02x%02x%02x", NFCID[4], NFCID[3], NFCID[2], NFCID[1]) + local Source = string.format("%08x:%s", PINNumber, IDString) + return sha2.sha256hex(Source) +end + +q3k.GetUserFromHash = function(Hash) + local Body, Code, Headers, Status = https.request('https://auth.hackerspace.pl/mifare', 'hash=' .. Hash) + if Code ~= 200 then + return nil + end + if #Body > 100 then + -- probably an error code + return nil + end + return Body +end + +-- debug bytes +local db = function(Data) + local s = "" + for _, Byte in pairs(Data) do + s = s .. string.format("%02x ", Byte) + end + print(s) +end + +local main = function() + q3k.SetupGPIO() + q3k.NFCSendAndAck(0x14, 0x01, 0x14, 0x01) + + while true do + q3k.NFCSendAndAck(0x4A, 1, 0) + if q3k.NFCWaitForIRQ(120) then + print "Card arrived in field" + local Bytes = q3k.NFCReadFrame(20) + if Bytes ~= nil and #Bytes == 20 then + local NFCID = { Bytes[14], Bytes[15], Bytes[16], Bytes[17] } + local Hash = q3k.CalculateHash(q3k.ReadPIN(), NFCID) + local User = q3k.GetUserFromHash(Hash) + if User == nil then + print "FAILED! no such user!" + else + print(string.format("User: %s", User)) + end + end + end + end +end + +main() diff --git a/lua-libs/JSON.lua b/lua-libs/JSON.lua new file mode 100644 index 0000000..f38ea9d --- /dev/null +++ b/lua-libs/JSON.lua @@ -0,0 +1,861 @@ +-- -*- coding: utf-8 -*- +-- +-- Copyright 2010-2013 Jeffrey Friedl +-- http://regex.info/blog/ +-- +-- Latest copy: http://regex.info/blog/lua/json +-- +local VERSION = 20130120.6 -- version history at end of file +local OBJDEF = { VERSION = VERSION } + +-- +-- Simple JSON encoding and decoding in pure Lua. +-- http://www.json.org/ +-- +-- +-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability +-- +-- +-- DECODING +-- +-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- If the JSON text is for an object or an array, e.g. +-- { "what": "books", "count": 3 } +-- or +-- [ "Larry", "Curly", "Moe" ] +-- +-- the result is a Lua table, e.g. +-- { what = "books", count = 3 } +-- or +-- { "Larry", "Curly", "Moe" } +-- +-- +-- The encode and decode routines accept an optional second argument, "etc", which is not used +-- during encoding or decoding, but upon error is passed along to error handlers. It can be of any +-- type (including nil). +-- +-- With most errors during decoding, this code calls +-- +-- JSON:onDecodeError(message, text, location, etc) +-- +-- with a message about the error, and if known, the JSON text being parsed and the byte count +-- where the problem was discovered. You can replace the default JSON:onDecodeError() with your +-- own function. +-- +-- The default onDecodeError() merely augments the message with data about the text and the +-- location if known (and if a second 'etc' argument had been provided to decode(), its value is +-- tacked onto the message as well), and then calls JSON.assert(), which itself defaults to Lua's +-- built-in assert(), and can also be overridden. +-- +-- For example, in an Adobe Lightroom plugin, you might use something like +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- LrErrors.throwUserError("Internal Error: invalid JSON data") +-- end +-- +-- or even just +-- +-- function JSON.assert(message) +-- LrErrors.throwUserError("Internal Error: " .. message) +-- end +-- +-- If JSON:decode() is passed a nil, this is called instead: +-- +-- JSON:onDecodeOfNilError(message, nil, nil, etc) +-- +-- and if JSON:decode() is passed HTML instead of JSON, this is called: +-- +-- JSON:onDecodeOfHTMLError(message, text, nil, etc) +-- +-- The use of the fourth 'etc' argument allows stronger coordination between decoding and error +-- reporting, especially when you provide your own error-handling routines. Continuing with the +-- the Adobe Lightroom plugin example: +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- local note = "Internal Error: invalid JSON data" +-- if type(etc) = 'table' and etc.photo then +-- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') +-- end +-- LrErrors.throwUserError(note) +-- end +-- +-- : +-- : +-- +-- for i, photo in ipairs(photosToProcess) do +-- : +-- : +-- local data = JSON:decode(someJsonText, { photo = photo }) +-- : +-- : +-- end +-- +-- +-- +-- + +-- DECODING AND STRICT TYPES +-- +-- Because both JSON objects and JSON arrays are converted to Lua tables, it's not normally +-- possible to tell which a Lua table came from, or guarantee decode-encode round-trip +-- equivalency. +-- +-- However, if you enable strictTypes, e.g. +-- +-- JSON = (loadfile "JSON.lua")() --load the routines +-- JSON.strictTypes = true +-- +-- then the Lua table resulting from the decoding of a JSON object or JSON array is marked via Lua +-- metatable, so that when re-encoded with JSON:encode() it ends up as the appropriate JSON type. +-- +-- (This is not the default because other routines may not work well with tables that have a +-- metatable set, for example, Lightroom API calls.) +-- +-- +-- ENCODING +-- +-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability + +-- On error during encoding, this code calls: +-- +-- JSON:onEncodeError(message, etc) +-- +-- which you can override in your local JSON object. +-- +-- +-- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT +-- +-- assert +-- onDecodeError +-- onDecodeOfNilError +-- onDecodeOfHTMLError +-- onEncodeError +-- +-- If you want to create a separate Lua JSON object with its own error handlers, +-- you can reload JSON.lua or use the :new() method. +-- +--------------------------------------------------------------------------- + + +local author = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json), version " .. tostring(VERSION) .. " ]-" +local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray +local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject + + +function OBJDEF:newArray(tbl) + return setmetatable(tbl or {}, isArray) +end + +function OBJDEF:newObject(tbl) + return setmetatable(tbl or {}, isObject) +end + +local function unicode_codepoint_as_utf8(codepoint) + -- + -- codepoint is a number + -- + if codepoint <= 127 then + return string.char(codepoint) + + elseif codepoint <= 2047 then + -- + -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 + -- + local highpart = math.floor(codepoint / 0x40) + local lowpart = codepoint - (0x40 * highpart) + return string.char(0xC0 + highpart, + 0x80 + lowpart) + + elseif codepoint <= 65535 then + -- + -- 1110yyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x1000) + local remainder = codepoint - 0x1000 * highpart + local midpart = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midpart + + highpart = 0xE0 + highpart + midpart = 0x80 + midpart + lowpart = 0x80 + lowpart + + -- + -- Check for an invalid character (thanks Andy R. at Adobe). + -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 + -- + if ( highpart == 0xE0 and midpart < 0xA0 ) or + ( highpart == 0xED and midpart > 0x9F ) or + ( highpart == 0xF0 and midpart < 0x90 ) or + ( highpart == 0xF4 and midpart > 0x8F ) + then + return "?" + else + return string.char(highpart, + midpart, + lowpart) + end + + else + -- + -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x40000) + local remainder = codepoint - 0x40000 * highpart + local midA = math.floor(remainder / 0x1000) + remainder = remainder - 0x1000 * midA + local midB = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midB + + return string.char(0xF0 + highpart, + 0x80 + midA, + 0x80 + midB, + 0x80 + lowpart) + end +end + +function OBJDEF:onDecodeError(message, text, location, etc) + if text then + if location then + message = string.format("%s at char %d of: %s", message, location, text) + else + message = string.format("%s: %s", message, text) + end + end + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError +OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError + +function OBJDEF:onEncodeError(message, etc) + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +local function grok_number(self, text, start, etc) + -- + -- Grab the integer part + -- + local integer_part = text:match('^-?[1-9]%d*', start) + or text:match("^-?0", start) + + if not integer_part then + self:onDecodeError("expected number", text, start, etc) + end + + local i = start + integer_part:len() + + -- + -- Grab an optional decimal part + -- + local decimal_part = text:match('^%.%d+', i) or "" + + i = i + decimal_part:len() + + -- + -- Grab an optional exponential part + -- + local exponent_part = text:match('^[eE][-+]?%d+', i) or "" + + i = i + exponent_part:len() + + local full_number_text = integer_part .. decimal_part .. exponent_part + local as_number = tonumber(full_number_text) + + if not as_number then + self:onDecodeError("bad number", text, start, etc) + end + + return as_number, i +end + + +local function grok_string(self, text, start, etc) + + if text:sub(start,start) ~= '"' then + self:onDecodeError("expected string's opening quote", text, start, etc) + end + + local i = start + 1 -- +1 to bypass the initial quote + local text_len = text:len() + local VALUE = "" + while i <= text_len do + local c = text:sub(i,i) + if c == '"' then + return VALUE, i + 1 + end + if c ~= '\\' then + VALUE = VALUE .. c + i = i + 1 + elseif text:match('^\\b', i) then + VALUE = VALUE .. "\b" + i = i + 2 + elseif text:match('^\\f', i) then + VALUE = VALUE .. "\f" + i = i + 2 + elseif text:match('^\\n', i) then + VALUE = VALUE .. "\n" + i = i + 2 + elseif text:match('^\\r', i) then + VALUE = VALUE .. "\r" + i = i + 2 + elseif text:match('^\\t', i) then + VALUE = VALUE .. "\t" + i = i + 2 + else + local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if hex then + i = i + 6 -- bypass what we just read + + -- We have a Unicode codepoint. It could be standalone, or if in the proper range and + -- followed by another in a specific range, it'll be a two-code surrogate pair. + local codepoint = tonumber(hex, 16) + if codepoint >= 0xD800 and codepoint <= 0xDBFF then + -- it's a hi surrogate... see whether we have a following low + local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if lo_surrogate then + i = i + 6 -- bypass the low surrogate we just read + codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) + else + -- not a proper low, so we'll just leave the first codepoint as is and spit it out. + end + end + VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) + + else + + -- just pass through what's escaped + VALUE = VALUE .. text:match('^\\(.)', i) + i = i + 2 + end + end + end + + self:onDecodeError("unclosed string", text, start, etc) +end + +local function skip_whitespace(text, start) + + local match_start, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 + if match_end then + return match_end + 1 + else + return start + end +end + +local grok_one -- assigned later + +local function grok_object(self, text, start, etc) + if not text:sub(start,start) == '{' then + self:onDecodeError("expected '{'", text, start, etc) + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' + + local VALUE = self.strictTypes and self:newObject { } or { } + + if text:sub(i,i) == '}' then + return VALUE, i + 1 + end + local text_len = text:len() + while i <= text_len do + local key, new_i = grok_string(self, text, i, etc) + + i = skip_whitespace(text, new_i) + + if text:sub(i, i) ~= ':' then + self:onDecodeError("expected colon", text, i, etc) + end + + i = skip_whitespace(text, i + 1) + + local val, new_i = grok_one(self, text, i) + + VALUE[key] = val + + -- + -- Expect now either '}' to end things, or a ',' to allow us to continue. + -- + i = skip_whitespace(text, new_i) + + local c = text:sub(i,i) + + if c == '}' then + return VALUE, i + 1 + end + + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or '}'", text, i, etc) + end + + i = skip_whitespace(text, i + 1) + end + + self:onDecodeError("unclosed '{'", text, start, etc) +end + +local function grok_array(self, text, start, etc) + if not text:sub(start,start) == '[' then + self:onDecodeError("expected '['", text, start, etc) + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' + local VALUE = self.strictTypes and self:newArray { } or { } + if text:sub(i,i) == ']' then + return VALUE, i + 1 + end + + local text_len = text:len() + while i <= text_len do + local val, new_i = grok_one(self, text, i) + + table.insert(VALUE, val) + + i = skip_whitespace(text, new_i) + + -- + -- Expect now either ']' to end things, or a ',' to allow us to continue. + -- + local c = text:sub(i,i) + if c == ']' then + return VALUE, i + 1 + end + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or '['", text, i, etc) + end + i = skip_whitespace(text, i + 1) + end + self:onDecodeError("unclosed '['", text, start, etc) +end + + +grok_one = function(self, text, start, etc) + -- Skip any whitespace + start = skip_whitespace(text, start) + + if start > text:len() then + self:onDecodeError("unexpected end of string", text, nil, etc) + end + + if text:find('^"', start) then + return grok_string(self, text, start, etc) + + elseif text:find('^[-0123456789 ]', start) then + return grok_number(self, text, start, etc) + + elseif text:find('^%{', start) then + return grok_object(self, text, start, etc) + + elseif text:find('^%[', start) then + return grok_array(self, text, start, etc) + + elseif text:find('^true', start) then + return true, start + 4 + + elseif text:find('^false', start) then + return false, start + 5 + + elseif text:find('^null', start) then + return nil, start + 4 + + else + self:onDecodeError("can't parse JSON", text, start, etc) + end +end + +function OBJDEF:decode(text, etc) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc) + end + + if text == nil then + self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc) + elseif type(text) ~= 'string' then + self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc) + end + + if text:match('^%s*$') then + return nil + end + + if text:match('^%s*<') then + -- Can't be JSON... we'll assume it's HTML + self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc) + end + + -- + -- Ensure that it's not UTF-32 or UTF-16. + -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), + -- but this package can't handle them. + -- + if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then + self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc) + end + + local success, value = pcall(grok_one, self, text, 1, etc) + if success then + return value + else + -- should never get here... JSON parse errors should have been caught earlier + assert(false, value) + return nil + end +end + +local function backslash_replacement_function(c) + if c == "\n" then + return "\\n" + elseif c == "\r" then + return "\\r" + elseif c == "\t" then + return "\\t" + elseif c == "\b" then + return "\\b" + elseif c == "\f" then + return "\\f" + elseif c == '"' then + return '\\"' + elseif c == '\\' then + return '\\\\' + else + return string.format("\\u%04x", c:byte()) + end +end + +local chars_to_be_escaped_in_JSON_string + = '[' + .. '"' -- class sub-pattern to match a double quote + .. '%\\' -- class sub-pattern to match a backslash + .. '%z' -- class sub-pattern to match a null + .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters + .. ']' + +local function json_string_literal(value) + local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) + return '"' .. newval .. '"' +end + +local function object_or_array(self, T, etc) + -- + -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON + -- object. If there are only numbers, it's a JSON array. + -- + -- If we'll be converting to a JSON object, we'll want to sort the keys so that the + -- end result is deterministic. + -- + local string_keys = { } + local seen_number_key = false + local maximum_number_key + + for key in pairs(T) do + if type(key) == 'number' then + seen_number_key = true + if not maximum_number_key or maximum_number_key < key then + maximum_number_key = key + end + elseif type(key) == 'string' then + table.insert(string_keys, key) + else + self:onEncodeError("can't encode table with a key of type " .. type(key), etc) + end + end + + if seen_number_key and #string_keys > 0 then + -- + -- Mixed key types... don't know what to do, so bail + -- + self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) + + elseif #string_keys == 0 then + -- + -- An array + -- + if seen_number_key then + return nil, maximum_number_key -- an array + else + -- + -- An empty table... + -- + if tostring(T) == "JSON array" then + return nil + elseif tostring(T) == "JSON object" then + return { } + else + -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects + return nil + end + end + else + -- + -- An object, so return a list of keys + -- + table.sort(string_keys) + return string_keys + end +end + +-- +-- Encode +-- +local encode_value -- must predeclare because it calls itself +function encode_value(self, value, parents, etc) + + + if value == nil then + return 'null' + end + + if type(value) == 'string' then + return json_string_literal(value) + elseif type(value) == 'number' then + if value ~= value then + -- + -- NaN (Not a Number). + -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. + -- + return "null" + elseif value >= math.huge then + -- + -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should + -- really be a package option. Note: at least with some implementations, positive infinity + -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. + -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" + -- case first. + -- + return "1e+9999" + elseif value <= -math.huge then + -- + -- Negative infinity. + -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. + -- + return "-1e+9999" + else + return tostring(value) + end + elseif type(value) == 'boolean' then + return tostring(value) + + elseif type(value) ~= 'table' then + self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) + + else + -- + -- A table to be converted to either a JSON object or array. + -- + local T = value + + if parents[T] then + self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) + else + parents[T] = true + end + + local result_value + + local object_keys, maximum_number_key = object_or_array(self, T, etc) + if maximum_number_key then + -- + -- An array... + -- + local ITEMS = { } + for i = 1, maximum_number_key do + table.insert(ITEMS, encode_value(self, T[i], parents, etc)) + end + + result_value = "[" .. table.concat(ITEMS, ",") .. "]" + elseif object_keys then + -- + -- An object + -- + + -- + -- We'll always sort the keys, so that comparisons can be made on + -- the results, etc. The actual order is not particularly + -- important (e.g. it doesn't matter what character set we sort + -- as); it's only important that it be deterministic... the same + -- every time. + -- + local PARTS = { } + for _, key in ipairs(object_keys) do + local encoded_key = encode_value(self, tostring(key), parents, etc) + local encoded_val = encode_value(self, T[key], parents, etc) + table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) + end + result_value = "{" .. table.concat(PARTS, ",") .. "}" + else + -- + -- An empty array/object... we'll treat it as an array, though it should really be an option + -- + result_value = "[]" + end + + parents[T] = false + return result_value + end +end + +local encode_pretty_value -- must predeclare because it calls itself +function encode_pretty_value(self, value, parents, indent, etc) + + if type(value) == 'string' then + return json_string_literal(value) + + elseif type(value) == 'number' then + return tostring(value) + + elseif type(value) == 'boolean' then + return tostring(value) + + elseif type(value) == 'nil' then + return 'null' + + elseif type(value) ~= 'table' then + self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) + + else + -- + -- A table to be converted to either a JSON object or array. + -- + local T = value + + if parents[T] then + self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) + end + parents[T] = true + + local result_value + + local object_keys = object_or_array(self, T, etc) + if not object_keys then + -- + -- An array... + -- + local ITEMS = { } + for i = 1, #T do + table.insert(ITEMS, encode_pretty_value(self, T[i], parents, indent, etc)) + end + + result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" + + else + + -- + -- An object -- can keys be numbers? + -- + + local KEYS = { } + local max_key_length = 0 + for _, key in ipairs(object_keys) do + local encoded = encode_pretty_value(self, tostring(key), parents, "", etc) + max_key_length = math.max(max_key_length, #encoded) + table.insert(KEYS, encoded) + end + local key_indent = indent .. " " + local subtable_indent = indent .. string.rep(" ", max_key_length + 2 + 4) + local FORMAT = "%s%" .. tostring(max_key_length) .. "s: %s" + + local COMBINED_PARTS = { } + for i, key in ipairs(object_keys) do + local encoded_val = encode_pretty_value(self, T[key], parents, subtable_indent, etc) + table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) + end + result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" + end + + parents[T] = false + return result_value + end +end + +function OBJDEF:encode(value, etc) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) + end + + local parents = {} + return encode_value(self, value, parents, etc) +end + +function OBJDEF:encode_pretty(value, etc) + local parents = {} + local subtable_indent = "" + return encode_pretty_value(self, value, parents, subtable_indent, etc) +end + +function OBJDEF.__tostring() + return "JSON encode/decode package" +end + +OBJDEF.__index = OBJDEF + +function OBJDEF:new(args) + local new = { } + + if args then + for key, val in pairs(args) do + new[key] = val + end + end + + return setmetatable(new, OBJDEF) +end + +return OBJDEF:new() + +-- +-- Version history: +-- +-- 20130120.6 Comment update: added a link to the specific page on my blog where this code can +-- be found, so that folks who come across the code outside of my blog can find updates +-- more easily. +-- +-- 20111207.5 Added support for the 'etc' arguments, for better error reporting. +-- +-- 20110731.4 More feedback from David Kolf on how to make the tests for Nan/Infinity system independent. +-- +-- 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules: +-- +-- * When encoding lua for JSON, Sparse numeric arrays are now handled by +-- spitting out full arrays, such that +-- JSON:encode({"one", "two", [10] = "ten"}) +-- returns +-- ["one","two",null,null,null,null,null,null,null,"ten"] +-- +-- In 20100810.2 and earlier, only up to the first non-null value would have been retained. +-- +-- * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999". +-- Version 20100810.2 and earlier created invalid JSON in both cases. +-- +-- * Unicode surrogate pairs are now detected when decoding JSON. +-- +-- 20100810.2 added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding +-- +-- 20100731.1 initial public release +-- diff --git a/lua-libs/bit.lua b/lua-libs/bit.lua new file mode 100644 index 0000000..94e60d7 --- /dev/null +++ b/lua-libs/bit.lua @@ -0,0 +1,260 @@ +--[[--------------- +LuaBit v0.4 +------------------- +a bitwise operation lib for lua. + +http://luaforge.net/projects/bit/ + +How to use: +------------------- + bit.bnot(n) -- bitwise not (~n) + bit.band(m, n) -- bitwise and (m & n) + bit.bor(m, n) -- bitwise or (m | n) + bit.bxor(m, n) -- bitwise xor (m ^ n) + bit.brshift(n, bits) -- right shift (n >> bits) + bit.blshift(n, bits) -- left shift (n << bits) + bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>) + +Please note that bit.brshift and bit.blshift only support number within +32 bits. + +2 utility functions are provided too: + bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence) + -- high bits first + bit.tonumb(bit_tbl) -- convert a bit table into a number +------------------- + +Under the MIT license. + +copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com) +--]]--------------- + +do + +------------------------ +-- bit lib implementions + +local function check_int(n) + -- checking not float + if(n - math.floor(n) > 0) then + error("trying to use bitwise operation on non-integer!") + end +end + +local function to_bits(n) + check_int(n) + if(n < 0) then + -- negative + return to_bits(bit.bnot(math.abs(n)) + 1) + end + -- to bits table + local tbl = {} + local cnt = 1 + while (n > 0) do + local last = math.mod(n,2) + if(last == 1) then + tbl[cnt] = 1 + else + tbl[cnt] = 0 + end + n = (n-last)/2 + cnt = cnt + 1 + end + + return tbl +end + +local function tbl_to_number(tbl) + local n = table.getn(tbl) + + local rslt = 0 + local power = 1 + for i = 1, n do + rslt = rslt + tbl[i]*power + power = power*2 + end + + return rslt +end + +local function expand(tbl_m, tbl_n) + local big = {} + local small = {} + if(table.getn(tbl_m) > table.getn(tbl_n)) then + big = tbl_m + small = tbl_n + else + big = tbl_n + small = tbl_m + end + -- expand small + for i = table.getn(small) + 1, table.getn(big) do + small[i] = 0 + end + +end + +local function bit_or(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i]== 0 and tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl_to_number(tbl) +end + +local function bit_and(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i]== 0 or tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl_to_number(tbl) +end + +local function bit_not(n) + + local tbl = to_bits(n) + local size = math.max(table.getn(tbl), 32) + for i = 1, size do + if(tbl[i] == 1) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + return tbl_to_number(tbl) +end + +local function bit_xor(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i] ~= tbl_n[i]) then + tbl[i] = 1 + else + tbl[i] = 0 + end + end + + --table.foreach(tbl, print) + + return tbl_to_number(tbl) +end + +local function bit_rshift(n, bits) + check_int(n) + + local high_bit = 0 + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + high_bit = 2147483648 -- 0x80000000 + end + + for i=1, bits do + n = n/2 + n = bit_or(math.floor(n), high_bit) + end + return math.floor(n) +end + +-- logic rightshift assures zero filling shift +local function bit_logic_rshift(n, bits) + check_int(n) + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + end + for i=1, bits do + n = n/2 + end + return math.floor(n) +end + +local function bit_lshift(n, bits) + check_int(n) + + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + end + + for i=1, bits do + n = n*2 + end + return bit_and(n, 4294967295) -- 0xFFFFFFFF +end + +local function bit_xor2(m, n) + local rhs = bit_or(bit_not(m), bit_not(n)) + local lhs = bit_or(m, n) + local rslt = bit_and(lhs, rhs) + return rslt +end + +-------------------- +-- bit lib interface + +bit = { + -- bit operations + bnot = bit_not, + band = bit_and, + bor = bit_or, + bxor = bit_xor, + brshift = bit_rshift, + blshift = bit_lshift, + bxor2 = bit_xor2, + blogic_rshift = bit_logic_rshift, + + -- utility func + tobits = to_bits, + tonumb = tbl_to_number, +} + +end + +--[[ +for i = 1, 100 do + for j = 1, 100 do + if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then + error("bit.xor failed.") + end + end +end +--]] + + + + + + + + + + + + + diff --git a/lua-libs/luai2c.tar.bz2 b/lua-libs/luai2c.tar.bz2 new file mode 100644 index 0000000..712c8e0 Binary files /dev/null and b/lua-libs/luai2c.tar.bz2 differ diff --git a/lua-libs/luasha2-0.1.tar.gz b/lua-libs/luasha2-0.1.tar.gz new file mode 100644 index 0000000..577fc37 Binary files /dev/null and b/lua-libs/luasha2-0.1.tar.gz differ diff --git a/lua-libs/mips-bin/libluai2c.so b/lua-libs/mips-bin/libluai2c.so new file mode 100755 index 0000000..87d3d0e Binary files /dev/null and b/lua-libs/mips-bin/libluai2c.so differ diff --git a/lua-libs/mips-bin/libsha2.so b/lua-libs/mips-bin/libsha2.so new file mode 100755 index 0000000..81741f0 Binary files /dev/null and b/lua-libs/mips-bin/libsha2.so differ