innards 0.1.0.pre
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.
- data/LICENSE.txt +14 -0
- data/README.md +27 -0
- data/Rakefile +12 -0
- data/lib/innards.rb +7 -0
- data/lib/innards/connection.rb +166 -0
- data/lib/innards/exceptions.rb +8 -0
- data/lib/innards/version.rb +10 -0
- metadata +118 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright (c) 2013 TurboRETS
|
2
|
+
|
3
|
+
This program is free software: you can redistribute it and/or modify
|
4
|
+
it under the terms of the GNU Affero General Public License as
|
5
|
+
published by the Free Software Foundation, either version 3 of the
|
6
|
+
License, or (at your option) any later version.
|
7
|
+
|
8
|
+
This program is distributed in the hope that it will be useful,
|
9
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
GNU Affero General Public License for more details.
|
12
|
+
|
13
|
+
You should have received a copy of the GNU Affero General Public License
|
14
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
[](https://gemnasium.com/TurboRETS/innards)
|
2
|
+
[](https://drone.io/github.com/TurboRETS/innards/latest)
|
3
|
+
[](https://codeclimate.com/github/TurboRETS/innards)
|
4
|
+
|
5
|
+
TurboRETS Innards
|
6
|
+
=================
|
7
|
+
|
8
|
+
Gem for accessing RETS Servers
|
9
|
+
|
10
|
+
TODO
|
11
|
+
-
|
12
|
+
- [X] RETS Server Login
|
13
|
+
- [ ] Fetch Metadata
|
14
|
+
- [ ] Fetch Resource/Class Data
|
15
|
+
- [ ] GetObject
|
16
|
+
|
17
|
+
Premature Usage Example
|
18
|
+
-
|
19
|
+
```ruby
|
20
|
+
require 'innards'
|
21
|
+
conn = Innards::Connection.new(:login_url => "http://Joe:Schmoe@www.dis.com:6103/rets/login")
|
22
|
+
conn.login!
|
23
|
+
```
|
24
|
+
|
25
|
+
License
|
26
|
+
-
|
27
|
+
Distributed under an AGPL license, see LICENSE file
|
data/Rakefile
ADDED
data/lib/innards.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'excon'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module Innards
|
6
|
+
class Connection
|
7
|
+
|
8
|
+
include Innards::Exceptions
|
9
|
+
|
10
|
+
VALID_RETS_VERSIONS = [
|
11
|
+
"1.5",
|
12
|
+
"1.7",
|
13
|
+
"1.7.2",
|
14
|
+
"1.8"
|
15
|
+
]
|
16
|
+
|
17
|
+
VALID_AUTH_MODES = [
|
18
|
+
"digest",
|
19
|
+
"basic"
|
20
|
+
]
|
21
|
+
|
22
|
+
DEFAULT_PARAMS = {
|
23
|
+
:login_url => "",
|
24
|
+
:login_uri => nil,
|
25
|
+
:proxy_uri => nil,
|
26
|
+
:user_agent => "innards/v#{Innards::Version::STRING}",
|
27
|
+
:rets_version => "1.7.2",
|
28
|
+
:auth_mode => "digest",
|
29
|
+
:cookies => [],
|
30
|
+
:digest => {},
|
31
|
+
:mock => false
|
32
|
+
}
|
33
|
+
|
34
|
+
def initialize(params = {})
|
35
|
+
|
36
|
+
@params = DEFAULT_PARAMS.merge(params)
|
37
|
+
|
38
|
+
raise ArgumentError.new("invalid url [:login_url]") unless has_valid_url?
|
39
|
+
raise ArgumentError.new("invalid rets version [:rets_version]") unless has_valid_rets_version?
|
40
|
+
raise ArgumentError.new("invalid auth mode [:auth_mode]") unless has_valid_auth_mode?
|
41
|
+
raise ArgumentError.new("invalid user agent [:user_agent]") unless has_valid_user_agent?
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def login!
|
46
|
+
tries ||= 3
|
47
|
+
@params[:login_uri] = URI(@params[:login_url])
|
48
|
+
@params[:current_request] = @params[:login_uri].path
|
49
|
+
make_request_to @params[:current_request]
|
50
|
+
true
|
51
|
+
rescue AuthenticationRequiredException => e
|
52
|
+
retry unless (tries -= 1).zero?
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
def has_valid_url?
|
58
|
+
(@params[:login_url] =~ URI::regexp)
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_valid_rets_version?
|
62
|
+
VALID_RETS_VERSIONS.include? @params[:rets_version]
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_valid_auth_mode?
|
66
|
+
VALID_AUTH_MODES.include? @params[:auth_mode]
|
67
|
+
end
|
68
|
+
|
69
|
+
def has_valid_user_agent?
|
70
|
+
!(@params[:user_agent].empty? || @params[:user_agent].nil?)
|
71
|
+
end
|
72
|
+
|
73
|
+
def connection
|
74
|
+
@conn = Excon.new("#{@params[:login_uri].scheme}://#{@params[:login_uri].host}:#{@params[:login_uri].port}") if @conn.nil?
|
75
|
+
@conn
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_request_to path
|
79
|
+
response = connection.get(:path => path, :headers => build_headers_for(path))
|
80
|
+
|
81
|
+
parse_cookie_from(response.headers['Set-Cookie']) if response.headers.has_key?('Set-Cookie')
|
82
|
+
parse_www_authenticate_from(response.headers['WWW-Authenticate']) if response.headers.has_key?('WWW-Authenticate')
|
83
|
+
|
84
|
+
raise AuthenticationRequiredException.new "Authentication Required" if response.status == 401
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_cookie_from set_cookie_header
|
89
|
+
set_cookie_header.scan(/(\w+)=(.+?);/) {
|
90
|
+
@params[:cookies].push([$1, $2])
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_www_authenticate_from www_authenticate_header
|
95
|
+
www_authenticate_header.scan(/(\w+)="(.*?)"/) {
|
96
|
+
@params[:digest][$1] = $2
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_headers_for path
|
101
|
+
headers = {
|
102
|
+
'User-Agent' => @params[:user_agent],
|
103
|
+
'Cookie' => build_cookie_header_for(path),
|
104
|
+
}
|
105
|
+
headers['Authorization'] = build_www_authenticate_header_for(path) unless @params[:digest].empty?
|
106
|
+
headers
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_cookie_header_for path
|
110
|
+
URI.encode_www_form(@params[:cookies]) unless @params[:cookies].empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_www_authenticate_header_for path
|
114
|
+
cnonce = md5(random)
|
115
|
+
header = [
|
116
|
+
%Q(Digest username="#{@params[:login_uri].user}"),
|
117
|
+
%Q(realm="#{@params[:digest]['realm']}"),
|
118
|
+
%Q(nonce="#{@params[:digest]['nonce']}"),
|
119
|
+
%Q(uri="#{@params[:current_request]}"),
|
120
|
+
%Q(response="#{request_digest(cnonce)}"),
|
121
|
+
]
|
122
|
+
|
123
|
+
if has_qop?
|
124
|
+
fields = [
|
125
|
+
%Q(cnonce="#{cnonce}"),
|
126
|
+
%Q(qop="#{@params[:digest]['qop']}"),
|
127
|
+
%Q(nc=00000001)
|
128
|
+
]
|
129
|
+
fields.each { |field| header << field }
|
130
|
+
end
|
131
|
+
|
132
|
+
header << %Q(opaque="#{@params[:digest]['opaque']}") if has_opaque?
|
133
|
+
header.join(", ")
|
134
|
+
end
|
135
|
+
|
136
|
+
def request_digest cnonce
|
137
|
+
a = [md5(a1), @params[:digest]['nonce'], md5(a2)]
|
138
|
+
a.insert(2, "00000001", cnonce, @params[:digest]['qop']) if has_qop?
|
139
|
+
md5(a.join(":"))
|
140
|
+
end
|
141
|
+
|
142
|
+
def has_opaque?
|
143
|
+
@params[:digest].has_key?('opaque') and !@params[:digest]['opaque'].empty?
|
144
|
+
end
|
145
|
+
|
146
|
+
def has_qop?
|
147
|
+
@params[:digest].has_key?('qop') and !@params[:digest]['qop'].empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
def a1
|
151
|
+
[@params[:login_uri].user, @params[:digest]['realm'], @params[:login_uri].password].join(":")
|
152
|
+
end
|
153
|
+
|
154
|
+
def a2
|
155
|
+
["GET", @params[:current_request]].join(":")
|
156
|
+
end
|
157
|
+
|
158
|
+
def random
|
159
|
+
"%x" % (Time.now.to_i + rand(65535))
|
160
|
+
end
|
161
|
+
|
162
|
+
def md5(str)
|
163
|
+
Digest::MD5.hexdigest(str)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: innards
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.pre
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Trippett
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: excon
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.25.1
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.25.1
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.14.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.0.2
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.0.2
|
78
|
+
description: Innards; the internal parts especially of a structure or mechanism; in
|
79
|
+
our case it's the core RETS library for TurboRETS
|
80
|
+
email:
|
81
|
+
- paul@turborets.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- lib/innards/connection.rb
|
87
|
+
- lib/innards/exceptions.rb
|
88
|
+
- lib/innards/version.rb
|
89
|
+
- lib/innards.rb
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
homepage: http://github.com/TurboRETS/innards
|
94
|
+
licenses:
|
95
|
+
- AGPL
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.3.6
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project: innards
|
114
|
+
rubygems_version: 1.8.24
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: RETS Library for Ruby
|
118
|
+
test_files: []
|