vinesmod 0.4.5

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 (151) hide show
  1. data/Gemfile +3 -0
  2. data/LICENSE +19 -0
  3. data/README.md +43 -0
  4. data/Rakefile +57 -0
  5. data/bin/vines +93 -0
  6. data/conf/certs/README +39 -0
  7. data/conf/certs/ca-bundle.crt +3366 -0
  8. data/conf/config.rb +149 -0
  9. data/lib/vines.rb +197 -0
  10. data/lib/vines/cluster.rb +246 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/command/bcrypt.rb +12 -0
  17. data/lib/vines/command/cert.rb +50 -0
  18. data/lib/vines/command/init.rb +68 -0
  19. data/lib/vines/command/register.rb +27 -0
  20. data/lib/vines/command/restart.rb +12 -0
  21. data/lib/vines/command/schema.rb +24 -0
  22. data/lib/vines/command/start.rb +28 -0
  23. data/lib/vines/command/stop.rb +18 -0
  24. data/lib/vines/command/unregister.rb +27 -0
  25. data/lib/vines/config.rb +213 -0
  26. data/lib/vines/config/host.rb +119 -0
  27. data/lib/vines/config/port.rb +132 -0
  28. data/lib/vines/config/pubsub.rb +108 -0
  29. data/lib/vines/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +35 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza.rb +175 -0
  37. data/lib/vines/stanza/iq.rb +48 -0
  38. data/lib/vines/stanza/iq/auth.rb +18 -0
  39. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  40. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  41. data/lib/vines/stanza/iq/error.rb +16 -0
  42. data/lib/vines/stanza/iq/ping.rb +16 -0
  43. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  44. data/lib/vines/stanza/iq/query.rb +10 -0
  45. data/lib/vines/stanza/iq/register.rb +42 -0
  46. data/lib/vines/stanza/iq/result.rb +16 -0
  47. data/lib/vines/stanza/iq/roster.rb +140 -0
  48. data/lib/vines/stanza/iq/session.rb +17 -0
  49. data/lib/vines/stanza/iq/vcard.rb +56 -0
  50. data/lib/vines/stanza/iq/version.rb +25 -0
  51. data/lib/vines/stanza/message.rb +43 -0
  52. data/lib/vines/stanza/presence.rb +156 -0
  53. data/lib/vines/stanza/presence/error.rb +23 -0
  54. data/lib/vines/stanza/presence/probe.rb +37 -0
  55. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  56. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  57. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  58. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  59. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  60. data/lib/vines/stanza/pubsub.rb +22 -0
  61. data/lib/vines/stanza/pubsub/create.rb +39 -0
  62. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  63. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  64. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  65. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  66. data/lib/vines/storage.rb +188 -0
  67. data/lib/vines/storage/local.rb +165 -0
  68. data/lib/vines/storage/null.rb +39 -0
  69. data/lib/vines/storage/sql.rb +260 -0
  70. data/lib/vines/store.rb +94 -0
  71. data/lib/vines/stream.rb +247 -0
  72. data/lib/vines/stream/client.rb +84 -0
  73. data/lib/vines/stream/client/auth.rb +74 -0
  74. data/lib/vines/stream/client/auth_restart.rb +29 -0
  75. data/lib/vines/stream/client/bind.rb +72 -0
  76. data/lib/vines/stream/client/bind_restart.rb +24 -0
  77. data/lib/vines/stream/client/closed.rb +13 -0
  78. data/lib/vines/stream/client/ready.rb +17 -0
  79. data/lib/vines/stream/client/session.rb +210 -0
  80. data/lib/vines/stream/client/start.rb +27 -0
  81. data/lib/vines/stream/client/tls.rb +38 -0
  82. data/lib/vines/stream/component.rb +58 -0
  83. data/lib/vines/stream/component/handshake.rb +26 -0
  84. data/lib/vines/stream/component/ready.rb +23 -0
  85. data/lib/vines/stream/component/start.rb +19 -0
  86. data/lib/vines/stream/http.rb +157 -0
  87. data/lib/vines/stream/http/auth.rb +22 -0
  88. data/lib/vines/stream/http/bind.rb +32 -0
  89. data/lib/vines/stream/http/bind_restart.rb +37 -0
  90. data/lib/vines/stream/http/ready.rb +29 -0
  91. data/lib/vines/stream/http/request.rb +172 -0
  92. data/lib/vines/stream/http/session.rb +120 -0
  93. data/lib/vines/stream/http/sessions.rb +65 -0
  94. data/lib/vines/stream/http/start.rb +23 -0
  95. data/lib/vines/stream/parser.rb +78 -0
  96. data/lib/vines/stream/sasl.rb +92 -0
  97. data/lib/vines/stream/server.rb +150 -0
  98. data/lib/vines/stream/server/auth.rb +13 -0
  99. data/lib/vines/stream/server/auth_restart.rb +13 -0
  100. data/lib/vines/stream/server/final_restart.rb +21 -0
  101. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  102. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  103. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  104. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  105. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  106. data/lib/vines/stream/server/outbound/start.rb +20 -0
  107. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  108. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  109. data/lib/vines/stream/server/ready.rb +24 -0
  110. data/lib/vines/stream/server/start.rb +13 -0
  111. data/lib/vines/stream/server/tls.rb +13 -0
  112. data/lib/vines/stream/state.rb +60 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +5 -0
  116. data/lib/vines/xmpp_server.rb +43 -0
  117. data/vines.gemspec +36 -0
  118. data/web/404.html +51 -0
  119. data/web/apple-touch-icon.png +0 -0
  120. data/web/chat/coffeescripts/chat.coffee +362 -0
  121. data/web/chat/coffeescripts/init.coffee +15 -0
  122. data/web/chat/index.html +16 -0
  123. data/web/chat/javascripts/app.js +1 -0
  124. data/web/chat/stylesheets/chat.css +144 -0
  125. data/web/favicon.png +0 -0
  126. data/web/lib/coffeescripts/button.coffee +25 -0
  127. data/web/lib/coffeescripts/contact.coffee +32 -0
  128. data/web/lib/coffeescripts/filter.coffee +49 -0
  129. data/web/lib/coffeescripts/layout.coffee +30 -0
  130. data/web/lib/coffeescripts/login.coffee +68 -0
  131. data/web/lib/coffeescripts/logout.coffee +5 -0
  132. data/web/lib/coffeescripts/navbar.coffee +84 -0
  133. data/web/lib/coffeescripts/notification.coffee +14 -0
  134. data/web/lib/coffeescripts/router.coffee +40 -0
  135. data/web/lib/coffeescripts/session.coffee +229 -0
  136. data/web/lib/coffeescripts/transfer.coffee +106 -0
  137. data/web/lib/images/dark-gray.png +0 -0
  138. data/web/lib/images/default-user.png +0 -0
  139. data/web/lib/images/light-gray.png +0 -0
  140. data/web/lib/images/logo-large.png +0 -0
  141. data/web/lib/images/logo-small.png +0 -0
  142. data/web/lib/images/white.png +0 -0
  143. data/web/lib/javascripts/base.js +12 -0
  144. data/web/lib/javascripts/icons.js +110 -0
  145. data/web/lib/javascripts/jquery.cookie.js +91 -0
  146. data/web/lib/javascripts/jquery.js +4 -0
  147. data/web/lib/javascripts/raphael.js +6 -0
  148. data/web/lib/javascripts/strophe.js +1 -0
  149. data/web/lib/stylesheets/base.css +385 -0
  150. data/web/lib/stylesheets/login.css +68 -0
  151. metadata +423 -0
@@ -0,0 +1,17 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Iq
6
+ # Session support is deprecated, but Adium requires it, so reply with an
7
+ # iq result stanza.
8
+ class Session < Iq
9
+ register "/iq[@id and @type='set']/ns:session", 'ns' => NAMESPACES[:session]
10
+
11
+ def process
12
+ stream.write(to_result)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Iq
6
+ class Vcard < Iq
7
+ NS = NAMESPACES[:vcard]
8
+
9
+ register "/iq[@id and @type='get' or @type='set']/ns:vCard", 'ns' => NS
10
+
11
+ def process
12
+ return unless allowed?
13
+ if local?
14
+ get? ? vcard_query : vcard_update
15
+ else
16
+ self['from'] = stream.user.jid.to_s
17
+ route
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def vcard_query
24
+ to = validate_to
25
+ jid = to ? to.bare : stream.user.jid.bare
26
+ card = storage(jid.domain).find_vcard(jid)
27
+
28
+ raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless card
29
+
30
+ doc = Document.new
31
+ result = doc.create_element('iq') do |node|
32
+ node['from'] = jid.to_s unless jid == stream.user.jid.bare
33
+ node['id'] = self['id']
34
+ node['to'] = stream.user.jid.to_s
35
+ node['type'] = 'result'
36
+ node << card
37
+ end
38
+ stream.write(result)
39
+ end
40
+
41
+ def vcard_update
42
+ to = validate_to
43
+ unless to.nil? || to == stream.user.jid.bare
44
+ raise StanzaErrors::Forbidden.new(self, 'auth')
45
+ end
46
+
47
+ storage.save_vcard(stream.user.jid, elements.first)
48
+
49
+ result = to_result
50
+ result.remove_attribute('from')
51
+ stream.write(result)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Iq
6
+ class Version < Query
7
+ NS = NAMESPACES[:version]
8
+
9
+ register "/iq[@id and @type='get']/ns:query", 'ns' => NS
10
+
11
+ def process
12
+ return if route_iq || to_pubsub_domain? || !allowed?
13
+ result = to_result.tap do |node|
14
+ node << node.document.create_element('query') do |query|
15
+ query.default_namespace = NS
16
+ query << node.document.create_element('name', 'Vines')
17
+ query << node.document.create_element('version', VERSION)
18
+ end
19
+ end
20
+ stream.write(result)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Message < Stanza
6
+ register "/message"
7
+
8
+ TYPE, FROM = %w[type from].map {|s| s.freeze }
9
+ VALID_TYPES = %w[chat error groupchat headline normal].freeze
10
+
11
+ VALID_TYPES.each do |type|
12
+ define_method "#{type}?" do
13
+ self[TYPE] == type
14
+ end
15
+ end
16
+
17
+ def process
18
+ unless self[TYPE].nil? || VALID_TYPES.include?(self[TYPE])
19
+ raise StanzaErrors::BadRequest.new(self, 'modify')
20
+ end
21
+
22
+ if local?
23
+ to = validate_to || stream.user.jid.bare
24
+ recipients = stream.connected_resources(to)
25
+ if recipients.empty?
26
+ if user = storage(to.domain).find_user(to)
27
+ msg_body = self.css('body').inner_text
28
+ unless msg_body==""
29
+ msg = {:from => stream.user.jid.to_s,:body=>msg_body,:to => to.bare.to_s}
30
+ storage(to.domain).save_offline_message(msg)
31
+ end
32
+ end
33
+ else
34
+ broadcast(recipients)
35
+ end
36
+ else
37
+ self[FROM] = stream.user.jid.to_s
38
+ route
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Presence < Stanza
6
+ register "/presence"
7
+
8
+ VALID_TYPES = %w[subscribe subscribed unsubscribe unsubscribed unavailable probe error].freeze
9
+
10
+ VALID_TYPES.each do |type|
11
+ define_method "#{type}?" do
12
+ self['type'] == type
13
+ end
14
+ end
15
+
16
+ def process
17
+ stream.last_broadcast_presence = @node.clone unless validate_to
18
+ unless self['type'].nil?
19
+ raise StanzaErrors::BadRequest.new(self, 'modify')
20
+ end
21
+ dir = outbound? ? 'outbound' : 'inbound'
22
+ method("#{dir}_broadcast_presence").call
23
+ end
24
+
25
+ def outbound?
26
+ !inbound?
27
+ end
28
+
29
+ def inbound?
30
+ stream.class == Vines::Stream::Server ||
31
+ stream.class == Vines::Stream::Component
32
+ end
33
+
34
+ def outbound_broadcast_presence
35
+ self['from'] = stream.user.jid.to_s
36
+ to = validate_to
37
+ type = (self['type'] || '').strip
38
+ initial = to.nil? && type.empty? && !stream.available?
39
+
40
+ recipients = if to.nil?
41
+ stream.available_subscribers
42
+ else
43
+ stream.user.subscribed_from?(to) ? stream.available_resources(to) : []
44
+ end
45
+
46
+ broadcast(recipients)
47
+ broadcast(stream.available_resources(stream.user.jid))
48
+
49
+ if initial
50
+ stream.available_subscribed_to_resources.each do |recipient|
51
+ if recipient.last_broadcast_presence
52
+ el = recipient.last_broadcast_presence.clone
53
+ el['to'] = stream.user.jid.to_s
54
+ el['from'] = recipient.user.jid.to_s
55
+ stream.write(el)
56
+ end
57
+ end
58
+ stream.remote_subscribed_to_contacts.each do |contact|
59
+ send_probe(contact.jid.bare)
60
+ end
61
+ #offline messages
62
+ storage.fetch_offline_messages(stream.user.jid).each do |offline_msg|
63
+ doc = Document.new
64
+ node = doc.create_element('message') do |el|
65
+ el['from'] = offline_msg[:from]
66
+ el['to'] = stream.user.jid.to_s
67
+ el['type'] = "chat"
68
+ el << doc.create_element("body") do |b|
69
+ b << offline_msg[:body]
70
+ end
71
+ end
72
+ stream.write(node)
73
+ end
74
+ storage.delete_offline_messages(stream.user.jid)
75
+ stream.available!
76
+
77
+ end
78
+
79
+ stream.remote_subscribers(to).each do |contact|
80
+ node = @node.clone
81
+ node['to'] = contact.jid.bare.to_s
82
+ router.route(node) rescue nil # ignore RemoteServerNotFound
83
+ end
84
+ end
85
+
86
+ def inbound_broadcast_presence
87
+ broadcast(stream.available_resources(validate_to))
88
+ end
89
+
90
+ private
91
+
92
+ def send_probe(to)
93
+ to = JID.new(to)
94
+ doc = Document.new
95
+ probe = doc.create_element('presence',
96
+ 'from' => stream.user.jid.bare.to_s,
97
+ 'id' => Kit.uuid,
98
+ 'to' => to.bare.to_s,
99
+ 'type' => 'probe')
100
+ router.route(probe) rescue nil # ignore RemoteServerNotFound
101
+ end
102
+
103
+ def auto_reply_to_subscription_request(from, type)
104
+ doc = Document.new
105
+ node = doc.create_element('presence') do |el|
106
+ el['from'] = from.to_s
107
+ el['id'] = self['id'] if self['id']
108
+ el['to'] = stream.user.jid.bare.to_s
109
+ el['type'] = type
110
+ end
111
+ stream.write(node)
112
+ end
113
+
114
+ # Send the contact's roster item to the current user's interested streams.
115
+ # Roster pushes are required, following presence subscription updates, to
116
+ # notify the user's clients of the contact's current state.
117
+ def send_roster_push(to)
118
+ contact = stream.user.contact(to)
119
+ stream.interested_resources(stream.user.jid).each do |recipient|
120
+ contact.send_roster_push(recipient)
121
+ end
122
+ end
123
+
124
+ # Notify the current user's interested streams of a contact's subscription
125
+ # state change as a result of receiving a subscribed, unsubscribe, or
126
+ # unsubscribed presence stanza.
127
+ def broadcast_subscription_change(contact)
128
+ stamp_from
129
+ stream.interested_resources(stamp_to).each do |recipient|
130
+ @node['to'] = recipient.user.jid.to_s
131
+ recipient.write(@node)
132
+ contact.send_roster_push(recipient)
133
+ end
134
+ end
135
+
136
+ # Validate that the incoming stanza has a 'to' attribute and strip any
137
+ # resource part from it so it's a bare jid. Return the bare JID object
138
+ # that was stamped.
139
+ def stamp_to
140
+ to = validate_to
141
+ raise StanzaErrors::BadRequest.new(self, 'modify') unless to
142
+ to.bare.tap do |bare|
143
+ self['to'] = bare.to_s
144
+ end
145
+ end
146
+
147
+ # Presence subscription stanzas must be addressed from the user's bare
148
+ # JID. Return the user's bare JID object that was stamped.
149
+ def stamp_from
150
+ stream.user.jid.bare.tap do |bare|
151
+ self['from'] = bare.to_s
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Presence
6
+ class Error < Presence
7
+ register "/presence[@type='error']"
8
+
9
+ def process
10
+ inbound? ? process_inbound : process_outbound
11
+ end
12
+
13
+ def process_outbound
14
+ # FIXME Implement error handling
15
+ end
16
+
17
+ def process_inbound
18
+ # FIXME Implement error handling
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Presence
6
+ class Probe < Presence
7
+ register "/presence[@type='probe']"
8
+
9
+ def process
10
+ inbound? ? process_inbound : process_outbound
11
+ end
12
+
13
+ def process_outbound
14
+ self['from'] = stream.user.jid.to_s
15
+ local? ? process_inbound : route
16
+ end
17
+
18
+ def process_inbound
19
+ to = validate_to
20
+ raise StanzaErrors::BadRequest.new(self, 'modify') unless to
21
+
22
+ user = storage(to.domain).find_user(to)
23
+ unless user && user.subscribed_from?(stream.user.jid)
24
+ auto_reply_to_subscription_request(to.bare, 'unsubscribed')
25
+ else
26
+ stream.available_resources(to).each do |recipient|
27
+ el = recipient.last_broadcast_presence.clone
28
+ el['from'] = recipient.user.jid.to_s
29
+ el['to'] = stream.user.jid.to_s
30
+ stream.write(el)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Presence
6
+ class Subscribe < Presence
7
+ register "/presence[@type='subscribe']"
8
+
9
+ def process
10
+ stamp_from
11
+ inbound? ? process_inbound : process_outbound
12
+ end
13
+
14
+ def process_outbound
15
+ to = stamp_to
16
+ stream.user.request_subscription(to)
17
+ storage.save_user(stream.user)
18
+ stream.update_user_streams(stream.user)
19
+ local? ? process_inbound : route
20
+ send_roster_push(to)
21
+ end
22
+
23
+ def process_inbound
24
+ to = stamp_to
25
+ contact = storage(to.domain).find_user(to)
26
+ if contact.nil?
27
+ auto_reply_to_subscription_request(to, 'unsubscribed')
28
+ elsif contact.subscribed_from?(stream.user.jid)
29
+ auto_reply_to_subscription_request(to, 'subscribed')
30
+ else
31
+ recipients = stream.available_resources(to)
32
+ if recipients.empty?
33
+ # TODO store subscription request per RFC 6121 3.1.3 #4
34
+ else
35
+ broadcast_to_available_resources([@node], to)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Stanza
5
+ class Presence
6
+ class Subscribed < Presence
7
+ register "/presence[@type='subscribed']"
8
+
9
+ def process
10
+ stamp_from
11
+ inbound? ? process_inbound : process_outbound
12
+ end
13
+
14
+ def process_outbound
15
+ to = stamp_to
16
+ stream.user.add_subscription_from(to)
17
+ storage.save_user(stream.user)
18
+ stream.update_user_streams(stream.user)
19
+ local? ? process_inbound : route
20
+ send_roster_push(to)
21
+ send_known_presence(to)
22
+ end
23
+
24
+ def process_inbound
25
+ to = stamp_to
26
+ user = storage(to.domain).find_user(to)
27
+ contact = user.contact(stream.user.jid) if user
28
+ return unless contact && contact.can_subscribe?
29
+ contact.subscribe_to
30
+ storage(to.domain).save_user(user)
31
+ stream.update_user_streams(user)
32
+ broadcast_subscription_change(contact)
33
+ end
34
+
35
+ private
36
+
37
+ # After approving a contact's subscription to this user's presence,
38
+ # broadcast this user's most recent presence stanzas to the contact.
39
+ def send_known_presence(to)
40
+ stanzas = stream.available_resources(stream.user.jid).map do |stream|
41
+ stream.last_broadcast_presence.clone.tap do |node|
42
+ node['from'] = stream.user.jid.to_s
43
+ node['id'] = Kit.uuid
44
+ end
45
+ end
46
+ broadcast_to_available_resources(stanzas, to)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end