hanvon 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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