hanvon 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjMwZjdlYmU0ZDJjMjMzOTI1Y2Q1YzU5NTczYjBlZWY4OTBlNzZmNg==
5
+ data.tar.gz: !binary |-
6
+ ZjlhYTY0YjAwODg4MTFjZGQyZmYxY2ExNjZlMDg0ZTgzN2M0M2MwNA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MTQ3MmJmMWQxOTFhNTQwY2ZhZWM5MDljZGNiYzFhMjI0YjRhYzBjMDE5MmI1
10
+ NjU0ZTczOGQxY2JhNjdjMGQxNzdlZGIzZmY0YmNlMTIxZTZjNmZhODk4NDcx
11
+ MTQ4NzE1ZmJjOTRhNTRlNTVhYmQ2ZThjYzA1ODQxYjNlNWNjOGE=
12
+ data.tar.gz: !binary |-
13
+ N2FmNWU3Y2Y5N2U3OGIwYTVjMzYxOWE2MzJiM2MxN2I4NDRiNjBkYTQ1NGJi
14
+ MDYyYzJhZmY2MmY1ODFiYTk0ODFmMGI3YzViNTQ2NWExZjhmMDNkMmQ0ZGE5
15
+ YTg1OTgyYTc5MjNjYmM5MjRiOGRhZjc3NWZjYjE3NjY2YjUwNWI=
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hanvon.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Heinrich Lee Yu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Ruby Hanvon Client Library
2
+
3
+ This is a ruby client that communicates with Hanvon Time & Attendance devices over the network.
4
+
5
+ Hanvon provides an SDK consisting only of a file named HwDevComm.dll. There is no official Linux support. Hence, I decided to reverse engineer the protocol. When used without a commukey / password, you can easily communicate with the device via a TCP socket and send the commands in plain text. However, when a key is used, messages sent and received are encrypted.
6
+
7
+ This is not official code from Hanvon. This is tested on the F710, but should work for the other models.
8
+
9
+ For more info on Hanvon devices, visit:
10
+
11
+ http://www.hanvon.com/en/products/FaceID/products/index.html
12
+
13
+ SDK documentation with list of functions is included in doc/
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'hanvon'
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
Binary file
data/hanvon.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hanvon/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hanvon"
8
+ spec.version = Hanvon::VERSION
9
+ spec.authors = ["Heinrich Lee Yu"]
10
+ spec.email = ["hleeyu@gmail.com"]
11
+ spec.description = %q{Ruby client for communicating with Hanvon Time & Attendance Devices over the network}
12
+ spec.summary = %q{Ruby client for Hanvon Time & Attendance Devices}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
data/lib/hanvon.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "hanvon/client"
2
+ require "hanvon/crypto"
3
+ require "hanvon/version"
4
+
5
+ module Hanvon
6
+
7
+ end
@@ -0,0 +1,85 @@
1
+ require 'socket'
2
+
3
+ module Hanvon
4
+ class Client
5
+
6
+ attr_accessor :host
7
+ attr_accessor :port
8
+ attr_accessor :password
9
+
10
+ attr_accessor :socket
11
+ attr_accessor :encryptor
12
+
13
+ def initialize(host, port = 9922, password = nil)
14
+ self.host = host
15
+ self.port = port
16
+ self.password = password
17
+
18
+ self.socket = TCPSocket.new(host, port)
19
+ self.encryptor = Crypto.new(password) unless password.nil?
20
+ end
21
+
22
+ def send_command(command, params = {})
23
+ param_strings = []
24
+ params.each { |k,v|
25
+ param_strings << "#{k}=\"#{v}\""
26
+ }
27
+
28
+ send("#{command}(#{param_strings.join(" ")})")
29
+ end
30
+
31
+ def send(message)
32
+ socket.write(encryptor ? encryptor.encrypt(message) : message)
33
+ parse_reply(read_reply)
34
+ end
35
+
36
+ def parse_reply(reply)
37
+ response = []
38
+
39
+ response_data = reply.sub(/\AReturn\(/, "").chomp(")")
40
+ response_data.split("\n").each_with_index { |r, i|
41
+ row_hash = {}
42
+ r.scan(/(\S+?)="(.*?)"/).each do |m|
43
+ row_hash[m[0]] = m[1]
44
+ end
45
+
46
+ if i == 0
47
+ row_hash.delete('result')
48
+ row_hash.delete('dev_id')
49
+ end
50
+
51
+ response << row_hash unless row_hash.empty?
52
+ }
53
+
54
+ response = response.first if response.size == 1
55
+
56
+ return response
57
+ end
58
+
59
+ def close
60
+ socket.close
61
+ end
62
+
63
+ protected
64
+
65
+ def read_reply
66
+ reply = ""
67
+
68
+ while true
69
+ chunk = socket.recv(1024)
70
+ reply += encryptor ? encryptor.decrypt(chunk, reply.length % 8) : chunk
71
+
72
+ if reply[-1] == ')' and reply =~ /\A\w+\((?:\w+\s*=\s*".*"\s*)*\)\z/
73
+ if reply.start_with?("Wait(")
74
+ reply = ""
75
+ else
76
+ break
77
+ end
78
+ end
79
+ end
80
+
81
+ reply
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,47 @@
1
+ module Hanvon
2
+ class Crypto
3
+
4
+ class InvalidPasswordException < StandardError; end
5
+
6
+ attr_accessor :password
7
+
8
+ def initialize(password)
9
+ self.password = password
10
+ end
11
+
12
+ def convert(message, offset = 0)
13
+ message.each_char.each_with_index.collect do |char, pos|
14
+ convert_char(char, pos + offset)
15
+ end.pack('C*')
16
+ end
17
+
18
+ alias_method :encrypt, :convert
19
+ alias_method :decrypt, :convert
20
+
21
+ def password=(password)
22
+ if valid_password?(password)
23
+ @password = password
24
+ compute_key
25
+ else
26
+ raise InvalidPasswordException
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def convert_char(char, pos)
33
+ return char.ord ^ @key[pos % 8]
34
+ end
35
+
36
+ def valid_password?(password)
37
+ password =~ /\A\d{1,8}\z/
38
+ end
39
+
40
+ def compute_key
41
+ @key = (0..7).collect do |pos|
42
+ (password[pos] || 0).ord + (2 ** pos >> 1)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module Hanvon
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hanvon::Crypto do
4
+
5
+ it "should not accept empty password" do
6
+ lambda {
7
+ Hanvon::Crypto.new('')
8
+ }.should raise_exception(Hanvon::Crypto::InvalidPasswordException)
9
+ end
10
+
11
+ it "should not accept password with more than 8 digits" do
12
+ lambda {
13
+ Hanvon::Crypto.new('123123123')
14
+ }.should raise_exception(Hanvon::Crypto::InvalidPasswordException)
15
+ end
16
+
17
+ it "should not accept non-numeric password" do
18
+ lambda {
19
+ Hanvon::Crypto.new('abc')
20
+ }.should raise_exception(Hanvon::Crypto::InvalidPasswordException)
21
+ end
22
+
23
+ context "with password 12345678" do
24
+ before :all do
25
+ @crypto = Hanvon::Crypto.new('12345678')
26
+ end
27
+
28
+ describe "#encrypt" do
29
+ it "returns encrypted message" do
30
+ @crypto.encrypt("GetDeviceInfo()").should == ['7656417c58303e1b547a5b5e526e7e'].pack("H*")
31
+ end
32
+ end
33
+
34
+ describe "#decrypt" do
35
+ it "returns original message" do
36
+ @crypto.decrypt(['7656417c58303e1b547a5b5e526e7e'].pack("H*")).should == "GetDeviceInfo()"
37
+ end
38
+ end
39
+ end
40
+
41
+ context "with password 00000000" do
42
+ before :all do
43
+ @crypto = Hanvon::Crypto.new('00000000')
44
+ end
45
+
46
+ describe "#encrypt" do
47
+ it "returns encrypted message" do
48
+ @crypto.encrypt("GetDeviceInfo()").should == ['775446705d36391355785c52576879'].pack("H*")
49
+ end
50
+ end
51
+
52
+ describe "#decrypt" do
53
+ it "returns original message" do
54
+ @crypto.decrypt(['775446705d36391355785c52576879'].pack("H*")).should == "GetDeviceInfo()"
55
+ end
56
+ end
57
+ end
58
+
59
+ context "with password 1234" do
60
+ before :all do
61
+ @crypto = Hanvon::Crypto.new('1234')
62
+ end
63
+
64
+ describe "#encrypt" do
65
+ it "returns encrypted message" do
66
+ @crypto.encrypt("GetDeviceInfo()").should == ['7656417c6d664923547a5b5e673809'].pack("H*")
67
+ end
68
+ end
69
+
70
+ describe "#decrypt" do
71
+ it "returns original message" do
72
+ @crypto.decrypt(['7656417c6d664923547a5b5e673809'].pack("H*")).should == "GetDeviceInfo()"
73
+ end
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1 @@
1
+ require 'hanvon'
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hanvon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Heinrich Lee Yu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Ruby client for communicating with Hanvon Time & Attendance Devices over
56
+ the network
57
+ email:
58
+ - hleeyu@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - doc/HanvonSDKManual.pdf
69
+ - hanvon.gemspec
70
+ - lib/hanvon.rb
71
+ - lib/hanvon/client.rb
72
+ - lib/hanvon/crypto.rb
73
+ - lib/hanvon/version.rb
74
+ - spec/hanvon_crypto_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: ''
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.2.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Ruby client for Hanvon Time & Attendance Devices
100
+ test_files:
101
+ - spec/hanvon_crypto_spec.rb
102
+ - spec/spec_helper.rb