tp-blather 0.8.2

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.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ def vcard_xml
4
+ <<-XML
5
+ <iq type="result" id="blather0007" to="romeo@example.net">
6
+ <vCard xmlns="vcard-temp">
7
+ <NICKNAME>Romeo</NICKNAME>
8
+ </vCard>
9
+ </iq>
10
+ XML
11
+ end
12
+
13
+ describe Blather::Stanza::Iq::Vcard do
14
+ it 'registers itself' do
15
+ Blather::XMPPNode.class_from_registration(:vCard, 'vcard-temp').should == Blather::Stanza::Iq::Vcard
16
+ end
17
+
18
+ it 'can be imported' do
19
+ query = Blather::XMPPNode.parse vcard_xml
20
+ query.should be_instance_of Blather::Stanza::Iq::Vcard
21
+ query.vcard.should be_instance_of Blather::Stanza::Iq::Vcard::Vcard
22
+ end
23
+
24
+ it 'ensures a vcard node is present on create' do
25
+ query = Blather::Stanza::Iq::Vcard.new
26
+ query.xpath('ns:vCard', :ns => 'vcard-temp').should_not be_empty
27
+ end
28
+
29
+ it 'ensures a vcard node exists when calling #vcard' do
30
+ query = Blather::Stanza::Iq::Vcard.new
31
+ query.vcard.remove
32
+ query.xpath('ns:vCard', :ns => 'vcard-temp').should be_empty
33
+
34
+ query.vcard.should_not be_nil
35
+ query.xpath('ns:vCard', :ns => 'vcard-temp').should_not be_empty
36
+ end
37
+
38
+ it 'ensures a vcard node is replaced when calling #vcard=' do
39
+ query = Blather::XMPPNode.parse vcard_xml
40
+
41
+ new_vcard = Blather::Stanza::Iq::Vcard::Vcard.new
42
+ new_vcard["NICKNAME"] = 'Mercutio'
43
+
44
+ query.vcard = new_vcard
45
+
46
+ query.xpath('ns:vCard', :ns => 'vcard-temp').size.should == 1
47
+ query.find_first('ns:vCard/ns:NICKNAME', :ns => 'vcard-temp').content.should == 'Mercutio'
48
+ end
49
+ end
50
+
51
+ describe Blather::Stanza::Iq::Vcard::Vcard do
52
+ it 'can set vcard elements' do
53
+ query = Blather::Stanza::Iq::Vcard.new :set
54
+ query.vcard['NICKNAME'] = 'Romeo'
55
+ query.find_first('ns:vCard/ns:NICKNAME', :ns => 'vcard-temp').content.should == 'Romeo'
56
+ end
57
+
58
+ it 'can set deep vcard elements' do
59
+ query = Blather::Stanza::Iq::Vcard.new :set
60
+ query.vcard['PHOTO/TYPE'] = 'image/png'
61
+ query.vcard['PHOTO/BINVAL'] = '===='
62
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.size.should == 2
63
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.detect { |n| n.element_name == 'TYPE' && n.content == 'image/png' }.should_not be_nil
64
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.detect { |n| n.element_name == 'BINVAL' && n.content == '====' }.should_not be_nil
65
+ end
66
+
67
+ it 'can get vcard elements' do
68
+ query = Blather::Stanza::Iq::Vcard.new :set
69
+ query.vcard['NICKNAME'] = 'Romeo'
70
+ query.vcard['NICKNAME'].should == 'Romeo'
71
+ end
72
+
73
+ it 'can get deep vcard elements' do
74
+ query = Blather::Stanza::Iq::Vcard.new :set
75
+ query.vcard['PHOTO/TYPE'] = 'image/png'
76
+ query.vcard['PHOTO/BINVAL'] = '===='
77
+ query.vcard['PHOTO/TYPE'].should == 'image/png'
78
+ query.vcard['PHOTO/BINVAL'].should == '===='
79
+ end
80
+
81
+ it 'returns nil on vcard elements which does not exist' do
82
+ query = Blather::Stanza::Iq::Vcard.new :set
83
+ query.vcard['NICKNAME'] = 'Romeo'
84
+ query.vcard['FN'].should be_nil
85
+ end
86
+
87
+ it 'can update vcard elements' do
88
+ query = Blather::XMPPNode.parse vcard_xml
89
+ query.vcard['NICKNAME'].should == 'Romeo'
90
+ query.vcard['NICKNAME'] = 'Mercutio'
91
+ query.vcard['NICKNAME'].should == 'Mercutio'
92
+ end
93
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blather::Stanza::Iq do
4
+ it 'registers itself' do
5
+ Blather::XMPPNode.class_from_registration(:iq, nil).should == Blather::Stanza::Iq
6
+ end
7
+
8
+ it 'must be importable' do
9
+ string = "<iq from='juliet@example.com/balcony' type='set' id='roster_4'></iq>"
10
+ Blather::XMPPNode.parse(string).should be_instance_of Blather::Stanza::Iq
11
+ end
12
+
13
+ it 'creates a new Iq stanza defaulted as a get' do
14
+ Blather::Stanza::Iq.new.type.should == :get
15
+ end
16
+
17
+ it 'sets the id when created' do
18
+ Blather::Stanza::Iq.new.id.should_not be_nil
19
+ end
20
+
21
+ it 'creates a new Stanza::Iq object on import' do
22
+ Blather::Stanza::Iq.import(Blather::XMPPNode.new('iq')).should be_kind_of Blather::Stanza::Iq
23
+ end
24
+
25
+ it 'creates a proper object based on its children' do
26
+ n = Blather::XMPPNode.new('iq')
27
+ n << Blather::XMPPNode.new('query', n.document)
28
+ Blather::Stanza::Iq.import(n).should be_kind_of Blather::Stanza::Iq::Query
29
+ end
30
+
31
+ it 'ensures type is one of Stanza::Iq::VALID_TYPES' do
32
+ lambda { Blather::Stanza::Iq.new :invalid_type_name }.should raise_error(Blather::ArgumentError)
33
+
34
+ Blather::Stanza::Iq::VALID_TYPES.each do |valid_type|
35
+ n = Blather::Stanza::Iq.new valid_type
36
+ n.type.should == valid_type
37
+ end
38
+ end
39
+
40
+ Blather::Stanza::Iq::VALID_TYPES.each do |valid_type|
41
+ it "provides a helper (#{valid_type}?) for type #{valid_type}" do
42
+ Blather::Stanza::Iq.new.should respond_to :"#{valid_type}?"
43
+ end
44
+ end
45
+
46
+ it 'removes the body when replying' do
47
+ iq = Blather::Stanza::Iq.new :get, 'me@example.com'
48
+ iq.from = 'them@example.com'
49
+ iq << Blather::XMPPNode.new('query', iq.document)
50
+ r = iq.reply
51
+ r.children.empty?.should == true
52
+ end
53
+
54
+ it 'does not remove the body when replying if we ask to keep it' do
55
+ iq = Blather::Stanza::Iq.new :get, 'me@example.com'
56
+ iq.from = 'them@example.com'
57
+ iq << Blather::XMPPNode.new('query', iq.document)
58
+ r = iq.reply :remove_children => false
59
+ r.children.empty?.should == false
60
+ end
61
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ def muc_invite_xml
4
+ <<-XML
5
+ <message
6
+ from='coven@chat.shakespeare.lit'
7
+ id='nzd143v8'
8
+ to='hecate@shakespeare.lit'>
9
+ <x xmlns='http://jabber.org/protocol/muc#user'>
10
+ <invite to='hecate@shakespeare.lit' from='crone1@shakespeare.lit/desktop'>
11
+ <reason>
12
+ Hey Hecate, this is the place for all good witches!
13
+ </reason>
14
+ </invite>
15
+ <password>foobar</password>
16
+ </x>
17
+ </message>
18
+ XML
19
+ end
20
+
21
+ def muc_decline_xml
22
+ <<-XML
23
+ <message
24
+ from='hecate@shakespeare.lit/broom'
25
+ id='jk2vs61v'
26
+ to='coven@chat.shakespeare.lit'>
27
+ <x xmlns='http://jabber.org/protocol/muc#user'>
28
+ <decline to='crone1@shakespeare.lit' from='hecate@shakespeare.lit'>
29
+ <reason>
30
+ Sorry, I'm too busy right now.
31
+ </reason>
32
+ </decline>
33
+ </x>
34
+ </message>
35
+ XML
36
+ end
37
+
38
+ describe 'Blather::Stanza::Message::MUCUser' do
39
+ it 'ensures a form node is present on create' do
40
+ c = Blather::Stanza::Message::MUCUser.new
41
+ c.xpath('ns:x', :ns => Blather::Stanza::Message::MUCUser.registered_ns).should_not be_empty
42
+ end
43
+
44
+ it 'ensures a form node exists when calling #muc' do
45
+ c = Blather::Stanza::Message::MUCUser.new
46
+ c.remove_children :x
47
+ c.xpath('ns:x', :ns => Blather::Stanza::Message::MUCUser.registered_ns).should be_empty
48
+
49
+ c.muc_user.should_not be_nil
50
+ c.xpath('ns:x', :ns => Blather::Stanza::Message::MUCUser.registered_ns).should_not be_empty
51
+ end
52
+
53
+ it 'ensures the message type is :normal' do
54
+ m = Blather::Stanza::Message::MUCUser.new
55
+ m.normal?.should == true
56
+ end
57
+
58
+ it "must be able to set the password" do
59
+ muc_user = Blather::Stanza::Message::MUCUser.new
60
+ muc_user.password.should == nil
61
+ muc_user.password = 'barbaz'
62
+ muc_user.password.should == 'barbaz'
63
+ muc_user.password = 'hello_world'
64
+ muc_user.password.should == 'hello_world'
65
+ end
66
+
67
+ it "should not be an #invite?" do
68
+ muc_user = Blather::Stanza::Message::MUCUser.new
69
+ muc_user.invite?.should == false
70
+ end
71
+
72
+ describe "with an invite element" do
73
+ it "should be an #invite?" do
74
+ muc_user = Blather::XMPPNode.parse(muc_invite_xml)
75
+ muc_user.invite?.should == true
76
+ end
77
+
78
+ it "should know the invite attributes properly" do
79
+ muc_user = Blather::XMPPNode.parse(muc_invite_xml)
80
+ muc_user.should be_instance_of Blather::Stanza::Message::MUCUser
81
+ invite = muc_user.invite
82
+ invite.to.should == 'hecate@shakespeare.lit'
83
+ invite.from.should == 'crone1@shakespeare.lit/desktop'
84
+ invite.reason.should == 'Hey Hecate, this is the place for all good witches!'
85
+ muc_user.password.should == 'foobar'
86
+ end
87
+
88
+ it "must be able to set the to jid" do
89
+ muc_user = Blather::Stanza::Message::MUCUser.new
90
+ invite = muc_user.invite
91
+ invite.to.should == nil
92
+ invite.to = 'foo@bar.com'
93
+ invite.to.should == 'foo@bar.com'
94
+ end
95
+
96
+ it "must be able to set the from jid" do
97
+ muc_user = Blather::Stanza::Message::MUCUser.new
98
+ invite = muc_user.invite
99
+ invite.from.should == nil
100
+ invite.from = 'foo@bar.com'
101
+ invite.from.should == 'foo@bar.com'
102
+ end
103
+
104
+ it "must be able to set the reason" do
105
+ muc_user = Blather::Stanza::Message::MUCUser.new
106
+ invite = muc_user.invite
107
+ invite.reason.should == ''
108
+ invite.reason = 'Please join'
109
+ invite.reason.should == 'Please join'
110
+ end
111
+ end
112
+
113
+ describe "with a decline element" do
114
+ it "should be an #invite_decline?" do
115
+ muc_user = Blather::XMPPNode.parse(muc_decline_xml)
116
+ muc_user.should be_instance_of Blather::Stanza::Message::MUCUser
117
+ muc_user.invite_decline?.should == true
118
+ end
119
+
120
+ it "should know the decline attributes properly" do
121
+ muc_user = Blather::XMPPNode.parse(muc_decline_xml)
122
+ decline = muc_user.decline
123
+ decline.to.should == 'crone1@shakespeare.lit'
124
+ decline.from.should == 'hecate@shakespeare.lit'
125
+ decline.reason.should == "Sorry, I'm too busy right now."
126
+ end
127
+
128
+ it "must be able to set the to jid" do
129
+ muc_user = Blather::Stanza::Message::MUCUser.new
130
+ decline = muc_user.decline
131
+ decline.to.should == nil
132
+ decline.to = 'foo@bar.com'
133
+ decline.to.should == 'foo@bar.com'
134
+ end
135
+
136
+ it "must be able to set the from jid" do
137
+ muc_user = Blather::Stanza::Message::MUCUser.new
138
+ decline = muc_user.decline
139
+ decline.from.should == nil
140
+ decline.from = 'foo@bar.com'
141
+ decline.from.should == 'foo@bar.com'
142
+ end
143
+
144
+ it "must be able to set the reason" do
145
+ muc_user = Blather::Stanza::Message::MUCUser.new
146
+ decline = muc_user.decline
147
+ decline.reason.should == ''
148
+ decline.reason = 'Please join'
149
+ decline.reason.should == 'Please join'
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,282 @@
1
+ require 'spec_helper'
2
+
3
+ def ichat_message_xml
4
+ <<-XML
5
+ <message from="juliet@example.com/balcony" to="romeo@example.net" type="chat" id="iChat_5FA6C6DC">
6
+ <body>Hello</body>
7
+ <html xmlns="http://www.w3.org/1999/xhtml">
8
+ <body style="background-color:#7bb5ee;color:#000000;">
9
+ <span style="font-family: 'Arial';font-size: 12px;color: #262626;">Hello</span>
10
+ <img alt="f5ad3a04d218d7160fa02415e02d41b3.jpg" src="message-attachments:1" width="30" height="30"/>
11
+ </body>
12
+ </html>
13
+
14
+ <x xmlns="http://www.apple.com/xmpp/message-attachments">
15
+ <attachment id="1">
16
+ <sipub xmlns="http://jabber.org/protocol/sipub" from="juliet@example.com/balcony" id="sipubid_77933F62" mime-type="binary/octet-stream" profile="http://jabber.org/protocol/si/profile/file-transfer">
17
+ <file xmlns="http://jabber.org/protocol/si/profile/file-transfer" xmlns:ichat="apple:profile:transfer-extensions" name="f5ad3a04d218d7160fa02415e02d41b3.jpg" size="1245" posixflags="000001A4"/>
18
+ </sipub>
19
+ </attachment>
20
+ </x>
21
+
22
+ <iq type="set" id="iChat_4CC32F1F" to="romeo@example.net">
23
+ <si xmlns="http://jabber.org/protocol/si" id="sid_60C2D273" mime-type="binary/octet-stream" profile="http://jabber.org/protocol/si/profile/file-transfer">
24
+ <file xmlns="http://jabber.org/protocol/si/profile/file-transfer" xmlns:ichat="apple:profile:transfer-extensions" name="f5ad3a04d218d7160fa02415e02d41b3.jpg" size="1245" posixflags="000001A4"/>
25
+ <feature xmlns="http://jabber.org/protocol/feature-neg">
26
+ <x xmlns="jabber:x:data" type="form">
27
+ <field type="list-single" var="stream-method">
28
+ <option><value>http://jabber.org/protocol/bytestreams</value></option>
29
+ </field>
30
+ </x>
31
+ </feature>
32
+ </si>
33
+ </iq>
34
+ </message>
35
+ XML
36
+ end
37
+
38
+ def message_xml
39
+ <<-XML
40
+ <message
41
+ to='romeo@example.net'
42
+ from='juliet@example.com/balcony'
43
+ type='chat'
44
+ xml:lang='en'>
45
+ <body>Wherefore art thou, Romeo?</body>
46
+ <x xmlns='jabber:x:data' type='form'>
47
+ <field var='field-name' type='text-single' label='description' />
48
+ </x>
49
+ <paused xmlns="http://jabber.org/protocol/chatstates"/>
50
+ </message>
51
+ XML
52
+ end
53
+
54
+ def delayed_message_xml
55
+ <<-XML
56
+ <message
57
+ from='coven@chat.shakespeare.lit/firstwitch'
58
+ id='162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2'
59
+ to='hecate@shakespeare.lit/broom'
60
+ type='groupchat'>
61
+ <body>Thrice the brinded cat hath mew'd.</body>
62
+ <delay xmlns='urn:xmpp:delay'
63
+ from='coven@chat.shakespeare.lit'
64
+ stamp='2002-10-13T23:58:37Z'>
65
+ Too slow
66
+ </delay>
67
+ </message>
68
+ XML
69
+ end
70
+
71
+ describe Blather::Stanza::Message do
72
+ it 'registers itself' do
73
+ Blather::XMPPNode.class_from_registration(:message, nil).should == Blather::Stanza::Message
74
+ end
75
+
76
+ it 'must be importable' do
77
+ Blather::XMPPNode.parse(message_xml).should be_instance_of Blather::Stanza::Message
78
+ Blather::XMPPNode.parse(ichat_message_xml).should be_instance_of Blather::Stanza::Message
79
+ end
80
+
81
+ it 'provides "attr_accessor" for body' do
82
+ s = Blather::Stanza::Message.new
83
+ s.body.should be_nil
84
+ s.find('body').should be_empty
85
+
86
+ s.body = 'test message'
87
+ s.body.should_not be_nil
88
+ s.find('body').should_not be_empty
89
+ end
90
+
91
+ it 'provides "attr_accessor" for subject' do
92
+ s = Blather::Stanza::Message.new
93
+ s.subject.should be_nil
94
+ s.find('subject').should be_empty
95
+
96
+ s.subject = 'test subject'
97
+ s.subject.should_not be_nil
98
+ s.find('subject').should_not be_empty
99
+ end
100
+
101
+ it 'provides "attr_accessor" for thread' do
102
+ s = Blather::Stanza::Message.new
103
+ s.thread.should be_nil
104
+ s.find('thread').should be_empty
105
+
106
+ s.thread = 1234
107
+ s.thread.should_not be_nil
108
+ s.find('thread').should_not be_empty
109
+ end
110
+
111
+ it 'can set a parent attribute for thread' do
112
+ s = Blather::Stanza::Message.new
113
+ s.thread.should be_nil
114
+ s.find('thread').should be_empty
115
+
116
+ s.thread = {4321 => 1234}
117
+ s.thread.should == '1234'
118
+ s.parent_thread.should == '4321'
119
+ s.find('thread[@parent="4321"]').should_not be_empty
120
+ end
121
+
122
+ it 'ensures type is one of Blather::Stanza::Message::VALID_TYPES' do
123
+ lambda { Blather::Stanza::Message.new nil, nil, :invalid_type_name }.should raise_error(Blather::ArgumentError)
124
+
125
+ Blather::Stanza::Message::VALID_TYPES.each do |valid_type|
126
+ msg = Blather::Stanza::Message.new nil, nil, valid_type
127
+ msg.type.should == valid_type
128
+ end
129
+ end
130
+
131
+ Blather::Stanza::Message::VALID_TYPES.each do |valid_type|
132
+ it "provides a helper (#{valid_type}?) for type #{valid_type}" do
133
+ Blather::Stanza::Message.new.should respond_to :"#{valid_type}?"
134
+ end
135
+ end
136
+
137
+ it 'ensures an html node exists when asked for xhtml_node' do
138
+ search_args = [
139
+ '/message/html_ns:html',
140
+ {:html_ns => Blather::Stanza::Message::HTML_NS}
141
+ ]
142
+ msg = Blather::Stanza::Message.new
143
+ msg.find_first(*search_args).should be_nil
144
+
145
+ msg.xhtml_node
146
+ msg.find_first(*search_args).should_not be_nil
147
+ end
148
+
149
+ it 'ensures a body node exists when asked for xhtml_node' do
150
+ search_args = [
151
+ '/message/html_ns:html/body_ns:body',
152
+ {:html_ns => Blather::Stanza::Message::HTML_NS,
153
+ :body_ns => Blather::Stanza::Message::HTML_BODY_NS}
154
+ ]
155
+ msg = Blather::Stanza::Message.new
156
+ msg.find_first(*search_args).should be_nil
157
+
158
+ msg.xhtml_node
159
+ msg.find_first(*search_args).should_not be_nil
160
+ end
161
+
162
+ it 'returns an existing node when asked for xhtml_node' do
163
+ msg = Blather::Stanza::Message.new
164
+ msg << (h = Blather::XMPPNode.new('html', msg.document))
165
+ h.namespace = Blather::Stanza::Message::HTML_NS
166
+ b = Blather::XMPPNode.new('body', msg.document)
167
+ b.namespace = Blather::Stanza::Message::HTML_BODY_NS
168
+ h << b
169
+
170
+ msg.xhtml_node.should ==(b)
171
+ end
172
+
173
+ it 'has an xhtml setter' do
174
+ msg = Blather::Stanza::Message.new
175
+ xhtml = "<some>xhtml</some>"
176
+ msg.xhtml = xhtml
177
+ msg.xhtml_node.inner_html.strip.should ==(xhtml)
178
+ end
179
+
180
+ it 'sets valid xhtml even if the input is not valid' do
181
+ msg = Blather::Stanza::Message.new
182
+ xhtml = "<some>xhtml"
183
+ msg.xhtml = xhtml
184
+ msg.xhtml_node.inner_html.strip.should ==("<some>xhtml</some>")
185
+ end
186
+
187
+ it 'sets xhtml with more than one root node' do
188
+ msg = Blather::Stanza::Message.new
189
+ xhtml = "<i>xhtml</i> more xhtml"
190
+ msg.xhtml = xhtml
191
+ msg.xhtml_node.inner_html.strip.should ==("<i>xhtml</i> more xhtml")
192
+ end
193
+
194
+ it 'has an xhtml getter' do
195
+ msg = Blather::Stanza::Message.new
196
+ xhtml = "<some>xhtml</some>"
197
+ msg.xhtml = xhtml
198
+ msg.xhtml.should ==(xhtml)
199
+ end
200
+
201
+ it 'finds xhtml body when html wrapper has wrong namespace' do
202
+ msg = Blather::XMPPNode.parse(ichat_message_xml)
203
+ msg.xhtml.should == "<span style=\"font-family: 'Arial';font-size: 12px;color: #262626;\">Hello</span>\n <img alt=\"f5ad3a04d218d7160fa02415e02d41b3.jpg\" src=\"message-attachments:1\" width=\"30\" height=\"30\"></img>"
204
+ end
205
+
206
+ it 'has a chat state setter' do
207
+ msg = Blather::Stanza::Message.new
208
+ msg.chat_state = :composing
209
+ msg.xpath('ns:composing', :ns => Blather::Stanza::Message::CHAT_STATE_NS).should_not be_empty
210
+ end
211
+
212
+ it 'will only add one chat state at a time' do
213
+ msg = Blather::Stanza::Message.new
214
+ msg.chat_state = :composing
215
+ msg.chat_state = :paused
216
+
217
+ msg.xpath('ns:*', :ns => Blather::Stanza::Message::CHAT_STATE_NS).size.should ==(1)
218
+ end
219
+
220
+ it 'ensures chat state setter accepts strings' do
221
+ msg = Blather::Stanza::Message.new
222
+ msg.chat_state = "gone"
223
+ msg.xpath('ns:gone', :ns => Blather::Stanza::Message::CHAT_STATE_NS).should_not be_empty
224
+ end
225
+
226
+ it 'ensures chat state is one of Blather::Stanza::Message::VALID_CHAT_STATES' do
227
+ lambda do
228
+ msg = Blather::Stanza::Message.new
229
+ msg.chat_state = :invalid_chat_state
230
+ end.should raise_error(Blather::ArgumentError)
231
+
232
+ Blather::Stanza::Message::VALID_CHAT_STATES.each do |valid_chat_state|
233
+ msg = Blather::Stanza::Message.new
234
+ msg.chat_state = valid_chat_state
235
+ msg.chat_state.should == valid_chat_state
236
+ end
237
+ end
238
+
239
+ it 'has a chat state getter' do
240
+ msg = Blather::Stanza::Message.new
241
+ msg.chat_state = :paused
242
+ msg.chat_state.should ==(:paused)
243
+ end
244
+
245
+ it 'imports correct chat state' do
246
+ Blather::XMPPNode.parse(message_xml).chat_state.should == :paused
247
+ end
248
+
249
+ it 'makes a form child available' do
250
+ n = Blather::XMPPNode.parse(message_xml)
251
+ n.form.fields.size.should == 1
252
+ n.form.fields.map { |f| f.class }.uniq.should == [Blather::Stanza::X::Field]
253
+ n.form.should be_instance_of Blather::Stanza::X
254
+
255
+ r = Blather::Stanza::Message.new
256
+ r.form.type = :form
257
+ r.form.type.should == :form
258
+ end
259
+
260
+ it 'ensures the form child is a direct child' do
261
+ r = Blather::Stanza::Message.new
262
+ r.form
263
+ r.xpath('ns:x', :ns => Blather::Stanza::X.registered_ns).should_not be_empty
264
+ end
265
+
266
+ it 'is not delayed' do
267
+ n = Blather::XMPPNode.parse(message_xml)
268
+ n.delay.should == nil
269
+ n.delayed?.should == false
270
+ end
271
+
272
+ describe "with a delay" do
273
+ it "is delayed" do
274
+ n = Blather::XMPPNode.parse(delayed_message_xml)
275
+ n.delayed?.should == true
276
+ n.delay.should be_instance_of Blather::Stanza::Message::Delay
277
+ n.delay.from.should == 'coven@chat.shakespeare.lit'
278
+ n.delay.stamp.should == Time.utc(2002, 10, 13, 23, 58, 37, 0)
279
+ n.delay.description.should == "Too slow"
280
+ end
281
+ end
282
+ end