jabber4r-revive 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,258 @@
1
+ # coding: utf-8
2
+
3
+ # License: see LICENSE
4
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
5
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
6
+
7
+ module Jabber
8
+ # The connection class encapsulates the connection to the Jabber
9
+ # service including managing the socket and controlling the parsing
10
+ # of the Jabber XML stream.
11
+ class Connection
12
+ DISCONNECTED = 1
13
+ CONNECTED = 2
14
+
15
+ # Public
16
+ attr_reader :host, :port, :status, :input, :output
17
+
18
+ # Internal
19
+ attr_reader :poll_thread, :parser_thread
20
+
21
+ # Internal
22
+ attr_reader :filters, :handlers
23
+
24
+ # Internal
25
+ attr_reader :socket, :parser
26
+
27
+ def initialize(host, port = 5222)
28
+ @host, @port = host, port
29
+
30
+ @handlers, @filters = {}, {}
31
+
32
+ @poll_counter = 10
33
+ @mutex = Mutex.new
34
+
35
+ @status = DISCONNECTED
36
+ end
37
+
38
+ # Public: Connects to the Jabber server through a TCP Socket and
39
+ # starts the Jabber parser.
40
+ #
41
+ # Returns nothing
42
+ def connect
43
+ @socket = TCPSocket.new(@host, @port)
44
+ @parser = Jabber::Protocol.Parser.new(socket, self)
45
+
46
+ register_parsing_thread
47
+ register_polling_thread
48
+
49
+ @status = CONNECTED
50
+ end
51
+
52
+ # Internal: Register new parser thread
53
+ #
54
+ # Returns nothing
55
+ def register_parsing_thread
56
+ @parser_thread = Thread.new { parser.parse }
57
+ end
58
+
59
+ # Internal: Register new polling thread
60
+ #
61
+ # Returns nothing
62
+ def register_polling_thread
63
+ @poll_thread = Thread.new { poll }
64
+ end
65
+
66
+ # Public: Closes the connection to the Jabber service
67
+ #
68
+ # Returns nothing
69
+ def close
70
+ parser_thread.kill if parser_thread # why if?
71
+ poll_thread.kill
72
+ socket.close if socket
73
+
74
+ @status = DISCONNECTED
75
+ end
76
+ alias :disconnect :close
77
+
78
+ # Public: Returns if this connection is connected to a Jabber service
79
+ #
80
+ # Returns boolean
81
+ def connected?
82
+ status == CONNECTED
83
+ end
84
+
85
+ # Public: Returns if this connection is NOT connected to a Jabber service
86
+ #
87
+ # Returns boolean
88
+ def disconnected?
89
+ status == DISCONNECTED
90
+ end
91
+
92
+ # Public: Adds a filter block to process received XML messages
93
+ #
94
+ # name - String the name of filter
95
+ # block - Block of code
96
+ #
97
+ # Returns nothing
98
+ def add_filter(name, &block)
99
+ raise ArgumentError, "Expected block to be given" if block.nil?
100
+
101
+ @filters[name] = block
102
+ end
103
+
104
+ # Public: Removes a filter block
105
+ #
106
+ # name - String the name of filter
107
+ #
108
+ # Returns Block of code
109
+ def remove_filter(name)
110
+ filters.delete(name)
111
+ end
112
+
113
+ # Public: Receiving xml element, and processing it
114
+ # NOTE: Synchonized by Mutex
115
+ #
116
+ # xml - String the string containing xml
117
+ # proc_object - Proc the proc object to call (default: nil)
118
+ # block - Block of ruby code
119
+ #
120
+ # Returns nothing
121
+ def send(xml, proc_object = nil, &block)
122
+ @mutex.synchronize { write_to_socket(xml, proc_object, &block) }
123
+ end
124
+
125
+ # Public: Receiving xml element, and processing it
126
+ # NOTE: Synchonized by Mutex
127
+ #
128
+ # xml_element - ParsedXMLElement the received from socket xml element
129
+ #
130
+ # Returns nothing
131
+ def receive(xml)
132
+ @mutex.synchronize { process_xml_from_socket(xml) }
133
+ end
134
+
135
+ # Internal: Sends XML data to the socket and (optionally) waits
136
+ # to process received data.
137
+ # NOTE: If both habdler and block are given, handler has higher proirity
138
+ #
139
+ # xml - String the xml data to send
140
+ # handler - [Proc|Lambda|#call] the proc object or labda to handle response data (optional)
141
+ # block - Block the block of ruby code (optional)
142
+ #
143
+ # Returns nothing
144
+ def write_to_socket(xml, handler = nil, &block)
145
+ Jabber.debug("SENDING:\n#{xml}")
146
+
147
+ handler = block if handler.nil?
148
+ handlers[Thread.current] = handler unless handler.nil?
149
+
150
+ socket.write(xml)
151
+
152
+ @poll_counter = 10
153
+ end
154
+
155
+ # Internal: Processes a received ParsedXMLElement and executes
156
+ # registered handlers and filters against it
157
+ #
158
+ # xml - ParsedXMLElement The received element
159
+ #
160
+ # Returns nothing
161
+ def process_xml_from_socket(xml)
162
+ sleep 0.1 while wait_for_consume?
163
+
164
+ Jabber.debug("RECEIVED:\n#{xml}")
165
+
166
+ consume_xml_by_handlers(xml) || consume_xml_by_filters(xml)
167
+ end
168
+
169
+ # Internal: Processes a received ParsedXMLElement by handlers
170
+ #
171
+ # xml - ParsedXMLElement The received element
172
+ #
173
+ # Returns boolean
174
+ def consume_xml_by_handlers(xml)
175
+ handlers.each do |thread, block|
176
+ begin
177
+ block.call(xml)
178
+
179
+ if xml.element_consumed?
180
+ handlers.delete(thread)
181
+ thread.wakeup if thread.alive?
182
+
183
+ return true
184
+ end
185
+ rescue Exception => error
186
+ puts error.to_s
187
+ puts error.backtrace.join("\n")
188
+ end
189
+ end
190
+
191
+ false
192
+ end
193
+
194
+ # Internal: Processes a received ParsedXMLElement by filters
195
+ #
196
+ # xml - ParsedXMLElement The received element
197
+ #
198
+ # Returns boolean
199
+ def consume_xml_by_filters(xml)
200
+ filters.each_value do |block|
201
+ begin
202
+ block.call(xml)
203
+
204
+ return true if xml.element_consumed?
205
+ rescue Exception => error
206
+ puts error.to_s
207
+ puts error.backtrace.join("\n")
208
+ end
209
+ end
210
+
211
+ false
212
+ end
213
+
214
+ # Internal: Should we wait for next part of socket data
215
+ #
216
+ # Returns boolean
217
+ def wait_for_consume?
218
+ handlers.size.zero? && filters.size.zero?
219
+ end
220
+
221
+ ##
222
+ # Mounts a block to handle exceptions if they occur during the
223
+ # poll send. This will likely be the first indication that
224
+ # the socket dropped in a Jabber Session.
225
+ #
226
+ def on_connection_exception(&block)
227
+ @exception_block = block
228
+ end
229
+
230
+ def parse_failure(exception = nil)
231
+ Thread.new { @exception_block.call(exception) if @exception_block }
232
+ end
233
+
234
+ ############################################################################
235
+ # All that under needs to be REFACTORED #
236
+ ############################################################################
237
+
238
+ ##
239
+ # Starts a polling thread to send "keep alive" data to prevent
240
+ # the Jabber connection from closing for inactivity.
241
+ #
242
+ def poll
243
+ sleep 10
244
+ while true
245
+ sleep 2
246
+ @poll_counter = @poll_counter - 1
247
+ if @poll_counter < 0
248
+ begin
249
+ send(" \t ")
250
+ rescue
251
+ Thread.new {@exception_block.call if @exception_block}
252
+ break
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+
3
+ # License: see LICENSE
4
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
5
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
6
+ # Copyright (C) 2013 Sergey Fedorov <strech_ftf@mail.ru>
7
+
8
+ require "singleton"
9
+ require "logger"
10
+
11
+ module Jabber
12
+ # Produces debug methods
13
+ #
14
+ # Example
15
+ #
16
+ # def warn(message)
17
+ # Debug.instance.send(:warn, message)
18
+ # end
19
+ # module_function m
20
+ [:warn, :debug, :info].each do |m|
21
+ define_method(m) do |message|
22
+ Debugger.send(m, message)
23
+ end
24
+ module_function m
25
+ end
26
+
27
+ class Debugger
28
+ include Singleton
29
+
30
+ attr_accessor :enabled, :logger
31
+
32
+ def initialize
33
+ @logger = Logger.new(STDOUT)
34
+ @enabled = false
35
+ end
36
+
37
+ class << self
38
+ def logger=(logger)
39
+ instance.logger = logger
40
+ end
41
+
42
+ def enable!
43
+ instance.enabled = true
44
+ end
45
+
46
+ def disable!
47
+ instance.enabled = false
48
+ end
49
+
50
+ def enabled?
51
+ instance.enabled
52
+ end
53
+
54
+ [:warn, :debug, :info].each do |m|
55
+ define_method(m) do |message|
56
+ enabled? && instance.logger.send(m, message)
57
+ end
58
+ end
59
+ end # class << self
60
+ end
61
+ end
@@ -0,0 +1,125 @@
1
+ # coding: utf-8
2
+
3
+ # License: see LICENSE
4
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
5
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
6
+
7
+ module Jabber
8
+ # The Jabber ID class is used to hold a parsed jabber identifier (account+host+resource)
9
+ class JID
10
+ PATTERN = /^(?:(?<node>[^@]*)@)??(?<host>[^@\/]*)(?:\/(?<resource>.*?))?$/.freeze
11
+
12
+ # The node (account)
13
+ attr_accessor :node
14
+
15
+ # The resource id
16
+ attr_accessor :resource
17
+
18
+ # The host name (or IP address)
19
+ attr_accessor :host
20
+
21
+ # Public: Convert something to Jabber::JID
22
+ #
23
+ # jid - [String|Jabber::JID] the jid of future Jabber::JID
24
+ #
25
+ # Returns Jabber::JID
26
+ def self.to_jid(jid)
27
+ return jid if jid.kind_of? self
28
+
29
+ new jid
30
+ end
31
+
32
+ # Constructs a JID from the supplied string of the format:
33
+ # node@host[/resource] (e.g. "rich_kilmer@jabber.com/laptop")
34
+ #
35
+ # jid - String the jabber id string to parse
36
+ # host - String the host of jabber server (optional)
37
+ # resource - String the resource of jabber id (optional)
38
+ #
39
+ # Examples
40
+ #
41
+ # jid = Jabber::JID.new("strech@localhost/attach")
42
+ # jid.node # => "strech"
43
+ # jid.host # => "localhost"
44
+ # jid.resource # => "attach"
45
+ #
46
+ # Raises ArgumentError
47
+ # Returns nothing
48
+ def initialize(jid, host = nil, resource = nil)
49
+ raise ArgumentError, "Node can't be empty" if jid.to_s.empty?
50
+
51
+ @node, @host, @resource = self.class.parse(jid)
52
+ @node, @host = @host, nil if @node.nil? && @host
53
+
54
+ @host = host unless host.nil?
55
+ @resource = resource unless resource.nil?
56
+
57
+ raise ArgumentError, "Couldn't create JID without host" if @host.to_s.empty?
58
+ end
59
+
60
+ # Public: Evalutes whether the node, resource and host are the same
61
+ #
62
+ # jid - Jabber::JID the other jabber id
63
+ #
64
+ # Returns boolean
65
+ def ==(jid)
66
+ jid.to_s == self.to_s
67
+ end
68
+
69
+ # Public: Compare accounts without resources
70
+ #
71
+ # jid - Jabber::JID the other jabber id
72
+ #
73
+ # Returns boolean
74
+ def same?(jid)
75
+ other_jid = self.class.to_jid(jid)
76
+
77
+ other_jid.node == node && other_jid.host == host
78
+ end
79
+
80
+ # Public: Strip resource from jid and return new object
81
+ #
82
+ # Returns Jabber::JID
83
+ def strip
84
+ self.class.new(node, host)
85
+ end
86
+
87
+ # Public: Strip resource from jid and return the same object
88
+ #
89
+ # Returns Jabber::JID
90
+ def strip!
91
+ @resource = nil
92
+
93
+ self
94
+ end
95
+
96
+ # Public: String representation of JID
97
+ #
98
+ # Returns String
99
+ def to_s
100
+ ["#{node}@#{host}", resource].compact.join "/"
101
+ end
102
+
103
+ # Public: Override #hash to hash based on the to_s method
104
+ #
105
+ # Returns Fixnum
106
+ def hash
107
+ to_s.hash
108
+ end
109
+
110
+ private
111
+ # Internal: Parse jid string for node, host, resource
112
+ #
113
+ # jid - String jabber id
114
+ #
115
+ # Examples
116
+ #
117
+ # result = Jabber::JID.parse("strech@localhost/pewsource")
118
+ # result # => ["strech", "localhost", "pewsource"]
119
+ #
120
+ # Rerturns Array
121
+ def self.parse(jid)
122
+ jid.match(PATTERN).captures
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,260 @@
1
+ # coding: utf-8
2
+
3
+ # License: see LICENSE
4
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
5
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
6
+
7
+ module Jabber::Protocol
8
+ ##
9
+ # A class used to build/parse IQ requests/responses
10
+ #
11
+ class Iq
12
+ attr_accessor :session,:to, :from, :id, :type, :xmlns, :data,:error,:errorcode
13
+ ERROR="error"
14
+ GET="get"
15
+ SET="set"
16
+ RESULT="result"
17
+
18
+ ##
19
+ # Factory to build an IQ object from xml element
20
+ #
21
+ # session:: [Jabber::Session] The Jabber session instance
22
+ # element:: [Jabber::Protocol::ParsedXMLElement] The received XML object
23
+ # return:: [Jabber::Protocol::Iq] The newly created Iq object
24
+ #
25
+ def self.from_element(session, element)
26
+ iq = new(session)
27
+
28
+ iq.from = Jabber::JID.new(element.attr_from) if element.attr_from
29
+ iq.to = Jabber::JID.new(element.attr_to) if element.attr_to
30
+
31
+ iq.id = element.attr_id
32
+ iq.type = element.attr_type
33
+ iq.xmlns = element.query.attr_xmlns
34
+ iq.data = element.query
35
+ iq.session = session
36
+
37
+ if element.attr_type = "error"
38
+ iq.error = element.error
39
+ iq.errorcode = element.error.attr_code
40
+ end
41
+
42
+ return iq
43
+ end
44
+
45
+ ##
46
+ # Default constructor to build an Iq object
47
+ # session:: [Jabber::Session] The Jabber session instance
48
+ # id:: [String=nil] The (optional) id of the Iq object
49
+ def initialize(session,id=nil)
50
+ @session=session
51
+ @id=id
52
+ end
53
+
54
+ ##
55
+ # Return an IQ object that uses the jabber:iq:private namespace
56
+ #
57
+ def self.get_private(session,id,ename,ns)
58
+ iq=Iq.new(session,id)
59
+ iq.type="get"
60
+ iq.xmlns="jabber:iq:private"
61
+ iq.data=XMLElement.new(ename,{'xmlns' => ns});
62
+ return iq
63
+ end
64
+
65
+
66
+ ##
67
+ # Generates an IQ roster request XML element
68
+ #
69
+ # id:: [String] The message id
70
+ # return:: [String] The XML data to send
71
+ #
72
+ def self.gen_roster(session, id)
73
+ iq = Iq.new(session, id)
74
+ iq.type = "get"
75
+ iq.xmlns = "jabber:iq:roster"
76
+ return iq
77
+ #return XMLElement.new("iq", {"type"=>"get", "id"=>id}).add_child("query", {"xmlns"=>"jabber:iq:roster"}).to_s
78
+ end
79
+
80
+ ##
81
+ # Generates an IQ authortization request XML element
82
+ #
83
+ # id:: [String] The message id
84
+ # username:: [String] The username
85
+ # password:: [String] The password
86
+ # email:: [String] The email address of the account
87
+ # name:: [String] The full name
88
+ # return:: [String] The XML data to send
89
+ #
90
+ def self.gen_registration(session, id, username, password, email, name)
91
+ iq = Iq.new(session, id)
92
+ iq.type = "set"
93
+ iq.xmlns = "jabber:iq:register"
94
+ iq.data = XMLElement.new("username").add_data(username).to_s
95
+ iq.data << XMLElement.new("password").add_data(password).to_s
96
+ iq.data << XMLElement.new("email").add_data(email).to_s
97
+ iq.data << XMLElement.new("name").add_data(name).to_s
98
+ return iq
99
+ end
100
+
101
+ ##
102
+ # Generates an IQ Roster Item add request XML element
103
+ #
104
+ # session:: [Session] The session
105
+ # id:: [String] The message id
106
+ # jid:: [JID] The Jabber ID to add to the roster
107
+ # name:: [String] The full name
108
+ # return:: [String] The XML data to send
109
+ #
110
+ def self.gen_add_rosteritem(session, id, jid, name)
111
+ iq = Iq.new(session, id)
112
+ iq.type = "set"
113
+ iq.xmlns = "jabber:iq:roster"
114
+ iq.data = XMLElement.new("item").add_attribute("jid", jid).add_attribute("name", name).to_s
115
+ return iq
116
+ end
117
+
118
+ ##
119
+ # Generates an IQ authortization request XML element
120
+ #
121
+ # id:: [String] The message id
122
+ # username:: [String] The username
123
+ # password:: [String] The password
124
+ # resource:: [String] The resource to bind this session to
125
+ # return:: [String] The XML data to send
126
+ #
127
+ def self.gen_auth(session, id, username, password, resource)
128
+ iq = Iq.new(session, id)
129
+ iq.type = "set"
130
+ iq.xmlns = "jabber:iq:auth"
131
+ iq.data = XMLElement.new("username").add_data(username).to_s
132
+ iq.data << XMLElement.new("password").add_data(password).to_s
133
+ iq.data << XMLElement.new("resource").add_data(resource).to_s
134
+ return iq
135
+ #element = XMLElement.new("iq", {"type"=>"set", "id"=>id}).add_child("query", {"xmlns"=>"jabber:iq:auth"}).add_child("username").add_data(username).to_parent.add_child("password").add_data(password).to_parent.add_child("resource").add_data(resource).to_parent.to_s
136
+ end
137
+
138
+ ##
139
+ # Generates an IQ digest authortization request XML element
140
+ #
141
+ # id:: [String] The message id
142
+ # username:: [String] The username
143
+ # digest:: [String] The SHA-1 hash of the sessionid and the password
144
+ # resource:: [String] The resource to bind this session to
145
+ # return:: [String] The XML data to send
146
+ #
147
+ def self.gen_auth_digest(session, id, username, digest, resource)
148
+ iq = Iq.new(session, id)
149
+ iq.type = "set"
150
+ iq.xmlns = "jabber:iq:auth"
151
+ iq.data = XMLElement.new("username").add_data(username).to_s
152
+ iq.data << XMLElement.new("digest").add_data(digest).to_s
153
+ iq.data << XMLElement.new("resource").add_data(resource).to_s
154
+ return iq
155
+ #return XMLElement.new("iq", {"type"=>"set", "id"=>id}).add_child("query", {"xmlns"=>"jabber:iq:auth"}).add_child("username").add_data(username).to_parent.add_child("digest").add_data(digest).to_parent.add_child("resource").add_data(resource).to_parent.to_s
156
+ end
157
+
158
+ ##
159
+ # Generates an IQ out of bounds XML element
160
+ #
161
+ # to:: [JID] The Jabber ID to send to
162
+ # url:: [String] The data to send
163
+ # desc:: [String=""] The description of the data
164
+ # return:: [String] The XML data to send
165
+ #
166
+ def self.gen_oob(session, to, url, desc="")
167
+ iq = Iq.new(session, nil)
168
+ iq.type = "set"
169
+ iq.xmlns = "jabber:iq:oob"
170
+ iq.data = XMLElement.new("url").add_data(url).to_s
171
+ iq.data << XMLElement.new("desc").add_data(desc).to_s
172
+ return iq
173
+ #return XMLElement.new("iq", {"type"=>"set"}).add_child("query", {"xmlns"=>"jabber:iq:oob"}).add_child("url").add_data(url).to_parent.add_child("desc").add_data(data).to_parent.to_s
174
+ end
175
+
176
+ ##
177
+ # Generates an VCard request XML element
178
+ #
179
+ # id:: [String] The message ID
180
+ # to:: [JID] The jabber id of the account to get the VCard for
181
+ # return:: [String] The XML data to send
182
+ #
183
+ def self.gen_vcard(session, id, to)
184
+ iq = Iq.new(session, id)
185
+ iq.xmlns = "vcard-temp"
186
+ iq.type = "get"
187
+ iq.to = to
188
+ return iq
189
+ #return XMLElement.new("iq", {"type"=>"get", "id"=>id, "to"=>to}).add_child("query", {"xmlns"=>"vcard-temp"}).to_s
190
+ end
191
+
192
+
193
+
194
+
195
+ ##
196
+ # Sends the IQ to the Jabber service for delivery
197
+ #
198
+ # wait:: [Boolean = false] Wait for reply before return?
199
+ # &block:: [Block] A block to process the message replies
200
+ #
201
+ def send(wait=false, &block)
202
+ if wait
203
+ iq = nil
204
+ blockedThread = Thread.current
205
+ @session.connection.send(self.to_s, block) do |je|
206
+ if je.element_tag == "iq" and je.attr_id == @id
207
+ je.consume_element
208
+ iq = self.class.from_element(@session, je)
209
+ blockedThread.wakeup
210
+ end
211
+ end
212
+ Thread.stop
213
+ return iq
214
+ else
215
+ @session.connection.send(self.to_s, block) if @session
216
+ end
217
+ end
218
+
219
+ ##
220
+ # Builds a reply to an existing Iq
221
+ #
222
+ # return:: [Jabber::Protocol::Iq] The result Iq
223
+ #
224
+ def reply
225
+ iq = Iq.new(@session,@id)
226
+ iq.to = @from
227
+ iq.id = @id
228
+ iq.type = 'result'
229
+ @is_reply = true
230
+ return iq
231
+ end
232
+
233
+ ##
234
+ # Generates XML that complies with the Jabber protocol for
235
+ # sending the Iq through the Jabber service.
236
+ #
237
+ # return:: [String] The XML string.
238
+ #
239
+ def to_xml
240
+ elem = XMLElement.new("iq", { "type"=>@type})
241
+ elem.add_attribute("to" ,@to) if @to
242
+ elem.add_attribute("id", @id) if @id
243
+ elem.add_child("query").add_attribute("xmlns",@xmlns).add_data(@data.to_s)
244
+ if @type=="error" then
245
+ e=elem.add_child("error");
246
+ e.add_attribute("code",@errorcode) if @errorcode
247
+ e.add_data(@error) if @error
248
+ end
249
+ return elem.to_s
250
+ end
251
+
252
+ ##
253
+ # see to_xml
254
+ #
255
+ def to_s
256
+ to_xml
257
+ end
258
+
259
+ end
260
+ end