xrc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ module Xrc
2
+ class Connection
3
+ attr_reader :block, :domain, :port, :socket
4
+
5
+ def initialize(options, &block)
6
+ @domain = options[:domain]
7
+ @port = options[:port]
8
+ @block = block
9
+ end
10
+
11
+ def connect
12
+ @socket = Connector.connect(domain: domain, port: port)
13
+ start
14
+ end
15
+
16
+ def encrypt
17
+ @socket = TslConnector.connect(socket: socket)
18
+ start
19
+ end
20
+
21
+ def open
22
+ write Elements::Stream.new(domain: domain)
23
+ end
24
+
25
+ def write(object)
26
+ socket << object.to_s
27
+ end
28
+
29
+ private
30
+
31
+ def start
32
+ open
33
+ parse
34
+ end
35
+
36
+ def parse
37
+ Parser.new(socket: socket, &block).parse
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Xrc
2
+ class Connection
3
+ class Connector
4
+ def self.connect(*args)
5
+ new(*args).connect
6
+ end
7
+
8
+ attr_reader :options
9
+
10
+ def initialize(options)
11
+ @options = options
12
+ end
13
+
14
+ def connect
15
+ hosts.find do |host|
16
+ if socket = connect_to(host)
17
+ break socket
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def domain
25
+ options[:domain]
26
+ end
27
+
28
+ def port
29
+ options[:port]
30
+ end
31
+
32
+ def hosts
33
+ HostsResolver.call(domain)
34
+ end
35
+
36
+ def connect_to(host)
37
+ TCPSocket.new(host, port)
38
+ rescue SocketError, Errno::ECONNREFUSED => exception
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ module Xrc
2
+ class Connection
3
+ class TslConnector
4
+ def self.connect(*args)
5
+ new(*args).connect
6
+ end
7
+
8
+ attr_reader :options
9
+
10
+ def initialize(options = {})
11
+ @options = options
12
+ end
13
+
14
+ def connect
15
+ ssl_socket.connect
16
+ end
17
+
18
+ private
19
+
20
+ def ssl_socket
21
+ ssl_socket = Socket.new(socket, context)
22
+ ssl_socket.sync_close = true
23
+ ssl_socket
24
+ end
25
+
26
+ def socket
27
+ options[:socket]
28
+ end
29
+
30
+ def context
31
+ context = OpenSSL::SSL::SSLContext.new
32
+ context.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+ context
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ module Xrc
2
+ module Elements
3
+ class Auth < REXML::Element
4
+ attr_reader :options
5
+
6
+ def initialize(options = {})
7
+ super("auth")
8
+ @options = options
9
+ add_sasl_namespace
10
+ add_plain_mechanism_attribute
11
+ add_credentials_text
12
+ end
13
+
14
+ private
15
+
16
+ def jid
17
+ options[:jid]
18
+ end
19
+
20
+ def password
21
+ options[:password]
22
+ end
23
+
24
+ def plain_credentials
25
+ "#{jid.strip}\x00#{jid.node}\x00#{password}"
26
+ end
27
+
28
+ def add_sasl_namespace
29
+ add_namespace(Namespaces::SASL)
30
+ end
31
+
32
+ def add_plain_mechanism_attribute
33
+ attributes["mechanism"] = "PLAIN"
34
+ end
35
+
36
+ def add_credentials_text
37
+ self.text = Base64.strict_encode64(plain_credentials)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ module Xrc
2
+ module Elements
3
+ class Bind < REXML::Element
4
+ def initialize(options = {})
5
+ super("iq")
6
+ bind = REXML::Element.new("bind")
7
+ bind.add_namespace(Namespaces::BIND)
8
+ if options[:resource]
9
+ resource = REXML::Element.new("resource")
10
+ resource.text = options[:resource]
11
+ bind.add(resource)
12
+ end
13
+ add_attributes("type" => "set")
14
+ add(bind)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Xrc
2
+ module Elements
3
+ class Join < Presence
4
+ def initialize(options)
5
+ super()
6
+ attributes["from"] = options[:from]
7
+ attributes["to"] = options[:to]
8
+ add(x)
9
+ end
10
+
11
+ private
12
+
13
+ def x
14
+ element = REXML::Element.new("x")
15
+ element.add_namespace(Namespaces::MUC)
16
+ element
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Xrc
2
+ module Elements
3
+ class Message < REXML::Element
4
+ def initialize(options = {})
5
+ super("message")
6
+ attributes["from"] = options[:from]
7
+ attributes["to"] = options[:to]
8
+ attributes["type"] = options[:type]
9
+ body.text = options[:body]
10
+ add(body)
11
+ end
12
+
13
+ private
14
+
15
+ def body
16
+ @body ||= REXML::Element.new("body")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Xrc
2
+ module Elements
3
+ class Ping < REXML::Element
4
+ def initialize(options = {})
5
+ super("iq")
6
+ attributes["from"] = options[:from]
7
+ attributes["to"] = options[:to]
8
+ attributes["type"] = "get"
9
+ add(ping)
10
+ end
11
+
12
+ private
13
+
14
+ def ping
15
+ element = REXML::Element.new("ping")
16
+ element.add_namespace(Namespaces::PING)
17
+ element
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Xrc
2
+ module Elements
3
+ class Presence < REXML::Element
4
+ def initialize
5
+ super("presence")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Xrc
2
+ module Elements
3
+ class Roster < REXML::Element
4
+ def initialize
5
+ super("iq")
6
+ query = REXML::Element.new("query")
7
+ query.add_namespace(Namespaces::ROSTER)
8
+ add_attributes("type" => "get")
9
+ add(query)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Xrc
2
+ module Elements
3
+ class Session < REXML::Element
4
+ def initialize
5
+ super("iq")
6
+ session = REXML::Element.new("session")
7
+ session.add_namespace(Namespaces::SESSION)
8
+ add_attributes("type" => "set")
9
+ add(session)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Xrc
2
+ module Elements
3
+ class Starttls < REXML::Element
4
+ def initialize
5
+ super("starttls")
6
+ add_namespace(Namespaces::TLS)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module Xrc
2
+ module Elements
3
+ class Stream
4
+ attr_reader :options
5
+
6
+ def initialize(options)
7
+ @options = options
8
+ end
9
+
10
+ def to_s
11
+ %W[
12
+ <stream:stream
13
+ xmlns:stream="http://etherx.jabber.org/streams"
14
+ xmlns="jabber:client"
15
+ to="#{options[:domain]}"
16
+ xml:lang="en"
17
+ version="1.0">
18
+ ].join(" ")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ module Xrc
2
+ class HostsResolver
3
+ def self.call(*args)
4
+ new(*args).call
5
+ end
6
+
7
+ attr_reader :domain
8
+
9
+ def initialize(domain)
10
+ @domain = domain
11
+ end
12
+
13
+ def call
14
+ resolved_hosts + [domain]
15
+ end
16
+
17
+ private
18
+
19
+ def resolved_hosts
20
+ sorted_srvs.lazy.map(&:target).map(&:to_s).to_a
21
+ end
22
+
23
+ def sorted_srvs
24
+ @sorted_srvs ||= srvs.sort_by do |resource|
25
+ [resource.priority, -resource.weight]
26
+ end
27
+ end
28
+
29
+ def srvs
30
+ Resolv::DNS.open do |dns|
31
+ dns.getresources(name, type_class)
32
+ end
33
+ end
34
+
35
+ def name
36
+ "_xmpp-client._tcp.#{domain}"
37
+ end
38
+
39
+ def type_class
40
+ Resolv::DNS::Resource::IN::SRV
41
+ end
42
+ end
43
+ end
data/lib/xrc/jid.rb ADDED
@@ -0,0 +1,44 @@
1
+ module Xrc
2
+ class Jid
3
+ PATTERN = /\A(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?\z/
4
+
5
+ attr_reader :raw
6
+
7
+ def initialize(raw)
8
+ @raw = raw
9
+ end
10
+
11
+ def node
12
+ sections[0]
13
+ end
14
+
15
+ def domain
16
+ sections[1]
17
+ end
18
+
19
+ def resource
20
+ sections[2]
21
+ end
22
+
23
+ def strip
24
+ "#{node}@#{domain}"
25
+ end
26
+
27
+ def to_s
28
+ str = strip
29
+ str << "/#{resource}" if resource
30
+ str
31
+ end
32
+
33
+ def ==(jid)
34
+ jid = Jid.new(jid) unless jid.is_a?(Jid)
35
+ to_s == jid.to_s || strip == jid.strip
36
+ end
37
+
38
+ private
39
+
40
+ def sections
41
+ @sections ||= raw.scan(PATTERN).first
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ module Xrc
2
+ class MessageBuilder
3
+ # Builds a Message object from a REXML::Element.
4
+ # @param [REXML::Element] element A message element
5
+ # @return [Xrc::Messages::Base] An ancestor of Xrc::Messages::Base instance
6
+ def self.build(element)
7
+ new(element).build
8
+ end
9
+
10
+ # @param [REXML::Element] element A message element
11
+ def initialize(element)
12
+ @element = element
13
+ end
14
+
15
+ def build
16
+ message_class.new(@element)
17
+ end
18
+
19
+ private
20
+
21
+ def message_class
22
+ case
23
+ when has_room_message?
24
+ Messages::RoomMessage
25
+ when has_private_message?
26
+ Messages::PrivateMessage
27
+ when has_subject?
28
+ Messages::Subject
29
+ else
30
+ Messages::Null
31
+ end
32
+ end
33
+
34
+ def has_room_message?
35
+ has_groupchat_type? && has_body?
36
+ end
37
+
38
+ def has_private_message?
39
+ has_chat_type? && has_body?
40
+ end
41
+
42
+ def has_chat_type?
43
+ type == "chat"
44
+ end
45
+
46
+ def has_groupchat_type?
47
+ type == "groupchat"
48
+ end
49
+
50
+ def has_body?
51
+ !!@element.elements["body"]
52
+ end
53
+
54
+ def has_subject?
55
+ !!@element.elements["subject"]
56
+ end
57
+
58
+ def type
59
+ @element.attributes["type"].to_s
60
+ end
61
+ end
62
+ end