jubjub 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.infinity_test ADDED
@@ -0,0 +1,35 @@
1
+ infinity_test do
2
+
3
+ notifications :growl do
4
+ show_images :mode => :mario_bros
5
+ end
6
+
7
+ use :rubies => %w(1.9.2 ree), :test_framework => :rspec
8
+
9
+ # use :specific_options => {'jruby' => 'J-cp bar/whisky-in-the.jar:.'}
10
+
11
+ before(:each_ruby) do |environment|
12
+ # ...
13
+ end
14
+
15
+ after(:each_ruby) do |environment|
16
+ # ...
17
+ end
18
+
19
+ before_run do
20
+ clear :terminal
21
+ end
22
+
23
+ after_run do
24
+ # ...
25
+ end
26
+
27
+ #heuristics('my_pattern') do |file|
28
+ # ...
29
+ #end
30
+
31
+ replace_patterns do |application|
32
+ # ...
33
+ end
34
+
35
+ end
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --tty
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2010 by Theo Cushion
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,78 @@
1
+ Description
2
+ ===========
3
+
4
+ Jubjub is an object to XMPP mapping library designed to be used inside of a web app to simplify the administration of XMPP resources. It aims to provide a very simple interface which hides the complexity of the XMPP protocol allowing your code to communicate intent rather than implementation details. This also makes the code significantly easier to mock and test.
5
+
6
+ Currently it uses [xmpp_gateway](https://github.com/theozaurus/xmpp_gateway) to communicate to the XMPP server. This allows Jubjub to be used within non evented web servers like [Phusion Passenger](http://www.modrails.com).
7
+
8
+ It is currently alpha software and merely a proof of concept.
9
+
10
+ Requirements
11
+ ============
12
+
13
+ It is currently fully tested against:
14
+
15
+ - Ruby 1.8.7
16
+ - Ruby 1.9.2
17
+
18
+ Examples
19
+ ========
20
+
21
+ require 'jubjub'
22
+ class User
23
+
24
+ attr_accessor :xmpp_jid, :xmpp_password
25
+
26
+ # :jid and :password are used to point to methods that contain
27
+ # the credentials required to login as a user
28
+ include Jubjub::User
29
+ jubjub_client :jid => :xmpp_jid, :password => :xmpp_password
30
+
31
+ end
32
+
33
+ u = User.new
34
+ u.xmpp_jid = "theozaurus@jabber.org"
35
+ u.xmpp_password = "ruby"
36
+
37
+ # Sensibly defaults to conference.YOUR_CONNECTION_JID
38
+ u.mucs
39
+ => [
40
+ #<Jubjub::Muc:0x1027222b8 @jid="all@conference.jabber.org" @name="all">,
41
+ #<Jubjub::Muc:0x102722038 @jid="all-linux-ru@conference.jabber.org" @name="all-linux-ru">,
42
+ #<Jubjub::Muc:0x102721b60 @jid="allgorithmus@conference.jabber.org" @name="Allgorithmus Project">,
43
+ ...
44
+
45
+ # Fancy a list from a different service?
46
+ u.mucs('conference.jabber.ru')
47
+ => [
48
+ #<Jubjub::Muc:0x10166e930 @jid="tota-room@conference.jabber.ru" @name="tota-room (5)">,
49
+ #<Jubjub::Muc:0x10166e6b0 @jid="friends_room@conference.jabber.ru" @name="Friends (6)">,
50
+ #<Jubjub::Muc:0x10166dcb0 @jid="think_linux@conference.jabber.ru" @name="think_linux (n/a)">
51
+ ...
52
+
53
+ # Create a room
54
+ room = u.mucs.create('jubjub')
55
+ => #<Jubjub::Muc:0x101532f58 @jid="jubjub@conference.jabber.org" @name=nil>
56
+
57
+ # Create a room somewhere else
58
+ room = u.mucs('chat.test.com').create('jubjub')
59
+ => #<Jubjub::Muc:0x10161c3b0 @jid="jubjub@chat.test.com" @name=nil>
60
+
61
+ # Create a room with a custom configuration
62
+ room = u.mucs.create('customjub'){|c|
63
+ c['muc#roomconfig_allowinvites'] = false
64
+ }
65
+
66
+ # Destroy a room
67
+ room.destroy
68
+ => true
69
+
70
+ TODO
71
+ ====
72
+
73
+ - Error handling
74
+ - MUC user role and affiliation control
75
+ - Service discovery
76
+ - Pubsub / PEP
77
+ - Operations that are not IQ based, such as rosters and two way messaging
78
+ - Other backends (for servers that are evented)
@@ -0,0 +1,17 @@
1
+ module Jubjub
2
+ module Connection
3
+ class XmppGateway
4
+ module Helper
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+
10
+ def write(stanza)
11
+ @connection.write(stanza)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,353 @@
1
+ require 'jubjub/connection/xmpp_gateway/helper'
2
+ require 'jubjub/muc'
3
+
4
+ module Jubjub
5
+ module Connection
6
+ class XmppGateway
7
+ class Muc
8
+
9
+ include Helper
10
+
11
+ def initialize(connection)
12
+ @connection = connection
13
+ end
14
+
15
+ # http://xmpp.org/extensions/xep-0045.html#createroom-instant
16
+ # <presence
17
+ # from='crone1@shakespeare.lit/desktop'
18
+ # to='darkcave@chat.shakespeare.lit/firstwitch'>
19
+ # <x xmlns='http://jabber.org/protocol/muc'/>
20
+ # </presence>
21
+ # <iq from='crone1@shakespeare.lit/desktop'
22
+ # id='create1'
23
+ # to='darkcave@chat.shakespeare.lit'
24
+ # type='set'>
25
+ # <query xmlns='http://jabber.org/protocol/muc#owner'>
26
+ # <x xmlns='jabber:x:data' type='submit'/>
27
+ # </query>
28
+ # </iq>
29
+ #
30
+ # Expected
31
+ # <iq from='darkcave@chat.shakespeare.lit'
32
+ # id='create2'
33
+ # to='crone1@shakespeare.lit/desktop'
34
+ # type='result'/>
35
+ #
36
+ def create(full_jid, configuration = nil)
37
+ room_jid = Jubjub::Jid.new full_jid.node, full_jid.domain
38
+
39
+ request = Nokogiri::XML::Builder.new do |xml|
40
+ xml.iq(:type => 'set', :to => room_jid) {
41
+ xml.query('xmlns' => 'http://jabber.org/protocol/muc#owner'){
42
+ xml.x('xmlns' => 'jabber:x:data', :type => 'submit') {
43
+ configuration.settings.each{|name,values|
44
+ xml.field('var' => name){
45
+ values.each {|v|
46
+ xml.value v
47
+ }
48
+ }
49
+ } if configuration
50
+ }
51
+ }
52
+ }
53
+ end
54
+
55
+ presence full_jid
56
+
57
+ success = write(
58
+ # Open room
59
+ request.to_xml
60
+ ).xpath(
61
+ # Check for valid response
62
+ '//iq[@type="result"]'
63
+ ).any?
64
+ Jubjub::Muc.new room_jid, nil, @connection if success
65
+ end
66
+
67
+ # http://xmpp.org/extensions/xep-0045.html#createroom-reserved
68
+ # <presence
69
+ # from='crone1@shakespeare.lit/desktop'
70
+ # to='darkcave@chat.shakespeare.lit/firstwitch'>
71
+ # <x xmlns='http://jabber.org/protocol/muc'/>
72
+ # </presence>
73
+ # <iq from='crone1@shakespeare.lit/desktop'
74
+ # id='create1'
75
+ # to='darkcave@chat.shakespeare.lit'
76
+ # type='get'>
77
+ # <query xmlns='http://jabber.org/protocol/muc#owner'/>
78
+ # </iq>
79
+ #
80
+ # Expected
81
+ # <iq from='darkcave@chat.shakespeare.lit'
82
+ # id='create1'
83
+ # to='crone1@shakespeare.lit/desktop'
84
+ # type='result'>
85
+ # <query xmlns='http://jabber.org/protocol/muc#owner'>
86
+ # <x xmlns='jabber:x:data' type='form'>
87
+ # <title>Configuration for "darkcave" Room</title>
88
+ # <instructions>
89
+ # Your room darkcave@macbeth has been created!
90
+ # The default configuration is as follows:
91
+ # - No logging
92
+ # - No moderation
93
+ # - Up to 20 occupants
94
+ # - No password required
95
+ # - No invitation required
96
+ # - Room is not persistent
97
+ # - Only admins may change the subject
98
+ # - Presence broadcasted for all users
99
+ # To accept the default configuration, click OK. To
100
+ # select a different configuration, please complete
101
+ # this form.
102
+ # </instructions>
103
+ # <field
104
+ # type='hidden'
105
+ # var='FORM_TYPE'>
106
+ # <value>http://jabber.org/protocol/muc#roomconfig</value>
107
+ # </field>
108
+ # <field
109
+ # label='Natural-Language Room Name'
110
+ # type='text-single'
111
+ # var='muc#roomconfig_roomname'/>
112
+ # <field
113
+ # label='Natural Language for Room Discussions'
114
+ # type='text-single'
115
+ # var='muc#roomconfig_lang'/>
116
+ # <field
117
+ # label='Enable Public Logging?'
118
+ # type='boolean'
119
+ # var='muc#roomconfig_enablelogging'>
120
+ # <value>0</value>
121
+ # </field>
122
+ # <field
123
+ # label='Maximum Number of Occupants'
124
+ # type='list-single'
125
+ # var='muc#roomconfig_maxusers'>
126
+ # <value>20</value>
127
+ # <option label='10'><value>10</value></option>
128
+ # <option label='20'><value>20</value></option>
129
+ # <option label='30'><value>30</value></option>
130
+ # <option label='50'><value>50</value></option>
131
+ # <option label='100'><value>100</value></option>
132
+ # <option label='None'><value>none</value></option>
133
+ # </field>
134
+ # <field
135
+ # label='Roles for which Presence is Broadcast'
136
+ # type='list-multi'
137
+ # var='muc#roomconfig_presencebroadcast'>
138
+ # <value>moderator</value>
139
+ # <value>participant</value>
140
+ # <value>visitor</value>
141
+ # <option label='Moderator'><value>moderator</value></option>
142
+ # <option label='Participant'><value>participant</value></option>
143
+ # <option label='Visitor'><value>visitor</value></option>
144
+ # </field>
145
+ # <field
146
+ # label='Roles and Affiliations that May Retrieve Member List'
147
+ # type='list-multi'
148
+ # var='muc#roomconfig_getmemberlist'>
149
+ # <value>moderator</value>
150
+ # <value>participant</value>
151
+ # <value>visitor</value>
152
+ # <option label='Moderator'><value>moderator</value></option>
153
+ # <option label='Participant'><value>participant</value></option>
154
+ # <option label='Visitor'><value>visitor</value></option>
155
+ # </field>
156
+ # <field
157
+ # label='Make Room Publicly Searchable?'
158
+ # type='boolean'
159
+ # var='muc#roomconfig_publicroom'>
160
+ # <value>1</value>
161
+ # </field>
162
+ # <field
163
+ # label='Make Room Persistent?'
164
+ # type='boolean'
165
+ # var='muc#roomconfig_persistentroom'>
166
+ # <value>0</value>
167
+ # </field>
168
+ # <field type='fixed'>
169
+ # <value>
170
+ # If a password is required to enter this room,
171
+ # you must specify the password below.
172
+ # </value>
173
+ # </field>
174
+ # <field
175
+ # label='Password'
176
+ # type='text-private'
177
+ # var='muc#roomconfig_roomsecret'/>
178
+ # <field
179
+ # label='Who May Discover Real JIDs?'
180
+ # type='list-single'
181
+ # var='muc#roomconfig_whois'>
182
+ # <option label='Moderators Only'>
183
+ # <value>moderators</value>
184
+ # </option>
185
+ # <option label='Anyone'>
186
+ # <value>anyone</value>
187
+ # </option>
188
+ # </field>
189
+ # <field type='fixed'>
190
+ # <value>
191
+ # You may specify additional people who have
192
+ # administrative privileges in the room. Please
193
+ # provide one Jabber ID per line.
194
+ # </value>
195
+ # </field>
196
+ # <field
197
+ # label='Room Admins'
198
+ # type='jid-multi'
199
+ # var='muc#roomconfig_roomadmins'/>
200
+ # <field type='fixed'>
201
+ # <value>
202
+ # You may specify additional owners for this
203
+ # room. Please provide one Jabber ID per line.
204
+ # </value>
205
+ # </field>
206
+ # <field
207
+ # label='Room Owners'
208
+ # type='jid-multi'
209
+ # var='muc#roomconfig_roomowners'/>
210
+ # </x>
211
+ # </query>
212
+ # </iq>
213
+ def configuration(full_jid)
214
+ room_jid = Jubjub::Jid.new full_jid.node, full_jid.domain
215
+
216
+ request = Nokogiri::XML::Builder.new do |xml|
217
+ xml.iq(:to => room_jid, :type => 'get') {
218
+ xml.query('xmlns' => 'http://jabber.org/protocol/muc#owner')
219
+ }
220
+ end
221
+
222
+ presence full_jid
223
+ response = write(
224
+ # Request room configuration
225
+ request.to_xml
226
+ ).xpath(
227
+ # Get fields
228
+ "//iq[@type='result']/muc_owner:query/x_data:x[@type='form']/x_data:field",
229
+ namespaces
230
+ ).inject({}){|result,field|
231
+ # Build MucConfiguration parameters
232
+ hash = {}
233
+ hash[:type] = field.attr 'type'
234
+ hash[:label] = field.attr 'label'
235
+
236
+ value = field.xpath('x_data:value', namespaces)
237
+ hash[:value] = hash[:type].match(/\-multi$/) ? value.map{|e| e.content } : value.text
238
+
239
+ options = field.xpath('x_data:option', namespaces).map{|o|
240
+ { :label => o.attr('label'), :value => o.xpath('x_data:value', namespaces).text }
241
+ }
242
+ hash[:options] = options if options.any?
243
+
244
+ result[field.attr 'var'] = hash
245
+ result
246
+ }
247
+
248
+ Jubjub::MucConfiguration.new response
249
+ end
250
+
251
+ # http://xmpp.org/extensions/xep-0045.html#destroyroom
252
+ # <iq from='crone1@shakespeare.lit/desktop'
253
+ # id='begone'
254
+ # to='heath@chat.shakespeare.lit'
255
+ # type='set'>
256
+ # <query xmlns='http://jabber.org/protocol/muc#owner'>
257
+ # <destroy jid='darkcave@chat.shakespeare.lit'>
258
+ # <reason>Macbeth doth come.</reason>
259
+ # </destroy>
260
+ # </query>
261
+ # </iq>
262
+ #
263
+ # Expected
264
+ # <iq from='heath@chat.shakespeare.lit'
265
+ # id='begone'
266
+ # to='crone1@shakespeare.lit/desktop'
267
+ # type='result'/>
268
+ def destroy(jid)
269
+ request = Nokogiri::XML::Builder.new do |xml|
270
+ xml.iq(:to => jid, :type => 'set') {
271
+ xml.query('xmlns' => 'http://jabber.org/protocol/muc#owner'){
272
+ xml.destroy
273
+ }
274
+ }
275
+ end
276
+
277
+ write(
278
+ # Generate stanza
279
+ request.to_xml
280
+ ).xpath(
281
+ # Check for valid response
282
+ '//iq[@type="result"]'
283
+ ).any?
284
+ end
285
+
286
+ # http://xmpp.org/extensions/xep-0045.html#disco-rooms
287
+ # <iq from='hag66@shakespeare.lit/pda'
288
+ # id='disco2'
289
+ # to='chat.shakespeare.lit'
290
+ # type='get'>
291
+ # <query xmlns='http://jabber.org/protocol/disco#items'/>
292
+ # </iq>
293
+ #
294
+ # Expected
295
+ # <iq from='chat.shakespeare.lit'
296
+ # id='disco2'
297
+ # to='hag66@shakespeare.lit/pda'
298
+ # type='result'>
299
+ # <query xmlns='http://jabber.org/protocol/disco#items'>
300
+ # <item jid='heath@chat.shakespeare.lit'
301
+ # name='A Lonely Heath'/>
302
+ # <item jid='darkcave@chat.shakespeare.lit'
303
+ # name='A Dark Cave'/>
304
+ # <item jid='forres@chat.shakespeare.lit'
305
+ # name='The Palace'/>
306
+ # <item jid='inverness@chat.shakespeare.lit'
307
+ # name='Macbeth&apos;s Castle'/>
308
+ # </query>
309
+ # </iq>
310
+ def list(jid)
311
+ request = Nokogiri::XML::Builder.new do |xml|
312
+ xml.iq(:to => jid, :type => 'get') {
313
+ xml.query('xmlns' => 'http://jabber.org/protocol/disco#items')
314
+ }
315
+ end
316
+
317
+ write(
318
+ # Generate stanza
319
+ request.to_xml
320
+ ).xpath(
321
+ # Pull out required parts
322
+ '//iq[@type="result"]/disco_items:query/disco_items:item',
323
+ namespaces
324
+ ).map{|item|
325
+ # Convert to Jubjub object
326
+ Jubjub::Muc.new item.attr('jid'), item.attr('name'), @connection
327
+ }
328
+ end
329
+
330
+ private
331
+
332
+ def presence(full_jid)
333
+ request = Nokogiri::XML::Builder.new do |xml|
334
+ xml.presence(:to => full_jid) {
335
+ xml.x('xmlns' => 'http://jabber.org/protocol/muc')
336
+ }
337
+ end
338
+
339
+ write request.to_xml
340
+ end
341
+
342
+ def namespaces
343
+ {
344
+ 'disco_items' => 'http://jabber.org/protocol/disco#items',
345
+ 'muc_owner' => "http://jabber.org/protocol/muc#owner",
346
+ 'x_data' => 'jabber:x:data'
347
+ }
348
+ end
349
+
350
+ end
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,47 @@
1
+ require "net/http"
2
+ require "nokogiri"
3
+ require "jubjub/connection/xmpp_gateway/muc"
4
+
5
+ module Jubjub
6
+ module Connection
7
+ class XmppGateway
8
+
9
+ attr_reader :jid
10
+
11
+ def initialize(jid,password,settings)
12
+ @jid = jid
13
+ @password = password
14
+ @settings = settings
15
+ end
16
+
17
+ def muc
18
+ @muc ||= Muc.new(self)
19
+ end
20
+
21
+ def write(stanza)
22
+ req = Net::HTTP::Post.new( url.path )
23
+ req.basic_auth( @jid.to_s, @password )
24
+ req.set_form_data( { 'stanza'=> stanza }, ';' )
25
+ res = Net::HTTP.new(url.host, url.port).start {|http| http.request req }
26
+ case res
27
+ when Net::HTTPSuccess
28
+ # OK
29
+ else
30
+ #res.error!
31
+ end
32
+ decode res.body
33
+ end
34
+
35
+ private
36
+
37
+ def url
38
+ URI.parse "http://#{@settings[:host]}:#{@settings[:port]}/"
39
+ end
40
+
41
+ def decode(http_body)
42
+ Nokogiri::XML::Document.parse http_body
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ module Jubjub
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ArgumentError < Error
6
+ end
7
+ end
data/lib/jubjub/jid.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Jubjub
2
+ class Jid
3
+
4
+ PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/
5
+
6
+ attr_reader :node, :domain, :resource
7
+
8
+ def initialize(node, domain = nil, resource = nil)
9
+ if node.is_a? Jid
10
+ @node = node.node
11
+ @domain = node.domain
12
+ @resource = node.resource
13
+ else
14
+ @node = node
15
+ @domain = domain
16
+ @resource = resource
17
+ end
18
+
19
+ if @domain.nil? && @resource.nil?
20
+ @node, @domain, @resource = node.to_s.scan(PATTERN).first
21
+ end
22
+ end
23
+
24
+ def to_s
25
+ (node ? "#{node}@" : '') + domain + (resource ? "/#{resource}" : '')
26
+ end
27
+
28
+ def ==(obj)
29
+ obj.is_a?( self.class ) && obj.to_s == self.to_s
30
+ end
31
+
32
+ end
33
+ end