xmpp4r 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +8 -0
  2. data/README.rdoc +4 -1
  3. data/Rakefile +10 -20
  4. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +20 -1
  5. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +7 -0
  6. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +7 -1
  7. data/lib/xmpp4r/callbacks.rb +9 -0
  8. data/lib/xmpp4r/caps/c.rb +14 -0
  9. data/lib/xmpp4r/caps/helper/helper.rb +1 -4
  10. data/lib/xmpp4r/client.rb +42 -15
  11. data/lib/xmpp4r/connection.rb +7 -3
  12. data/lib/xmpp4r/debuglog.rb +22 -1
  13. data/lib/xmpp4r/discovery.rb +1 -0
  14. data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
  15. data/lib/xmpp4r/discovery/iq/discoinfo.rb +2 -2
  16. data/lib/xmpp4r/discovery/iq/discoitems.rb +2 -2
  17. data/lib/xmpp4r/errors.rb +5 -2
  18. data/lib/xmpp4r/httpbinding/client.rb +9 -19
  19. data/lib/xmpp4r/last.rb +2 -0
  20. data/lib/xmpp4r/last/helper/helper.rb +37 -0
  21. data/lib/xmpp4r/last/iq/last.rb +67 -0
  22. data/lib/xmpp4r/location.rb +2 -0
  23. data/lib/xmpp4r/location/helper/helper.rb +56 -0
  24. data/lib/xmpp4r/location/location.rb +179 -0
  25. data/lib/xmpp4r/message.rb +32 -0
  26. data/lib/xmpp4r/presence.rb +1 -1
  27. data/lib/xmpp4r/pubsub/children/configuration.rb +1 -1
  28. data/lib/xmpp4r/pubsub/children/items.rb +11 -2
  29. data/lib/xmpp4r/pubsub/children/publish.rb +14 -0
  30. data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
  31. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +2 -3
  32. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +4 -4
  33. data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +90 -0
  34. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +58 -19
  35. data/lib/xmpp4r/reliable.rb +168 -0
  36. data/lib/xmpp4r/rexmladdons.rb +6 -0
  37. data/lib/xmpp4r/roster/helper/roster.rb +5 -2
  38. data/lib/xmpp4r/sasl.rb +19 -8
  39. data/lib/xmpp4r/stream.rb +133 -31
  40. data/lib/xmpp4r/streamparser.rb +9 -1
  41. data/lib/xmpp4r/test/listener_mocker.rb +118 -0
  42. data/lib/xmpp4r/xmpp4r.rb +3 -1
  43. data/test/bytestreams/tc_ibb.rb +6 -4
  44. data/test/bytestreams/tc_socks5bytestreams.rb +3 -2
  45. data/test/caps/tc_helper.rb +4 -2
  46. data/test/dataforms/tc_data.rb +1 -1
  47. data/test/last/tc_helper.rb +75 -0
  48. data/test/lib/clienttester.rb +43 -14
  49. data/test/muc/tc_muc_mucclient.rb +6 -2
  50. data/test/pubsub/tc_helper.rb +131 -8
  51. data/test/pubsub/tc_nodeconfig.rb +7 -0
  52. data/test/reliable/tc_disconnect_cleanup.rb +334 -0
  53. data/test/reliable/tc_disconnect_exception.rb +37 -0
  54. data/test/reliable/tc_listener_mocked_test.rb +68 -0
  55. data/test/reliable/tc_reliable_connection.rb +31 -0
  56. data/test/roster/tc_helper.rb +21 -11
  57. data/test/rpc/tc_helper.rb +2 -2
  58. data/test/tc_callbacks.rb +3 -3
  59. data/test/tc_message.rb +15 -0
  60. data/test/tc_stream.rb +59 -121
  61. data/test/tc_streamError.rb +2 -4
  62. data/test/tc_streamparser.rb +26 -13
  63. data/test/ts_xmpp4r.rb +0 -9
  64. data/test/tune/tc_helper_recv.rb +0 -2
  65. data/test/vcard/tc_helper.rb +1 -1
  66. data/xmpp4r.gemspec +31 -84
  67. metadata +116 -167
  68. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +0 -62
@@ -4,4 +4,5 @@
4
4
 
5
5
  require 'xmpp4r/discovery/iq/discoinfo'
6
6
  require 'xmpp4r/discovery/iq/discoitems'
7
+ require 'xmpp4r/discovery/helper/helper'
7
8
  require 'xmpp4r/discovery/helper/responder'
@@ -0,0 +1,58 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r'
6
+ require 'xmpp4r/discovery'
7
+
8
+ module Jabber
9
+ module Discovery
10
+ ##
11
+ # A Helper to manage service and item discovery.
12
+ class Helper
13
+ def initialize(client)
14
+ @stream = client
15
+ end
16
+
17
+ ##
18
+ # Service discovery on a JID.
19
+ # jid:: [JID]
20
+ # return:: [Jabber::Discovery::IqQueryDiscoInfo]
21
+ def get_info_for(jid, node = nil)
22
+ iq = Jabber::Iq.new(:get, jid)
23
+ iq.from = @stream.jid
24
+ disco = Jabber::Discovery::IqQueryDiscoInfo.new
25
+ disco.node = node
26
+ iq.add(disco)
27
+
28
+ res = nil
29
+
30
+ @stream.send_with_id(iq) { |reply|
31
+ res = reply.query
32
+ }
33
+
34
+ res
35
+ end
36
+
37
+ ##
38
+ # Item discovery on a JID.
39
+ # jid:: [JID]
40
+ # return:: [Jabber::Discovery::IqQueryDiscoItems]
41
+ def get_items_for(jid, node = nil)
42
+ iq = Jabber::Iq.new(:get, jid)
43
+ iq.from = @stream.jid
44
+ disco = Jabber::Discovery::IqQueryDiscoItems.new
45
+ disco.node = node
46
+ iq.add(disco)
47
+
48
+ res = nil
49
+
50
+ @stream.send_with_id(iq) { |reply|
51
+ res = reply.query
52
+ }
53
+
54
+ res
55
+ end
56
+ end
57
+ end
58
+ end
@@ -29,14 +29,14 @@ module Jabber
29
29
  end
30
30
 
31
31
  ##
32
- # Get the queried Service Discovery node or nil
32
+ # Set the queried Service Discovery node or nil
33
33
  # val:: [String]
34
34
  def node=(val)
35
35
  attributes['node'] = val
36
36
  end
37
37
 
38
38
  ##
39
- # Get the queried Service Discovery node or nil
39
+ # Set the queried Service Discovery node or nil
40
40
  # (chaining-friendly)
41
41
  # val:: [String]
42
42
  def set_node(val)
@@ -32,13 +32,13 @@ module Jabber
32
32
  end
33
33
 
34
34
  ##
35
- # Get the queried Service Discovery node or nil
35
+ # Set the queried Service Discovery node or nil
36
36
  def node=(val)
37
37
  attributes['node'] = val
38
38
  end
39
39
 
40
40
  ##
41
- # Get the queried Service Discovery node or nil
41
+ # Set the queried Service Discovery node or nil
42
42
  # (chaining-friendly)
43
43
  def set_node(val)
44
44
  self.node = val
@@ -12,6 +12,9 @@ module Jabber
12
12
  # A client-side only argument error
13
13
  class ArgumentError < JabberError; end
14
14
 
15
+ # Server disconnected us
16
+ class ServerDisconnected < JabberError; end
17
+
15
18
  ##
16
19
  # An error returned from the server
17
20
  #
@@ -45,16 +48,16 @@ module Jabber
45
48
 
46
49
  class ComponentAuthenticationFailure < JabberError; end
47
50
 
48
- # TODO : Give this a better name
49
51
  class NoNameXmlnsRegistered < JabberError
50
52
  def initialize(klass)
51
53
  super "Class #{klass} has not set name and xmlns"
52
54
  end
53
55
  end
54
56
 
55
- # TODO : Give this a better name
56
57
  class SOCKS5Error < JabberError; end
57
58
 
59
+ class InvalidChatState < JabberError; end
60
+
58
61
  ##
59
62
  # A class used to build/parse <error/> elements.
60
63
  # Look at XEP-0086 for explanation:
@@ -2,8 +2,10 @@
2
2
  # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
3
  # Website::http://home.gna.org/xmpp4r/
4
4
 
5
+ # TODO: eval <body type='terminate' condition=
5
6
 
6
7
  require 'xmpp4r/client'
8
+ require 'xmpp4r/semaphore'
7
9
  require 'net/http'
8
10
 
9
11
  module Jabber
@@ -50,6 +52,7 @@ module Jabber
50
52
  @last_send = Time.at(0)
51
53
  @send_buffer = ''
52
54
 
55
+ @http_requests = 1
53
56
  @http_wait = 20
54
57
  @http_hold = 1
55
58
  @http_content_type = 'text/xml; charset=utf-8'
@@ -73,7 +76,7 @@ module Jabber
73
76
  @stream_features = {}
74
77
  @http_rid = IdGenerator.generate_id.to_i
75
78
  @pending_rid = @http_rid
76
- @pending_rid_lock = Mutex.new
79
+ @pending_rid_lock = Semaphore.new
77
80
 
78
81
  req_body = REXML::Element.new('body')
79
82
  req_body.attributes['rid'] = @http_rid
@@ -82,7 +85,7 @@ module Jabber
82
85
  req_body.attributes['wait'] = @http_wait.to_s
83
86
  req_body.attributes['to'] = @jid.domain
84
87
  if host
85
- req_body.attributes['route'] = 'xmpp:#{host}:#{port}'
88
+ req_body.attributes['route'] = "xmpp:#{host}:#{port}"
86
89
  end
87
90
  req_body.attributes['secure'] = 'true'
88
91
  req_body.attributes['xmlns'] = 'http://jabber.org/protocol/httpbind'
@@ -107,19 +110,6 @@ module Jabber
107
110
  @features_sem.run
108
111
  end
109
112
 
110
- ##
111
- # Send a stanza, additionally with block
112
- #
113
- # This method ensures a 'jabber:client' namespace for
114
- # the stanza
115
- def send(xml, &block)
116
- if xml.kind_of? REXML::Element
117
- xml.add_namespace('jabber:client')
118
- end
119
-
120
- super
121
- end
122
-
123
113
  ##
124
114
  # Ensure that there is one pending request
125
115
  #
@@ -148,7 +138,7 @@ module Jabber
148
138
  # result:: [REXML::Element]
149
139
  def receive_elements_with_rid(rid, elements)
150
140
  while rid > @pending_rid
151
- @pending_rid_lock.lock
141
+ @pending_rid_lock.wait
152
142
  end
153
143
  @pending_rid = rid + 1
154
144
 
@@ -156,7 +146,7 @@ module Jabber
156
146
  receive(e)
157
147
  }
158
148
 
159
- @pending_rid_lock.unlock
149
+ @pending_rid_lock.run
160
150
  end
161
151
 
162
152
  ##
@@ -167,12 +157,12 @@ module Jabber
167
157
  request.content_length = body.size
168
158
  request.body = body
169
159
  request['Content-Type'] = @http_content_type
170
- Jabber::debuglog("HTTP REQUEST (#{@pending_requests}/#{@http_requests}):\n#{request.body}")
160
+ Jabber::debuglog("HTTP REQUEST (#{@pending_requests + 1}/#{@http_requests}):\n#{request.body}")
171
161
  response = Net::HTTP.start(@uri.host, @uri.port) { |http|
172
162
  http.use_ssl = true if @uri.kind_of? URI::HTTPS
173
163
  http.request(request)
174
164
  }
175
- Jabber::debuglog("HTTP RESPONSE (#{@pending_requests}/#{@http_requests}):\n#{response.body}")
165
+ Jabber::debuglog("HTTP RESPONSE (#{@pending_requests + 1}/#{@http_requests}): #{response.class}\n#{response.body}")
176
166
 
177
167
  unless response.kind_of? Net::HTTPSuccess
178
168
  # Unfortunately, HTTPResponses aren't exceptions
@@ -0,0 +1,2 @@
1
+ require 'xmpp4r/last/iq/last'
2
+ require 'xmpp4r/last/helper/helper'
@@ -0,0 +1,37 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r'
6
+ require 'xmpp4r/last'
7
+
8
+ module Jabber
9
+ module LastActivity
10
+ ##
11
+ # A Helper to manage discovery of Last Activity.
12
+ class Helper
13
+ def initialize(client)
14
+ @stream = client
15
+ end
16
+
17
+ ##
18
+ # Gets the last activity from a JID.
19
+ # jid:: [JID]
20
+ # return:: [Jabber::LastActivity::IqQueryLastActivity]
21
+ def get_last_activity_from(jid)
22
+ iq = Jabber::Iq.new(:get, jid)
23
+ iq.from = @stream.jid
24
+ iq.add(Jabber::LastActivity::IqQueryLastActivity.new)
25
+
26
+ reply = @stream.send_with_id(iq)
27
+
28
+ if reply.query && reply.query.kind_of?(IqQueryLastActivity)
29
+ reply.query
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,67 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r/query'
6
+
7
+ module Jabber
8
+ module LastActivity
9
+ NS_LAST_ACTIVITY = 'jabber:iq:last'
10
+
11
+ ##
12
+ # Class for handling Last Activity queries
13
+ # (XEP-0012)
14
+ class IqQueryLastActivity < IqQuery
15
+ name_xmlns 'query', NS_LAST_ACTIVITY
16
+
17
+ ##
18
+ # Get the number of seconds since last activity.
19
+ #
20
+ # With a bare jid, this will return the number of seconds since the
21
+ # client was last seen (offline user query).
22
+ #
23
+ # With a full jid, this will return the number of seconds that the
24
+ # client has been idle (online user query).
25
+ #
26
+ # With a server, this will return the server or component's uptime in
27
+ # seconds (server / component query).
28
+ def seconds
29
+ attributes['seconds'] ? attributes['seconds'].to_i : nil
30
+ end
31
+
32
+ ##
33
+ # Set the number of seconds since last activity
34
+ def seconds=(val)
35
+ attributes['seconds'] = val.to_s
36
+ end
37
+
38
+ ##
39
+ # Set the number of seconds since last activity
40
+ # (chaining-friendly)
41
+ def set_second(val)
42
+ self.seconds = val
43
+ self
44
+ end
45
+
46
+ ##
47
+ # For an offline user query, get the last status.
48
+ def status
49
+ self.text
50
+ end
51
+
52
+ ##
53
+ # For an offline user query, set the last status.
54
+ def status=(val)
55
+ self.text = val
56
+ end
57
+
58
+ ##
59
+ # For an offline user query, set the last status.
60
+ # (chaining-friendly)
61
+ def set_status(val)
62
+ self.status = val
63
+ self
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,2 @@
1
+ require 'xmpp4r/location/location'
2
+ require 'xmpp4r/location/helper/helper'
@@ -0,0 +1,56 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r'
6
+ require 'xmpp4r/pubsub'
7
+ require 'xmpp4r/location/location'
8
+
9
+ module Jabber
10
+ module UserLocation
11
+ ##
12
+ # A Helper for XEP-0080 User Location
13
+ #
14
+ # Use this helper to send a user's location, or receive them from a
15
+ # specified jid. Described at http://www.xmpp.org/extensions/xep-0080.html
16
+ #
17
+ # For example:
18
+ # <pre>
19
+ # h = UserLocation::Helper( @client, 'radio1@hug.hellomatty.com' )
20
+ # h.add_userlocation_callback do |location|
21
+ # puts "Now in: #{location.locality}"
22
+ # end
23
+ # </pre>
24
+ class Helper < PubSub::ServiceHelper
25
+ ##
26
+ # Send out the current location.
27
+ #
28
+ # location:: [Jabber::UserLocation::Location] current_location
29
+ def current_location(location)
30
+ item = Jabber::PubSub::Item.new()
31
+ item.add(location)
32
+
33
+ publish_item_to(NS_USERLOCATION, item)
34
+ end
35
+
36
+ ##
37
+ # Use this method to indicate that you wish to stop publishing
38
+ # a location.
39
+ def stop_publishing
40
+ current_location(Jabber::UserLocation::Location.new())
41
+ end
42
+
43
+ ##
44
+ # Add a callback that will be invoked when a location is received
45
+ # from the jid specified when you constructed the Helper.
46
+ def add_userlocation_callback(prio = 200, ref = nil, &block)
47
+ add_event_callback(prio, ref) do |event|
48
+ location = event.first_element('items/item/location')
49
+ if location
50
+ block.call(location)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,179 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'time'
6
+ require 'xmpp4r/xmppelement'
7
+ require 'rexml/element'
8
+
9
+ module Jabber
10
+ module UserLocation
11
+ NS_USERLOCATION = 'http://jabber.org/protocol/geoloc'
12
+ ALLOWABLE_ATTRIBUTES = %w(accuracy alt area bearing building country
13
+ datum description floor lat locality lon postalcode region room
14
+ speed street text timestamp uri)
15
+
16
+ ##
17
+ # The <geoloc> XMPP element, as defined in XEP-0080 User Location
18
+ #
19
+ # See http://xmpp.org/extensions/xep-0080.html - this element
20
+ # encapsulates data about a user's current location. These are
21
+ # expressed as child elements such as <locality>, <lat>, etc.
22
+ # which are also managed by this class.
23
+ #
24
+ # If the element has no children then it indicates that the user
25
+ # has stopped publishing their location.
26
+ class Location < XMPPElement
27
+ name_xmlns 'geoloc', NS_USERLOCATION
28
+ force_xmlns true
29
+
30
+ ##
31
+ # Construct a new <location> element.
32
+ #
33
+ # Supply no arguments to make an empty element to indicate that
34
+ # location is no longer being published.
35
+ #
36
+ # attributes:: [Hash] location attributes
37
+ def initialize(attributes = {})
38
+ super()
39
+
40
+ # validate attributes
41
+ attributes = attributes.select do |k,v|
42
+ ALLOWABLE_ATTRIBUTES.include?(k) && !v.nil?
43
+ end
44
+
45
+ attributes.each do |k,v|
46
+ v = x.xmlschema if v.is_a?(Time)
47
+ add_element(REXML::Element.new(k)).text = v.to_s
48
+ end
49
+ end
50
+
51
+
52
+ ##
53
+ # Returns true if a location is currently being published, otherwise false.
54
+ def published?
55
+ (elements.size > 0)
56
+ end
57
+
58
+ ##
59
+ # Get the accuracy attribute of this location.
60
+ def accuracy
61
+ first_element('accuracy').text if first_element('accuracy')
62
+ end
63
+
64
+ ##
65
+ # Get the alt attribute of this location.
66
+ def alt
67
+ first_element('alt').text if first_element('alt')
68
+ end
69
+
70
+ ##
71
+ # Get the area attribute of this location.
72
+ def area
73
+ first_element('area').text if first_element('area')
74
+ end
75
+
76
+ ##
77
+ # Get the bearing attribute of this location.
78
+ def bearing
79
+ first_element('bearing').text if first_element('bearing')
80
+ end
81
+
82
+ ##
83
+ # Get the building attribute of this location.
84
+ def building
85
+ first_element('building').text if first_element('building')
86
+ end
87
+
88
+ ##
89
+ # Get the country attribute of this location.
90
+ def country
91
+ first_element('country').text if first_element('country')
92
+ end
93
+
94
+ ##
95
+ # Get the datum attribute of this location.
96
+ def datum
97
+ first_element('datum').text if first_element('datum')
98
+ end
99
+
100
+ ##
101
+ # Get the description attribute of this location.
102
+ def description
103
+ first_element('description').text if first_element('description')
104
+ end
105
+
106
+ ##
107
+ # Get the floor attribute of this location.
108
+ def floor
109
+ first_element('floor').text if first_element('floor')
110
+ end
111
+
112
+ ##
113
+ # Get the lat attribute of this location.
114
+ def lat
115
+ first_element('lat').text if first_element('lat')
116
+ end
117
+
118
+ ##
119
+ # Get the locality attribute of this location.
120
+ def locality
121
+ first_element('locality').text if first_element('locality')
122
+ end
123
+
124
+ ##
125
+ # Get the lon attribute of this location.
126
+ def lon
127
+ first_element('lon').text if first_element('lon')
128
+ end
129
+
130
+ ##
131
+ # Get the postalcode attribute of this location.
132
+ def postalcode
133
+ first_element('postalcode').text if first_element('postalcode')
134
+ end
135
+
136
+ ##
137
+ # Get the region attribute of this location.
138
+ def region
139
+ first_element('region').text if first_element('region')
140
+ end
141
+
142
+ ##
143
+ # Get the room attribute of this location.
144
+ def room
145
+ first_element('room').text if first_element('room')
146
+ end
147
+
148
+ ##
149
+ # Get the speed attribute of this location.
150
+ def speed
151
+ first_element('speed').text if first_element('speed')
152
+ end
153
+
154
+ ##
155
+ # Get the street attribute of this location.
156
+ def street
157
+ first_element('street').text if first_element('street')
158
+ end
159
+
160
+ ##
161
+ # Get the text attribute of this location.
162
+ def text
163
+ first_element('text').text if first_element('text')
164
+ end
165
+
166
+ ##
167
+ # Get the timestamp attribute of this location.
168
+ def timestamp
169
+ first_element('timestamp').text if first_element('timestamp')
170
+ end
171
+
172
+ ##
173
+ # Get the uri attribute of this location.
174
+ def uri
175
+ first_element('uri').text if first_element('uri')
176
+ end
177
+ end
178
+ end
179
+ end