karmasphere-client 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +41 -0
- data/bin/rubykarmaclient +135 -0
- data/lib/karmasphere.rb +5 -0
- data/lib/karmasphere/client.rb +116 -0
- data/lib/karmasphere/constants.rb +18 -0
- data/lib/karmasphere/debug.rb +22 -0
- data/lib/karmasphere/query.rb +92 -0
- data/lib/karmasphere/response.rb +116 -0
- data/lib/rubytorrent/bencoding.rb +182 -0
- data/test/test_all.rb +8 -0
- data/test/test_client.rb +264 -0
- data/test/test_query.rb +120 -0
- data/test/test_response.rb +65 -0
- metadata +57 -0
data/README
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Karmasphere Ruby Client Library
|
2
|
+
|
3
|
+
produced by Meng Weng Wong 200606
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
After installation, run
|
8
|
+
|
9
|
+
rubykarmaclient --ip4=127.0.0.2
|
10
|
+
|
11
|
+
You should get back a response from the Karmasphere system
|
12
|
+
showing the reputation of that IP address.
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
The Karmasphere Client has been packaged as a rubygem. As
|
17
|
+
it is still in active development, and as the rest of the
|
18
|
+
Karmasphere system is still in alpha, this gem has not yet
|
19
|
+
been uploaded to RubyForge (20060701).
|
20
|
+
|
21
|
+
== User Documentation
|
22
|
+
|
23
|
+
To learn about the Karmasphere system, see http://www.karmasphere.com/userguide/
|
24
|
+
|
25
|
+
== Developer Documentation
|
26
|
+
|
27
|
+
If you are an MTA or Anti-Spam Vendor, see http://www.karmasphere.com/devzone/
|
28
|
+
|
29
|
+
== Bugs
|
30
|
+
|
31
|
+
See http://rt.karmasphere.com/
|
32
|
+
|
33
|
+
== History
|
34
|
+
|
35
|
+
[20060701] Packaged for rake/rubygems by Meng Weng Wong <mengwong@karmasphere.com>.
|
36
|
+
[200606] Originally written by Dave Brown <dagbrown>.
|
37
|
+
|
38
|
+
== License
|
39
|
+
|
40
|
+
This client software is distributed under the same terms as Ruby.
|
41
|
+
|
data/bin/rubykarmaclient
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# mengwong@pobox.com 20060629
|
4
|
+
# Karmasphere client for Ruby
|
5
|
+
|
6
|
+
# usage:
|
7
|
+
# rubykarmaclient --host=slave.karmasphere.com --ip4=127.0.0.2
|
8
|
+
|
9
|
+
# ----------------------------------------------------------
|
10
|
+
# libraries
|
11
|
+
# ----------------------------------------------------------
|
12
|
+
|
13
|
+
require "socket"
|
14
|
+
require "rubygems" # if run from trunk/lib, with ../bin/rubykarmaclient, the dev libs will load instead of the system libs
|
15
|
+
require "rubytorrent/bencoding"
|
16
|
+
require "karmasphere/client"
|
17
|
+
require "karmasphere/query"
|
18
|
+
require "karmasphere/response"
|
19
|
+
|
20
|
+
# ----------------------------------------------------------
|
21
|
+
# options processing
|
22
|
+
# ----------------------------------------------------------
|
23
|
+
|
24
|
+
require "getoptlong"
|
25
|
+
opts = GetoptLong.new(
|
26
|
+
[ "--host" , GetoptLong::REQUIRED_ARGUMENT ],
|
27
|
+
[ "--port" , GetoptLong::REQUIRED_ARGUMENT ],
|
28
|
+
[ "--composite" , GetoptLong::REQUIRED_ARGUMENT ],
|
29
|
+
[ "--combiner" , GetoptLong::REQUIRED_ARGUMENT ],
|
30
|
+
[ "--ip4" , GetoptLong::REQUIRED_ARGUMENT ],
|
31
|
+
[ "--ip6" , GetoptLong::REQUIRED_ARGUMENT ],
|
32
|
+
[ "--domain" , GetoptLong::REQUIRED_ARGUMENT ],
|
33
|
+
[ "--url" , GetoptLong::REQUIRED_ARGUMENT ],
|
34
|
+
[ "--email" , GetoptLong::REQUIRED_ARGUMENT ],
|
35
|
+
[ "--flags" , GetoptLong::REQUIRED_ARGUMENT ],
|
36
|
+
[ "--username" , GetoptLong::REQUIRED_ARGUMENT ],
|
37
|
+
[ "--password" , GetoptLong::REQUIRED_ARGUMENT ],
|
38
|
+
[ "--tcp" , GetoptLong::NO_ARGUMENT ],
|
39
|
+
[ "--help" , GetoptLong::NO_ARGUMENT ],
|
40
|
+
[ "--single" , GetoptLong::NO_ARGUMENT ],
|
41
|
+
[ "--debug", "-d" , GetoptLong::NO_ARGUMENT ]
|
42
|
+
);
|
43
|
+
|
44
|
+
myopts = {};
|
45
|
+
opts.each { |opt, arg| myopts[opt] = arg }
|
46
|
+
|
47
|
+
# ----------------------------------------------------------
|
48
|
+
# symbolic constants
|
49
|
+
# ----------------------------------------------------------
|
50
|
+
|
51
|
+
FL_WANTFACTS = 1
|
52
|
+
DEFAULT_IP = "127.0.0.2"
|
53
|
+
DEFAULT_COMPOSITE = "karmasphere.email-sender"
|
54
|
+
|
55
|
+
if myopts.has_key? '--help' then
|
56
|
+
print "usage: rubykarmaclient
|
57
|
+
[ --username=xxx --password=yyy ]
|
58
|
+
[ --ip4=127.0.0.2 ]
|
59
|
+
[ --domain=example.com ]
|
60
|
+
[ --url=http://www.example.com/phishing/ ]
|
61
|
+
[ --tcp ]
|
62
|
+
[ --composite=karmasphere.email-sender ]
|
63
|
+
"
|
64
|
+
exit;
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# ----------------------------------------------------------
|
69
|
+
# data preparation
|
70
|
+
# ----------------------------------------------------------
|
71
|
+
|
72
|
+
identities = []
|
73
|
+
[ "--ip4", "--ip6", "--domain", "--url", "--email" ].each do |identifier|
|
74
|
+
identities << myopts[identifier] if myopts[identifier];
|
75
|
+
end
|
76
|
+
|
77
|
+
identities << DEFAULT_IP if identities.length == 0;
|
78
|
+
|
79
|
+
print "identifiers = #{identities}\n" if myopts['--debug'];
|
80
|
+
|
81
|
+
# ----------------------------------------------------------
|
82
|
+
# main
|
83
|
+
# ----------------------------------------------------------
|
84
|
+
|
85
|
+
#
|
86
|
+
# set up client
|
87
|
+
#
|
88
|
+
|
89
|
+
clientargs = { }
|
90
|
+
|
91
|
+
if myopts.has_key? '--tcp' then clientargs[:type] = :CONNECTION_TCP else clientargs[:type] = :CONNECTION_UDP end
|
92
|
+
|
93
|
+
if myopts.has_key? '--host' then clientargs[:host] = myopts['--host'] end
|
94
|
+
|
95
|
+
if myopts.has_key? '--username' then clientargs[:principal] = myopts['--username'] end
|
96
|
+
if myopts.has_key? '--password' then clientargs[:credentials] = myopts['--password'] end
|
97
|
+
|
98
|
+
client=Karmasphere::Client.new(clientargs)
|
99
|
+
|
100
|
+
#
|
101
|
+
# set up query
|
102
|
+
#
|
103
|
+
|
104
|
+
query_id = myopts['--id'] || "000";
|
105
|
+
|
106
|
+
queryargs = { :Id => query_id,
|
107
|
+
:Identities => identities }
|
108
|
+
|
109
|
+
if myopts.has_key? '--flags' then
|
110
|
+
queryargs[:Flags] = myopts['--flags']
|
111
|
+
else
|
112
|
+
queryargs[:Flags] = FL_WANTFACTS
|
113
|
+
end
|
114
|
+
|
115
|
+
if myopts.has_key? '--composite' then
|
116
|
+
queryargs[:Composites] = myopts['--composite'].split(',')
|
117
|
+
else
|
118
|
+
queryargs[:Composites] = [ DEFAULT_COMPOSITE ]
|
119
|
+
end
|
120
|
+
|
121
|
+
print "query args: " , queryargs.inspect, "\n" if myopts['--debug'];
|
122
|
+
query = Karmasphere::Query.new( queryargs )
|
123
|
+
print query.inspect, "\n" if myopts['--debug'];
|
124
|
+
|
125
|
+
#
|
126
|
+
# display response
|
127
|
+
#
|
128
|
+
|
129
|
+
response = client.ask(query)
|
130
|
+
|
131
|
+
if myopts['--debug']
|
132
|
+
puts response.instance_variable_get('@hash').inspect
|
133
|
+
end
|
134
|
+
|
135
|
+
puts response
|
data/lib/karmasphere.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# The (mostly-complete) Karmasphere Client
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
require "socket"
|
5
|
+
require "rubytorrent/bencoding"
|
6
|
+
require "stringio"
|
7
|
+
require "fcntl"
|
8
|
+
require "karmasphere/debug"
|
9
|
+
|
10
|
+
module Karmasphere
|
11
|
+
CONNECTION_UDP = :CONNECTION_UDP
|
12
|
+
CONNECTION_TCP = :CONNECTION_TCP
|
13
|
+
|
14
|
+
class CredentialsError < Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
class Client
|
18
|
+
private
|
19
|
+
def send_query(query)
|
20
|
+
if @auth
|
21
|
+
querystr=query.to_hash.merge(@auth).to_bencoding
|
22
|
+
else
|
23
|
+
querystr=query.to_hash.to_bencoding
|
24
|
+
end
|
25
|
+
|
26
|
+
if TCPSocket === @socket then
|
27
|
+
@socket.send [querystr.length].pack("N"),0
|
28
|
+
end
|
29
|
+
|
30
|
+
Karmasphere.dprint("sending query string #{querystr}\n");
|
31
|
+
@socket.send querystr,0
|
32
|
+
Karmasphere.dprint("sent.\n");
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_response(query)
|
36
|
+
# (hack, not guaranteed to work) handle out-of-order responses
|
37
|
+
loop do
|
38
|
+
if @response_pool.has_key? query.id then
|
39
|
+
return @response_pool.delete(query.id)
|
40
|
+
else
|
41
|
+
response=Response.new( decode(receive_response), query )
|
42
|
+
@response_pool[response.id] = response;
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def receive_response
|
48
|
+
if TCPSocket === @socket then
|
49
|
+
length_s = @socket.recvfrom(4)[0]
|
50
|
+
length_n = length_s.unpack("N")[0]
|
51
|
+
Karmasphere.dprint("client decoding TCP response of #{length_n} bytes\n");
|
52
|
+
@socket.recvfrom(length_n)[0]
|
53
|
+
else
|
54
|
+
Karmasphere.dprint("client awaiting response from #@socket\n");
|
55
|
+
reply, from = @socket.recvfrom(8192)
|
56
|
+
Karmasphere.dprint("client response received from #@socket\n");
|
57
|
+
reply
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def decode(querystr)
|
62
|
+
RubyTorrent::BStream.new(
|
63
|
+
StringIO.new( querystr )
|
64
|
+
).to_a[0]
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_query(query)
|
68
|
+
send_query query
|
69
|
+
find_response query
|
70
|
+
end
|
71
|
+
|
72
|
+
public
|
73
|
+
def initialize(*args)
|
74
|
+
host="query.karmasphere.com"
|
75
|
+
port=8666
|
76
|
+
type=CONNECTION_UDP
|
77
|
+
if Hash === args[0] then
|
78
|
+
opts = args[0]
|
79
|
+
host = opts[:host] if opts.has_key? :host
|
80
|
+
port = opts[:port] if opts.has_key? :port
|
81
|
+
type = opts[:type] if opts.has_key? :type and
|
82
|
+
opts[:type] == CONNECTION_TCP ||
|
83
|
+
opts[:type] == CONNECTION_UDP
|
84
|
+
@auth = nil
|
85
|
+
if opts.has_key? :principal then
|
86
|
+
unless opts.has_key? :credentials
|
87
|
+
raise CredentialsError, "principal missing credentials"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if opts.has_key? :credentials then
|
91
|
+
unless opts.has_key? :principal
|
92
|
+
raise CredentialsError, "credentials missing principal"
|
93
|
+
end
|
94
|
+
@auth = { "a" => [ opts[:principal], opts[:credentials] ] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if type == CONNECTION_UDP
|
99
|
+
@socket = UDPSocket.new
|
100
|
+
Karmasphere.dprint "UDP client: attempting to connect to #{host}:#{port}\n"
|
101
|
+
@socket.connect host, port
|
102
|
+
@socket.fcntl(Fcntl::F_SETFD,1) if defined? Fcntl::F_SETFD
|
103
|
+
Karmasphere.dprint "UDP client: created socket #@socket\n"
|
104
|
+
else
|
105
|
+
Karmasphere.dprint "TCP client: attempting to connect to #{host}:#{port}\n"
|
106
|
+
@socket = TCPSocket.new host, port
|
107
|
+
end
|
108
|
+
|
109
|
+
@response_pool={}
|
110
|
+
end
|
111
|
+
|
112
|
+
def ask(query)
|
113
|
+
make_query(query)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Constants used in the Karmasphere Query class
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
module Karmasphere
|
6
|
+
IDT_IP4_ADDRESS = 0
|
7
|
+
IDT_IP6_ADDRESS = 1
|
8
|
+
IDT_DOMAIN_NAME = 2
|
9
|
+
IDT_EMAIL_ADDRESS = 3
|
10
|
+
IDT_URL = 4
|
11
|
+
IDT_OPAQUE = 5
|
12
|
+
AUTHENTIC = "a"
|
13
|
+
SMTP_CLIENT_IP = "smtp.client-ip"
|
14
|
+
SMTP_ENV_HELO = "smtp.env.helo"
|
15
|
+
SMTP_ENV_MAIL_FROM = "smtp.env.mail-from"
|
16
|
+
SMTP_ENV_RCPT_TO = "smtp.env.rcpt-to"
|
17
|
+
SMTP_HEADER_FROM_ADDRESS = "smtp.header.from.address"
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Karmasphere
|
3
|
+
|
4
|
+
# if you want debugging turned on,
|
5
|
+
# set any one of these environment variables:
|
6
|
+
# * KARMADEBUG
|
7
|
+
# * RUBYDEBUG
|
8
|
+
# or change the following line to read .find(lambda { true })
|
9
|
+
|
10
|
+
KARMADEBUG = %w{KARMADEBUG RUBYDEBUG}.find(nil) do |debugenv|
|
11
|
+
ENV[debugenv] and ENV[debugenv].size and ENV[debugenv] != "0"
|
12
|
+
end
|
13
|
+
|
14
|
+
# use +dprint+ instead of print. it will print to STDERR,
|
15
|
+
# but only when debugging is turned on.
|
16
|
+
|
17
|
+
def dprint(*args)
|
18
|
+
STDERR.print("KarmaDebug: ", "%0.6f: " % Time.now, *args) if KARMADEBUG
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :dprint
|
22
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Karmasphere Query class (translation of the Perl code)
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
require "karmasphere/constants"
|
6
|
+
|
7
|
+
module Karmasphere
|
8
|
+
class Query
|
9
|
+
@@ID=0
|
10
|
+
Realflag = {
|
11
|
+
:Id => "_",
|
12
|
+
:Identities => "i",
|
13
|
+
:Composites => "s",
|
14
|
+
:Combiners => "c",
|
15
|
+
:Flags => "fl"
|
16
|
+
}
|
17
|
+
|
18
|
+
private
|
19
|
+
def guess_identity_type(addr)
|
20
|
+
case addr
|
21
|
+
when /^[0-9\.]{7,15}$/: IDT_IP4_ADDRESS
|
22
|
+
when /^[0-9a-f:\/]{2,64}$/i: IDT_IP6_ADDRESS
|
23
|
+
when /^https?:\/\//: IDT_URL
|
24
|
+
when /@/: IDT_EMAIL_ADDRESS
|
25
|
+
when /\./: IDT_DOMAIN_NAME
|
26
|
+
else IDT_OPAQUE
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
def initialize(*args)
|
33
|
+
self.id = "q#{@@ID += 1}"
|
34
|
+
|
35
|
+
if args.length > 0
|
36
|
+
opts = args[0] if Hash === args[0]
|
37
|
+
|
38
|
+
[:Id, :Identities, :Composites, :Composite, :Feeds,
|
39
|
+
:Combiners, :Combiner, :Flags].each do |option|
|
40
|
+
|
41
|
+
if opts.has_key? option
|
42
|
+
send("#{option.to_s.downcase}=", opts[option])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_accessor :id, :composites, :feeds, :combiners
|
49
|
+
|
50
|
+
attr_writer :flags
|
51
|
+
def flags
|
52
|
+
@flags.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def composite=(newcomposite)
|
56
|
+
self.composites=newcomposite
|
57
|
+
end
|
58
|
+
|
59
|
+
def feed=(newfeed)
|
60
|
+
@feeds=[newfeed]
|
61
|
+
end
|
62
|
+
|
63
|
+
def identities=(stuff)
|
64
|
+
if String === stuff then
|
65
|
+
return self.identities=[stuff]
|
66
|
+
end
|
67
|
+
|
68
|
+
@identities = if Array === stuff then
|
69
|
+
stuff.map do |item|
|
70
|
+
if Array === item then
|
71
|
+
item
|
72
|
+
else
|
73
|
+
[item, guess_identity_type(item)]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
attr_reader :identities
|
79
|
+
alias identity= identities=
|
80
|
+
|
81
|
+
def to_hash
|
82
|
+
retval={}
|
83
|
+
[:Id, :Identities, :Composites,
|
84
|
+
:Feeds, :Combiners, :Flags].each do |k|
|
85
|
+
if instance_variables.member? "@#{k.to_s.downcase}"
|
86
|
+
retval[Realflag[k]]=send "#{k.to_s.downcase}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
retval
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Desired Output Format
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
# the Java, Perl and C clients all produce the same output format.
|
6
|
+
#
|
7
|
+
# 20060907-15:53:55 mengwong@newyears-air:~/src/karmasphere/trunk/lib% karmaclient --ip4=127.0.0.2 --host=bowie.tx.karmasphere.com
|
8
|
+
# Response id 'q0': 12ms, 1 combinations, 8 facts
|
9
|
+
# Combiner 'karmasphere.emailchecker': verdict 1000 (isipp.iadb-vouched-ip: if-pass(0) => return good(1.0))
|
10
|
+
# Feed 'sorbs.dul' opinion -1000 (Dynamic IP Addresses See: http://www.sorbs.net/lookup.shtml?$)
|
11
|
+
# Feed 'spamhaus.xbl-cbl' opinion -1000 (http://www.spamhaus.org/query/bl?ip=$)
|
12
|
+
# Feed 'cymru.bogons' opinion -1000 (Invalid source IP address (cymru))
|
13
|
+
# Feed 'dsbl.list' opinion -1000 (http://dsbl.org/listing?$)
|
14
|
+
# Feed 'dsbl.multihop' opinion -1000 (http://dsbl.org/listing?$)
|
15
|
+
# Feed 'spamcop.bl' opinion 2147483647 (null data)
|
16
|
+
# Feed 'isipp.iadb-vouched-ip' opinion 1000 (Vouched by ISIPP/IADB)
|
17
|
+
# Feed 'spamhaus.sbl' opinion -1000 (http://www.spamhaus.org/query/bl?ip=$)
|
18
|
+
# 20060907-15:54:01 mengwong@newyears-air:~/src/karmasphere/trunk/lib%
|
19
|
+
|
20
|
+
#------------------------------------------------------------------------
|
21
|
+
# Response class for the Karmasphere client
|
22
|
+
#------------------------------------------------------------------------
|
23
|
+
|
24
|
+
module Karmasphere
|
25
|
+
class Response
|
26
|
+
private
|
27
|
+
def combination_get(key, combiner=nil)
|
28
|
+
if combinations then
|
29
|
+
if !combiner and combinations.has_key?("default") then
|
30
|
+
combination('default')
|
31
|
+
elsif !combiner then
|
32
|
+
combinations.values.find do |combo|
|
33
|
+
if combo.has_key? key then combo[key] end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
my_combination=combination(combiner)
|
37
|
+
if my_combination then
|
38
|
+
return my_combination[key]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
public
|
45
|
+
|
46
|
+
def initialize(hash, query=nil)
|
47
|
+
@hash=hash
|
48
|
+
@query=query
|
49
|
+
end
|
50
|
+
|
51
|
+
def id; @hash["_"] end
|
52
|
+
|
53
|
+
def time; @hash["time"] if @hash.has_key? "time" end
|
54
|
+
|
55
|
+
def facts
|
56
|
+
if @hash.has_key? "facts" then
|
57
|
+
@hash["facts"]
|
58
|
+
elsif @hash.has_key? "f" then
|
59
|
+
@hash["f"]
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def combinations
|
66
|
+
if @hash.has_key? "c" then
|
67
|
+
@hash["c"]
|
68
|
+
elsif @hash.has_key? "combiners" then
|
69
|
+
@hash["combiners"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def combination(combiner)
|
74
|
+
combinations[combiner]
|
75
|
+
end
|
76
|
+
|
77
|
+
def combiner_names
|
78
|
+
if combinations then
|
79
|
+
combinations.keys
|
80
|
+
else
|
81
|
+
[]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def verdict(name=nil); combination_get "v",name; end
|
86
|
+
alias value verdict
|
87
|
+
def data(name=nil); combination_get "d", name; end
|
88
|
+
|
89
|
+
def error; @hash["error"] if @hash.has_key? "error"; end
|
90
|
+
def message; @hash["message"] if @hash.has_key? "message"; end
|
91
|
+
|
92
|
+
def to_s;
|
93
|
+
"Response_id '#{id}': " +
|
94
|
+
if time then "#{time}ms, " else "" end +
|
95
|
+
"#{combiner_names.length} combinations, " +
|
96
|
+
"#{facts.length} facts\n" +
|
97
|
+
if error then
|
98
|
+
"Error: #{message}\n"
|
99
|
+
else
|
100
|
+
combiner_names.map do |name|
|
101
|
+
"Combiner '#{name}': verdict #{value(name)} " +
|
102
|
+
"(#{data(name) || '(undef)'})\n"
|
103
|
+
end.join("") +
|
104
|
+
facts.map do |fact|
|
105
|
+
"Feed '#{fact['f']}' opinion #{fact['v']} (" +
|
106
|
+
if fact.has_key? "d" then
|
107
|
+
fact["d"]
|
108
|
+
else
|
109
|
+
"null data"
|
110
|
+
end + ")" +
|
111
|
+
if fact.has_key? "i" then " (identity=#{fact['i']})" else "" end + "\n"
|
112
|
+
end.join("")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
## bencoding.rb -- parse and generate bencoded values.
|
2
|
+
## Copyright 2004 William Morgan.
|
3
|
+
##
|
4
|
+
## This file is part of RubyTorrent. RubyTorrent is free software;
|
5
|
+
## you can redistribute it and/or modify it under the terms of version
|
6
|
+
## 2 of the GNU General Public License as published by the Free
|
7
|
+
## Software Foundation.
|
8
|
+
##
|
9
|
+
## RubyTorrent is distributed in the hope that it will be useful, but
|
10
|
+
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
## General Public License (in the file COPYING) for more details.
|
13
|
+
|
14
|
+
require 'uri'
|
15
|
+
require 'digest/sha1'
|
16
|
+
|
17
|
+
module RubyTorrent
|
18
|
+
|
19
|
+
## we mess in the users' namespaces in this file. there's no good way
|
20
|
+
## around it. i don't think it's too egregious though.
|
21
|
+
|
22
|
+
class BEncodingError < StandardError; end
|
23
|
+
|
24
|
+
class BStream
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
@@classes = []
|
28
|
+
|
29
|
+
def initialize(s)
|
30
|
+
@s = s
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.register_bencoded_class(c)
|
34
|
+
@@classes.push c
|
35
|
+
end
|
36
|
+
|
37
|
+
def each
|
38
|
+
happy = true
|
39
|
+
begin
|
40
|
+
happy = false
|
41
|
+
c = @s.getc
|
42
|
+
@@classes.each do |klass|
|
43
|
+
if klass.bencoded? c
|
44
|
+
o = klass.parse_bencoding(c, @s)
|
45
|
+
happy = true
|
46
|
+
yield o
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end unless c.nil?
|
50
|
+
unless happy
|
51
|
+
@s.ungetc c unless c.nil?
|
52
|
+
end
|
53
|
+
end while happy
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class String
|
61
|
+
def to_bencoding
|
62
|
+
self.length.to_s + ":" + self.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.bencoded?(c)
|
66
|
+
(?0 .. ?9).include? c
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.parse_bencoding(c, s)
|
70
|
+
lens = c.chr
|
71
|
+
while ((x = s.getc) != ?:)
|
72
|
+
unless (?0 .. ?9).include? x
|
73
|
+
s.ungetc x
|
74
|
+
raise RubyTorrent::BEncodingError, "invalid bencoded string length #{lens} + #{x}"
|
75
|
+
end
|
76
|
+
lens += x.chr
|
77
|
+
end
|
78
|
+
raise RubyTorrent::BEncodingError, %{invalid length #{lens} in bencoded string} unless lens.length <= 20
|
79
|
+
len = lens.to_i
|
80
|
+
raise RubyTorrent::BEncodingError, %{invalid length #{lens} in bencoded string} unless len >= 0
|
81
|
+
(len > 0 ? s.read(len) : "")
|
82
|
+
end
|
83
|
+
|
84
|
+
RubyTorrent::BStream.register_bencoded_class self
|
85
|
+
end
|
86
|
+
|
87
|
+
class Integer
|
88
|
+
def to_bencoding
|
89
|
+
"i" + self.to_s + "e"
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.bencoded?(c)
|
93
|
+
c == ?i
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.parse_bencoding(c, s)
|
97
|
+
ints = ""
|
98
|
+
while ((x = s.getc.chr) != 'e')
|
99
|
+
raise RubyTorrent::BEncodingError, "invalid bencoded integer #{x.inspect}" unless x =~ /\d|-/
|
100
|
+
ints += x
|
101
|
+
end
|
102
|
+
raise RubyTorrent::BEncodingError, "invalid integer #{ints} (too long)" unless ints.length <= 20
|
103
|
+
int = ints.to_i
|
104
|
+
raise RubyTorrent::BEncodingError, %{can't parse bencoded integer "#{ints}"} if (int == 0) && (ints !~ /^0$/) #'
|
105
|
+
int
|
106
|
+
end
|
107
|
+
|
108
|
+
RubyTorrent::BStream.register_bencoded_class self
|
109
|
+
end
|
110
|
+
|
111
|
+
class Time
|
112
|
+
def to_bencoding
|
113
|
+
self.to_i.to_bencoding
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module URI
|
118
|
+
def to_bencoding
|
119
|
+
self.to_s.to_bencoding
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Symbol#to_bencoding added by dagbrown 2006/6/3 for karmaclient
|
124
|
+
|
125
|
+
class Symbol
|
126
|
+
def to_bencoding
|
127
|
+
to_s.bencoding
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Array
|
132
|
+
def to_bencoding
|
133
|
+
"l" + self.map { |e| e.to_bencoding }.join + "e"
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.bencoded?(c)
|
137
|
+
c == ?l
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.parse_bencoding(c, s)
|
141
|
+
ret = RubyTorrent::BStream.new(s).map { |x| x }
|
142
|
+
raise RubyTorrent::BEncodingError, "missing list terminator" unless s.getc == ?e
|
143
|
+
ret
|
144
|
+
end
|
145
|
+
|
146
|
+
RubyTorrent::BStream.register_bencoded_class self
|
147
|
+
end
|
148
|
+
|
149
|
+
class Hash
|
150
|
+
def to_bencoding
|
151
|
+
"d" + keys.sort.map do |k|
|
152
|
+
v = self[k]
|
153
|
+
if v.nil?
|
154
|
+
nil
|
155
|
+
else
|
156
|
+
[k.to_bencoding, v.to_bencoding].join
|
157
|
+
end
|
158
|
+
end.compact.join + "e"
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.bencoded?(c)
|
162
|
+
c == ?d
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.parse_bencoding(c, s)
|
166
|
+
ret = {}
|
167
|
+
key = nil
|
168
|
+
RubyTorrent::BStream.new(s).each do |x|
|
169
|
+
if key == nil
|
170
|
+
key = x
|
171
|
+
else
|
172
|
+
ret[key] = x
|
173
|
+
key = nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
raise RubyTorrent::BEncodingError, "no dictionary terminator" unless s.getc == ?e
|
178
|
+
ret
|
179
|
+
end
|
180
|
+
|
181
|
+
RubyTorrent::BStream.register_bencoded_class self
|
182
|
+
end
|
data/test/test_all.rb
ADDED
data/test/test_client.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Tests for the client
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
require "test/unit"
|
6
|
+
require "rubytorrent/bencoding"
|
7
|
+
require "socket"
|
8
|
+
require "karmasphere/client"
|
9
|
+
require "karmasphere/query"
|
10
|
+
require "karmasphere/response"
|
11
|
+
require "karmasphere/debug"
|
12
|
+
|
13
|
+
# For test purposes, we create a mini-server which bencodes
|
14
|
+
# and transmits this response no matter what input is given.
|
15
|
+
# In this test suite -- we're testing the client -- we
|
16
|
+
# decode the response from the mini-server and match it
|
17
|
+
# against this expected value.
|
18
|
+
|
19
|
+
$stub_response = {
|
20
|
+
"_" => 12345,
|
21
|
+
"facts" => [
|
22
|
+
{
|
23
|
+
"f" => 4000,
|
24
|
+
"v" => -1,
|
25
|
+
"d" => "Invalid Source IP Address (cymru)"
|
26
|
+
}
|
27
|
+
],
|
28
|
+
"combiners" => {
|
29
|
+
"karmasphere.emailchecker" => {
|
30
|
+
"v" => -1000,
|
31
|
+
"d" => "<f4000: if-fail(0) => return bad(1.0)>"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
# We have four test cases in total, exploring each combination of:
|
37
|
+
# * Turnaround vs Stub
|
38
|
+
# * TCP vs UDP
|
39
|
+
|
40
|
+
$port = 8666
|
41
|
+
|
42
|
+
class TC_Client_Turnaround_TCP < Test::Unit::TestCase
|
43
|
+
@@port = $port+1
|
44
|
+
|
45
|
+
def setup
|
46
|
+
@thr=Thread.new do
|
47
|
+
Karmasphere.dprint("TC_Client_Turnaround_TCP: constructing listen server on port #@@port\n");
|
48
|
+
@server = TCPServer.new(nil, @@port)
|
49
|
+
|
50
|
+
# mengwong 20060701: i believe TCPServers are
|
51
|
+
# automatically created with reuseaddr = true,
|
52
|
+
# but you may use the following code to check.
|
53
|
+
Karmasphere.dprint("created server with #{@server.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).inspect}\n");
|
54
|
+
|
55
|
+
while(session = @server.accept)
|
56
|
+
Thread.new do
|
57
|
+
Karmasphere.dprint("accepted new session #{session.inspect}\n");
|
58
|
+
len_str = session.recv(4)
|
59
|
+
len=len_str.unpack("N")[0]
|
60
|
+
info=session.recv(len)
|
61
|
+
Karmasphere.dprint("received #{len} bytes of info: #{info}\n");
|
62
|
+
session.send len_str+info, 0
|
63
|
+
Karmasphere.dprint("echoing back #{len} bytes of info: #{info}\n");
|
64
|
+
session.close
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def teardown
|
71
|
+
@server.close
|
72
|
+
@server = nil
|
73
|
+
@thr.kill
|
74
|
+
GC.start
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_startup
|
78
|
+
Karmasphere.dprint("test_client: test_startup: beginning\n");
|
79
|
+
|
80
|
+
# we need to wait for the listener thread established in
|
81
|
+
# setup to be ready. we do a poor man's thread
|
82
|
+
# synchronization by simply testing the @server. if we
|
83
|
+
# were doing this right, we'd use a MonitorMixin as
|
84
|
+
# documented on page 138 of the pickax book.
|
85
|
+
while not @server do
|
86
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
87
|
+
Thread.pass
|
88
|
+
end
|
89
|
+
|
90
|
+
Karmasphere.dprint("test_client: test_startup: proceeding.\n");
|
91
|
+
|
92
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
93
|
+
:port => @@port,
|
94
|
+
:type => Karmasphere::CONNECTION_TCP)
|
95
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
96
|
+
response = client.ask(query)
|
97
|
+
Karmasphere.dprint("test_client: got back response " , response.instance_variable_get("@hash"));
|
98
|
+
assert_equal query.to_hash, response.instance_variable_get("@hash")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class TC_Client_Turnaround_UDP < Test::Unit::TestCase
|
103
|
+
@@port = $port + 2
|
104
|
+
def setup
|
105
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: constructing listen server on port #@@port\n");
|
106
|
+
@thr=Thread.new do
|
107
|
+
|
108
|
+
@server = UDPSocket.new
|
109
|
+
@server.bind(nil, @@port)
|
110
|
+
|
111
|
+
info=@server.recvfrom(8192)
|
112
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: received ", info, "\n");
|
113
|
+
|
114
|
+
@server.flush
|
115
|
+
client_h=info[1][2]
|
116
|
+
client_p=info[1][1]
|
117
|
+
query=info[0]
|
118
|
+
@server.send query, 0, client_h, client_p
|
119
|
+
@server.flush
|
120
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: sent stuff back to #{client_h}:#{client_p}\n");
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def teardown
|
126
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: tearing down server.\n");
|
127
|
+
@server.close
|
128
|
+
@server = nil
|
129
|
+
@thr.kill
|
130
|
+
GC.start
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_startup
|
134
|
+
while not @server do
|
135
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
136
|
+
Thread.pass
|
137
|
+
end
|
138
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
139
|
+
:port => @@port
|
140
|
+
)
|
141
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
142
|
+
response = client.ask(query)
|
143
|
+
assert_equal query.to_hash, response.instance_variable_get("@hash")
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_credentials
|
148
|
+
while not @server do
|
149
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
150
|
+
Thread.pass
|
151
|
+
end
|
152
|
+
assert_raises Karmasphere::CredentialsError do
|
153
|
+
Karmasphere::Client.new(:host => "localhost",
|
154
|
+
:port => @@port,
|
155
|
+
:principal => "foo")
|
156
|
+
end
|
157
|
+
assert_raises Karmasphere::CredentialsError do
|
158
|
+
Karmasphere::Client.new(:host => "localhost",
|
159
|
+
:port => @@port,
|
160
|
+
:credentials => "bar")
|
161
|
+
end
|
162
|
+
|
163
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
164
|
+
:port => @@port,
|
165
|
+
:principal => "foo",
|
166
|
+
:credentials => "bar"
|
167
|
+
)
|
168
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
169
|
+
response = client.ask(query)
|
170
|
+
assert_equal query.to_hash.merge({"a" => [ "foo", "bar" ]}),
|
171
|
+
response.instance_variable_get("@hash")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class TC_Client_Stub_UDP < Test::Unit::TestCase
|
176
|
+
@@port = $port + 3
|
177
|
+
def setup
|
178
|
+
@thr=Thread.new do
|
179
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: constructing listen server on port #@@port\n");
|
180
|
+
|
181
|
+
@server = UDPSocket.new
|
182
|
+
@server.bind("localhost", @@port)
|
183
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: created server #@port\n");
|
184
|
+
@server_ready = true
|
185
|
+
|
186
|
+
info=@server.recvfrom(8192)
|
187
|
+
@server.flush
|
188
|
+
client_h=info[1][2]
|
189
|
+
client_p=info[1][1]
|
190
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: server received packet from #{client_h}:#{client_p}\n");
|
191
|
+
query=info[0]
|
192
|
+
@server.send $stub_response.to_bencoding, 0, client_h, client_p
|
193
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: miniserver sent stuff back to #{client_h}:#{client_p}\n");
|
194
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: close_write()d.\n");
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def teardown
|
199
|
+
@thr.kill
|
200
|
+
@server.close
|
201
|
+
@server = nil
|
202
|
+
GC.start
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_startup
|
206
|
+
while not @server_ready do
|
207
|
+
Karmasphere.dprint("Stub_UDP test_startup: server not ready. passing.\n");
|
208
|
+
Thread.pass
|
209
|
+
end
|
210
|
+
Karmasphere.dprint("test_startup: proceeding.\n");
|
211
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
212
|
+
:port => @@port
|
213
|
+
)
|
214
|
+
query = Karmasphere::Query.new(:Id => 12345)
|
215
|
+
Karmasphere.dprint("test_startup: client asking query...\n");
|
216
|
+
response = client.ask(query)
|
217
|
+
Karmasphere.dprint("test_startup: ... client received response #{response}\n");
|
218
|
+
assert_equal $stub_response, response.instance_variable_get('@hash')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class TC_Client_Stub_TCP < Test::Unit::TestCase
|
223
|
+
@@port = $port + 4
|
224
|
+
def setup
|
225
|
+
Karmasphere.dprint("TC_Client_Stub_TCP: constructing listen server on port #@@port\n");
|
226
|
+
@thr = Thread.new do
|
227
|
+
|
228
|
+
@server = TCPServer.new(nil, @@port)
|
229
|
+
while(session = @server.accept)
|
230
|
+
Thread.new do
|
231
|
+
while not session.closed?
|
232
|
+
len_str = session.recv(4)
|
233
|
+
len=len_str.unpack("N")[0]
|
234
|
+
info=session.recv(len)
|
235
|
+
response_str=$stub_response.to_bencoding
|
236
|
+
response_len_str = [ response_str.length ].pack "N"
|
237
|
+
session.send response_len_str+response_str, 0
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def teardown
|
245
|
+
@server.close
|
246
|
+
@server = nil
|
247
|
+
@thr.kill
|
248
|
+
GC.start
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_startup
|
252
|
+
while not @server do
|
253
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
254
|
+
Thread.pass
|
255
|
+
end
|
256
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
257
|
+
:port => @@port,
|
258
|
+
:type => Karmasphere::CONNECTION_TCP)
|
259
|
+
query = Karmasphere::Query.new(:Id => 12345)
|
260
|
+
response = client.ask(query)
|
261
|
+
assert_equal $stub_response, response.instance_variable_get('@hash')
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
data/test/test_query.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "karmasphere/query"
|
3
|
+
|
4
|
+
class TC_Basic_Query < Test::Unit::TestCase
|
5
|
+
def test_automatic_query_id
|
6
|
+
client=Karmasphere::Query.new
|
7
|
+
assert_kind_of Karmasphere::Query, client
|
8
|
+
|
9
|
+
client2=Karmasphere::Query.new
|
10
|
+
assert_kind_of Karmasphere::Query, client2
|
11
|
+
|
12
|
+
assert_equal client.id, "q1"
|
13
|
+
assert_equal client.to_hash, { "_" => "q1" }
|
14
|
+
|
15
|
+
assert_equal client2.id, "q2"
|
16
|
+
assert_equal client2.to_hash, { "_" => "q2" }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_query_identities_munging
|
20
|
+
client=Karmasphere::Query.new(:Id => "q2")
|
21
|
+
assert_kind_of Karmasphere::Query, client
|
22
|
+
|
23
|
+
client.identities=[ ["127.0.0.1", 0 ] ]
|
24
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
25
|
+
assert_equal( { "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
26
|
+
client.to_hash)
|
27
|
+
|
28
|
+
client.identities = [ "127.0.0.1" ]
|
29
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
30
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
31
|
+
client.to_hash)
|
32
|
+
|
33
|
+
client.identities = "127.0.0.1"
|
34
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
35
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
36
|
+
client.to_hash)
|
37
|
+
|
38
|
+
client.identity = "127.0.0.1"
|
39
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
40
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
41
|
+
client.to_hash)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_query_identities_guessing
|
45
|
+
client=Karmasphere::Query.new(:Id => "q3")
|
46
|
+
assert_kind_of Karmasphere::Query, client
|
47
|
+
|
48
|
+
client.identities = "127.0.0.1"
|
49
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities )
|
50
|
+
assert_equal({ "_" => "q3", "i" => [ ["127.0.0.1", 0 ] ] },
|
51
|
+
client.to_hash)
|
52
|
+
|
53
|
+
client.identities = "::1/128"
|
54
|
+
assert_equal( [ ["::1/128", 1 ] ], client.identities )
|
55
|
+
assert_equal({ "_" => "q3", "i" => [ ["::1/128", 1 ] ] },
|
56
|
+
client.to_hash)
|
57
|
+
|
58
|
+
client.identities = "example.com"
|
59
|
+
assert_equal( [ ["example.com", 2 ] ], client.identities )
|
60
|
+
assert_equal({ "_" => "q3", "i" => [ ["example.com", 2 ] ] },
|
61
|
+
client.to_hash)
|
62
|
+
|
63
|
+
client.identities = "example@example.com"
|
64
|
+
assert_equal( [ ["example@example.com", 3 ] ], client.identities )
|
65
|
+
assert_equal({ "_" => "q3", "i" => [ ["example@example.com", 3 ] ] },
|
66
|
+
client.to_hash)
|
67
|
+
|
68
|
+
client.identities = "http://www.example.com/"
|
69
|
+
assert_equal( [ ["http://www.example.com/", 4 ] ], client.identities )
|
70
|
+
assert_equal({ "_" => "q3", "i" => [ ["http://www.example.com/", 4 ] ] },
|
71
|
+
client.to_hash)
|
72
|
+
|
73
|
+
client.identities = "chunky bacon"
|
74
|
+
assert_equal( [ ["chunky bacon", 5 ] ], client.identities )
|
75
|
+
assert_equal({ "_" => "q3", "i" => [ ["chunky bacon", 5 ] ] },
|
76
|
+
client.to_hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_query_identities_on_construction
|
80
|
+
client=Karmasphere::Query.new(:Id => 'q4',
|
81
|
+
:Identities => "example.com")
|
82
|
+
assert_kind_of Karmasphere::Query, client
|
83
|
+
|
84
|
+
assert_equal( [ ["example.com", 2 ] ], client.identities,
|
85
|
+
"Automatically-detected identity at construction time")
|
86
|
+
assert_equal({ "_" => "q4", "i" => [ ["example.com", 2 ] ] },
|
87
|
+
client.to_hash, "Simple identity at construction time")
|
88
|
+
|
89
|
+
client = Karmasphere::Query.new( :Id => 'q5',
|
90
|
+
:Identities => [ [ "127.0.0.1",
|
91
|
+
Karmasphere::IDT_IP4_ADDRESS,
|
92
|
+
Karmasphere::AUTHENTIC ] ] )
|
93
|
+
assert_equal( [ [ "127.0.0.1", 0, "a" ] ], client.identities )
|
94
|
+
assert_equal({ "_" => "q5", "i" => [ ["127.0.0.1", 0, "a" ] ] },
|
95
|
+
client.to_hash, "Complicated identity at construction time")
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_query_example_query
|
99
|
+
client = Karmasphere::Query.new( :Id => 12345,
|
100
|
+
:Identities => [ [ "127.0.0.1", Karmasphere::IDT_IP4_ADDRESS,
|
101
|
+
Karmasphere::SMTP_CLIENT_IP ],
|
102
|
+
[ "antony@karmasphere.com",
|
103
|
+
Karmasphere::IDT_EMAIL_ADDRESS,
|
104
|
+
Karmasphere::SMTP_ENV_MAIL_FROM ],
|
105
|
+
[ "karmasphere.com",
|
106
|
+
Karmasphere::IDT_DOMAIN_NAME,
|
107
|
+
Karmasphere::SMTP_ENV_HELO ] ],
|
108
|
+
:Composites => "karmasphere.emailchecker",
|
109
|
+
:Flags => 1 )
|
110
|
+
|
111
|
+
assert_kind_of Karmasphere::Query, client
|
112
|
+
|
113
|
+
assert_equal( { "_" => 12345,
|
114
|
+
"i" => [ [ "127.0.0.1", 0, "smtp.client-ip" ],
|
115
|
+
[ "antony@karmasphere.com", 3, "smtp.env.mail-from" ],
|
116
|
+
[ "karmasphere.com", 2, "smtp.env.helo" ] ],
|
117
|
+
"s" => "karmasphere.emailchecker", "fl" => 1 },
|
118
|
+
client.to_hash, "Test query from the protocol spec")
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "karmasphere/response"
|
3
|
+
|
4
|
+
$response_hash = {
|
5
|
+
"_" => 12345,
|
6
|
+
"facts" => [
|
7
|
+
{
|
8
|
+
"f" => 4000,
|
9
|
+
"v" => -1,
|
10
|
+
"d" => "Invalid Source IP Address (cymru)"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"combiners" => {
|
14
|
+
"karmasphere.emailchecker" => {
|
15
|
+
"v" => -1000,
|
16
|
+
"d" => "<f4000: if-fail(0) => return bad(1.0)>"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
class TC_BasicResponse < Test::Unit::TestCase
|
22
|
+
def test_basicresponse
|
23
|
+
response=nil
|
24
|
+
assert_nothing_raised {
|
25
|
+
response=Karmasphere::Response.new({"_" => 12345})
|
26
|
+
}
|
27
|
+
assert_kind_of Karmasphere::Response, response
|
28
|
+
assert_equal 12345, response.id
|
29
|
+
assert_nil response.time
|
30
|
+
assert_nil response.combinations
|
31
|
+
assert_equal [], response.facts
|
32
|
+
assert_equal [], response.combiner_names
|
33
|
+
assert_nil response.error
|
34
|
+
assert_nil response.message
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_errorresponse
|
38
|
+
response=nil
|
39
|
+
assert_nothing_raised {
|
40
|
+
response=Karmasphere::Response.new({"_" => "q314159",
|
41
|
+
"error" => 1,
|
42
|
+
"message" => "an error occurred"})
|
43
|
+
}
|
44
|
+
assert_kind_of Karmasphere::Response, response
|
45
|
+
assert_equal "q314159", response.id
|
46
|
+
# I don't think I need go through all those assert_nils again
|
47
|
+
assert_equal 1, response.error
|
48
|
+
assert_equal "an error occurred", response.message
|
49
|
+
assert_equal "Response_id 'q314159': 0 combinations, 0 facts\nError: an error occurred\n", response.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_success
|
53
|
+
response=nil
|
54
|
+
assert_nothing_raised {
|
55
|
+
response=Karmasphere::Response.new $response_hash
|
56
|
+
}
|
57
|
+
assert_kind_of Karmasphere::Response, response
|
58
|
+
assert_equal ["karmasphere.emailchecker"], response.combiner_names
|
59
|
+
assert_equal([{
|
60
|
+
"f" => 4000,
|
61
|
+
"v" => -1,
|
62
|
+
"d" => "Invalid Source IP Address (cymru)"
|
63
|
+
}], response.facts)
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: karmasphere-client
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.6.4
|
7
|
+
date: 2007-04-30 00:00:00 -07:00
|
8
|
+
summary: Karmasphere Client
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: devsupport@karmasphere.com
|
12
|
+
homepage: http://www.karmasphere.com.com/devzone/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: karmasphere
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
post_install_message:
|
30
|
+
authors:
|
31
|
+
- Meng Weng Wong
|
32
|
+
files:
|
33
|
+
- bin/rubykarmaclient
|
34
|
+
- test/test_all.rb
|
35
|
+
- test/test_client.rb
|
36
|
+
- test/test_query.rb
|
37
|
+
- test/test_response.rb
|
38
|
+
- lib/karmasphere
|
39
|
+
- lib/karmasphere.rb
|
40
|
+
- lib/rubytorrent
|
41
|
+
- lib/karmasphere/client.rb
|
42
|
+
- lib/karmasphere/constants.rb
|
43
|
+
- lib/karmasphere/debug.rb
|
44
|
+
- lib/karmasphere/query.rb
|
45
|
+
- lib/karmasphere/response.rb
|
46
|
+
- lib/rubytorrent/bencoding.rb
|
47
|
+
- README
|
48
|
+
test_files:
|
49
|
+
- test/test_all.rb
|
50
|
+
rdoc_options: []
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
executables:
|
54
|
+
- rubykarmaclient
|
55
|
+
extensions: []
|
56
|
+
requirements: []
|
57
|
+
dependencies: []
|