jabber4r-revive 0.9.0

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.
@@ -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