cold-widow (empty) → 0.1.2
raw patch · 11 files changed
+416/−0 lines, 11 filesdep +basedep +bytestringdep +cold-widowsetup-changed
Dependencies added: base, bytestring, cold-widow, hspec
Files
- LICENSE +30/−0
- README.md +108/−0
- Setup.hs +2/−0
- cold-widow.cabal +61/−0
- compact-decode/Main.hs +5/−0
- decode/Main.hs +27/−0
- encode/Main.hs +20/−0
- lib/Codec/Binary/Coldwidow.hs +88/−0
- src/Main.hs +5/−0
- tests/Codec/Binary/ColdwidowSpec.hs +68/−0
- tests/Spec.hs +2/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Mihai Giurgeanu (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Mihai Giurgeanu nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,108 @@+# cold-widow++Executables and Haskell library to transfer files via QR Codes.++The idea is to generate a list of qr-coedes representing the archived+version folder. The qr-codes will be read as text values by any qr-code+reader supporting alpahnumeric encoding. The texts can be send via any+technology, like email, sms, whatsapp, skype, hangouts, etc to the final+destination. At the final destination, you feed these texts to the decoder+and get the original file structure.++## Installation++The only supported installation method is from source files,+[stack](http://www.haskellstack.org/).++## Building from source++### Prerequisites++You will need to [download and install stack](https://docs.haskellstack.org/en/stable/README/#how-to-install).++You also need [git](https://git-scm.com/) to get the latest source code.++### Get the source++ git clone https://github.com/mihaigiurgeanu/cold-widow.git+ +### Building++To build the project, you need first to run `stack setup`. This command+will make sure you have the correct haskell compiler, and, if you don't+have it, it will download and install one in a separate location in such+a way to not interract with your existing haskell environment (if you have one):++ #> cd /the/location/of/cold-widow/+ #> stack setup+ +After the setup (you only need to run setup once) you may build, test or install+the software. To build, simply issue:++ #> stack build+ +To run the tests:++ #> stack test+ +To install it in the stack's install directory, type:++ #> stack install+ +## Usage++The only functions implemented until now are encoding and decoding a file to/from+a textual form using only the alphanumeric symbols allowed in a QR Code. This will+allow you to read the generated QR Code with any QR Code reader, copy paste the+text in an email or whatever transport you choose.++To generate QR Codes you need to use external programs to archive and compress+your files, to split the archive in appropriate size to be encoded in the QR Codes.+For example:++ #> tar cv informic-0.1.0/*.patch | bzip2 -9 | split -b 2900 - informic-0.1.0/x+ +will archive the files with the extension `.patch` located in the `informic-0.1.0/`+folder, will compress the archive using `bzpi2` utility, will split the resulting+compressed archived in files named `xaa`, `xab`, `xac`, etc. of 2900 bytes each+and will put these files into `informic-0.1.0/` folder.++To encode those files using _cold-widow's encode45_ you could use the following:++ #> cd informic-0.1.0+ #> for i in x*; do encode45 $i > $i.txt; done + +Then you should use a qr-code generator to generate one qr-code for each+`xaa.txt`, `xab.txt`, `xac.txt`, etc files generated by the above commands. Scan+the qr-codes with you mobile phone and copy-paste the text into a email message+that you can send to anyone you want.++Finally, using `decode45` you can convert the fragments of text back to the original+archive. Copy in the clipboard the text coresponding to first part (the file `xaa`+in the example above) and paste it in a file, for example in the `xaa.txt` file:++ #> decode45 xaa < xaa.txt++This will generate on disk the file named `xaa` with the same contents of the +original `xaa` file which is a part of the splited compressed archive. After+doing this for all file parts, you can use the following to obtain the original+files structure:++ #> cat x* | bzcat | tar xv++## encode45++The `encode45` utility will get a file as first argument and will output+the encoded text representing the file. The text will contain only characters+allowed by the qr-code alphanumeric mode.++To use it as a qr-code, you need to pass a maximum of about 2900 bytes file to+the `encode45` utility.++## decode45++`decode45` will read from standard output a text containing only the characters+allowed in qr-code alphanumeric mode and will decoded as a binary file. The name+of the file to which `decode45` will save the binary data _must_ be passed as+the first argument of the `decode45` method.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cold-widow.cabal view
@@ -0,0 +1,61 @@+name: cold-widow+version: 0.1.2+synopsis: File transfer via QR Codes.+description: Utilities and Haskell library to transfer files via qr-codes.+homepage: https://github.com/mihaigiurgeanu/cold-widow#readme+license: BSD3+license-file: LICENSE+author: Mihai Giurgeanu+maintainer: mihai.giurgeau@gmail.com+copyright: 2016 Mihai Giurgeanu+category: Utility+build-type: Simple+cabal-version: >=1.10+extra-doc-files: README.md++library+ hs-source-dirs: lib+ exposed-modules: Codec.Binary.Coldwidow+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5,+ bytestring++Executable cold-widow+ hs-source-dirs: src+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5++executable encode45+ hs-source-dirs: encode+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5,+ bytestring,+ cold-widow++executable decode45+ hs-source-dirs: decode+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5,+ bytestring,+ cold-widow++executable compact-decode45+ hs-source-dirs: compact-decode+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5,+ bytestring++Test-Suite cold-widow-tests+ hs-source-dirs: tests+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ default-language: Haskell2010+ other-modules: Codec.Binary.ColdwidowSpec+ build-depends: base >= 4.7 && < 5,+ hspec,+ bytestring,+ cold-widow
+ compact-decode/Main.hs view
@@ -0,0 +1,5 @@+-- compact-decode.hs+-- A small variant of decode, meant to have very small source code++module Main where{import System.Environment (getArgs);import qualified Data.ByteString.Lazy as B;import System.IO (withBinaryFile, IOMode(..));import Data.Word (Word8);import Numeric (readInt);import Data.Maybe (fromJust, isJust);import Data.List (elemIndex);import Data.Bits (shiftR);main=do{as<-getArgs;let {fn = as!!0};t<-getContents;let{(z, nz)=span (=='0') t};withBinaryFile fn WriteMode (\h -> do {mapM_ (\_ -> B.hPut h (B.singleton 0)) z;if null nz then return () else B.hPut h $ ui $ dcd $ filter (\c -> c /= '\n' && c/= '\r') nz;});};cs ="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";b=45;dcd e=let{p=rdi e}in case p of{[(val, [])]->val;otherwise->error $ "Parse error at: " ++ (take 10 $ snd $ p !! 0);};ui 0=B.singleton 0;ui x=B.pack $ ui' x [];rdi=readInt b iD fD;iD d=isJust $ elemIndex d cs;fD d = fromJust $ elemIndex d cs;ui' 0 r = r;ui' v r = ui' (v `shiftR` 8) $ (fromInteger v :: Word8) : r;}+
+ decode/Main.hs view
@@ -0,0 +1,27 @@+-- File: decode/Main.hs++module Main where++import System.Environment (getArgs)+import qualified Data.ByteString.Lazy as B+import System.IO (withBinaryFile, IOMode(..))+import Codec.Binary.Coldwidow++main :: IO ()+main = do + args <- getArgs+ let fileName = args !! 0+ encodedText <- getContents+ let (zeroes, nonzeroes) = span (=='0') encodedText+ withBinaryFile+ fileName+ WriteMode+ (\h -> do+ mapM_ (\_ -> B.hPut h (B.singleton 0)) zeroes+ if null nonzeroes+ then return ()+ else B.hPut h $ unpackInteger $ decode $ filter (\c -> c /= '\n' && c/= '\r') nonzeroes)++++
+ encode/Main.hs view
@@ -0,0 +1,20 @@+-- File: encode/Main.hs++module Main where++import System.Environment (getArgs)+import qualified Data.ByteString.Lazy as B++import Codec.Binary.Coldwidow++main :: IO ()+main = do + args <- getArgs+ let fileName = args !! 0+ bytes <- B.readFile fileName+ let (zeroes, nonzeroes) = B.span (==0) bytes+ mapM_ (\_ -> putStr "0") (B.unpack zeroes)+ if B.null nonzeroes+ then return ()+ else putStr $ encode $ packInteger nonzeroes+
+ lib/Codec/Binary/Coldwidow.hs view
@@ -0,0 +1,88 @@+{- |+Module: Codec.Binary.Coldwidow+Description: Base45 encoding/decoding ++QR Code alphanumeric mode accepts a set of 45 characters. This module offers+functions to encode/decode binary data to/from text representation using+only the 45 characters allowed by the qr-code alphanumeric mode.+-}+module Codec.Binary.Coldwidow (encode, decode, packInteger, unpackInteger) where++import Data.Bits (shiftL, (.|.), shiftR)+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B+import Data.Word (Word8)+import Numeric (showIntAtBase, readInt)+import Data.List (elemIndex)+import Data.Maybe (fromJust, isJust)+++chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',+ 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*',+ '+', '-', '.', '/', ':']++base = 45++-- external inteface++-- |Encodes binary data into a String. The resulting string will contain+-- only the 45 characters allowed by the qr-code alphanumeric mode. The+-- binary data to be encoded should be represented as a Haskell Integer. To+-- convert a ByteString to a Haskell Integer you can use the 'packInteger'+-- function defined bellow.+encode :: Integer -> String+encode x = showIntAtBase base toDigit x ""++-- |Converts a BinaryString to an Integer. This is used to represent binary+-- data as a Haskell Integer tha can be passed to 'encode' function defined above.+packInteger :: ByteString -> Integer+packInteger s = packInteger' (B.unpack s) 0++-- |Decodes binary data from its text representation. The text representation of+-- data, obtained with the 'encode' function defined above, will contain only+-- characters allowed in the qr-code alphanumeric mode. The decoded binary data+-- returned by this function will be represented as a Haskell Integer. To convert+-- it into a BinaryString you can use the 'unpackInteger' function defined bellow.+decode :: String -> Integer+decode encoded = let parsed = readEncodedInt encoded+ in case parsed of+ [(val, [])] -> val+ otherwise -> error $ "Error parsing encoded value."++parseMessages+ where+ parseMessages = concatMap parseMessage parsed++-- |Converts a Haskell Integer into a ByteString. The Integer holds the binary+-- represantation of the data you obtain by using the `decode` function defined+-- above. You can use the ByteString represantation to, for example, save the+-- binary data into a file.+unpackInteger :: Integer -> ByteString+unpackInteger 0 = B.singleton 0+unpackInteger x = B.pack $ unpackInteger' x []++-- internal functions++toDigit :: Int -> Char +toDigit i = chars !! i -- TODO: make a more efficient implementation++readEncodedInt :: ReadS Integer+readEncodedInt = readInt base isDigit fromDigit++isDigit :: Char -> Bool+isDigit digit = isJust $ elemIndex digit chars++fromDigit :: Char -> Int+fromDigit digit = fromJust $ elemIndex digit chars++packInteger' :: [Word8] -> Integer -> Integer+packInteger' [] result = result+packInteger' (d:ds) result = packInteger' ds ((fromIntegral d) .|. (result `shiftL` 8))++unpackInteger' :: Integer -> [Word8] -> [Word8]+unpackInteger' 0 result = result+unpackInteger' value result = unpackInteger' (value `shiftR` 8) $ (fromInteger value :: Word8) : result++parseMessage :: (Integer, String) -> String+parseMessage (parsed, remainder) = "\n\tStopped at: " ++ (take 20 remainder)+
+ src/Main.hs view
@@ -0,0 +1,5 @@+module Main where++main :: IO ()+main = do+ putStrLn "hello world"
+ tests/Codec/Binary/ColdwidowSpec.hs view
@@ -0,0 +1,68 @@+-- File: tests/Codec/Binary/ColdwidowSpec.hs++module Codec.Binary.ColdwidowSpec (spec) where++import Test.Hspec+import Codec.Binary.Coldwidow++import Data.ByteString.Lazy (singleton, pack)++spec :: Spec+spec = do+ describe "encode" $ do+ it "returns character \"0\" when encoding value 0" $+ encode 0 `shouldBe` "0"+ it "returns character \":\" when encoding vlaue 44" $+ encode 44 `shouldBe` ":"+ it "returns characters \"10\" when encoding value 45" $+ encode 45 `shouldBe` "10"+ it "returns charcters \"11\" when encoding value 46" $+ encode 46 `shouldBe` "11"+ it "returns charcters \"1:\" when encoding value 89" $+ encode 89 `shouldBe` "1:"+ it "returns characters \":0\" when encoding value 45*44" $+ encode (45*44) `shouldBe` ":0"+ it "returns characters \"::\" when encoding value 45*44+44" $+ encode (45*44+44) `shouldBe` "::"+ + describe "decode" $ do+ it "decodes \"0\" as 0" $+ decode "0" `shouldBe` 0+ it "decodes \":\" as 44" $+ decode ":" `shouldBe` 44+ it "decodes \"10\" as 45" $+ decode "10" `shouldBe` 45+ it "decodes \"11\" as 46" $+ decode "11" `shouldBe` 46+ it "decodes \"1:\" as 89" $+ decode "1:" `shouldBe` 89+ it "decodes \":0\" as 45*44" $+ decode ":0" `shouldBe` (45*44)+ it "decodes \"::\" as 45*44+44" $+ decode "::" `shouldBe` (45*44+44)+ it "decodes \"Q\" as 26" $+ decode "Q" `shouldBe` 26++ describe "packInteger" $ do+ it "packs [0] as 0" $+ packInteger (singleton 0) `shouldBe` 0+ it "packs [0, 0] as 0" $+ packInteger (pack [0, 0]) `shouldBe` 0+ it "packs [1] as 1" $+ packInteger (singleton 1) `shouldBe` 1+ it "packs [0, 1] as 1" $+ packInteger (pack [0, 1]) `shouldBe` 1+ it "packs [1, 0] as 256" $+ packInteger (pack [1, 0]) `shouldBe` 256++ describe "unpackInteger" $ do+ it "unpacks 0 as [0]" $+ unpackInteger 0 `shouldBe` (singleton 0)+ it "unpacks 1 as [1]" $+ unpackInteger 1 `shouldBe` (singleton 1)+ it "unpacks 256 as [1, 0]" $+ unpackInteger 256 `shouldBe` (pack [1, 0])+ it "unpacks 65535 as [255, 255]" $+ unpackInteger 65535 `shouldBe` (pack [255, 255])+ it "unpacks 1+2*256+3*256*256+4*256*256*256 as [4, 3, 2, 1]" $+ unpackInteger (1+2*256+3*256*256+4*256*256*256) `shouldBe` (pack [4, 3, 2, 1])
+ tests/Spec.hs view
@@ -0,0 +1,2 @@+-- File: tests/Spec.hs+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}