xmpp4r 0.4 → 0.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.
- data/CHANGELOG +8 -0
- data/README.rdoc +4 -1
- data/Rakefile +10 -20
- data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +20 -1
- data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +7 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +7 -1
- data/lib/xmpp4r/callbacks.rb +9 -0
- data/lib/xmpp4r/caps/c.rb +14 -0
- data/lib/xmpp4r/caps/helper/helper.rb +1 -4
- data/lib/xmpp4r/client.rb +42 -15
- data/lib/xmpp4r/connection.rb +7 -3
- data/lib/xmpp4r/debuglog.rb +22 -1
- data/lib/xmpp4r/discovery.rb +1 -0
- data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
- data/lib/xmpp4r/discovery/iq/discoinfo.rb +2 -2
- data/lib/xmpp4r/discovery/iq/discoitems.rb +2 -2
- data/lib/xmpp4r/errors.rb +5 -2
- data/lib/xmpp4r/httpbinding/client.rb +9 -19
- data/lib/xmpp4r/last.rb +2 -0
- data/lib/xmpp4r/last/helper/helper.rb +37 -0
- data/lib/xmpp4r/last/iq/last.rb +67 -0
- data/lib/xmpp4r/location.rb +2 -0
- data/lib/xmpp4r/location/helper/helper.rb +56 -0
- data/lib/xmpp4r/location/location.rb +179 -0
- data/lib/xmpp4r/message.rb +32 -0
- data/lib/xmpp4r/presence.rb +1 -1
- data/lib/xmpp4r/pubsub/children/configuration.rb +1 -1
- data/lib/xmpp4r/pubsub/children/items.rb +11 -2
- data/lib/xmpp4r/pubsub/children/publish.rb +14 -0
- data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
- data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +2 -3
- data/lib/xmpp4r/pubsub/helper/nodehelper.rb +4 -4
- data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +90 -0
- data/lib/xmpp4r/pubsub/helper/servicehelper.rb +58 -19
- data/lib/xmpp4r/reliable.rb +168 -0
- data/lib/xmpp4r/rexmladdons.rb +6 -0
- data/lib/xmpp4r/roster/helper/roster.rb +5 -2
- data/lib/xmpp4r/sasl.rb +19 -8
- data/lib/xmpp4r/stream.rb +133 -31
- data/lib/xmpp4r/streamparser.rb +9 -1
- data/lib/xmpp4r/test/listener_mocker.rb +118 -0
- data/lib/xmpp4r/xmpp4r.rb +3 -1
- data/test/bytestreams/tc_ibb.rb +6 -4
- data/test/bytestreams/tc_socks5bytestreams.rb +3 -2
- data/test/caps/tc_helper.rb +4 -2
- data/test/dataforms/tc_data.rb +1 -1
- data/test/last/tc_helper.rb +75 -0
- data/test/lib/clienttester.rb +43 -14
- data/test/muc/tc_muc_mucclient.rb +6 -2
- data/test/pubsub/tc_helper.rb +131 -8
- data/test/pubsub/tc_nodeconfig.rb +7 -0
- data/test/reliable/tc_disconnect_cleanup.rb +334 -0
- data/test/reliable/tc_disconnect_exception.rb +37 -0
- data/test/reliable/tc_listener_mocked_test.rb +68 -0
- data/test/reliable/tc_reliable_connection.rb +31 -0
- data/test/roster/tc_helper.rb +21 -11
- data/test/rpc/tc_helper.rb +2 -2
- data/test/tc_callbacks.rb +3 -3
- data/test/tc_message.rb +15 -0
- data/test/tc_stream.rb +59 -121
- data/test/tc_streamError.rb +2 -4
- data/test/tc_streamparser.rb +26 -13
- data/test/ts_xmpp4r.rb +0 -9
- data/test/tune/tc_helper_recv.rb +0 -2
- data/test/vcard/tc_helper.rb +1 -1
- data/xmpp4r.gemspec +31 -84
- metadata +116 -167
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +0 -62
| @@ -0,0 +1,168 @@ | |
| 1 | 
            +
            require 'xmpp4r/stream'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jabber
         | 
| 4 | 
            +
              module Reliable
         | 
| 5 | 
            +
                
         | 
| 6 | 
            +
                class Connection < Jabber::Client
         | 
| 7 | 
            +
                  def initialize(full_jid, config)
         | 
| 8 | 
            +
                    super(full_jid)
         | 
| 9 | 
            +
                    @servers = config[:servers]
         | 
| 10 | 
            +
                    @port = config[:port] || 5222
         | 
| 11 | 
            +
                    @max_retry = config[:max_retry] || 30
         | 
| 12 | 
            +
                    @retry_sleep = config[:retry_sleep] || 2
         | 
| 13 | 
            +
                    if(@servers.nil? or @servers.empty?)
         | 
| 14 | 
            +
                      @servers = [@jid.domain]
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  
         | 
| 18 | 
            +
                  def connect
         | 
| 19 | 
            +
                    retry_count = 0
         | 
| 20 | 
            +
                    server_to_use = nil
         | 
| 21 | 
            +
                    server_pool = @servers.dup.sort{ rand <=> rand }
         | 
| 22 | 
            +
                    begin
         | 
| 23 | 
            +
                      server_to_use = server_pool.shift
         | 
| 24 | 
            +
                      server_pool.push(server_to_use)
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      Jabber::debuglog "timeout will be: #{@retry_sleep.to_f}"
         | 
| 27 | 
            +
                      Timeout.timeout(@retry_sleep.to_f){
         | 
| 28 | 
            +
                        Jabber::debuglog "trying to connect to #{server_to_use}"
         | 
| 29 | 
            +
                        super(server_to_use, @port)
         | 
| 30 | 
            +
                      }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      Jabber::debuglog self.jid.to_s + " connected to " + server_to_use.to_s
         | 
| 33 | 
            +
                      Jabber::debuglog "out of possible servers " + @servers.inspect
         | 
| 34 | 
            +
                    rescue Exception, Timeout::Error => e
         | 
| 35 | 
            +
                      Jabber::warnlog "#{server_to_use} error: #{e.inspect}. Will attempt to reconnect in #{@retry_sleep}"
         | 
| 36 | 
            +
                      sleep(@retry_sleep.to_f)
         | 
| 37 | 
            +
                      if(retry_count >= @max_retry.to_i)
         | 
| 38 | 
            +
                        Jabber::warnlog "reached max retry count on exception, failing"
         | 
| 39 | 
            +
                        raise e
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                      retry_count += 1
         | 
| 42 | 
            +
                      retry
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                        
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                
         | 
| 48 | 
            +
                class Listener
         | 
| 49 | 
            +
                  def initialize(full_jid, password, config, &block)
         | 
| 50 | 
            +
                    @on_message_block = block
         | 
| 51 | 
            +
                    @full_jid = full_jid
         | 
| 52 | 
            +
                    @config = config
         | 
| 53 | 
            +
                    @password = password
         | 
| 54 | 
            +
                    @max_retry = config[:max_retry] || 30
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                  
         | 
| 57 | 
            +
                  def setup_connection
         | 
| 58 | 
            +
                    @connection = Connection.new(@full_jid, @config)
         | 
| 59 | 
            +
                    if @on_message_block
         | 
| 60 | 
            +
                      @connection.add_message_callback(&@on_message_block)
         | 
| 61 | 
            +
                    else
         | 
| 62 | 
            +
                      @connection.add_message_callback do |msg|
         | 
| 63 | 
            +
                        self.on_message(msg)
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                    
         | 
| 67 | 
            +
                    #We could just reconnect in @connection.on_exception, 
         | 
| 68 | 
            +
                    #but by raising into this seperate thread, we avoid growing our stack trace
         | 
| 69 | 
            +
                    @reconnection_thread = Thread.new do
         | 
| 70 | 
            +
                      first_run = true
         | 
| 71 | 
            +
                      begin
         | 
| 72 | 
            +
                        self.start unless first_run
         | 
| 73 | 
            +
                        loop do 
         | 
| 74 | 
            +
                          sleep(1)
         | 
| 75 | 
            +
                          Thread.pass
         | 
| 76 | 
            +
                        end
         | 
| 77 | 
            +
                      rescue => e
         | 
| 78 | 
            +
                        first_run = false
         | 
| 79 | 
            +
                        retry
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                    @exception_handlers = []
         | 
| 83 | 
            +
                    @connection.on_exception do |e, connection, where_failed|
         | 
| 84 | 
            +
                      self.run_exception_handlers(e, connection, where_failed)
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                  
         | 
| 88 | 
            +
                  def run_exception_handlers(e, connection, where_failed)
         | 
| 89 | 
            +
                    @exception_handlers.each do |ex_handler|
         | 
| 90 | 
            +
                      ex_handler.call(e, connection, where_failed)
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                    if where_failed == :sending
         | 
| 93 | 
            +
                      @message_to_send_on_reconnect = @message_now_sending
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                    if where_failed != :close && !@connection.is_connected?
         | 
| 96 | 
            +
                      @reconnection_thread.raise(e)
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                  
         | 
| 100 | 
            +
                  def add_exception_handler(&block)
         | 
| 101 | 
            +
                    @exception_handlers << block
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                  
         | 
| 104 | 
            +
                  def start
         | 
| 105 | 
            +
                    setup_connection unless @connection
         | 
| 106 | 
            +
                    connect
         | 
| 107 | 
            +
                    auth
         | 
| 108 | 
            +
                    send_presence
         | 
| 109 | 
            +
                    if @message_to_send_on_reconnect
         | 
| 110 | 
            +
                      send_message(@message_to_send_on_reconnect)
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
                    @message_to_send_on_reconnect = nil
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
                  
         | 
| 115 | 
            +
                  #Stop the listener. (close the connection)
         | 
| 116 | 
            +
                  def stop
         | 
| 117 | 
            +
                    @connection.close if @connection and @connection.is_connected?
         | 
| 118 | 
            +
                    @connection = nil
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                  
         | 
| 121 | 
            +
                  def connect
         | 
| 122 | 
            +
                    @connection.connect        
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                  
         | 
| 125 | 
            +
                  def auth
         | 
| 126 | 
            +
                    @connection.auth(@password)
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
                  
         | 
| 129 | 
            +
                  def send_presence
         | 
| 130 | 
            +
                    presence_message = @config[:presence_message]
         | 
| 131 | 
            +
                    if presence_message && !presence_message.empty?
         | 
| 132 | 
            +
                      @connection.send(Jabber::Presence.new.set_show(:chat).set_status(presence_message))
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                  
         | 
| 136 | 
            +
                  #TODO: test and fix situation where we get disconnected while sending but then successfully reconnect 
         | 
| 137 | 
            +
                  # (and make sure in such cases we resent)      
         | 
| 138 | 
            +
                  def send_message(message)
         | 
| 139 | 
            +
                    unless @connection
         | 
| 140 | 
            +
                      raise ::ArgumentError, "Can't send messages while listener is stopped.  Plase 'start' the listener first."
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
                    retry_count = 0
         | 
| 143 | 
            +
                    begin
         | 
| 144 | 
            +
                      while(not @connection.is_connected?)
         | 
| 145 | 
            +
                        #wait
         | 
| 146 | 
            +
                        Thread.pass
         | 
| 147 | 
            +
                      end
         | 
| 148 | 
            +
                      @message_now_sending = message
         | 
| 149 | 
            +
                      @connection.send(message)
         | 
| 150 | 
            +
                      return true #true, message was sent
         | 
| 151 | 
            +
                    rescue => e
         | 
| 152 | 
            +
                      if e.is_a?(Interrupt)
         | 
| 153 | 
            +
                        raise e
         | 
| 154 | 
            +
                      end
         | 
| 155 | 
            +
                      if(retry_count > @max_retry.to_i)
         | 
| 156 | 
            +
                        Jabber::debuglog "reached max retry count on message re-send, failing"
         | 
| 157 | 
            +
                        raise e
         | 
| 158 | 
            +
                      end
         | 
| 159 | 
            +
                      retry_count += 1
         | 
| 160 | 
            +
                      Jabber::debuglog "retrying message send.." + e.inspect
         | 
| 161 | 
            +
                      retry
         | 
| 162 | 
            +
                    end
         | 
| 163 | 
            +
                  end
         | 
| 164 | 
            +
                  
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
                
         | 
| 167 | 
            +
              end
         | 
| 168 | 
            +
            end
         | 
    
        data/lib/xmpp4r/rexmladdons.rb
    CHANGED
    
    | @@ -17,6 +17,12 @@ module REXML | |
| 17 17 | 
             
              # this class adds a few helper methods to REXML::Element
         | 
| 18 18 | 
             
              class Element
         | 
| 19 19 |  | 
| 20 | 
            +
                def each_elements(*els, &block)
         | 
| 21 | 
            +
                  els.inject([ ]) do |res, e|
         | 
| 22 | 
            +
                    res + each_element(e, &block)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 20 26 | 
             
                ##
         | 
| 21 27 | 
             
                # Replaces or adds a child element of name <tt>e</tt> with text <tt>t</tt>.
         | 
| 22 28 | 
             
                def replace_element_text(e, t)
         | 
| @@ -36,7 +36,7 @@ module Jabber | |
| 36 36 | 
             
                  # <b>Attention:</b> If you send presence and receive presences
         | 
| 37 37 | 
             
                  # before the roster has arrived, the Roster helper will let them
         | 
| 38 38 | 
             
                  # pass through and does *not* keep them!
         | 
| 39 | 
            -
                  def initialize(stream)
         | 
| 39 | 
            +
                  def initialize(stream, startnow = true)
         | 
| 40 40 | 
             
                    @stream = stream
         | 
| 41 41 | 
             
                    @items = {}
         | 
| 42 42 | 
             
                    @items_lock = Mutex.new
         | 
| @@ -66,10 +66,13 @@ module Jabber | |
| 66 66 | 
             
                        handle_presence(pres)
         | 
| 67 67 | 
             
                      end
         | 
| 68 68 | 
             
                    }
         | 
| 69 | 
            +
                    get_roster if startnow
         | 
| 70 | 
            +
                  end
         | 
| 69 71 |  | 
| 72 | 
            +
                  def get_roster
         | 
| 70 73 | 
             
                    # Request the roster
         | 
| 71 74 | 
             
                    rosterget = Iq.new_rosterget
         | 
| 72 | 
            -
                    stream.send(rosterget)
         | 
| 75 | 
            +
                    @stream.send(rosterget)
         | 
| 73 76 | 
             
                  end
         | 
| 74 77 |  | 
| 75 78 | 
             
                  ##
         | 
    
        data/lib/xmpp4r/sasl.rb
    CHANGED
    
    | @@ -122,7 +122,6 @@ module Jabber | |
| 122 122 | 
             
                    state = :key
         | 
| 123 123 | 
             
                    key = ''
         | 
| 124 124 | 
             
                    value = ''
         | 
| 125 | 
            -
             | 
| 126 125 | 
             
                    text.scan(/./) do |ch|
         | 
| 127 126 | 
             
                      if state == :key
         | 
| 128 127 | 
             
                        if ch == '='
         | 
| @@ -133,6 +132,9 @@ module Jabber | |
| 133 132 |  | 
| 134 133 | 
             
                      elsif state == :value
         | 
| 135 134 | 
             
                        if ch == ','
         | 
| 135 | 
            +
                          # due to our home-made parsing of the challenge, the key could have
         | 
| 136 | 
            +
                          # leading whitespace. strip it, or that would break jabberd2 support.
         | 
| 137 | 
            +
                          key = key.strip
         | 
| 136 138 | 
             
                          res[key] = value
         | 
| 137 139 | 
             
                          key = ''
         | 
| 138 140 | 
             
                          value = ''
         | 
| @@ -151,9 +153,12 @@ module Jabber | |
| 151 153 | 
             
                        end
         | 
| 152 154 | 
             
                      end
         | 
| 153 155 | 
             
                    end
         | 
| 156 | 
            +
                    # due to our home-made parsing of the challenge, the key could have
         | 
| 157 | 
            +
                    # leading whitespace. strip it, or that would break jabberd2 support.
         | 
| 158 | 
            +
                    key = key.strip
         | 
| 154 159 | 
             
                    res[key] = value unless key == ''
         | 
| 155 160 |  | 
| 156 | 
            -
                    Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text | 
| 161 | 
            +
                    Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text}\n#{res.inspect}")
         | 
| 157 162 |  | 
| 158 163 | 
             
                    res
         | 
| 159 164 | 
             
                  end
         | 
| @@ -172,7 +177,7 @@ module Jabber | |
| 172 177 | 
             
                    response['nc'] = '00000001'
         | 
| 173 178 | 
             
                    response['qop'] = 'auth'
         | 
| 174 179 | 
             
                    response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
         | 
| 175 | 
            -
                    response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
         | 
| 180 | 
            +
                    response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'], response['authzid'])
         | 
| 176 181 | 
             
                    response.each { |key,value|
         | 
| 177 182 | 
             
                      unless %w(nc qop response charset).include? key
         | 
| 178 183 | 
             
                        response[key] = "\"#{value}\""
         | 
| @@ -180,7 +185,7 @@ module Jabber | |
| 180 185 | 
             
                    }
         | 
| 181 186 |  | 
| 182 187 | 
             
                    response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
         | 
| 183 | 
            -
                    Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}")
         | 
| 188 | 
            +
                    Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}\n#{response.inspect}")
         | 
| 184 189 |  | 
| 185 190 | 
             
                    r = REXML::Element.new('response')
         | 
| 186 191 | 
             
                    r.add_namespace NS_SASL
         | 
| @@ -224,14 +229,20 @@ module Jabber | |
| 224 229 |  | 
| 225 230 | 
             
                  ##
         | 
| 226 231 | 
             
                  # Calculate the value for the response field
         | 
| 227 | 
            -
                  def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
         | 
| 232 | 
            +
                  def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop, authzid)
         | 
| 228 233 | 
             
                    a1_h = h("#{username}:#{realm}:#{passwd}")
         | 
| 229 234 | 
             
                    a1 = "#{a1_h}:#{nonce}:#{cnonce}"
         | 
| 230 | 
            -
                     | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 235 | 
            +
                    if authzid
         | 
| 236 | 
            +
                      a1 += ":#{authzid}"
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
                    if qop == 'auth-int' || qop == 'auth-conf'
         | 
| 239 | 
            +
                      a2 = "AUTHENTICATE:#{digest_uri}:00000000000000000000000000000000"
         | 
| 240 | 
            +
                    else
         | 
| 241 | 
            +
                      a2 = "AUTHENTICATE:#{digest_uri}"
         | 
| 242 | 
            +
                    end
         | 
| 233 243 | 
             
                    hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
         | 
| 234 244 | 
             
                  end
         | 
| 235 245 | 
             
                end
         | 
| 236 246 | 
             
              end
         | 
| 237 247 | 
             
            end
         | 
| 248 | 
            +
             | 
    
        data/lib/xmpp4r/stream.rb
    CHANGED
    
    | @@ -35,6 +35,9 @@ module Jabber | |
| 35 35 | 
             
                # connection status
         | 
| 36 36 | 
             
                attr_reader :status
         | 
| 37 37 |  | 
| 38 | 
            +
                # number of stanzas currently being processed
         | 
| 39 | 
            +
                attr_reader :processing
         | 
| 40 | 
            +
             | 
| 38 41 | 
             
                ##
         | 
| 39 42 | 
             
                # Initialize a new stream
         | 
| 40 43 | 
             
                def initialize
         | 
| @@ -48,12 +51,14 @@ module Jabber | |
| 48 51 | 
             
                  @send_lock = Mutex.new
         | 
| 49 52 | 
             
                  @last_send = Time.now
         | 
| 50 53 | 
             
                  @exception_block = nil
         | 
| 54 | 
            +
                  @tbcbmutex = Mutex.new
         | 
| 51 55 | 
             
                  @threadblocks = []
         | 
| 52 56 | 
             
                  @wakeup_thread = nil
         | 
| 53 57 | 
             
                  @streamid = nil
         | 
| 54 58 | 
             
                  @streamns = 'jabber:client'
         | 
| 55 59 | 
             
                  @features_sem = Semaphore.new
         | 
| 56 60 | 
             
                  @parser_thread = nil
         | 
| 61 | 
            +
                  @processing = 0
         | 
| 57 62 | 
             
                end
         | 
| 58 63 |  | 
| 59 64 | 
             
                ##
         | 
| @@ -76,7 +81,7 @@ module Jabber | |
| 76 81 | 
             
                        close!
         | 
| 77 82 | 
             
                      end
         | 
| 78 83 | 
             
                    rescue Exception => e
         | 
| 79 | 
            -
                      Jabber:: | 
| 84 | 
            +
                      Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
         | 
| 80 85 |  | 
| 81 86 | 
             
                      if @exception_block
         | 
| 82 87 | 
             
                        Thread.new do
         | 
| @@ -85,7 +90,7 @@ module Jabber | |
| 85 90 | 
             
                          @exception_block.call(e, self, :start)
         | 
| 86 91 | 
             
                        end
         | 
| 87 92 | 
             
                      else
         | 
| 88 | 
            -
                        Jabber:: | 
| 93 | 
            +
                        Jabber::warnlog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}"
         | 
| 89 94 | 
             
                        close!
         | 
| 90 95 | 
             
                        raise
         | 
| 91 96 | 
             
                      end
         | 
| @@ -116,7 +121,7 @@ module Jabber | |
| 116 121 | 
             
                ##
         | 
| 117 122 | 
             
                # This method is called by the parser when a failure occurs
         | 
| 118 123 | 
             
                def parse_failure(e)
         | 
| 119 | 
            -
                  Jabber:: | 
| 124 | 
            +
                  Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
         | 
| 120 125 |  | 
| 121 126 | 
             
                  # A new thread has to be created because close will cause the thread
         | 
| 122 127 | 
             
                  # to commit suicide(???)
         | 
| @@ -128,7 +133,7 @@ module Jabber | |
| 128 133 | 
             
                      @exception_block.call(e, self, :parser)
         | 
| 129 134 | 
             
                    end
         | 
| 130 135 | 
             
                  else
         | 
| 131 | 
            -
                    Jabber:: | 
| 136 | 
            +
                    Jabber::warnlog "Stream#parse_failure was called by XML parser. Dumping " +
         | 
| 132 137 | 
             
                      "backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}"
         | 
| 133 138 | 
             
                    close
         | 
| 134 139 | 
             
                    raise
         | 
| @@ -170,6 +175,7 @@ module Jabber | |
| 170 175 | 
             
                #
         | 
| 171 176 | 
             
                # element:: [REXML::Element] The received element
         | 
| 172 177 | 
             
                def receive(element)
         | 
| 178 | 
            +
                  @tbcbmutex.synchronize { @processing += 1 }
         | 
| 173 179 | 
             
                  Jabber::debuglog("RECEIVED:\n#{element.to_s}")
         | 
| 174 180 |  | 
| 175 181 | 
             
                  if element.namespace('').to_s == '' # REXML namespaces are always strings
         | 
| @@ -219,13 +225,21 @@ module Jabber | |
| 219 225 | 
             
                    end
         | 
| 220 226 | 
             
                  end
         | 
| 221 227 |  | 
| 228 | 
            +
                  if @xmlcbs.process(stanza)
         | 
| 229 | 
            +
                    @tbcbmutex.synchronize { @processing -= 1 }
         | 
| 230 | 
            +
                    return true
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
             | 
| 222 233 | 
             
                  # Iterate through blocked threads (= waiting for an answer)
         | 
| 223 234 | 
             
                  #
         | 
| 224 235 | 
             
                  # We're dup'ping the @threadblocks here, so that we won't end up in an
         | 
| 225 236 | 
             
                  # endless loop if Stream#send is being nested. That means, the nested
         | 
| 226 237 | 
             
                  # threadblock won't receive the stanza currently processed, but the next
         | 
| 227 238 | 
             
                  # one.
         | 
| 228 | 
            -
                  threadblocks =  | 
| 239 | 
            +
                  threadblocks = nil
         | 
| 240 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 241 | 
            +
                    threadblocks = @threadblocks.dup
         | 
| 242 | 
            +
                  end
         | 
| 229 243 | 
             
                  threadblocks.each { |threadblock|
         | 
| 230 244 | 
             
                    exception = nil
         | 
| 231 245 | 
             
                    r = false
         | 
| @@ -236,26 +250,68 @@ module Jabber | |
| 236 250 | 
             
                    end
         | 
| 237 251 |  | 
| 238 252 | 
             
                    if r == true
         | 
| 239 | 
            -
                      @ | 
| 253 | 
            +
                      @tbcbmutex.synchronize do
         | 
| 254 | 
            +
                        @threadblocks.delete(threadblock)
         | 
| 255 | 
            +
                      end
         | 
| 240 256 | 
             
                      threadblock.wakeup
         | 
| 241 | 
            -
                       | 
| 257 | 
            +
                      @tbcbmutex.synchronize { @processing -= 1 }
         | 
| 258 | 
            +
                      return true
         | 
| 242 259 | 
             
                    elsif exception
         | 
| 243 | 
            -
                      @ | 
| 260 | 
            +
                      @tbcbmutex.synchronize do
         | 
| 261 | 
            +
                        @threadblocks.delete(threadblock)
         | 
| 262 | 
            +
                      end
         | 
| 244 263 | 
             
                      threadblock.raise(exception)
         | 
| 245 264 | 
             
                    end
         | 
| 246 265 | 
             
                  }
         | 
| 247 266 |  | 
| 248 267 | 
             
                  Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})")
         | 
| 249 | 
            -
                   | 
| 250 | 
            -
                   | 
| 268 | 
            +
                  Jabber::debuglog("TRYING stanzacbs...")
         | 
| 269 | 
            +
                  if @stanzacbs.process(stanza)
         | 
| 270 | 
            +
                      @tbcbmutex.synchronize { @processing -= 1 }
         | 
| 271 | 
            +
                      return true
         | 
| 272 | 
            +
                  end
         | 
| 273 | 
            +
                  r = false
         | 
| 274 | 
            +
                  Jabber::debuglog("TRYING message/iq/presence/cbs...")
         | 
| 251 275 | 
             
                  case stanza
         | 
| 252 276 | 
             
                  when Message
         | 
| 253 | 
            -
                     | 
| 277 | 
            +
                    r = @messagecbs.process(stanza)
         | 
| 254 278 | 
             
                  when Iq
         | 
| 255 | 
            -
                     | 
| 279 | 
            +
                    r = @iqcbs.process(stanza)
         | 
| 256 280 | 
             
                  when Presence
         | 
| 257 | 
            -
                     | 
| 281 | 
            +
                    r = @presencecbs.process(stanza)
         | 
| 258 282 | 
             
                  end
         | 
| 283 | 
            +
                  @tbcbmutex.synchronize { @processing -= 1 }
         | 
| 284 | 
            +
                  return r
         | 
| 285 | 
            +
                end
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                ##
         | 
| 288 | 
            +
                # Get the list of iq callbacks.
         | 
| 289 | 
            +
                def iq_callbacks
         | 
| 290 | 
            +
                  @iqcbs
         | 
| 291 | 
            +
                end
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                ##
         | 
| 294 | 
            +
                # Get the list of message callbacks.
         | 
| 295 | 
            +
                def message_callbacks
         | 
| 296 | 
            +
                  @messagecbs
         | 
| 297 | 
            +
                end
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                ##
         | 
| 300 | 
            +
                # Get the list of presence callbacks.
         | 
| 301 | 
            +
                def presence_callbacks
         | 
| 302 | 
            +
                  @presencecbs
         | 
| 303 | 
            +
                end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                ##
         | 
| 306 | 
            +
                # Get the list of stanza callbacks.
         | 
| 307 | 
            +
                def stanza_callbacks
         | 
| 308 | 
            +
                  @stanzacbs
         | 
| 309 | 
            +
                end
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                ##
         | 
| 312 | 
            +
                # Get the list of xml callbacks.
         | 
| 313 | 
            +
                def xml_callbacks
         | 
| 314 | 
            +
                  @xmlcbs
         | 
| 259 315 | 
             
                end
         | 
| 260 316 |  | 
| 261 317 | 
             
                ##
         | 
| @@ -276,7 +332,6 @@ module Jabber | |
| 276 332 | 
             
                    raise @exception if @exception
         | 
| 277 333 | 
             
                  end
         | 
| 278 334 | 
             
                  def wakeup
         | 
| 279 | 
            -
                    # TODO: Handle threadblock removal if !alive?
         | 
| 280 335 | 
             
                    @waiter.run
         | 
| 281 336 | 
             
                  end
         | 
| 282 337 | 
             
                  def raise(exception)
         | 
| @@ -305,11 +360,17 @@ module Jabber | |
| 305 360 | 
             
                # &block:: [Block] The optional block
         | 
| 306 361 | 
             
                def send(xml, &block)
         | 
| 307 362 | 
             
                  Jabber::debuglog("SENDING:\n#{xml}")
         | 
| 308 | 
            -
                   | 
| 363 | 
            +
                  if block
         | 
| 364 | 
            +
                    threadblock = ThreadBlock.new(block)
         | 
| 365 | 
            +
                    @tbcbmutex.synchronize do
         | 
| 366 | 
            +
                      @threadblocks.unshift(threadblock)
         | 
| 367 | 
            +
                    end
         | 
| 368 | 
            +
                  end
         | 
| 309 369 | 
             
                  begin
         | 
| 310 370 | 
             
                    # Temporarily remove stanza's namespace to
         | 
| 311 371 | 
             
                    # reduce bandwidth consumption
         | 
| 312 | 
            -
                    if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client'
         | 
| 372 | 
            +
                    if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client' and
         | 
| 373 | 
            +
                        xml.prefix != 'stream' and xml.name != 'stream'
         | 
| 313 374 | 
             
                      xml.delete_namespace
         | 
| 314 375 | 
             
                      send_data(xml.to_s)
         | 
| 315 376 | 
             
                      xml.add_namespace(@streamns)
         | 
| @@ -317,7 +378,7 @@ module Jabber | |
| 317 378 | 
             
                      send_data(xml.to_s)
         | 
| 318 379 | 
             
                    end
         | 
| 319 380 | 
             
                  rescue Exception => e
         | 
| 320 | 
            -
                    Jabber:: | 
| 381 | 
            +
                    Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
         | 
| 321 382 |  | 
| 322 383 | 
             
                    if @exception_block
         | 
| 323 384 | 
             
                      Thread.new do
         | 
| @@ -326,7 +387,7 @@ module Jabber | |
| 326 387 | 
             
                        @exception_block.call(e, self, :sending)
         | 
| 327 388 | 
             
                      end
         | 
| 328 389 | 
             
                    else
         | 
| 329 | 
            -
                      Jabber:: | 
| 390 | 
            +
                      Jabber::warnlog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}"
         | 
| 330 391 | 
             
                      close!
         | 
| 331 392 | 
             
                      raise
         | 
| 332 393 | 
             
                    end
         | 
| @@ -336,7 +397,7 @@ module Jabber | |
| 336 397 | 
             
                  if block and Thread.current != @parser_thread
         | 
| 337 398 | 
             
                    threadblock.wait
         | 
| 338 399 | 
             
                  elsif block
         | 
| 339 | 
            -
                    Jabber:: | 
| 400 | 
            +
                    Jabber::warnlog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!")
         | 
| 340 401 | 
             
                  end
         | 
| 341 402 | 
             
                end
         | 
| 342 403 |  | 
| @@ -393,13 +454,17 @@ module Jabber | |
| 393 454 | 
             
                end
         | 
| 394 455 |  | 
| 395 456 | 
             
                ##
         | 
| 396 | 
            -
                # Adds a callback block to process received XML messages
         | 
| 457 | 
            +
                # Adds a callback block to process received XML messages, these
         | 
| 458 | 
            +
                # will be handled before any blocks given to Stream#send or other
         | 
| 459 | 
            +
                # callbacks.
         | 
| 397 460 | 
             
                #
         | 
| 398 461 | 
             
                # priority:: [Integer] The callback's priority, the higher, the sooner
         | 
| 399 462 | 
             
                # ref:: [String] The callback's reference
         | 
| 400 463 | 
             
                # &block:: [Block] The optional block
         | 
| 401 464 | 
             
                def add_xml_callback(priority = 0, ref = nil, &block)
         | 
| 402 | 
            -
                  @ | 
| 465 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 466 | 
            +
                    @xmlcbs.add(priority, ref, block)
         | 
| 467 | 
            +
                  end
         | 
| 403 468 | 
             
                end
         | 
| 404 469 |  | 
| 405 470 | 
             
                ##
         | 
| @@ -407,7 +472,9 @@ module Jabber | |
| 407 472 | 
             
                #
         | 
| 408 473 | 
             
                # ref:: [String] The reference of the callback to delete
         | 
| 409 474 | 
             
                def delete_xml_callback(ref)
         | 
| 410 | 
            -
                  @ | 
| 475 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 476 | 
            +
                    @xmlcbs.delete(ref)
         | 
| 477 | 
            +
                  end
         | 
| 411 478 | 
             
                end
         | 
| 412 479 |  | 
| 413 480 | 
             
                ##
         | 
| @@ -417,7 +484,9 @@ module Jabber | |
| 417 484 | 
             
                # ref:: [String] The callback's reference
         | 
| 418 485 | 
             
                # &block:: [Block] The optional block
         | 
| 419 486 | 
             
                def add_message_callback(priority = 0, ref = nil, &block)
         | 
| 420 | 
            -
                  @ | 
| 487 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 488 | 
            +
                    @messagecbs.add(priority, ref, block)
         | 
| 489 | 
            +
                  end
         | 
| 421 490 | 
             
                end
         | 
| 422 491 |  | 
| 423 492 | 
             
                ##
         | 
| @@ -425,7 +494,9 @@ module Jabber | |
| 425 494 | 
             
                #
         | 
| 426 495 | 
             
                # ref:: [String] The reference of the callback to delete
         | 
| 427 496 | 
             
                def delete_message_callback(ref)
         | 
| 428 | 
            -
                  @ | 
| 497 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 498 | 
            +
                    @messagecbs.delete(ref)
         | 
| 499 | 
            +
                  end
         | 
| 429 500 | 
             
                end
         | 
| 430 501 |  | 
| 431 502 | 
             
                ##
         | 
| @@ -435,7 +506,9 @@ module Jabber | |
| 435 506 | 
             
                # ref:: [String] The callback's reference
         | 
| 436 507 | 
             
                # &block:: [Block] The optional block
         | 
| 437 508 | 
             
                def add_stanza_callback(priority = 0, ref = nil, &block)
         | 
| 438 | 
            -
                  @ | 
| 509 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 510 | 
            +
                    @stanzacbs.add(priority, ref, block)
         | 
| 511 | 
            +
                  end
         | 
| 439 512 | 
             
                end
         | 
| 440 513 |  | 
| 441 514 | 
             
                ##
         | 
| @@ -443,7 +516,9 @@ module Jabber | |
| 443 516 | 
             
                #
         | 
| 444 517 | 
             
                # ref:: [String] The reference of the callback to delete
         | 
| 445 518 | 
             
                def delete_stanza_callback(ref)
         | 
| 446 | 
            -
                  @ | 
| 519 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 520 | 
            +
                    @stanzacbs.delete(ref)
         | 
| 521 | 
            +
                  end
         | 
| 447 522 | 
             
                end
         | 
| 448 523 |  | 
| 449 524 | 
             
                ##
         | 
| @@ -453,7 +528,9 @@ module Jabber | |
| 453 528 | 
             
                # ref:: [String] The callback's reference
         | 
| 454 529 | 
             
                # &block:: [Block] The optional block
         | 
| 455 530 | 
             
                def add_presence_callback(priority = 0, ref = nil, &block)
         | 
| 456 | 
            -
                  @ | 
| 531 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 532 | 
            +
                    @presencecbs.add(priority, ref, block)
         | 
| 533 | 
            +
                  end
         | 
| 457 534 | 
             
                end
         | 
| 458 535 |  | 
| 459 536 | 
             
                ##
         | 
| @@ -461,7 +538,9 @@ module Jabber | |
| 461 538 | 
             
                #
         | 
| 462 539 | 
             
                # ref:: [String] The reference of the callback to delete
         | 
| 463 540 | 
             
                def delete_presence_callback(ref)
         | 
| 464 | 
            -
                  @ | 
| 541 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 542 | 
            +
                    @presencecbs.delete(ref)
         | 
| 543 | 
            +
                  end
         | 
| 465 544 | 
             
                end
         | 
| 466 545 |  | 
| 467 546 | 
             
                ##
         | 
| @@ -471,7 +550,9 @@ module Jabber | |
| 471 550 | 
             
                # ref:: [String] The callback's reference
         | 
| 472 551 | 
             
                # &block:: [Block] The optional block
         | 
| 473 552 | 
             
                def add_iq_callback(priority = 0, ref = nil, &block)
         | 
| 474 | 
            -
                  @ | 
| 553 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 554 | 
            +
                    @iqcbs.add(priority, ref, block)
         | 
| 555 | 
            +
                  end
         | 
| 475 556 | 
             
                end
         | 
| 476 557 |  | 
| 477 558 | 
             
                ##
         | 
| @@ -480,7 +561,9 @@ module Jabber | |
| 480 561 | 
             
                # ref:: [String] The reference of the callback to delete
         | 
| 481 562 | 
             
                #
         | 
| 482 563 | 
             
                def delete_iq_callback(ref)
         | 
| 483 | 
            -
                  @ | 
| 564 | 
            +
                  @tbcbmutex.synchronize do
         | 
| 565 | 
            +
                    @iqcbs.delete(ref)
         | 
| 566 | 
            +
                  end
         | 
| 484 567 | 
             
                end
         | 
| 485 568 | 
             
                ##
         | 
| 486 569 | 
             
                # Closes the connection to the Jabber service
         | 
| @@ -489,9 +572,28 @@ module Jabber | |
| 489 572 | 
             
                end
         | 
| 490 573 |  | 
| 491 574 | 
             
                def close!
         | 
| 492 | 
            -
                   | 
| 575 | 
            +
                  pr = 1
         | 
| 576 | 
            +
                  n = 0
         | 
| 577 | 
            +
                  # In some cases, we might lost count of some stanzas
         | 
| 578 | 
            +
                  # (for example, if the handler raises an exception)
         | 
| 579 | 
            +
                  # so we can't block forever.
         | 
| 580 | 
            +
                  while pr > 0 and n <= 1000
         | 
| 581 | 
            +
                    @tbcbmutex.synchronize { pr = @processing }
         | 
| 582 | 
            +
                    if pr > 0
         | 
| 583 | 
            +
                      n += 1
         | 
| 584 | 
            +
                      Jabber::debuglog("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
         | 
| 585 | 
            +
                      #puts("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
         | 
| 586 | 
            +
                      Thread::pass
         | 
| 587 | 
            +
                    end
         | 
| 588 | 
            +
                  end
         | 
| 589 | 
            +
             | 
| 590 | 
            +
                  # Order Matters here! If this method is called from within 
         | 
| 591 | 
            +
                  # @parser_thread then killing @parser_thread first would 
         | 
| 592 | 
            +
                  # mean the other parts of the method fail to execute. 
         | 
| 593 | 
            +
                  # That would be bad. So kill parser_thread last
         | 
| 493 594 | 
             
                  @fd.close if @fd and !@fd.closed?
         | 
| 494 595 | 
             
                  @status = DISCONNECTED
         | 
| 596 | 
            +
                  @parser_thread.kill if @parser_thread
         | 
| 495 597 | 
             
                end
         | 
| 496 598 | 
             
              end
         | 
| 497 599 | 
             
            end
         |