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,160 @@
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
+ # The presence class is used to construct presence messages to
10
+ # send to the Jabber service.
11
+ #
12
+ class Presence
13
+ attr_accessor :to, :from, :id, :type
14
+
15
+ # The state to show (chat, xa, dnd, away)
16
+ attr_accessor :show
17
+
18
+ # The status message
19
+ attr_accessor :status
20
+ attr_accessor :priority
21
+
22
+ ##
23
+ # Constructs a Presence object w/the supplied id
24
+ #
25
+ # id:: [String] The message ID
26
+ # show:: [String] The state to show
27
+ # status:: [String] The status message
28
+ #
29
+ def initialize(id, show=nil, status=nil)
30
+ @id = id
31
+ @show = show if show
32
+ @status = status if status
33
+ end
34
+
35
+ ##
36
+ # Generate a presence object for initial presence notification
37
+ #
38
+ # id:: [String] The message ID
39
+ # show:: [String] The state to show
40
+ # status:: [String] The status message
41
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
42
+ #
43
+ def self.gen_initial(id, show=nil, status=nil)
44
+ Presence.new(id, show, status)
45
+ end
46
+
47
+ ##
48
+ # Generate a presence object w/show="normal" (normal availability)
49
+ #
50
+ # id:: [String] The message ID
51
+ # status:: [String=nil] The status message
52
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
53
+ #
54
+ def self.gen_normal(id, status=nil)
55
+ Presence.new(id, "normal", status)
56
+ end
57
+
58
+ ##
59
+ # Generate a presence object w/show="chat" (free for chat)
60
+ #
61
+ # id:: [String] The message ID
62
+ # status:: [String=nil] The status message
63
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
64
+ #
65
+ def self.gen_chat(id, status=nil)
66
+ Presence.new(id, "chat", status)
67
+ end
68
+
69
+ ##
70
+ # Generate a presence object w/show="xa" (extended away)
71
+ #
72
+ # id:: [String] The message ID
73
+ # status:: [String=nil] The status message
74
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
75
+ #
76
+ def self.gen_xa(id, status=nil)
77
+ Presence.new(id, "xa", status)
78
+ end
79
+
80
+ ##
81
+ # Generate a presence object w/show="dnd" (do not disturb)
82
+ #
83
+ # id:: [String] The message ID
84
+ # status:: [String=nil] The status message
85
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
86
+ #
87
+ def self.gen_dnd(id, status=nil)
88
+ Presence.new(id, "dnd", status)
89
+ end
90
+
91
+ ##
92
+ # Generate a presence object w/show="away" (away from resource)
93
+ #
94
+ # id:: [String] The message ID
95
+ # status:: [String=nil] The status message
96
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
97
+ #
98
+ def self.gen_away(id, status=nil)
99
+ Presence.new(id, "away", status)
100
+ end
101
+
102
+ ##
103
+ # Generate a presence object w/show="unavailable" (not free for chat)
104
+ #
105
+ # id:: [String] The message ID
106
+ # status:: [String=nil] The status message
107
+ # return:: [Jabber::Protocol::Presence] The newly created Presence object
108
+ #
109
+ def self.gen_unavailable(id, status=nil)
110
+ p = Presence.new(id)
111
+ p.type="unavailable"
112
+ p
113
+ end
114
+
115
+ def self.gen_new_subscription(to)
116
+ p = Presence.new(Jabber.gen_random_id)
117
+ p.type = "subscribe"
118
+ p.to = to
119
+ p
120
+ end
121
+
122
+ def self.gen_accept_subscription(id, jid)
123
+ p = Presence.new(id)
124
+ p.type = "subscribed"
125
+ p.to = jid
126
+ p
127
+ end
128
+
129
+ def self.gen_accept_unsubscription(id, jid)
130
+ p = Presence.new(id)
131
+ p.type = "unsubscribed"
132
+ p.to = jid
133
+ p
134
+ end
135
+
136
+ ##
137
+ # Generates the xml representation of this Presence object
138
+ #
139
+ # return:: [String] The presence XML message to send the Jabber service
140
+ #
141
+ def to_xml
142
+ e = XMLElement.new("presence")
143
+ e.add_attribute("id", @id) if @id
144
+ e.add_attribute("from", @from) if @from
145
+ e.add_attribute("to", @to) if @to
146
+ e.add_attribute("type", @type) if @type
147
+ e.add_child("show").add_data(@show) if @show
148
+ e.add_child("status").add_data(@status) if @status
149
+ e.add_child("priority") if @priority
150
+ e.to_s
151
+ end
152
+
153
+ ##
154
+ # see _to_xml
155
+ #
156
+ def to_s
157
+ to_xml
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,144 @@
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
+ # Utility class to create valid XML strings
10
+ #
11
+ class XMLElement
12
+
13
+ # The parent XMLElement
14
+ attr_accessor :parent
15
+
16
+ ##
17
+ # Construct an XMLElement for the supplied tag and attributes
18
+ #
19
+ # tag:: [String] XML tag
20
+ # attributes:: [Hash = {}] The attribute hash[attribute]=value
21
+ def initialize(tag, attributes={})
22
+ @tag = tag
23
+ @elements = []
24
+ @attributes = attributes
25
+ @data = ""
26
+ end
27
+
28
+ ##
29
+ # Adds an attribute to this element
30
+ #
31
+ # attrib:: [String] The attribute name
32
+ # value:: [String] The attribute value
33
+ # return:: [Jabber::Protocol::XMLElement] self for chaining
34
+ #
35
+ def add_attribute(attrib, value)
36
+ @attributes[attrib]=value
37
+ self
38
+ end
39
+
40
+ ##
41
+ # Adds data to this element
42
+ #
43
+ # data:: [String] The data to add
44
+ # return:: [Jabber::Protocol::XMLElement] self for chaining
45
+ #
46
+ def add_data(data)
47
+ @data += data.to_s
48
+ self
49
+ end
50
+
51
+ ##
52
+ # Sets the namespace for this tag
53
+ #
54
+ # ns:: [String] The namespace
55
+ # return:: [Jabber::Protocol::XMLElement] self for chaining
56
+ #
57
+ def set_namespace(ns)
58
+ @tag+=":#{ns}"
59
+ self
60
+ end
61
+
62
+ ##
63
+ # Adds cdata to this element
64
+ #
65
+ # cdata:: [String] The cdata to add
66
+ # return:: [Jabber::Protocol::XMLElement] self for chaining
67
+ #
68
+ def add_cdata(cdata)
69
+ @data += "<![CDATA[#{cdata.to_s}]]>"
70
+ self
71
+ end
72
+
73
+ ##
74
+ # Returns the parent element
75
+ #
76
+ # return:: [Jabber::Protocol::XMLElement] The parent XMLElement
77
+ #
78
+ def to_parent
79
+ @parent
80
+ end
81
+
82
+ ##
83
+ # Adds a child to this element of the supplied tag
84
+ #
85
+ # tag:: [String] The element tag
86
+ # attributes:: [Hash = {}] The attributes hash[attribute]=value
87
+ # return:: [Jabber::Protocol::XMLElement] newly created child element
88
+ #
89
+ def add_child(tag, attributes={})
90
+ child = XMLElement.new(tag, attributes)
91
+ child.parent = self
92
+ @elements << child
93
+ return child
94
+ end
95
+
96
+ ##
97
+ # Adds arbitrary XML data to this object
98
+ #
99
+ # xml:: [String] the xml to add
100
+ #
101
+ def add_xml(xml)
102
+ @xml = xml
103
+ end
104
+
105
+ ##
106
+ # Recursively builds the XML string by traversing this element's
107
+ # children.
108
+ #
109
+ # format:: [Boolean] True to pretty-print (format) the output string
110
+ # indent:: [Integer = 0] The indent level (recursively more)
111
+ #
112
+ def to_xml(format, indent=0)
113
+ result = ""
114
+ result += " "*indent if format
115
+ result += "<#{@tag}"
116
+ @attributes.each {|attrib, value| result += (' '+attrib.to_s+'="'+value.to_s+'"') }
117
+ if @data=="" and @elements.size==0
118
+ result +="/>"
119
+ result +="\n" if format
120
+ return result
121
+ end
122
+ result += ">"
123
+ result += "\n" if format and @data==""
124
+ result += @data if @data!=""
125
+ @elements.each {|element| result+=element.to_xml(format, indent+4)}
126
+ result += @xml if not @xml.nil?
127
+ result += " "*indent if format and @data==""
128
+ result+="</#{@tag}>"
129
+ result+="\n" if format
130
+ return result
131
+ end
132
+
133
+ ##
134
+ # Climbs to the top of this elements parent tree and then returns
135
+ # the to_xml XML string.
136
+ #
137
+ # return:: [String] The XML string of this element (from the topmost parent).
138
+ #
139
+ def to_s
140
+ return @parent.to_s if @parent
141
+ return to_xml(true)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,257 @@
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
+ require "singleton"
8
+ require "socket"
9
+
10
+ module Jabber
11
+ class JabberConnectionException < RuntimeError
12
+ attr_reader :data
13
+
14
+ def initialize(writing, data)
15
+ @writing = writing
16
+ @data = data
17
+ end
18
+
19
+ def writing?
20
+ @writing
21
+ end
22
+ end
23
+
24
+ ##
25
+ # The Protocol module contains helper methods for constructing
26
+ # Jabber protocol elements and classes that implement protocol
27
+ # elements.
28
+ #
29
+ module Protocol
30
+
31
+ USE_PARSER = :rexml # either :rexml or :xmlparser
32
+
33
+ ##
34
+ # The parser to use for stream processing. The current
35
+ # available parsers are:
36
+ #
37
+ # * Jabber::Protocol::ExpatJabberParser uses XMLParser
38
+ # * Jabber::Protocol::REXMLJabberParser uses REXML
39
+ #
40
+ # return:: [Class] The parser class
41
+ #
42
+ def Protocol.Parser
43
+ if USE_PARSER==:xmlparser
44
+ Jabber::Protocol::ExpatJabberParser
45
+ else
46
+ Jabber::Protocol::REXMLJabberParser
47
+ end
48
+ end
49
+
50
+ ##
51
+ # Generates an open stream XML element
52
+ #
53
+ # host:: [String] The host being connected to
54
+ # return:: [String] The XML data to send
55
+ #
56
+ def self.gen_open_stream(host)
57
+ return ('<?xml version="1.0" encoding="UTF-8" ?><stream:stream to="'+host+'" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">')
58
+ end
59
+
60
+ ##
61
+ # Generates an close stream XML element
62
+ #
63
+ # return:: [String] The XML data to send
64
+ #
65
+ def self.gen_close_stream
66
+ return "</stream:stream>"
67
+ end
68
+
69
+ if USE_PARSER == :xmlparser
70
+ require 'xmlparser'
71
+ ##
72
+ # The ExpatJabberParser uses XMLParser (expat) to parse the incoming XML stream
73
+ # of the Jabber protocol and fires ParsedXMLElements at the Connection
74
+ # instance.
75
+ #
76
+ class ExpatJabberParser
77
+
78
+ # status if the parser is started
79
+ attr_reader :started
80
+
81
+ ##
82
+ # Constructs a parser for the supplied stream (socket input)
83
+ #
84
+ # stream:: [IO] Socket input stream
85
+ # listener:: [#receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
86
+ #
87
+ def initialize(stream, listener)
88
+ @stream = stream
89
+ def @stream.gets
90
+ super(">")
91
+ end
92
+ @listener = listener
93
+ end
94
+
95
+ ##
96
+ # Begins parsing the XML stream and does not return until
97
+ # the stream closes.
98
+ #
99
+ def parse
100
+ @started = false
101
+
102
+ parser = XMLParser.new("UTF-8")
103
+ def parser.unknownEncoding(e)
104
+ raise "Unknown encoding #{e.to_s}"
105
+ end
106
+ def parser.default
107
+ end
108
+
109
+ begin
110
+ parser.parse(@stream) do |type, name, data|
111
+ begin
112
+ case type
113
+ when XMLParser::START_ELEM
114
+ case name
115
+ when "stream:stream"
116
+ openstream = ParsedXMLElement.new(name)
117
+ data.each {|key, value| openstream.add_attribute(key, value)}
118
+ @listener.receive(openstream)
119
+ @started = true
120
+ else
121
+ if @current.nil?
122
+ @current = ParsedXMLElement.new(name.clone)
123
+ else
124
+ @current = @current.add_child(name.clone)
125
+ end
126
+ data.each {|key, value| @current.add_attribute(key.clone, value.clone)}
127
+ end
128
+ when XMLParser::CDATA
129
+ @current.append_data(data.clone) if @current
130
+ when XMLParser::END_ELEM
131
+ case name
132
+ when "stream:stream"
133
+ @started = false
134
+ else
135
+ @listener.receive(@current) unless @current.element_parent
136
+ @current = @current.element_parent
137
+ end
138
+ end
139
+ rescue
140
+ puts "Error #{$!}"
141
+ end
142
+ end
143
+ rescue XMLParserError
144
+ line = parser.line
145
+ print "XML Parsing error(#{line}): #{$!}\n"
146
+ end
147
+ end
148
+ end
149
+ else # USE REXML
150
+ require 'rexml/document'
151
+ require 'rexml/parsers/sax2parser'
152
+ require 'rexml/source'
153
+
154
+ ##
155
+ # The REXMLJabberParser uses REXML to parse the incoming XML stream
156
+ # of the Jabber protocol and fires ParsedXMLElements at the Connection
157
+ # instance.
158
+ #
159
+ class REXMLJabberParser
160
+ # status if the parser is started
161
+ attr_reader :started
162
+
163
+ ##
164
+ # Constructs a parser for the supplied stream (socket input)
165
+ #
166
+ # stream:: [IO] Socket input stream
167
+ # listener:: [Object.receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
168
+ #
169
+ def initialize(stream, listener)
170
+ @stream = stream
171
+
172
+ # this hack fixes REXML version "2.7.3" and "2.7.4"
173
+ if REXML::Version=="2.7.3" || REXML::Version=="2.7.4"
174
+ def @stream.read(len=nil)
175
+ len = 100 unless len
176
+ super(len)
177
+ end
178
+ def @stream.gets(char=nil)
179
+ super(">")
180
+ end
181
+ def @stream.readline(char=nil)
182
+ super(">")
183
+ end
184
+ def @stream.readlines(char=nil)
185
+ super(">")
186
+ end
187
+ end
188
+
189
+ @listener = listener
190
+ @current = nil
191
+ end
192
+
193
+ ##
194
+ # Begins parsing the XML stream and does not return until
195
+ # the stream closes.
196
+ #
197
+ def parse
198
+ #puts "PARSE"
199
+ @started = false
200
+ begin
201
+ parser = REXML::Parsers::SAX2Parser.new @stream
202
+
203
+ parser.listen(:end_document) do
204
+ raise Jabber::ConnectionForceCloseError
205
+ end
206
+
207
+ parser.listen( :start_element ) do |uri, localname, qname, attributes|
208
+ puts "START ELEMENT"
209
+ case qname
210
+ when "stream:stream"
211
+ openstream = ParsedXMLElement.new(qname)
212
+ attributes.each { |attr, value| openstream.add_attribute(attr, value) }
213
+ @listener.receive(openstream)
214
+ @started = true
215
+ else
216
+ if @current.nil?
217
+ @current = ParsedXMLElement.new(qname)
218
+ else
219
+ @current = @current.add_child(qname)
220
+ end
221
+ attributes.each { |attr, value| @current.add_attribute(attr, value) }
222
+ end
223
+ end
224
+ parser.listen( :end_element ) do |uri, localname, qname|
225
+ puts "END ELEMENT"
226
+ case qname
227
+ when "stream:stream"
228
+ @started = false
229
+ else
230
+ @listener.receive(@current) unless @current.element_parent
231
+ @current = @current.element_parent
232
+ end
233
+ end
234
+ parser.listen( :characters ) do | text |
235
+ puts "CHARACTERS"
236
+ @current.append_data(text) if @current
237
+ end
238
+ parser.listen( :cdata ) do | text |
239
+ puts "CDATA"
240
+ @current.append_data(text) if @current
241
+ end
242
+ parser.parse
243
+ rescue REXML::ParseException => e
244
+ puts "FAIL"
245
+
246
+ puts e.backtrace.join "\n"
247
+ puts e.message
248
+ @listener.parse_failure
249
+ rescue Jabber::ConnectionForceCloseError => e
250
+ @listener.parse_failure(e)
251
+ end
252
+ end
253
+ end
254
+ end # USE_PARSER
255
+ end
256
+ end
257
+