xmpp4r 0.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/ChangeLog +10 -0
  2. data/README +12 -4
  3. data/Rakefile +1 -1
  4. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +2 -0
  5. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +2 -0
  6. data/data/doc/xmpp4r/examples/basic/client.rb +2 -2
  7. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +1 -0
  8. data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +1 -1
  9. data/lib/xmpp4r/bytestreams.rb +4 -0
  10. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +10 -4
  11. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +4 -0
  12. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +5 -1
  13. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +5 -1
  14. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +5 -1
  15. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +4 -0
  16. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +20 -6
  17. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +4 -0
  18. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +4 -0
  19. data/lib/xmpp4r/bytestreams/iq/si.rb +4 -0
  20. data/lib/xmpp4r/component.rb +1 -1
  21. data/lib/xmpp4r/dataforms.rb +4 -0
  22. data/lib/xmpp4r/delay.rb +4 -0
  23. data/lib/xmpp4r/discovery.rb +4 -0
  24. data/lib/xmpp4r/feature_negotiation.rb +4 -0
  25. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +4 -0
  26. data/lib/xmpp4r/iq.rb +1 -1
  27. data/lib/xmpp4r/message.rb +10 -3
  28. data/lib/xmpp4r/muc.rb +4 -0
  29. data/lib/xmpp4r/muc/helper/simplemucclient.rb +4 -0
  30. data/lib/xmpp4r/presence.rb +9 -5
  31. data/lib/xmpp4r/rexmladdons.rb +65 -3
  32. data/lib/xmpp4r/roster.rb +4 -0
  33. data/lib/xmpp4r/roster/helper/roster.rb +18 -3
  34. data/lib/xmpp4r/sasl.rb +59 -10
  35. data/lib/xmpp4r/stream.rb +22 -2
  36. data/lib/xmpp4r/vcard.rb +4 -0
  37. data/lib/xmpp4r/vcard/helper/vcard.rb +4 -4
  38. data/lib/xmpp4r/version.rb +4 -0
  39. data/lib/xmpp4r/xmpp4r.rb +1 -1
  40. data/setup.rb +800 -575
  41. data/test/bytestreams/tc_socks5bytestreams.rb +46 -0
  42. data/test/tc_iq.rb +1 -1
  43. data/test/tc_rexml.rb +60 -0
  44. data/test/ts_xmpp4r.rb +2 -1
  45. data/test/vcard/tc_helper.rb +49 -0
  46. metadata +96 -94
  47. data/test/roster/.tc_helper.rb.swp +0 -0
data/ChangeLog CHANGED
@@ -1,3 +1,13 @@
1
+ XMPP4R 0.3.1 (23/04/2007)
2
+ =========================
3
+ * SASL fixes
4
+ * Message#x and Presence#x support element selection by namespace
5
+ * Proper XML entity escaping for REXML text nodes
6
+ * Improvements to FileTransfer::Helper and SOCKS5BytestreamsServer
7
+ * Vcard::Helper fixes
8
+ * Update Digest module usage to reflect recent Ruby versions
9
+ * More documentation
10
+
1
11
  XMPP4R 0.3 (20/07/2006)
2
12
  =======================
3
13
  * SRV lookup capability in Client#connect
data/README CHANGED
@@ -1,8 +1,14 @@
1
1
  XMPP4R
2
2
  ----------
3
- by Lucas Nussbaum <lucas@lucas-nussbaum.net>,
4
- Stephan Maka <stephan@spaceboyz.net> and
5
- Kirill A. Shutemov <k.shutemov@gmail.com>
3
+ by:
4
+ Lucas Nussbaum <lucas@lucas-nussbaum.net>
5
+ Stephan Maka <stephan@spaceboyz.net>
6
+ Kirill A. Shutemov <k.shutemov@gmail.com>
7
+ Yuki Mitsui
8
+ Peter Schrammel
9
+ Olli
10
+ Vojtech Vobr
11
+ Andreas Wiese
6
12
 
7
13
  Currently, all the information is provided on
8
14
 
@@ -13,7 +19,9 @@ The integrated HTML documentation can be built with
13
19
  rake rdoc
14
20
 
15
21
  If you need to ask questions, feel free to ask them on the
16
- xmpp4r-devel@gna.org mailing list.
22
+ xmpp4r-devel@gna.org mailing list. When reporting problems,
23
+ please include a protocol dump which can be enabled with:
24
+ Jabber::debug = true
17
25
 
18
26
  XMPP4R is released under the Ruby license (see the LICENSE file), which is
19
27
  compatible with the GNU GPL (see the COPYING file) via an explicit
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ end
16
16
  # Globals
17
17
 
18
18
  PKG_NAME = 'xmpp4r'
19
- PKG_VERSION = '0.3'
19
+ PKG_VERSION = '0.3.1'
20
20
 
21
21
  PKG_FILES = ['ChangeLog', 'README', 'COPYING', 'LICENSE', 'setup.rb', 'Rakefile', 'UPDATING']
22
22
  Find.find('lib/', 'data/', 'test/', 'tools/') do |f|
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
1
3
  require 'yaml'
2
4
  require 'xmpp4r'
3
5
  require 'xmpp4r/bytestreams'
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
1
3
  require 'xmpp4r'
2
4
  require 'xmpp4r/helpers/filetransfer'
3
5
 
@@ -22,7 +22,7 @@ class BasicClient
22
22
  quit = true if line.nil?
23
23
  if not quit
24
24
  command, args = line.split(' ', 2)
25
- args.chomp!
25
+ args = args.to_s.chomp
26
26
  # main case
27
27
  case command
28
28
  when 'exit'
@@ -52,7 +52,7 @@ class BasicClient
52
52
  ##
53
53
  # connect <jid> <password>
54
54
  def do_connect(args)
55
- @jid, @password = args.split(' ', 3)
55
+ @jid, @password = args.split(' ', 2)
56
56
  @jid = JID::new(@jid)
57
57
  @cl = Client::new(@jid)
58
58
  @cl.connect
@@ -56,6 +56,7 @@ cl.add_message_callback do |m|
56
56
  end
57
57
  cl.send(Presence::new)
58
58
  m = Message::new(nil, body)
59
+ m.subject = subject
59
60
  STDIN.each_line { |l|
60
61
  l.chomp!
61
62
  m.set_to(JID::new(l).to_s)
@@ -34,7 +34,7 @@ doc.root.each_element { |e|
34
34
  elsif e.name == 'presence'
35
35
  pres = Jabber::Presence.new.import(e)
36
36
 
37
- if (pres.from.strip == jid) || (Digest::MD5.new(pres.from.strip.to_s).to_s == jidhash)
37
+ if (pres.from.strip == jid) || (Digest::MD5.hexdigest(pres.from.strip.to_s) == jidhash)
38
38
  if (jid == '') && !jidhash.nil?
39
39
  jid = pres.from.strip
40
40
  end
@@ -1,3 +1,7 @@
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
+
1
5
  require 'xmpp4r/bytestreams/iq/si.rb'
2
6
  require 'xmpp4r/bytestreams/iq/bytestreams.rb'
3
7
  require 'xmpp4r/bytestreams/helper/ibb/base.rb'
@@ -1,3 +1,7 @@
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
+
1
5
  require 'callbacks'
2
6
 
3
7
  require 'xmpp4r/bytestreams/iq/si'
@@ -242,8 +246,10 @@ module Jabber
242
246
  # jid:: [JID] to send the file to
243
247
  # source:: File-transfer source, implementing the FileSource interface
244
248
  # desc:: [String] or [nil] Optional file description
249
+ # from:: [String] or [nil] Optional jid for components
245
250
  # result:: [Bytestreams::SOCKS5BytestreamsInitiator] or [Bytestreams::IBBInitiator] or [nil]
246
- def offer(jid, source, desc=nil)
251
+ def offer(jid, source, desc=nil, from=nil)
252
+ from = from || @my_jid || @stream.jid
247
253
  session_id = Jabber::IdGenerator.instance.generate_id
248
254
 
249
255
  offered_methods = {}
@@ -255,7 +261,7 @@ module Jabber
255
261
  end
256
262
 
257
263
  iq = Iq::new(:set, jid)
258
- iq.from = @my_jid
264
+ iq.from = from
259
265
  si = iq.add(Bytestreams::IqSi.new(session_id, Bytestreams::IqSi::PROFILE_FILETRANSFER, source.mime))
260
266
 
261
267
  file = si.add(Bytestreams::IqSiFile.new(source.filename, source.size))
@@ -299,9 +305,9 @@ module Jabber
299
305
  end
300
306
 
301
307
  if stream_method == Bytestreams::IqQueryBytestreams::NS_BYTESTREAMS and @allow_bytestreams
302
- Bytestreams::SOCKS5BytestreamsInitiator.new(@stream, session_id, @my_jid || @stream.jid, jid)
308
+ Bytestreams::SOCKS5BytestreamsInitiator.new(@stream, session_id, from, jid)
303
309
  elsif stream_method == Bytestreams::IBB::NS_IBB and @allow_ibb
304
- Bytestreams::IBBInitiator.new(@stream, session_id, @my_jid || @stream.jid, jid)
310
+ Bytestreams::IBBInitiator.new(@stream, session_id, from, jid)
305
311
  else # Target responded with a stream_method we didn't offer
306
312
  eanswer = response.answer
307
313
  eanswer.type = :error
@@ -1,3 +1,7 @@
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
+
1
5
  require 'base64'
2
6
 
3
7
  module Jabber
@@ -1,3 +1,7 @@
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
+
1
5
  module Jabber
2
6
  module Bytestreams
3
7
  ##
@@ -5,7 +9,7 @@ module Jabber
5
9
  class IBBInitiator < IBB
6
10
  # You may set the block-size before open
7
11
  attr_accessor :block_size
8
-
12
+
9
13
  ##
10
14
  # Open the stream to the peer,
11
15
  # waits for successful result
@@ -1,3 +1,7 @@
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
+
1
5
  module Jabber
2
6
  module Bytestreams
3
7
  ##
@@ -28,7 +32,7 @@ module Jabber
28
32
  reply = iq.answer(false)
29
33
  reply.type = :result
30
34
  @stream.send(reply)
31
-
35
+
32
36
  connect_lock.unlock
33
37
  true
34
38
  else
@@ -1,3 +1,7 @@
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
+
1
5
  require 'socket'
2
6
  require 'thread'
3
7
  require 'timeout'
@@ -122,7 +126,7 @@ module Jabber
122
126
  # initiator_jid and target_jid.
123
127
  # result:: [String] SHA1 hash
124
128
  def stream_address
125
- Digest::SHA1.new("#{@session_id}#{@initiator_jid}#{@target_jid}").hexdigest
129
+ Digest::SHA1.hexdigest("#{@session_id}#{@initiator_jid}#{@target_jid}")
126
130
  end
127
131
 
128
132
  ##
@@ -1,3 +1,7 @@
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
+
1
5
  module Jabber
2
6
  module Bytestreams
3
7
  ##
@@ -1,3 +1,7 @@
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
+
1
5
  module Jabber
2
6
  module Bytestreams
3
7
  ##
@@ -19,12 +23,17 @@ module Jabber
19
23
  # Will start to listen on the given TCP port and
20
24
  # accept new peers
21
25
  # port:: [Fixnum] TCP port to listen on
22
- def initialize(port)
26
+ # listen_on:: [String] Optional address for the server socket to listen on (i.e. '0.0.0.0' or '::')
27
+ def initialize(port, listen_on=nil)
23
28
  @port = port
24
29
  @addresses = []
25
30
  @peers = []
26
31
  @peers_lock = Mutex.new
27
- socket = TCPServer.new(port)
32
+ if listen_on
33
+ socket = TCPServer.new(listen_on, port)
34
+ else
35
+ socket = TCPServer.new(port)
36
+ end
28
37
 
29
38
  Thread.new {
30
39
  loop {
@@ -45,6 +54,9 @@ module Jabber
45
54
 
46
55
  ##
47
56
  # Find the socket a peer is associated to
57
+ #
58
+ # This method also performs some housekeeping, ie. removing
59
+ # peers with closed sockets.
48
60
  # addr:: [String] Address like SOCKS5Bytestreams#stream_address
49
61
  # result:: [TCPSocker] or [nil]
50
62
  def peer_sock(addr)
@@ -58,11 +70,13 @@ module Jabber
58
70
  removes << peer
59
71
  elsif peer.address == addr and res.nil?
60
72
  res = peer.socket
61
- else
62
- # If we sent multiple addresses of our own, clients may
63
- # connect multiple times. Close these connections here.
64
- removes << peer
65
73
  end
74
+
75
+ # If we sent multiple addresses of our own, clients may
76
+ # connect multiple times. DO NOT close any other connections
77
+ # here. These may belong to other concurrent bytestreams,
78
+ # believe that the peer will close any unneeded sockets
79
+ # which will then be picked up by the next call to peer_sock.
66
80
  }
67
81
 
68
82
  # If we sent multiple addresses of our own, clients may
@@ -1,3 +1,7 @@
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
+
1
5
  require 'socket'
2
6
 
3
7
  module Jabber
@@ -1,3 +1,7 @@
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
+
1
5
  module Jabber
2
6
  module Bytestreams
3
7
  ##
@@ -1,3 +1,7 @@
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
+
1
5
  require 'time' # For Time#xmlschema
2
6
 
3
7
  require 'xmpp4r/feature_negotiation/iq/feature'
@@ -83,7 +83,7 @@ module Jabber
83
83
  # Throws AuthenticationFailure
84
84
  # secret:: [String] the shared secret
85
85
  def auth(secret)
86
- hash = Digest::SHA1::new(@streamid.to_s + secret).to_s
86
+ hash = Digest::SHA1::hexdigest(@streamid.to_s + secret)
87
87
  authenticated = false
88
88
  send("<handshake>#{hash}</handshake>") { |r|
89
89
  if r.prefix == 'stream' and r.name == 'error'
@@ -1 +1,5 @@
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
+
1
5
  require 'xmpp4r/dataforms/x/data.rb'
@@ -1 +1,5 @@
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
+
1
5
  require 'xmpp4r/delay/x/delay.rb'
@@ -1,2 +1,6 @@
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
+
1
5
  require 'xmpp4r/discovery/iq/discoinfo.rb'
2
6
  require 'xmpp4r/discovery/iq/discoitems.rb'
@@ -1 +1,5 @@
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
+
1
5
  require 'xmpp4r/feature_negotiation/iq/feature.rb'
@@ -1,3 +1,7 @@
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
+
1
5
  require 'xmpp4r/iq'
2
6
  require 'xmpp4r/dataforms/x/data'
3
7
 
@@ -156,7 +156,7 @@ module Jabber
156
156
  query = IqQuery::new
157
157
  query.add_namespace('jabber:iq:auth')
158
158
  query.add(REXML::Element::new('username').add_text(jid.node))
159
- query.add(REXML::Element::new('digest').add_text(Digest::SHA1.new(session_id + password).hexdigest))
159
+ query.add(REXML::Element::new('digest').add_text(Digest::SHA1.hexdigest(session_id + password)))
160
160
  query.add(REXML::Element::new('resource').add_text(jid.resource)) if not jid.resource.nil?
161
161
  iq.add(query)
162
162
  iq
@@ -82,9 +82,16 @@ module Jabber
82
82
  end
83
83
 
84
84
  ##
85
- # Get the first <x/> element of this stanza, or nil if none found.
86
- def x
87
- first_element('x')
85
+ # Get the first <x/> element in this stanza, or nil if none found.
86
+ # namespace:: [String] Optional, find the first <x/> element having this xmlns
87
+ # result:: [REXML::Element] or nil
88
+ def x(namespace=nil)
89
+ each_element('x') { |x|
90
+ if namespace.nil? or namespace == x.namespace
91
+ return x
92
+ end
93
+ }
94
+ nil
88
95
  end
89
96
 
90
97
  ##
@@ -1,3 +1,7 @@
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
+
1
5
  require 'xmpp4r/muc/x/muc'
2
6
  require 'xmpp4r/muc/x/mucuserinvite'
3
7
  require 'xmpp4r/muc/x/mucuseritem'
@@ -1,3 +1,7 @@
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
+
1
5
  require 'xmpp4r/delay/x/delay'
2
6
  require 'xmpp4r/muc/helper/mucclient'
3
7
 
@@ -94,12 +94,16 @@ module Jabber
94
94
  end
95
95
 
96
96
  ##
97
- # Get the first <x/> element of this stanza
97
+ # Get the first <x/> element in this stanza, or nil if none found.
98
+ # namespace:: [String] Optional, find the first <x/> element having this xmlns
98
99
  # result:: [REXML::Element] or nil
99
- def x
100
- xe = nil
101
- each_element('x') { |e| xe = e }
102
- xe
100
+ def x(namespace=nil)
101
+ each_element('x') { |x|
102
+ if namespace.nil? or namespace == x.namespace
103
+ return x
104
+ end
105
+ }
106
+ nil
103
107
  end
104
108
 
105
109
  ##
@@ -10,8 +10,23 @@ require 'rexml/source'
10
10
  oldverbose = $VERBOSE
11
11
  $VERBOSE = false
12
12
 
13
- # REXML module. This file only adds a few methods to the REXML module, to
14
- # ease the coding.
13
+ # REXML module. This file only adds the following methods to the REXML module, to
14
+ # ease the coding:
15
+ # * replace_element_text
16
+ # * first_element
17
+ # * first_element_text
18
+ # * typed_add
19
+ # * import
20
+ # * self.import
21
+ # * delete_elements
22
+ #
23
+ # Further definitions are just copied from REXML out of Ruby-1.8.4 to solve issues
24
+ # with REXML in Ruby-1.8.2.
25
+ #
26
+ # The redefinitions of Text::normalize and Attribute#initialize address an issue
27
+ # where entities in element texts and attributes were not escaped. This modifies
28
+ # the behavious of REXML a bit but Sean Russell intends a similar behaviour for
29
+ # the future of REXML.
15
30
  module REXML
16
31
  # this class adds a few helper methods to REXML::Element
17
32
  class Element
@@ -116,7 +131,8 @@ module REXML
116
131
  @variables = {}
117
132
  end
118
133
 
119
- def namespaces=( namespaces={} )
134
+ def namespaces=( namespaces )
135
+ namespaces ||= {}
120
136
  Functions::namespace_context = namespaces
121
137
  @namespaces = namespaces
122
138
  end
@@ -819,6 +835,52 @@ module REXML
819
835
 
820
836
 
821
837
  ############################################################################
838
+
839
+ class Text
840
+ # Escapes all possible entities
841
+ def Text::normalize( input, doctype=nil, entity_filter=nil )
842
+ copy = input
843
+ # Doing it like this rather than in a loop improves the speed
844
+ if doctype
845
+ # Replace all ampersands that aren't part of an entity
846
+ copy = copy.gsub( EREFERENCE, '&amp;' )
847
+ doctype.entities.each_value do |entity|
848
+ copy = copy.gsub( entity.value,
849
+ "&#{entity.name};" ) if entity.value and
850
+ not( entity_filter and entity_filter.include?(entity) )
851
+ end
852
+ else
853
+ # Replace all ampersands
854
+ copy = copy.gsub( '&', '&amp;' )
855
+ DocType::DEFAULT_ENTITIES.each_value do |entity|
856
+ copy = copy.gsub(entity.value, "&#{entity.name};" )
857
+ end
858
+ end
859
+ copy
860
+ end
861
+ end
862
+
863
+ class Attribute
864
+ def initialize( first, second=nil, parent=nil )
865
+ @normalized = @unnormalized = @element = nil
866
+ if first.kind_of? Attribute
867
+ self.name = first.expanded_name
868
+ @value = first.value
869
+ if second.kind_of? Element
870
+ @element = second
871
+ else
872
+ @element = first.element
873
+ end
874
+ elsif first.kind_of? String
875
+ @element = parent if parent.kind_of? Element
876
+ self.name = first
877
+ @value = Text::normalize(second.to_s)
878
+ else
879
+ raise "illegal argument #{first.class.name} to Attribute constructor"
880
+ end
881
+ end
882
+ end
883
+
822
884
  end
823
885
 
824
886
  # Restore the old $VERBOSE setting