xmpp4r-0.5.6/0000755000175000017500000000000013647121573014205 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/0000755000175000017500000000000013647121573015116 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/0000755000175000017500000000000013647121573015663 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/0000755000175000017500000000000013647121573017115 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/0000755000175000017500000000000013647121573020733 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/0000755000175000017500000000000013647121573022500 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/sendfile.rb0000644000175000017500000000373513647121573024626 0ustar debbiecocoadebbiecocoarequire 'xmpp4r' require 'xmpp4r/bytestreams' require 'yaml' Jabber::debug = true if ARGV.size != 3 puts "Usage: #{$0} " exit end conf = YAML::load File.new('sendfile.conf') cl = Jabber::Client.new(Jabber::JID.new(conf['jabber']['jid'])) puts "Connecting Jabber client..." cl.connect puts "Authenticating..." cl.auth conf['jabber']['password'] puts "Starting local Bytestreams server" bss = Jabber::Bytestreams::SOCKS5BytestreamsServer.new(conf['local']['port']) conf['local']['addresses'].each { |address| bss.add_address(address) } ft = Jabber::FileTransfer::Helper.new(cl) #ft.allow_bytestreams = false source = Jabber::FileTransfer::FileSource.new(ARGV[2]) puts "Offering #{source.filename} to #{ARGV[0]}" stream = ft.offer(Jabber::JID.new(ARGV[0]), source, ARGV[1]) if stream puts "Starting stream initialization (#{stream.class})" if stream.kind_of? Jabber::Bytestreams::SOCKS5BytestreamsInitiator stream.add_streamhost(bss) (conf['proxies'] || []).each { |proxy| puts "Querying proxy #{proxy}" stream.add_streamhost proxy } puts "Offering streamhosts " + stream.streamhosts.collect { |sh| sh.jid }.join(' ') stream.add_streamhost_callback { |streamhost,state,e| case state when :connecting puts "Connecting to #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :success puts "Successfully using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :failure puts "Error using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port}): #{e}" end } end stream.open if stream.kind_of? Jabber::Bytestreams::SOCKS5BytestreamsInitiator puts "Using streamhost #{stream.streamhost_used.jid} (#{stream.streamhost_used.host}:#{stream.streamhost_used.port})" end while buf = source.read print "." $stdout.flush stream.write buf stream.flush end puts "!" stream.close else puts "Peer declined" end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/fileserve.conf0000644000175000017500000000031413647121573025331 0ustar debbiecocoadebbiecocoajabber: jid: user@server.com/fileserve password: *** directory: /tmp/fileserve socks: proxies: - transfer.jabber.freenet.de - proxy.jabber.org addresses: - 172.22.99.197 port: 65023 xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/xmpping.rb0000644000175000017500000000677413647121573024525 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby # =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # This is PING for Jabber # # Please customize your ~/.xmppingrc require 'xmpp4r' require 'xmpp4r/httpbinding' require 'xmpp4r/version/iq/version' require 'xmpp4r/discovery/iq/discoinfo' require 'optparse' require 'yaml' require 'thread' ## # Options ## interval = 5 jid = nil conf_filename = "#{ENV['HOME']}/.xmppingrc" accountname = 'default' OptionParser.new { |opts| opts.banner = 'Usage: xmpping.rb [-d] [-a ACCOUNT] [-c FILENAME] [-i SECONDS] -t ' opts.separator 'Ping a destination JID with various stanzas' opts.on('-t', '--to JID', 'Destionation Jabber-ID') { |j| jid = Jabber::JID.new(j) } opts.on('-a', '--account ACCOUNT', 'Account tag to use (default: default)') { |a| accountname = a } opts.on('-c', '--config FILENAME', 'Configuration file (default: ~/.xmppingrc)') { |c| conf_filename = c } opts.on('-i', '--interval SECONDS', 'Wait SECONDS between each stanza (default: 5)') { |sec| interval = sec.to_i } opts.on('-d', '--debug', 'Enable XMPP4R debugging (print stanzas)') { Jabber::debug = true } opts.on_tail('-h', '--help', 'Show help') { puts opts exit } opts.parse!(ARGV) unless jid puts opts exit end } ## # Configuration ## begin conf_file = File.new(conf_filename) rescue Exception => e puts "Unable to open config file: #{e.to_s}" exit end conf = YAML::load(conf_file) unless conf puts "#{conf_filename} is no valid YAML document" exit end account = conf[accountname] unless account puts "Account #{accountname} not found in #{conf_filename}" exit end ## # Connection ## if account['http bind'] cl = Jabber::HTTPBinding::Client.new(Jabber::JID.new(account['jid'])) cl.connect(account['http bind']) else cl = Jabber::Client.new(Jabber::JID.new(account['jid'])) cl.connect(account['host'], (account['port'] ? account['port'].to_i : 5222)) end cl.auth(account['password']) ## # Reply printer ## def print_reply(iq, roundtrip) roundtrip_s = ((roundtrip * 100).round / 100.0).to_s + " sec" output = "Received a #{iq.query.namespace} #{iq.type} (id: #{iq.id}) from #{iq.from} (#{roundtrip_s}): " if iq.query.kind_of?(Jabber::Version::IqQueryVersion) output += "#{iq.query.iname}-#{iq.query.version} #{iq.query.os}" elsif iq.query.namespace == 'jabber:iq:time' output += "#{iq.query.first_element_text('display')} (#{iq.query.first_element_text('tz')})" elsif iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoInfo) identity = iq.query.identity if identity output += "#{identity.iname} (#{identity.category} #{identity.type})" else output += " missing" end else output += iq.query.to_s end puts output end ## # Main loop ## require 'mprofiler' MemoryProfiler.start(:string_debug=>true) puts "XMPPING #{cl.jid} -> #{jid}" query_methods = ['jabber:iq:version', 'jabber:iq:time', 'http://jabber.org/protocol/disco#info'] query_method = 0 loop { Thread.new { iq = Jabber::Iq.new_query(:get, jid) iq.query.add_namespace(query_methods[query_method]) time1 = Time.new begin cl.send_with_id(iq) { |reply| print_reply(reply, Time.new - time1) true } rescue Jabber::ServerError => e puts "Error for #{iq.query.namespace} to #{iq.to}: #{e.error.to_s.inspect}" end } query_method += 1 if query_method >= query_methods.size query_method = 0 end sleep(interval) } xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb0000644000175000017500000002136613647121573025526 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby require 'gtk2' $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/muc/helper/simplemucclient' require 'xmpp4r/version/helper/simpleresponder' #Jabber::debug = true class Gtk::Label def self.new_show(str = nil, mnemonic = false) label = new(str, mnemonic) label.show label end end class SetupWindow < Gtk::Window def initialize super(Gtk::Window::TOPLEVEL) self.title = "GtkMUCClient setup" signal_connect("delete_event") { cancel } vbox = Gtk::VBox.new vbox.set_border_width(4) add(vbox) vbox.show frame1 = Gtk::Frame.new('Jabber Account Settings') frame1.set_border_width(4) frame1.show vbox.add(frame1) layout1 = Gtk::Table.new(4, 2) layout1.row_spacings = 4 layout1.column_spacings = 8 layout1.show frame1.add(layout1) layout1.attach(Gtk::Label.new_show('Jabber ID:'), 0, 1, 1, 2) @entry_jid = Gtk::Entry.new @entry_jid.text = 'collector@jabber.ccc.de' @entry_jid.show layout1.attach(@entry_jid, 1, 2, 1, 2) layout1.attach(Gtk::Label.new_show('Password:'), 0, 1, 2, 3) @entry_password = Gtk::Entry.new @entry_password.visibility = false @entry_password.show layout1.attach(@entry_password, 1, 2, 2, 3) layout1.attach(Gtk::Label.new_show('Resource:'), 0, 1, 3, 4) @entry_resource = Gtk::Entry.new @entry_resource.text = 'gtkmucclient' @entry_resource.show layout1.attach(@entry_resource, 1, 2, 3, 4) frame2 = Gtk::Frame.new('Multi-User Chat Settings') frame2.set_border_width(4) frame2.show vbox.add(frame2) layout2 = Gtk::Table.new(3, 2) layout2.row_spacings = 4 layout2.column_spacings = 8 layout2.show frame2.add(layout2) layout2.attach(Gtk::Label.new_show('Room:'), 0, 1, 1, 2) @entry_room = Gtk::Entry.new @entry_room.text = 'test@conference.jabber.org' @entry_room.show layout2.attach(@entry_room, 1, 2, 1, 2) layout2.attach(Gtk::Label.new_show('Nick:'), 0, 1, 2, 3) @entry_nick = Gtk::Entry.new @entry_nick.text = 'XMPP4R-Fan' @entry_nick.show layout2.attach(@entry_nick, 1, 2, 2, 3) hbox = Gtk::HBox.new hbox.show vbox.add(hbox) button_ok = Gtk::Button.new("Ok") button_ok.set_border_width(4) hbox.add(button_ok) button_ok.signal_connect("clicked") { ok } button_ok.can_default = true button_ok.grab_default button_ok.show button_cancel = Gtk::Button.new("Cancel") button_cancel.set_border_width(4) hbox.add(button_cancel) button_cancel.signal_connect("clicked") { cancel } button_cancel.show end def error_dialog(msg) dialog = Gtk::MessageDialog.new(self, Gtk::Dialog::MODAL, Gtk::MessageDialog::ERROR, Gtk::MessageDialog::BUTTONS_OK, msg) dialog.signal_connect("response") { dialog.destroy } dialog.run end def ok jid = Jabber::JID.new(@entry_jid.text) mucjid = Jabber::JID.new(@entry_room.text) if jid.node.nil? error_dialog("Your Jabber ID must contain a user name and therefore contain one @ character.") elsif jid.resource error_dialog("If you intend to set a custom resource, put that in the right text field. Remove the slash!") elsif @entry_resource.text.empty? error_dialog("Please set a resource. This is a somewhat unimportant setting...") elsif mucjid.node.nil? error_dialog("Please set a room name, e.g. myroom@conference.jabber.org") elsif mucjid.resource error_dialog("The MUC room must not contain a resource. Remove the slash!") elsif @entry_nick.text.empty? error_dialog("Please set a nick for MUC.") else jid.resource = @entry_resource.text mucjid.resource = @entry_nick.text password = @entry_password.text destroy ChatWindow.new(jid, password, mucjid).show end end def cancel destroy Gtk.main_quit end end class ChatWindow < Gtk::Window def initialize(jid, password, mucjid) super(Gtk::Window::TOPLEVEL) self.title = "GtkMUCClient: #{mucjid.resource} in #{mucjid.strip}" signal_connect("delete_event") { destroy; Gtk.main_quit } layout = Gtk::VBox.new @topic = Gtk::Entry.new @topic.editable = false @topic.show layout.pack_start(@topic, false, false, 2) layout_mid = Gtk::HPaned.new layout_mid.position = 500 layout_mid.show layout.pack_start(layout_mid) @buffer_scroll = Gtk::ScrolledWindow.new @buffer_scroll.show @buffer_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) layout_mid.pack1(@buffer_scroll, true, true) @buffer_view = Gtk::TextView.new @buffer_view.editable = false @buffer_view.cursor_visible = false @buffer_view.wrap_mode = Gtk::TextTag::WRAP_WORD @buffer_view.modify_font(Pango::FontDescription.new('monospace 12')) @buffer_view.show @buffer_scroll.add_with_viewport(@buffer_view) roster_scroll = Gtk::ScrolledWindow.new roster_scroll.show roster_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) layout_mid.pack2(roster_scroll, true, true) @roster = Gtk::ListStore.new(String) @roster.set_sort_column_id(0) roster_view = Gtk::TreeView.new(@roster) roster_view.append_column Gtk::TreeViewColumn.new("Participant", Gtk::CellRendererText.new, {:text => 0}) roster_view.show roster_scroll.add_with_viewport(roster_view) @input = Gtk::Entry.new @input.grab_focus @input.signal_connect("activate") { on_input(@input.text) @input.text = '' } @input.show layout.pack_start(@input, false, false, 2) layout.show add(layout) print_buffer "Welcome to the XMPP4R sample GTK2 Multi-User Chat client" print_buffer "Commands start with a slash, type \"/help\" for a list" @client = Jabber::Client.new(jid) Jabber::Version::SimpleResponder.new(@client, "XMPP4R example: GtkMUCClient", Jabber::XMPP4R_VERSION, IO.popen("uname -sr").readlines.to_s.strip) Thread.new { begin print_buffer "Connecting for domain #{jid.domain}..." @client.connect print_buffer "Authenticating for #{jid.strip}..." @client.auth(password) print_buffer "Attempting to join #{mucjid.strip} as #{mucjid.resource}..." @muc = Jabber::MUC::SimpleMUCClient.new(@client) register_handlers @muc.join(mucjid) rescue Exception => e puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" print_buffer("Error: #{e}") end } set_size_request(600, 400) end def print_buffer(s, time=nil) @buffer_view.buffer.insert(@buffer_view.buffer.end_iter, "[#{(time || Time.new).getlocal.strftime('%I:%M')}] #{s}\n") va = @buffer_scroll.vadjustment va.value = va.upper end def register_handlers @muc.on_room_message { |time,text| print_buffer("*** #{text}", time) } @muc.on_message { |time,nick,text| if text =~ /^\/me (.+)$/ print_buffer("*#{nick} #{$1}", time) else print_buffer("<#{nick}> #{text}", time) end } @muc.on_private_message { |time,nick,text| print_buffer("<-(#{nick}) #{text}", time) } @muc.on_join { |time,nick| @roster.append[0] = nick } @muc.on_self_leave { |time| print_buffer("You have exited the room", time) } @muc.on_leave { |time,nick| del_iter = nil @roster.each { |m,p,iter| del_iter = iter if iter[0] == nick } @roster.remove(del_iter) if del_iter } @muc.on_subject { |time,nick,subject| @topic.text = subject } end def on_input(line) commands = { 'help' => [ 'Display this help', lambda { commands.each { |cmd,a| print_buffer "/#{cmd.ljust(10)} - #{a[0]}" } } ], 'msg' => [ 'Send a private message to a user', lambda { |args| # Limitation: it is not possible to send private messages # to a user with a space in his nickname to = args.shift text = args.join(' ') @muc.say(text, to) print_buffer "->(#{to}) #{text}" } ], 'subject' => [ 'Change the room\'s subject', lambda { |args| @muc.subject = args.join(' ') } ], 'quit' => [ 'Leave room with optional message, then disconnect client and shut down', lambda { |args| @muc.exit(args.join(' ')) if @muc.active? @client.close Gtk.main_quit } ] } if line =~ /^\// args = line.split(/ /) cmd = args.shift[1..-1].downcase command = commands[cmd] if command help, func = command func.call(args) else print_buffer "Unknown command: #{cmd}, use \"/help\"" end else @muc.say(line) end end end Gtk.init SetupWindow.new.show Gtk.main xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/pep-aggregator/0000755000175000017500000000000013647121573025404 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/pep-aggregator/index.xsl0000644000175000017500000001443713647121573027254 0ustar debbiecocoadebbiecocoa PEP aggregator

PEP aggregator

This aggregator consumes events by the XMPP extension Personal Eventing via Pubsub. If you want to participate you'll need a PEP-enabled server (ejabberd), client (Gajim or Psi) and in your Jabber roster. The source code can be viewed online.

Stopped listening

♫ Listening

☻ Feeling ()

↻ Doing ()

Stopped chatting

Chatting at

Stopped browsing

Browsing

Stopped gaming

Playing as on

Stopped watching a video

✇ Watching

xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/pep-aggregator/pep-aggregator.rb0000644000175000017500000000703613647121573030643 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby require 'xmpp4r/framework/bot' require 'xmpp4r/pubsub' require 'xmpp4r/vcard' require 'ramaze' if ARGV.size < 3 puts "Usage: #{$0} [Status message]" exit end HTTP_PORT = ARGV.shift.to_i JID = ARGV.shift PASSWORD = ARGV.shift STATUS_MESSAGE = ARGV.shift def xml_namespaces(filename, excludes=[]) namespaces_recursive = nil namespaces_recursive = lambda { |element| namespaces = element.namespaces element.each_element { |child| namespaces.merge!(namespaces_recursive.call(child)) } namespaces } root = REXML::Document.new(File.new(filename)).root all = namespaces_recursive.call(root) res = {} all.each { |prefix,uri| res[prefix] = uri unless excludes.include? prefix } res end Jabber::debug = true class VcardCache < Jabber::Vcard::Helper attr_reader :vcards def initialize(stream) super @vcards = {} end def get(jid) unless @vcards[jid] begin @vcards[jid] = super rescue Jabber::ServerError @vcards[jid] = :error end end @vcards[jid] end def get_until(jid, timeout=10) begin Timeout::timeout(timeout) { get(jid) } rescue Timeout::Error @vcards[jid] = :timeout end @vcards[jid] end end MAX_ITEMS = 50 $jid_items = [] $bot = Jabber::Framework::Bot.new(JID, PASSWORD) class << $bot def accept_subscription_from?(jid) roster.add(jid, nil, true) true end end $vcards = VcardCache.new($bot.stream) xml_namespaces('index.xsl', %w(xmlns xsl pa j p)).each { |prefix,node| $bot.add_pep_notification(node) do |from,item| from.strip! item.add_namespace(Jabber::PubSub::NS_PUBSUB) item.attributes['node'] = node is_duplicate = false $jid_items.each { |jid1,item1| if jid1.to_s == from.to_s and node == item1.attributes['node'] is_duplicate = (item.to_s == item1.to_s) break end } unless is_duplicate $jid_items.unshift([from, item]) $jid_items = $jid_items[0..(MAX_ITEMS-1)] end end } $bot.set_presence(nil, STATUS_MESSAGE || "Busy aggregating PEP events...") class WebController < Ramaze::Controller map '/' template_root __DIR__ engine :XSLT def index "" + "" + $jid_items.collect do |jid,item_orig| item = item_orig.deep_clone item.attributes['jabber:from'] = jid.to_s vcard = $vcards.get_until(jid) if vcard.kind_of? Jabber::Vcard::IqVcard item.attributes['jabber:from-name'] = vcard['NICKNAME'] || vcard['FN'] || jid.node item.attributes['jabber:has-avatar'] = (vcard['PHOTO/TYPE'] and vcard['PHOTO/BINVAL']) ? 'true' : 'false' else item.attributes['jabber:from-name'] = jid.node item.attributes['jabber:has-avatar'] = 'false' end item end.join + "" end def avatar(jid) trait :engine => :None vcard = $vcards.get_until(jid) if vcard.kind_of? Jabber::Vcard::IqVcard if vcard['PHOTO/TYPE'] and vcard.photo_binval response['Content-Type'] = vcard['PHOTO/TYPE'] response.body = vcard.photo_binval else response['Status'] = 404 end else response['Status'] = 404 end throw :respond end end Ramaze::start(:port => HTTP_PORT) xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/shellmgr/0000755000175000017500000000000013647121573024315 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb0000755000175000017500000000016013647121573027516 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby -w require 'shellmgr' sh = Shell.new { |s| puts s } while true do l = gets sh.puts l end xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb0000644000175000017500000000211213647121573026453 0ustar debbiecocoadebbiecocoarequire 'thread' class Shell PROMPT = "_-=READY=-_" def initialize(shell = "/bin/bash", delay = 1, &block) @cb = block @shell = shell @delay = delay @parent_to_child_read, @parent_to_child_write = IO.pipe @child_to_parent_read, @child_to_parent_write = IO.pipe @child_pid = fork do @parent_to_child_write.close @child_to_parent_read.close $stdin.reopen(@parent_to_child_read) $stdout.reopen(@child_to_parent_write) $stderr.reopen(@child_to_parent_write) exec("/bin/bash") end buffer = "" semaphore = Mutex.new Thread.new do while true c = @child_to_parent_read.read(1) semaphore.synchronize { buffer += c } end end Thread.new do ch = "" while true do sleep @delay semaphore.synchronize { if buffer == ch and ch != "" @cb.call buffer buffer = "" end ch = buffer } end end end def puts(str) @parent_to_child_write.puts(str) end def kill @child_pid.kill end end xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb0000755000175000017500000000204013647121573027763 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby -w $:.unshift '../../lib' require 'shellmgr' require 'xmpp4r' include Jabber if ARGV.length != 3 puts "usage: ./shellmgr_jabber.rb jid_to_use password jid_to_authorize" exit(1) end myjid = JID.new(ARGV[0]) mypassword = ARGV[1] authjid = JID.new(ARGV[2]) myjid = JID.new(myjid.node, myjid.domain, 'RSM') cl = Client.new(myjid) cl.connect cl.auth(mypassword) mainthread = Thread.current sh = Shell.new { |str| puts "-----RECEIVING-----\n#{str}" cl.send(Message.new(authjid, str)) } cl.add_message_callback do |m| if JID.new(m.from).strip.to_s != authjid.strip.to_s puts "Received message from non-authorized user #{m.from}" else if m.body == "killshell" cl.send(Message.new(authjid, "Exiting...")) mainthread.wakeup else puts "-----EXECUTING-----\n#{m.body}" sh.puts(m.body) end end end cl.send(Presence.new) puts "Connected ! Ask #{authjid.to_s} to send commands to #{myjid.to_s}" cl.send(Message.new(authjid, "I'm ready to receive commands from you.")) Thread.stop cl.close sh.kill xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/sendfile.conf0000644000175000017500000000025213647121573025137 0ustar debbiecocoadebbiecocoa--- jabber: jid: user@server/resource password: password local: port: 65010 addresses: [172.22.16.3] proxies: - proxy.jabber.org - transfer.jabber.freenet.de xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/minimuc.rb0000644000175000017500000001540313647121573024471 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/iq/query/discoinfo' require 'xmpp4r/iq/query/discoitems' require 'xmpp4r/x/muc' #Jabber::debug = true class Room class RoomException < Exception attr_reader :error def initialize(error, msg) @error = Jabber::ErrorResponse.new(error, msg) end end def initialize(stream, name) @users = {} # nick => jid @stream = stream @name = name end def num_users @users.size end def each_user(&block) @users.each { |n,j| yield(n, j) } end def handle_message(msg) origin = msg.from msg.from = nil @users.each { |nick,jid| if jid == origin msg.from = Jabber::JID.new(@name.node, @name.domain, nick) end } unless msg.from.nil? broadcast(msg) end true end def handle_presence(pres) reason = nil # Check if nick already registered @users.each { |nick,jid| if pres.from != jid && pres.to.resource == nick puts "#{pres.from} tried to use already active #{pres.to}" raise RoomException.new('conflict', "Nick already used") end } # New user? unless @users.has_key?(pres.to.resource) # Check nick length if pres.to.resource.size < 1 puts "#{pres.from} tried to use empty nick" raise RoomException.new('not-acceptable', "Nick must contain characters") end # Push user-list userinfo = Jabber::Presence.import(pres) userinfo.to = pres.from userinfo.add(Jabber::XMUCUser.new).add(Jabber::XMUCUserItem.new(:none, :participant)) print "Sending all users for #{pres.to} to #{pres.from}:" @users.each { |nick,jid| userinfo.from = Jabber::JID.new(@name.node, @name.domain, nick) print " #{nick} (#{jid})" @stream.send(userinfo) } puts " Ok" # Add the new user puts "Adding #{pres.from} as #{pres.to}" @users[pres.to.resource] = pres.from send_message("#{pres.to.resource} joins #{@name.node}") reason = "#{pres.to.resource} joins #{@name.node}" end # Remove dead rats if pres.type == :error || pres.type == :unavailable was_nick = nil @users.delete_if { |nick,jid| was_nick = nick jid == pres.from } unless was_nick.nil? send_message("#{was_nick} has left #{@name.node}") reason = "#{was_nick} has left #{@name.node}" end end # Advertise users presence to all puts "Advertising user to all" x = Jabber::XMUCUserItem.new(:none, :participant, pres.from) x.reason = reason pres.add(Jabber::XMUCUser.new).add(x) pres.from = pres.to broadcast(pres) end def send_message(body) msg = Jabber::Message.new msg.from = Jabber::JID.new(@name.node, @name.domain, "") msg.type = :groupchat msg.body = body broadcast(msg) end def broadcast(stanza) x = stanza.class::import(stanza) @users.each { |nick,jid| x.to = jid @stream.send(x) } end end class MUC def initialize(jid, secret, addr, port=5347) @rooms = {} @component = Jabber::Component.new(jid, addr, port) @component.connect @component.auth(secret) @component.add_iq_callback { |iq| handle_iq(iq) } @component.add_presence_callback { |pres| handle_presence(pres) } @component.add_message_callback { |msg| handle_message(msg) } end def handle_iq(iq) puts "#{iq.from} #{iq.queryns} to #{iq.to}" if iq.query.kind_of?(Jabber::IqQueryDiscoInfo) handle_disco_info(iq) true elsif iq.query.kind_of?(Jabber::IqQueryDiscoItems) handle_disco_items(iq) true else false end end def handle_presence(pres) room = nil @rooms.each { |name,r| room = r if name == pres.to.strip } if room.nil? && pres.type.nil? && (pres.to.node.to_s != '') room = Room.new(@component, pres.to.strip) @rooms[pres.to.strip] = room elsif room.nil? && pres.type != :error pres.to, pres.from = pres.from, pres.to pres.type = :error pres.add(Jabber::ErrorResponse.new('item-not-found')) @component.send(pres) return(true) end begin room.handle_presence(pres) rescue Room::RoomException => e pres.to, pres.from = pres.from, pres.to pres.type = :error pres.add(e.error) @component.send(pres) return(true) end return(false) end def handle_message(msg) puts "Message from #{msg.from} to #{msg.to} (#{msg.type}): #{msg.body.inspect}" return if msg.type == :error room = @rooms[msg.to] unless room.nil? room.handle_message(msg) else msg.to, msg.from = msg.from, msg.to msg.type = :error msg.add(Jabber::ErrorResponse.new('item-not-found', 'The chatroom you are trying to reach is currently not available.')) @component.send(msg) end end def handle_disco_info(iq) if (iq.type == :get) iq.type = :result iq.query = Jabber::IqQueryDiscoInfo.new if iq.to.node == nil iq.query.add(Jabber::DiscoIdentity.new('conference', 'Minimal Multi-User Chat', 'text')) else room = @rooms[iq.to] if room.nil? iq.type = :error iq.add_element(Jabber::ErrorResponse.new('item-not-found')) else iq.query.add(Jabber::DiscoIdentity.new('conference', "#{iq.to.node} (#{room.num_users})", 'text')) end end [Jabber::IqQueryDiscoInfo.new.namespace, Jabber::IqQueryDiscoItems.new.namespace, Jabber::XMUC.new.namespace, Jabber::XMUCUser.new.namespace].each { |ns| iq.query.add(Jabber::DiscoFeature.new(ns)) } else iq.type = :error iq.add_element(Jabber::ErrorResponse.new('bad-request')) end iq.to, iq.from = iq.from, iq.to @component.send(iq) end def handle_disco_items(iq) if (iq.type == :get) iq.type = :result if iq.to.node == nil @rooms.each { |name,room| iq.query.add(Jabber::DiscoItem.new(Jabber::JID.new(name, @component.jid.domain), name)) } elsif iq.to.resource == nil room = @rooms[iq.to.strip] unless room.nil? room.each_user { |nick,jid| iq.query.add(Jabber::DiscoItem.new(jid, nick)) } else iq.type = :error iq.add_element(Jabber::ErrorResponse.new('item-not-found')) end end else iq.type = :error iq.add_element(Jabber::ErrorResponse.new('bad-request')) end iq.to, iq.from = iq.from, iq.to @component.send(iq) end end if ARGV.size != 3 puts "Syntax: ./minimuc.rb " puts "Example: ./minimuc.rb conference.xmpp4r.mil minimuc localhost" exit end muc = MUC.new(Jabber::JID.new(ARGV[0]), ARGV[1], ARGV[2]) Thread.stop xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/getonline.rb0000755000175000017500000000246713647121573025025 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script can get all roster entries require 'optparse' require 'xmpp4r' include Jabber get = true jid = JID.new('lucastest@linux.ensimag.fr/rosterget') password = 'lucastest' domains = [] OptionParser.new do |opts| opts.banner = 'Usage: roster.rb -j jid -p password -d domain' opts.separator '' opts.on('-j', '--jid JID', 'sets the jid') { |j| jid = JID.new(j) } opts.on('-p', '--password PASSWORD', 'sets the password') { |p| password = p } opts.on('-d', '--domain DOMAIN', 'sets the domain') { |d| domains << d } opts.on_tail('-h', '--help', 'Show this message') { puts opts exit } opts.parse!(ARGV) end cl = Client.new(jid) cl.connect cl.auth(password) exit = false nb = 0 cl.add_iq_callback { |i| fjid = JID.new(i.from) if i.type == :result and fjid.resource == "admin" domain = fjid.domain items = nil i.each_element('item') { |e| items = e } raise "items nil" if items.nil? puts "--- Online users for #{domain} (seconds, sent, received)" c = 0 items.each_element('user') do |e| puts "#{e.attribute('jid')} #{e.attribute('name')}" c += 1 end puts "--- #{domain} : #{c} online users" nb -= 1 end } for d in domains do cl.send(Iq.new_browseget.set_to("#{d}/admin")) nb += 1 end while nb > 0 cl.process(1) end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/recvfile.rb0000644000175000017500000000432213647121573024625 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby require 'xmpp4r' require 'xmpp4r/bytestreams/helper/filetransfer' Jabber::debug = true if ARGV.size != 3 puts "Usage: #{$0} " exit end #Jabber::debug = true cl = Jabber::Client.new(Jabber::JID.new(ARGV[0])) puts "Connecting Jabber client..." cl.connect puts "Authenticating..." cl.auth ARGV[1] ft = Jabber::FileTransfer::Helper.new(cl) ft.add_incoming_callback { |iq,file| puts "Incoming file transfer from #{iq.from}: #{file.fname} (#{file.size / 1024} KB)" filename = "#{ARGV[2]}/#{file.fname.split(/\//).last}" offset = nil if File::exist?(filename) puts "#{filename} already exists" if File::size(filename) < file.size and file.range offset = File::size(filename) puts "Peer supports , will retrieve #{file.fname} starting at #{offset}" else puts "#{file.fname} is already fully retrieved, declining file-transfer" ft.decline(iq) next end end Thread.new { begin puts "Accepting #{file.fname}" stream = ft.accept(iq, offset) if stream.kind_of?(Jabber::Bytestreams::SOCKS5Bytestreams) stream.connect_timeout = 5 stream.add_streamhost_callback { |streamhost,state,e| case state when :connecting puts "Connecting to #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :success puts "Successfully using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :failure puts "Error using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port}): #{e}" end } end puts "Waiting for stream configuration" if stream.accept puts "Stream established" outfile = File.new(filename, (offset ? 'ab' : 'wb')) while buf = stream.read(65536) outfile.write(buf) print '.' $stdout.flush end puts '!' outfile.close stream.close else puts "Stream failed" end rescue Exception => e puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" end } } cl.send(Jabber::Presence.new(:chat, 'Send me files!')) puts "Waiting." Thread.stop cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/migrate.rb0000755000175000017500000000627513647121573024472 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script can be used to migrate from one jabber account to another $:.unshift '../../../../../lib' require 'optparse' require 'xmpp4r' include Jabber jidfrom = JID.new('lucas@linux.ensimag.fr/JabberMigrate') pwfrom = 'z' jidto = JID.new('lucas@nussbaum.fr/JabberMigrate') pwto = 'z' BOTHOLD = "Hi, you are subscribed to my presence. I just changed my JID. The new one is #{jidto.strip}. You might want to update your roster. Thank you, and sorry for the inconvenience !" BOTHNEW = "Hi, you are subscribed to the presence of my previous JID : #{jidfrom.strip}. I just changed my JID, and this is the new one. You might want to update your roster. Thank you, and sorry for the inconvenience !" clfrom = Client.new(jidfrom) clfrom.connect clfrom.auth(pwfrom) clto = Client.new(jidto) clto.connect clto.auth(pwto) #clfrom.send(Presence.new) #clto.send(Presence.new) clfrom.send(Iq.new_rosterget) exit = false clfrom.add_iq_callback { |i| if i.type == :result and i.queryns == 'jabber:iq:roster' i.query.each_element do |e| e.text = '' jid = e.attribute('jid') name = e.attribute('name') subscription = e.attribute('subscription') ask = e.attribute('ask') jid &&= jid.value next if jid =~ /@(icq|msn|aim|yahoo).blop.info/ # next if jid !~ /lucas@im.apinc.org/ puts "Processing #{e.to_s}" # Jabber::debug = true name &&= name.value subscription &&= subscription.value ask &&= ask.value puts subscription case subscription when 'from' # il veut me voir, je veux pas le voir. # envoi unsubscribed clfrom.send(Presence.new.set_to(jid).set_type(:unsubscribed)) # envoi message d'info OLD & NEW clfrom.send(Message.new(jid, BOTHOLD).set_type(:chat)) clto.send(Message.new(jid, BOTHNEW).set_type(:chat)) when 'to' # je veux le voir, il veut pas me voir # envoi unsubscribe clfrom.send(Presence.new.set_to(jid).set_type(:unsubscribe)) # envoi subscribe avec message pres = Presence.new.set_to(jid).set_type(:subscribe) pres.add(Element.new('status').add_text("Hi, I was previously subscribed to your presence with my JID #{jidfrom.strip}. Can I re-subscribe to your presence ? Thank you.")) clto.send(pres) when 'both' # envoi unsubscribed clfrom.send(Presence.new.set_to(jid).set_type(:unsubscribed)) # envoi unsubscribe clfrom.send(Presence.new.set_to(jid).set_type(:unsubscribe)) # update roster iq = Iq.new_rosterset e.delete_attribute('ask') e.delete_attribute('subscription') iq.query.add_element(e) clto.send(iq) # envoi message d'info OLD & NEW clfrom.send(Message.new(jid, BOTHOLD).set_type(:chat)) pres = Presence.new.set_to(jid).set_type(:subscribe) pres.add(Element.new('status').add_text("Hi, I was previously subscribed to your presence with my JID #{jidfrom.strip}. Can I re-subscribe to your presence ? Thank you.")) clto.send(pres) clto.send(Message.new(jid, BOTHNEW).set_type(:chat)) end end end } while not exit clfrom.process clto.process end clfrom.close clto.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb0000755000175000017500000000645713647121573026312 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # # XMPP4R - XMPP Library for Ruby # Copyright (C) 2005 Stephan Maka # Released under Ruby's license (see the LICENSE file) or GPL, at your option # # # Roster-Discovery example # # If you don't understand this code read JEP-0030 (Service Discovery) first: # http://www.jabber.org/jeps/jep-0030.html # # This examples exposes your roster to Service Discovery and can be browsed # by anyone. Please use with care if you want to keep your roster private! # # The Psi client has a very pretty Service Discovery dialog. But be sure # to turn off "Auto-browse into objects" for big rosters. # $:.unshift '../../../../../lib' require 'thread' require 'xmpp4r' require 'xmpp4r/helpers/roster' require 'xmpp4r/iq/query/discoinfo' require 'xmpp4r/iq/query/discoitems' # Command line argument checking if ARGV.size != 2 puts("Usage: ./rosterdiscovery.rb ") exit end # Building up the connection #Jabber::debug = true jid = Jabber::JID.new(ARGV[0]) cl = Jabber::Client.new(jid) cl.connect cl.auth(ARGV[1]) # The roster instance roster = Jabber::Helpers::Roster.new(cl) cl.add_iq_callback { |iq| if iq.query.kind_of?(Jabber::IqQueryDiscoInfo) || iq.query.kind_of?(Jabber::IqQueryDiscoItems) # Prepare the stanza for result iq.from, iq.to = iq.to, iq.from iq.type = :result if iq.query.kind_of?(Jabber::IqQueryDiscoInfo) # iq.to and iq.from are already switched here: puts("#{iq.to} requests info of #{iq.from} node #{iq.query.node.inspect}") if iq.query.node.nil? iq.query.add(Jabber::DiscoIdentity.new('directory', 'Roster discovery', 'user')) else # Count contacts in group in_group = 0 roster.items.each { |jid,item| if item.groups.include?(iq.query.node) in_group += 1 end } iq.query.add(Jabber::DiscoIdentity.new('directory', "#{iq.query.node} (#{in_group})", 'group')) end iq.query.add(Jabber::DiscoFeature.new(Jabber::IqQueryDiscoInfo.new.namespace)) iq.query.add(Jabber::DiscoFeature.new(Jabber::IqQueryDiscoItems.new.namespace)) elsif iq.query.kind_of?(Jabber::IqQueryDiscoItems) # iq.to and iq.from are already switched here: puts("#{iq.to} requests items of #{iq.from} node #{iq.query.node.inspect}") if iq.query.node.nil? # Make items from group names groups = [] roster.items.each { |jid,item| groups += item.groups } groups.uniq.each { |group| iq.query.add(Jabber::DiscoItem.new(cl.jid, group, group)) } # Collect all ungrouped roster items roster.items.each { |jid,item| if item.groups == [] iq.query.add(Jabber::DiscoItem.new(item.jid, item.iname.to_s == '' ? item.jid : item.iname)) end } else # Add a discovery item for each roster item in that group roster.items.each { |jid,item| if item.groups.include?(iq.query.node) iq.query.add(Jabber::DiscoItem.new(item.jid, item.iname.to_s == '' ? item.jid : item.iname)) end } end end cl.send(iq) true end } # Initial presence cl.send(Jabber::Presence.new.set_status("Discover my roster at #{jid}")) # Main loop: loop do cl.process Thread.stop end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/versionpoll.rb0000644000175000017500000000515013647121573025402 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script will request the version information of a list of JID given # on stdin. $:.unshift '../../../../../lib' require 'optparse' require 'xmpp4r/client' require 'xmpp4r/version' require 'xmpp4r/discovery' include Jabber #Jabber::debug = true # settings jid = JID.new('bot@localhost/Bot') password = 'bot' domains = [] domains_ejabberd = [] OptionParser.new do |opts| opts.banner = 'Usage: versionpoll.rb -j jid -p password -d DOMAINS' opts.separator '' opts.on('-j', '--jid JID', 'sets the jid') { |j| jid = JID.new(j) } opts.on('-p', '--password PASSWORD', 'sets the password') { |p| password = p } opts.on('-d', '--domain DOMAIN', 'sets the domain') { |d| domains << d } opts.on_tail('-h', '--help', 'Show this message') { puts opts exit } opts.parse!(ARGV) end cl = Client.new(jid) cl.connect cl.auth(password) sent = [] queried = [] activity = false cl.add_iq_callback do |i| fjid = JID.new(i.from) if i.type == :result and fjid.resource == "admin" domain = fjid.domain items = i.first_element('item') raise "items nil" if items.nil? items.each_element('user') do |e| j = e.attribute('jid') if not queried.include?(j) activity = true queried << j cl.send(Iq.new_browseget.set_to(j)) end end elsif i.type == :result and i.query.kind_of? Discovery::IqQueryDiscoItems i.query.items.each do |e| j = e.jid if not queried.include?(j) activity = true queried << j iq = Iq.new(:get) iq.query = Version::IqQueryVersion.new iq.set_to(j) cl.send(iq) end end end end cl.add_iq_callback do |i| if i.type == :result u = i.first_element('user') if u u.each_element('user') do |e| if (a = e.attribute('type')) if a.value == 'client' activity = true iq = Iq.new(:get) iq.query = Version::IqQueryVersion.new iq.set_to(JID.new(e.attribute('jid').to_s)) cl.send(iq) end end end end end end cl.add_iq_callback do |iq| if iq.type == :result and iq.query.class == Version::IqQueryVersion activity = true r = [ iq.from.to_s, iq.query.iname, iq.query.version, iq.query.os ] puts r.inspect end end cl.send(Presence.new) for d in domains do cl.send(Iq.new_browseget.set_to("#{d}/admin")) end for d in domains_ejabberd do iq = Iq.new(:get, d) iq.add(Discovery::IqQueryDiscoItems.new).node = 'online users' cl.send(iq) end activity = true while activity activity = false # other threads might set activity to true sleep 10 end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/fileserve.rb0000644000175000017500000002055513647121573025020 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby require 'yaml' require 'xmpp4r' require 'xmpp4r/bytestreams' require 'xmpp4r/roster' require 'xmpp4r/version' Jabber::debug = true def human_readable(num) unit = '' units = %w(K M G T P E) while num > 10240 and units.size > 0 num /= 1024 unit = units.shift end "#{num} #{unit}B" end class Transfer attr_reader :peer, :filename, :bytes, :filesize def initialize(filetransfer, peer, filename, filesize, msgblock) @filetransfer = filetransfer @peer = peer @filename = filename @filesize = filesize @msgblock = msgblock @done = false @bytes = 0 @stats = [0] @stats_lock = Mutex.new @stats_thread = Thread.new { stats_loop } @streamhost_cb = lambda { |streamhost,state,e| case state when :connecting say "Connecting to #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :success say "Successfully using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})" when :failure say "Error using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port}): #{e}" end } end def done? @stats_thread.kill if @done @done end def stats_loop loop { sleep 1 @stats_lock.synchronize { @stats.push @bytes @stats.shift if @stats.size > 5 } } end def stats @stats_lock.synchronize { if @stats.first >= @stats.last 0 else (@stats.last - @stats.first) / (@stats.size - 1) end } end def say(text) @msgblock.call(text) end def transfer(from, to) while buf = from.read @bytes += to.write buf end end end class Upload < Transfer def initialize(filetransfer, iq, filename, filesize, can_range, msgblock) super(filetransfer, iq.from, filename, filesize, msgblock) if filename.size < 1 say "What is this file for?" @done = true return end offset = nil if File::exist?(filename) if File::size(filename) < filesize and can_range @bytes = offset = File::size(filename) say "Will retrieve #{filename} starting at #{offset}" else say "#{filename} already exists" filetransfer.decline(iq) @done = true return end end Thread.new { begin stream = filetransfer.accept(iq, offset) if stream.kind_of?(Jabber::Bytestreams::SOCKS5Bytestreams) stream.connect_timeout = 5 stream.add_streamhost_callback(nil, nil, &@streamhost_cb) end if stream.accept outfile = File.new(filename, (offset ? 'a' : 'w')) transfer(stream, outfile) outfile.close stream.close @done = true else say "Stream failed" @done = true end rescue puts $!.backtrace.first say "Error: #{$!}" @done = true end } end end class Download < Transfer def initialize(filetransfer, peer, filename, msgblock, socksconf) begin filesize = File.size(filename) rescue filesize = 0 end super(filetransfer, peer, filename, filesize, msgblock) Thread.new { begin raise "No regular file" unless File.file?(filename) source = Jabber::FileTransfer::FileSource.new(filename) stream = filetransfer.offer(peer, source) unless stream raise "Well, you should accept what you request..." @done = true end if stream.kind_of?(Jabber::Bytestreams::SOCKS5Bytestreams) socksconf.call(stream) stream.add_streamhost_callback(nil, nil, &@streamhost_cb) end stream.open transfer(source, stream) stream.close @done = true rescue say "Error: #{$!}" @done = true end } end end class FileServe def initialize(conf) @uploads = 0 @downloads = 0 @transfers = [] @transfers_lock = Mutex.new @client = Jabber::Client.new Jabber::JID.new(conf['jabber']['jid']) @client.connect @client.auth(conf['jabber']['password']) @ft = Jabber::FileTransfer::Helper.new(@client) Jabber::Version::SimpleResponder.new(@client, "XMPP4R FileServe example", Jabber::XMPP4R_VERSION, IO.popen('uname -sr').readlines.to_s.strip) register_handlers @directory = conf['directory'] @directory.gsub!(/\/+$/, '') @socksserver = Jabber::Bytestreams::SOCKS5BytestreamsServer.new(conf['socks']['port']) conf['socks']['addresses'].each { |addr| @socksserver.add_address(addr) } @proxies = [] conf['socks']['proxies'].collect { |jid| puts "Querying proxy #{jid}..." begin @proxies.push(Jabber::Bytestreams::SOCKS5Bytestreams::query_streamhost(@client, jid)) rescue puts "Error: #{$!}" end } Thread.new { presence } Thread.new { cleaner } # Panic reboot ;-) @client.on_exception { initialize(conf) } end def presence rate_upload = 0 rate_download = 0 old_status = nil loop { # Update the @rate_* variables @transfers_lock.synchronize { @transfers.each { |t| if t.kind_of?(Upload) and t.stats > rate_upload rate_upload = t.stats elsif t.kind_of?(Download) and t.stats > rate_download rate_download = t.stats end } } status = "Attempted #{@downloads} downloads (max. #{human_readable rate_download}/s) and #{@uploads} uploads (max. #{human_readable rate_upload}/s)" @client.send(Jabber::Presence.new(:chat, status)) if status != old_status old_status = status sleep 1 } end def register_handlers @client.add_message_callback { |msg| if msg.type == :chat and msg.body and msg.from != 'pentabarf@pentabarf.org/rails' puts "<#{msg.from}> #{msg.body.strip}" cmd, arg = msg.body.split(/ /, 2) command(msg.from, cmd, arg) end } @ft.add_incoming_callback { |iq,file| say = lambda { |text| say(iq.from, text) } puts "Incoming file transfer from #{iq.from}: #{file.fname} (#{file.size / 1024} KB)" filename = file.fname.split(/\//).last filename.gsub!(/^\.+/, '') puts "Range: #{file.range != nil}" transfer = Upload.new(@ft, iq, "#{@directory}/#{filename}", file.size, file.range != nil, say) @uploads += 1 @transfers_lock.synchronize { @transfers.push(transfer) } } roster = Jabber::Roster::Helper.new(@client) roster.add_subscription_request_callback { |item,presence| roster.accept_subscription(presence.from) } end def command(from, cmd, arg) say = lambda { |text| say(from, text) } socksconf = lambda { |stream| stream.add_streamhost(@socksserver) @proxies.each { |sh| stream.add_streamhost(sh) } } case cmd when 'get' arg.gsub!(/\//, '') arg.gsub!(/^\.+/, '') transfer = Download.new(@ft, from, "#{@directory}/#{arg}", say, socksconf) @downloads += 1 @transfers_lock.synchronize { @transfers.push(transfer) } when 'ls' text = "" Dir.foreach(@directory) { |file| next if file =~ /^\./ path = "#{@directory}/#{file}" text += "#{file} (#{human_readable File.size(path)})\n" if File.file?(path) } say.call(text.strip) when 'stat' @transfers_lock.synchronize { text = "#{@transfers.size} transfers:\n" @transfers.each { |t| text += "#{t.filename} (#{t.peer}): #{(t.bytes * 100) / t.filesize}% (#{human_readable t.stats}/s)\n" } } say.call(text.strip) when 'help' say.call "Download a file: get \nList directory contents: ls\nLook who is currently wasting bandwidth: stat\nUpload a file, simply send this file" else say.call "Unknown command: #{cmd}, try help" end end def say(to, text) puts ">#{to}< #{text.strip}" @client.send(Jabber::Message.new(to, text).set_type(:chat)) end def cleaner loop { @transfers_lock.synchronize { @transfers.delete_if { |t| t.done? } } sleep 1 } end end FileServe.new(YAML::load(File.new('fileserve.conf'))) puts "Up and running!" Thread.stop xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/0000755000175000017500000000000013647121573024475 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/README0000644000175000017500000000346313647121573025363 0ustar debbiecocoadebbiecocoaWhat is this? This is an example of what you can do with XMPP4R. It is a conferencing component in which you can walk around, travel to various places, look at things and talk to other visitors on the same places. If you like Multi-User Dungeons (MUDs) this is for you! --- How does it work? The component loads a few worlds from a few XML files. Each world is a component. Once joined the chat will tell you what you can do. Remember that you can get a command listing anytime by saying '?'. Reading 'You can go north, west' you may say 'go north' to go in the northern direction and 'go west' to go in the western direction. You'll then find yourself at some other place but still in the same MUC conference. Your groupchat roster will change as you'll notice different people and things that are just in the same place, not other places. Before starting to hack the scripts you may want to take a look at tower.xml. Note that users are s, too and the whole world could be serialized back to XML by just issuing "world.to_s". Please note that the code, especially the error handling, is of extreme poor quality and was mostly written in one afternoon. If I'm going to develop this further everything should be rewritten... --- How to try? Because this is a component you are going to need your own Jabber Daemon - which you'll need anyways if you're going to experiment with XMPP4R. ;-) Syntax: ./adventure.rb Example: ./adventure.rb mud.example.com geheimnis localhost --- Messages seem to have random order? I don't know any solution for this. One may add short delays between messages, but that would only be a very dirty hack. RFC3920: "10. Server Rules for Handling XML Stanzas Compliant server implementations MUST ensure in-order processing of XML stanzas between any two entities." xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/world.rb0000644000175000017500000002446513647121573026164 0ustar debbiecocoadebbiecocoarequire 'rexml/document' class World < REXML::Element def initialize(muc) super('world') @muc = muc end def send(resource, stanza) # Avoid sending to things without JID if stanza.to != nil @muc.send(node, resource, stanza) end end def World.new_from_file(muc, filename) file = File.new(filename) world = World.new(muc) world.import(REXML::Document.new(file).root) file.close world end def add(xmlelement) if xmlelement.kind_of?(REXML::Element) && (xmlelement.name == 'place') super(Place.new.import(xmlelement)) elsif xmlelement.kind_of?(REXML::Element) && (xmlelement.name == 'thing') && !xmlelement.kind_of?(Player) super(Thing.new(self).import(xmlelement)) else super(xmlelement) end end def node attributes['node'] end def iname attributes['name'] end def place(placename) pl = nil each_element('place') { |place| if place.iname == placename pl = place end } pl end def each_thing_by_place(place, &block) each_element('thing') { |t| if t.place == place yield(t) end } end def move_thing(thing, newplace) each_thing_by_place(thing.place) { |t| # Call leave hooks t.on_leave(thing, newplace) # Broadcast unavailability presence to leaver unless t.presence.nil? pres = Jabber::Presence.import(t.presence) pres.type = :unavailable pres.to = thing.jid send(t.iname, pres) unless t.jid == thing.jid end # Broadcast unavailability presence to all who are here unless thing.presence.nil? pres = Jabber::Presence.import(thing.presence) pres.type = :unavailable pres.to = t.jid send(thing.iname, pres) unless thing.jid == t.jid end } # Enter new place oldplace = thing.place thing.place = newplace each_thing_by_place(thing.place) { |t| # Broadcast availability presence to enterer unless t.presence.nil? pres = Jabber::Presence.import(t.presence) pres.to = thing.jid send(t.iname, pres) end # Broadcast availability presence to all who are here unless thing.presence.nil? pres = Jabber::Presence.import(thing.presence) pres.to = t.jid send(thing.iname, pres) end } thing.send_message(nil, " ") subject = newplace.nil? ? " " : newplace.dup subject[0] = subject[0,1].upcase thing.send_message(nil, "Entering #{newplace}", subject) thing.send_message(nil, " ") thing.see(place(newplace)) each_thing_by_place(thing.place) { |t| # Call enter hooks t.on_enter(thing, oldplace) } end def handle_presence(pres) # A help for the irritated first: if pres.type == :subscribe msg = Jabber::Message.new(pres.from) msg.type = :normal msg.subject = "Adventure component help" msg.body = "You don't need to subscribe to my presence. Simply use your Jabber client to join the MUC or conference at #{pres.to.strip}" send(nil, msg) return(true) end # Look if player is already known player = nil each_element('thing') { |thing| if thing.kind_of?(Player) && pres.to.resource == thing.iname player = thing end # Disallow nick changes if thing.kind_of?(Player) && (pres.from == thing.jid) && (player != thing) answer = pres.answer(false) answer.type = :error answer.add(Jabber::ErrorResponse.new('not-acceptable', 'Nickchange not allowed')) send(thing.iname, answer) return(true) end } # Either nick-collission or empty nick unless (player.nil? || pres.from == player.jid) && (pres.to.resource.to_s.size > 1) answer = pres.answer answer.type = :error if (pres.to.resource.to_s.size > 1) answer.add(Jabber::ErrorResponse.new('conflict', 'Nickname already used')) else answer.add(Jabber::ErrorResponse.new('not-acceptable', 'Please use a nickname')) end send(nil, answer) return(true) end # Add the valid player if player.nil? player = add(Player.new(self, pres.to.resource, pres.from)) player.presence = pres move_thing(player, attributes['start']) player.send_message('Help!', 'Send "?" to get a list of available commands any time.') # Or broadcast updated presence else player.presence = pres each_thing_by_place(player.place) { |t| # Broadcast presence to all who are here pres = Jabber::Presence.import(player.presence) pres.to = t.jid send(player.iname, pres) } end # Remove the player instantly if pres.type == :error || pres.type == :unavailable move_thing(player, nil) delete_element(player) end end def handle_message(msg) player = nil each_element('thing') { |thing| if thing.kind_of?(Player) && msg.to.resource == nil && msg.from == thing.jid player = thing end } if player.nil? answer = msg.answer answer.type = :error answer.add(Jabber::ErrorResponse.new('forbidden')) send(msg.to.resource, answer) return(true) end unless command(player, msg.body) each_thing_by_place(player.place) { |thing| thing.send_message(player.iname, msg.body) } end end def command(player, text) if text == '?' player.send_message(nil, "(Command) who") place(player.place).each_element('go') { |go| player.send_message(nil, "(Command) go #{go.attributes['spec']}") } each_thing_by_place(player.place) { |thing| thing.each_element('on-command') { |c| player.send_message(nil, "(Command) #{c.attributes['command']} #{thing.command_name}") } } return(true) else words = text.split(/ /) cmd = words.shift what = words.shift || "" if cmd == 'go' oldplace = place(player.place) newplace = nil oldplace.each_element('go') { |go| if go.attributes['spec'] == what newplace = go.attributes['place'] end } if newplace.nil? player.send_message(nil, 'You cannot go there') else move_thing(player, newplace) end return(true) elsif cmd == 'who' player.send_message(nil, "Players in \"#{iname}\":") each_element('thing') { |thing| if thing.kind_of?(Player) player.send_message(nil, "#{thing.iname} is at/in #{thing.place}") end } return(true) else handled = false each_thing_by_place(player.place) { |thing| if what.downcase == thing.command_name.downcase thing.each_element('on-command') { |c| if c.attributes['command'] == cmd thing.command(player, c, words) handled = true end } end } return(true) if handled end end false end end class Place < REXML::Element def initialize super('place') end def iname attributes['name'] end end class Thing < REXML::Element def initialize(world) super('thing') @world = world end def add(xmlelement) if xmlelement.kind_of?(REXML::Element) && (xmlelement.name == 'presence') super(Jabber::Presence.import(xmlelement)) else super(xmlelement) end end def iname attributes['name'] end def command_name attributes['command-name'].nil? ? iname : attributes['command-name'] end def place attributes['place'] end def place=(p) attributes['place'] = p end def jid nil end def presence xe = nil each_element('presence') { |pres| xe = Jabber::Presence.import(pres) } if self.kind_of?(Player) xe.add(Jabber::MUC::XMUCUser.new).add(Jabber::MUC::XMUCUserItem.new(:none, :participant)) else xe.add(Jabber::MUC::XMUCUser.new).add(Jabber::MUC::XMUCUserItem.new(:owner, :moderator)) end xe end def presence=(pres) delete_elements('presence') add(pres) end def see(place) end def send_message(fromresource, text, subject=nil) end def send_message_to_place(fromresource, text) @world.each_element('thing') { |thing| if thing.place == place thing.send_message(fromresource, text) end } end def on_enter(thing, from) each_element('on-enter') { |c| command(thing, c, [from]) } end def on_leave(thing, to) each_element('on-leave') { |c| command(thing, c, [to]) } end def command(source, command, arguments) command.each_element { |action| text = action.text.nil? ? "" : action.text.dup text.gsub!('%self%', iname) text.gsub!('%actor%', source.iname) text.gsub!('%place%', place) if action.name == 'say' || action.name == 'tell' sender = nil sender = iname if action.name == 'say' if action.attributes['to'] == 'all' send_message_to_place(sender, text) else source.send_message(sender, text) end end } end end class Player < Thing def initialize(world, iname, jid) super(world) attributes['name'] = iname attributes['jid'] = jid.to_s end def jid attributes['jid'].nil? ? nil : Jabber::JID.new(attributes['jid']) end def see(place) return if place.nil? place.text.strip.split(/\n/).each do |line| send_message(nil, line.strip) end send_message(nil, " ") directions = [] place.each_element('go') { |go| directions.push(go.attributes['spec']) } send_message(nil, "You can go #{directions.join(', ')}") end def send_message(fromresource, text, subject=nil) msg = Jabber::Message.new(jid, text) msg.type = :groupchat msg.subject = subject unless subject.nil? @world.send(fromresource, msg) end def on_enter(thing, from) if thing != self if from.nil? send_message(nil, "#{thing.iname} spawns") else send_message(nil, "#{thing.iname} enters #{place} coming from #{from}") end end end def on_leave(thing, to) if thing != self if to.nil? send_message(nil, "#{thing.iname} disintegrates") else send_message(nil, "#{thing.iname} leaves #{place} going to #{to}") end end end end xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb0000755000175000017500000000064013647121573027022 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../lib' require 'xmpp4r' require 'xmpp4r/discovery' require 'xmpp4r/muc/x/muc' require 'adventuremuc' #Jabber::debug = true if ARGV.size != 3 puts "Syntax: ./adventure.rb " puts "See README for further help" exit end muc = AdventureMUC.new(Jabber::JID.new(ARGV[0]), ARGV[1], ARGV[2]) muc.add_world('tower.xml') muc.add_world('cube.xml') Thread.stop xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb0000644000175000017500000000775013647121573027535 0ustar debbiecocoadebbiecocoarequire 'world' class AdventureMUC def initialize(jid, secret, addr, port=5347) @worlds = {} @component = Jabber::Component.new(jid) @component.connect(addr, port) @component.auth(secret) @component.on_exception { |e,| puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" } @component.add_iq_callback { |iq| handle_iq(iq) } @component.add_presence_callback { |pres| handle_presence(pres) } @component.add_message_callback { |msg| handle_message(msg) } puts "Adventure component up and running" end def add_world(file) print "Adding world from #{file}..." begin world = World.new_from_file(self, file) rescue Exception => e puts " #{e.to_s}" exit end @worlds[world.node] = world puts " #{world.iname}" end def send(worldnode, worldresource, stanza) stanza.from = Jabber::JID.new(worldnode, @component.jid.domain, worldresource) @component.send(stanza) end def handle_iq(iq) puts "iq: from #{iq.from} type #{iq.type} to #{iq.to}: #{iq.queryns}" if iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoInfo) handle_disco_info(iq) true elsif iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoItems) handle_disco_items(iq) true else false end end def handle_disco_info(iq) if iq.type != :get answer = iq.answer answer.type = :error answer.add(Jabber::ErrorResponse.new('bad-request')) @component.send(answer) if iq.type != :error return end answer = iq.answer answer.type = :result if iq.to.node == nil answer.query.add(Jabber::Discovery::Identity.new('conference', 'Adventure component', 'text')) answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoInfo.new.namespace)) answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoItems.new.namespace)) else world = @worlds[iq.to.node] if world.nil? answer.type = :error answer.query.add(Jabber::ErrorResponse.new('item-not-found', 'The world you are trying to reach is currently unavailable.')) else answer.query.add(Jabber::Discovery::Identity.new('conference', world.iname, 'text')) answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoInfo.new.namespace)) answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoItems.new.namespace)) answer.query.add(Jabber::Discovery::Feature.new(Jabber::MUC::XMUC.new.namespace)) answer.query.add(Jabber::Discovery::Feature.new(Jabber::MUC::XMUCUser.new.namespace)) end end @component.send(answer) end def handle_disco_items(iq) if iq.type != :get answer = iq.answer answer.add(Jabber::ErrorResponse.new('bad-request')) @component.send(answer) return end answer = iq.answer answer.type = :result if iq.to.node == nil @worlds.each { |node,world| answer.query.add(Jabber::Discovery::Item.new(Jabber::JID.new(node, @component.jid.domain), world.iname)) } end @component.send(answer) end def handle_presence(pres) puts "presence: from #{pres.from} type #{pres.type} to #{pres.to}" world = @worlds[pres.to.node] if world.nil? answer = pres.answer answer.type = :error answer.add(Jabber::ErrorResponse.new('item-not-found', 'The world you are trying to reach is currently unavailable.')) @component.send(answer) else world.handle_presence(pres) end true end def handle_message(msg) puts "message: from #{msg.from} type #{msg.type} to #{msg.to}: #{msg.body.inspect}" world = @worlds[msg.to.node] if world.nil? answer = msg.answer answer.type = :error answer.add(Jabber::ErrorResponse.new('item-not-found', 'The world you are trying to reach is currently unavailable.')) @component.send(answer) else world.handle_message(msg) end true end end xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/cube.xml0000644000175000017500000000067513647121573026145 0ustar debbiecocoadebbiecocoa This is a big white room. There are doors up, down, left, right, in the back and in front of you. There's nothing else. xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/adventure/tower.xml0000644000175000017500000000422413647121573026361 0ustar debbiecocoadebbiecocoa You are in a beautiful garden. There's an impressive tower beneath. Its door is open. Welcome to the tower's lower room. There's a ladder to upstairs. This is the upper room. There's only a bed here. A wizard is currently sleeping in it. There's a very beautiful well in the middle of this place. A frog sits on the well's edge. It looks frightening happy. Hello great hero! Welcome to %place% The %self% waves towards %actor% I would love to come with you, great hero. But I'm immobile due to the simple nature of this game. Would *YOU* like to /code/ me movable? So we could enjoy great adventures together... %actor% touches the %self% Quaaack! That feels good. chat Happy *snork* dnd Sleeping... go bother my assistant, the frog. You see a beautiful garden. There's a well behind the garden. But a huge green monster is sitting on it's edge. There's a great view from up here xmpp4r-0.5.6/data/doc/xmpp4r/examples/advanced/xmppingrc.sample0000644000175000017500000000034513647121573025714 0ustar debbiecocoadebbiecocoadefault: jid: user@host/xmpping password: secret other_account: jid: me@domain/ping password: xxx host: 127.0.0.1 port: 65222 web_account: jid: me@domain/ping password: xxx http bind: http://domain/http-bind/ xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/0000755000175000017500000000000013647121573022014 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/client.rb0000755000175000017500000000244413647121573023626 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # Basic console client that does nothing, but easy to modify to test things. # to test, start, then type : # connect login@server/resource password # auth $:.unshift '../../../../../lib' require 'xmpp4r/client' include Jabber Jabber::debug = true class BasicClient def initialize puts "Welcome to this Basic Console Jabber Client!" quit = false # main loop while not quit do print "> " $>.flush line = gets quit = true if line.nil? if not quit command, args = line.split(' ', 2) args = args.to_s.chomp # main case case command when 'exit' quit = true when 'connect' do_connect(args) when 'help' do_help when 'auth' do_auth else puts "Command \"#{command}\" unknown" end end end puts "Goodbye!" end def do_help puts <<-EOF # exit - exits # connect user@server/resource password - connects # auth - sends authentification EOF end ## # connect def do_connect(args) @jid, @password = args.split(' ', 2) @jid = JID.new(@jid) @cl = Client.new(@jid) @cl.connect end ## # auth def do_auth @cl.auth(@password, false) end end BasicClient.new xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/rosterprint.rb0000755000175000017500000000136513647121573024744 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib/' require 'xmpp4r' require 'xmpp4r/roster/helper/roster' # Command line argument checking if ARGV.size != 2 puts("Usage: ./rosterprint.rb ") exit end # Building up the connection #Jabber::debug = true jid = Jabber::JID.new(ARGV[0]) cl = Jabber::Client.new(jid) cl.connect cl.auth(ARGV[1]) # The roster instance roster = Jabber::Roster::Helper.new(cl) mainthread = Thread.current roster.add_query_callback { |iq| mainthread.wakeup } Thread.stop roster.groups.each { |group| if group.nil? puts "*** Ungrouped ***" else puts "*** #{group} ***" end roster.find_by_group(group).each { |item| puts "- #{item.iname} (#{item.jid})" } print "\n" } cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/tune_server.rb0000755000175000017500000000210013647121573024676 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/roster' require 'xmpp4r/tune' require 'rbosa' # # Send XEP-0118 User Tune events... # # See Jabber::UserTune::Helper for the gory details... # # NB needs rbosa library to access iTunes - only on MacOSX # if ARGV.length != 2: puts "Usage: ruby tune_server.rb " exit 1 end jid=ARGV[0] pw=ARGV[1] Jabber::debug=true cl = Jabber::Client.new(jid) cl.connect cl.auth(pw) # Following XEP-0163 PEP we need to # ensure we have a 'both' subscription to the Tune client roster = Jabber::Roster::Helper.new(cl) roster.add_subscription_request_callback do |item,pres| roster.accept_subscription(pres.from) reply = pres.answer reply.type = :subscribe cl.send(reply) end cl.send(Jabber::Presence.new.set_show(:chat)) t=Jabber::UserTune::Helper.new(cl, nil) itunes=OSA.app('iTunes') loop do track = itunes.current_track if track puts "Now playing: #{track.name} by #{track.artist}" t.now_playing(Jabber::UserTune::Tune.new(track.artist, track.name)) end sleep 5 end xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/rosterwatch.rb0000755000175000017500000001157713647121573024724 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # # XMPP4R - XMPP Library for Ruby # Copyright (C) 2005 Stephan Maka # Released under Ruby's license (see the LICENSE file) or GPL, at your option # # # Roster-Watch example # # # Learn how a roster looks like # how presences are received # about subscription requests and answers # what vCards contain # # It's recommended to insert 'p' commands in this script. :-) # # This script does: # # * Listing roster changes # # * Subscribe to roster items which have a subscription of "none" or "from" # WARNING: Chances are that you don't want that :-) # # * Requesting vCards for unnamed items in your roster and renaming them # to the or field in the vCard # # * Listing presence changes # # * Listing subscription and unsubscription requests and answers $:.unshift '../../../../../lib/' require 'xmpp4r' require 'xmpp4r/roster/helper/roster' require 'xmpp4r/vcard/helper/vcard' # Command line argument checking if ARGV.size != 2 puts("Usage: ./rosterwatch.rb ") exit end # Building up the connection #Jabber::debug = true jid = Jabber::JID.new(ARGV[0]) cl = Jabber::Client.new(jid) cl.connect cl.auth(ARGV[1]) # The roster instance roster = Jabber::Roster::Helper.new(cl) # Callback to handle updated roster items roster.add_update_callback { |olditem,item| if [:from, :none].include?(item.subscription) && item.ask != :subscribe puts("Subscribing to #{item.jid}") item.subscribe end # Print the item if olditem.nil? # We didn't knew before: puts("#{item.iname} (#{item.jid}, #{item.subscription}) #{item.groups.join(', ')}") else # Showing whats different: puts("#{olditem.iname} (#{olditem.jid}, #{olditem.subscription}) #{olditem.groups.join(', ')} -> #{item.iname} (#{item.jid}, #{item.subscription}) #{item.groups.join(', ')}") end # If the item has no name associated... unless item.iname Thread.new do puts("#{item.jid} has no nickname... getting vCard") begin # ...get a vCard vcard = Jabber::Vcard::Helper.new(cl).get(item.jid.strip) unless vcard.nil? # Rename him to vCard's field if vcard['NICKNAME'] item.iname = vcard['NICKNAME'] puts("Renaming #{item.jid} to #{vcard['NICKNAME']}") item.send # Rename him to vCard's field elsif vcard['FN'] item.iname = vcard['FN'] puts("Renaming #{item.jid} to #{vcard['FN']}") item.send # We've got a lazy one else puts("#{item.jid} provided no details in vCard") end end rescue Exception => e # This will be (mostly) thrown by Jabber::Vcard::Helper#get puts("Error getting vCard for #{item.jid}: #{e.to_s}") end end end } # Presence updates: roster.add_presence_callback { |item,oldpres,pres| # Can't look for something that just does not exist... if pres.nil? # ...so create it: pres = Jabber::Presence.new end if oldpres.nil? # ...so create it: oldpres = Jabber::Presence.new end # Print name and jid: name = "#{pres.from}" if item.iname name = "#{item.iname} (#{pres.from})" end puts(name) # Print type changes: unless oldpres.type.nil? && pres.type.nil? puts(" Type: #{oldpres.type.inspect} -> #{pres.type.inspect}") end # Print show changes: unless oldpres.show.nil? && pres.show.nil? puts(" Show: #{oldpres.show.to_s.inspect} -> #{pres.show.to_s.inspect}") end # Print status changes: unless oldpres.status.nil? && pres.status.nil? puts(" Status: #{oldpres.status.to_s.inspect} -> #{pres.status.to_s.inspect}") end # Print priority changes: unless oldpres.priority.nil? && pres.priority.nil? puts(" Priority: #{oldpres.priority.inspect} -> #{pres.priority.inspect}") end # Note: presences with type='error' will reflect our own show/status/priority # as it is mostly just a reply from a server. This is *not* a bug. } # Subscription requests and responses: subscription_callback = lambda { |item,pres| name = pres.from if item != nil && item.iname != nil name = "#{item.iname} (#{pres.from})" end case pres.type when :subscribe then puts("Subscription request from #{name}") when :subscribed then puts("Subscribed to #{name}") when :unsubscribe then puts("Unsubscription request from #{name}") when :unsubscribed then puts("Unsubscribed from #{name}") else raise "The Roster Helper is buggy!!! subscription callback with type=#{pres.type}" end } roster.add_subscription_callback(0, nil, &subscription_callback) roster.add_subscription_request_callback(0, nil, &subscription_callback) # Send initial presence # this is important for receiving presence of subscribed users cl.send(Jabber::Presence.new.set_show(:dnd).set_status('Watching my roster change...')) # Main loop: Thread.stop cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/roster.rb0000755000175000017500000000165513647121573023671 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script can get all roster entries #$:.unshift '../../../../../lib' require 'optparse' require 'xmpp4r' require 'xmpp4r/roster/iq/roster' include Jabber jid = JID.new('lucastest@linux.ensimag.fr/rosterget') password = 'lucastest' OptionParser.new do |opts| opts.banner = 'Usage: roster.rb -t get -j jid -p password' opts.separator '' opts.on('-j', '--jid JID', 'sets the jid') { |j| jid = JID.new(j) } opts.on('-p', '--password PASSWORD', 'sets the password') { |p| password = p } opts.on_tail('-h', '--help', 'Show this message') { puts opts exit } opts.parse!(ARGV) end cl = Client.new(jid) cl.connect cl.auth(password) cl.send(Iq.new_rosterget) exit = false cl.add_iq_callback { |i| if i.type == :result and i.query.kind_of?(Roster::IqQueryRoster) i.query.each_element { |e| e.text = '' puts e.to_s } exit = true end } while not exit cl.process end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/muc_owner_config.rb0000644000175000017500000000051113647121573025661 0ustar debbiecocoadebbiecocoarequire 'xmpp4r' require 'xmpp4r/muc' client = Jabber::Client.new('admin@myserver.co.uk/ruby') muc = Jabber::MUC::MUCClient.new(client) client.connect client.auth('admin') muc.join('room@conference.myserver.co.uk/admin') muc.configure( 'muc#roomconfig_roomname' => 'roomname', 'muc#roomconfig_persistentroom' => 1 ) xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/tune_client.rb0000755000175000017500000000232613647121573024660 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/roster' require 'xmpp4r/discovery' require 'xmpp4r/caps' require 'xmpp4r/tune' # # Echo tunes received via XEP-0118 User Tune notifications # # See Jabber::UserTune::Helper for the gory details # if ARGV.length != 3 puts "Usage: ruby tune_client.rb " exit 1 end jid = ARGV[0] pw = ARGV[1] dj_jid = ARGV[2] #Jabber::debug=true cl = Jabber::Client.new(Jabber::JID.new(jid)) cl.connect cl.auth(pw) # PEP (XEP-0163 says we need: # - a 'both' subscription to the dj # - told the server that we support the '...tune+notify' feature # before we can receive events roster = Jabber::Roster::Helper.new(cl) roster.add_subscription_request_callback(0, nil) do |item,pres| roster.accept_subscription(pres.from) end caps = Jabber::Caps::Helper.new(cl, [Jabber::Discovery::Identity.new('client', nil, 'pc')], [Jabber::Discovery::Feature.new('http://jabber.org/protocol/tune+notify')] ) t = Jabber::UserTune::Helper.new(cl, dj_jid) t.add_usertune_callback do |tune| puts "from:#{dj_jid} tune:#{tune.artist} plays #{tune.title}" end p = Jabber::Presence.new() p.type = :subscribe p.to = dj_jid cl.send p Thread.stop xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/mucsimplebot.rb0000755000175000017500000000351013647121573025046 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby $:.unshift '../../../../../lib/' require 'xmpp4r' require 'xmpp4r/muc/helper/simplemucclient' if ARGV.size != 3 puts "Usage: #{$0} " exit end # Print a line formatted depending on time.nil? def print_line(time, line) if time.nil? puts line else puts "#{time.strftime('%I:%M')} #{line}" end end #Jabber::debug = true cl = Jabber::Client.new(Jabber::JID.new(ARGV[0])) cl.connect cl.auth(ARGV[1]) # For waking up... mainthread = Thread.current # This is the SimpleMUCClient helper! m = Jabber::MUC::SimpleMUCClient.new(cl) # SimpleMUCClient callback-blocks m.on_join { |time,nick| print_line time, "#{nick} has joined!" puts "Users: " + m.roster.keys.join(', ') } m.on_leave { |time,nick| print_line time, "#{nick} has left!" } m.on_message { |time,nick,text| print_line time, "<#{nick}> #{text}" # Avoid reacting on messaged delivered as room history unless time # Bot: invite astro@spaceboyz.net if text.strip =~ /^(.+?): invite (.+)$/ jid = $2 if $1.downcase == m.jid.resource.downcase m.invite(jid => "Inviting you on behalf of #{nick}") m.say("Inviting #{jid}...") end # Bot: subject This is room is powered by XMPP4R elsif text.strip =~ /^(.+?): subject (.+)$/ if $1.downcase == m.jid.resource.downcase m.subject = $2 end # Bot: exit please elsif text.strip =~ /^(.+?): exit please$/ if $1.downcase == m.jid.resource.downcase puts "exiting" m.exit "Exiting on behalf of #{nick}" mainthread.wakeup end end end } m.on_room_message { |time,text| print_line time, "- #{text}" } m.on_subject { |time,nick,subject| print_line time, "*** (#{nick}) #{subject}" } m.join(ARGV[2]) # Wait for being waken up by m.on_message Thread.stop cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/register.rb0000755000175000017500000000144313647121573024172 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib' require 'xmpp4r' # Argument checking if ARGV.size < 2 puts("Usage: #{$0} [field1=value1] [fieldN=valueN]") exit end # The usual procedure cl = Jabber::Client.new(Jabber::JID.new(ARGV[0])) puts "Connecting" cl.connect # Registration of the new user account puts "Registering..." begin fields = {} ARGV[2..-1].each { |a| k, v = a.split('=', 2) fields[k] = v } cl.register(ARGV[1], fields) puts "Successful" rescue Jabber::ServerError => e puts "Error: #{e.error.text}" if e.error.type == :modify puts "Accepted registration information:" instructions, fields = cl.register_info fields.each { |info| puts "* #{info}" } puts "(#{instructions})" end end # Shutdown cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/remove_registration.rb0000644000175000017500000000046413647121573026434 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby $:.unshift '../../../../../lib' require 'xmpp4r' include Jabber if ARGV.size != 2 puts "Warning! This example UNREGISTERS user accounts!" puts "Usage: #{$0} " exit end cl = Client.new(JID.new(ARGV[0])) cl.connect cl.auth(ARGV[1]) cl.remove_registration cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/send_vcard.rb0000755000175000017500000000311413647121573024453 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # Demonstration of the Vcard helper class # # * Retrieves your own vCard # * Modify fields given on the command line # * Dumps the vCard # * Send your vCard to the server $:.unshift('../../../../../lib') require 'xmpp4r' require 'xmpp4r/vcard/helper/vcard' include Jabber # settings if ARGV.length < 2 puts "Usage:\t./send_vcard.rb [= [... =] ]" puts "Example:\t./send_vcard.rb user@server/resource password NICKNAME=User \"FN=A user\"" exit 1 end # Do the client stuff... myJID = JID.new(ARGV.shift) myPassword = ARGV.shift cl = Client.new(myJID) cl.connect cl.auth(myPassword) # The Vcard helper vcard_helper = Vcard::Helper.new(cl) begin puts "Retrieving vCard information for #{cl.jid.strip}" vcard = vcard_helper.get # Inspect the command line for vCard fields to be changed ARGV.each { |arg| arg.scan(/^(.+?)=(.*)$/) { |field,text| puts "field #{field}: #{vcard[field].inspect} => #{text.inspect}" vcard[field] = text } } # Dump the vCard vcard.fields.each { |field| if field.split(/\//).pop == 'BINVAL' puts "#{field}:\tBINVAL" else puts "#{field}:\t#{vcard[field].inspect}" end } begin puts "Sending vCard information for #{cl.jid.strip}" vcard_helper.set(vcard) rescue Exception => e puts "Sorry, we stumbled upon the following when sending the vCard of #{cl.jid.strip}: #{e.to_s.inspect}" end rescue Exception => e puts "Sorry, we stumbled upon the following when requesting the vCard of #{cl.jid.strip}: #{e.to_s.inspect}" end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/component.rb0000755000175000017500000000025613647121573024351 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib' require 'xmpp4r' include Jabber c = Component.new('tada', 'localhost', 60001) c.connect c.auth('jabber-rocks') Thread.stop xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/echo.rb0000755000175000017500000000144413647121573023265 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This bot will reply to every message it receives. To end the game, send 'exit' $:.unshift '../../../../../lib' require 'xmpp4r/client' include Jabber # settings if ARGV.length != 2 puts "Run with ./echo_thread.rb user@server/resource password" exit 1 end myJID = JID.new(ARGV[0]) myPassword = ARGV[1] cl = Client.new(myJID) cl.connect cl.auth(myPassword) cl.send(Presence.new) puts "Connected ! send messages to #{myJID.strip.to_s}." mainthread = Thread.current cl.add_message_callback do |m| if m.type != :error m2 = Message.new(m.from, "You sent: #{m.body}") m2.type = m.type cl.send(m2) if m.body == 'exit' m2 = Message.new(m.from, "Exiting ...") m2.type = m.type cl.send(m2) mainthread.wakeup end end end Thread.stop cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/mass_sender.rb0000755000175000017500000000331313647121573024647 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script will send a jabber message to a list of JID given on stdin. $:.unshift '../../../../../lib' require 'optparse' require 'xmpp4r' include Jabber #Jabber::debug = true # - message in file # - subject in command line # - JID list on stdin # settings jid = JID.new('bot@localhost/Bot') password = 'bot' filename = 'message.txt' subject = "Message de test" OptionParser.new do |opts| opts.banner = 'Usage: mass_sender.rb -j jid -p password' opts.separator '' opts.on('-j', '--jid JID', 'sets the jid') { |j| jid = JID.new(j) } opts.on('-p', '--password PASSWORD', 'sets the password') { |p| password = p } opts.on('-f', '--filename MESSAGE', 'sets the filename containing the message') { |f| filename = f } opts.on('-s', '--subject SUBJECT', 'sets the subject') { |s| subject = s } opts.on_tail('-h', '--help', 'Show this message') { puts opts exit } opts.parse!(ARGV) end body = IO::read(filename).chomp cl = Client.new(jid) cl.connect cl.auth(password) exit = false sent = [] cl.add_message_callback do |m| if m.type != :error if !sent.include?(m.from) cl.send(Message.new(m.from, "Je suis un robot. Si tu souhaites contacter un administrateur du serveur, envoie un message à lucas@nussbaum.fr ou rejoins la salle jabberfr@chat.jabberfr.org.")) sent << m.from end if m.body == 'exitnowplease' cl.send(Message.new(m.from, "Exiting ...")) exit = true end cl.send(Message.new('lucas@nussbaum.fr', "From #{m.from}: #{m.body.to_s}")) end end cl.send(Presence.new) m = Message.new(nil, body) m.subject = subject STDIN.each_line { |l| l.chomp! m.set_to(JID.new(l).to_s) cl.send(m) } while not exit do cl.process(1) end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/mucinfo.rb0000755000175000017500000000167513647121573024015 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/muc/helper/mucbrowser' if ARGV.size != 3 puts "Usage: #{$0} " exit end jid, password, muc_jid = Jabber::JID.new(ARGV.shift), ARGV.shift, Jabber::JID.new(ARGV.shift) cl = Jabber::Client.new(jid) cl.connect cl.auth(password) browser = Jabber::MUC::MUCBrowser.new(cl) print "Querying #{muc_jid} for identity..."; $stdout.flush name = browser.muc_name(muc_jid) if name.nil? puts " Sorry, but the queried MUC component doesn't seem to support MUC or Groupchat." else puts " #{name}" print "Querying #{muc_jid} for its rooms..."; $stdout.flush rooms = browser.muc_rooms(muc_jid) puts " #{rooms.size} rooms found" max_room_length = 0 rooms.each_key { |jid| max_room_length = jid.to_s.size if jid.to_s.size > max_room_length } rooms.each { |jid,name| puts "#{jid.to_s.ljust(max_room_length)} #{name}" } end cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/rosterrename.rb0000755000175000017500000000127213647121573025054 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/roster/iq/roster' # Command line argument checking if ARGV.size < 4 puts("Usage: ./rosterrename.rb [ ... ]") exit end # Building up the connection #Jabber::debug = true jid = Jabber::JID.new(ARGV[0]) cl = Jabber::Client.new(jid) cl.connect cl.auth(ARGV[1]) # The iq stanza iq = Jabber::Iq.new(:set) # The new roster instance and item element iq.add(Jabber::Roster::IqQueryRoster.new).add(Jabber::Roster::RosterItem.new(ARGV[2], ARGV[3])).groups = ARGV[4..ARGV.size] # Sending the stanza cl.send(iq) # Don't look at the results: cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/jabbersend.rb0000755000175000017500000000174413647121573024451 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # This script will send a jabber message to the specified JID. The subject can be # specified using the '-s' option, and the message will be taken from stdin. $:.unshift '../../../../../lib' require 'optparse' require 'xmpp4r' include Jabber # settings myJID = JID.new('bot@localhost/Bot') myPassword = 'bot' to = nil subject = '' OptionParser.new do |opts| opts.banner = 'Usage: jabbersend.rb -s \'subject\' -t dest@domain' opts.separator '' opts.on('-s', '--subject SUBJECT', 'sets the message\'s subject') { |s| subject = s } opts.on('-t', '--to DESTJID', 'sets the receiver') { |t| to = JID.new(t) } opts.on_tail('-h', '--help', 'Show this message') { puts opts exit } opts.parse!(ARGV) end if to.nil? puts "No receiver specified. See jabbersend -h" end cl = Client.new(myJID) cl.connect cl.auth(myPassword) body = STDIN.readlines.join m = Message.new(to, body).set_type(:normal).set_id('1').set_subject(subject) puts m.to_s cl.send(m) cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/change_password.rb0000755000175000017500000000141113647121573025510 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby # # A tool to change the password of a Jabber account # $:.unshift('../../../../../lib') require 'xmpp4r' include Jabber def with_status(str, &block) print "#{str}..." $stdout.flush begin yield puts " Ok" rescue Exception => e puts " Exception: #{e.to_s}" raise e end end # settings if ARGV.length != 3 puts "Run with ./change_password.rb user@server/resource oldpassword newpassword" exit 1 end my_jid = JID.new(ARGV[0]) my_jid.resource = 'change_password' if my_jid.resource.nil? old_password = ARGV[1] new_password = ARGV[2] cl = Client.new(my_jid) with_status('Connecting') { cl.connect } with_status('Authenticating') { cl.auth(old_password) } with_status('Changing password') { cl.password = new_password } cl.close xmpp4r-0.5.6/data/doc/xmpp4r/examples/basic/versionbot.rb0000755000175000017500000000344413647121573024543 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../../../../../lib' require 'xmpp4r' require 'xmpp4r/version/iq/version' require 'xmpp4r/version/helper/simpleresponder' # A Hash containing all Version Query answers with their JIDs as keys: versions = {} # Command line argument checking if ARGV.size != 2 puts("Usage: ./versionbot.rb ") exit end # Building up the connection #Jabber::debug = true jid = Jabber::JID.new(ARGV[0]) cl = Jabber::Client.new(jid) cl.connect cl.auth(ARGV[1]) cl.on_exception { |*a| p a[0].backtrace exit! } # I'm not sure about the portability of 'uname -sr' here ;-) # but that's all needed to answer version queries: Jabber::Version::SimpleResponder.new(cl, 'xmpp4r Versionbot example', Jabber::XMPP4R_VERSION, IO.popen('uname -sr').readlines.to_s.strip) cl.add_iq_callback { |iq| # Filter for version query results if (iq.type == :result) && iq.query.kind_of?(Jabber::Version::IqQueryVersion) puts "Version query result from #{iq.from}" # Keep track of results per JID versions[iq.from] = iq.query # Print details puts " Name: #{iq.query.iname.inspect}" puts " Version: #{iq.query.version.inspect}" puts " OS: #{iq.query.os.inspect}" end } cl.add_presence_callback { |pres| # Already fingerprinted or offline? unless versions.has_key?(pres.from) || (pres.type == :unavailable) || (pres.type == :error) # Construct a new query iq = Jabber::Iq.new(:get, pres.from) # and ask for the version iq.query = Jabber::Version::IqQueryVersion.new puts "Asking #{iq.to} for his/her/its version" versions[pres.from] = :asking cl.send(iq) end } # Send initial presence cl.send(Jabber::Presence.new.set_show(:xa).set_status('I am the evil fingerprinting robot')) # Main loop: loop do cl.process sleep(1) end cl.close xmpp4r-0.5.6/setup.rb0000755000175000017500000010651713647121573015707 0ustar debbiecocoadebbiecocoa#!/usr/bin/env ruby # # setup.rb # # Copyright (c) 2000-2005 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end unless Errno.const_defined?(:ENOTEMPTY) # Windows? module Errno class ENOTEMPTY # We do not raise this exception, implementation is not needed. end end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class ConfigTable include Enumerable def initialize(rbconfig) @rbconfig = rbconfig @items = [] @table = {} # options @install_prefix = nil @config_opt = nil @verbose = true @no_harm = false end attr_accessor :install_prefix attr_accessor :config_opt attr_writer :verbose def verbose? @verbose end attr_writer :no_harm def no_harm? @no_harm end def [](key) lookup(key).resolve(self) end def []=(key, val) lookup(key).set val end def names @items.map {|i| i.name } end def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or setup_rb_error "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def load_script(path, inst = nil) if File.file?(path) MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path end end def savefile '.config' end def load_savefile begin File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) self[k] = v.strip end rescue Errno::ENOENT setup_rb_error $!.message + "\n#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value? and i.value end } end def load_standard_entries standard_entries(@rbconfig).each do |ent| add ent end end def standard_entries(rbconfig) c = rbconfig rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V > 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = c['rubylibdir'] librubyverarch = c['archdir'] siteruby = c['sitedir'] siterubyver = c['sitelibdir'] siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = c['sitedir'] siterubyver = "$siteruby/#{version}" siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" siterubyver = siteruby siterubyverarch = "$siterubyver/#{c['arch']}" end parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') } if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end [ ExecItem.new('installdirs', 'std/site/home', 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ {|val, table| case val when 'std' table['rbdir'] = '$librubyver' table['sodir'] = '$librubyverarch' when 'site' table['rbdir'] = '$siterubyver' table['sodir'] = '$siterubyverarch' when 'home' setup_rb_error '$HOME was not set' unless ENV['HOME'] table['prefix'] = ENV['HOME'] table['rbdir'] = '$libdir/ruby' table['sodir'] = '$libdir/ruby' end }, PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', parameterize.call(c['libdir']), 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for system configuration files'), PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), 'the directory for local state data'), PathItem.new('libruby', 'path', libruby, 'the directory for ruby libraries'), PathItem.new('librubyver', 'path', librubyver, 'the directory for standard ruby libraries'), PathItem.new('librubyverarch', 'path', librubyverarch, 'the directory for standard ruby extensions'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] end private :standard_entries def load_multipackage_entries multipackage_entries().each do |ent| add ent end end def multipackage_entries [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] end private :multipackage_entries ALIASES = { 'std-ruby' => 'librubyver', 'stdruby' => 'librubyver', 'rubylibdir' => 'librubyver', 'archdir' => 'librubyverarch', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } def fixup ALIASES.each do |ali, name| @table[ali] = @table[name] end @items.freeze @table.freeze @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end def parse_opt(opt) m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" m.to_a[1,2] end def dllext @rbconfig['DLEXT'] end def value_config?(name) lookup(name).value? end class Item def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value? true end def value @value end def resolve(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < Item def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val case val when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' else setup_rb_error "config: --#{@name} accepts only yes/no for argument" end end end class PathItem < Item def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < Item def config_type 'program' end end class SelectItem < Item def initialize(name, selection, default, desc) super @ok = selection.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class ExecItem < Item def initialize(name, selection, desc, &block) super name, selection, nil, desc @ok = selection.split('/') @action = block end def config_type 'exec' end def value? false end def resolve(table) setup_rb_error "$#{name()} wrongly used as option value" end undef set def evaluate(val, table) v = val.strip.downcase unless @ok.include?(v) setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" end @action.call v, table end end class PackageSelectionItem < Item def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class MetaConfigEnvironment def initialize(config, installer) @config = config @installer = installer end def config_names @config.names end def config?(name) @config.key?(name) end def bool_config?(name) @config.lookup(name).config_type == 'bool' end def path_config?(name) @config.lookup(name).config_type == 'path' end def value_config?(name) @config.lookup(name).config_type != 'exec' end def add_config(item) @config.add item end def add_bool_config(name, default, desc) @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) @config.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) @config.lookup(name).default = default end def remove_config(name) @config.remove(name) end # For only multipackage def packages raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer @installer.packages end # For only multipackage def declare_packages(list) raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer @installer.packages = list end end end # class ConfigTable # This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # Does not check '/', it's too abnormal. dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(path) $stderr.puts "rm -f #{path}" if verbose? return if no_harm? force_remove_file path end def rm_rf(path) $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? remove_tree path end def remove_tree(path) if File.symlink?(path) remove_file path elsif File.dir?(path) remove_tree0 path else force_remove_file path end end def remove_tree0(path) Dir.foreach(path) do |ent| next if ent == '.' next if ent == '..' entpath = "#{path}/#{ent}" if File.symlink?(entpath) remove_file entpath elsif File.dir?(entpath) remove_tree0 entpath else force_remove_file entpath end end begin Dir.rmdir path rescue Errno::ENOTEMPTY # directory may not be empty end end def move_file(src, dest) force_remove_file dest begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def force_remove_file(path) begin remove_file path rescue end end def remove_file(path) File.chmod 0777, path File.unlink path end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(*args) $stderr.puts args.join(' ') if verbose? system(*args) or raise RuntimeError, "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end def ruby(*args) command config('rubyprog'), *args end def make(task = nil) command(*[config('makeprog'), task].compact) end def extdir?(dir) File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end def files_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.file?("#{dir}/#{ent}") } } end DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) def directories_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end # This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @config[key] end alias config get_config # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file?(srcfile(path)) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.4.1' Copyright = 'Copyright (c) 2000-2005 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end def ToplevelInstaller.multipackage? File.dir?(File.dirname($0) + '/packages') end def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) @config = config # cache @valid_task_re = nil end def config(key) @config[key] end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' parsearg_config init_installers exec_config exec_setup exec_install else case task when 'config', 'test' ; when 'clean', 'distclean' @config.load_savefile if File.exist?(@config.savefile) else @config.load_savefile end __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig" end def init_installers @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg when '-q', '--quiet' @config.verbose = false when '--verbose' @config.verbose = true when '--help' print_usage $stdout exit 0 when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def valid_task?(t) valid_task_re() =~ t end def valid_task_re @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ end def parsearg_no_options unless ARGV.empty? task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config evalopt = [] set = [] @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i @config.config_opt = ARGV.dup break end name, value = *@config.parse_opt(i) if @config.value_config?(name) @config[name] = value else evalopt.push [name, value] end set.push name end evalopt.each do |name, value| @config.lookup(name).evaluate value, @config end # Check if configuration is valid set.each do |n| @config[n] if @config.value_config?(n) end end def parsearg_install @config.no_harm = false @config.install_prefix = '' while a = ARGV.shift case a when '--no-harm' @config.no_harm = true when /\A--prefix=/ path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' @config.install_prefix = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, ' --help', 'print this message' out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' @config.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_test @installer.exec_test end def exec_show @config.each do |i| printf "%-20s %s\n", i.name, i.value if i.value? end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller include FileOperations def initialize(ardir_root, config) super @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end attr_reader :packages def packages=(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_test run_hook 'pre-test' each_selected_installers {|inst| inst.exec_test } run_hook 'post-test' end def exec_clean rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def run_hook(id) @root_installer.run_hook id end # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end end # class ToplevelInstallerMulti class Installer FILETYPES = %w( bin lib ext data conf man ) include FileOperations include HookScriptAPI def initialize(config, srcroot, objroot) @config = config @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end def noop(rel) end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # Config Access # # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end def verbose_off begin save, @config.verbose = @config.verbose?, false yield ensure @config.verbose = save end end # # TASK config # def exec_config exec_task_traverse 'config' end alias config_dir_bin noop alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end alias config_dir_data noop alias config_dir_conf noop alias config_dir_man noop def extconf ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) files_of(curr_srcdir()).each do |fname| update_shebang_line "#{curr_srcdir()}/#{fname}" end end alias setup_dir_lib noop def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end alias setup_dir_data noop alias setup_dir_conf noop alias setup_dir_man noop def update_shebang_line(path) return if no_harm? return if config('shebang') == 'never' old = Shebang.load(path) if old $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 new = new_shebang(old) return if new.to_s == old.to_s else return unless config('shebang') == 'all' new = Shebang.new(config('rubypath')) end $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? open_atomic_writer(path) {|output| File.open(path, 'rb') {|f| f.gets if old # discard output.puts new.to_s output.print f.read } } end def new_shebang(old) if /\Aruby/ =~ File.basename(old.cmd) Shebang.new(config('rubypath'), old.args) elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Shebang.new(config('rubypath'), old.args[1..-1]) else return old unless config('shebang') == 'all' Shebang.new(config('rubypath')) end end def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin File.open(tmpfile, 'wb', &block) File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end class Shebang def Shebang.load(path) line = nil File.open(path) {|f| line = f.gets } return nil unless /\A#!/ =~ line parse(line) end def Shebang.parse(line) cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') new(cmd, args) end def initialize(cmd, args = []) @cmd = cmd @args = args end attr_reader :cmd attr_reader :args def to_s "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") end end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files rubyextentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 end def install_dir_conf(rel) # FIXME: should not remove current config files # (rename previous file to .old/.org) install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 end def install_dir_man(rel) install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @config.install_prefix list.each do |fname| install fname, dest, mode, @config.install_prefix end end def libfiles glob_reject(%w(*.y *.output), targetfiles()) end def rubyextentions(dir) ents = glob_select("*.#{@config.dllext}", targetfiles()) if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end ents end def targetfiles mapdir(existfiles() - hookfiles()) end def mapdir(ents) ents.map {|ent| if File.exist?(ent) then ent # objdir else "#{curr_srcdir()}/#{ent}" # srcdir end } end # picked up many entries from cvs-1.11.1/src/ignore.c JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) def existfiles glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def glob_select(pat, ents) re = globs2re([pat]) ents.select {|ent| re =~ ent } end def glob_reject(pats, ents) re = globs2re(pats) ents.reject {|ent| re =~ ent } end GLOB2REGEX = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } def globs2re(pats) /\A(?:#{ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') })\z/ end # # TASK test # TESTDIR = 'test' def exec_test unless File.directory?('test') $stderr.puts 'no test in this package' if verbose? return end $stderr.puts 'Running tests...' if verbose? begin require 'test/unit' rescue LoadError setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' end runner = Test::Unit::AutoRunner.new(true) runner.to_run << TESTDIR runner.run end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f @config.savefile rm_f 'InstalledFiles' end alias clean_dir_bin noop alias clean_dir_lib noop alias clean_dir_data noop alias clean_dir_conf noop alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f @config.savefile rm_f 'InstalledFiles' end alias distclean_dir_bin noop alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end alias distclean_dir_data noop alias distclean_dir_conf noop alias distclean_dir_man noop # # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end def run_hook(id) path = [ "#{curr_srcdir()}/#{id}", "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } return unless path begin instance_eval File.read(path), path, 1 rescue raise if $DEBUG setup_rb_error "hook #{path} failed:\n" + $!.message end end end # class Installer class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end if $0 == __FILE__ begin ToplevelInstaller.invoke rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end xmpp4r-0.5.6/LICENSE0000644000175000017500000000516113647121573015215 0ustar debbiecocoadebbiecocoaXMPP4R is copyrighted free software by Lucas Nussbaum , Stephan Maka , and others. You can redistribute it and/or modify it under either the terms of the GPL (see COPYING file), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or executable form, provided that you do at least ONE of the following: a) distribute the executables and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard executables non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under this terms. They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some files under the ./missing directory. See each file for the copying condition. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. xmpp4r-0.5.6/README.rdoc0000644000175000017500000000407713647121573016023 0ustar debbiecocoadebbiecocoa= XMPP4R A XMPP/Jabber library for ruby == Project Home You can find info about the xmpp4r project and how to contribute at the project home page: http://xmpp4r.github.io If you need to ask questions, or want feedback on proposed changes please feel free to ask them on the github issues tracker. https://github.com/xmpp4r/xmpp4r/issues If you are having a problem and would like to report it please include a protocol dump which can be enabled in your code with: Jabber::debug = true == Contributors === Present Maintainer Harisankar P S (hsps@rubykitchen.org) === List of Contributors http://github.com/xmpp4r/xmpp4r/contributors == Installation There are a number of ways that you can install xmpp4r depending on your needs and the methods you prefer. === Install over the network using RubyGems You can install the current release of the xmpp4r library via rubygems: sudo gem install xmpp4r The latest stable version is 0.5.6 === Install from local source code (Developers Only) If you have a local Git clone of the source repository or a tarball you can install xmpp4r using several methods. First you'll need to get a local copy. Clone the Git repository (recommended): git clone git://github.com/xmpp4r/xmpp4r.git OR download a snapshot of the latest source in .tar.gz format from: http://github.com/xmpp4r/xmpp4r/tarball/master ==== Install : Using Rake # Show all available rake tasks cd xmpp4r/ rake -T # Package up the gem file and install it rake gem:install ==== Install : Using setup.rb This will install a copy of the library in your Ruby path and does not require RubyGems to be installed. cd xmpp4r/ ./setup.rb == Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request == License XMPP4R is released under the Ruby license (see the LICENSE file), which is compatible with the GNU GPL (see the COPYING file) via an explicit dual-licensing clause. xmpp4r-0.5.6/xmpp4r.gemspec0000644000175000017500000002577613647121573017025 0ustar debbiecocoadebbiecocoa# WARNING : RAKE AUTO-GENERATED FILE. DO NOT MANUALLY EDIT! # RUN : 'rake gem:update_gemspec' Gem::Specification.new do |s| s.activated = false s.authors = ["Lucas Nussbaum", "Stephan Maka", "Glenn Rempe", "Kaoru Maeda", "Harisankar P S"] s.bindir = "bin" s.description = "XMPP/Jabber library for ruby" s.email = "mailme@hsps.in" s.extra_rdoc_files = ["CHANGELOG", "COPYING", "LICENSE", "README.rdoc", "README_ruby19.txt"] s.files = ["CHANGELOG", "COPYING", "LICENSE", "README.rdoc", "README_ruby19.txt", "Rakefile", "data/doc", "data/doc/xmpp4r", "data/doc/xmpp4r/examples", "data/doc/xmpp4r/examples/advanced", "data/doc/xmpp4r/examples/advanced/adventure", "data/doc/xmpp4r/examples/advanced/adventure/README", "data/doc/xmpp4r/examples/advanced/adventure/adventure.rb", "data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb", "data/doc/xmpp4r/examples/advanced/adventure/cube.xml", "data/doc/xmpp4r/examples/advanced/adventure/tower.xml", "data/doc/xmpp4r/examples/advanced/adventure/world.rb", "data/doc/xmpp4r/examples/advanced/fileserve.conf", "data/doc/xmpp4r/examples/advanced/fileserve.rb", "data/doc/xmpp4r/examples/advanced/getonline.rb", "data/doc/xmpp4r/examples/advanced/gtkmucclient.rb", "data/doc/xmpp4r/examples/advanced/migrate.rb", "data/doc/xmpp4r/examples/advanced/minimuc.rb", "data/doc/xmpp4r/examples/advanced/pep-aggregator", "data/doc/xmpp4r/examples/advanced/pep-aggregator/index.xsl", "data/doc/xmpp4r/examples/advanced/pep-aggregator/pep-aggregator.rb", "data/doc/xmpp4r/examples/advanced/recvfile.rb", "data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb", "data/doc/xmpp4r/examples/advanced/sendfile.conf", "data/doc/xmpp4r/examples/advanced/sendfile.rb", "data/doc/xmpp4r/examples/advanced/shellmgr", "data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb", "data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb", "data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb", "data/doc/xmpp4r/examples/advanced/versionpoll.rb", "data/doc/xmpp4r/examples/advanced/xmpping.rb", "data/doc/xmpp4r/examples/advanced/xmppingrc.sample", "data/doc/xmpp4r/examples/basic", "data/doc/xmpp4r/examples/basic/change_password.rb", "data/doc/xmpp4r/examples/basic/client.rb", "data/doc/xmpp4r/examples/basic/component.rb", "data/doc/xmpp4r/examples/basic/echo.rb", "data/doc/xmpp4r/examples/basic/jabbersend.rb", "data/doc/xmpp4r/examples/basic/mass_sender.rb", "data/doc/xmpp4r/examples/basic/muc_owner_config.rb", "data/doc/xmpp4r/examples/basic/mucinfo.rb", "data/doc/xmpp4r/examples/basic/mucsimplebot.rb", "data/doc/xmpp4r/examples/basic/register.rb", "data/doc/xmpp4r/examples/basic/remove_registration.rb", "data/doc/xmpp4r/examples/basic/roster.rb", "data/doc/xmpp4r/examples/basic/rosterprint.rb", "data/doc/xmpp4r/examples/basic/rosterrename.rb", "data/doc/xmpp4r/examples/basic/rosterwatch.rb", "data/doc/xmpp4r/examples/basic/send_vcard.rb", "data/doc/xmpp4r/examples/basic/tune_client.rb", "data/doc/xmpp4r/examples/basic/tune_server.rb", "data/doc/xmpp4r/examples/basic/versionbot.rb", "lib/xmpp4r", "lib/xmpp4r.rb", "lib/xmpp4r/base64.rb", "lib/xmpp4r/bytestreams", "lib/xmpp4r/bytestreams.rb", "lib/xmpp4r/bytestreams/helper", "lib/xmpp4r/bytestreams/helper/filetransfer.rb", "lib/xmpp4r/bytestreams/helper/ibb", "lib/xmpp4r/bytestreams/helper/ibb/base.rb", "lib/xmpp4r/bytestreams/helper/ibb/initiator.rb", "lib/xmpp4r/bytestreams/helper/ibb/target.rb", "lib/xmpp4r/bytestreams/helper/socks5bytestreams", "lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb", "lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb", "lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb", "lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb", "lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb", "lib/xmpp4r/bytestreams/iq", "lib/xmpp4r/bytestreams/iq/bytestreams.rb", "lib/xmpp4r/bytestreams/iq/si.rb", "lib/xmpp4r/callbacks.rb", "lib/xmpp4r/caps", "lib/xmpp4r/caps.rb", "lib/xmpp4r/caps/c.rb", "lib/xmpp4r/caps/helper", "lib/xmpp4r/caps/helper/generator.rb", "lib/xmpp4r/caps/helper/helper.rb", "lib/xmpp4r/client.rb", "lib/xmpp4r/command", "lib/xmpp4r/command/helper", "lib/xmpp4r/command/helper/responder.rb", "lib/xmpp4r/command/iq", "lib/xmpp4r/command/iq/command.rb", "lib/xmpp4r/component.rb", "lib/xmpp4r/connection.rb", "lib/xmpp4r/dataforms", "lib/xmpp4r/dataforms.rb", "lib/xmpp4r/dataforms/x", "lib/xmpp4r/dataforms/x/data.rb", "lib/xmpp4r/debuglog.rb", "lib/xmpp4r/delay", "lib/xmpp4r/delay.rb", "lib/xmpp4r/delay/x", "lib/xmpp4r/delay/x/delay.rb", "lib/xmpp4r/discovery", "lib/xmpp4r/discovery.rb", "lib/xmpp4r/discovery/helper", "lib/xmpp4r/discovery/helper/helper.rb", "lib/xmpp4r/discovery/helper/responder.rb", "lib/xmpp4r/discovery/iq", "lib/xmpp4r/discovery/iq/discoinfo.rb", "lib/xmpp4r/discovery/iq/discoitems.rb", "lib/xmpp4r/entity_time", "lib/xmpp4r/entity_time.rb", "lib/xmpp4r/entity_time/iq.rb", "lib/xmpp4r/entity_time/responder.rb", "lib/xmpp4r/errors.rb", "lib/xmpp4r/feature_negotiation", "lib/xmpp4r/feature_negotiation.rb", "lib/xmpp4r/feature_negotiation/iq", "lib/xmpp4r/feature_negotiation/iq/feature.rb", "lib/xmpp4r/framework", "lib/xmpp4r/framework/base.rb", "lib/xmpp4r/framework/bot.rb", "lib/xmpp4r/httpbinding", "lib/xmpp4r/httpbinding.rb", "lib/xmpp4r/httpbinding/client.rb", "lib/xmpp4r/idgenerator.rb", "lib/xmpp4r/iq.rb", "lib/xmpp4r/jid.rb", "lib/xmpp4r/last", "lib/xmpp4r/last.rb", "lib/xmpp4r/last/helper", "lib/xmpp4r/last/helper/helper.rb", "lib/xmpp4r/last/iq", "lib/xmpp4r/last/iq/last.rb", "lib/xmpp4r/location", "lib/xmpp4r/location.rb", "lib/xmpp4r/location/helper", "lib/xmpp4r/location/helper/helper.rb", "lib/xmpp4r/location/location.rb", "lib/xmpp4r/message.rb", "lib/xmpp4r/muc", "lib/xmpp4r/muc.rb", "lib/xmpp4r/muc/helper", "lib/xmpp4r/muc/helper/mucbrowser.rb", "lib/xmpp4r/muc/helper/mucclient.rb", "lib/xmpp4r/muc/helper/simplemucclient.rb", "lib/xmpp4r/muc/iq", "lib/xmpp4r/muc/iq/mucadmin.rb", "lib/xmpp4r/muc/iq/mucadminitem.rb", "lib/xmpp4r/muc/iq/mucowner.rb", "lib/xmpp4r/muc/item.rb", "lib/xmpp4r/muc/x", "lib/xmpp4r/muc/x/muc.rb", "lib/xmpp4r/muc/x/mucuserinvite.rb", "lib/xmpp4r/muc/x/mucuseritem.rb", "lib/xmpp4r/observable", "lib/xmpp4r/observable.rb", "lib/xmpp4r/observable/contact.rb", "lib/xmpp4r/observable/helper.rb", "lib/xmpp4r/observable/observable_thing.rb", "lib/xmpp4r/observable/pubsub.rb", "lib/xmpp4r/observable/subscription.rb", "lib/xmpp4r/observable/thread_store.rb", "lib/xmpp4r/presence.rb", "lib/xmpp4r/pubsub", "lib/xmpp4r/pubsub.rb", "lib/xmpp4r/pubsub/children", "lib/xmpp4r/pubsub/children/configuration.rb", "lib/xmpp4r/pubsub/children/event.rb", "lib/xmpp4r/pubsub/children/item.rb", "lib/xmpp4r/pubsub/children/items.rb", "lib/xmpp4r/pubsub/children/node_config.rb", "lib/xmpp4r/pubsub/children/publish.rb", "lib/xmpp4r/pubsub/children/retract.rb", "lib/xmpp4r/pubsub/children/subscription.rb", "lib/xmpp4r/pubsub/children/subscription_config.rb", "lib/xmpp4r/pubsub/children/unsubscribe.rb", "lib/xmpp4r/pubsub/helper", "lib/xmpp4r/pubsub/helper/nodebrowser.rb", "lib/xmpp4r/pubsub/helper/nodehelper.rb", "lib/xmpp4r/pubsub/helper/oauth_service_helper.rb", "lib/xmpp4r/pubsub/helper/servicehelper.rb", "lib/xmpp4r/pubsub/iq", "lib/xmpp4r/pubsub/iq/pubsub.rb", "lib/xmpp4r/query.rb", "lib/xmpp4r/reliable.rb", "lib/xmpp4r/rexmladdons.rb", "lib/xmpp4r/roster", "lib/xmpp4r/roster.rb", "lib/xmpp4r/roster/helper", "lib/xmpp4r/roster/helper/roster.rb", "lib/xmpp4r/roster/iq", "lib/xmpp4r/roster/iq/roster.rb", "lib/xmpp4r/roster/x", "lib/xmpp4r/roster/x/roster.rb", "lib/xmpp4r/rpc", "lib/xmpp4r/rpc.rb", "lib/xmpp4r/rpc/helper", "lib/xmpp4r/rpc/helper/client.rb", "lib/xmpp4r/rpc/helper/server.rb", "lib/xmpp4r/rpc/helper/xmlrpcaddons.rb", "lib/xmpp4r/rpc/iq", "lib/xmpp4r/rpc/iq/rpc.rb", "lib/xmpp4r/sasl.rb", "lib/xmpp4r/semaphore.rb", "lib/xmpp4r/stream.rb", "lib/xmpp4r/streamparser.rb", "lib/xmpp4r/test", "lib/xmpp4r/test/listener_mocker.rb", "lib/xmpp4r/tune", "lib/xmpp4r/tune.rb", "lib/xmpp4r/tune/helper", "lib/xmpp4r/tune/helper/helper.rb", "lib/xmpp4r/tune/tune.rb", "lib/xmpp4r/vcard", "lib/xmpp4r/vcard.rb", "lib/xmpp4r/vcard/helper", "lib/xmpp4r/vcard/helper/vcard.rb", "lib/xmpp4r/vcard/iq", "lib/xmpp4r/vcard/iq/vcard.rb", "lib/xmpp4r/version", "lib/xmpp4r/version.rb", "lib/xmpp4r/version/helper", "lib/xmpp4r/version/helper/responder.rb", "lib/xmpp4r/version/helper/simpleresponder.rb", "lib/xmpp4r/version/iq", "lib/xmpp4r/version/iq/version.rb", "lib/xmpp4r/x.rb", "lib/xmpp4r/xhtml", "lib/xmpp4r/xhtml.rb", "lib/xmpp4r/xhtml/html.rb", "lib/xmpp4r/xmpp4r.rb", "lib/xmpp4r/xmppelement.rb", "lib/xmpp4r/xmppstanza.rb", "setup.rb", "test/bytestreams", "test/bytestreams/tc_ibb.rb", "test/bytestreams/tc_socks5bytestreams.rb", "test/caps", "test/caps/tc_helper.rb", "test/dataforms", "test/dataforms/tc_data.rb", "test/delay", "test/delay/tc_xdelay.rb", "test/discovery", "test/discovery/tc_responder.rb", "test/entity_time", "test/entity_time/tc_responder.rb", "test/last", "test/last/tc_helper.rb", "test/lib", "test/lib/assert_equal_xml.rb", "test/lib/clienttester.rb", "test/muc", "test/muc/tc_muc_mucclient.rb", "test/muc/tc_muc_simplemucclient.rb", "test/muc/tc_mucowner.rb", "test/pubsub", "test/pubsub/tc_helper.rb", "test/pubsub/tc_nodeconfig.rb", "test/pubsub/tc_subscriptionconfig.rb", "test/reliable", "test/reliable/tc_disconnect_cleanup.rb", "test/reliable/tc_disconnect_exception.rb", "test/reliable/tc_listener_mocked_test.rb", "test/reliable/tc_reliable_connection.rb", "test/roster", "test/roster/tc_helper.rb", "test/roster/tc_iqqueryroster.rb", "test/roster/tc_xroster.rb", "test/rpc", "test/rpc/tc_helper.rb", "test/tc_callbacks.rb", "test/tc_class_names.rb", "test/tc_client.rb", "test/tc_errors.rb", "test/tc_idgenerator.rb", "test/tc_iq.rb", "test/tc_iqquery.rb", "test/tc_jid.rb", "test/tc_message.rb", "test/tc_presence.rb", "test/tc_rexml.rb", "test/tc_stream.rb", "test/tc_streamComponent.rb", "test/tc_streamError.rb", "test/tc_streamSend.rb", "test/tc_streamparser.rb", "test/tc_xmppstanza.rb", "test/ts_xmpp4r.rb", "test/tune", "test/tune/tc_helper_recv.rb", "test/tune/tc_helper_send.rb", "test/tune/tc_tune.rb", "test/vcard", "test/vcard/tc_helper.rb", "test/vcard/tc_iqvcard.rb", "test/version", "test/version/tc_helper.rb", "test/version/tc_iqqueryversion.rb", "test/xhtml", "test/xhtml/tc_html.rb", "tools/gen_requires.bash", "tools/xmpp4r-gemspec-test.rb", "xmpp4r.gemspec"] s.has_rdoc = true s.homepage = "http://xmpp4r.github.io" s.name = "xmpp4r" s.platform = "ruby" s.rdoc_options = ["--quiet", "--title", "XMPP/Jabber library for ruby", "--opname", "index.html", "--main", "lib/xmpp4r.rb", "--line-numbers", "--inline-source"] s.require_paths = ["lib"] s.required_ruby_version = ">= 1.8.4" s.required_rubygems_version = ">= 0" s.rubyforge_project = "xmpp4r" s.rubygems_version = "1.8.23" s.specification_version = 3 s.summary = "XMPP/Jabber library for ruby" s.version = "0.5.6" endxmpp4r-0.5.6/README_ruby19.txt0000644000175000017500000000414413647121573017121 0ustar debbiecocoadebbiecocoaRuby 1.9 is a development release in anticipation of Ruby 2.0, which has overall better performance, real threading, and character encoding support. Note: Ruby 1.9 is a development release, meaning that everything is subject to change without prior notice. Among other things, this means that xmpp4r could stop working on Ruby 1.9 at any time. This version of xmpp4r has made a number of internal changes (nothing visible at the API) to remove depency on deprecated Ruby Kernel APIs, support the new encoding APIs, etc. At the present time, all tests pass except tc_helper.rb and tc_stream.rb. These tests themselves make assumptions about timinings of events, assumptions that are not guaranteed with true multi-tasking. Initial analysis indicates that xmpp4r is operating correctly, it is the tests themselves that need to be corrected, but this could turn out to be incorrect. The executing of these two tests are disabled by a check in ts_xmpp4r.rb, which is marked as a TODO. A specific example: test_bidi in test/tc_stream.rb defines two threads, one pumps out requests, the other echoes them. The receiver then verifies that it gets back what it sent. With Ruby 1.8, these threads tend to alternate in lock step, and the test usually passes. What happens in Ruby 1.9 is that the first thread waits for a message, and the second one creates a callback block, generates a message, and then proceeds on to create a second callback block -- even before the first message has been responded to. The way xmpp4r works is that callbacks are saved on a pushdown stack. The net result is that the first response typically is processed first by the second callback, which decides that the ids don't match, and the test fails. The way it is supposed to work is that the reply callback is supposed to only process requests destined for it (and return true) and ignore everything else (returning false). This is but one test. Many of the tests in these two files are of this nature. The current status of the tests that are expected to pass on Ruby 1.9 can generally be found here: http://intertwingly.net/projects/ruby19/logs/xmpp4r.html xmpp4r-0.5.6/lib/0000755000175000017500000000000013647121573014753 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r.rb0000755000175000017500000001272213647121573016541 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # ==Introduction # # XMPP4R is a XMPP/Jabber library for Ruby. It can be used to build scripts # using Jabber, full-featured Jabber clients, and components. It is written # with extensibility in mind. # # ==XML management # # All the XML parsing is REXML's, and XML stanzas like (class # Jabber::Message) or (class Jabber::Iq) are indirect # derivatives from REXML's Element class. This provide a maximum flexibity: # the user can access attributes and childs using either the XMPP4R's helpers # or directly using REXML's methods. # # ===Automatic element casting # # Because there are special classes derived from REXML::Element to ease # development on the protocol level, Elements must be cast to them. This is # done via REXML::Element.import. This method is also used in import class # methods of some Element classes. # # The first occurance of this feature is in Jabber::Stream::receive: # * stanzas are cast to Jabber::Message class # * stanzas are cast to Jabber::Presence class # * stanzas are cast to Jabber::Iq class # # This is not only useful for stanzas but all other XML processing, too: # * children elements of and are converted to Jabber::X # * children elements of all three stanzas are converted to Jabber::ErrorResponse # * children elements of are converted to Jabber::IqQuery # * children elements of are converted to Jabber::IqVcard # # The following conversion facilities are only executed if the respective # library parts are loaded. See below for more details on Non-basic features. # * Jabber::IqQuery elements are converted to Jabber::Roster::IqQueryRoster if their # namespace is 'jabber:iq:roster' # * Jabber::IqQuery elements are converted to Jabber::Version::IqQueryVersion if their # namespace is 'jabber:iq:version' # * Jabber::IqQuery elements are converted to Jabber::Discovery::IqQueryDiscoInfo if their # namespace is 'http://jabber.org/protocol/disco#info' # * Jabber::IqQuery elements are converted to Jabber::Discovery::IqQueryDiscoItems if their # namespace is 'http://jabber.org/protocol/disco#items' # * children elements of Jabber::Roster::IqQueryRoster are converted # to Jabber::Roster::RosterItem # * children elements of Jabber::IqQueryDiscoInfo are converted # to Jabber::Discovery::DiscoIdentity # * children elements of Jabber::IqQueryDiscoInfo are converted # to Jabber::Discovery::DiscoFeature # * children elements of Jabber::IqQueryDiscoItems are converted # to Jabber::Discovery::DiscoItem # # To use this, don't check for: # iq.queryns == 'http://jabber.org/protocol/disco#info' # # But instead check for the query's class: # iq.query.kind_of?(Jabber::IqQueryDiscoInfo) # # ==Where to begin? # # Because it is built in an extensible way, it might be hard for newcomers to # understand where to look at documentation for a specific method. For example, # Client inherits from Connection, which itself inherits from Stream. # # A newcomer should have a look at the Jabber::Client and # Jabber::Component classes, and their parent classes # Jabber::Connection and Jabber::Stream. The best way to # understand how to use them is probably to look at the examples in the # examples/ dir. # # ==Non-basic features # # require 'xmpp4r' only includes basic functionality for # Connections, Authentication, Stream processing, Callbacks, Stanza handling # and Debugging to keep the library's footprint small. # # There is code for features that aren't required by a *basic* client. These # must be additionally included to use them. # # ===Protocol-level features # # You're highly advised to read the according RFCs and JEPs if you intend to # use them. The benefit will be that you'll understand the protocols and be # going to be more efficient when programming with them. # # * Jabber::Bytestreams, Jabber::FileTransfer: require 'xmpp4r/bytestreams' # * Jabber::Dataforms: require 'xmpp4r/dataforms' # * Jabber::Delay: require 'xmpp4r/delay' # * Jabber::Discovery: require 'xmpp4r/discovery' # * Jabber::FeatureNegotiation: require 'xmpp4r/feature_negotiation' # * Jabber::MUC: require 'xmpp4r/muc' # * Jabber::Roster: require 'xmpp4r/roster' # * Jabber::Vcard: require 'xmpp4r/vcard' # * Jabber::Version: require 'xmpp4r/version' # # ===Helpers # # Helpers are intended to give more simplistic interfaces to various tasks # of Jabber clients at the cost of flexibility. But you won't need that # level of flexibility in most cases. # # * Jabber::Roster::Helper: require 'xmpp4r/roster' # * Jabber::MUC::MUCBrowser, Jabber::MUC::MUCClient, Jabber::MUC::SimpleMUCClient: require 'xmpp4r/muc' # * Jabber::Version::SimpleResponder, Jabber::Version::Responder: require 'xmpp4r/version' # * Jabber::Vcard::Helper: require 'xmpp4r/vcard' # * Jabber::FileTransfer::Helper, Jabber::Bytestreams::SOCKS5BytestreamsServer: require 'xmpp4r/bytestreams' # # ==Debugging # # Dumping your Jabber stream can be enabled this way: # Jabber::debug = true require 'xmpp4r/xmpp4r' xmpp4r-0.5.6/lib/xmpp4r/0000755000175000017500000000000013647121573016205 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/client.rb0000644000175000017500000002434113647121573020014 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'resolv' require 'xmpp4r/connection' require 'xmpp4r/sasl' module Jabber # The client class provides everything needed to build a basic XMPP # Client. # # If you want your connection to survive disconnects and timeouts, # catch exception in Stream#on_exception and re-call Client#connect # and Client#auth. Don't forget to re-send initial Presence and # everything else you need to setup your session. class Client < Connection # The client's JID attr_reader :jid ## # Create a new Client. # # Remember to *always* put a resource in your JID unless the server can do SASL. def initialize(jid) super() @jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s)) @authenticated = false end ## # connect to the server # (chaining-friendly) # # If you omit the optional host argument SRV records for your jid will # be resolved. If none works, fallback is connecting to the domain part # of the jid. # host:: [String] Optional c2s host, will be extracted from jid if nil # port:: [Fixnum] The server port (default: 5222) # return:: self def connect(host = nil, port = 5222) if host.nil? begin srv = [] Resolv::DNS.open { |dns| # If ruby version is too old and SRV is unknown, this will raise a NameError # which is caught below Jabber::debuglog("RESOLVING:\n_xmpp-client._tcp.#{@jid.domain} (SRV)") srv = dns.getresources("_xmpp-client._tcp.#{@jid.domain}", Resolv::DNS::Resource::IN::SRV) } # Sort SRV records: lowest priority first, highest weight first srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) } srv.each { |record| begin connect(record.target.to_s, record.port) # Success return self rescue SocketError, Errno::ECONNREFUSED # Try next SRV record end } rescue NameError Jabber::debuglog "Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later!" end # Fallback to normal connect method end super(host.nil? ? jid.domain : host, port) self end ## # Close the connection, # sends tag first def close if @status == CONNECTED send("") end super end ## # Start the stream-parser and send the client-specific stream opening element def start super send(generate_stream_start(@jid.domain)) { |e| if e.name == 'stream' true else false end } end ## # Authenticate with the server # # Throws ClientAuthenticationFailure # # Authentication mechanisms are used in the following preference: # * SASL DIGEST-MD5 # * SASL PLAIN # * Non-SASL digest # password:: [String] def auth(password) begin if @stream_mechanisms.include? 'DIGEST-MD5' auth_sasl SASL.new(self, 'DIGEST-MD5'), password elsif @stream_mechanisms.include? 'PLAIN' auth_sasl SASL.new(self, 'PLAIN'), password else auth_nonsasl(password) end @authenticated = true rescue Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}") raise ClientAuthenticationFailure.new, $!.to_s end end ## # Resource binding (RFC3920bis-06 - section 8.) # # XMPP allows to bind to multiple resources def bind(desired_resource=nil) iq = Iq.new(:set) bind = iq.add REXML::Element.new('bind') bind.add_namespace @stream_features['bind'] if desired_resource resource = bind.add REXML::Element.new('resource') resource.text = desired_resource end jid = nil semaphore = Semaphore.new send_with_id(iq) do |reply| reply_bind = reply.first_element('bind') if reply_bind reported_jid = reply_bind.first_element('jid') if reported_jid and reported_jid.text jid = JID.new(reported_jid.text) end end semaphore.run end semaphore.wait jid end ## # Resource unbinding (RFC3920bis-06 - section 8.6.3.) def unbind(desired_resource) iq = Iq.new(:set) unbind = iq.add REXML::Element.new('unbind') unbind.add_namespace @stream_features['unbind'] resource = unbind.add REXML::Element.new('resource') resource.text = desired_resource send_with_id(iq) end ## # Use a SASL authentication mechanism and bind to a resource # # If there was no resource given in the jid, the jid/resource # generated by the server will be accepted. # # This method should not be used directly. Instead, Client#auth # may look for the best mechanism suitable. # sasl:: Descendant of [Jabber::SASL::Base] # password:: [String] def auth_sasl(sasl, password) sasl.auth(password) # Restart stream after SASL auth restart # And wait for features - again @features_sem.wait # Resource binding (RFC3920 - 7) if @stream_features.has_key? 'bind' Jabber::debuglog("**********Handling bind") @jid = bind(@jid.resource) end # Session starting if @stream_features.has_key? 'session' iq = Iq.new(:set) session = iq.add REXML::Element.new('session') session.add_namespace @stream_features['session'] semaphore = Semaphore.new send_with_id(iq) { semaphore.run } semaphore.wait end end def restart stop start end ## # See Client#auth_anonymous_sasl def auth_anonymous auth_anonymous_sasl end ## # Shortcut for anonymous connection to server # # Throws ClientAuthenticationFailure def auth_anonymous_sasl if self.supports_anonymous? begin auth_sasl SASL.new(self, 'ANONYMOUS'), "" rescue Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}") raise ClientAuthenticationFailure, $!.to_s end else raise ClientAuthenticationFailure, 'Anonymous authentication unsupported' end end ## # Reports whether or not anonymous authentication is reported # by the client. # # Returns true or false def supports_anonymous? @stream_mechanisms.include? 'ANONYMOUS' end ## # Send auth with given password and wait for result # (non-SASL) # # Throws ServerError # password:: [String] the password # digest:: [Boolean] use Digest authentication def auth_nonsasl(password, digest=true) authset = nil if digest authset = Iq.new_authset_digest(@jid, @streamid.to_s, password) else authset = Iq.new_authset(@jid, password) end send_with_id(authset) $>.flush true end ## # Get instructions and available fields for registration # return:: [instructions, fields] Where instructions is a String and fields is an Array of Strings def register_info instructions = nil fields = [] reg = Iq.new_registerget reg.to = jid.domain send_with_id(reg) do |answer| if answer.query answer.query.each_element { |e| if e.namespace == 'jabber:iq:register' if e.name == 'instructions' instructions = e.text.strip else fields << e.name end end } end true end [instructions, fields] end ## # Register a new user account # (may be used instead of Client#auth) # # This method may raise ServerError if the registration was # not successful. # # password:: String # fields:: {String=>String} additional registration information # # XEP-0077 Defines the following fields for registration information: # http://www.xmpp.org/extensions/xep-0077.html # # 'username' => 'Account name associated with the user' # 'nick' => 'Familiar name of the user' # 'password' => 'Password or secret for the user' # 'name' => 'Full name of the user' # 'first' => 'First name or given name of the user' # 'last' => 'Last name, surname, or family name of the user' # 'email' => 'Email address of the user' # 'address' => 'Street portion of a physical or mailing address' # 'city' => 'Locality portion of a physical or mailing address' # 'state' => 'Region portion of a physical or mailing address' # 'zip' => 'Postal code portion of a physical or mailing address' # 'phone' => 'Telephone number of the user' # 'url' => 'URL to web page describing the user' # 'date' => 'Some date (e.g., birth date, hire date, sign-up date)' # def register(password, fields={}) reg = Iq.new_register(jid.node, password) reg.to = jid.domain fields.each { |name,value| reg.query.add(REXML::Element.new(name)).text = value } send_with_id(reg) end ## # Remove the registration of a user account # # *WARNING:* this deletes your roster and everything else # stored on the server! def remove_registration reg = Iq.new_register reg.to = jid.domain reg.query.add(REXML::Element.new('remove')) send_with_id(reg) end ## # Change the client's password # # Threading is suggested, as this code waits # for an answer. # # Raises an exception upon error response (ServerError from # Stream#send_with_id). # new_password:: [String] New password def password=(new_password) iq = Iq.new_query(:set, @jid.domain) iq.query.add_namespace('jabber:iq:register') iq.query.add(REXML::Element.new('username')).text = @jid.node iq.query.add(REXML::Element.new('password')).text = new_password err = nil send_with_id(iq) end end end xmpp4r-0.5.6/lib/xmpp4r/semaphore.rb0000644000175000017500000000151013647121573020512 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber ## # This class implements semaphore for threads synchronization. class Semaphore ## # Initialize new semaphore # # val:: [Integer] number of threads, that can enter to section def initialize(val=0) @tickets = val @lock = Mutex.new @cond = ConditionVariable.new end ## # Waits until are available some free tickets def wait @lock.synchronize { @cond.wait(@lock) while !(@tickets > 0) @tickets -= 1 } end ## # Unlocks guarded section, increments number of free tickets def run @lock.synchronize { @tickets += 1 @cond.signal } end end end xmpp4r-0.5.6/lib/xmpp4r/httpbinding.rb0000644000175000017500000000027013647121573021043 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/httpbinding/client' xmpp4r-0.5.6/lib/xmpp4r/presence.rb0000644000175000017500000001327413647121573020345 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppstanza' require 'xmpp4r/x' module Jabber ## # The presence class is used to construct presence messages to # send to the Jabber service. class Presence < XMPPStanza name_xmlns 'presence', 'jabber:client' force_xmlns true include Comparable include XParent ## # Create presence stanza # show:: [Symbol] Initial Availability Status (see show) # status:: [String] Initial status message # priority:: [Fixnum] Initial priority value def initialize(show=nil, status=nil, priority=nil) super() set_show(show) if show set_status(status) if status set_priority(priority) if priority end ## # Get type of presence # # result:: [Symbol] or [Nil] Possible values are: # * :error # * :probe (Servers send this to request presence information) # * :subscribe (Subscription request) # * :subscribed (Subscription approval) # * :unavailable (User has gone offline) # * :unsubscribe (Unsubscription request) # * :unsubscribed (Unsubscription approval) # * [nil] (available) # See RFC3921 - 2.2.1. for explanation. def type case super when 'error' then :error when 'probe' then :probe when 'subscribe' then :subscribe when 'subscribed' then :subscribed when 'unavailable' then :unavailable when 'unsubscribe' then :unsubscribe when 'unsubscribed' then :unsubscribed else nil end end ## # Set type of presence # val:: [Symbol] See type for possible subscription types def type=(val) case val when :error then super('error') when :probe then super('probe') when :subscribe then super('subscribe') when :subscribed then super('subscribed') when :unavailable then super('unavailable') when :unsubscribe then super('unsubscribe') when :unsubscribed then super('unsubscribed') else super(nil) end end ## # Set type of presence (chaining-friendly) # val:: [Symbol] See type for possible subscription types def set_type(val) self.type = val self end ## # Get Availability Status (RFC3921 - 5.2) # result:: [Symbol] or [Nil] Valid values according to RFC3921: # * nil (Available, no element) # * :away # * :chat (Free for chat) # * :dnd (Do not disturb) # * :xa (Extended away) def show e = first_element('show') text = e ? e.text : nil case text when 'away' then :away when 'chat' then :chat when 'dnd' then :dnd when 'xa' then :xa else nil end end ## # Set Availability Status # val:: [Symbol] or [Nil] See show for explanation def show=(val) xe = first_element('show') if xe.nil? xe = add_element('show') end case val when String then raise "Invalid value for show." when :away then text = 'away' when :chat then text = 'chat' when :dnd then text = 'dnd' when :xa then text = 'xa' when nil then text = nil else raise "Invalid value for show." end if text.nil? delete_element(xe) else xe.text = text end end ## # Set Availability Status (chaining-friendly) # val:: [Symbol] or [Nil] See show for explanation def set_show(val) self.show = val self end ## # Get status message # result:: [String] or nil def status first_element_text('status') end ## # Set status message # val:: [String] or nil def status=(val) if val.nil? delete_element('status') else replace_element_text('status', val) end end ## # Set status message (chaining-friendly) # val:: [String] or nil def set_status(val) self.status = val self end ## # Get presence priority, or nil if absent # result:: [Integer] def priority e = first_element_text('priority') if e return e.to_i else return nil end end ## # Set presence priority # val:: [Integer] Priority value between -128 and +127 # # *Warning:* negative values make you receive no subscription requests etc. # (RFC3921 - 2.2.2.3.) def priority=(val) if val.nil? delete_element('priority') else replace_element_text('priority', val) end end ## # Set presence priority (chaining-friendly) # val:: [Integer] Priority value between -128 and +127 def set_priority(val) self.priority = val self end ## # Compare two presences using priority # (with cmp_interest as fall-back). def <=>(o) if priority.to_i == o.priority.to_i cmp_interest(o) else priority.to_i <=> o.priority.to_i end end ## # Compare two presences. The most suitable to talk with is the # biggest. PRESENCE_STATUS = { :chat => 4, nil => 3, :dnd => 2, :away => 1, :xa => 0, :unavailable => -1, :error => -2 } def cmp_interest(o) if type.nil? if o.type.nil? # both available. PRESENCE_STATUS[show] <=> PRESENCE_STATUS[o.show] else return -1 end elsif o.type.nil? return 1 else # both are non-nil. We consider this is equal. return 0 end end end end xmpp4r-0.5.6/lib/xmpp4r/caps.rb0000644000175000017500000000004413647121573017456 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/caps/helper/helper' xmpp4r-0.5.6/lib/xmpp4r/stream.rb0000644000175000017500000004154613647121573020037 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/callbacks' require 'socket' require 'thread' require 'xmpp4r/semaphore' require 'xmpp4r/streamparser' require 'xmpp4r/presence' require 'xmpp4r/message' require 'xmpp4r/iq' require 'xmpp4r/debuglog' require 'xmpp4r/idgenerator' module Jabber ## # The stream class manages a connection stream (a file descriptor using which # XML messages are read and sent) # # You may register callbacks for the three Jabber stanzas # (message, presence and iq) and use the send and send_with_id # methods. # # To ensure the order of received stanzas, callback blocks are # launched in the parser thread. If further blocking operations # are intended in those callbacks, run your own thread there. class Stream DISCONNECTED = 1 CONNECTED = 2 # file descriptor used attr_reader :fd # connection status attr_reader :status # number of stanzas currently being processed attr_reader :processing ## # Initialize a new stream def initialize @fd = nil @status = DISCONNECTED @xmlcbs = CallbackList.new @stanzacbs = CallbackList.new @messagecbs = CallbackList.new @iqcbs = CallbackList.new @presencecbs = CallbackList.new @send_lock = Mutex.new @last_send = Time.now @exception_block = nil @tbcbmutex = Mutex.new @threadblocks = [] @wakeup_thread = nil @streamid = nil @streamns = 'jabber:client' @features_sem = Semaphore.new @parser_thread = nil @processing = 0 end ## # Start the XML parser on the fd def start(fd) @stream_mechanisms = [] @stream_features = {} @fd = fd @parser = StreamParser.new(@fd, self) @parser_thread = Thread.new do Thread.current.abort_on_exception = true begin @parser.parse Jabber::debuglog("DISCONNECTED\n") if @exception_block Thread.new { close!; @exception_block.call(nil, self, :disconnected) } else close! end rescue Exception => e Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}") if @exception_block Thread.new do Thread.current.abort_on_exception = true close @exception_block.call(e, self, :start) end else Jabber::warnlog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}" close! raise end end end @status = CONNECTED end def stop @parser_thread.kill @parser = nil end ## # Mounts a block to handle exceptions if they occur during the # poll send. This will likely be the first indication that # the socket dropped in a Jabber Session. # # The block has to take three arguments: # * the Exception # * the Jabber::Stream object (self) # * a symbol where it happened, namely :start, :parser, :sending and :end def on_exception(&block) @exception_block = block end ## # This method is called by the parser when a failure occurs def parse_failure(e) Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}") # A new thread has to be created because close will cause the thread # to commit suicide(???) if @exception_block # New thread, because close will kill the current thread Thread.new do Thread.current.abort_on_exception = true close @exception_block.call(e, self, :parser) end else Jabber::warnlog "Stream#parse_failure was called by XML parser. Dumping " + "backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}" close raise end end ## # This method is called by the parser upon receiving def parser_end if @exception_block Thread.new do Thread.current.abort_on_exception = true close @exception_block.call(nil, self, :close) end else close end end ## # Returns if this connection is connected to a Jabber service # return:: [Boolean] Connection status def is_connected? return @status == CONNECTED end ## # Returns if this connection is NOT connected to a Jabber service # # return:: [Boolean] Connection status def is_disconnected? return @status == DISCONNECTED end ## # Processes a received REXML::Element and executes # registered thread blocks and filters against it. # # element:: [REXML::Element] The received element def receive(element) @tbcbmutex.synchronize { @processing += 1 } Jabber::debuglog("RECEIVED:\n#{element.to_s}") if element.namespace('').to_s == '' # REXML namespaces are always strings element.add_namespace(@streamns) end case element.prefix when 'stream' case element.name when 'stream' stanza = element @streamid = element.attributes['id'] @streamns = element.namespace('') if element.namespace('') # Hack: component streams are basically client streams. # Someday we may want to create special stanza classes # for components/s2s deriving from normal stanzas but # posessing these namespaces @streamns = 'jabber:client' if @streamns == 'jabber:component:accept' unless element.attributes['version'] # isn't XMPP compliant, so Jabber::debuglog("FEATURES: server not XMPP compliant, will not wait for features") @features_sem.run # don't wait for end when 'features' stanza = element element.each { |e| if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl' e.each_element('mechanism') { |mech| @stream_mechanisms.push(mech.text) } else @stream_features[e.name] = e.namespace end } Jabber::debuglog("FEATURES: received") @features_sem.run else stanza = element end else # Any stanza, classes are registered by XMPPElement::name_xmlns begin stanza = XMPPStanza::import(element) rescue NoNameXmlnsRegistered stanza = element end end if @xmlcbs.process(stanza) @tbcbmutex.synchronize { @processing -= 1 } return true end # Iterate through blocked threads (= waiting for an answer) # # We're dup'ping the @threadblocks here, so that we won't end up in an # endless loop if Stream#send is being nested. That means, the nested # threadblock won't receive the stanza currently processed, but the next # one. threadblocks = nil @tbcbmutex.synchronize do threadblocks = @threadblocks.dup end threadblocks.each { |threadblock| exception = nil r = false begin r = threadblock.call(stanza) rescue Exception => e exception = e end if r == true @tbcbmutex.synchronize do @threadblocks.delete(threadblock) end threadblock.wakeup @tbcbmutex.synchronize { @processing -= 1 } return true elsif exception @tbcbmutex.synchronize do @threadblocks.delete(threadblock) end threadblock.raise(exception) end } Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})") Jabber::debuglog("TRYING stanzacbs...") if @stanzacbs.process(stanza) @tbcbmutex.synchronize { @processing -= 1 } return true end r = false Jabber::debuglog("TRYING message/iq/presence/cbs...") case stanza when Message r = @messagecbs.process(stanza) when Iq r = @iqcbs.process(stanza) when Presence r = @presencecbs.process(stanza) end @tbcbmutex.synchronize { @processing -= 1 } return r end ## # Get the list of iq callbacks. def iq_callbacks @iqcbs end ## # Get the list of message callbacks. def message_callbacks @messagecbs end ## # Get the list of presence callbacks. def presence_callbacks @presencecbs end ## # Get the list of stanza callbacks. def stanza_callbacks @stanzacbs end ## # Get the list of xml callbacks. def xml_callbacks @xmlcbs end ## # This is used by Jabber::Stream internally to # keep track of any blocks which were passed to # Stream#send. class ThreadBlock def initialize(block) @block = block @waiter = Semaphore.new @exception = nil end def call(*args) @block.call(*args) end def wait @waiter.wait raise @exception if @exception end def wakeup @waiter.run end def raise(exception) @exception = exception @waiter.run end end def send_data(data) @send_lock.synchronize do @last_send = Time.now @fd << data @fd.flush end end ## # Sends XML data to the socket and (optionally) waits # to process received data. # # Do not invoke this in a callback but in a seperate thread # because we may not suspend the parser-thread (in whose # context callbacks are executed). # # xml:: [String] The xml data to send # &block:: [Block] The optional block def send(xml, &block) Jabber::debuglog("SENDING:\n#{xml}") if block threadblock = ThreadBlock.new(block) @tbcbmutex.synchronize do @threadblocks.unshift(threadblock) end end begin # Temporarily remove stanza's namespace to # reduce bandwidth consumption if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client' and xml.prefix != 'stream' and xml.name != 'stream' xml.delete_namespace send_data(xml.to_s) xml.add_namespace(@streamns) else send_data(xml.to_s) end rescue Exception => e Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}") if @exception_block Thread.new do Thread.current.abort_on_exception = true close! @exception_block.call(e, self, :sending) end else Jabber::warnlog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}" close! raise end end # The parser thread might be running this (think of a callback running send()) # If this is the case, we mustn't stop (or we would cause a deadlock) if block and Thread.current != @parser_thread threadblock.wait elsif block Jabber::warnlog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!") end end ## # Send an XMMP stanza with an Jabber::XMPPStanza#id. The id will be # generated by Jabber::IdGenerator if not already set. # # The block will be called once: when receiving a stanza with the # same Jabber::XMPPStanza#id. There is no need to return true to # complete this! Instead the return value of the block will be # returned. This is a direct result of unique request/response # stanza identification via the id attribute. # # The block may be omitted. Then, the result will be the response # stanza. # # Be aware that if a stanza with type='error' is received # the function does not yield but raises an ServerError with # the corresponding error element. # # Please see Stream#send for some implementational details. # # Please read the note about nesting at Stream#send # xml:: [XMPPStanza] def send_with_id(xml, &block) if xml.id.nil? xml.id = Jabber::IdGenerator.instance.generate_id end res = nil error = nil send(xml) do |received| if received.kind_of? XMPPStanza and received.id == xml.id if received.type == :error error = (received.error ? received.error : ErrorResponse.new) true elsif block_given? res = yield(received) true else res = received true end else false end end unless error.nil? raise ServerError.new(error) end res end ## # Adds a callback block to process received XML messages, these # will be handled before any blocks given to Stream#send or other # callbacks. # # priority:: [Integer] The callback's priority, the higher, the sooner # ref:: [String] The callback's reference # &block:: [Block] The optional block def add_xml_callback(priority = 0, ref = nil, &block) @tbcbmutex.synchronize do @xmlcbs.add(priority, ref, block) end end ## # Delete an XML-messages callback # # ref:: [String] The reference of the callback to delete def delete_xml_callback(ref) @tbcbmutex.synchronize do @xmlcbs.delete(ref) end end ## # Adds a callback block to process received Messages # # priority:: [Integer] The callback's priority, the higher, the sooner # ref:: [String] The callback's reference # &block:: [Block] The optional block def add_message_callback(priority = 0, ref = nil, &block) @tbcbmutex.synchronize do @messagecbs.add(priority, ref, block) end end ## # Delete an Message callback # # ref:: [String] The reference of the callback to delete def delete_message_callback(ref) @tbcbmutex.synchronize do @messagecbs.delete(ref) end end ## # Adds a callback block to process received Stanzas # # priority:: [Integer] The callback's priority, the higher, the sooner # ref:: [String] The callback's reference # &block:: [Block] The optional block def add_stanza_callback(priority = 0, ref = nil, &block) @tbcbmutex.synchronize do @stanzacbs.add(priority, ref, block) end end ## # Delete a Stanza callback # # ref:: [String] The reference of the callback to delete def delete_stanza_callback(ref) @tbcbmutex.synchronize do @stanzacbs.delete(ref) end end ## # Adds a callback block to process received Presences # # priority:: [Integer] The callback's priority, the higher, the sooner # ref:: [String] The callback's reference # &block:: [Block] The optional block def add_presence_callback(priority = 0, ref = nil, &block) @tbcbmutex.synchronize do @presencecbs.add(priority, ref, block) end end ## # Delete a Presence callback # # ref:: [String] The reference of the callback to delete def delete_presence_callback(ref) @tbcbmutex.synchronize do @presencecbs.delete(ref) end end ## # Adds a callback block to process received Iqs # # priority:: [Integer] The callback's priority, the higher, the sooner # ref:: [String] The callback's reference # &block:: [Block] The optional block def add_iq_callback(priority = 0, ref = nil, &block) @tbcbmutex.synchronize do @iqcbs.add(priority, ref, block) end end ## # Delete an Iq callback # # ref:: [String] The reference of the callback to delete # def delete_iq_callback(ref) @tbcbmutex.synchronize do @iqcbs.delete(ref) end end ## # Closes the connection to the Jabber service def close close! end def close! pr = 1 n = 0 # In some cases, we might lost count of some stanzas # (for example, if the handler raises an exception) # so we can't block forever. while pr > 0 and n <= 20 @tbcbmutex.synchronize { pr = @processing } if pr > 0 n += 1 Jabber::debuglog("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS") #puts("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS") sleep 0.1 end end # Order Matters here! If this method is called from within # @parser_thread then killing @parser_thread first would # mean the other parts of the method fail to execute. # That would be bad. So kill parser_thread last @fd.close if @fd and !@fd.closed? @status = DISCONNECTED @parser_thread.kill if @parser_thread end end end xmpp4r-0.5.6/lib/xmpp4r/feature_negotiation/0000755000175000017500000000000013647121573022240 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/feature_negotiation/iq/0000755000175000017500000000000013647121573022651 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/feature_negotiation/iq/feature.rb0000644000175000017500000000124113647121573024627 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' require 'xmpp4r/dataforms/x/data' module Jabber module FeatureNegotiation ## # Feature negotiation, # can appear as direct child to Iq # or as child of IqSi class IqFeature < XMPPElement name_xmlns 'feature', 'http://jabber.org/protocol/feature-neg' ## # First child with xmlns='jabber:x:data' def x res = nil each_element('x') { |e| res = e if e.namespace == 'jabber:x:data' } res end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams.rb0000644000175000017500000000136513647121573021101 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/bytestreams/iq/si.rb' require 'xmpp4r/bytestreams/iq/bytestreams.rb' require 'xmpp4r/bytestreams/helper/ibb/base.rb' require 'xmpp4r/bytestreams/helper/ibb/initiator.rb' require 'xmpp4r/bytestreams/helper/ibb/target.rb' require 'xmpp4r/bytestreams/helper/filetransfer.rb' require 'xmpp4r/bytestreams/helper/socks5bytestreams/base.rb' require 'xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb' require 'xmpp4r/bytestreams/helper/socks5bytestreams/server.rb' require 'xmpp4r/bytestreams/helper/socks5bytestreams/target.rb' require 'xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb' xmpp4r-0.5.6/lib/xmpp4r/xmpp4r.rb0000644000175000017500000000114613647121573017766 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io ## # The Jabber module is the root namespace of the library. module Jabber # XMPP4R Version number. This is the ONLY place where the version number # should be specified. This constant is used to determine the version of # package tarballs and generated gems. XMPP4R_VERSION = '0.5.6' end require 'xmpp4r/client' require 'xmpp4r/reliable' require 'xmpp4r/component' require 'xmpp4r/debuglog' require 'xmpp4r/errors' require 'xmpp4r/test/listener_mocker' xmpp4r-0.5.6/lib/xmpp4r/framework/0000755000175000017500000000000013647121573020202 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/framework/bot.rb0000644000175000017500000001031113647121573021307 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/framework/base' require 'xmpp4r' require 'xmpp4r/roster' require 'xmpp4r/caps/c' require 'xmpp4r/discovery' require 'xmpp4r/xhtml' module Jabber module Framework ## # Abstract handler methods that *may* be implemented by a deriving class: # * on_message(text) # * on_message_xhtml(html_body, text) class Bot < Base helper :roster, Roster::Helper helper(:disco_default) { |cl| Discovery::Responder.new(cl, nil, [Jabber::Discovery::Identity.new('client', 'XMPP4R Bot', 'bot')] ) } helper(:disco_caps) { |cl| Discovery::Responder.new(cl, "http://home.gna.org/xmpp4r/#{Jabber::XMPP4R_VERSION}", [Jabber::Discovery::Identity.new('client', 'XMPP4R Bot', 'bot')] ) } def initialize(jid, password) cl = Jabber::Client.new(jid) cl.connect cl.auth(password) super(cl) roster.add_subscription_request_callback do |item,presence| if accept_subscription_from?(presence.from.strip) roster.accept_subscription(presence.from.strip) else roster.decline_subscription(presence.from.strip) end end @pep_notifications = [] cl.add_message_callback do |msg| if msg.type != :error and msg.body if (html = msg.first_element('html')) and respond_to? :on_message_xhtml on_message_xhtml(html.body, msg.body) elsif respond_to? :on_message on_message(msg.body) end elsif msg.type != :error and (event = msg.first_element('event')) event.each_element('items') do |items| node = items.attributes['node'] items.each_element('item') do |item| @pep_notifications.each { |notification_node,callback| if node == notification_node callback.call(msg.from, item) end } end end else false end end add_cap('presence') add_cap(Caps::NS_CAPS) add_cap('message') if respond_to? :on_message add_cap(XHTML::NS_XHTML_IM) if respond_to? :on_message_xhtml @presence_show = nil @presence_status = nil end ## # Add feature namespace to Capabilities Discovery def add_cap(capability) disco_default.add_feature(capability) disco_caps.add_feature(capability) end ## # Front-end for Roster::Helper#add_subscription_request_callback # # Can be overwritten, must return true or false def accept_subscription_from?(jid) true end ## # Send a simple text chat message def send_message(to, text) msg = Message.new msg.type = :chat msg.to = to msg.body = text @stream.send(msg) end ## # Send an XHTML chat message # text:: [String] alternate plain text body, generated from xhtml_contents if nil def send_message_xhtml(to, xhtml_contents, text=nil) msg = Message.new msg.type = :chat msg.to = to html = msg.add(XHTML::HTML.new(xhtml_contents)) msg.body = text ? text : html.to_text @stream.send(msg) end ## # Set and send a Presence def set_presence(show=nil, status=nil) @presence_show = show @presence_status = status send_presence end private def send_presence roster.wait_for_roster # TODO: vcard photo hash if @presence_show == :unavailable presence = Presence.new(nil, @presence_status) presence.type = :unavailable else presence = Presence.new(@presence_show, @presence_status) end presence.add(disco_caps.generate_caps) @stream.send(presence) end public def add_pep_notification(node, &callback) add_cap("#{node}+notify") @pep_notifications << [node, callback] end end end end xmpp4r-0.5.6/lib/xmpp4r/framework/base.rb0000644000175000017500000000236613647121573021450 0ustar debbiecocoadebbiecocoarequire 'thread' module Jabber module Framework class Base def self.helper(name, klass=nil, &factory) if klass.nil? and factory.nil? raise "helper #{name} needs a class or factory" end define_method(name) do @helpers_lock.synchronize do if @helpers[name] @helpers[name] else if factory @helpers[name] = instance_eval { factory.call(@stream) } elsif klass @helpers[name] = klass.new(@stream) else raise end end end end end attr_accessor :stream def initialize(stream) @stream = stream @helpers = {} @helpers_lock = Mutex.new # Catch all unhandled iq stanzas (lowest priority) @stream.add_iq_callback(-1000, self, &method(:on_unhandled_iq)) end def on_unhandled_iq(iq) if iq.type == :get or iq.type == :set answer = iq.answer(true) answer.type = :error answer.add(ErrorResponse.new('feature-not-implemented')) @stream.send(answer) true else false end end end end end xmpp4r-0.5.6/lib/xmpp4r/vcard.rb0000644000175000017500000000033613647121573017633 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/vcard/helper/vcard.rb' require 'xmpp4r/vcard/iq/vcard.rb' xmpp4r-0.5.6/lib/xmpp4r/errors.rb0000644000175000017500000001671413647121573020057 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber # CUSTOM ERROR CLASSES # All of our custom errors are superclassed by JabberError < StandardError class JabberError < StandardError; end # A client-side only argument error class ArgumentError < JabberError; end # Server disconnected us class ServerDisconnected < JabberError; end ## # An error returned from the server # # e.g. This exception can be raised by helpers when they # receive a server reply with type='error' # # The ServerError carries a Jabber::ErrorResponse element # class ServerError < JabberError #:nodoc: ## # The error element which caused this exception attr_reader :error ## # Initialize a ServerError by passing an ErrorResponse instance # error:: [Error] def initialize(error) @error = error end ## # Sample output: # 'subscription-required: Please subscribe first' def to_s "#{@error.error}: #{@error.text}" end end class ClientAuthenticationFailure < JabberError; end class ComponentAuthenticationFailure < JabberError; end class NoNameXmlnsRegistered < JabberError def initialize(klass) super "Class #{klass} has not set name and xmlns" end end class SOCKS5Error < JabberError; end class InvalidChatState < JabberError; end ## # A class used to build/parse elements. # Look at XEP-0086 for explanation: # http://www.xmpp.org/extensions/xep-0086.html # # FIXME : XEP-0086 is officially deprecated. What effect does that have on this class? Any? # class ErrorResponse < XMPPElement name_xmlns 'error' ## # errorcondition:: [nil] or [String] of the following: # * "bad-request" # * "conflict" # * "feature-not-implemented" # * "forbidden" # * "gone" # * "internal-server-error" # * "item-not-found" # * "jid-malformed" # * "not-acceptable" # * "not-allowed" # * "not-authorized" # * "payment-required" # * "recipient-unavailable" # * "redirect" # * "registration-required" # * "remote-server-not-found" # * "remote-server-timeout" # * "resource-constraint" # * "service-unavailable" # * "subscription-required" # * "undefined-condition" # * "unexpected-request" # Will raise an [Exception] if not [nil] and none of the above # # Also sets type and code to appropriate values according to errorcondition # # text: [nil] or [String] ErrorResponse text def initialize(errorcondition=nil, text=nil) if errorcondition.nil? super() set_text(text) unless text.nil? else errortype = nil errorcode = nil @@Errors.each { |cond,type,code| if errorcondition == cond errortype = type errorcode = code end } if errortype.nil? || errorcode.nil? raise ArgumentError, "Unknown error condition when initializing ErrorReponse" end super() set_error(errorcondition) set_type(errortype) set_code(errorcode) set_text(text) unless text.nil? end end ## # Get the 'Legacy error code' or nil # result:: [Integer] Error code def code if attributes['code'] attributes['code'].to_i else nil end end ## # Set the 'Legacy error code' or nil # i:: [Integer] Error code def code=(i) if i.nil? attributes['code'] = nil else attributes['code'] = i.to_s end end ## # Set the 'Legacy error code' (chaining-friendly) def set_code(i) self.code = i self end ## # Get the 'XMPP error condition' # # This can be anything that possess the specific namespace, # checks don't apply here def error name = nil each_element { |e| name = e.name if (e.namespace == 'urn:ietf:params:xml:ns:xmpp-stanzas') && (e.name != 'text') } name end ## # Set the 'XMPP error condition' # # One previous element with that namespace will be deleted before # # s:: [String] Name of the element to be added, # namespace will be added automatically, checks don't apply here def error=(s) xe = nil each_element { |e| xe = e if (e.namespace == 'urn:ietf:params:xml:ns:xmpp-stanzas') && (e.name != 'text') } unless xe.nil? delete_element(xe) end add_element(s).add_namespace('urn:ietf:params:xml:ns:xmpp-stanzas') end ## # Set the 'XMPP error condition' (chaining-friendly) def set_error(s) self.error = s self end ## # Get the errors element text # result:: [String] or nil def text first_element_text('text') || super end ## # Set the errors element text # (Previous elements will be deleted first) # s:: [String] content or [nil] if no element def text=(s) delete_elements('text') unless s.nil? e = add_element('text') e.add_namespace('urn:ietf:params:xml:ns:xmpp-stanzas') e.text = s end end ## # Set the errors element text (chaining-friendly) def set_text(s) self.text = s self end ## # Get the type of error # (meaning how to proceed) # result:: [Symbol] or [nil] as following: # * :auth # * :cancel # * :continue # * :modify # * :wait def type case attributes['type'] when 'auth' then :auth when 'cancel' then :cancel when 'continue' then :continue when 'modify' then :modify when 'wait' then :wait else nil end end ## # Set the type of error (see ErrorResponse#type) def type=(t) case t when :auth then attributes['type'] = 'auth' when :cancel then attributes['type'] = 'cancel' when :continue then attributes['type'] = 'continue' when :modify then attributes['type'] = 'modify' when :wait then attributes['type'] = 'wait' else attributes['type'] = nil end end ## # Set the type of error (chaining-friendly) def set_type(t) self.type = t self end ## # Possible XMPP error conditions, types and codes (XEP-0086) @@Errors = [['bad-request', :modify, 400], ['conflict', :cancel, 409], ['feature-not-implemented', :cancel, 501], ['forbidden', :auth, 403], ['gone', :modify, 302], ['internal-server-error', :wait, 500], ['item-not-found', :cancel, 404], ['jid-malformed', :modify, 400], ['not-acceptable', :modify, 406], ['not-allowed', :cancel, 405], ['not-authorized', :auth, 401], ['payment-required', :auth, 402], ['recipient-unavailable', :wait, 404], ['redirect', :modify, 302], ['registration-required', :auth, 407], ['remote-server-not-found', :cancel, 404], ['remote-server-timeout', :wait, 504], ['resource-constraint', :wait, 500], ['service-unavailable', :cancel, 503], ['subscription-required', :auth, 407], ['undefined-condition', nil, 500], ['unexpected-request', :wait, 400]] end end xmpp4r-0.5.6/lib/xmpp4r/entity_time.rb0000644000175000017500000000033313647121573021063 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/entity_time/iq' require 'xmpp4r/entity_time/responder' xmpp4r-0.5.6/lib/xmpp4r/rexmladdons.rb0000644000175000017500000001204413647121573021053 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'rexml/document' require 'rexml/parsers/xpathparser' require 'rexml/source' # Turn $VERBOSE off to suppress warnings about redefinition oldverbose = $VERBOSE $VERBOSE = false # REXML : Adds custom helper methods to the REXML module. # module REXML # this class adds a few helper methods to REXML::Element class Element def each_elements(*els, &block) els.inject([ ]) do |res, e| res + each_element(e, &block) end end ## # Replaces or adds a child element of name e with text t. def replace_element_text(e, t, namespace = nil) el = first_element(e, namespace) if el.nil? el = REXML::Element.new(e) el.add_namespace(namespace) add_element(el) end if t el.text = t end self end ## # Replaces or adds a child element of name e with content of t. def replace_element_content(e, c, namespace = nil) el = first_element(e, namespace) if el.nil? el = REXML::Element.new(e) el.add_namespace(namespace) add_element(el) end if c el.children.each do |ch| ch.remove end c.root.children.each do |ch| el.add ch end end self end ## # Returns first element of name e def first_element(e, namespace = nil) if namespace each_element_with_attribute("xmlns", namespace, 1, e) { |el| return el } else each_element(e) { |el| return el } end return nil end ## # Returns text of first element of name e def first_element_text(e, namespace = nil) el = first_element(e, namespace) if el return el.text else return nil end end ## # This method works like first_element_text except that it # returns content of all children, not just the value of the first # child text element. # # Returns content of first element of name e def first_element_content(e, namespace = nil) el = first_element(e, namespace) if el return el.children.join else return nil end end # This method does exactly the same thing as add(), but it can be # overriden by subclasses to provide on-the-fly object creations. # For example, if you import a REXML::Element of name 'plop', and you # have a Plop class that subclasses REXML::Element, with typed_add you # can get your REXML::Element to be "magically" converted to Plop. def typed_add(e) add(e) end ## # import this element's children and attributes def import(xmlelement) if @name and @name != xmlelement.name raise "Trying to import an #{xmlelement.name} to a #{@name} !" end add_attributes(xmlelement.attributes.clone) @context = xmlelement.context xmlelement.each do |e| if e.kind_of? REXML::Element typed_add(e.deep_clone) elsif e.kind_of? REXML::Text add_text(e.value) else add(e.clone) end end self end def self.import(xmlelement) self.new(xmlelement.name).import(xmlelement) end ## # Deletes one or more children elements, # not just one like REXML::Element#delete_element def delete_elements(element) while(delete_element(element)) do end end ## # Test for equality of two elements, useful for assert_equal in # test cases. Tries to parse String o as XML. # # See Test::Unit::Assertions def ==(o) return false unless self.kind_of? REXML::Element if o.kind_of? REXML::Element # Ok elsif o.kind_of? String # Parse o begin o = REXML::Document.new(o).root rescue REXML::ParseException return false end else # Cannot compare with anything other than Elements or Strings return false end return false unless name == o.name attributes.each_attribute do |attr| return false unless attr.value == o.attributes[attr.name] end o.attributes.each_attribute do |attr| return false unless attributes[attr.name] == attr.value end children.each_with_index do |child,i| return false unless child == o.children[i] end return true end end # class Element # FIXME : Is this still needed now that we're a bit past Ruby 1.8.3?? # Very dirty fix for the :progress problem in REXML from Ruby 1.8.3 # http://www.germane-software.com/projects/rexml/ticket/34 # the fix proposed in REXML changeset 1145 only fixes this for pipes, not for # TCP sockets, so we have to keep this. class IOSource def position 0 end def current_line [0, 0, ""] end end # class IOSource end # module REXML # Restore the old $VERBOSE setting $VERBOSE = oldverbose xmpp4r-0.5.6/lib/xmpp4r/roster/0000755000175000017500000000000013647121573017523 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/roster/iq/0000755000175000017500000000000013647121573020134 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/roster/iq/roster.rb0000755000175000017500000001324513647121573022007 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module Roster ## # Class for handling roster updates # # You must do 'client.send(Iq.new_rosterget)' or else you will # have nothing to put in receive_iq() # # You must require 'xmpp4r/rosterquery' to use this class # as its functionality is not needed for a working XMPP implementation. # This will make [IqQuery] convert all Queries with namespace 'jabber:iq:roster' # to [IqQueryRoster] # # This contains multiple children. See RosterItem. class IqQueryRoster < IqQuery name_xmlns 'query', 'jabber:iq:roster' ## # Iterate through all items # &block:: Yield for every [RosterItem] def each(&block) each_element { |item| # XPath won't work here as it's missing a prefix... yield(item) if item.kind_of?(RosterItem) } end ## # Get roster item by JID # jid:: [JID] or [Nil] # result:: [RosterItem] def [](jid) each { |item| return(item) if item.jid == jid } nil end ## # Get all items # result:: [Array] of [RosterItem] def to_a a = [] each { |item| a.push(item) } a end ## # Update roster by stanza # (to be fed by an iq_callback) # iq:: [Iq] Containing new roster # filter:: [Boolean] If false import non-roster-like results too def receive_iq(iq, filter=true) if filter && (((iq.type != :set) && (iq.type != :result)) || (iq.queryns != 'jabber:iq:roster')) return end import(iq.query) end ## # Output for "p" # # JIDs of all contained [RosterItem] elements are joined with a comma # result:: [String] def inspect jids = to_a.collect { |item| item.jid.inspect } jids.join(', ') end end ## # Class containing the elements of the roster # # The 'name' attribute has been renamed to 'iname' here # as 'name' is already used by REXML::Element for the # element's name. It's still name='...' in XML. class RosterItem < XMPPElement name_xmlns 'item', 'jabber:iq:roster' ## # Construct a new roster item # jid:: [JID] Jabber ID # iname:: [String] Name in the roster # subscription:: [Symbol] Type of subscription (see RosterItem#subscription=) # ask:: [Symbol] or [Nil] Can be :subscribe def initialize(jid=nil, iname=nil, subscription=nil, ask=nil) super() self.jid = jid self.iname = iname self.subscription = subscription self.ask = ask end ## # Get name of roster item # # names can be set by the roster's owner himself # return:: [String] def iname attributes['name'] end ## # Set name of roster item # val:: [String] Name for this item def iname=(val) attributes['name'] = val end ## # Get JID of roster item # Resource of the JID will _not_ be stripped # return:: [JID] def jid (a = attributes['jid']) ? JID.new(a) : nil end ## # Set JID of roster item # val:: [JID] or nil def jid=(val) attributes['jid'] = val.nil? ? nil : val.to_s end ## # Get subscription type of roster item # result:: [Symbol] or [Nil] The following values are valid according to RFC3921: # * :both # * :from # * :none # * :remove # * :to def subscription case attributes['subscription'] when 'both' then :both when 'from' then :from when 'none' then :none when 'remove' then :remove when 'to' then :to else nil end end ## # Set subscription type of roster item # val:: [Symbol] or [Nil] See subscription for possible Symbols def subscription=(val) case val when :both then attributes['subscription'] = 'both' when :from then attributes['subscription'] = 'from' when :none then attributes['subscription'] = 'none' when :remove then attributes['subscription'] = 'remove' when :to then attributes['subscription'] = 'to' else attributes['subscription'] = nil end end ## # Get if asking for subscription # result:: [Symbol] nil or :subscribe def ask case attributes['ask'] when 'subscribe' then :subscribe else nil end end ## # Set if asking for subscription # val:: [Symbol] nil or :subscribe def ask=(val) case val when :subscribe then attributes['ask'] = 'subscribe' else attributes['ask'] = nil end end ## # Get groups the item belongs to # result:: [Array] of [String] The groups def groups result = [] each_element('group') { |group| result.push(group.text) } result.uniq end ## # Set groups the item belongs to, # deletes old groups first. # # See JEP 0083 for nested groups # ary:: [Array] New groups, duplicate values will be removed def groups=(ary) # Delete old group elements delete_elements('group') # Add new group elements ary.uniq.each { |group| add_element('group').text = group } end end end #Module Roster end #Module Jabber xmpp4r-0.5.6/lib/xmpp4r/roster/helper/0000755000175000017500000000000013647121573021002 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/roster/helper/roster.rb0000644000175000017500000004034013647121573022646 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/callbacks' require 'thread' require 'xmpp4r/roster/iq/roster' module Jabber module Roster ## # The Roster helper intercepts stanzas with Jabber::IqQueryRoster # and stanzas, but provides cbs which allow the programmer # to keep track of updates. # # A thread for any received stanza is spawned, so the user can invoke # accept_subscription et al in the callback blocks, without stopping # the current (= parser) thread when waiting for a reply. class Helper ## # All items in your roster # items:: [Hash] ([JID] => [Roster::Helper::RosterItem]) attr_reader :items ## # Initialize a new Roster helper # # Registers its cbs (prio = 120, ref = self) # # Request a roster # (Remember to send initial presence afterwards!) # # The initialization will not wait for the roster being received, # use wait_for_roster. # # Attention: If you send presence and receive presences # before the roster has arrived, the Roster helper will let them # pass through and does *not* keep them! def initialize(stream, startnow = true) @stream = stream @items = {} @items_lock = Mutex.new @roster_wait = Semaphore.new @query_cbs = CallbackList.new @update_cbs = CallbackList.new @presence_cbs = CallbackList.new @subscription_cbs = CallbackList.new @subscription_request_cbs = CallbackList.new # Register cbs stream.add_iq_callback(120, self) { |iq| if iq.query.kind_of?(IqQueryRoster) Thread.new do Thread.current.abort_on_exception = true handle_iq_query_roster(iq) end true else false end } stream.add_presence_callback(120, self) { |pres| Thread.new do Thread.current.abort_on_exception = true handle_presence(pres) end } get_roster if startnow end def get_roster # Request the roster rosterget = Iq.new_rosterget @stream.send(rosterget) end ## # Wait for first roster query result to arrive def wait_for_roster @roster_wait.wait @roster_wait.run end ## # Add a callback to be called when a query has been processed # # Because update callbacks are called for each roster item, # this may be appropriate to notify that *anything* has updated. # # Arguments for callback block: The received stanza def add_query_callback(prio = 0, ref = nil, &block) @query_cbs.add(prio, ref, block) end ## # Add a callback for Jabber::Roster::Helper::RosterItem updates # # Note that this will be called much after initialization # for the answer of the initial roster request # # The block receives two objects: # * the old Jabber::Roster::Helper::RosterItem # * the new Jabber::Roster::Helper::RosterItem def add_update_callback(prio = 0, ref = nil, &block) @update_cbs.add(prio, ref, block) end ## # Add a callback for Jabber::Presence updates # # This will be called for stanzas for known RosterItems. # Unknown JIDs may still pass and can be caught via Jabber::Stream#add_presence_callback. # # The block receives three objects: # * the Jabber::Roster::Helper::RosterItem # * the old Jabber::Presence (or nil) # * the new Jabber::Presence (or nil) def add_presence_callback(prio = 0, ref = nil, &block) @presence_cbs.add(prio, ref, block) end ## # Add a callback for subscription updates, # which will be called upon receiving a stanza # with type: # * :subscribed # * :unsubscribe # * :unsubscribed # # The block receives two objects: # * the Jabber::Roster::Helper::RosterItem (or nil) # * the stanza def add_subscription_callback(prio = 0, ref = nil, &block) @subscription_cbs.add(prio, ref, block) end ## # Add a callback for subscription requests, # which will be called upon receiving a stanza # # The block receives two objects: # * the Jabber::Roster::Helper::RosterItem (or nil) # * the stanza # # Response to this event can be taken with accept_subscription # and decline_subscription. # # Example usage: # my_roster.add_subscription_request_callback do |item,presence| # if accept_subscription_requests # my_roster.accept_subscription(presence.from) # else # my_roster.decline_subscription(presence.from) # end # end def add_subscription_request_callback(prio = 0, ref = nil, &block) @subscription_request_cbs.add(prio, ref, block) end private ## # Handle received stanzas, # used internally def handle_iq_query_roster(iq) # If the contains we just ignore that # and assume an empty roster iq.query.each_element('item') do |item| olditem, newitem = nil, nil @items_lock.synchronize { olditem = @items[item.jid] # Handle deletion of item if item.subscription == :remove @items.delete(item.jid) else newitem = @items[item.jid] = RosterItem.new(@stream).import(item) end } @update_cbs.process(olditem, newitem) end @roster_wait.run @query_cbs.process(iq) end ## # Handle received stanzas, # used internally def handle_presence(pres) item = self[pres.from] if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type) @subscription_cbs.process(item, pres) true elsif pres.type == :subscribe @subscription_request_cbs.process(item, pres) true else unless item.nil? update_presence(item, pres) true # Callback consumed stanza else false # Callback did not consume stanza end end end ## # Update the presence of an item, # used internally # # Callbacks are called here def update_presence(item, pres) # This requires special handling, to announce all resources offline if pres.from.resource.nil? and pres.type == :error oldpresences = [] item.each_presence do |oldpres| oldpresences << oldpres end item.add_presence(pres) oldpresences.each { |oldpres| @presence_cbs.process(item, oldpres, pres) } else oldpres = item.presence_of(pres.from).nil? ? nil : Presence.new.import(item.presence_of(pres.from)) item.add_presence(pres) @presence_cbs.process(item, oldpres, pres) end end public ## # Get an item by jid # # If not available tries to look for it with the resource stripped def [](jid) jid = JID.new(jid) unless jid.kind_of? JID @items_lock.synchronize { if @items.has_key?(jid) @items[jid] elsif @items.has_key?(jid.strip) @items[jid.strip] else nil end } end ## # Returns the list of RosterItems which, stripped, are equal to the # one you are looking for. def find(jid) jid = JID.new(jid) unless jid.kind_of? JID j = jid.strip l = {} @items_lock.synchronize { @items.each_pair do |k, v| l[k] = v if k.strip == j end } l end ## # Groups in this Roster, # sorted by name # # Contains +nil+ if there are ungrouped items # result:: [Array] containing group names (String) def groups res = [] @items_lock.synchronize { @items.each_pair do |jid,item| res += item.groups res += [nil] if item.groups == [] end } res.uniq.sort { |a,b| a.to_s <=> b.to_s } end ## # Get items in a group # # When group is nil, return ungrouped items # group:: [String] Group name # result:: Array of [RosterItem] def find_by_group(group) res = [] @items_lock.synchronize { @items.each_pair do |jid,item| res.push(item) if item.groups.include?(group) res.push(item) if item.groups == [] and group.nil? end } res end ## # Add a user to your roster # # Threading is encouraged as the function waits for # a result. ServerError is thrown upon error. # # See Jabber::Roster::Helper::RosterItem#subscribe for details # about subscribing. (This method isn't used here but the # same functionality applies.) # # If the item is already in the local roster # it will simply send itself # jid:: [JID] to add # iname:: [String] Optional item name # subscribe:: [Boolean] Whether to subscribe to this jid def add(jid, iname=nil, subscribe=false) if self[jid] self[jid].send else request = Iq.new_rosterset request.query.add(Jabber::Roster::RosterItem.new(jid, iname)) @stream.send_with_id(request) # Adding to list is handled by handle_iq_query_roster end if subscribe # Actually the item *should* already be known now, # but we do it manually to exclude conditions. pres = Presence.new.set_type(:subscribe).set_to(jid.strip) @stream.send(pres) end end ## # Accept a subscription request # * Sends a stanza # * Adds the contact to your roster # jid:: [JID] of contact # iname:: [String] Optional roster item name def accept_subscription(jid, iname=nil) pres = Presence.new.set_type(:subscribed).set_to(jid.strip) @stream.send(pres) unless self[jid.strip] request = Iq.new_rosterset request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname)) @stream.send_with_id(request) end end ## # Decline a subscription request # * Sends a stanza def decline_subscription(jid) pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip) @stream.send(pres) end ## # These are extensions to RosterItem to carry presence information. # This information is *not* stored in XML! class RosterItem < Jabber::Roster::RosterItem ## # Tracked (online) presences of this RosterItem attr_reader :presences ## # Initialize an empty RosterItem def initialize(stream) super() @stream = stream @presences = [] @presences_lock = Mutex.new end ## # Send the updated RosterItem to the server, # i.e. if you modified iname, groups, ... def send request = Iq.new_rosterset request.query.add(self) @stream.send(request) end ## # Remove item # # This cancels both subscription *from* the contact to you # and from you *to* the contact. # # The methods waits for a roster push from the server (success) # or throws ServerError upon failure. def remove request = Iq.new_rosterset request.query.add(Jabber::Roster::RosterItem.new(jid, nil, :remove)) @stream.send_with_id(request) # Removing from list is handled by Roster#handle_iq_query_roster end ## # Is any presence of this person on-line? # # (Or is there any presence? Unavailable presences are # deleted.) def online? @presences_lock.synchronize { @presences.select { |pres| pres.type.nil? }.size > 0 } end ## # Iterate through all received stanzas def each_presence(&block) # Don't lock here, we don't know what block does... @presences.each { |pres| yield(pres) } end ## # Get specific presence # jid:: [JID] Full JID with resource def presence_of(jid) @presences_lock.synchronize { @presences.each { |pres| return(pres) if pres.from == jid } } nil end ## # Get presence of highest-priority available resource of this person # # Returns nil if contact is offline def presence @presences_lock.synchronize { @presences.select { |pres| pres.type.nil? }.max { |pres1, pres2| (pres1.priority || 0) <=> (pres2.priority || 0) } } end ## # Add presence and sort presences # (unless type is :unavailable or :error) # # This overwrites previous stanzas with the same destination # JID to keep track of resources. Old presence stanzas with # type == :unavailable will be deleted. # # If type == :error and the presence's origin has no # specific resource the contact is treated completely offline. def add_presence(newpres) @presences_lock.synchronize { # Delete old presences with the same JID @presences.delete_if do |pres| pres.from == newpres.from or pres.from.resource.nil? or pres.type == :unavailable end if newpres.type == :error and newpres.from.resource.nil? # Replace by single error presence @presences = [newpres] else # Add new presence @presences.push(newpres) end @presences.sort! } end ## # Send subscription request to the user # # The block given to Jabber::Roster::Roster#add_update_callback will # be called, carrying the RosterItem with ask="subscribe" # # This function returns immediately after sending the subscription # request and will not wait of approval or declination as it may # take months for the contact to decide. ;-) def subscribe pres = Presence.new.set_type(:subscribe).set_to(jid.strip) @stream.send(pres) end ## # Unsubscribe from a contact's presence # # This method waits for a presence with type='unsubscribed' # from the contact. It may throw ServerError upon failure. # # subscription attribute of the item is *from* or *none* # afterwards. As long as you don't remove that item and # subscription='from' the contact is subscribed to your # presence. def unsubscribe pres = Presence.new.set_type(:unsubscribe).set_to(jid.strip) @stream.send(pres) { |answer| answer.type == :unsubscribed and answer.from.strip == pres.to } end ## # Deny the contact to see your presence. # # This method will not wait and returns immediately # as you will need no confirmation for this action. # # Though, you will get a roster update for that item, # carrying either subscription='to' or 'none'. def cancel_subscription pres = Presence.new.set_type(:unsubscribed).set_to(jid) @stream.send(pres) end end end #Class Roster end #Module Roster end #Module Jabber xmpp4r-0.5.6/lib/xmpp4r/roster/x/0000755000175000017500000000000013647121573017772 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/roster/x/roster.rb0000755000175000017500000000677313647121573021655 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/x' require 'xmpp4r/jid' module Jabber module Roster ## # Implementation of JEP-0144 # for # attached to stanzas # # Should be backwards compatible to JEP-0093, # as only action attribute of roster items are missing there. # Pay attention to the namespace which is jabber:x:roster # for JEP-0093! class XRoster < X name_xmlns 'x', 'jabber:x:roster' end #Class XRoster class RosterX < XRoster name_xmlns 'x', 'http://jabber.org/protocol/rosterx' end ## # Class containing an element # # The 'name' attribute has been renamed to 'iname' here # as 'name' is already used by REXML::Element for the # element's name. It's still name='...' in XML. # # This is all a bit analoguous to Jabber::RosterItem, used by # Jabber::IqQueryRoster. But this class lacks the subscription and # ask attributes. class XRosterItem < XMPPElement name_xmlns 'item', 'jabber:x:roster' ## # Construct a new roster item # jid:: [JID] Jabber ID # iname:: [String] Name in the roster def initialize(jid=nil, iname=nil) super() self.jid = jid self.iname = iname end ## # Get name of roster item # # names can be set by the roster's owner himself # return:: [String] def iname attributes['name'] end ## # Set name of roster item # val:: [String] Name for this item def iname=(val) attributes['name'] = val end ## # Get JID of roster item # Resource of the JID will _not_ be stripped # return:: [JID] def jid JID.new(attributes['jid']) end ## # Set JID of roster item # val:: [JID] or nil def jid=(val) attributes['jid'] = val.nil? ? nil : val.to_s end ## # Get action for this roster item # * :add # * :modify # * :delete # result:: [Symbol] (defaults to :add according to JEP-0144) def action case attributes['action'] when 'modify' then :modify when 'delete' then :delete else :add end end ## # Set action for this roster item # (see action) def action=(a) case a when :modify then attributes['action'] = 'modify' when :delete then attributes['action'] = 'delete' else attributes['action'] = 'add' end end ## # Get groups the item belongs to # result:: [Array] of [String] The groups def groups result = [] each_element('group') { |group| result.push(group.text) } result end ## # Set groups the item belongs to, # deletes old groups first. # # See JEP 0083 for nested groups # ary:: [Array] New groups, duplicate values will be removed def groups=(ary) # Delete old group elements delete_elements('group') # Add new group elements ary.uniq.each { |group| add_element('group').text = group } end end #Class XRosterItem class RosterXItem < XRosterItem name_xmlns 'item', 'http://jabber.org/protocol/rosterx' end end #Module Roster end #Module Jabber xmpp4r-0.5.6/lib/xmpp4r/iq.rb0000644000175000017500000001233513647121573017147 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppstanza' require 'xmpp4r/jid' require 'digest/sha1' require 'xmpp4r/query' require 'xmpp4r/vcard/iq/vcard' module Jabber ## # IQ: Information/Query # (see RFC3920 - 9.2.3 # # A class used to build/parse IQ requests/responses class Iq < XMPPStanza name_xmlns 'iq', 'jabber:client' force_xmlns true @@element_classes = {} ## # Build a new stanza # type:: [Symbol] or nil, see Iq#type # to:: [JID] Recipient def initialize(type = nil, to = nil) super() if not to.nil? set_to(to) end if not type.nil? set_type(type) end end ## # Get the type of the Iq stanza # # The following values are allowed: # * :get # * :set # * :result # * :error # result:: [Symbol] or nil def type case super when 'get' then :get when 'set' then :set when 'result' then :result when 'error' then :error else nil end end ## # Set the type of the Iq stanza (see Iq#type) # v:: [Symbol] or nil def type=(v) case v when :get then super('get') when :set then super('set') when :result then super('result') when :error then super('error') else super(nil) end end ## # Set the type of the Iq stanza (chaining-friendly) # v:: [Symbol] or nil def set_type(v) self.type = v self end ## # Returns the iq's query child, or nil # result:: [IqQuery] def query first_element('query') end ## # Delete old elements named newquery.name # # newquery:: [REXML::Element] will be added def query=(newquery) delete_elements(newquery.name) add(newquery) end ## # Returns the iq's query's namespace, or nil # result:: [String] def queryns e = first_element('query') if e return e.namespace else return nil end end ## # Returns the iq's child, or nil # result:: [IqVcard] def vcard first_element('vCard') end ## # Returns the iq's child, or nil # result:: [IqVcard] def pubsub first_element('pubsub') end ## # Returns the iq's child, or nil # resulte:: [IqCommand] def command first_element("command") end ## # Create a new Iq stanza with an unspecified query child # ( has no namespace) def Iq.new_query(type = nil, to = nil) iq = Iq.new(type, to) query = IqQuery.new iq.add(query) iq end ## # Create a new jabber:iq:auth set Stanza. def Iq.new_authset(jid, password) iq = Iq.new(:set) query = IqQuery.new query.add_namespace('jabber:iq:auth') query.add(REXML::Element.new('username').add_text(jid.node)) query.add(REXML::Element.new('password').add_text(password)) query.add(REXML::Element.new('resource').add_text(jid.resource)) if not jid.resource.nil? iq.add(query) iq end ## # Create a new jabber:iq:auth set Stanza for Digest authentication def Iq.new_authset_digest(jid, session_id, password) iq = Iq.new(:set) query = IqQuery.new query.add_namespace('jabber:iq:auth') query.add(REXML::Element.new('username').add_text(jid.node)) query.add(REXML::Element.new('digest').add_text(Digest::SHA1.hexdigest(session_id + password))) query.add(REXML::Element.new('resource').add_text(jid.resource)) if not jid.resource.nil? iq.add(query) iq end ## # Create a new jabber:iq:register set stanza for service/server registration # username:: [String] (Element will be ommited if unset) # password:: [String] (Element will be ommited if unset) def Iq.new_register(username=nil, password=nil) iq = Iq.new(:set) query = IqQuery.new query.add_namespace('jabber:iq:register') query.add(REXML::Element.new('username').add_text(username)) if username query.add(REXML::Element.new('password').add_text(password)) if password iq.add(query) iq end ## # Create a new jabber:iq:register get stanza for retrieval # of accepted registration information def Iq.new_registerget iq = Iq.new(:get) query = IqQuery.new query.add_namespace('jabber:iq:register') iq.add(query) iq end ## # Create a new jabber:iq:roster get Stanza. # # IqQueryRoster is unused here because possibly not require'd def Iq.new_rosterget iq = Iq.new(:get) query = IqQuery.new query.add_namespace('jabber:iq:roster') iq.add(query) iq end ## # Create a new jabber:iq:roster get Stanza. def Iq.new_browseget iq = Iq.new(:get) query = IqQuery.new query.add_namespace('jabber:iq:browse') iq.add(query) iq end ## # Create a new jabber:iq:roster set Stanza. def Iq.new_rosterset iq = Iq.new(:set) query = IqQuery.new query.add_namespace('jabber:iq:roster') iq.add(query) iq end end end xmpp4r-0.5.6/lib/xmpp4r/connection.rb0000644000175000017500000001467213647121573020703 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io begin require 'openssl' rescue LoadError end require 'xmpp4r/stream' module Jabber ## # The connection class manages the TCP connection to the Jabber server # class Connection < Stream attr_reader :host, :port # How many seconds to wait for # before proceeding attr_accessor :features_timeout # Keep-alive interval in seconds, or nil to disable keepalive, # defaults to 60 (see private method keepalive_loop for # implementation details) attr_accessor :keepalive_interval # Allow TLS negotiation? Defaults to true attr_accessor :allow_tls # Optional CA-Path for TLS-handshake attr_accessor :ssl_capath # Optional callback for verification of SSL peer attr_accessor :ssl_verifycb #whether to use the old and deprecated SSL protocol #Defaults to false attr_accessor :use_ssl class SSLSocketUTF8 < OpenSSL::SSL::SSLSocket # #readline returns incorrect encodings for UTF8 strings # because SSLSocket does not support encoding conversions # This forces a correct encoding, and pervents REXML exceptions def sysread *args super.force_encoding ::Encoding::UTF_8 end end ## # Create a new connection to the given host and port def initialize super() @host = nil @port = nil @allow_tls = defined? OpenSSL @tls = false @ssl_capath = nil @ssl_verifycb = nil @features_timeout = 10 @keepalive_interval = 60 @use_ssl = false end ## # Connect to the Jabber server through a TCP Socket, # start the Jabber parser, # invoke to accept_features to wait for TLS, # start the keep-alive thread def connect(host, port) @host = host @port = port # Reset is_tls?, so that it works when reconnecting @tls = false Jabber::debuglog("CONNECTING:\n#{@host}:#{@port}") @socket = TCPSocket.new(@host, @port) # We want to use the old and deprecated SSL protocol (usually on port 5223) if @use_ssl ssl = SSLSocketUTF8.new(@socket) ssl.connect # start SSL session ssl.sync_close = true Jabber::debuglog("SSL connection established.") @socket = ssl end start accept_features unless @keepalive_interval.nil? @keepaliveThread = Thread.new do Thread.current.abort_on_exception = true keepalive_loop end end end ## # Closing connection: # first kill keepaliveThread (but only if it's not me), then call Stream#close! def close! @keepaliveThread.kill if @keepaliveThread and @keepaliveThread.alive? and @keepaliveThread != Thread.current super @keepaliveThread.kill if @keepaliveThread and @keepaliveThread.alive? end def accept_features begin Timeout::timeout(@features_timeout) { Jabber::debuglog("FEATURES: waiting...") @features_sem.wait Jabber::debuglog("FEATURES: waiting finished") } rescue Timeout::Error Jabber::debuglog("FEATURES: timed out when waiting, stream peer seems not XMPP compliant") end if @allow_tls and not is_tls? and @stream_features['starttls'] == 'urn:ietf:params:xml:ns:xmpp-tls' begin starttls rescue Jabber::debuglog("STARTTLS:\nFailure: #{$!}") end end end ## # Start the parser on the previously connected socket def start super(@socket) end ## # Do a # (will be automatically done by connect if stream peer supports this) def starttls stls = REXML::Element.new('starttls') stls.add_namespace('urn:ietf:params:xml:ns:xmpp-tls') reply = nil send(stls) { |r| reply = r true } if reply.name != 'proceed' raise ServerError.new(reply.first_element('error')) end # Don't be interrupted stop begin error = nil # Context/user set-able stuff ctx = OpenSSL::SSL::SSLContext.new if @ssl_capath ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.ca_path = @ssl_capath else ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE end ctx.verify_callback = @ssl_verifycb # SSL connection establishing sslsocket = SSLSocketUTF8.new(@socket, ctx) sslsocket.sync_close = true Jabber::debuglog("TLSv1: OpenSSL handshake in progress") sslsocket.connect # Make REXML believe it's a real socket class << sslsocket def kind_of?(o) o == IO ? true : super end end # We're done and will use it @tls = true @socket = sslsocket rescue error = $! ensure Jabber::debuglog("TLSv1: restarting parser") start accept_features raise error if error end end ## # Have we gone to TLS mode? # result:: [true] or [false] def is_tls? @tls end def generate_stream_start(to=nil, from=nil, id=nil, xml_lang="en", xmlns="jabber:client", version="1.0") stream_start_string = " element # fields:: [Hash] Initialize with keys as XPath element names and values for element texts def initialize(fields=nil) super() unless fields.nil? fields.each { |name,value| self[name] = value } end end ## # Get an elements/fields text # # vCards have too much possible children, so ask for them here # and extract the result with iqvcard.element('...').text # name:: [String] XPath def [](name) text = nil each_element(name) { |child| text = child.text } text end ## # Set an elements/fields text # name:: [String] XPath # text:: [String] Value def []=(name, text) xe = self name.split(/\//).each do |elementname| # Does the children already exist? newxe = nil xe.each_element(elementname) { |child| newxe = child } if newxe.nil? # Create a new xe = xe.add_element(elementname) else # Or take existing xe = newxe end end xe.text = text end ## # Get vCard field names # # Example: # ["NICKNAME", "BDAY", "ORG/ORGUNIT", "PHOTO/TYPE", "PHOTO/BINVAL"] # # result:: [Array] of [String] def fields element_names(self).uniq end ## # Get the PHOTO/BINVAL (Avatar picture) field decoded from Base64 # result:: [String] or [nil] def photo_binval if (binval = self['PHOTO/BINVAL']) Base64::decode64(binval) else nil end end private ## # Recursive helper function, # returns all element names in an array, concatenated # to their parent's name with a slash def element_names(xe, prefix='') # :nodoc: res = [] xe.each_element { |child| if child.kind_of?(REXML::Element) children = element_names(child, "#{prefix}#{child.name}/") if children == [] res.push("#{prefix}#{child.name}") else res += children end end } res end end end end xmpp4r-0.5.6/lib/xmpp4r/vcard/helper/0000755000175000017500000000000013647121573020563 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/vcard/helper/vcard.rb0000644000175000017500000000422513647121573022212 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module Vcard ## # The Vcard helper retrieves vCards class Helper ## # Initialize a new Vcard helper def initialize(stream) @stream = stream end ## # Retrieve vCard of an entity # # Raises exception upon retrieval error, please catch that! # (The exception is ServerError and is raisen by # Stream#send_with_id. # # Usage of Threads is suggested here as vCards can be very # big (see /iq/vCard/PHOTO/BINVAL). # # jid:: [Jabber::JID] or nil (should be stripped, nil for the client's own vCard) # result:: [Jabber::IqVcard] or nil (nil results may be handled as empty vCards) def get(jid=nil) res = nil request = Iq.new(:get, jid) request.from = @stream.jid # Enable components to use this request.add(IqVcard.new) @stream.send_with_id(request) { |answer| # No check for sender or queryns needed (see send_with_id) if answer.type == :result res = answer.vcard true else false end } res end ## # Set your own vCard (Clients only) # # Raises exception when setting fails # # Usage of Threads suggested here, too. The function # waits for approval from the server. # # iqvcard:: [Jabber::IqVcard] def set(iqvcard) iq = Iq.new(:set) iq.add(iqvcard) @stream.send_with_id(iq) { |answer| if answer.type == :result true else false end } end ## # Quickly initialize a Vcard helper and get # a vCard. See Vcard#get def self.get(stream, jid=nil) new(stream).get(jid) end ## # Quickly initialize a Vcard helper and set # your vCard. See Vcard#set def self.set(stream, iqvcard) new(stream).set(iqvcard) end end end end xmpp4r-0.5.6/lib/xmpp4r/last/0000755000175000017500000000000013647121573017150 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/last/iq/0000755000175000017500000000000013647121573017561 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/last/iq/last.rb0000644000175000017500000000322113647121573021047 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module LastActivity NS_LAST_ACTIVITY = 'jabber:iq:last' ## # Class for handling Last Activity queries # (XEP-0012) class IqQueryLastActivity < IqQuery name_xmlns 'query', NS_LAST_ACTIVITY ## # Get the number of seconds since last activity. # # With a bare jid, this will return the number of seconds since the # client was last seen (offline user query). # # With a full jid, this will return the number of seconds that the # client has been idle (online user query). # # With a server, this will return the server or component's uptime in # seconds (server / component query). def seconds attributes['seconds'] ? attributes['seconds'].to_i : nil end ## # Set the number of seconds since last activity def seconds=(val) attributes['seconds'] = val.to_s end ## # Set the number of seconds since last activity # (chaining-friendly) def set_second(val) self.seconds = val self end ## # For an offline user query, get the last status. def status self.text end ## # For an offline user query, set the last status. def status=(val) self.text = val end ## # For an offline user query, set the last status. # (chaining-friendly) def set_status(val) self.status = val self end end end end xmpp4r-0.5.6/lib/xmpp4r/last/helper/0000755000175000017500000000000013647121573020427 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/last/helper/helper.rb0000644000175000017500000000154513647121573022240 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/last' module Jabber module LastActivity ## # A Helper to manage discovery of Last Activity. class Helper def initialize(client) @stream = client end ## # Gets the last activity from a JID. # jid:: [JID] # return:: [Jabber::LastActivity::IqQueryLastActivity] def get_last_activity_from(jid) iq = Jabber::Iq.new(:get, jid) iq.from = @stream.jid iq.add(Jabber::LastActivity::IqQueryLastActivity.new) reply = @stream.send_with_id(iq) if reply.query && reply.query.kind_of?(IqQueryLastActivity) reply.query else nil end end end end end xmpp4r-0.5.6/lib/xmpp4r/muc.rb0000644000175000017500000000076213647121573017323 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/dataforms' require 'xmpp4r/muc/x/muc' require 'xmpp4r/muc/x/mucuserinvite' require 'xmpp4r/muc/x/mucuseritem' require 'xmpp4r/muc/iq/mucowner' require 'xmpp4r/muc/iq/mucadmin' require 'xmpp4r/muc/iq/mucadminitem' require 'xmpp4r/muc/helper/mucbrowser' require 'xmpp4r/muc/helper/mucclient' require 'xmpp4r/muc/helper/simplemucclient' xmpp4r-0.5.6/lib/xmpp4r/rpc.rb0000644000175000017500000000010613647121573017313 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/rpc/helper/client' require 'xmpp4r/rpc/helper/server' xmpp4r-0.5.6/lib/xmpp4r/xmppstanza.rb0000644000175000017500000000714513647121573020746 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' require 'xmpp4r/errors' require 'xmpp4r/jid' module Jabber ## # root class of all Jabber XML elements class XMPPStanza < XMPPElement ## # Compose a response by doing the following: # * Create a new XMPPStanza of the same subclass # with the same element-name # * Import xmppstanza if import is true # * Swap 'to' and 'from' # * Copy 'id' # * Does not take care about the type # # *Attention*: Be careful when answering to stanzas with # type == :error - answering to an error may generate # another error on the other side, which could be leading to a # ping-pong effect quickly! # # xmppstanza:: [XMPPStanza] source # import:: [true or false] Copy attributes and children of source # result:: [XMPPStanza] answer stanza def XMPPStanza.answer(xmppstanza, import=true) x = xmppstanza.class.new if import x.import(xmppstanza) end x.from = xmppstanza.to x.to = xmppstanza.from x.id = xmppstanza.id x end ## # Return the first child def error first_element('error') end ## # Compose a response of this XMPPStanza # (see XMPPStanza.answer) # result:: [XMPPStanza] New constructed stanza def answer(import=true) XMPPStanza.answer(self, import) end ## # Makes some changes to the structure of an XML element to help # it respect the specification. For example, in a message, we should # have < < { rest of tags } def normalize end ## # get the to attribute # # return:: [String] the element's to attribute def to (a = attribute('to')).nil? ? a : JID.new(a.value) end ## # set the to attribute # # v:: [String] the value to set def to= (v) add_attribute('to', v ? v.to_s : nil) end ## # set the to attribute (chaining-friendly) # # v:: [String] the value to set def set_to(v) self.to = v self end ## # get the from attribute # # return:: [String] the element's from attribute def from (a = attribute('from')).nil? ? a : JID.new(a.value) end ## # set the from attribute # # v:: [String] the value from set def from= (v) add_attribute('from', v ? v.to_s : nil) end ## # set the from attribute (chaining-friendly) # # v:: [String] the value from set def set_from(v) add_attribute('from', v ? v.to_s : nil) self end ## # get the id attribute # # return:: [String] the element's id attribute def id (a = attribute('id')).nil? ? a : a.value end ## # set the id attribute # # v:: [String] the value id set def id= (v) add_attribute('id', v.to_s) end ## # set the id attribute (chaining-friendly) # # v:: [String] the value id set def set_id(v) add_attribute('id', v.to_s) self end ## # get the type attribute # # return:: [String] the element's type attribute def type (a = attribute('type')).nil? ? a : a.value end ## # set the type attribute # # v:: [String] the value type set def type= (v) add_attribute('type', v) end ## # set the type attribute (chaining-friendly) # # v:: [String] the value type set def set_type(v) add_attribute('type', v) self end end end xmpp4r-0.5.6/lib/xmpp4r/roster.rb0000644000175000017500000000040613647121573020050 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/roster/iq/roster.rb' require 'xmpp4r/roster/helper/roster.rb' require 'xmpp4r/roster/x/roster.rb' xmpp4r-0.5.6/lib/xmpp4r/message.rb0000644000175000017500000001262413647121573020163 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppstanza' require 'xmpp4r/x' module Jabber ## # The Message class manages the stanzas, # which is used for all messaging communication. class Message < XMPPStanza CHAT_STATES = %w(active composing gone inactive paused).freeze name_xmlns 'message', 'jabber:client' force_xmlns true include XParent ## # Create a new message # >to:: a JID or a String object to send the message to. # >body:: the message's body def initialize(to = nil, body = nil) super() if not to.nil? set_to(to) end if !body.nil? add_element(REXML::Element.new("body").add_text(body)) end end ## # Get the type of the Message stanza # # The following Symbols are allowed: # * :chat # * :error # * :groupchat # * :headline # * :normal # result:: [Symbol] or nil def type case super when 'chat' then :chat when 'error' then :error when 'groupchat' then :groupchat when 'headline' then :headline when 'normal' then :normal else nil end end ## # Set the type of the Message stanza (see Message#type for details) # v:: [Symbol] or nil def type=(v) case v when :chat then super('chat') when :error then super('error') when :groupchat then super('groupchat') when :headline then super('headline') when :normal then super('normal') else super(nil) end end ## # Set the type of the Message stanza (chaining-friendly) # v:: [Symbol] or nil def set_type(v) self.type = v self end ## # Returns the message's body, or nil. # This is the message's plain-text content. def body first_element_text('body') end ## # Sets the message's body # # b:: [String] body to set def body=(b) replace_element_text('body', b) end ## # Sets the message's body # # b:: [String] body to set # return:: [REXML::Element] self for chaining def set_body(b) self.body = b self end ## # Returns the message's xhtml body, or nil. # This is the message's xhtml-text content. def xhtml_body html = first_element('html', 'http://jabber.org/protocol/xhtml-im') if html html.first_element_content('body', 'http://www.w3.org/1999/xhtml') else first_element_content('body', 'http://www.w3.org/1999/xhtml') end end ## # Sets the message's xhtml body # # b:: [String] xhtml body to set (Note: must be a valid xhtml) def xhtml_body=(b) begin b = REXML::Document.new("#{b}") rescue REXML::ParseException raise ArgumentError, "Body is not a valid xhtml. Have you forgot to close some tag?" end html = first_element('html', 'http://jabber.org/protocol/xhtml-im') if html html.replace_element_content('body', b, 'http://www.w3.org/1999/xhtml') else el = REXML::Element.new('html') el.add_namespace('http://jabber.org/protocol/xhtml-im') el.replace_element_content('body', b, 'http://www.w3.org/1999/xhtml') add_element(el) end end ## # Sets the message's xhtml body # # b:: [String] xhtml body to set (Note: must be a valid xhtml) # return:: [REXML::Element] self for chaining def set_xhtml_body(b) self.xhtml_body = b self end ## # sets the message's subject # # s:: [String] subject to set def subject=(s) replace_element_text('subject', s) end ## # sets the message's subject # # s:: [String] subject to set # return:: [REXML::Element] self for chaining def set_subject(s) self.subject = s self end ## # Returns the message's subject, or nil def subject first_element_text('subject') end ## # sets the message's thread # s:: [String] thread to set def thread=(s) delete_elements('thread') replace_element_text('thread', s) unless s.nil? end ## # gets the message's thread (chaining-friendly) # Please note that this are not [Thread] but a [String]-Identifier to track conversations # s:: [String] thread to set def set_thread(s) self.thread = s self end ## # Returns the message's thread, or nil def thread first_element_text('thread') end ## # Returns the current chat state, or nil if no chat state is set def chat_state each_elements(*CHAT_STATES) { |el| return el.name.to_sym } return nil end ## # Sets the chat state :active, :composing, :gone, :inactive, :paused def chat_state=(s) s = s.to_s raise InvalidChatState, "Chat state must be one of #{CHAT_STATES.join(', ')}" unless CHAT_STATES.include?(s) CHAT_STATES.each { |state| delete_elements(state) } add_element(REXML::Element.new(s).add_namespace('http://jabber.org/protocol/chatstates')) end ## # Sets the message's chat state def set_chat_state(s) self.state = s self end CHAT_STATES.each do |state| define_method("#{state}?") do chat_state == state.to_sym end end end end xmpp4r-0.5.6/lib/xmpp4r/observable.rb0000644000175000017500000000054313647121573020660 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/observable/thread_store.rb' require 'xmpp4r/observable/observable_thing.rb' require 'xmpp4r/observable/helper.rb' xmpp4r-0.5.6/lib/xmpp4r/caps/0000755000175000017500000000000013647121573017133 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/caps/helper/0000755000175000017500000000000013647121573020412 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/caps/helper/helper.rb0000644000175000017500000000520513647121573022220 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/discovery' require 'xmpp4r/caps/c' require 'xmpp4r/caps/helper/generator' module Jabber module Caps ## # A Helper to manage advertising and discovery of entity capabilities. # # Following XEP-0115 (ver 1.4 http://www.xmpp.org/extensions/xep-0115.html). # you can use this Helper to, for example, advertise that your client # wishes to receive XEP-0118 User Tune notifications, eg: # # caps_helper=Jabber::Caps::Helper(cl, # [Jabber::Discovery::Identity.new('client',nil,'bot')], # [Jabber::Discovery::Feature.new('http://jabber.org/protocol/tune+notify')] # ) class Helper attr_accessor :identities, :features, :node ## # Construct a new Caps Helper. # # This will send a message containing # a (Jabber::Caps::C) stanza to your server. # client:: [Jabber::Stream] # i:: [Array] of [Jabber::Discovery::Identity] objects that this entity will advertise # f:: [Array] of [Jabber::Discovery::Feature] objects that this entity will advertise # n:: [String] an identifier representing the software underlying this entity def initialize(client,i=[],f=[],n="http://home.gna.org/xmpp4r/##{Jabber::XMPP4R_VERSION}") @stream = client @identities = i @features = f @node = n @stream.add_iq_callback(250) do |iq| if iq.type == :get and iq.query.kind_of? Jabber::Discovery::IqQueryDiscoInfo handle_discoinfo_query(iq) true else false end end p = Jabber::Presence.new() p.add(c) @stream.send(p) end ## # Return a element for inclusion in your own # stanzas. def c Jabber::Caps::C.new(node, ver) end ## # Send actual identities/ features back to a requesting entity def handle_discoinfo_query(iq) caps_reply = Jabber::XMPPStanza.answer(iq) caps_reply.type = :result caps_reply.query = Jabber::Discovery::IqQueryDiscoInfo.new @identities.each { |i| caps_reply.query.add(i) } @features.each { |f| caps_reply.query.add(f) } @stream.send(caps_reply) end ## # Generate 'ver', an opaque hash used to represent this entity's # capabilities # # See http://www.xmpp.org/extensions/xep-0115.html#ver def ver Caps::generate_ver(@identities, @features) end end end end xmpp4r-0.5.6/lib/xmpp4r/caps/helper/generator.rb0000644000175000017500000001245713647121573022736 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/discovery' require 'xmpp4r/dataforms' require 'xmpp4r/caps/c' require 'digest' require 'xmpp4r/base64' module Jabber module Caps def self.generate_ver_str(identities, features, forms=[]) # 1. Initialize an empty string S. s = '' # 2. Sort the service discovery identities [14] by category and # then by type (if it exists) and then by xml:lang (if it # exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/' # [NAME]. Note that each slash is included even if the TYPE, # LANG, or NAME is not included. identities.sort! do |identity1,identity2| cmp_result = nil [:category, :type, :xml_lang, :iname].each do |field| value1 = identity1.send(field) value2 = identity2.send(field) if value1 != value2 cmp_result = value1 <=> value2 break end end cmp_result end # 3. For each identity, append the 'category/type/lang/name' to # S, followed by the '<' character. s += identities.collect do |identity| [:category, :type, :xml_lang, :iname].collect do |field| identity.send(field).to_s end.join('/') + '<' end.join # 4. Sort the supported service discovery features. [15] features.sort! do |feature1,feature2| feature1.var <=> feature2.var end # 5. For each feature, append the feature to S, followed by the # '<' character. s += features.collect do |feature| feature.var.to_s + '<' end.join # 6. If the service discovery information response includes # XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., by # the XML character data of the element). forms.sort! do |form1,form2| fform_type1 = form1.field('FORM_TYPE') fform_type2 = form2.field('FORM_TYPE') form_type1 = fform_type1 ? fform_type1.values.to_s : nil form_type2 = fform_type2 ? fform_type2.values.to_s : nil form_type1 <=> form_type2 end # 7. For each extended service discovery information form: forms.each do |form| # 7.1. Append the XML character data of the FORM_TYPE field's # element, followed by the '<' character. fform_type = form.field('FORM_TYPE') form_type = fform_type ? fform_type.first_element_text('value') : nil s += "#{form_type}<" # 7.2. Sort the fields by the value of the "var" attribute. fields = form.fields(false).sort do |field1,field2| field1.var <=> field2.var end # 7.3. For each field: fields.each do |field| # 7.3.1. Append the value of the "var" attribute, followed by # the '<' character. s += "#{field.var}<" # 7.3.2. Sort values by the XML character data of the # element. values = field.values.sort do |value1,value2| value1 <=> value2 end # 7.3.3. For each element, append the XML character # data, followed by the '<' character. s += values.collect do |value| "#{value}<" end.join end end # 8. Ensure that S is encoded according to the UTF-8 encoding # (RFC 3269 [16]). # (given in XMPP4R) s end ## # Implementation of the algorithm defined at: # http://www.xmpp.org/extensions/xep-0115.html#ver-gen def self.generate_ver(identities, features, forms=[], hash='sha-1') s = generate_ver_str(identities, features, forms) # 9. Compute the verification string by hashing S using the # algorithm specified in the 'hash' attribute (e.g., SHA-1 as # defined in RFC 3174 [17]). The hashed data MUST be generated # with binary output and encoded using Base64 as specified in # Section 4 of RFC 4648 [18] (note: the Base64 output MUST NOT # include whitespace and MUST set padding bits to zero). [19] # See http://www.iana.org/assignments/hash-function-text-names hash_klass = case hash when 'md2' then nil when 'md5' then Digest::MD5 when 'sha-1' then Digest::SHA1 when 'sha-224' then nil when 'sha-256' then Digest::SHA256 when 'sha-384' then Digest::SHA384 when 'sha-512' then Digest::SHA512 end if hash_klass Base64::encode64(hash_klass::digest(s)).strip else nil end end ## # Generate a ver hash from a Jabber::Discovery::IqQueryDiscoInfo result # query:: [Jabber::Discovery::IqQueryDiscoInfo] def self.generate_ver_from_discoinfo(query, hash='sha-1') identities = [] features = [] forms = [] query.each_element do |element| if element.kind_of? Discovery::Identity identities << element elsif element.kind_of? Discovery::Feature features << element elsif element.kind_of? Dataforms::XData forms << element end end generate_ver(identities, features, forms, hash) end end end xmpp4r-0.5.6/lib/xmpp4r/caps/c.rb0000644000175000017500000000325613647121573017710 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' module Jabber module Caps NS_CAPS='http://jabber.org/protocol/caps' ## # The XMPP element, used to advertise entity capabilities. # # See http://www.xmpp.org/extensions/xep-0115.html#protocol. # # You should not need to construct this element directly, see # Jabber::Caps::Helper. class C < XMPPElement name_xmlns 'c', NS_CAPS force_xmlns true def initialize(node = nil, ver = nil) super() add_attribute('node', node) if node if ver add_attribute('ver', ver) add_attribute('hash', 'sha-1') end end ## # Get the value of this element's 'ver' attribute, # an opaque hash representing this entity's capabilities. def ver attributes['ver'] end ## # Get the value of this element's 'node' attribute, # a 'unique identifier for the software underlying the entity' def node attributes['node'] end ## # Get the value of this element's 'hash' attribute, # the algorithm used in generating the 'ver' attribute def hash attributes['hash'] end ## # Get the value of this element's 'ext' attribute, # the list of extensions for legacy clients. def ext attributes['ext'] end ## # Is this a legacy caps response, as defined by version 1.3 of # the XEP-0115 specification? def legacy? hash.nil? || hash.empty? end end end end xmpp4r-0.5.6/lib/xmpp4r/tune.rb0000644000175000017500000000007713647121573017511 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/tune/tune' require 'xmpp4r/tune/helper/helper' xmpp4r-0.5.6/lib/xmpp4r/delay/0000755000175000017500000000000013647121573017303 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/delay/x/0000755000175000017500000000000013647121573017552 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/delay/x/delay.rb0000644000175000017500000000437613647121573021207 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/x' require 'xmpp4r/jid' require 'time' module Jabber module Delay ## # Implementation of JEP 0091 # for # applied on and stanzas # # One may also use XDelay#text for a descriptive reason # for the delay. # # Please note that you must require 'xmpp4r/xdelay' to use # this class as it's not required by a basic XMPP implementation. # elements with the specific namespace will then be # converted to XDelay automatically. class XDelay < X name_xmlns 'x', 'jabber:x:delay' ## # Initialize a new XDelay element # # insertnow:: [Boolean] Set the stamp to [Time::now] def initialize(insertnow=true) super() if insertnow set_stamp(Time.now) end end ## # Get the timestamp # result:: [Time] or nil def stamp if attributes['stamp'] begin # Actually this should be Time.xmlschema, # but "unfortunately, the 'jabber:x:delay' namespace predates" JEP 0082 Time.parse("#{attributes['stamp']}Z") rescue ArgumentError nil end else nil end end ## # Set the timestamp # t:: [Time] or nil def stamp=(t) if t.nil? attributes['stamp'] = nil else attributes['stamp'] = t.strftime("%Y%m%dT%H:%M:%S") end end ## # Set the timestamp (chaining-friendly) def set_stamp(t) self.stamp = t self end ## # Get the timestamp's origin # result:: [JID] def from if attributes['from'] JID.new(attributes['from']) else nil end end ## # Set the timestamp's origin # jid:: [JID] def from=(jid) attributes['from'] = jid.nil? ? nil : jid.to_s end ## # Set the timestamp's origin (chaining-friendly) def set_from(jid) self.from = jid self end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/0000755000175000017500000000000013647121573020547 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/bytestreams/iq/0000755000175000017500000000000013647121573021160 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/bytestreams/iq/bytestreams.rb0000755000175000017500000000716013647121573024056 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams NS_BYTESTREAMS = 'http://jabber.org/protocol/bytestreams' ## # Class for accessing elements with # xmlns='http://jabber.org/protocol/bytestreams' # in stanzas. class IqQueryBytestreams < IqQuery name_xmlns 'query', NS_BYTESTREAMS ## # Initialize such a # sid:: [String] Session-ID # mode:: [Symbol] :tcp or :udp def initialize(sid=nil, mode=nil) super() self.sid = sid self.mode = mode end ## # Session-ID def sid attributes['sid'] end ## # Set Session-ID def sid=(s) attributes['sid'] = s end ## # Transfer mode # result:: :tcp or :udp def mode case attributes['mode'] when 'udp' then :udp else :tcp end end ## # Set the transfer mode # m:: :tcp or :udp def mode=(m) case m when :udp then attributes['mode'] = 'udp' else attributes['mode'] = 'tcp' end end ## # Get the child # result:: [StreamHostUsed] def streamhost_used first_element('streamhost-used') end ## # Get the text of the child # result:: [JID] or [nil] def activate j = first_element_text('activate') j ? JID.new(j) : nil end ## # Set the text of the child # s:: [JID] def activate=(s) replace_element_text('activate', s ? s.to_s : nil) end end ## # element, normally appear # as children of IqQueryBytestreams class StreamHost < XMPPElement name_xmlns 'streamhost', NS_BYTESTREAMS ## # Initialize a element # jid:: [JID] # host:: [String] Hostname or IP address # port:: [Fixnum] Port number def initialize(jid=nil, host=nil, port=nil) super() self.jid = jid self.host = host self.port = port end ## # Get the JID of the streamhost def jid (a = attributes['jid']) ? JID.new(a) : nil end ## # Set the JID of the streamhost def jid=(j) attributes['jid'] = (j ? j.to_s : nil) end ## # Get the host address of the streamhost def host attributes['host'] end ## # Set the host address of the streamhost def host=(h) attributes['host'] = h end ## # Get the zeroconf attribute of the streamhost def zeroconf attributes['zeroconf'] end ## # Set the zeroconf attribute of the streamhost def zeroconf=(s) attributes['zeroconf'] = s end ## # Get the port number of the streamhost def port p = attributes['port'].to_i (p == 0 ? nil : p) end ## # Set the port number of the streamhost def port=(p) attributes['port'] = p.to_s end end ## # element, normally appears # as child of IqQueryBytestreams class StreamHostUsed < XMPPElement name_xmlns 'streamhost-used', NS_BYTESTREAMS def initialize(jid=nil) super() self.jid = jid end def jid (a = attributes['jid']) ? JID.new(a) : nil end def jid=(j) attributes['jid'] = (j ? j.to_s : nil) end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/iq/si.rb0000644000175000017500000001013313647121573022116 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'time' # For Time#xmlschema require 'xmpp4r/feature_negotiation/iq/feature' module Jabber module Bytestreams PROFILE_FILETRANSFER = 'http://jabber.org/protocol/si/profile/file-transfer' ## # Iq child 'si' for Stream-Initiation class IqSi < XMPPElement name_xmlns 'si', 'http://jabber.org/protocol/si' force_xmlns true def initialize(id=nil, profile=nil, mime_type=nil) super() self.id = id self.profile = profile self.mime_type = mime_type end ## # Session ID of this stream def id attributes['id'] end ## # Set Session ID of this stream def id=(s) attributes['id'] = s end ## # MIME type of this stream def mime_type attributes['mime-type'] end ## # Set MIME type of this stream def mime_type=(s) attributes['mime-type'] = s end ## # Stream profile, can indicate file-transfer def profile attributes['profile'] end ## # Set stream profile def profile=(s) attributes['profile'] = s end ## # child # result:: [IqSiFile] def file first_element('file') end ## # child # result:: [IqFeature] def feature first_element('feature') end end ## # File-transfer meta-information, # may appear as in IqSi class IqSiFile < XMPPElement name_xmlns 'file', PROFILE_FILETRANSFER def initialize(fname=nil, size=nil) super() self.fname = fname self.size = size end ## # Get filename (attribute 'name') def fname attributes['name'] end ## # Set filename (attribute 'name') def fname=(s) attributes['name'] = s end ## # Get MD5 hash def hash attributes['hash'] end ## # Set MD5 hash def hash=(s) attributes['hash'] = s end ## # Get file date # result:: [Time] or nil def date begin Time.xmlschema(attributes['date']) rescue ArgumentError nil end end ## # Set file date # d:: [Time] or nil def date=(d) attributes['date'] = (d ? d.xmlschema : nil) end ## # File size in bytes # result:: [Fixnum] def size (attributes['size'] =~ /^\d+$/) ? attributes['size'].to_i : nil end ## # Set file size def size=(s) attributes['size'] = s ? s.to_s : nil end ## # File description def description first_element_text('desc') end ## # Set file description def description=(s) replace_element_text('desc', s) end ## # child # # A file-transfer offer may contain this with # no attributes set, indicating the ability to # do ranged transfers. # result:: [IqSiFileRange] def range first_element('range') end end ## # Information for ranged transfers class IqSiFileRange < XMPPElement name_xmlns 'range', PROFILE_FILETRANSFER def initialize(offset=nil, length=nil) super() self.offset = offset self.length = length end ## # File offset (for continuing an interrupted transfer) def offset (attributes['offset'] =~ /^\d+$/) ? attributes['offset'].to_i : nil end ## # Set file offset def offset=(o) attributes['offset'] = (o ? o.to_s : nil) end ## # File length (if not to transfer whole file) def length (attributes['length'] =~ /^\d+$/) ? attributes['length'].to_i : nil end ## # Set file length def length=(o) attributes['length'] = (o ? o.to_s : nil) end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/0000755000175000017500000000000013647121573022026 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/0000755000175000017500000000000013647121573025520 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb0000644000175000017500000000467313647121573027345 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams ## # SOCKS5 Bytestreams implementation of the target site class SOCKS5BytestreamsTarget < SOCKS5Bytestreams ## # See SOCKS5Bytestreams#initialize def initialize(stream, session_id, initiator_jid, target_jid) @connect_timeout = 60 @accept_ready = Semaphore::new super end def accept_wait @accept_ready.wait end ## # Wait until the stream has been established # # May raise various exceptions def accept error = nil connect_sem = Semaphore.new iq_received_sem = Semaphore.new @stream.add_iq_callback(200, self) { |iq| if iq.type == :set and iq.from == @initiator_jid and iq.to == @target_jid and iq.query.kind_of?(IqQueryBytestreams) begin iq_received_sem.run @stream.delete_iq_callback(self) iq.query.each_element('streamhost') { |streamhost| if streamhost.host and streamhost.port and not @socks begin @socks = connect_socks(streamhost) @streamhost_used = streamhost rescue Exception => e Jabber::debuglog("SOCKS5 Bytestreams: #{e.class}: #{e}\n#{e.backtrace.join("\n")}") @streamhost_cbs.process(streamhost, :failure, e) end end } reply = iq.answer(false) if @streamhost_used reply.type = :result reply.add(IqQueryBytestreams.new) reply.query.add(StreamHostUsed.new(@streamhost_used.jid)) else reply.type = :error reply.add(ErrorResponse.new('item-not-found')) end @stream.send(reply) rescue Exception => e error = e end connect_sem.run true else false end } @accept_ready.run begin Timeout::timeout(@connect_timeout) { iq_received_sem.wait } connect_sem.wait rescue Timeout::Error @stream.delete_iq_callback(self) end raise error if error (@socks != nil) end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb0000644000175000017500000000576313647121573030062 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams ## # SOCKS5Bytestreams implementation for the initiator side class SOCKS5BytestreamsInitiator < SOCKS5Bytestreams attr_reader :streamhosts def initialize(stream, session_id, initiator_jid, target_jid) super @streamhosts = [] end ## # Add a streamhost which will be offered to the target # # streamhost:: can be: # * [StreamHost] if already got all information (host/port) # * [SOCKS5BytestreamsServer] if this is the local streamhost # * [String] or [JID] if information should be automatically resolved by SOCKS5Bytestreams::query_streamhost def add_streamhost(streamhost) if streamhost.kind_of?(StreamHost) @streamhosts << streamhost elsif streamhost.kind_of?(SOCKS5BytestreamsServer) streamhost.each_streamhost(@initiator_jid) { |sh| @streamhosts << sh } elsif streamhost.kind_of?(String) or streamhost.kind_of?(JID) @streamhosts << SOCKS5Bytestreams::query_streamhost(@stream, streamhost, @initiator_jid) else raise "Unknown streamhost type: #{streamhost.class}" end end ## # Send the configured streamhosts to the target, # wait for an answer and # connect to the host the target chose. def open iq1 = Iq.new(:set, @target_jid) iq1.from = @initiator_jid bs = iq1.add IqQueryBytestreams.new(@session_id) @streamhosts.each { |se| bs.add(se) } peer_used = nil @stream.send_with_id(iq1) { |response| if response.query.kind_of?(IqQueryBytestreams) peer_used = response.query.streamhost_used raise "No streamhost-used" unless peer_used raise "Invalid streamhost-used" unless peer_used.jid end } @streamhost_used = nil @streamhosts.each { |sh| if peer_used.jid == sh.jid @streamhost_used = sh break end } if @streamhost_used.jid == @initiator_jid # This is our own JID, so the target chose SOCKS5BytestreamsServer @socks = @streamhost_used.server.peer_sock(stream_address) raise "Target didn't connect" unless @socks @streamhost_cbs.process(@streamhost_used, :success, nil) else begin @socks = connect_socks(@streamhost_used) rescue Exception => e Jabber::debuglog("SOCKS5 Bytestreams: #{e.class}: #{e}\n#{e.backtrace.join("\n")}") @streamhost_cbs.process(@streamhost_used, :failure, e) raise e end iq2 = Iq.new(:set, @streamhost_used.jid) iq2.add(IqQueryBytestreams.new(@session_id)).activate = @target_jid.to_s @stream.send_with_id(iq2) end end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb0000644000175000017500000001131013647121573026753 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'socket' require 'thread' require 'timeout' require 'digest/sha1' require 'xmpp4r/callbacks' require 'xmpp4r/bytestreams/helper/socks5bytestreams/socks5' module Jabber module Bytestreams ## # SOCKS5 Bytestreams (JEP-0065) implementation # # Don't use directly, use SOCKS5BytestreamsInitiator # and SOCKS5BytestreamsTarget class SOCKS5Bytestreams ## # [StreamHost] the SOCKS connection is using attr_reader :streamhost_used ## # SOCKS connection timeout (for trying multiple streamhosts) # # default: nil, use the OS' default timeout attr_accessor :connect_timeout def initialize(stream, session_id, initiator_jid, target_jid) @stream = stream @session_id = session_id @initiator_jid = (initiator_jid.kind_of?(String) ? JID.new(initiator_jid) : initiator_jid) @target_jid = (target_jid.kind_of?(String) ? JID.new(target_jid) : target_jid) @socks = nil @connect_timeout = nil @streamhost_used = nil @streamhost_cbs = CallbackList.new end ## # Add a callback that will be called when there is action regarding # SOCKS stream-hosts # # Usage of this callback is optional and serves informational purposes only. # # block takes three arguments: # * The StreamHost instance that is currently being tried # * State information (is either :connecting, :authenticating, :success or :failure) # * The exception value for the state :failure, else nil def add_streamhost_callback(priority = 0, ref = nil, &block) @streamhost_cbs.add(priority, ref, block) end ## # Receive from the stream-host # length:: [Fixnum] Amount of bytes (Will be passed to TCPSocket#read for the underlying SOCKS5 connection) # result:: [String] (or [nil] if finished) def read(length=nil) @socks.read(length) end ## # Flush the SOCKS5 socket def flush @socks.flush end ## # Send to the stream-host # buf:: [String] Data # result:: [Fixnum] Amount of bytes sent def write(buf) @socks.write(buf) # FIXME: On FreeBSD this throws Errno::EPERM after it has already written a few # kilobytes, and when there are multiple sockets. ktrace told, that this originates # from the syscall, not ruby. end ## # Close the stream-host connection def close @socks.close end ## # Query a JID for its stream-host information # # SOCKS5BytestreamsInitiator#add_streamhost can do this for you. # Use this method if you plan to do multiple transfers, so # you can cache the result. # stream:: [Stream] to operate on # streamhost:: [JID] of the proxy # my_jid:: [JID] Optional sender JID for Component operation def self.query_streamhost(stream, streamhost, my_jid=nil) res = nil iq = Iq.new(:get, streamhost) iq.from = my_jid iq.add(IqQueryBytestreams.new) stream.send_with_id(iq) { |reply| reply.query.each_element { |e| if e.kind_of?(StreamHost) e.jid = reply.from # Help misconfigured proxys res = e end } } if res and res.jid and res.host and res.port res else nil end end private ## # The address the stream-host expects from us. # According to JEP-0096 this is the SHA1 hash # of the concatenation of session_id, # initiator_jid and target_jid. # result:: [String] SHA1 hash def stream_address Digest::SHA1.hexdigest("#{@session_id}#{@initiator_jid}#{@target_jid}") end ## # Try a streamhost # result:: [SOCKS5Socket] def connect_socks(streamhost) Timeout::timeout(@connect_timeout, Errno::ETIMEDOUT) { Jabber::debuglog("SOCKS5 Bytestreams: connecting to proxy #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})") @streamhost_cbs.process(streamhost, :connecting, nil) socks = SOCKS5Socket.new(streamhost.host, streamhost.port) Jabber::debuglog("SOCKS5 Bytestreams: connected, authenticating") @streamhost_cbs.process(streamhost, :authenticating, nil) socks.auth socks.connect_domain(stream_address, 0) Jabber::debuglog("SOCKS5 Bytestreams: connected") @streamhost_cbs.process(streamhost, :success, nil) socks } end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb0000644000175000017500000001435113647121573027357 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams ## # The SOCKS5BytestreamsServer is an implementation of a SOCKS5 server. # # You can use it if you're reachable by your SOCKS5Bytestreams peers, # thus avoiding use of an external proxy. # # ==Usage: # * Instantiate with an unfirewalled port # * Add your external IP addresses with SOCKS5BytestreamsServer#add_address # * Once you've got an *outgoing* SOCKS5BytestreamsInitiator, do # SOCKS5BytestreamsInitiator#add_streamhost(my_socks5bytestreamsserver) # *before* you do SOCKS5BytestreamsInitiator#open class SOCKS5BytestreamsServer ## # Start a local SOCKS5BytestreamsServer # # Will start to listen on the given TCP port and # accept new peers # port:: [Fixnum] TCP port to listen on # listen_on:: [String] Optional address for the server socket to listen on (i.e. '0.0.0.0' or '::') def initialize(port, listen_on=nil) @port = port @addresses = [] @peers = [] @peers_lock = Mutex.new if listen_on socket = TCPServer.new(listen_on, port) else socket = TCPServer.new(port) end Thread.new do Thread.current.abort_on_exception = true loop do peer = SOCKS5BytestreamsPeer.new(socket.accept) Thread.new do Thread.current.abort_on_exception = true begin peer.start rescue Jabber::debuglog("SOCKS5 BytestreamsServer: Error accepting peer: #{$!}") end end @peers_lock.synchronize do @peers << peer end end end end ## # Find the socket a peer is associated to # # This method also performs some housekeeping, ie. removing # peers with closed sockets. # addr:: [String] Address like SOCKS5Bytestreams#stream_address # result:: [TCPSocker] or [nil] def peer_sock(addr) res = nil @peers_lock.synchronize { removes = [] @peers.each { |peer| if peer.socket and peer.socket.closed? # Queue peers with closed socket for removal removes << peer elsif peer.address == addr and res.nil? res = peer.socket end # If we sent multiple addresses of our own, clients may # connect multiple times. DO NOT close any other connections # here. These may belong to other concurrent bytestreams, # believe that the peer will close any unneeded sockets # which will then be picked up by the next call to peer_sock. } # If we sent multiple addresses of our own, clients may # connect multiple times. Close these connections here. @peers.delete_if { |peer| if removes.include? peer peer.socket.close rescue IOError true else false end } } res end ## # Add an external IP address # # This is a must-have, as SOCKS5BytestreamsInitiator must inform # the target where to connect def add_address(address) @addresses << address end ## # Iterate through all configured addresses, # yielding SOCKS5BytestreamsServerStreamHost # instances, which should be passed to # SOCKS5BytestreamsInitiator#add_streamhost # # This will be automatically invoked if you pass an instance # of SOCKS5BytestreamsServer to # SOCKS5BytestreamsInitiator#add_streamhost # my_jid:: [JID] My Jabber-ID def each_streamhost(my_jid, &block) @addresses.each { |address| yield SOCKS5BytestreamsServerStreamHost.new(self, my_jid, address, @port) } end end ## # A subclass of StreamHost which possesses a # server attribute, to let SOCKS5BytestreamsInitiator # know this is the local SOCKS5BytestreamsServer class SOCKS5BytestreamsServerStreamHost < StreamHost attr_reader :server def initialize(server, jid=nil, host=nil, port=nil) super(jid, host, port) @server = server end end ## # This class will be instantiated by SOCKS5BytestreamsServer # upon accepting a new connection class SOCKS5BytestreamsPeer attr_reader :address, :socket ## # Initialize a new peer # socket:: [TCPSocket] def initialize(socket) @socket = socket Jabber::debuglog("SOCKS5 BytestreamsServer: accepted peer #{@socket.peeraddr[2]}:#{@socket.peeraddr[1]}") end ## # Start handshake process def start if !@socket.respond_to? :getbyte class << @socket; alias getbyte getc; end end auth_ver = @socket.getbyte if auth_ver != 5 # Unsupported version @socket.close return end auth_nmethods = @socket.getbyte auth_methods = @socket.read(auth_nmethods) unless auth_methods.index("\x00") # Client won't accept no authentication @socket.write("\x05\xff") @socket.close return end @socket.write("\x05\x00") Jabber::debuglog("SOCKS5 BytestreamsServer: peer #{@socket.peeraddr[2]}:#{@socket.peeraddr[1]} authenticated") req = @socket.read(4) if req != "\x05\x01\x00\x03" # Unknown version, command, reserved, address-type @socket.close return end req_addrlen = @socket.getbyte req_addr = @socket.read(req_addrlen) req_port = @socket.read(2) if req_port != "\x00\x00" # Port is not 0 @socket.write("\x05\x01") @socket.close return end @socket.write("\x05\x00\x00\x03#{req_addrlen.chr}#{req_addr}\x00\x00") Jabber::debuglog("SOCKS5 BytestreamsServer: peer #{@socket.peeraddr[2]}:#{@socket.peeraddr[1]} connected for #{req_addr}") @address = req_addr end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb0000644000175000017500000000330513647121573027255 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'socket' module Jabber module Bytestreams ## # A SOCKS5 client implementation # # ==Usage: # * Initialize with proxy's address and port # * Authenticate # * Connect to target host class SOCKS5Socket < TCPSocket ## # Connect to SOCKS5 proxy def initialize(socks_host, socks_port) super(socks_host, socks_port) end ## # Authenticate for SOCKS5 proxy # # Currently supports only 'no authentication required' def auth write("\x05\x01\x00") buf = read(2) if buf.nil? or buf != "\x05\x00" close raise SOCKS5Error.new("Invalid SOCKS5 authentication: #{buf.inspect}") end self end ## # Issue a CONNECT request to a host name # which is to be resolved by the proxy. # domain:: [String] Host name # port:: [Fixnum] Port number def connect_domain(domain, port) write("\x05\x01\x00\x03#{domain.size.chr}#{domain}#{[port].pack("n")}") buf = read(4) if buf.nil? or buf[0..1] != "\005\000" close raise SOCKS5Error.new("Invalid SOCKS5 connect: #{buf.inspect}") end case buf.respond_to?(:bytes) ? buf.bytes.to_a[3] : buf[3] when 1 then read(6) # IPv4 addr when 3 then read(3 + domain.size) # Domain when 4 then read(18) # IPv6 addr else close raise SOCKS5Error.new("Invalid SOCKS5 address type #{buf[3].to_s}") end self end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/ibb/0000755000175000017500000000000013647121573022562 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/ibb/target.rb0000644000175000017500000000263713647121573024405 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams ## # Implementation of IBB at the target side class IBBTarget < IBB # You may read the block-size after accept attr_reader :block_size def initialize(stream, session_id, initiator_jid, target_jid) # Target and Initiator are swapped here, because we're the target super(stream, session_id, target_jid, initiator_jid) @accept_ready = Semaphore::new end def accept_wait @accept_ready.wait end ## # Wait for the initiator side to start # the stream. def accept connect_sem = Semaphore.new @stream.add_iq_callback(200, self) { |iq| open = iq.first_element('open') if iq.type == :set and iq.from == @peer_jid and iq.to == @my_jid and open and open.attributes['sid'] == @session_id @stream.delete_iq_callback(self) activate @block_size = (open.attributes['block-size'] || 4096).to_i reply = iq.answer(false) reply.type = :result @stream.send(reply) connect_sem.run true else false end } @accept_ready.run connect_sem.wait true end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb0000644000175000017500000000142713647121573025115 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module Bytestreams ## # Implementation of IBB at the initiator side class IBBInitiator < IBB # You may set the block-size before open attr_accessor :block_size ## # Open the stream to the peer, # waits for successful result # # May throw ServerError def open iq = Iq.new(:set, @peer_jid) open = iq.add REXML::Element.new('open') open.add_namespace IBB::NS_IBB open.attributes['sid'] = @session_id open.attributes['block-size'] = @block_size.to_s @stream.send_with_id(iq) activate end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/ibb/base.rb0000644000175000017500000001567713647121573024041 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/base64' module Jabber module Bytestreams ## # In-Band Bytestreams (JEP-0047) implementation # # Don't use directly, use IBBInitiator and IBBTarget # # In-Band Bytestreams should only be used when transferring # very small amounts of binary data, because it is slow and # increases server load drastically. # # Note that the constructor takes a lot of arguments. In-Band # Bytestreams do not specify a way to initiate the stream, # this should be done via Stream Initiation. class IBB NS_IBB = 'http://jabber.org/protocol/ibb' ## # Create a new bytestream # # Will register a callback to intercept data # of this stream. This data will be buffered, you can retrieve # it with receive def initialize(stream, session_id, my_jid, peer_jid) @stream = stream @session_id = session_id @my_jid = (my_jid.kind_of?(String) ? JID.new(my_jid) : my_jid) @peer_jid = (peer_jid.kind_of?(String) ? JID.new(peer_jid) : peer_jid) @active = false @seq_send = 0 @seq_recv = 0 @queue = [] @queue_lock = Mutex.new @pending = Semaphore.new @sendbuf = '' @sendbuf_lock = Mutex.new @block_size = 4096 # Recommended by JEP0047 end def active? @active end ## # Send data # # Data is buffered to match block_size in each packet. # If you need the data to be sent immediately, use # flush afterwards. # buf:: [String] def write(buf) @sendbuf_lock.synchronize { @sendbuf += buf while @sendbuf.size >= @block_size send_data(@sendbuf[0..@block_size-1]) @sendbuf = @sendbuf[@block_size..-1].to_s end } end ## # Empty the send-buffer by sending remaining data def flush @sendbuf_lock.synchronize { while @sendbuf.size > 0 send_data(@sendbuf[0..@block_size-1]) @sendbuf = @sendbuf[@block_size..-1].to_s end } end ## # Receive data # # Will wait until the Message with the next sequence number # is in the stanza queue. def read if active? res = nil while res.nil? @queue_lock.synchronize { @queue.each { |item| # Find next data if item.type == :data and item.seq == @seq_recv.to_s res = item break # No data? Find close elsif item.type == :close and res.nil? res = item end } @queue.delete_if { |item| item == res } } # No data? Wait for next to arrive... @pending.wait unless res end if res.type == :data @seq_recv += 1 @seq_recv = 0 if @seq_recv > 65535 res.data elsif res.type == :close deactivate nil # Closed end else nil end end ## # Close the stream # # Waits for acknowledge from peer, # may throw ServerError def close if active? flush deactivate iq = Iq.new(:set, @peer_jid) close = iq.add REXML::Element.new('close') close.add_namespace IBB::NS_IBB close.attributes['sid'] = @session_id @stream.send_with_id(iq) end end private ## # Send data directly # data:: [String] def send_data(databuf) if active? msg = Message.new msg.from = @my_jid msg.to = @peer_jid data = msg.add REXML::Element.new('data') data.add_namespace NS_IBB data.attributes['sid'] = @session_id data.attributes['seq'] = @seq_send.to_s data.text = Base64::encode64(databuf) # TODO: Implement AMP correctly amp = msg.add REXML::Element.new('amp') amp.add_namespace 'http://jabber.org/protocol/amp' deliver_at = amp.add REXML::Element.new('rule') deliver_at.attributes['condition'] = 'deliver-at' deliver_at.attributes['value'] = 'stored' deliver_at.attributes['action'] = 'error' match_resource = amp.add REXML::Element.new('rule') match_resource.attributes['condition'] = 'match-resource' match_resource.attributes['value'] = 'exact' match_resource.attributes['action'] = 'error' @stream.send(msg) @seq_send += 1 @seq_send = 0 if @seq_send > 65535 else raise 'Attempt to send data when not activated' end end def activate unless active? @stream.add_message_callback(200, self) { |msg| data = msg.first_element('data') if msg.from == @peer_jid and msg.to == @my_jid and data and data.attributes['sid'] == @session_id if msg.type == nil @queue_lock.synchronize { @queue.push IBBQueueItem.new(:data, data.attributes['seq'], data.text.to_s) @pending.run } elsif msg.type == :error @queue_lock.synchronize { @queue << IBBQueueItem.new(:close) @pending.run } end true else false end } @stream.add_iq_callback(200, self) { |iq| close = iq.first_element('close') if iq.type == :set and close and close.attributes['sid'] == @session_id answer = iq.answer(false) answer.type = :result @stream.send(answer) @queue_lock.synchronize { @queue << IBBQueueItem.new(:close) @pending.run } true else false end } @active = true end end def deactivate if active? @stream.delete_message_callback(self) @stream.delete_iq_callback(self) @active = false end end end ## # Represents an item in the internal data queue class IBBQueueItem attr_reader :type, :seq def initialize(type, seq=nil, data_text='') unless [:data, :close].include? type raise "Unknown IBBQueueItem type: #{type}" end @type = type @seq = seq @data = data_text end ## # Return the Base64-*decoded* data # # There's no need to catch Exceptions here, # as none are thrown. def data Base64::decode64(@data) end end end end xmpp4r-0.5.6/lib/xmpp4r/bytestreams/helper/filetransfer.rb0000644000175000017500000002322313647121573025041 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/callbacks' require 'xmpp4r/bytestreams/iq/si' require 'xmpp4r/bytestreams/iq/bytestreams' require 'xmpp4r/dataforms/x/data' require 'xmpp4r/bytestreams/helper/ibb/base' require 'xmpp4r/bytestreams/helper/socks5bytestreams/base' require 'xmpp4r/bytestreams/helper/socks5bytestreams/target' module Jabber module FileTransfer ## # The TransferSource is an interface (Mix-in) # which sources for FileTransfer#offer should include module TransferSource ## # Filename of the offered file def filename end ## # Mime-type of the offered file, can be nil def mime end ## # Size of the offered file def size end ## # MD5-Sum of the offered file, can be nil def md5 end ## # Date of the offered file, can be nil def date end ## # Read a chunk from the source # # If this is a ranged transfer, it should # implement length checking # length:: [Fixnum] def read(length=nil) end ## # Seek in the source for ranged transfers def seek(position) end ## # Set the amount of data to send for ranged transfers def length=(l) end ## # Does implement the methods seek and length= ? # # FileTransfer will only then offer a ranged transfer. # result:: [false] or [true] def can_range? false end end ## # Simple implementation of TransferSource # for sending simple files # (supports ranged transfers) class FileSource include TransferSource def initialize(filename) @file = File.new(filename, "rb") @filename = filename @bytes_read = 0 @length = nil end def filename File::basename @filename end ## # Everything is 'application/octet-stream' def mime 'application/octet-stream' end def size File.size @filename end def date @file.mtime end ## # Because it can_range?, this method implements length checking def read(length=512) if @length return nil if @bytes_read >= @length # Already read everything requested if @bytes_read + length > @length # Will we read more than requested? length = @length - @bytes_read # Truncate it! end end buf = @file.read(length) @bytes_read += buf.size if buf buf end def seek(position) @file.seek(position) end def length=(l) @length = l end def can_range? true end end ## # The FileTransfer helper provides the ability to respond # to incoming and to offer outgoing file-transfers. class Helper ## # Set this if you want to use this helper in a Component attr_accessor :my_jid ## # Set this to false if you don't want to use SOCKS5Bytestreams attr_accessor :allow_bytestreams ## # Set this to false if you don't want to use IBB attr_accessor :allow_ibb ## # Create a new FileTransfer instance def initialize(stream) @stream = stream @my_jid = nil @allow_bytestreams = true @allow_ibb = true @incoming_cbs = CallbackList.new @stream.add_iq_callback(150, self) { |iq| if iq.type == :set file = iq.first_element('si/file') field = nil iq.each_element('si/feature/x') { |e| field = e.field('stream-method') } if file and field @incoming_cbs.process(iq, file) true else false end else false end } end ## # Add a callback which will be invoked upon an incoming file-transfer # # block takes two arguments: # * Iq # * Bytestreams::IqSiFile in the Iq # You may then invoke accept or decline def add_incoming_callback(priority = 0, ref = nil, &block) @incoming_cbs.add(priority, ref, block) end ## # Accept an incoming file-transfer, # to be used in a block given to add_incoming_callback # # offset and length will be ignored if there is no # 'si/file/range' in iq. # iq:: [Iq] of file-transfer we want to accept # offset:: [Fixnum] or [nil] # length:: [Fixnum] or [nil] # result:: [Bytestreams::SOCKS5BytestreamsTarget] or [Bytestreams::IBBTarget] or [nil] if no valid stream-method def accept(iq, offset=nil, length=nil) oldsi = iq.first_element('si') answer = iq.answer(false) answer.type = :result si = answer.add(Bytestreams::IqSi.new) if (offset or length) and oldsi.file.range si.add(Bytestreams::IqSiFile.new) si.file.add(Bytestreams::IqSiFileRange.new(offset, length)) end si.add(FeatureNegotiation::IqFeature.new.import(oldsi.feature)) si.feature.x.type = :submit stream_method = si.feature.x.field('stream-method') if stream_method.options.keys.include?(Bytestreams::NS_BYTESTREAMS) and @allow_bytestreams stream_method.values = [Bytestreams::NS_BYTESTREAMS] stream_method.options = [] @stream.send(answer) Bytestreams::SOCKS5BytestreamsTarget.new(@stream, oldsi.id, iq.from, iq.to) elsif stream_method.options.keys.include?(Bytestreams::IBB::NS_IBB) and @allow_ibb stream_method.values = [Bytestreams::IBB::NS_IBB] stream_method.options = [] @stream.send(answer) Bytestreams::IBBTarget.new(@stream, oldsi.id, iq.from, iq.to) else eanswer = iq.answer(false) eanswer.type = :error eanswer.add(ErrorResponse.new('bad-request')).type = :cancel eanswer.error.add(REXML::Element.new('no-valid-streams')).add_namespace('http://jabber.org/protocol/si') @stream.send(eanswer) nil end end ## # Decline an incoming file-transfer, # to be used in a block given to add_incoming_callback # iq:: [Iq] of file-transfer we want to decline def decline(iq) answer = iq.answer(false) answer.type = :error error = answer.add(ErrorResponse.new('forbidden', 'Offer declined')) error.type = :cancel @stream.send(answer) end ## # Offer a file to somebody # # Will wait for a response from the peer # # The result is a stream which you can configure, or nil # if the peer responded with an invalid stream-method. # # May raise an ServerError # jid:: [JID] to send the file to # source:: File-transfer source, implementing the FileSource interface # desc:: [String] or [nil] Optional file description # from:: [String] or [nil] Optional jid for components # result:: [Bytestreams::SOCKS5BytestreamsInitiator] or [Bytestreams::IBBInitiator] or [nil] def offer(jid, source, desc=nil, from=nil) from = from || @my_jid || @stream.jid session_id = Jabber::IdGenerator.instance.generate_id offered_methods = {} if @allow_bytestreams offered_methods[Bytestreams::NS_BYTESTREAMS] = nil end if @allow_ibb offered_methods[Bytestreams::IBB::NS_IBB] = nil end iq = Iq.new(:set, jid) iq.from = from si = iq.add(Bytestreams::IqSi.new(session_id, Bytestreams::PROFILE_FILETRANSFER, source.mime)) file = si.add(Bytestreams::IqSiFile.new(source.filename, source.size)) file.hash = source.md5 file.date = source.date file.description = desc if desc file.add(Bytestreams::IqSiFileRange.new) if source.can_range? feature = si.add(REXML::Element.new('feature')) feature.add_namespace 'http://jabber.org/protocol/feature-neg' x = feature.add(Dataforms::XData.new(:form)) stream_method_field = x.add(Dataforms::XDataField.new('stream-method', :list_single)) stream_method_field.options = offered_methods begin stream_method = nil response = nil @stream.send_with_id(iq) do |r| response = r si = response.first_element('si') if si and si.feature and si.feature.x stream_method = si.feature.x.field('stream-method').values.first if si.file and si.file.range if source.can_range? source.seek(si.file.range.offset) if si.file.range.offset source.length = si.file.range.length if si.file.range.length else source.read(si.file.range.offset) end end end end rescue ServerError => e if e.error.code == 403 # Declined return false else raise e end end if stream_method == Bytestreams::NS_BYTESTREAMS and @allow_bytestreams Bytestreams::SOCKS5BytestreamsInitiator.new(@stream, session_id, from, jid) elsif stream_method == Bytestreams::IBB::NS_IBB and @allow_ibb Bytestreams::IBBInitiator.new(@stream, session_id, from, jid) else # Target responded with a stream_method we didn't offer eanswer = response.answer eanswer.type = :error eanswer.add ErrorResponse.new('bad-request') @stream.send(eanswer) nil end end end end end xmpp4r-0.5.6/lib/xmpp4r/command/0000755000175000017500000000000013647121573017623 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/command/iq/0000755000175000017500000000000013647121573020234 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/command/iq/command.rb0000644000175000017500000000667513647121573022215 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/iq' module Jabber module Command ## # Class for handling ad-hoc commands # (JEP 0050) # # A command is uniquely identified by its node attribute. class IqCommand < Iq name_xmlns 'command', 'http://jabber.org/protocol/commands' def initialize(node=nil, action=nil) super() set_node(node) set_action(action) end ## # Get the node of the Command stanza # result:: [String] or nil def node attributes['node'] end ## # Set the node of the Command stanza # v:: [String] or nil def node=(v) attributes['node'] = v end ## # Set the node of the Command stanza (chaining-friendly) # v:: [String] or nil def set_node(v) self.node = v self end ## # Get the sessionid of the Command stanza # result:: [String] or nil def sessionid attributes['sessionid'] end ## # Set the sessionid of the Command stanza # v:: [String] or nil def sessionid=(v) attributes['sessionid'] = v end ## # Set the sessionid of the Command stanza (chaining-friendly) # v:: [String] or nil def set_sessionid(v) self.sessionid = v self end ## # Get the action of the Command stanza # # The following Symbols are allowed: # * :execute # * :cancel # * :prev # * :next # * :complete # return:: [Symbol] or nil def action case attributes['action'] when 'execute' then :execute when 'cancel' then :cancel when 'prev' then :prev when 'next' then :next when 'complete' then :complete else nil end end ## # Set the action of the Command stanza (see IqCommand#action for details) # v:: [Symbol] or nil def action=(v) attributes['action'] = case v when :execute then 'execute' when :cancel then 'cancel' when :prev then 'prev' when :next then 'next' when :complete then 'complete' else nil end end ## # Set the action of the Command stanza (chaining-friendly) # v:: [Symbol] or nil def set_action(v) self.action = v self end ## # Get the status of the Command stanza # # The following Symbols are allowed: # * :executing # * :completed # * :canceled # return:: [Symbol] or nil def status case attributes['status'] when 'executing' then :executing when 'completed' then :completed when 'canceled' then :canceled else nil end end ## # Set the status of the Command stanza (see IqCommand#status for details) # v:: [Symbol] or nil def status=(v) attributes['status'] = case v when :executing then 'executing' when :completed then 'completed' when :canceled then 'canceled' else nil end end ## # Set the status of the Command stanza (chaining-friendly) # v:: [Symbol] or nil def set_status(v) self.status = v self end ## # Get the actions allowed # return:: [REXML::Element] or nil def actions first_element('actions') end end end end xmpp4r-0.5.6/lib/xmpp4r/command/helper/0000755000175000017500000000000013647121573021102 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/command/helper/responder.rb0000644000175000017500000000247613647121573023441 0ustar debbiecocoadebbiecocoamodule Jabber module Command ## # The Responder Helper handles the low-level stuff of the # Ad-hoc commands (JEP 0050). class Responder ## # Initialize a Responder def initialize(stream) @stream = stream @commandsdiscocbs = CallbackList.new @commandexeccbs = CallbackList.new stream.add_iq_callback(180, self) { |iq| iq_callback(iq) } end ## # Add a callback for stanzas asking for the list # of ad-hoc commands def add_commands_disco_callback(priority = 0, ref = nil, &block) @commandsdiscocbs.add(priority, ref, block) end ## # Add a callback for stanzas asking for the execution # of an ad-hoc command def add_commands_exec_callback(priority = 0, ref = nil, &block) @commandexeccbs.add(priority, ref, block) end ## # Handles stanzas and execute callbacks def iq_callback(iq) if iq.type == :get if iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoItems) && iq.query.node == "http://jabber.org/protocol/commands" @commandsdiscocbs.process(iq) end elsif iq.type == :set && iq.command @commandexeccbs.process(iq) end end end end end xmpp4r-0.5.6/lib/xmpp4r/base64.rb0000644000175000017500000000136613647121573017624 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io begin require 'base64' rescue LoadError ## # Ruby 1.9 has dropped the Base64 module, # this is a replacement # # We could replace all call by Array#pack('m') # and String#unpack('m'), but this module # improves readability. module Base64 ## # Encode a String # data:: [String] Binary # result:: [String] Binary in Base64 def self.encode64(data) [data].pack('m') end ## # Decode a Base64-encoded String # data64:: [String] Binary in Base64 # result:: [String] Binary def self.decode64(data64) data64.unpack('m').first end end end xmpp4r-0.5.6/lib/xmpp4r/httpbinding/0000755000175000017500000000000013647121573020517 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/httpbinding/client.rb0000644000175000017500000003302313647121573022323 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # TODO: eval element' end @streamid = res_body.attributes['authid'] @status = CONNECTED @http_sid = res_body.attributes['sid'] @http_wait = res_body.attributes['wait'].to_i if res_body.attributes['wait'] @http_hold = res_body.attributes['hold'].to_i if res_body.attributes['hold'] @http_inactivity = res_body.attributes['inactivity'].to_i @http_polling = res_body.attributes['polling'].to_i @http_polling = 5 if @http_polling == 0 @http_requests = res_body.attributes['requests'].to_i @http_requests = 1 if @http_requests == 0 receive_elements_with_rid(@http_rid, res_body.children) @features_sem.run end ## # Ensure that there is one pending request # # Will be automatically called if you've sent # a stanza. def ensure_one_pending_request return if is_disconnected? if @lock.synchronize { @pending_requests } < 1 send_data('') end end ## # Close the session by sending # def close @status = DISCONNECTED req_body = nil @lock.synchronize { req_body = " @pending_rid cv = ConditionVariable.new @pending_rid_cv << [rid, cv] @pending_rid_cv.sort! while rid > @pending_rid cv.wait(@pending_rid_lock) end end end elements.each { |e| receive(e) } # Signal waiting elements @pending_rid_lock.synchronize do @pending_rid = rid + 1 # Ensure @pending_rid is modified atomically if @pending_rid_cv.size > 0 && @pending_rid_cv.first.first == @pending_rid next_rid, cv = @pending_rid_cv.shift cv.signal end end end ## # Do a POST request def post(body, debug_info) body = body.to_s request = Net::HTTP::Post.new(@uri.path) request.content_length = body.size request.body = body request['Content-Type'] = @http_content_type Jabber::debuglog("HTTP REQUEST (#{@pending_requests}/#{@http_requests}) #{debug_info}:\n#{request.body}") net_http_args = [@uri.host, @uri.port] unless @proxy_args.empty? unless no_proxy?(@uri) net_http_args.concat @proxy_args end end http = Net::HTTP.new(*net_http_args) if @uri.kind_of? URI::HTTPS http.use_ssl = true @http_ssl_setup and @http_ssl_setup.call(http) end http.read_timeout = @http_wait * 1.1 response = http.start { |http| http.request(request) } Jabber::debuglog("HTTP RESPONSE (#{@pending_requests}/#{@http_requests}) #{debug_info}: #{response.class}\n#{response.body}") unless response.kind_of? Net::HTTPSuccess # Unfortunately, HTTPResponses aren't exceptions # TODO: rescue'ing code should be able to distinguish raise Net::HTTPBadResponse, "#{response.class}" end body = REXML::Document.new(response.body).root if body.name != 'body' and body.namespace != 'http://jabber.org/protocol/httpbind' raise REXML::ParseException.new('Malformed body') end body end ## # Check whether uri should be accessed without proxy def no_proxy?(uri) @no_proxy.each do |host_addr| return true if uri.host.match(Regexp.quote(host_addr) + '$') end return false end ## # Prepare data to POST and # handle the result def post_data(data, restart = false) req_body = nil current_rid = nil debug_info = '' begin begin @lock.synchronize { # Do not send unneeded requests if data.size < 1 and @pending_requests > 0 and !restart @pending_requests += 1 # compensate for decrement in ensure clause Jabber::debuglog "post_data: not sending excessive poll" return end req_body = " e Jabber::debuglog("POST error (will retry) #{debug_info}: #{e.class}: #{e}, #{e.backtrace}") receive_elements_with_rid(current_rid, []) # It's not good to resend on *any* exception, # but there are too many cases (Timeout, 404, 502) # where resending is appropriate # TODO: recognize these conditions and act appropriate # FIXME: resending the same data with a new rid is wrong. should resend with the same rid send_data(data) end end ## # Restart stream after SASL authentication def restart Jabber::debuglog("Restarting after SASL") @stream_mechanisms = [] @stream_features = {} @features_sem = Semaphore.new send_data('', true) # restart end ## # Send data, # buffered and obeying 'polling' and 'requests' limits def send_data(data, restart = false) Jabber::debuglog("send_data") while true do # exit by return @lock.synchronize do if @last_send + 0.05 >= Time.now Jabber::debuglog("send_data too fast: waiting 0.05sec") next end @send_buffer += data limited_by_polling = false if @pending_requests + 1 == @http_requests && @send_buffer.size == 0 limited_by_polling = (@last_send + @http_polling >= Time.now) end limited_by_requests = (@pending_requests + 1 > @http_requests) # Can we send? if !limited_by_polling and !limited_by_requests or !@authenticated Jabber::debuglog("send_data non_limited") data = @send_buffer @send_buffer = '' Thread.new do Thread.current.abort_on_exception = true post_data(data, restart) end return elsif limited_by_requests Jabber::debuglog("send_data limited by requests") # Do nothing. # When a pending request gets an response, it calls send_data('') return elsif # limited_by_polling && @authenticated # Wait until we get some data to send, or @http_polling expires Jabber::debuglog("send_data limited by polling: #{@http_polling}") else Jabber::errorlog("send_data: can't happen: pending_requests=#{@pending_requests} http_requests=#{@http_requests} send_buffer.size=#{@send_buffer.size} limited_by_polling=#{limited_by_polling} limited_by_requests=#{limited_by_requests}") return end end sleep(0.1) end end end end end xmpp4r-0.5.6/lib/xmpp4r/x.rb0000644000175000017500000000203613647121573017002 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' module Jabber ## # A class used to build/parse elements # # These elements may occur as "attachments" # in [Message] and [Presence] stanzas class X < XMPPElement name_xmlns 'x' force_xmlns true end module XParent ## # Get the first element in this stanza, or nil if none found. # wanted_xmlns:: [String] Optional, find the first element having this xmlns, # wanted_xmlns can also be a derivate of XMPPElement from which the namespace will be taken # result:: [REXML::Element] or nil def x(wanted_xmlns=nil) if wanted_xmlns.kind_of? Class and wanted_xmlns.ancestors.include? XMPPElement wanted_xmlns = wanted_xmlns.new.namespace end each_element('x') { |x| if wanted_xmlns.nil? or wanted_xmlns == x.namespace return x end } nil end end end xmpp4r-0.5.6/lib/xmpp4r/discovery/0000755000175000017500000000000013647121573020214 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/discovery/iq/0000755000175000017500000000000013647121573020625 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/discovery/iq/discoitems.rb0000755000175000017500000000652013647121573023323 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module Discovery NS_DISCO_ITEMS = 'http://jabber.org/protocol/disco#items' ## # Class for handling Service Discovery queries, # items # (JEP 0030) # # This may contain multiple Item elements, # describing multiple services to be browsed by Jabber clients. # These may then get further information about these items by # querying IqQueryDiscoInfo and further sub-items by querying # IqQueryDiscoItems. class IqQueryDiscoItems < IqQuery name_xmlns 'query', NS_DISCO_ITEMS ## # Get the queried Service Discovery node or nil # # A Service Discovery node is _not_ a JID node, # this may be a bit confusing. It's just to make # Service Discovery browsing a bit more structured. def node attributes['node'] end ## # Set the queried Service Discovery node or nil def node=(val) attributes['node'] = val end ## # Set the queried Service Discovery node or nil # (chaining-friendly) def set_node(val) self.node = val self end ## # Get all item children # result:: Array of [Discovery::Item] def items get_elements('item') end end ## # Service Discovery item to add() to IqQueryDiscoItems # # Please note that JEP 0030 requires the jid to occur class Item < XMPPElement name_xmlns 'item', NS_DISCO_ITEMS ## # Initialize a new Service Discovery # to be added to IqQueryDiscoItems # jid:: [JID] # iname:: [String] Item name # node:: [String] Service Discovery node (_not_ JID#node) def initialize(jid=nil, iname=nil, node=nil) super() set_jid(jid) set_iname(iname) set_node(node) end ## # Get the item's jid or nil # result:: [String] def jid JID.new(attributes['jid']) end ## # Set the item's jid # val:: [JID] def jid=(val) attributes['jid'] = val.to_s end ## # Set the item's jid (chaining-friendly) # val:: [JID] def set_jid(val) self.jid = val self end ## # Get the item's name or nil # # This has been renamed from to "iname" here # to keep REXML::Element#name accessible # result:: [String] def iname attributes['name'] end ## # Set the item's name # val:: [String] def iname=(val) attributes['name'] = val end ## # Set the item's name (chaining-friendly) # val:: [String] def set_iname(val) self.iname = val self end ## # Get the item's Service Discovery node or nil # result:: [String] def node attributes['node'] end ## # Set the item's Service Discovery node # val:: [String] def node=(val) attributes['node'] = val end ## # Set the item's Service Discovery node (chaining-friendly) # val:: [String] def set_node(val) self.node = val self end end end end xmpp4r-0.5.6/lib/xmpp4r/discovery/iq/discoinfo.rb0000755000175000017500000001147113647121573023136 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module Discovery NS_DISCO_INFO = 'http://jabber.org/protocol/disco#info' ## # Class for handling Service Discovery queries, # info # (JEP 0030) # # This may contain multiple Identity and Feature # elements, describing the type and the supported namespaces of # the service. class IqQueryDiscoInfo < IqQuery name_xmlns 'query', NS_DISCO_INFO ## # Get the queried Service Discovery node or nil # # See IqQueryDiscoItems#node for a # small explanation of this. def node attributes['node'] end ## # Set the queried Service Discovery node or nil # val:: [String] def node=(val) attributes['node'] = val end ## # Set the queried Service Discovery node or nil # (chaining-friendly) # val:: [String] def set_node(val) self.node = val self end ## # Get the first identity child # result:: [Identity] def identity first_element('identity') end ## # Get list of identities # result:: [Array] of [Identity] def identities get_elements('identity') end ## # Get list of features # result:: [Array] of [String] def features res = [] each_element('feature') { |feature| res.push(feature.var) } res end end ## # Service Discovery identity to add() to IqQueryDiscoInfo # # Please note that XEP 0030 requires both category and type to occur, # for a reference see: http://www.xmpp.org/registrar/disco-categories.html class Identity < XMPPElement name_xmlns 'identity', NS_DISCO_INFO ## # Initialize a new Identity # category:: [String] Initial category or nil # iname:: [String] Initial identity name or nil # type:: [String] Initial type or nil def initialize(category=nil, iname=nil, type=nil) super() set_category(category) set_iname(iname) set_type(type) end ## # Get the identity's category or nil # result:: [String] def category attributes['category'] end ## # Set the identity's category # # Service Discovery categories should be somewhat # standardized by some registry, so clients may # represent specific categories by specific icons... # (see http://www.jabber.org/registrar/disco-categories.html) # val:: [String] def category=(val) attributes['category'] = val end ## # Set the identity's category (chaining-friendly) # val:: [String] def set_category(val) self.category = val self end ## # Get the identity's name or nil # # This has been renamed from to "iname" here # to keep REXML::Element#name accessible # result:: [String] def iname attributes['name'] end ## # Set the identity's name # val:: [String] def iname=(val) attributes['name'] = val end ## # Set the identity's name (chaining-friendly) # val:: [String] def set_iname(val) self.iname = val self end ## # Get the identity's type or nil # result:: [String] def type attributes['type'] end ## # Set the identity's type # (see http://www.jabber.org/registrar/disco-categories.html) # val:: [String] def type=(val) attributes['type'] = val end ## # Set the identity's type (chaining-friendly) # val:: [String] def set_type(val) self.type = val self end end ## # Service Discovery feature to add() to IqQueryDiscoInfo # # Please note that JEP 0030 requires var to be set, # for a reference see: http://www.xmpp.org/registrar/disco-features.html class Feature < XMPPElement name_xmlns 'feature', NS_DISCO_INFO ## # Create a new element # var:: [String] New var def initialize(var=nil) super() set_var(var) end ## # Get the feature's var or nil # result:: [String] def var attributes['var'] end ## # Set the feature's var # # This is a namespace the identity supports. # val:: [String] def var=(val) attributes['var'] = val end ## # Set the feature's var (chaining-friendly) # val:: [String] def set_var(val) self.var = val self end end end end xmpp4r-0.5.6/lib/xmpp4r/discovery/helper/0000755000175000017500000000000013647121573021473 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/discovery/helper/responder.rb0000644000175000017500000001074513647121573024030 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' require 'xmpp4r/discovery/iq/discoinfo' require 'xmpp4r/discovery/iq/discoitems' require 'xmpp4r/caps/helper/generator' module Jabber module Discovery ## # Responds to Service Discovery queries on a given node # # Modify returned elements by these attributes: # * Responder#identities # * Responder#features (Responder#add_features is a short-cut accepting an Array of Strings, too) # * Responder#forms # * Responder#items class Responder ## # Service Discovery node this Responder is responsible for # (will not answer queries for other nodes) attr_reader :node ## # Identities returned on Discovery Info query # # Array of [Discovery::Identity] attr_accessor :identities ## # Features returned on Discovery Info query, # # Array of [Discovery::Feature] attr_accessor :features ## # Forms returned on Discovery Info query # (such as Software Information) # # Array of [Dataforms::XData] attr_accessor :forms ## # Children returned on Discovery Item query # # May contain other Discovery::Responder instances # which will generate an item dynamically from their # first identity # # Array of [Discovery::Item] or [Discovery::Responder] (mixed) attr_accessor :items ## # Set the JID this helper feels responsible for # (default: nil, responsible for any JID) attr_accessor :my_jid CALLBACK_PRIORITY = 180 ## # Initialize responder for a specific node # stream:: [Jabber::Stream] # node:: [nil] or [String] def initialize(stream, node=nil, identities=[], features=[], items=[]) @stream = stream @my_jid = nil @node = node @identities = identities @features = [] add_features(features) @forms = [] @items = items @stream.add_iq_callback(CALLBACK_PRIORITY, self) do |iq| my_nodes = [@node, "#{@node}##{generate_ver}"] if iq.type == :get and iq.query.kind_of? IqQueryDiscoInfo and my_nodes.include?(iq.query.node) answer = iq.answer(false) answer.type = :result query = answer.add(IqQueryDiscoInfo.new) query.node = iq.query.node (@identities + @features + @forms).each do |element| query.add(element) end @stream.send(answer) true # handled elsif iq.type == :get and iq.query.kind_of? IqQueryDiscoItems and my_nodes.include?(iq.query.node) answer = iq.answer(false) answer.type = :result query = answer.add(IqQueryDiscoItems.new) query.node = iq.query.node @items.each do |item| if item.kind_of? Responder query.add(item.generate_item) else query.add(item) end end @stream.send(answer) true # handled else false # not handled end end end ## # Add a feature # feature:: [Jabber::Discovery::Feature] or [String] def add_feature(feature) if feature.kind_of? Feature @features << feature else @features << Feature.new(feature.to_s) end end ## # Add a series of features # features:: Array of [Jabber::Discovery::Feature] or [String] def add_features(features) features.each { |feature| add_feature(feature) } end ## # Generate a XEP-0115: Entity Capabilities element # for inclusion in Presence stanzas. This enables efficient # caching of Service Discovery information. def generate_caps Caps::C.new(@node, generate_ver) end ## # Generate an item for inclusion in items discovery in other # responders # return:: [Discovery::Item] or nil def generate_item i = @identities.first if i Item.new(@my_jid || @stream.jid, i.iname, @node) else nil end end private def generate_ver Caps::generate_ver(@identities, @features, @forms) end end end end xmpp4r-0.5.6/lib/xmpp4r/discovery/helper/helper.rb0000644000175000017500000000242513647121573023302 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/discovery' module Jabber module Discovery ## # A Helper to manage service and item discovery. class Helper def initialize(client) @stream = client end ## # Service discovery on a JID. # jid:: [JID] # return:: [Jabber::Discovery::IqQueryDiscoInfo] def get_info_for(jid, node = nil) iq = Jabber::Iq.new(:get, jid) iq.from = @stream.jid disco = Jabber::Discovery::IqQueryDiscoInfo.new disco.node = node iq.add(disco) res = nil @stream.send_with_id(iq) { |reply| res = reply.query } res end ## # Item discovery on a JID. # jid:: [JID] # return:: [Jabber::Discovery::IqQueryDiscoItems] def get_items_for(jid, node = nil) iq = Jabber::Iq.new(:get, jid) iq.from = @stream.jid disco = Jabber::Discovery::IqQueryDiscoItems.new disco.node = node iq.add(disco) res = nil @stream.send_with_id(iq) { |reply| res = reply.query } res end end end end xmpp4r-0.5.6/lib/xmpp4r/jid.rb0000755000175000017500000000714013647121573017305 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber ## # The JID class represents a Jabber Identifier as described by # RFC3920 section 3.1. # # Note that you can use JIDs also for Sorting, Hash keys, ... class JID include Comparable PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/ begin require 'idn' USE_STRINGPREP = true rescue LoadError USE_STRINGPREP = false end ## # Create a new JID. If called as new('a@b/c'), parse the string and # split (node, domain, resource) def initialize(node = "", domain = nil, resource = nil) @resource = resource @domain = domain @node = node if @domain.nil? and @resource.nil? and @node @node, @domain, @resource = @node.to_s.scan(PATTERN).first end if USE_STRINGPREP @node = IDN::Stringprep.nodeprep(@node) if @node @domain = IDN::Stringprep.nameprep(@domain) if @domain @resource = IDN::Stringprep.resourceprep(@resource) if @resource else @node.downcase! if @node @domain.downcase! if @domain end raise ArgumentError, 'Node too long' if (@node || '').length > 1023 raise ArgumentError, 'Domain too long' if (@domain || '').length > 1023 raise ArgumentError, 'Resource too long' if (@resource || '').length > 1023 end ## # Returns a string representation of the JID # * "" # * "domain" # * "node@domain" # * "domain/resource" # * "node@domain/resource" def to_s s = @domain s = "#{@node}@#{s}" if @node s += "/#{@resource}" if @resource return s end ## # Returns a new JID with resource removed. # return:: [JID] def strip JID.new(@node, @domain) end alias_method :bare, :strip ## # Removes the resource (sets it to nil) # return:: [JID] self def strip! @resource = nil self end alias_method :bare!, :strip! ## # Returns a hash value of the String representation # (see JID#to_s) def hash return to_s.hash end ## # Ccompare to another JID # # String representations are compared, see JID#to_s def eql?(o) to_s.eql?(o.to_s) end ## # Ccompare to another JID # # String representations are compared, see JID#to_s def ==(o) to_s == o.to_s end ## # Compare two JIDs, # helpful for sorting etc. # # String representations are compared, see JID#to_s def <=>(o) to_s <=> o.to_s end # Get the JID's node def node @node end # Set the JID's node def node=(v) @node = v.to_s if USE_STRINGPREP @node = IDN::Stringprep.nodeprep(@node) if @node end end # Get the JID's domain def domain return nil if @domain.empty? @domain end # Set the JID's domain def domain=(v) @domain = v.to_s if USE_STRINGPREP @domain = IDN::Stringprep.nodeprep(@domain) end end # Get the JID's resource def resource @resource end # Set the JID's resource def resource=(v) @resource = v.to_s if USE_STRINGPREP @resource = IDN::Stringprep.nodeprep(@resource) end end # Escape JID def JID::escape(jid) return jid.to_s.gsub('@', '%') end # Test if jid is empty def empty? to_s.empty? end # Test id jid is strepped def stripped? @resource.nil? end alias_method :bared?, :stripped? end end xmpp4r-0.5.6/lib/xmpp4r/version.rb0000644000175000017500000000043313647121573020217 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/version/helper/responder.rb' require 'xmpp4r/version/helper/simpleresponder.rb' require 'xmpp4r/version/iq/version.rb' xmpp4r-0.5.6/lib/xmpp4r/xmppelement.rb0000644000175000017500000001102013647121573021062 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/rexmladdons' module Jabber ## # This class represents an XML element and provides functionality # for automatic casting of XML element classes according to their # element name and namespace. # # Deriving classes must met these criteria: # * The element name and namespace must be specified by calling # the name_xmlns class method # * The class constructor must be callable with no mandatory parameter class XMPPElement < REXML::Element @@name_xmlns_classes = {} @@force_xmlns = false ## # Specify XML element name and xmlns for a deriving class, # this pair and the class will be added to a global pool # # If the namespace is nil the class is a "wildcard class" # matching elements with any xmlns if no other class with # that namespace was defined def self.name_xmlns(name, xmlns=nil) @@name_xmlns_classes[[name, xmlns]] = self end ## # Set whether this element is always built with an xmlns attribute def self.force_xmlns(force) @@force_xmlns = force end ## # Whether this element is always built with an xmlns attribute def self.force_xmlns? @@force_xmlns end ## # Find the name and namespace for a given class. # This class must have registered these two values # by calling name_xmlns at definition time. # # Raises an exception if none was found # klass:: [Class] # result:: [String, String] name and namespace def self.name_xmlns_for_class(klass) klass.ancestors.each do |klass1| @@name_xmlns_classes.each do |name_xmlns,k| if klass1 == k return name_xmlns end end end raise NoNameXmlnsRegistered.new(klass) end ## # Find a class for given name and namespace # name:: [String] # xmlns:: [String] # result:: A descendant of XMPPElement or REXML::Element def self.class_for_name_xmlns(name, xmlns) if @@name_xmlns_classes.has_key? [name, xmlns] @@name_xmlns_classes[[name, xmlns]] elsif @@name_xmlns_classes.has_key? [name, nil] @@name_xmlns_classes[[name, nil]] else REXML::Element end end ## # Import another REXML::Element descendant to: # * Either an element class that registered with # name and xmlns before # * Or if none was found to the class itself # (you may call this class method on a deriving class) def self.import(element) klass = class_for_name_xmlns(element.name, element.namespace) if klass != self and klass.ancestors.include?(self) klass.new.import(element) else self.new.import(element) end end ## # Initialize this element, which will then be initialized # with the name registered with name_xmlns. def initialize(*arg) if arg.empty? name, xmlns = self.class::name_xmlns_for_class(self.class) super(name) if self.class::force_xmlns? add_namespace(xmlns) end else super end end ## # Add a child element which will be imported according to the # child's name and xmlns # element:: [REXML::Element] Child # result:: [REXML::Element or descendant of XMPPElement] New child def typed_add(element) if element.kind_of? REXML::Element element_ns = (element.namespace.to_s == '') ? namespace : element.namespace klass = XMPPElement::class_for_name_xmlns(element.name, element_ns) if klass != element.class element = klass.import(element) end end super(element) end def parent=(new_parent) if parent and parent.namespace('') == namespace('') and attributes['xmlns'].nil? add_namespace parent.namespace('') end super if new_parent and new_parent.namespace('') == namespace('') delete_namespace end end def clone cloned = self.class.new cloned.add_attributes self.attributes.clone cloned.context = @context cloned end ## # Generic XML attribute 'xml:lang' # (REXML provides no shortcut) def xml_lang attributes['xml:lang'] end ## # Set XML language attribute def xml_lang=(l) attributes['xml:lang'] = l end ## # Set XML language attribute (chainable) def set_xml_lang(l) self.xml_lang = l self end end end xmpp4r-0.5.6/lib/xmpp4r/entity_time/0000755000175000017500000000000013647121573020537 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/entity_time/responder.rb0000644000175000017500000000230013647121573023060 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/entity_time/iq' module Jabber::EntityTime ## # XEP 202 Entity Time implementation # # @see http://xmpp.org/extensions/xep-0202.html # # @example # # # class Responder CALLBACK_PRIORITY = 180 ## # +to_s+ allows the responder to be added to another responder as a feature def to_s NS_TIME end def initialize(stream) @stream = stream stream.add_iq_callback(CALLBACK_PRIORITY, self) do |iq| iq_callback(iq) end end # /initialize def iq_callback(iq) if iq.type == :get && iq.first_element('time') && iq.first_element('time').namespace === NS_TIME answer = iq.answer(false) answer.type = :result answer.add(IqTime.new(Time.now)) @stream.send(answer) return true end false end end # /Responder end # /Jabber::EntityTime xmpp4r-0.5.6/lib/xmpp4r/entity_time/iq.rb0000644000175000017500000000204413647121573021475 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'time' module Jabber::EntityTime NS_TIME = 'urn:xmpp:time' class IqTime < Jabber::IqQuery name_xmlns 'time', NS_TIME def initialize(time=nil) super() set_time(time) end def set_time(time=nil) time ||= Time.now tzo = self.add_element('tzo') tzo.add_text(time_zone_offset(time)) utc = self.add_element('utc') utc.add_text(utc_time(time)) end private def utc_time(time) raise ArgumentError, 'invalid time object' unless time.respond_to?(:utc) time.utc.strftime('%Y-%m-%dT%H:%M:%SZ') end def time_zone_offset(time) raise ArgumentError, 'invalid time object' unless time.respond_to?(:utc_offset) sec_offset = time.utc_offset h_offset = sec_offset.to_i / 3600 m_offset = sec_offset.abs % 60 "%+03d:%02d"%[h_offset, m_offset] end end # /IqTime end # /Jabber::EntityTime xmpp4r-0.5.6/lib/xmpp4r/xhtml/0000755000175000017500000000000013647121573017341 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/xhtml/html.rb0000644000175000017500000000661513647121573020642 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/xmppelement' module Jabber module XHTML NS_XHTML_IM = 'http://jabber.org/protocol/xhtml-im' NS_XHTML = 'http://www.w3.org/1999/xhtml' ## # XHTML-IM (XEP-0071) container # # The important methods are: # * HTML#contents= # * HTML#to_text class HTML < XMPPElement name_xmlns 'html', NS_XHTML_IM force_xmlns true ## # Initialize element with HTML contents (see HTML#contents=) def initialize(contents=[]) super() self.contents = contents end ## # Get first XHTML::Body child def body first_element('body') || add(Body.new) end ## # Replace first XHTML::Body child def body=(body) delete_elements('body') add(body) end ## # Replace first XHTML::Body child (chainable) def set_body(body) self.body = body self end ## # Set contents of this HTML document. The "contents" parameter can be: # * An Array of REXML::Element and Strings which will replace the current children of the body # * A single REXML::Element which will replace all other children of the body # * An instance of XHTML::Body which will replace the current body # * A String comprising an HTML fragment. This will be parsed, which could raise an Exception. # We must never send invalid XML over an XMPP stream. If you intend to put variable data in # your HTML, use something like Rails' Builder::XmlMarkup or Ramaze::Gestalt def contents=(contents) if contents.kind_of? String self.body = REXML::Document.new("#{contents}").root elsif contents.kind_of? Body self.body = contents elsif contents.kind_of? Array self.body = Body.new contents.each do |element| if element.kind_of? String body.add_text(element) else body.add(element) end end else self.body = Body.new body.add(contents) end end ## # HTML#contents= chainable def set_contents(contents) self.contents = contents self end ## # Convert contents of this XHTML container to plain text # for easy usage with an additional fall-back in message stanzas # # The resulting string is recursively composed of the text nodes of # all children. # This works because of the design criteria of HTML/XHTML: # readable content is not being put into attributes but as text children. # # If you require clickable links and proper information representation # then compose the text yourself! def to_text text_getter = nil # Create binding so that the following lambda can work recursively text_getter = lambda do |element| if element.kind_of? REXML::Text element.value elsif element.kind_of? REXML::Element element.children.collect { |child| text_getter.call(child) }.join end end text_getter.call(self) # Finally, execute and return results end end ## # HTML Body element, must be the only child of XHTML::HTML class Body < XMPPElement name_xmlns 'body', NS_XHTML force_xmlns true end end end xmpp4r-0.5.6/lib/xmpp4r/idgenerator.rb0000644000175000017500000000156013647121573021037 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'singleton' module Jabber ## # The Jabber::IdGenerator class generates unique IDs for use # in XMMP stanzas. Jabber::IdGenerator includes the Singleton # Mixin, usage as following: # Jabber::IdGenerator.generate_id # => "23" class IdGenerator include Singleton def initialize @last_id = 0 end ## # Generate an unique ID. # # This is kind of boring this way, as it just counts up # a number. Maybe something more random somewhen... def IdGenerator.generate_id IdGenerator.instance.generate_id end def generate_id @last_id += 1 timefrac = Time.new.to_f.to_s.split(/\./, 2).last[-3..-1] "#{@last_id}#{timefrac}" end end end xmpp4r-0.5.6/lib/xmpp4r/component.rb0000644000175000017500000000531513647121573020540 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/connection' module Jabber ## # The component class provides everything needed to build a XMPP Component. # # Components are more flexible as they are only restricted in the use of a # fixed domain. node and resource of JIDs are freely choosable for all stanzas. class Component < Connection # The component's JID attr_reader :jid # The server's address attr_reader :server_address # The server's port attr_reader :server_port # Create a new Component # jid:: [JID] def initialize(jid, server_address=nil, server_port=5347) super() @jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s)) if server_address $stderr.puts "Passing server and port to Jabber::Component.new is " + "obsolete and will vanish in some later XMPP4R release. " + "Please use these arguments when calling " + "Jabber::Client#connect" @server_address = server_address @server_port = server_port end end # Connect to the server # (chaining-friendly) # server:: [String] Hostname # port:: [Integer] TCP port (5347) # return:: self def connect(server=nil, port=5347) if server super(server, port) else super(@server_address, @server_port) end self end ## # Close the connection, # sends tag first def close send("") super end def generate_stream_start(to=nil, from=nil, id=nil, xml_lang="en", xmlns="jabber:component:accept", version="1.0") super end private :generate_stream_start ## # Start the stream-parser and send the component-specific stream opening element def start super send(generate_stream_start(@jid)) { |e| if e.name == 'stream' true else false end } end ## # Send auth with given secret and wait for result # # Throws ComponentAuthenticationFailure # secret:: [String] the shared secret def auth(secret) hash = Digest::SHA1::hexdigest(@streamid.to_s + secret) authenticated = false send("#{hash}") { |r| if r.prefix == 'stream' and r.name == 'error' true elsif r.name == 'handshake' authenticated = true true else false end } unless authenticated raise ComponentAuthenticationFailure.new, "Component authentication failed" end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub.rb0000644000175000017500000000050713647121573020034 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/pubsub/iq/pubsub' require 'xmpp4r/pubsub/children/event' require 'xmpp4r/pubsub/children/item' require 'xmpp4r/pubsub/children/items' require 'xmpp4r/pubsub/children/publish' require 'xmpp4r/pubsub/children/subscription' require 'xmpp4r/pubsub/children/unsubscribe' require 'xmpp4r/pubsub/helper/servicehelper' xmpp4r-0.5.6/lib/xmpp4r/muc/0000755000175000017500000000000013647121573016771 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/muc/iq/0000755000175000017500000000000013647121573017402 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/muc/iq/mucadmin.rb0000644000175000017500000000075613647121573021534 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/x' module Jabber module MUC class IqQueryMUCAdmin < IqQuery name_xmlns 'query', 'http://jabber.org/protocol/muc#admin' include XParent def items r = [] each_element('item') { |item| r << item if item.kind_of? IqQueryMUCAdminItem } r end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/iq/mucowner.rb0000644000175000017500000000052113647121573021564 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/x' module Jabber module MUC class IqQueryMUCOwner < IqQuery name_xmlns 'query', 'http://jabber.org/protocol/muc#owner' include XParent end end end xmpp4r-0.5.6/lib/xmpp4r/muc/iq/mucadminitem.rb0000644000175000017500000000075713647121573022414 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/muc/item' module Jabber module MUC class IqQueryMUCAdminItem < MUC::UserItem name_xmlns 'item', 'http://jabber.org/protocol/muc#admin' def initialize(affiliation=nil, role=nil, jid=nil) super() set_affiliation(affiliation) set_role(role) set_jid(jid) end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/helper/0000755000175000017500000000000013647121573020250 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/muc/helper/mucbrowser.rb0000644000175000017500000000523613647121573022773 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/discovery' module Jabber module MUC ## # The MUCBrowser helper can be used to discover # Multi-User-Chat components via Service Discovery # # See JEP 0045 sections 6.1. and 6.2. # # Usage of its functions should be threaded as # responses can take a while class MUCBrowser ## # Initialize a new MUCBrowser helper def initialize(stream) @stream = stream end ## # Retrieve the name of a MUC component, # depending upon whether the target entity supports # the MUC protocol. # # A return-value of nil does *not* mean that the entity # does not exist or does not support Service Discovery! # nil just means that this is not a MUC-compliant service. # # Throws an ServerError when receiving # # jid:: [JID] Target entity (set only domain!) # return:: [String] or [nil] def muc_name(jid) iq = Iq.new(:get, jid) iq.from = @stream.jid # Enable components to use this iq.add(Discovery::IqQueryDiscoInfo.new) res = nil @stream.send_with_id(iq) do |answer| if answer.type == :result answer.query.each_element('feature') { |feature| # Look if the component has a MUC or Groupchat feature if feature.var == 'http://jabber.org/protocol/muc' or feature.var == 'gc-1.0' # If so, get the identity if answer.query.first_element('identity') res = answer.query.first_element('identity').iname end end } true else false end end res end ## # Retrieve the existing rooms of a MUC component # # The resulting Hash contains pairs of room JID and room name # # Usage: # my_mucbrowse_helper.muc_rooms('conference.jabber.org').each { |jid,name| ... } # # Throws an exception when receiving # jid:: [JID] Target entity (set only domain!) # return:: [Hash] def muc_rooms(jid) iq = Iq.new(:get, jid) iq.from = @stream.jid # Enable components to use this iq.add(Discovery::IqQueryDiscoItems.new) rooms = {} @stream.send_with_id(iq) do |answer| answer.query.each_element('item') { |item| rooms[item.jid] = item.iname } end rooms end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/helper/simplemucclient.rb0000644000175000017500000002271513647121573024001 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/delay/x/delay' require 'xmpp4r/muc/helper/mucclient' require 'xmpp4r/muc/iq/mucadminitem' module Jabber module MUC ## # This class attempts to implement a lot of complexity of the # Multi-User Chat protocol. If you want to implement JEP0045 # yourself, use Jabber::MUC::MUCClient for some minor # abstraction. # # Minor flexibility penalty: the on_* callbacks are no # CallbackLists and may therefore only used once. A second # invocation will overwrite the previous set up block. # # *Hint:* the parameter time may be nil if the server didn't # send it. # # Example usage: # my_muc = Jabber::MUC::SimpleMUCClient.new(my_client) # my_muc.on_message { |time,nick,text| # puts (time || Time.new).strftime('%I:%M') + " <#{nick}> #{text}" # } # my_muc.join(Jabber::JID.new('jdev@conference.jabber.org/XMPP4R-Bot')) # # Please take a look at Jabber::MUC::MUCClient for # derived methods, such as MUCClient#join, MUCClient#exit, # ... class SimpleMUCClient < MUCClient ## # Initialize a SimpleMUCClient # stream:: [Stream] to operate on # jid:: [JID] room@component/nick # password:: [String] Optional password def initialize(stream) super @room_message_block = nil @message_block = nil @private_message_block = nil @subject_block = nil @subject = nil @join_block = nil add_join_callback(999) { |pres| # Presence time time = nil pres.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } # Invoke... @join_block.call(time, pres.from.resource) if @join_block false } @leave_block = nil @self_leave_block = nil add_leave_callback(999) { |pres| # Presence time time = nil pres.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } # Invoke... if pres.from == jid @self_leave_block.call(time) if @self_leave_block else @leave_block.call(time, pres.from.resource) if @leave_block end false } end private def handle_message(msg) super # Message time (e.g. history) time = nil msg.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } sender_nick = msg.from.resource if msg.subject @subject = msg.subject @subject_block.call(time, sender_nick, @subject) if @subject_block end if msg.body if sender_nick.nil? @room_message_block.call(time, msg.body) if @room_message_block else if msg.type == :chat @private_message_block.call(time, msg.from.resource, msg.body) if @private_message_block elsif msg.type == :groupchat @message_block.call(time, msg.from.resource, msg.body) if @message_block else # ...? end end end end public ## # Room subject/topic # result:: [String] The subject def subject @subject end ## # Change the room's subject/topic # # This will not be reflected by SimpleMUCClient#subject # immediately, wait for SimpleMUCClient#on_subject # s:: [String] New subject def subject=(s) msg = Message.new msg.subject = s send(msg) end ## # Send a simple text message # text:: [String] Message body # to:: [String] Optional nick if directed to specific user def say(text, to=nil) send(Message.new(nil, text), to) end ## # Request the MUC to invite users to this room # # Sample usage: # my_muc.invite( {'wiccarocks@shakespeare.lit/laptop' => 'This coven needs both wiccarocks and hag66.', # 'hag66@shakespeare.lit' => 'This coven needs both hag66 and wiccarocks.'} ) # recipients:: [Hash] of [JID] => [String] Reason def invite(recipients) msg = Message.new x = msg.add(XMUCUser.new) recipients.each { |jid,reason| x.add(XMUCUserInvite.new(jid, reason)) } send(msg) end ## # Administratively remove one or more users from the room. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.kick 'pistol', 'Avaunt, you cullion!' # my_muc.kick(['Bill', 'Linus'], 'Stop flaming') # # recipients:: [Array] of, or single [String]: Nicks # reason:: [String] Kick reason def kick(recipients, reason) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.nick = recipient item.role = :none item.reason = reason item } send_affiliations(items) end ## # Administratively ban one or more user jids from the room. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.ban 'pistol@foobar.com', 'Avaunt, you cullion!' # # recipients:: [Array] of, or single [String]: JIDs # reason:: [String] Ban reason def ban(recipients, reason) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.jid = recipient item.affiliation = :outcast item.reason = reason item } send_affiliations(items) end ## # Unban one or more user jids for the room. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.unban 'pistol@foobar.com' # # recipients:: [Array] of, or single [String]: JIDs def unban(recipients) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.jid = recipient item.affiliation = :none item } send_affiliations(items) end ## # Promote one or more users in the room to moderator. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.promote 'pistol' # # recipients:: [Array] of, or single [String]: Nicks def promote(recipients) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.nick = recipient item.role = :moderator item } send_affiliations(items) end ## # Demote one or more users in the room to participant. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.demote 'pistol' # # recipients:: [Array] of, or single [String]: Nicks def demote(recipients) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.nick = recipient item.role = :participant item } send_affiliations(items) end ## # Block to be invoked when a message *from* the room arrives # # Example: # Astro has joined this session # block:: Takes two arguments: time, text def on_room_message(&block) @room_message_block = block end ## # Block to be invoked when a message from a participant to # the whole room arrives # block:: Takes three arguments: time, sender nickname, text def on_message(&block) @message_block = block end ## # Block to be invoked when a private message from a participant # to you arrives. # block:: Takes three arguments: time, sender nickname, text def on_private_message(&block) @private_message_block = block end ## # Block to be invoked when somebody sets a new room subject # block:: Takes three arguments: time, nickname, new subject def on_subject(&block) @subject_block = block end ## # Block to be called when somebody enters the room # # If there is a non-nil time passed to the block, chances # are great that this is initial presence from a participant # after you have joined the room. # block:: Takes two arguments: time, nickname def on_join(&block) @join_block = block end ## # Block to be called when somebody leaves the room # block:: Takes two arguments: time, nickname def on_leave(&block) @leave_block = block end ## # Block to be called when *you* leave the room # # Deactivation occurs *afterwards*. # block:: Takes one argument: time def on_self_leave(&block) @self_leave_block = block end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/helper/mucclient.rb0000644000175000017500000003324513647121573022567 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/muc/x/muc' require 'xmpp4r/muc/iq/mucowner' require 'xmpp4r/muc/iq/mucadmin' require 'xmpp4r/dataforms' module Jabber module MUC ## # The MUCClient Helper handles low-level stuff of the # Multi-User Chat (JEP 0045). # # Use one instance per room. # # Note that one client cannot join a single room multiple # times. At least the clients' resources must be different. # This is a protocol design issue. But don't consider it as # a bug, it is just a clone-preventing feature. class MUCClient ## # Sender JID, set this to use MUCClient from Components # my_jid:: [JID] Defaults to nil attr_accessor :my_jid ## # MUC room roster # roster:: [Hash] of [String] Nick => [Presence] attr_reader :roster ## # MUC JID # jid:: [JID] room@component/nick attr_reader :jid ## # Initialize a MUCClient # # Call MUCClient#join *after* you have registered your # callbacks to avoid reception of stanzas after joining # and before registration of callbacks. # stream:: [Stream] to operate on def initialize(stream) # Attributes initialization @stream = stream @my_jid = nil @jid = nil @roster = {} @roster_lock = Mutex.new @active = false @join_cbs = CallbackList.new @leave_cbs = CallbackList.new @presence_cbs = CallbackList.new @message_cbs = CallbackList.new @private_message_cbs = CallbackList.new end ## # Join a room # # This registers its own callbacks on the stream # provided to initialize and sends initial presence # to the room. May throw ServerError if joining # fails. # jid:: [JID] room@component/nick # password:: [String] Optional password # opts:: [Hash] If the parameter :history => false is passed then you will receive no history messages after initial presence # return:: [MUCClient] self (chain-able) def join(jid, password=nil, opts={}) if active? raise "MUCClient already active" end @jid = (jid.kind_of?(JID) ? jid : JID.new(jid)) activate # Joining pres = Presence.new pres.to = @jid pres.from = @my_jid xmuc = XMUC.new xmuc.password = password # Add history/maxstanzas as requested. false=0 xmuc.add_element REXML::Element.new('history').tap { |hist| hist.add_attribute 'maxstanzas', (opts[:history]||0).to_i.to_s } unless opts[:history].nil? pres.add(xmuc) # We don't use Stream#send_with_id here as it's unknown # if the MUC component *always* uses our stanza id. error = nil @stream.send(pres) { |r| if from_room?(r.from) and r.kind_of?(Presence) and r.type == :error # Error from room error = r.error true # type='unavailable' may occur when the MUC kills our previous instance, # but all join-failures should be type='error' elsif r.from == jid and r.kind_of?(Presence) and r.type != :unavailable # Our own presence reflected back - success if r.x(XMUCUser) and (i = r.x(XMUCUser).items.first) @affiliation = i.affiliation # we're interested in if it's :owner @role = i.role # :moderator ? end handle_presence(r, false) true else # Everything else false end } if error deactivate raise ServerError.new(error) end self end ## # Exit the room # # * Sends presence with type='unavailable' with an optional # reason in , # * then waits for a reply from the MUC component (will be # processed by leave-callbacks), # * then deletes callbacks from the stream. # reason:: [String] Optional custom exit message def exit(reason=nil) unless active? raise "MUCClient hasn't yet joined" end pres = Presence.new pres.type = :unavailable pres.to = jid pres.from = @my_jid pres.status = reason if reason @stream.send(pres) { |r| Jabber::debuglog "exit: #{r.to_s.inspect}" if r.kind_of?(Presence) and r.type == :unavailable and r.from == jid @leave_cbs.process(r) true else false end } deactivate self end ## # Is the MUC client active? # # This is false after initialization, # true after joining and # false after exit/kick def active? @active end ## # The MUCClient's own nick # (= resource) # result:: [String] Nickname def nick @jid ? @jid.resource : nil end ## # Change nick # # Threading is, again, suggested. This method waits for two # stanzas, one indicating unavailabilty of the old # transient JID, one indicating availability of the new # transient JID. # # If the service denies nick-change, ServerError will be raised. def nick=(new_nick) unless active? raise "MUCClient not active" end new_jid = JID.new(@jid.node, @jid.domain, new_nick) # Joining pres = Presence.new pres.to = new_jid pres.from = @my_jid error = nil # Keeping track of the two stanzas enables us to process stanzas # which don't arrive in the order specified by JEP-0045 presence_unavailable = false presence_available = false # We don't use Stream#send_with_id here as it's unknown # if the MUC component *always* uses our stanza id. @stream.send(pres) { |r| if from_room?(r.from) and r.kind_of?(Presence) and r.type == :error # Error from room error = r.error elsif r.from == @jid and r.kind_of?(Presence) and r.type == :unavailable and r.x and r.x.kind_of?(XMUCUser) and r.x.status_code == 303 # Old JID is offline, but wait for the new JID and let stanza be handled # by the standard callback presence_unavailable = true handle_presence(r) elsif r.from == new_jid and r.kind_of?(Presence) and r.type != :unavailable # Our own presence reflected back - success presence_available = true handle_presence(r) end if error or (presence_available and presence_unavailable) true else false end } if error raise ServerError.new(error) end # Apply new JID @jid = new_jid end ## # The room name # (= node) # result:: [String] Room name def room @jid ? @jid.node : nil end ## # Send a stanza to the room # # If stanza is a Jabber::Message, stanza.type will be # automatically set to :groupchat if directed to room or :chat # if directed to participant. # stanza:: [XMPPStanza] to send # to:: [String] Stanza destination recipient, or room if +nil+ def send(stanza, to=nil) if stanza.kind_of? Message stanza.type = to ? :chat : :groupchat end stanza.from = @my_jid stanza.to = JID.new(jid.node, jid.domain, to) @stream.send(stanza) end ## # Add a callback for stanzas indicating availability # of a MUC participant # # This callback will *not* be called for initial presences when # a client joins a room, but only for the presences afterwards. # # The callback will be called from MUCClient#handle_presence with # one argument: the stanza. # Note that this stanza will have been already inserted into # MUCClient#roster. def add_join_callback(prio = 0, ref = nil, &block) @join_cbs.add(prio, ref, block) end ## # Add a callback for stanzas indicating unavailability # of a MUC participant # # The callback will be called with one argument: the stanza. # # Note that this is called just *before* the stanza is removed from # MUCClient#roster, so it is still possible to see the last presence # in the given block. # # If the presence's origin is your MUC JID, the MUCClient will be # deactivated *afterwards*. def add_leave_callback(prio = 0, ref = nil, &block) @leave_cbs.add(prio, ref, block) end ## # Add a callback for a stanza which is neither a join # nor a leave. This will be called when a room participant simply # changes his status. def add_presence_callback(prio = 0, ref = nil, &block) @presence_cbs.add(prio, ref, block) end ## # Add a callback for stanza directed to the whole room. # # See MUCClient#add_private_message_callback for private messages # between MUC participants. def add_message_callback(prio = 0, ref = nil, &block) @message_cbs.add(prio, ref, block) end ## # Add a callback for stanza with type='chat'. # # These stanza are normally not broadcasted to all room occupants # but are some sort of private messaging. def add_private_message_callback(prio = 0, ref = nil, &block) @private_message_cbs.add(prio, ref, block) end ## # Does this JID belong to that room? # jid:: [JID] # result:: [true] or [false] def from_room?(jid) @jid.strip == jid.strip end private ## # call_join_cbs:: [Bool] Do not call them if we receive initial presences from room def handle_presence(pres, call_join_cbs=true) # :nodoc: if pres.type == :unavailable or pres.type == :error @leave_cbs.process(pres) @roster_lock.synchronize { @roster.delete(pres.from.resource) } if pres.from == jid and !(pres.x and pres.x.kind_of?(XMUCUser) and pres.x.status_code == 303) deactivate end else is_join = ! @roster.has_key?(pres.from.resource) @roster_lock.synchronize { @roster[pres.from.resource] = pres } if is_join @join_cbs.process(pres) if call_join_cbs else @presence_cbs.process(pres) end end end def handle_message(msg) # :nodoc: if msg.type == :chat @private_message_cbs.process(msg) else # type == :groupchat or anything else @message_cbs.process(msg) end end def activate # :nodoc: @active = true # Callbacks @stream.add_presence_callback(150, self) { |presence| if from_room?(presence.from) handle_presence(presence) true else false end } @stream.add_message_callback(150, self) { |message| if from_room?(message.from) handle_message(message) true else false end } end def deactivate # :nodoc: @active = false @jid = nil # Callbacks @stream.delete_presence_callback(self) @stream.delete_message_callback(self) end public def owner? @affiliation == :owner end ## # Use this method to configure a MUC room of which you are the owner. # # options:: [Hash] where keys are the features of the room you wish # to configure. See http://www.xmpp.org/extensions/xep-0045.html#registrar-formtype-owner def configure(options={}) get_room_configuration submit_room_configuration(options) end def get_room_configuration raise 'You are not the owner' unless owner? iq = Iq.new(:get, jid.strip) iq.from = my_jid iq.add(IqQueryMUCOwner.new) fields = [] @stream.send_with_id(iq) do |answer| raise "Configuration not possible for this room" unless answer.query && answer.query.x(Dataforms::XData) answer.query.x(Dataforms::XData).fields.each do |field| if (var = field.attributes['var']) fields << var end end end fields end def submit_room_configuration(options) # fill out the reply form iq = Iq.new(:set, jid.strip) iq.from = my_jid query = IqQueryMUCOwner.new form = Dataforms::XData.new form.type = :submit options.each do |var, values| field = Dataforms::XDataField.new values = [values] unless values.is_a?(Array) field.var, field.values = var, values form.add(field) end query.add(form) iq.add(query) @stream.send_with_id(iq) end ## # Push a list of new affiliations to the room # items:: [Array] of, or single [IqQueryMUCAdminItem] def send_affiliations(items) iq = Iq.new(:set, jid.strip) iq.from = my_jid iq.add(IqQueryMUCAdmin.new) items = [items] unless items.kind_of? Array items.each { |item| iq.query.add(item) } @stream.send_with_id(iq) end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/item.rb0000644000175000017500000000620413647121573020256 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module MUC ## # Don't use this. It is the base class (unifying shared # attributes) of XMUCUserItem and IqQueryMUCAdminItem class UserItem < XMPPElement def affiliation case attributes['affiliation'] when 'admin' then :admin when 'member' then :member when 'none' then :none when 'outcast' then :outcast when 'owner' then :owner else nil end end def affiliation=(v) case v when :admin then attributes['affiliation'] = 'admin' when :member then attributes['affiliation'] = 'member' when :none then attributes['affiliation'] = 'none' when :outcast then attributes['affiliation'] = 'outcast' when :owner then attributes['affiliation'] = 'owner' else attributes['affiliation'] = nil end end def set_affiliation(v) self.affiliation = v self end def jid attributes['jid'].nil? ? nil : JID.new(attributes['jid']) end def jid=(j) attributes['jid'] = j.nil? ? nil : j.to_s end def set_jid(j) self.jid = j self end def nick attributes['nick'] end def nick=(n) attributes['nick'] = n end def set_nick(n) self.nick = n self end def role case attributes['role'] when 'moderator' then :moderator when 'none' then :none when 'participant' then :participant when 'visitor' then :visitor else nil end end def role=(r) case r when :moderator then attributes['role'] = 'moderator' when :none then attributes['role'] = 'none' when :participant then attributes['role'] = 'participant' when :visitor then attributes['role'] = 'visitor' else attributes['role'] = nil end end def set_role(r) self.role = r self end def reason text = nil each_element('reason') { |xe| text = xe.text } text end def reason=(s) delete_elements('reasion') add_element('reason').text = s end def set_reason(s) self.reason = s self end def continue c = nil each_element('continue') { |xe| c = xe } c.nil? end def continue=(c) delete_elements('continue') add_element('continue') if c end def set_continue(c) self.continue = c self end def actors a = [] each_element('actor') { |xe| a.push(JID.new(xe.attributes['jid'])) } a end def actors=(a) delete_elements('actor') a.each { |jid| e = add_element('actor') e.attributes['jid'] = jid.to_s } end def set_actors(a) self.actors = a self end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/x/0000755000175000017500000000000013647121573017240 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/muc/x/muc.rb0000644000175000017500000000330113647121573020346 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/muc/x/mucuseritem' require 'xmpp4r/muc/x/mucuserinvite' module Jabber module MUC ## # Class for elements # with namespace http://jabber.org/protocol/muc # # See JEP-0045 for details class XMUC < X name_xmlns 'x', 'http://jabber.org/protocol/muc' ## # Text content of the element def password first_element_text('password') end ## # Set the password for joining a room # (text content of the element) def password=(s) if s replace_element_text('password', s) else delete_elements('password') end end end ## # Class for elements # with namespace http://jabber.org/protocol/muc#user # # See JEP-0058 for details class XMUCUser < X name_xmlns 'x', 'http://jabber.org/protocol/muc#user' ## # Retrieve the three-digit code in # # result:: [Fixnum] or nil def status_code e = nil each_element('status') { |xe| e = xe } if e and e.attributes['code'].size == 3 and e.attributes['code'].to_i != 0 e.attributes['code'].to_i else nil end end ## # Get all elements # result:: [Array] of [XMUCUserItem] def items res = [] each_element('item') { |item| res << item } res end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/x/mucuserinvite.rb0000644000175000017500000000222413647121573022467 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber module MUC class XMUCUserInvite < XMPPElement name_xmlns 'invite', 'http://jabber.org/protocol/muc#user' def initialize(to=nil, reason=nil) super() set_to(to) set_reason(reason) end def to attributes['to'].nil? ? nil : JID.new(attributes['to']) end def to=(j) attributes['to'] = j.nil? ? nil : j.to_s end def set_to(j) self.to = j self end def from attributes['from'].nil? ? nil : JID.new(attributes['from']) end def from=(j) attributes['from'] = (j.nil? ? nil : j.to_s) end def set_from(j) self.from = j self end def reason first_element_text('reason') end def reason=(s) if s replace_element_text('reason', s) else delete_elements('reason') end end def set_reason(s) self.reason = s self end end end end xmpp4r-0.5.6/lib/xmpp4r/muc/x/mucuseritem.rb0000644000175000017500000000141413647121573022127 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/muc/item' module Jabber module MUC class XMUCUserItem < MUC::UserItem name_xmlns 'item', 'http://jabber.org/protocol/muc#user' def initialize(affiliation=nil, role=nil, jid=nil) super() set_affiliation(affiliation) set_role(role) set_jid(jid) end def continue c = nil each_element('continue') { |xe| c = xe } c.nil? end def continue=(c) delete_elements('continue') add_element('continue') if c end def set_continue(c) self.continue = c self end end end end xmpp4r-0.5.6/lib/xmpp4r/rpc/0000755000175000017500000000000013647121573016771 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/rpc/iq/0000755000175000017500000000000013647121573017402 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/rpc/iq/rpc.rb0000644000175000017500000000100613647121573020510 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module RPC class IqQueryRPC < IqQuery NS_RPC = 'jabber:iq:rpc' name_xmlns 'query', NS_RPC # TODO: Is typed_add with a String right here? def typed_add(e) if e.kind_of? String typed_add(REXML::Document.new(e).root) else super end end end end end xmpp4r-0.5.6/lib/xmpp4r/rpc/helper/0000755000175000017500000000000013647121573020250 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/rpc/helper/client.rb0000644000175000017500000000530613647121573022057 0ustar debbiecocoadebbiecocoarequire 'xmlrpc/server' require 'xmlrpc/parser' require 'xmlrpc/create' require 'xmlrpc/config' require 'xmlrpc/utils' # ParserWriterChooseMixin require 'xmpp4r/dataforms/x/data' require 'xmpp4r/rpc/iq/rpc' require 'xmpp4r/rpc/helper/xmlrpcaddons' module Jabber module RPC ## # XMLRPC Client class Client include XMLRPC::ParserWriterChooseMixin include XMLRPC::ParseContentType attr_accessor :my_jid ## # xmppstream # stream:: [Stream] # jid where you want to send the rpc requests to # jid:: [JID] rpcserver@jabberserver/ressource def initialize(stream,jid) @jid = JID.new(jid) @my_jid = stream.jid @stream = stream @parser = nil @create = XMLRPC::Create.new end ## # automatically trys to find a method # thanx to eric cestari :) def method_missing(methodname, *args) send("call", methodname,*args) end def call(method, *args) ok, param = call2(method, *args) if ok param else raise param end end def call2(method, *args) request = @create.methodCall(method, *args) data = do_rpc(request) parser().parseMethodResponse(data) end ## # adds multi rpcalls to the query # methods:: [Array] def multicall(*methods) ok, params = multicall2(*methods) if ok params else raise params end end ## # generate a multicall # methods:: [Array] def multicall2(*methods) gen_multicall(methods) end def do_rpc(xmlrpc) iq = Iq.new(:set, @jid) iq.from = @my_jid iq.id = IdGenerator::generate_id rpcquery = iq.add(IqQueryRPC.new) rpcquery.typed_add(xmlrpc) result = nil @stream.send_with_id(iq) do |iqreply| if iqreply.query.kind_of?(IqQueryRPC) result = iqreply.query.to_s end end result end private def gen_multicall(methods=[]) ok, params = call2("system.multicall", methods.collect { |m| { 'methodName' => m[0], 'params' => m[1..-1] } } ) if ok params = params.collect{ |param| if param.is_a? Array param[0] elsif param.is_a? Hash XMLRPC::FaultException.new(param["faultCode"], param["faultString"]) else raise "Wrong multicall return value" end } end return ok, params end end end # Helpers end # Jabber xmpp4r-0.5.6/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb0000644000175000017500000000356413647121573023303 0ustar debbiecocoadebbiecocoarequire "xmlrpc/parser" require "xmlrpc/create" require "xmlrpc/config" require "xmlrpc/utils" # ParserWriterChooseMixin module XMLRPC class Create # Avoids warnings remove_method(:methodCall) remove_method(:methodResponse) ## # create a Method Call # name:: [String] name of the method # params:: [Array] params of the method as a array def methodCall(name, *params) name = name.to_s if name !~ /[a-zA-Z0-9_.:\/]+/ raise ArgumentError, "Wrong XML-RPC method-name" end parameter = params.collect { |param| @writer.ele("param", conv2value(param)) } tree = @writer.document( @writer.ele("methodCall", @writer.tag("methodName", name), @writer.ele("params", *parameter) ) ) @writer.document_to_str(tree) + "\n" end ## # create a response to a method call # is_ret:: [TrueClass] is this a return (true) or a error (false) # params:: [Array] a array of params def methodResponse(is_ret, *params) if is_ret begin resp = params.collect do |param| @writer.ele("param", conv2value(param)) end resp = [@writer.ele("params", *resp)] rescue Exception => e error = XMLRPC::FaultException.new(XMLRPC::BasicServer::ERR_UNCAUGHT_EXCEPTION, "Uncaught exception '#{e.message}' serialising params into response") resp = @writer.ele("fault", conv2value(error.to_h)) end else if params.size != 1 or params[0] === XMLRPC::FaultException raise ArgumentError, "no valid fault-structure given" end resp = @writer.ele("fault", conv2value(params[0].to_h)) end tree = @writer.document(@writer.ele("methodResponse", resp)) @writer.document_to_str(tree) + "\n" end end end xmpp4r-0.5.6/lib/xmpp4r/rpc/helper/server.rb0000644000175000017500000000333313647121573022105 0ustar debbiecocoadebbiecocoarequire 'xmlrpc/server' require 'xmlrpc/parser' require 'xmlrpc/create' require 'xmlrpc/config' require 'xmlrpc/utils' # ParserWriterChooseMixin require 'xmpp4r/dataforms/x/data' require 'xmpp4r/rpc/iq/rpc' require 'xmpp4r/rpc/helper/xmlrpcaddons' module Jabber module RPC ## # XMLRPC Server class Server < XMLRPC::BasicServer include XMLRPC::ParserWriterChooseMixin include XMLRPC::ParseContentType ## # new - creates a new server # def initialize(stream,class_delim=".") super(class_delim) @stream = stream @stream.add_iq_callback(120,"Helpers::RPCServer") { |iq| if iq.type == :set and iq.type != :result handle_iq(iq) true else false end } end ## # handles incoming iqs # iq:: [Jabber::IQ] - the jabber iq def handle_iq(iq) if iq.type == :set if iq.query.kind_of?(IqQueryRPC) data = iq.query response = IqQueryRPC.new data.elements.each { |rpc| if rpc response.typed_add(handle_rpc_requests(rpc)) end } respiq = iq.answer(false) respiq.type = :result respiq.add(response) @stream.send(respiq) end end end private ## # handles the rpc requests # takes rpcdata:: [String] def handle_rpc_requests(rpcdata) resp = process(rpcdata.to_s) if resp == nil or resp.size <= 0 raise Jabber::ErrorResponse.new(:forbidden) else return resp end end end # RPCServer end # Helpers end # Jabber xmpp4r-0.5.6/lib/xmpp4r/discovery.rb0000644000175000017500000000047213647121573020544 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/discovery/iq/discoinfo' require 'xmpp4r/discovery/iq/discoitems' require 'xmpp4r/discovery/helper/helper' require 'xmpp4r/discovery/helper/responder' xmpp4r-0.5.6/lib/xmpp4r/version/0000755000175000017500000000000013647121573017672 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/version/iq/0000755000175000017500000000000013647121573020303 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/version/iq/version.rb0000755000175000017500000000477013647121573022330 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/query' module Jabber module Version ## # Class for handling queries for 'Software Version' # (JEP 0092) # # Notice that according to JEP 0092 only the element can be omitted, # (iname) and must be present class IqQueryVersion < IqQuery name_xmlns 'query', 'jabber:iq:version' ## # Create a new element def initialize(iname=nil, version=nil, os=nil) super() set_iname(iname) if iname set_version(version) if version set_os(os) if os end ## # Get the name of the software # # This has been renamed to 'iname' here to keep # REXML::Element#name accessible def iname first_element_text('name') end ## # Set the name of the software # # The element won't be deleted if text is nil as # it must occur in a version query, but its text will # be empty. def iname=(text) replace_element_text('name', text.nil? ? '' : text) end ## # Set the name of the software (chaining-friendly) # result:: [String] or nil def set_iname(text) self.iname = text self end ## # Get the version of the software # result:: [String] or nil def version first_element_text('version') end ## # Set the version of the software # # The element won't be deleted if text is nil as # it must occur in a version query def version=(text) replace_element_text('version', text.nil? ? '' : text) end ## # Set the version of the software (chaining-friendly) # text:: [String] def set_version(text) self.version = text self end ## # Get the operating system or nil # (os is not mandatory for Version Query) def os first_element_text('os') end ## # Set the os of the software # text:: [String] or nil def os=(text) if text replace_element_text('os', text) else delete_elements('os') end end ## # Set the os of the software (chaining-friendly) # text:: [String] or nil def set_os(text) self.os = text self end end end end xmpp4r-0.5.6/lib/xmpp4r/version/helper/0000755000175000017500000000000013647121573021151 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/version/helper/simpleresponder.rb0000644000175000017500000000237313647121573024716 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' require 'xmpp4r/version/helper/responder' module Jabber module Version ## # A class to answer version requests using IqQueryVersion # # This is simplification as one doesn't need dynamic # version answering normally. # # Example usage: # Jabber::Version::SimpleResponder.new(my_client, "My cool XMPP4R script", "1.0", "Younicks") class SimpleResponder < Responder attr_accessor :name attr_accessor :version attr_accessor :os ## # Initialize a new version responder # # Registers it's callback (prio = 180, ref = self) # stream:: [Stream] Where to register callback handlers # name:: [String] Software name for answers # version:: [String] Software versio for answers # os:: [String] Optional operating system name for answers def initialize(stream, name, version, os=nil) super stream @name = name @version = version @os = os add_version_callback(180, self) { |iq,block| block.call(@name, @version, @os) } end end end end xmpp4r-0.5.6/lib/xmpp4r/version/helper/responder.rb0000644000175000017500000000403513647121573023501 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/callbacks' require 'xmpp4r/version/iq/version' module Jabber module Version ## # A class to answer version requests using IqQueryVersion # # If you don't need the flexibility of dynamic responses with # the callback you can register with add_version_callback, # take a look at SimpleResponder class Responder ## # Initialize a new version responder # # Registers it's callback (prio = 180, ref = self) # stream:: [Stream] Where to register callback handlers def initialize(stream) @stream = stream @versioncbs = CallbackList.new stream.add_iq_callback(180, self) { |iq| iq_callback(iq) } end ## # Add a callback for Iq stanzas with IqQueryVersion # # First argument passed to block is the Iq stanza, # second argument is a block, which can be called with # software name, version and os # # Example: # my_version_helper.add_version_callback { |iq,block| # block.call('Cool client', '6.0', 'Cool OS') # } def add_version_callback(priority = 0, ref = nil, &block) @versioncbs.add(priority, ref, block) end ## # callback handler to answer Software Version queries # (registered by constructor and used internally only) # # Used internally def iq_callback(iq) if iq.type == :get if iq.query.kind_of?(IqQueryVersion) replyblock = lambda { |name,version,os| answer = iq.answer answer.type = :result answer.query.set_iname(name).set_version(version).set_os(os) @stream.send(answer) true } @versioncbs.process(iq, replyblock) else false end else false end end end end end xmpp4r-0.5.6/lib/xmpp4r/delay.rb0000644000175000017500000000026613647121573017634 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/delay/x/delay.rb' xmpp4r-0.5.6/lib/xmpp4r/query.rb0000644000175000017500000000053113647121573017676 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' module Jabber ## # A class used to build/parse IQ Query requests/responses # class IqQuery < XMPPElement name_xmlns 'query' force_xmlns true end end xmpp4r-0.5.6/lib/xmpp4r/debuglog.rb0000644000175000017500000000327713647121573020333 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'logger' module Jabber def Jabber::logger @@logger ||= Logger.new($stderr) end # Set the logger to use for debug and warn (if enabled) def Jabber::logger=(logger) @@logger = logger end # Is debugging mode enabled ? @@debug = false # Is warnings mode enabled ? @@warnings = false # Enable/disable debugging mode. When debug mode is enabled, information # can be logged using Jabber::debuglog. When debug mode is disabled, calls # to Jabber::debuglog are just ignored. def Jabber::debug=(debug) @@debug = debug if @@debug debuglog('Debugging mode enabled.') #if debug is enabled, we should automatically enable warnings too Jabber::warnings = true end end # Enable/disable warnings mode. def Jabber::warnings=(warnings) @@warnings = warnings if @@warnings warnlog('Warnings mode enabled.') end end # returns true if debugging mode is enabled. If you just want to log # something if debugging is enabled, use Jabber::debuglog instead. def Jabber::debug @@debug end # Outputs a string only if debugging mode is enabled. If the string includes # several lines, 4 spaces are added at the beginning of each line but the # first one. Time is prepended to the string. def Jabber::debuglog(string) return if not @@debug logger.debug string.chomp.gsub("\n", "\n ") end # Outputs a string only if warnings mode is enabled. def Jabber::warnlog(string) return if not @@warnings logger.warn string.chomp.gsub("\n", "\n ") end end xmpp4r-0.5.6/lib/xmpp4r/xhtml.rb0000644000175000017500000000003413647121573017663 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/xhtml/html' xmpp4r-0.5.6/lib/xmpp4r/feature_negotiation.rb0000644000175000017500000000030713647121573022565 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/feature_negotiation/iq/feature.rb' xmpp4r-0.5.6/lib/xmpp4r/callbacks.rb0000644000175000017500000001012613647121573020451 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber ## # This class manages a list of callbacks. # # ==Callbacks management and priority # # Callbacks are managed by the class CallbackList. When they are added, a # priority (just a number or anything Comparable with other priorities) is # specified. The biggest the priority is, the earliest the callback will be # considered. # # Callbacks are processed for a given set of objects as long as they return # false. If you want to stop processing, you must return true. Example : # cbl = CallbackList.new # c1 = false # c2 = false # c3 = false # cbl.add(10) { c1 = true; 1 } # cbl.add(5) { c2 = true; true } # cbl.add(0) { c3 = true } # cbl.process('aa') # puts "#{c1} #{c2} #{c3}" # This example would display "true true false" as callbacks processing was # stopped after the second callback returned true. # # In XMPP4R, callbacks' priorities are quite normalized since we want to be # able to "cascade" callbacks in a clean way. Here are values your code should # take into account : # # >= 200:: logging & debugging callbacks. Those callbacks should not consume # elements. # 100-199:: Where Helpers register their callbacks. The normal value is 100, # and Helpers shouldn't register something else unless there's a # very good reason to. # < 100:: all those numbers are normally available for your application. # That's enough, don't you think ? class CallbackList include Enumerable # Create a new list of callbacks def initialize @list = [] end ## # Add a callback to the list # # List will be sorted afterwards # # prio:: [Integer] the callback's priority, the higher, the sooner. # ref:: [String] the callback's reference # block:: [Block] a block to execute # return:: [Jabber::CallbackList] The list, for chaining def add(prio = 0, ref = nil, proc = nil, &block) block = proc if proc @list.push(Callback.new(prio, ref, block)) @list.sort! { |a, b| b.priority <=> a.priority } self end ## # Delete a callback by reference # ref:: [String] the reference of the callback to delete # return:: [CallBackList] The list, for chaining def delete(ref) @list.delete_if { |item| item.ref == ref } self end def each(&block) @list.each(&block) end ## # Number of elements in the list # return:: [Integer] The number of elements def length @list.length end ## # Process an element through all my callbacks. returns e.consumed? # e:: [Object] The elements to pass to the callback. You can pass # several, but of course, you block must know how to handle them. # return:: [Boolean] true if the element has been consumed def process(*e) # If somebody adds a new callback the list will get modified # and sorted(!) while still iterating through it. So we use a # local copy of @list. Any freshly added callback will receive # the next stanzas, not the current. list = @list.dup # process through callbacks list.each do |item| return true if item.block.call(*e) == true end false end end # This class is used to store callbacks inside CallbackList. See the # CallbackList class for more detailed explanations. class Callback # The Callback's priority attr_reader :priority # The Callback's reference, using for deleting purposes attr_reader :ref # The Callback's block to execute attr_reader :block ## # Create a new callback # priority:: [Integer] the callback's priority. The higher, the sooner it # will be executed # ref:: [String] The callback's reference def initialize(priority = 0, ref = nil, block = Proc.new {}) @priority = priority @ref = ref @block = block end def to_s "#<#{[self.class, priority, ref].compact * " "}>" end end end xmpp4r-0.5.6/lib/xmpp4r/last.rb0000644000175000017500000000010213647121573017466 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/last/iq/last' require 'xmpp4r/last/helper/helper' xmpp4r-0.5.6/lib/xmpp4r/dataforms/0000755000175000017500000000000013647121573020165 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/dataforms/x/0000755000175000017500000000000013647121573020434 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/dataforms/x/data.rb0000644000175000017500000001565613647121573021707 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/x' require 'xmpp4r/jid' module Jabber module Dataforms ## # Data Forms (JEP-0004) implementation class XData < X name_xmlns 'x', 'jabber:x:data' def initialize(type=nil) super() self.type = type end ## # Search a field by it's var-name # var:: [String] # result:: [XDataField] or [nil] def field(var) each_element { |xe| return xe if xe.kind_of?(XDataField) and xe.var == var } nil end def fields(including_hidden=false) fields = [] each_element do |xe| if xe.kind_of?(XDataField) and (including_hidden or (xe.type != :hidden and xe.type != :fixed)) fields << xe end end fields end ## # Type of this Data Form # result:: * :cancel # * :form # * :result # * :submit # * nil def type case attributes['type'] when 'cancel' then :cancel when 'form' then :form when 'result' then :result when 'submit' then :submit else nil end end ## # Set the type (see type) def type=(t) case t when :cancel then attributes['type'] = 'cancel' when :form then attributes['type'] = 'form' when :result then attributes['type'] = 'result' when :submit then attributes['type'] = 'submit' else attributes['type'] = nil end end ## # Get the Data Form title # return:: [XDataTitle] or nil def title first_element('title') end ## # Set the Data Form title # title:: [String] def title=(title) delete_elements('title') add_element(XDataTitle.new(title)) end ## # Get the Data Form instructions # return:: [Array] of [XDataInstructions] or nil def instructions fields = [] each_element('instructions') do |xe| fields << xe end fields end ## # Add Data Form instructions # i:: [String] def instructions=(i) add(XDataInstructions.new(i)) end end ## # Child of XData, contains the title of this Data Form class XDataTitle < XMPPElement name_xmlns 'title', 'jabber:x:data' def initialize(title=nil) super() add_text(title) end def to_s text.to_s end def title text end end ## # Child of XData, contains the instructions of this Data Form class XDataInstructions < XMPPElement name_xmlns 'instructions', 'jabber:x:data' def initialize(instructions=nil) super() add_text(instructions) end def to_s text.to_s end def instructions text end end ## # Child of XData, contains configurable/configured options of this Data Form class XDataField < XMPPElement name_xmlns 'field', 'jabber:x:data' def initialize(var=nil, type=nil) super() self.var = var self.type = type end def label attributes['label'] end def label=(s) attributes['label'] = s end def var attributes['var'] end def var=(s) attributes['var'] = s end ## # Type of this field # result:: # * :boolean # * :fixed # * :hidden # * :jid_multi # * :jid_single # * :list_multi # * :list_single # * :text_multi # * :text_private # * :text_single # * nil def type case attributes['type'] when 'boolean' then :boolean when 'fixed' then :fixed when 'hidden' then :hidden when 'jid-multi' then :jid_multi when 'jid-single' then :jid_single when 'list-multi' then :list_multi when 'list-single' then :list_single when 'text-multi' then :text_multi when 'text-private' then :text_private when 'text-single' then :text_single else nil end end ## # Set the type of this field (see type) def type=(t) case t when :boolean then attributes['type'] = 'boolean' when :fixed then attributes['type'] = 'fixed' when :hidden then attributes['type'] = 'hidden' when :jid_multi then attributes['type'] = 'jid-multi' when :jid_single then attributes['type'] = 'jid-single' when :list_multi then attributes['type'] = 'list-multi' when :list_single then attributes['type'] = 'list-single' when :text_multi then attributes['type'] = 'text-multi' when :text_private then attributes['type'] = 'text-private' when :text_single then attributes['type'] = 'text-single' else attributes['type'] = nil end end ## # Is this field required (has the child)? def required? res = false each_element('required') { res = true } res end ## # Set if this field is required # r:: [true] or [false] def required=(r) delete_elements('required') if r add REXML::Element.new('required') end end ## # Get the values (in a Data Form with type='submit') def values res = [] each_element('value') { |e| res << e.text } res end ## # Set the values def values=(ary) delete_elements('value') ary.each { |v| add(REXML::Element.new('value')).text = v } end ## # Get the first value def value values.first end ## # Remove all and set one value def value=(val) self.values = [val] end ## # Get the options (in a Data Form with type='form') def options res = {} each_element('option') { |e| value = nil e.each_element('value') { |ve| value = ve.text } res[value] = e.attributes['label'] } res end ## # Set the options def options=(hsh) delete_elements('option') hsh.each { |value,label| o = add(REXML::Element.new('option')) o.attributes['label'] = label o.add(REXML::Element.new('value')).text = value } end end ## # The element, can contain XDataField elements class XDataReported < XMPPElement name_xmlns 'reported', 'jabber:x:data' end end end xmpp4r-0.5.6/lib/xmpp4r/streamparser.rb0000644000175000017500000000573413647121573021253 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'rexml/parsers/sax2parser' require 'rexml/source' require 'xmpp4r/rexmladdons' module Jabber ## # The StreamParser uses REXML to parse the incoming XML stream # of the Jabber protocol and fires XMPPStanza at the Connection # instance. # class StreamParser # status if the parser is started attr_reader :started ## # Constructs a parser for the supplied stream (socket input) # # stream:: [IO] Socket input stream # listener:: [Object.receive(XMPPStanza)] The listener (usually a Jabber::Protocol::Connection instance) # def initialize(stream, listener) @stream = stream @listener = listener @current = nil end ## # Begins parsing the XML stream and does not return until # the stream closes. # def parse @started = false begin parser = REXML::Parsers::SAX2Parser.new @stream parser.listen( :start_element ) do |uri, localname, qname, attributes| e = REXML::Element.new(qname) if attributes.kind_of? Hash unnormalized_attributes = {} attributes.each_pair do |key, value| unnormalized_attributes[key] = REXML::Text::unnormalize(value) end elsif attributes.kind_of? Array unnormalized_attributes = [] attributes.each do |value| unnormalized_attributes << [value[0], REXML::Text::unnormalize(value[1])] end end e.add_attributes unnormalized_attributes @current = @current.nil? ? e : @current.add_element(e) # Handling not only when it is being # received as a top-level tag but also as a child of the # top-level element itself. This way, we handle stream # restarts (ie. after SASL authentication). if @current.name == 'stream' and @current.parent.nil? @started = true @listener.receive(@current) @current = nil end end parser.listen( :end_element ) do |uri, localname, qname| if qname == 'stream:stream' and @current.nil? @started = false @listener.parser_end else @listener.receive(@current) unless @current.parent @current = @current.parent end end parser.listen( :end_document ) do raise Jabber::ServerDisconnected, "Server Disconnected!" end parser.listen( :characters ) do | text | @current.add(REXML::Text.new(text.to_s, @current.whitespace, nil, true)) if @current end parser.listen( :cdata ) do | text | @current.add(REXML::CData.new(text)) if @current end parser.parse rescue REXML::ParseException => e @listener.parse_failure(e) end end end end xmpp4r-0.5.6/lib/xmpp4r/sasl.rb0000644000175000017500000001545013647121573017501 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'digest/md5' require 'xmpp4r/base64' module Jabber ## # Helpers for SASL authentication (RFC2222) # # You might not need to use them directly, they are # invoked by Jabber::Client#auth module SASL NS_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl' ## # Factory function to obtain a SASL helper for the specified mechanism def SASL.new(stream, mechanism) case mechanism when 'DIGEST-MD5' DigestMD5.new(stream) when 'PLAIN' Plain.new(stream) when 'ANONYMOUS' Anonymous.new(stream) else raise "Unknown SASL mechanism: #{mechanism}" end end ## # SASL mechanism base class (stub) class Base def initialize(stream) @stream = stream end private def generate_auth(mechanism, text=nil) auth = REXML::Element.new 'auth' auth.add_namespace NS_SASL auth.attributes['mechanism'] = mechanism auth.text = text auth end def generate_nonce Digest::MD5.hexdigest(Time.new.to_f.to_s) end end ## # SASL PLAIN authentication helper (RFC2595) class Plain < Base ## # Authenticate via sending password in clear-text def auth(password) auth_text = "#{@stream.jid.strip}\x00#{@stream.jid.node}\x00#{password}" error = nil @stream.send(generate_auth('PLAIN', Base64::encode64(auth_text).gsub(/\s/, ''))) { |reply| if reply.name != 'success' error = reply.first_element(nil).name end true } raise error if error end end ## # SASL Anonymous authentication helper class Anonymous < Base ## # Authenticate by sending nothing with the ANONYMOUS token def auth(password) auth_text = "#{@stream.jid.node}" error = nil @stream.send(generate_auth('ANONYMOUS', Base64::encode64(auth_text).gsub(/\s/, ''))) { |reply| if reply.name != 'success' error = reply.first_element(nil).name end true } raise error if error end end ## # SASL DIGEST-MD5 authentication helper (RFC2831) class DigestMD5 < Base ## # Sends the wished auth mechanism and wait for a challenge # # (proceed with DigestMD5#auth) def initialize(stream) super challenge = {} error = nil @stream.send(generate_auth('DIGEST-MD5')) { |reply| if reply.name == 'challenge' and reply.namespace == NS_SASL challenge = decode_challenge(reply.text) else error = reply.first_element(nil).name end true } raise error if error @nonce = challenge['nonce'] @realm = challenge['realm'] end def decode_challenge(challenge) text = Base64::decode64(challenge) res = {} state = :key key = '' value = '' text.scan(/./) do |ch| if state == :key if ch == '=' state = :value else key += ch end elsif state == :value if ch == ',' # due to our home-made parsing of the challenge, the key could have # leading whitespace. strip it, or that would break jabberd2 support. key = key.strip res[key] = value key = '' value = '' state = :key elsif ch == '"' and value == '' state = :quote else value += ch end elsif state == :quote if ch == '"' state = :value else value += ch end end end # due to our home-made parsing of the challenge, the key could have # leading whitespace. strip it, or that would break jabberd2 support. key = key.strip res[key] = value unless key == '' Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text}\n#{res.inspect}") res end ## # * Send a response # * Wait for the server's challenge (which aren't checked) # * Send a blind response to the server's challenge def auth(password) response = {} response['nonce'] = @nonce response['charset'] = 'utf-8' response['username'] = @stream.jid.node response['realm'] = @realm || @stream.jid.domain response['cnonce'] = generate_nonce response['nc'] = '00000001' response['qop'] = 'auth' response['digest-uri'] = "xmpp/#{@stream.jid.domain}" response['response'] = response_value(@stream.jid.node, response['realm'], response['digest-uri'], password, @nonce, response['cnonce'], response['qop'], response['authzid']) response.each { |key,value| unless %w(nc qop response charset).include? key response[key] = "\"#{value}\"" end } response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',') Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}\n#{response.inspect}") r = REXML::Element.new('response') r.add_namespace NS_SASL r.text = Base64::encode64(response_text).gsub(/\s/, '') success_already = false error = nil @stream.send(r) { |reply| if reply.name == 'success' success_already = true elsif reply.name != 'challenge' error = reply.first_element(nil).name end true } return if success_already raise error if error # TODO: check the challenge from the server r.text = nil @stream.send(r) { |reply| if reply.name != 'success' error = reply.first_element(nil).name end true } raise error if error end private ## # Function from RFC2831 def h(s); Digest::MD5.digest(s); end ## # Function from RFC2831 def hh(s); Digest::MD5.hexdigest(s); end ## # Calculate the value for the response field def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop, authzid) a1_h = h("#{username}:#{realm}:#{passwd}") a1 = "#{a1_h}:#{nonce}:#{cnonce}" if authzid a1 += ":#{authzid}" end if qop == 'auth-int' || qop == 'auth-conf' a2 = "AUTHENTICATE:#{digest_uri}:00000000000000000000000000000000" else a2 = "AUTHENTICATE:#{digest_uri}" end hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}") end end end end xmpp4r-0.5.6/lib/xmpp4r/dataforms.rb0000644000175000017500000000027113647121573020512 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/dataforms/x/data.rb' xmpp4r-0.5.6/lib/xmpp4r/location.rb0000644000175000017500000000011313647121573020335 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/location/location' require 'xmpp4r/location/helper/helper' xmpp4r-0.5.6/lib/xmpp4r/pubsub/0000755000175000017500000000000013647121573017505 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/pubsub/iq/0000755000175000017500000000000013647121573020116 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/pubsub/iq/pubsub.rb0000644000175000017500000000073713647121573021752 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub NS_PUBSUB = 'http://jabber.org/protocol/pubsub' class IqPubSub < XMPPElement name_xmlns 'pubsub', NS_PUBSUB force_xmlns true end class IqPubSubOwner < XMPPElement name_xmlns 'pubsub', NS_PUBSUB + '#owner' force_xmlns true end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/helper/0000755000175000017500000000000013647121573020764 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb0000644000175000017500000000705513647121573025517 0ustar debbiecocoadebbiecocoamodule Jabber module PubSub # Jabber::Stream helper that will transparently sign PubSub requests module OAuthPubSubStreamHelper attr_accessor :pubsubjid, :oauth_consumer, :oauth_token, :oauth_options # enhanced #send_with_id method that signs stanzas def send_with_id(iq) if iq.first_element("pubsub") oauth = OAuthServiceHelper.create_oauth_node(self.jid, self.pubsubjid, self.oauth_consumer, self.oauth_token, self.oauth_options) iq.pubsub.add(oauth) end super(iq) end end # PubSub service helper for use with OAuth-authenticated nodes class OAuthServiceHelper < ServiceHelper def initialize(stream, pubsubjid, oauth_consumer, oauth_token, options = {}) # imbue the stream with magical OAuth signing powers stream.extend(OAuthPubSubStreamHelper) stream.oauth_consumer = oauth_consumer stream.oauth_token = oauth_token stream.oauth_options = options stream.pubsubjid = pubsubjid super(stream, pubsubjid) end # add the OAuth sauce (XEP-0235) # The `options` hash may contain the following parameters: # :oauth_nonce => nonce (one will be generated otherwise) # :oauth_timestamp => timestamp (one will be generated otherwise) # :oauth_signature_method => signature method (defaults to HMAC-SHA1) # :oauth_version => OAuth version (defaults to "1.0") def self.create_oauth_node(jid, pubsubjid, oauth_consumer, oauth_token, options = {}) require 'oauth' request = OAuth::RequestProxy.proxy \ "method" => "iq", "uri" => [jid.strip.to_s, pubsubjid.strip.to_s] * "&", "parameters" => { "oauth_consumer_key" => oauth_consumer.key, "oauth_nonce" => options[:oauth_nonce] || OAuth::Helper.generate_nonce, "oauth_timestamp" => options[:oauth_timestamp] || OAuth::Helper.generate_timestamp, "oauth_token" => oauth_token.token, "oauth_signature_method" => options[:oauth_signature_method] || "HMAC-SHA1", "oauth_version" => options[:oauth_version] || "1.0" } request.sign!(:consumer => oauth_consumer, :token => oauth_token) # TODO create XMPPElements for OAuth elements oauth = REXML::Element.new("oauth") oauth.attributes['xmlns'] = 'urn:xmpp:oauth:0' oauth_consumer_key = REXML::Element.new("oauth_consumer_key") oauth_consumer_key.text = request.oauth_consumer_key oauth.add(oauth_consumer_key) oauth_token_node = REXML::Element.new("oauth_token") oauth_token_node.text = request.oauth_token oauth.add(oauth_token_node) oauth_signature_method = REXML::Element.new("oauth_signature_method") oauth_signature_method.text = request.oauth_signature_method oauth.add(oauth_signature_method) oauth_signature = REXML::Element.new("oauth_signature") oauth_signature.text = request.oauth_signature oauth.add(oauth_signature) oauth_timestamp = REXML::Element.new("oauth_timestamp") oauth_timestamp.text = request.oauth_timestamp oauth.add(oauth_timestamp) oauth_nonce = REXML::Element.new("oauth_nonce") oauth_nonce.text = request.oauth_nonce oauth.add(oauth_nonce) oauth_version = REXML::Element.new("oauth_version") oauth_version.text = request.oauth_version oauth.add(oauth_version) oauth end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/helper/nodehelper.rb0000644000175000017500000000727613647121573023452 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # For documentation of the return values please see [Jabber::PubSub::ServiceHelper] # This class is only a wrapper around [Jabber::PubSub::ServiceHelper] # require 'xmpp4r/pubsub/helper/servicehelper' require 'xmpp4r/pubsub/helper/nodebrowser' module Jabber module PubSub class NodeHelper < ServiceHelper attr_reader :nodename attr_reader :name attr_reader :jid attr_reader :my_subscriptions ## # creates a new node # new(client,service,nodename) # stream:: [Jabber::Stream] # jid:: [String] (jid of the pubsub service) # nodename:: [String] def initialize(stream, jid, nodename = nil, create_if_not_exist = true) super(stream,jid) @nodename = nodename @jid = jid @stream = stream if create_if_not_exist and !node_exist? # if no nodename is given a instant node will created # (if the service supports instant nodes) @nodename = create_node else get_subscriptions end end ## # creates the node # create(configuration=nil) # configuration:: [Jabber::XData] def create_node(configuration = Jabber::PubSub::NodeConfig.new) unless node_exist? super(@nodename,configuration) else false end end ## # get the configuration of the node # get_configuration(configuration=nil) # configuration:: [Jabber::XData] def get_configuration(subid = nil) get_options(@nodename, subid) end ## # set the configuration of the node # set_configuration(configuration=nil) # configuration:: [Jabber::XData] # subid:: [String] default is nil def set_configuration(configuration,subid = nil) set_options(@nodename, configuration, subid) end ## # deletes the node # delete def delete_node delete(@nodename) end ## # publishing content on this node # publish_content(items) # items:: [REXML::Element] def publish_content(items) publish_item_to(@nodename,items) end ## # gets all items from the node # get_all_items def get_all_items get_items_from(@nodename) end ## # get a count of items # get_items(count) # count:: [Fixnum] def get_items(count) get_items_from(@nodename,count) end ## # get all node affiliations # get_affiliations def get_affiliations affiliations end ## # get all subscriptions on this node # get_subscriptions def get_subscriptions get_subscriptions_from(@nodename) end ## # get all subscribers subscribed on this node # get_subscribers def get_subscribers @subscriptions = subscribers(@nodename) end ## # subscribe to this node # do_subscribe def do_subscribe subscribe_to(@nodename) get_subscriptions end ## # unsubscribe from this node # do_unsubscribe(subid = nil) # subid:: [String] def do_unsubscribe(subid) unsubscribe(@nodename,subid) end ## # purge all items from this node # purge_items def purge_items purge(@nodename) end private def node_exist? nodebrowser = PubSub::NodeBrowser.new(@stream) nodebrowser.nodes(@jid).include?(@nodename) end def disco_info end end #class end #module end #module xmpp4r-0.5.6/lib/xmpp4r/pubsub/helper/servicehelper.rb0000644000175000017500000003654213647121573024163 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # It's recommented to read the XEP-0060 before you use this Helper. (Maybe its # better not use the helper for now ) ;) # The whole code is getting better, but may still contain bugs - be careful! # # Maybe the following structure is good # ( taken from the xep-0060 ) # # entity usecases # retrieve all subscriptions # retrieve all affiliations # NOTE: the disco stuff will done by the nodebrowserhelper # subscriber usecases # subscribe # unsubscribe # configure subscription options # retrieve items from a node # publisher usecases # publish a item to a node # delete a item from a node # owner usecases # create a node # configure a node # request default configuration options # delete a node # purge all node items # manage subscription requests # process pending subscriptions # manage subscriptions # manage affiliations # # collection nodes # # If someone want to implement something i think its better to do this in # this order because everyone who reads the xep-0060 do know where to search in the file # require 'xmpp4r/pubsub/iq/pubsub' require 'xmpp4r/pubsub/children/event' require 'xmpp4r/pubsub/children/item' require 'xmpp4r/pubsub/children/items' require 'xmpp4r/pubsub/children/subscription' require 'xmpp4r/pubsub/children/unsubscribe' require 'xmpp4r/pubsub/children/node_config' require 'xmpp4r/pubsub/children/subscription_config' require 'xmpp4r/pubsub/children/retract' require 'xmpp4r/dataforms' module Jabber module PubSub ## # A Helper representing a PubSub Service class ServiceHelper ## # Creates a new representation of a pubsub service # stream:: [Jabber::Stream] # pubsubjid:: [String] or [Jabber::JID] def initialize(stream, pubsubjid) @stream = stream @pubsubjid = pubsubjid @event_cbs = CallbackList.new @stream.add_message_callback(200,self) { |message| handle_message(message) } end ## # get all subscriptions on a pubsub component # return:: [Hash] of [PubSub::Subscription] def get_subscriptions_from_all_nodes iq = basic_pubsub_query(:get) entities = iq.pubsub.add(REXML::Element.new('subscriptions')) res = nil @stream.send_with_id(iq) { |reply| if reply.pubsub.first_element('subscriptions') res = [] reply.pubsub.first_element('subscriptions').each_element('subscription') { |subscription| res << Jabber::PubSub::Subscription.import(subscription) } end } res end ## # get subids for a passed node # return:: [Array] of subids def get_subids_for(node) ret = [] get_subscriptions_from_all_nodes.each do |subscription| if subscription.node == node ret << subscription.subid end end return ret end ## # subscribe to a node # node:: [String] # return:: [Hash] of { attributename => value } def subscribe_to(node) iq = basic_pubsub_query(:set) sub = REXML::Element.new('subscribe') sub.attributes['node'] = node sub.attributes['jid'] = @stream.jid.strip.to_s iq.pubsub.add(sub) res = nil @stream.send_with_id(iq) do |reply| pubsubanswer = reply.pubsub if pubsubanswer.first_element('subscription') res = PubSub::Subscription.import(pubsubanswer.first_element('subscription')) end end # @stream.send_with_id(iq) res end ## # Unsubscribe from a node with an optional subscription id # # May raise ServerError # node:: [String] # subid:: [String] or nil (not supported) # return:: true def unsubscribe_from(node, subid=nil) ret = [] if subid.nil? subids = get_subids_for(node) else subids = [ subid ] end subids << nil if subids.empty? subids.each do |sid| iq = basic_pubsub_query(:set) unsub = PubSub::Unsubscribe.new unsub.node = node unsub.jid = @stream.jid.strip unsub.subid = sid iq.pubsub.add(unsub) res = false @stream.send_with_id(iq) { |reply| res = reply.kind_of?(Jabber::Iq) and reply.type == :result } # @stream.send_with_id(iq) ret << res end ret end ## # gets all items from a pubsub node # node:: [String] # count:: [Fixnum] # subid:: [String] # return:: [Hash] { id => [Jabber::PubSub::Item] } def get_items_from(node, count=nil, subid=nil) if subid.nil? # Hm... no subid passed. Let's see if we can provide one. subids = get_subids_for(node) if ! subids.empty? # If more than one, sorry. We'll just respect the first. subid = subids[0] end end iq = basic_pubsub_query(:get) items = Jabber::PubSub::Items.new items.max_items = count items.subid = subid unless subid.nil? # if subid is still nil, we haven't any... why bother? items.node = node iq.pubsub.add(items) res = nil @stream.send_with_id(iq) { |reply| if reply.kind_of?(Iq) and reply.pubsub and reply.pubsub.first_element('items') res = {} reply.pubsub.first_element('items').each_element('item') do |item| res[item.attributes['id']] = item.children.first if item.children.first end end true } res end ## # NOTE: this method sends only one item per publish request because some services # may not allow batch processing. Maybe this will changed in the future? # node:: [String] # item:: [Jabber::PubSub::Item] # return:: true def publish_item_to(node,item) iq = basic_pubsub_query(:set) publish = iq.pubsub.add(REXML::Element.new('publish')) publish.attributes['node'] = node if item.kind_of?(Jabber::PubSub::Item) publish.add(item) @stream.send_with_id(iq) end end ## # node:: [String] # item:: [REXML::Element] # id:: [String] # return:: true def publish_item_with_id_to(node,item,id) iq = basic_pubsub_query(:set) publish = iq.pubsub.add(REXML::Element.new('publish')) publish.attributes['node'] = node if item.kind_of?(REXML::Element) xmlitem = Jabber::PubSub::Item.new xmlitem.id = id xmlitem.import(item) publish.add(xmlitem) else raise "given item is not a proper xml document or Jabber::PubSub::Item" end @stream.send_with_id(iq) end ## # deletes an item from a persistent node # node:: [String] # item_id:: [String] or [Array] of [String] # return:: true def delete_item_from(node, item_id) iq = basic_pubsub_query(:set) retract = iq.pubsub.add(Jabber::PubSub::Retract.new) retract.node = node if item_id.kind_of? Array item_id.each { |id| xmlitem = Jabber::PubSub::Item.new xmlitem.id = id retract.add(xmlitem) } else xmlitem = Jabber::PubSub::Item.new xmlitem.id = item_id retract.add(xmlitem) end @stream.send_with_id(iq) end ## # purges all items on a persistent node # node:: [String] # return:: true def purge_items_from(node) iq = basic_pubsub_query(:set, true) purge = REXML::Element.new('purge') purge.attributes['node'] = node iq.pubsub.add(purge) @stream.send_with_id(iq) end ## # Create a new node on the pubsub service # node:: [String] the node name - otherwise you get a automatically generated one (in most cases) # configure:: [Jabber::PubSub::NodeConfig] if you want to configure your node (default nil) # return:: [String] def create_node(node = nil, configure = Jabber::PubSub::NodeConfig.new) rnode = nil iq = basic_pubsub_query(:set) iq.pubsub.add(REXML::Element.new('create')).attributes['node'] = node if configure if configure.kind_of?(Jabber::PubSub::NodeConfig) iq.pubsub.add(configure) end end @stream.send_with_id(iq) do |reply| if reply.kind_of?(Jabber::Iq) and reply.type == :result rnode = node end end rnode end ## # Create a new collection node on the pubsub service # node:: [String] the node name - otherwise you get an automatically generated one (in most cases) # configure:: [Jabber::PubSub::NodeConfig] if you want to configure your node (default nil) # return:: [String] def create_collection_node(node = nil, configure = Jabber::PubSub::NodeConfig.new) if configure.options['pubsub#node_type'] && configure.options['pubsub#node_type'] != 'collection' raise Jabber::ArgumentError, "Invalid node_type specified in node configuration. Either do not specify one, or use 'collection'" end configure.options = configure.options.merge({'pubsub#node_type' => 'collection'}) create_node(node, configure) end ## # get configuration from a node # node:: [String] # return:: [Jabber::PubSub::Configure] def get_config_from(node) iq = basic_pubsub_query(:get, true) iq.pubsub.add(Jabber::PubSub::OwnerNodeConfig.new(node)) ret = nil @stream.send_with_id(iq) do |reply| ret = reply.pubsub.first_element('configure') end ret end ## # set configuration for a node # node:: [String] # options:: [Jabber::PubSub::NodeConfig] # return:: true on success def set_config_for(node, config) iq = basic_pubsub_query(:set, true) iq.pubsub.add(config) @stream.send_with_id(iq) end ## # Delete a pubsub node # node:: [String] # return:: true def delete_node(node) iq = basic_pubsub_query(:set,true) iq.pubsub.add(REXML::Element.new('delete')).attributes['node'] = node @stream.send_with_id(iq) end def set_affiliations(node, jid, role = 'publisher') iq = basic_pubsub_query(:set, true) affiliations = iq.pubsub.add(REXML::Element.new('affiliations')) affiliations.attributes['node'] = node affiliation = affiliations.add(REXML::Element.new('affiliation')) affiliation.attributes['jid'] = jid.to_s affiliation.attributes['affiliation'] = role.to_s res = nil @stream.send_with_id(iq) { |reply| true } res end ## # shows the affiliations on a pubsub service # node:: [String] # return:: [Hash] of { node => symbol } def get_affiliations(node = nil) iq = basic_pubsub_query(:get) affiliations = iq.pubsub.add(REXML::Element.new('affiliations')) affiliations.attributes['node'] = node res = nil @stream.send_with_id(iq) { |reply| if reply.pubsub.first_element('affiliations') res = {} reply.pubsub.first_element('affiliations').each_element('affiliation') do |affiliation| # TODO: This should be handled by an affiliation element class aff = case affiliation.attributes['affiliation'] when 'owner' then :owner when 'publisher' then :publisher when 'none' then :none when 'outcast' then :outcast else nil end res[affiliation.attributes['node']] = aff end end true } res end ## # shows all subscriptions on the given node # node:: [String] # return:: [Array] of [Jabber::Pubsub::Subscription] def get_subscriptions_from(node) iq = basic_pubsub_query(:get) entities = iq.pubsub.add(REXML::Element.new('subscriptions')) entities.attributes['node'] = node res = nil @stream.send_with_id(iq) { |reply| if reply.pubsub.first_element('subscriptions') res = [] if reply.pubsub.first_element('subscriptions').attributes['node'] == node reply.pubsub.first_element('subscriptions').each_element('subscription') { |subscription| res << PubSub::Subscription.import(subscription) } end end true } res end ## # shows all jids of subscribers of a node # node:: [String] # return:: [Array] of [String] def get_subscribers_from(node) res = [] get_subscriptions_from(node).each { |sub| res << sub.jid } res end ## # get options from a subscription # node:: [String] # jid:: [Jabber::JID] or [String] # subid:: [String] or nil # return:: [Jabber::PubSub::OwnerConfigure] def get_options_from(node, jid, subid = nil) iq = basic_pubsub_query(:get) iq.pubsub.add(Jabber::PubSub::SubscriptionConfig.new(node, jid.kind_of?(String) ? Jabber::JID.new(jid).strip: jid.strip, subid)) ret = nil @stream.send_with_id(iq) do |reply| ret = reply.pubsub.first_element('options') end ret end ## # set options for a subscription # node:: [String] # jid:: [Jabber::JID] or [String] # options:: [Jabber::PubSub::SubscriptionConfig} specifying configuration options # subid:: [String] or nil # return:: true def set_options_for(node, jid, options, subid = nil) iq = basic_pubsub_query(:set) iq.pubsub.add(Jabber::PubSub::SubscriptionConfig.new(node, jid.kind_of?(String) ? Jabber::JID.new(jid).strip: jid.strip, options, subid)) ret = false @stream.send_with_id(iq) do |reply| ret = ( reply.type == :result ) end ret end ## # String representation # result:: [String] The PubSub service's JID def to_s @pubsubjid.to_s end ## # Register callbacks for incoming events # (i.e. Message stanzas containing) PubSub notifications def add_event_callback(prio = 200, ref = nil, &block) @event_cbs.add(prio, ref, block) end private ## # creates a basic pubsub iq # basic_pubsub_query(type) # type:: [Symbol] def basic_pubsub_query(type,ownerusecase = false) iq = Jabber::Iq.new(type,@pubsubjid) if ownerusecase iq.add(IqPubSubOwner.new) else iq.add(IqPubSub.new) end iq.from = @stream.jid #.strip iq end ## # handling incoming events # handle_message(message) # message:: [Jabber::Message] def handle_message(message) if message.from == @pubsubjid and message.first_element('event').kind_of?(Jabber::PubSub::Event) event = message.first_element('event') @event_cbs.process(event) end end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/helper/nodebrowser.rb0000644000175000017500000000732713647121573023653 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/discovery' module Jabber module PubSub class NodeBrowser ## # Initialize a new NodeBrowser # new(stream,pubsubservice) # stream:: [Jabber::Stream] def initialize(stream) @stream = stream end ## # Retrieve the nodes # Throws an ServerError when receiving # # jid:: [JID] Target entity (set only domain!) # return:: [Array] of [String] or [nil] def nodes(jid) iq = Iq.new(:get,jid) iq.from = @stream.jid iq.add(Discovery::IqQueryDiscoItems.new) nodes = [] @stream.send_with_id(iq) do |answer| answer.query.each_element('item') { |item| nodes.push(item.node) } end nodes end ## # Retrieve the nodes with names # Throws a ServerError when receiving # # jid:: [Jabber::JID] Target entity (set only domain!) # return:: [Array] of [Hash] with keys 'node' => [String] and 'name' => [String] or [nil] def nodes_names(jid) iq = Iq.new(:get,jid) iq.from = @stream.jid iq.add(Discovery::IqQueryDiscoItems.new) nodes = [] @stream.send_with_id(iq) do |answer| answer.query.each_element('item') do |item| nodes.push( {'node' => item.node,'name' => item.iname } ) end end nodes end ## # Retrieve the items from a node # Throws an ServerError when receiving # # jid:: [Jabber::JID] Target entity (set only domain!) # node:: [String] # return:: [Array] of [Hash] with keys 'name' => [String] and 'jid' => [Jabber::JID] def items(jid,node) iq = Iq.new(:get,jid) iq.from = @stream.jid discoitems = Discovery::IqQueryDiscoItems.new discoitems.node = node iq.add(discoitems) items = [] err = nil @stream.send_with_id(iq) do |answer| answer.query.each_element('item') { |item| items.push( {'jid' => item.jid,'name' => item.iname } ) } end items end ## # get disco info for a node # jid:: [Jabber::JID] # node:: [String] # return:: [Hash] with possible keys type:: [String] ,category:: [String],features:: [Array] of feature, nodeinformation:: [Jabber::XData] # check http://www.xmpp.org/extensions/xep-0060.html#entity for more infos def get_info(jid,node) iq = Iq.new(:get,jid) iq.from = @stream.jid discoinfo = Discovery::IqQueryDiscoInfo.new discoinfo.node = node iq.add(discoinfo) info = {} @stream.send_with_id(iq) do |answer| identity = answer.query.identity info['type'] = identity.type info['category'] = identity.category info['features'] = answer.query.features answer.query.each_element('x') { |x| info['nodeinformation'] = x } end info end # this is only for a xep <-> nodebrowser.rb understanding alias get_metadata get_info ## # get type of node # jid:: [Jabber::JID] # node:: [String] # def type(jid,node) info = get_info(jid,node) return info['type'] end ## # get category of node # jid:: [Jabber::JID] # node:: [String] # def category(jid,node) info = get_info(jid,node) return info['category'] end end #class end #module end #module xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/0000755000175000017500000000000013647121573021275 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/pubsub/children/items.rb0000644000175000017500000000223613647121573022746 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub ## # Items # a collection of Items class Items < XMPPElement include Enumerable name_xmlns 'items', NS_PUBSUB def node attributes['node'] end def node=(mynodename) attributes['node'] = mynodename end def subid attributes['subid'] end def subid=(mysubid) attributes['subid'] = mysubid.to_s end def max_items attributes['max_items'] end def max_items=(mymaxitems) attributes['max_items'] = mymaxitems.to_s end def items get_elements("item") end def each(&block) items.each(&block) end end ## # Items wrapped in a Pubsub Event. # # See example 2 in http://www.xmpp.org/extensions/xep-0060.html#intro-howitworks # and http://www.xmpp.org/extensions/xep-0060.html#schemas-event class EventItems < Items name_xmlns 'items', NS_PUBSUB + "#event" end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/item.rb0000644000175000017500000000144613647121573022565 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub ## # Item # One PubSub Item class Item < XMPPElement name_xmlns 'item', NS_PUBSUB def initialize(id=nil) super() attributes['id'] = id end def id attributes['id'] end def id=(myid) attributes['id'] = myid end end ## # An Item wrapped in a Pubsub Event. # # See example 2 in http://www.xmpp.org/extensions/xep-0060.html#intro-howitworks # and http://www.xmpp.org/extensions/xep-0060.html#schemas-event class EventItem < Item name_xmlns 'item', NS_PUBSUB + "#event" end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/publish.rb0000644000175000017500000000143113647121573023267 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' module Jabber module PubSub ## # Publish # # A XMPP element, see example 1 in # http://www.xmpp.org/extensions/xep-0060.html#intro-howitworks class Publish < XMPPElement include Enumerable name_xmlns 'publish', NS_PUBSUB ## # support for enumerating elements def each(&block) items.each(&block) end ## # return child elements def items get_elements("item") end ## # return the node for this publication def node attributes['node'] end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/node_config.rb0000644000175000017500000000226513647121573024101 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' require 'xmpp4r/pubsub/children/configuration' module Jabber module PubSub ## # NodeConfig # # A XMPP element for pubsub nodes, see example 123 in # http://www.xmpp.org/extensions/xep-0060.html#owner-create-and-configure class NodeConfig < Configuration name_xmlns 'configure', NS_PUBSUB ## # Construct a element. # node:: [String] the node to configure # options:: [Hash] the configuration to apply def initialize(node = nil, options = nil) super() self.node = node self.options = options end private def form_type 'http://jabber.org/protocol/pubsub#node_config' end end ## # # OwnerNodeConfig # # A stanza for 'Owner' use cases # # see example 125 in # http://www.xmpp.org/extensions/xep-0060.html#owner-configure-request class OwnerNodeConfig < NodeConfig name_xmlns 'configure', NS_PUBSUB + '#owner' end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/configuration.rb0000644000175000017500000000400513647121573024470 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' require 'xmpp4r/pubsub/iq/pubsub' module Jabber module PubSub class Configuration < XMPPElement ## # get the underlying XData form element def form children.each do |c| return c if c.kind_of?(Jabber::Dataforms::XData) end nil end def form=(form) children.each do |c| delete_element(c) if c.kind_of?(Jabber::Dataforms::XData) delete(c) end end add(form) end # Implemented by NodeConfig and SubscriptionConfig def form_type '' end def options=(options) if options self.form = form_for_options( form_type, options ) end end ## # get a list of the configured options represented by this stanza # return:: [Hash] the options and their values def options configured_options = {} if !form.nil? form.fields.each do |f| f.values.size == 1 ? configured_options[f.var] = f.values.first : configured_options[f.var] = f.values end end configured_options end def node=(node) attributes['node'] = node end def node attributes['node'] end private ## # creates a Dataform for configuration # config:: [Hash] the desired configuration options def form_for_options(type, options) form = Jabber::Dataforms::XData.new(:submit) pubsub_config = Jabber::Dataforms::XDataField.new('FORM_TYPE', :hidden) pubsub_config.values = [type] form.add(pubsub_config) options.each_pair do |key, value| f = Jabber::Dataforms::XDataField.new(key) f.values = value.is_a?(Array) ? value : [value] form.add(f) end form end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/retract.rb0000644000175000017500000000146113647121573023270 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' module Jabber module PubSub ## # Retract # # A XMPP element, see example 103 in # http://xmpp.org/extensions/xep-0060.html#publisher-delete class Retract < XMPPElement name_xmlns 'retract', NS_PUBSUB ## # get the node for this retraction def node attributes['node'] end ## # set the node for this retraction def node=(s) attributes['node'] = s end ## # Get children def items res = [] each_element('item') { |item| res << item } res end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/subscription_config.rb0000644000175000017500000000337213647121573025700 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' require 'xmpp4r/pubsub/children/configuration' require 'xmpp4r/pubsub/iq/pubsub' module Jabber module PubSub ## # SubscriptionConfig # # An XMPP element, see example 57 in # http://www.xmpp.org/extensions/xep-0060.html#subscriber-configure-success class SubscriptionConfig < Configuration name_xmlns 'options', NS_PUBSUB ## # Construct a new Options stanza # node:: [String] the node to which this subscription applies # jid:: [String] or [Jabber::JID] the jid that holds the subscription # options:: [Hash] the configuration for this subscription # subid:: [String] (optional) subscription id def initialize(node = nil, jid = nil, options = nil, subid = nil) super() self.node = node self.jid = jid self.options = options self.subid = subid end ## # set the 'jid' attribute of this stanza # jid:: [Jabber::JID] or [String] the jid owning the subscription def jid=(jid) attributes['jid'] = jid.to_s end ## # get the 'jid' attribute for this stanza def jid attributes['jid'] ? Jabber::JID.new(attributes['jid']) : nil end ## # set the 'subid' attribute # subid:: [String] the subscription id def subid=(subid) attributes['subid'] = subid end ## # get the 'subid' attribute def subid attributes['subid'] end private def form_type 'http://jabber.org/protocol/pubsub#subscribe_options' end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/event.rb0000644000175000017500000000221513647121573022743 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub ## # Event # a publishing event class Event < XMPPElement name_xmlns 'event', NS_PUBSUB + '#event' force_xmlns true ## # return payload def payload elements end ## # add payload # payload:: [REXML::Element] def payload=(pl) add_element = pl end ## # return the payload type def event_type? # each child of event # this should iterate only one time each_element('./event/*') { |plelement| case plelement.name when 'collection' then return :collection when 'configuration' then return :configuration when 'delete' then return :delete when 'items' then return :items when 'purge' then return :purge when 'subscription' then return :subscription else return nil end } end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/unsubscribe.rb0000644000175000017500000000231613647121573024150 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub ## # Unsubscribe class Unsubscribe < XMPPElement name_xmlns 'unsubscribe' def initialize(myjid=nil,mynode=nil,mysubid=nil) super() jid = myjid node = mynode subid = mysubid end ## # shows the jid # return:: [String] def jid (a = attribute('jid')).nil? ? a : JID.new(a.value) end ## # sets the jid # =:: [Jabber::JID] or [String] def jid=(myjid) add_attribute('jid', myjid ? myjid.to_s : nil) end ## # shows the node # return:: [String] def node attributes['node'] end ## # sets the node # =:: [String] def node=(mynode) attributes['node'] = mynode end ## # shows the subid # return:: [String] def subid attributes['subid'] end ## # sets the subid # =:: [String] def subid=(mysubid) attributes['subid'] = mysubid end end end end xmpp4r-0.5.6/lib/xmpp4r/pubsub/children/subscription.rb0000644000175000017500000000301113647121573024341 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/iq' module Jabber module PubSub ## # Subscription class Subscription < XMPPElement name_xmlns 'subscription', NS_PUBSUB def initialize(myjid=nil,mynode=nil,mysubid=nil,mysubscription=nil) super() jid = myjid node = mynode subid = mysubid state = mysubscription end def jid (a = attribute('jid')).nil? ? a : JID.new(a.value) end def jid=(myjid) add_attribute('jid', myjid ? myjid.to_s : nil) end def node attributes['node'] end def node=(mynode) attributes['node'] = mynode end def subid attributes['subid'] end def subid=(mysubid) attributes['subid'] = mysubid end def state # each child of event # this should interate only one time case attributes['subscription'] when 'none' then return :none when 'pending' then return :pending when 'subscribed' then return :subscribed when 'unconfigured' then return :unconfigured else return nil end end def state=(mystate) attributes['subscription'] = mystate end alias subscription state def need_approval? state == :pending end end end end xmpp4r-0.5.6/lib/xmpp4r/reliable.rb0000644000175000017500000001176013647121573020316 0ustar debbiecocoadebbiecocoarequire 'xmpp4r/stream' module Jabber module Reliable class Connection < Jabber::Client def initialize(full_jid, config) super(full_jid) @servers = config[:servers] @port = config[:port] || 5222 @max_retry = config[:max_retry] || 30 @retry_sleep = config[:retry_sleep] || 2 if(@servers.nil? or @servers.empty?) @servers = [@jid.domain] end end def connect retry_count = 0 server_to_use = nil server_pool = @servers.dup.sort{ rand <=> rand } begin server_to_use = server_pool.shift server_pool.push(server_to_use) Jabber::debuglog "timeout will be: #{@retry_sleep.to_f}" Timeout.timeout(@retry_sleep.to_f){ Jabber::debuglog "trying to connect to #{server_to_use}" super(server_to_use, @port) } Jabber::debuglog self.jid.to_s + " connected to " + server_to_use.to_s Jabber::debuglog "out of possible servers " + @servers.inspect rescue Exception, Timeout::Error => e Jabber::warnlog "#{server_to_use} error: #{e.inspect}. Will attempt to reconnect in #{@retry_sleep}" sleep(@retry_sleep.to_f) if(retry_count >= @max_retry.to_i) Jabber::warnlog "reached max retry count on exception, failing" raise e end retry_count += 1 retry end end end class Listener def initialize(full_jid, password, config, &block) @on_message_block = block @full_jid = full_jid @config = config @password = password @max_retry = config[:max_retry] || 30 end def setup_connection @connection = Connection.new(@full_jid, @config) if @on_message_block @connection.add_message_callback(&@on_message_block) else @connection.add_message_callback do |msg| self.on_message(msg) end end #We could just reconnect in @connection.on_exception, #but by raising into this seperate thread, we avoid growing our stack trace @reconnection_thread = Thread.new do first_run = true begin self.start unless first_run loop do sleep(1) Thread.pass end rescue => e first_run = false retry end end @exception_handlers = [] @connection.on_exception do |e, connection, where_failed| self.run_exception_handlers(e, connection, where_failed) end end def run_exception_handlers(e, connection, where_failed) @exception_handlers.each do |ex_handler| ex_handler.call(e, connection, where_failed) end if where_failed == :sending @message_to_send_on_reconnect = @message_now_sending end if where_failed != :close && !@connection.is_connected? @reconnection_thread.raise(e) end end def add_exception_handler(&block) @exception_handlers << block end def start setup_connection unless @connection connect auth send_presence if @message_to_send_on_reconnect send_message(@message_to_send_on_reconnect) end @message_to_send_on_reconnect = nil end #Stop the listener. (close the connection) def stop @connection.close if @connection and @connection.is_connected? @connection = nil end def connect @connection.connect end def auth @connection.auth(@password) end def send_presence presence_message = @config[:presence_message] if presence_message && !presence_message.empty? @connection.send(Jabber::Presence.new.set_show(:chat).set_status(presence_message)) end end #TODO: test and fix situation where we get disconnected while sending but then successfully reconnect # (and make sure in such cases we resent) def send_message(message) unless @connection raise ::ArgumentError, "Can't send messages while listener is stopped. Plase 'start' the listener first." end retry_count = 0 begin while(not @connection.is_connected?) #wait Thread.pass end @message_now_sending = message @connection.send(message) return true #true, message was sent rescue => e if e.is_a?(Interrupt) raise e end if(retry_count > @max_retry.to_i) Jabber::debuglog "reached max retry count on message re-send, failing" raise e end retry_count += 1 Jabber::debuglog "retrying message send.." + e.inspect retry end end end end end xmpp4r-0.5.6/lib/xmpp4r/test/0000755000175000017500000000000013647121573017164 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/test/listener_mocker.rb0000644000175000017500000000744613647121573022711 0ustar debbiecocoadebbiecocoamodule Jabber module Test class ListenerMocker class << self attr_accessor :with_socket_mocked_callback_proc end def self.with_socket_mocked(callback_proc) TCPSocket.class_eval{ ListenerMocker.with_socket_mocked_callback_proc = callback_proc } TCPSocket.class_eval do alias_method :initialize_old, :initialize def initialize(*args) initialize_old(*args) if ListenerMocker.with_socket_mocked_callback_proc.call(args) end end yield ensure TCPSocket.class_eval do if method_defined?(:initialize_old) alias_method :initialize, :initialize_old end end end class << self attr_accessor :mock_clients, :tracker_of_callers end def self.mocker_proc Proc.new do attr_accessor :messagecbs, :connected ListenerMocker.mock_clients ||= {} ListenerMocker.tracker_of_callers ||= {} def connect Jabber::debuglog("(in mock) connected #{@jid.bare}") self.connected = true end def close! ListenerMocker.mock_clients[@jid.bare.to_s] = nil ListenerMocker.tracker_of_callers[@jid.bare.to_s] = nil self.connected = false end def auth(password) auth_nonsasl(password) end def auth_nonsasl(password, digest=true) Jabber::debuglog("(in mock) authed #{@jid.bare}") if(ListenerMocker.mock_clients[@jid.bare.to_s]) #raise a stack trace about multiple clients raise "\n\n ---> READ ME: this is actualy 2 stack traces: <---- \n\n"+ "There is already a client connected on that jid #{@jid.bare.to_s}. "+ "The mock library cannot support multiple listeners connected as the same user! originally called in:\n"+ ListenerMocker.tracker_of_callers[@jid.bare.to_s].backtrace.join("\n")+"\n\n second trace: \n" else #store a stack trace so that next time we have multiple client, we can alert about it... begin throw "just saving a stack trace" rescue => e ListenerMocker.tracker_of_callers[@jid.bare.to_s] = e end end ListenerMocker.mock_clients[@jid.bare.to_s] = self true end def send(xml, &block) Jabber::debuglog("(in mock) sending #{xml} #{xml.class}") if(xml.is_a?(Jabber::Message)) xml.from = @jid # unless xml.to # raise "no jid!" # end if ListenerMocker.mock_clients[xml.to.bare.to_s] Jabber::debuglog("(in mock) to #{xml.to.bare.to_s}") ListenerMocker.mock_clients[xml.to.bare.to_s].messagecbs.process(xml) else Jabber::debuglog("(in mock) no client listening as #{xml.to.bare.to_s}") end end end def is_connected? self.connected end end end def self.mock_out_all_connections Jabber::Reliable::Connection.class_eval(&Jabber::Test::ListenerMocker.mocker_proc) end def self.mock_out(listener) listener.instance_eval do class << self def setup_connection super @connection.instance_eval do class << self self.class_eval(&Jabber::Test::ListenerMocker.mocker_proc) end end end end end listener end end end endxmpp4r-0.5.6/lib/xmpp4r/observable/0000755000175000017500000000000013647121573020331 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/observable/helper.rb0000644000175000017500000002740413647121573022144 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # This is the helper for xmpp4r/observable, and was based on XMPP4R-Observable # by Pablo Lorenzoni . require 'time' require 'xmpp4r' require 'xmpp4r/roster' require 'xmpp4r/observable/contact' require 'xmpp4r/observable/pubsub' require 'xmpp4r/observable/subscription' module Jabber class NotConnected < StandardError; end #:nodoc: # Jabber::Observable - Creates observable Jabber Clients class Observable # This is what actually makes our object Observable. include ObservableThing attr_reader :subs, :pubsub, :jid, :auto # Create a new Jabber::Observable client. You will be automatically connected # to the Jabber server and your status message will be set to the string # passed in as the status_message argument. # # jabber = Jabber::Observable.new("me@example.com", "password", nil, "Talk to me - Please!") # # jid:: your jid (either a string or a JID object) # password:: your password # status:: your status. Check Jabber::Observable#status for documentation # status_message:: some funny string # host:: the server host (if different from the one in the jid) # port:: the server port (default: 5222) def initialize(jid, password, status=nil, status_message="Available", host=nil, port=5222) # Basic stuff @jid = jid.respond_to?(:resource) ? jid : Jabber::JID.new(jid) @password = password @host = host @port = port # Message dealing @delivered_messages = 0 @deferred_messages = Queue.new start_deferred_delivery_thread # Connection stuff @connect_mutex = Mutex.new @client = nil @roster = nil @disconnected = false # Tell everybody I am here status(status, status_message) # Subscription Accessor @subs = Jabber::Observable::Subscriptions.new(self) # PubSub Accessor @pubsub = Jabber::Observable::PubSub.new(self) # Auto Observer placeholder @auto = nil # Our contacts Hash @contacts = Hash.new end def inspect # :nodoc: sprintf("#<%s:0x%x @jid=%s, @delivered_messages=%d, @deferred_messages=%d, @observer_count=%s, @notification_count=%s, @pubsub=%s>", self.class.name, __id__, @jid, @delivered_messages, @deferred_messages.length, observer_count.inspect, notification_count.inspect, @pubsub.inspect) end # Count the registered observers in each thing def observer_count h = {} [ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing| h[thing] = count_observers(thing) end h end # Count the notifications really sent for each thing def notification_count h = {} [ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing| h[thing] = count_notifications(thing) end h end # Attach an auto-observer based on QObserver def attach_auto_observer raise StandardError, "Already attached." unless @auto.nil? @auto = QObserver.new [ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing| self.add_observer(thing, @auto) end end # Dettach the auto-observer def dettach_auto_observer raise StandardError, "Not attached." if @auto.nil? [ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing| self.delete_observer(thing, @auto) end @auto = nil end # Send a message to jabber user jid. # # jid:: jid of the recipient # message:: what is to be delivered (either a string or a Jabber::Message) # type:: can be either one of: # * :normal: a normal message. # * :chat (default): a one-to-one chat message. # * :groupchat: a group-chat message. # * :headline: a "headline" message. # * :error: an error message. # # If the recipient is not in your contacts list, the message will be queued # for later delivery, and the Contact will be automatically asked for # authorization (see Jabber::Observable#add). def deliver(jid, message, type=nil) contacts(jid).each do |contact| # Check if we're subscribed to contact if @subs.subscribed_to?(contact) # Yes! we are! if message.instance_of?(Jabber::Message) msg = message msg.to = contact.jid msg.type = type unless type.nil? # Let's keep the Jabber::Message type unless passed else msg = Jabber::Message.new(contact.jid) msg.body = message msg.type = type.nil? ? :chat : type end @delivered_messages += 1 send!(msg) else # No... Let's add it and defer the delivery. @subs.add(contact.jid) deliver_deferred(contact.jid, message, type) end end end # Set your presence, with a message. # # presence:: any of these: # * nil: online. # * :chat: free for chat. # * :away: away from the computer. # * :dnd: do not disturb. # * :xa: extended away. # message:: a string that you wish your contacts see when you change your presence. def status(presence, message) @status_message = message @presence = presence send!(Jabber::Presence.new(@presence, @status_message)) end # Transform a passed list of contacts in one or more Jabber::Observable::Contact objects. # # contact:: one of more jids of contacts def contacts(*contact) ret = [] contact.each do |c| jid = c.to_s # Do we already have it? if ! @contacts.include?(jid) # Nope. @contacts[jid] = c.instance_of?(Jabber::Observable::Contact) ? c : Jabber::Observable::Contact.new(c, self) end ret << @contacts[jid] end ret end # Returns true if the Jabber client is connected to the Jabber server, # false otherwise. def connected? @client.respond_to?(:is_connected?) && @client.is_connected? end # Pass the underlying Roster helper. def roster return @roster unless @roster.nil? @roster = Jabber::Roster::Helper.new(client) end # Pass the underlying Jabber client. def client connect! unless connected? @client end # Send a Jabber stanza over-the-wire. # # msg:: the stanza to be sent. def send!(msg) retries = 0 max = 4 begin retries += 1 client.send(msg) rescue Errno::ECONNRESET => e # Connection reset. Sleep progressively and retry until success or exhaustion. sleep ((retries^2) * 60) + 30 disconnect reconnect retry unless retries > max raise e rescue Errno::EPIPE, IOError => e # Some minor error. Sleep 2 seconds and retry until success or exhaustion. sleep 2 disconnect reconnect retry unless retries > max raise e end end # Use this to force the client to reconnect after a disconnect. def reconnect @disconnected = false connect! end # Use this to force the client to disconnect and not automatically # reconnect. def disconnect disconnect!(false) end # Queue messages for delivery once a user has accepted our authorization # request. Works in conjunction with the deferred delivery thread. # # You can use this method if you want to manually add friends and still # have the message queued for later delivery. def deliver_deferred(jid, message, type) msg = {:to => jid, :message => message, :type => type, :time => Time.now} @deferred_messages.enq msg end # Sets the maximum time to wait for a message to be delivered (in # seconds). It will be removed of the queue afterwards. def deferred_max_wait=(seconds) @deferred_max_wait = seconds end # Get the maximum time to wait for a message to be delivered. Default: 600 # seconds (10 minutes). def deferred_max_wait @deferred_max_wait || 600 end private # Create the infrastructure for connection and do it. # # Note that we use a Mutex to prevent double connection attempts and will # raise a SecurityError if that happens. def connect! raise RuntimeError, "Connections disabled - use Jabber::Observable::reconnect() to reconnect." if @disconnected raise SecurityError, "Connection attempt while already trying to connect!" if @connect_mutex.locked? @connect_mutex.synchronize do # Assure we're not connected. disconnect!(false) if connected? # Connection jid = Jabber::JID.new(@jid) my_client = Jabber::Client.new(jid) my_client.connect(@host, @port) my_client.auth(@password) @roster = nil @client = my_client # Post-connect register_default_callbacks status(@presence, @status_message) if @pubsub.nil? @pubsub = Jabber::Observable::PubSub.new(self) else @pubsub.attach! end end end # Really disconnect the client # # auto_reconnect:: Sets the flag for auto-reconnection def disconnect!(auto_reconnect = true) if connected? client.close end @roster = nil @client = nil @pubsub.set_service(nil) @disconnected = auto_reconnect end # This will register callbacks for every "thing" we made observable. # # The observable things we register here are :message, :presence, :iq, # :new_subscription, and :subscription_request # # Note we can also observe :event, but that is dealt with in # Jabber::Observable::PubSub def register_default_callbacks # The three basic "things": :message, :presence and :iq # (note that :presence is based on roster) client.add_message_callback do |message| unless message.body.nil? changed(:message) notify_observers(:message, message) end end roster.add_presence_callback do |roster_item, old_presence, new_presence| simple_jid = roster_item.jid.strip.to_s presence = case new_presence.type when nil then new_presence.show || :online when :unavailable then :unavailable else nil end changed(:presence) notify_observers(:presence, simple_jid, presence, new_presence) end client.add_iq_callback do |iq| changed(:iq) notify_observers(:iq, iq) end # We'll also expose roster's :new_subscription and :subscription_request roster.add_subscription_callback do |roster_item, presence| if presence.type == :subscribed changed(:new_subscription) notify_observers(:new_subscription, [roster_item, presence]) end end roster.add_subscription_request_callback do |roster_item, presence| roster.accept_subscription(presence.from) if @subs.accept? changed(:subscription_request) notify_observers(:subscription_request, [roster_item, presence]) end end # Starts the deferred delivery thread # # This will monitor the @deferred_messages queue and try to deliver messages. def start_deferred_delivery_thread return if ! @deferred_delivery_thread.nil? and @deferred_delivery_thread.alive? @deferred_delivery_thread = Thread.new do loop do # Check the queue every 10 seconds. Effectivelly block if nothing is queued. sleep 10 while @deferred_messages.empty? # Hm... something has been queued message = @deferred_messages.deq if @subs.subscribed_to?(message[:to]) # Great! We're subscribed! deliver(message[:to], message[:message], message[:type]) else # Still not subscribed. Enqueue it again (unless deferred_max_wait was reached) @deferred_messages.enq message unless Time.now > (deferred_max_wait + message[:time]) end # Wait 5 seconds between every message still in the queue sleep 5 end end end end # of class Observable end # of module Jabber xmpp4r-0.5.6/lib/xmpp4r/observable/pubsub.rb0000644000175000017500000001373313647121573022165 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/pubsub' require 'xmpp4r/pubsub/helper/servicehelper' require 'xmpp4r/pubsub/helper/nodebrowser' require 'xmpp4r/pubsub/helper/nodehelper' module Jabber class Observable # Jabber::Observable::PubSub - Convenience subclass to deal with PubSub class PubSub class NoService < StandardError; end #:nodoc: class AlreadySet < StandardError; end #:nodoc: # Creates a new PubSub object # # observable:: points a Jabber::Observable object def initialize(observable) @observable = observable @helper = @service_jid = nil @disco = Jabber::Discovery::Helper.new(@observable.client) attach! end def attach! begin domain = Jabber::JID.new(@observable.jid).domain @service_jid = "pubsub." + domain set_service(@service_jid) rescue @helper = @service_jid = nil end return has_service? end def inspect #:nodoc: if has_service? sprintf("#<%s:0x%x @service_jid=%s>", self.class.name, __id__, @service_jid) else sprintf("#<%s:0x%x @has_service?=false>", self.class.name, __id__) end end # Checks if the PubSub service is set def has_service? ! @helper.nil? end # Sets the PubSub service. Just one service is allowed. If nil, reset. def set_service(service) if service.nil? @helper = @service_jid = nil else raise NotConnected, "You are not connected" if ! @observable.connected? raise AlreadySet, "You already have a PubSub service (#{@service_jid})." if has_service? @helper = Jabber::PubSub::ServiceHelper.new(@observable.client, service) @service_jid = service @helper.add_event_callback do |event| @observable.changed(:event) @observable.notify_observers(:event, event) end end end # Subscribe to a node. def subscribe_to(node) raise_noservice if ! has_service? @helper.subscribe_to(node) unless is_subscribed_to?(node) end # Unsubscribe from a node. def unsubscribe_from(node) raise_noservice if ! has_service? @helper.unsubscribe_from(node) end # Return the subscriptions we have in the configured PubSub service. def subscriptions raise_noservice if ! has_service? @helper.get_subscriptions_from_all_nodes() end # Create a PubSub node (Lots of options still have to be encoded!) def create_node(node) raise_noservice if ! has_service? begin @helper.create_node(node) rescue => e raise e return nil end @my_nodes << node if defined? @my_nodes node end # Return an array of nodes I own def my_nodes if ! defined? @my_nodes ret = [] subscriptions.each do |sub| ret << sub.node if sub.attributes['affiliation'] == 'owner' end @my_nodes = ret end return @my_nodes end # Return true if a given node exists def node_exists?(node) ret = [] if ! defined? @existing_nodes or ! @existing_nodes.include?(node) # We'll renew @existing_nodes if we haven't got it the first time reply = @disco.get_items_for(@service_jid) reply.items.each do |item| ret << item.node end @existing_nodes = ret end return @existing_nodes.include?(node) end # Returns an array of nodes I am subscribed to def subscribed_nodes ret = [] subscriptions.each do |sub| next if sub.node.nil? ret << sub.node if sub.attributes['subscription'] == 'subscribed' and ! my_nodes.include?(sub.node) end return ret end # Return true if we're subscribed to that node def is_subscribed_to?(node) ret = false subscriptions.each do |sub| ret = true if sub.node == node and sub.attributes['subscription'] == 'subscribed' end return ret end # Delete a PubSub node (Lots of options still have to be encoded!) def delete_node(node) raise_noservice if ! has_service? begin @helper.delete_node(node) rescue => e raise e return nil end @my_nodes.delete(node) if defined? @my_nodes node end # Publish an Item. This infers an item of Jabber::PubSub::Item kind is passed def publish_item(node, item) raise_noservice if ! has_service? @helper.publish_item_to(node, item) end # Publish Simple Item. This is an item with one element and some text to it. def publish_simple_item(node, text) raise_noservice if ! has_service? item = Jabber::PubSub::Item.new xml = REXML::Element.new('value') xml.text = text item.add(xml) publish_item(node, item) end # Publish atom Item. This is an item with one atom entry with title, body and time. def publish_atom_item(node, title, body, time = Time.now) raise_noservice if ! has_service? item = Jabber::PubSub::Item.new entry = REXML::Element.new('entry') entry.add_namespace("http://www.w3.org/2005/Atom") mytitle = REXML::Element.new('title') mytitle.text = title entry.add(mytitle) mybody = REXML::Element.new('body') mybody.text = body entry.add(mybody) published = REXML::Element.new("published") published.text = time.utc.iso8601 entry.add(published) item.add(entry) publish_item(node, item) end # Get items from a node def get_items_from(node, count = nil) raise_noservice if ! has_service? @helper.get_items_from(node, count) end private def raise_noservice #:nodoc: raise NoService, "Have you forgot to call #set_service ?" end end # of class PubSub end # of class Observable end # of module Jabber xmpp4r-0.5.6/lib/xmpp4r/observable/contact.rb0000644000175000017500000000302613647121573022312 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # module Jabber class Observable # Jabber::Observable::Contact - Convenience subclass to deal with Contacts class Contact # Creates a new Jabber::Observable::Contact # # jid:: jid to be used # observable:: observable to be used def initialize(jid, observable) @jid = jid.respond_to?(:resource) ? jid : JID.new(jid) @observable = observable end # Returns the stripped version of the JID def jid; @jid.strip; end def inspect #:nodoc: sprintf("#<%s:0x%x @jid=%s>", self.class.name, __id__, @jid.to_s) end # Are e subscribed? def subscribed? [:to, :both].include?(subscription) end # Get the subscription from the roster_item def subscription items = @observable.roster.items return false unless items.include?(jid) items[jid].subscription end # Send a request asking for authorization def ask_for_authorization! request!(:subscribe) end # Sends a request asking for unsubscription def unsubscribe! request!(:unsubscribe) end private # Really send the request. def request!(type) request = Jabber::Presence.new.set_type(type) request.to = jid @observable.send!(request) end end # of class Contact end # of class Observable end # of module Jabber xmpp4r-0.5.6/lib/xmpp4r/observable/observable_thing.rb0000644000175000017500000001163413647121573024200 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # This was based on Observable module from Ruby. module ObservableThing require 'xmpp4r/observable/thread_store' # Adds an observer for some "thing". # # thing:: what will be observed. # observer:: the observer. # func:: the observer method that will be called (default: :update). def add_observer(thing, observer, func = :update) @things = {} unless defined? @things @things[thing] = {} unless ! @things[thing].nil? unless observer.respond_to? func raise NoMethodError, "observer does not respond to `#{func.to_s}'" end @things[thing][observer] = func unless @things[thing].include?(observer) end # Deletes an observer for some "thing". # # thing:: what has been observed. # observer:: the observer. def delete_observer(thing, observer) @things[thing].delete observer if defined? @things and ! @things[thing].nil? end # Delete observers for some "thing". # # thing:: what has been observed (if nil, deletes all observers). def delete_observers(thing = nil) if thing.nil? @things.clear if defined? @things else @things[thing].clear if defined? @things and ! @things[thing].nil? end end # Count the number of observers for some "thing". # # thing:: what has been observed (if nil, count all observers). def count_observers(thing = nil) return 0 if ! defined? @things size = 0 if thing.nil? @things.each { |thing, hash| size += hash.size } else size = @things[thing].size unless @things[thing].nil? end size end # Count the number of notifications for some "thing". # # thing:: what has been observed. def count_notifications(thing) return 0 if (! defined?(@things_counter)) or (! @things_counter.include?(thing)) @things_counter[thing] end # Change the state of some "thing". # # thing:: what will have the state changed. # state:: the state (default = true). def changed(thing, state = true) @things_state = {} unless defined? @things_state @things_state[thing] = state end # Check the state of some "thing". # # thing: what to have its state checked. def changed?(thing) if defined? @things_state and @things_state[thing] true else false end end # Notify all observers of "thing" about something. This will only be # enforced if the state of that "thing" is true. Also, if the observer # returns the Symbol :delete_me, it will be deleted after being notified. # # thing:: what has been observed. # args:: notification to be sent to the observers of "thing". def notify_observers(thing, *arg) if changed?(thing) if defined? @things and ! @things[thing].nil? @things[thing].each { |observer, func| increase_counter(thing) @thread_store = ThreadStore.new if ! defined? @thread_store @thread_store.add Thread.new { if observer.send(func, thing, *arg) == :delete_me delete_observer(thing, observer) end } } end changed(thing, false) end end # Is there pending notifications? def pending_notifications? return false if ! defined? @thread_store @thread_store.size > 0 end # Wait all notifications def wait_notifications sleep 1 while pending_notifications? end private def increase_counter(thing) @things_counter = {} unless defined? @things_counter @things_counter[thing] = 0 unless @things_counter.include?(thing) @things_counter[thing] += 1 end end # QObserver - simple observer-to-queue class class QObserver require 'thread' def initialize @queues = Hash.new end def inspect h = {} @queues.each do |q, queue| h[q] = queue.size end if h.length > 0 sprintf("#<%s:0x%x size=%s>", self.class.name, __id__, h.inspect) else sprintf("#<%s:0x%x>", self.class.name, __id__) end end # Return the queues we have registered def queues @queues.keys end # Received something in this queue? # # q:: queue def received?(q) @queues.include?(q) and ! @queues[q].empty? end # Get the contents of the queue in an array (or pass each item to the # given block. # # q:: queue def received(q) return nil if ! @queues.include?(q) if block_given? yield @queues[q].deq while ! @queues[q].empty? else a = [] a << @queues[q].deq while ! @queues[q].empty? return a end end # Get the size of a given queue # # q:: queue def size(q) @queues[q].size rescue 0 end # update method for our Observer # # thing:: what to be updated def update(thing, *args) @queues[thing] = Queue.new if ! @queues.include?(thing) @queues[thing].enq args end end xmpp4r-0.5.6/lib/xmpp4r/observable/subscription.rb0000644000175000017500000000340213647121573023401 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber class Observable # Jabber::Observable::Subscriptions - convenience class to deal with # Presence subscriptions # # observable:: points to a Jabber::Observable object class Subscriptions def initialize(observable) @observable = observable @accept = true end # Ask the users specified by jids for authorization (i.e., ask them to add # you to their contact list), unless already in the contact list. # # Because the authorization process depends on the other user accepting your # request, results are notified to observers of :new_subscription. def add(*jids) @observable.contacts(*jids).each do |contact| next if subscribed_to?(contact) contact.ask_for_authorization! end end # Remove the jabber users specified by jids from the contact list. def remove(*jids) @observable.contacts(*jids).each do |contact| contact.unsubscribe! end end # Returns true if this Jabber account is subscribed to status updates for # the jabber user jid, false otherwise. def subscribed_to?(jid) @observable.contacts(jid).each do |contact| return contact.subscribed? end end # Returns true if auto-accept subscriptions is enabled (default), false otherwise. def accept? @accept end # Change whether or not subscriptions are automatically accepted. def accept=(accept_status) @accept = accept_status end end # of class Subscription end # of class Observable end # of module Jabber xmpp4r-0.5.6/lib/xmpp4r/observable/thread_store.rb0000644000175000017500000000327713647121573023352 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # # This file's copyright (c) 2009 by Pablo Lorenzzoni # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io # # Class ThreadStore class ThreadStore attr_reader :cycles, :max # Create a new ThreadStore. # # max:: max number of threads to store (when exhausted, older threads will # just be killed and discarded). Default is 100. If 0 or negative no # threads will be discarded until #keep is called. def initialize(max = 100) @store = [] @max = max > 0 ? max : 0 @cycles = 0 @killer_thread = Thread.new do loop do sleep 2 while @store.empty? sleep 1 @store.each_with_index do |thread, i| th = @store.delete_at(i) if thread.nil? or ! thread.alive? th = nil end @cycles += 1 end end end def inspect # :nodoc: sprintf("#<%s:0x%x @max=%d, @size=%d @cycles=%d>", self.class.name, __id__, @max, size, @cycles) end # Add a new thread to the ThreadStore def add(thread) if thread.instance_of?(Thread) and thread.respond_to?(:alive?) @store << thread keep(@max) if @max > 0 end end # Keep only the number passed of threads # # n:: number of threads to keep (default to @max if @max > 0) def keep(n = nil) if n.nil? raise ArgumentError, "You need to pass the number of threads to keep!" if @max == 0 n = @max end @store.shift.kill while @store.length > n end # Kill all threads def kill! @store.shift.kill while @store.length > 0 end # How long is our ThreadStore def size; @store.length; end end # of class ThreadStore xmpp4r-0.5.6/lib/xmpp4r/tune/0000755000175000017500000000000013647121573017160 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/tune/helper/0000755000175000017500000000000013647121573020437 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/lib/xmpp4r/tune/helper/helper.rb0000644000175000017500000000312513647121573022244 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/pubsub' require 'xmpp4r/tune/tune' module Jabber module UserTune ## # A Helper for XEP-0118 User Tune # # Use this helper to send user tunes, or receive them from a # specified jid. Described at http://www.xmpp.org/extensions/xep-0118.html # # For example: #
    # h = UserTune::Helper( @client, 'radio1@hug.hellomatty.com' )
    # h.add_usertune_callback do |tune|
    #   puts "Now playing: #{tune.title} by #{tune.artist}"
    # end
    # 
# # Also see the examples provided. class Helper < PubSub::ServiceHelper ## # Send out the tune currently playing. # # track:: [Jabber::UserTune::Tune] the tune currently playing def now_playing(track) item = Jabber::PubSub::Item.new() item.add(track) publish_item_to(NS_USERTUNE, item) end ## # Use this method to indicate that you have stopped playing # a tune. def stop_playing now_playing(Jabber::UserTune::Tune.new()) end ## # Add a callback that will be invoked when a tune is received # from the jid specified when you constructed the Helper. def add_usertune_callback(prio = 200, ref = nil, &block) add_event_callback(prio, ref) do |event| tune = event.first_element('items/item/tune') if tune block.call(tune) end end end end end end xmpp4r-0.5.6/lib/xmpp4r/tune/tune.rb0000644000175000017500000000721413647121573020464 0ustar debbiecocoadebbiecocoa# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r/xmppelement' require 'rexml/element' module Jabber module UserTune NS_USERTUNE = 'http://jabber.org/protocol/tune' ## # The XMPP element, as defined in XEP-0118 User Tune # # See http://www.xmpp.org/extensions/xep-0118.html - this element # encapsulates metadata (artist, track etc) about a tune the user # is currently playing. These are expressed as child elements # such as , etc which are also managed by this class. # # If the element has no children then it indicates that the user # has stopped playing a tune. Use the Tune#playing? method to discover # this? class Tune < XMPPElement name_xmlns 'tune', NS_USERTUNE force_xmlns true ## # Construct a new <tune> element. # # Supply no arguments to make an # empty element to indicate that tune playing has stopped. # # artist:: [String] the artist or performer of the song or piece # title:: [String] the title of the song or piece # length:: [Fixnum] the duration of the song or piece in seconds # track:: [String] a unique identifier for the tune; e.g., the track number within a collection or the specific URI for the object (e.g., a stream or audio file) # source:: [String] the collection (e.g., album) or other source (e.g., a band website that hosts streams or audio files) # uri:: [String] a URI or URL pointing to information about the song, collection, or artist # rating:: [Numeric] a number indicating how much you like this song - will be clamped into an integer 0 <= x <= 10 def initialize(artist = nil, title = nil, length = nil, track = nil, source = nil, uri = nil, rating = nil) super() add_element(REXML::Element.new('artist')).text = artist if artist add_element(REXML::Element.new('title')).text = title if title add_element(REXML::Element.new('length')).text = length.to_s if length add_element(REXML::Element.new('track')).text = track if track add_element(REXML::Element.new('source')).text = source if source add_element(REXML::Element.new('uri')).text = uri if uri if rating and rating.kind_of?(Numeric) r = rating.to_i r = 0 if r < 0 r = 10 if r > 10 add_element(REXML::Element.new('rating')).text = r.to_s end end ## # Returns true if a tune is currently playing, otherwise false. def playing? (elements.size > 0) end ## # Get the artist for this tune. def artist first_element('artist').text if first_element('artist') end ## # Get the title of this tune. def title first_element('title').text if first_element('title') end ## # Get the length of this tune, in seconds. def length first_element('length').text.to_i if first_element('length') end ## # Get an identitier for this tune. def track first_element('track').text if first_element('track') end ## # Get the source of this tune, such as an album. def source first_element('source').text if first_element('source') end ## # Get a URI that represents this tune. def uri first_element('uri').text if first_element('uri') end ## # Get the rating for this track def rating first_element('rating').text.to_i if first_element('rating') end end end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/lib/xmpp4r/location/�������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�020015� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/lib/xmpp4r/location/helper/������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�021274� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/lib/xmpp4r/location/helper/helper.rb���������������������������������������������������0000644�0001750�0001750�00000003165�13647121573�023105� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'xmpp4r' require 'xmpp4r/pubsub' require 'xmpp4r/location/location' module Jabber module UserLocation ## # A Helper for XEP-0080 User Location # # Use this helper to send a user's location, or receive them from a # specified jid. Described at http://www.xmpp.org/extensions/xep-0080.html # # For example: # <pre> # h = UserLocation::Helper( @client, 'radio1@hug.hellomatty.com' ) # h.add_userlocation_callback do |location| # puts "Now in: #{location.locality}" # end # </pre> class Helper < PubSub::ServiceHelper ## # Send out the current location. # # location:: [Jabber::UserLocation::Location] current_location def current_location(location) item = Jabber::PubSub::Item.new() item.add(location) publish_item_to(NS_USERLOCATION, item) end ## # Use this method to indicate that you wish to stop publishing # a location. def stop_publishing current_location(Jabber::UserLocation::Location.new()) end ## # Add a callback that will be invoked when a location is received # from the jid specified when you constructed the Helper. def add_userlocation_callback(prio = 200, ref = nil, &block) add_event_callback(prio, ref) do |event| location = event.first_element('items/item/location') if location block.call(location) end end end end end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/lib/xmpp4r/location/location.rb��������������������������������������������������������0000644�0001750�0001750�00000011345�13647121573�022156� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io require 'time' require 'xmpp4r/xmppelement' require 'rexml/element' module Jabber module UserLocation NS_USERLOCATION = 'http://jabber.org/protocol/geoloc' ALLOWABLE_ATTRIBUTES = %w(accuracy alt area bearing building country datum description floor lat locality lon postalcode region room speed street text timestamp uri) ## # The <geoloc> XMPP element, as defined in XEP-0080 User Location # # See http://xmpp.org/extensions/xep-0080.html - this element # encapsulates data about a user's current location. These are # expressed as child elements such as <locality>, <lat>, etc. # which are also managed by this class. # # If the element has no children then it indicates that the user # has stopped publishing their location. class Location < XMPPElement name_xmlns 'geoloc', NS_USERLOCATION force_xmlns true ## # Construct a new <location> element. # # Supply no arguments to make an empty element to indicate that # location is no longer being published. # # attributes:: [Hash] location attributes def initialize(attributes = {}) super() # validate attributes attributes = attributes.select do |k,v| ALLOWABLE_ATTRIBUTES.include?(k) && !v.nil? end attributes.each do |k,v| v = x.xmlschema if v.is_a?(Time) add_element(REXML::Element.new(k)).text = v.to_s end end ## # Returns true if a location is currently being published, otherwise false. def published? (elements.size > 0) end ## # Get the accuracy attribute of this location. def accuracy first_element('accuracy').text if first_element('accuracy') end ## # Get the alt attribute of this location. def alt first_element('alt').text if first_element('alt') end ## # Get the area attribute of this location. def area first_element('area').text if first_element('area') end ## # Get the bearing attribute of this location. def bearing first_element('bearing').text if first_element('bearing') end ## # Get the building attribute of this location. def building first_element('building').text if first_element('building') end ## # Get the country attribute of this location. def country first_element('country').text if first_element('country') end ## # Get the datum attribute of this location. def datum first_element('datum').text if first_element('datum') end ## # Get the description attribute of this location. def description first_element('description').text if first_element('description') end ## # Get the floor attribute of this location. def floor first_element('floor').text if first_element('floor') end ## # Get the lat attribute of this location. def lat first_element('lat').text if first_element('lat') end ## # Get the locality attribute of this location. def locality first_element('locality').text if first_element('locality') end ## # Get the lon attribute of this location. def lon first_element('lon').text if first_element('lon') end ## # Get the postalcode attribute of this location. def postalcode first_element('postalcode').text if first_element('postalcode') end ## # Get the region attribute of this location. def region first_element('region').text if first_element('region') end ## # Get the room attribute of this location. def room first_element('room').text if first_element('room') end ## # Get the speed attribute of this location. def speed first_element('speed').text if first_element('speed') end ## # Get the street attribute of this location. def street first_element('street').text if first_element('street') end ## # Get the text attribute of this location. def text first_element('text').text if first_element('text') end ## # Get the timestamp attribute of this location. def timestamp first_element('timestamp').text if first_element('timestamp') end ## # Get the uri attribute of this location. def uri first_element('uri').text if first_element('uri') end end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/tools/���������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�015345� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/tools/gen_requires.bash����������������������������������������������������������������0000755�0001750�0001750�00000001502�13647121573�020675� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # Generate a full graph of the requires in the Ruby files of this library. # # Output : The graph is generated as a .png file in the same dir as this script. # # This script requires the following tools to be installed: # # * mktemp # * dot (Try 'sudo port install graphviz' on OS X) export TMPDIR='/tmp' export TMPFILE=$(mktemp $TMPDIR/gen_requires.XXXXXX) export OUTFILE='gen_requires.png' export SELFDIR=`pwd` export LIBDIR=$SELFDIR/../lib ####################################### # Unlikely you need to edit below here ####################################### cd $LIBDIR echo "strict digraph requirestree { " > $TMPFILE grep -r "^require " * | grep -v swp | sed "s/^\(.*\).rb:require '\(.*\)'/\1 -> \2;/" | sed 's/\//_/g' >> $TMPFILE echo "}" >> $TMPFILE cd $SELFDIR dot -Tpng $TMPFILE -o $OUTFILE rm -f $TMPFILE ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/tools/xmpp4r-gemspec-test.rb�����������������������������������������������������������0000755�0001750�0001750�00000000435�13647121573�021527� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env ruby # Since GitHub doesn't allow unsafe commands in a gemspec this script # will simulate running in their environment. require 'rubygems/specification' data = File.read('../xmpp4r.gemspec') spec = nil Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join puts spec �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/CHANGELOG������������������������������������������������������������������������������0000644�0001750�0001750�00000011506�13647121573�015422� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ xmpp4r 0.5.6 (February 14th, 2013) ==================================== * [DOC] Wrong inline documentation in `client.rb`; details of port number parameter and removal of un-used 'use_ssl parameter' * Prevent REXML exceptions on UTF-8 messages * Fixed broken parse method * Make sure to open files in binary mode, in order to correctly transfer files on Windows * Prevent REXML exceptions on UTF-8 messages * Better control of history on muc.join xmpp4r 0.5.5 (October 6th, 2013) =============================== * Removed unnecessary dependency on pry. * Merge pull request #1 from Strech/patch-1 * Non-bosh connection flushes correctly * Elaborated debug messages in HTTPBinding::Client * Modified to work with BOSH with HTTPBinding::Client - Support stream restart with urn:xmpp:xbosh way. - Removed unnecessary wait until authenticated. - Added Semaphore to wait for auth and bind response. - Hook to setup SSL flags (certificate, etc.) - Adjusted HTTP::Net read_timeout according to http_wait. - Proxy handling. - Respect poll/request BOSH parameters to avoid overactivity. XMPP4R 0.5 (15/06/2009) ======================= * Many bugs fixed and tests cleanups (better Ruby 1.9 support, in particular) * Support for reliable connections (that auto-reconnect when being disconnected, without losing messages) * Better notification of server disconnects XMPP4R 0.4 (05/08/2008) ======================= * Initial support for Ruby 1.9 (see README_ruby19.txt) * Complete PubSub API Change - more logical and better for childclasses, support for collection node creation * a Helper to assist with XEP-0115 Entity Capabilities * SASL anonymous support * File transfer fixes * MUC room configuration fixes * initial support for XEP-0118 User Tune * fix for an xmlrpc exception-during-serialisation bug, which would cause a hang * Support auto-gem generation on GitHub with improved and DRY'er RakeFile and gemspec. * Add support for the old SSL protocol (needed to connect to GTalk) * Changed API for Client, Component, Connection, Stream to remove need for antiquated 'threaded' param in the initializer. * Use a Logger instance instead of directly writing to stdout * Re-factored & consolidated Error classes. See xmpp4r/errors.rb for all custom errors that can be caught. All inherit from Jabber::Error which itself inherits from Ruby's StandardError. This is a first step in re-factoring errors. The next step will be to convert all 'raise' calls to raise a custom Jabber::Error or one of its children instead of anonymous RuntimeErrors. This allows much more granularity in catching and handling errors later. If you were catching Jabber::ErrorException before you should probably change that in your code to now catch Jabber::Error if you want to catch everything or one of the custom children of Jabber::Error defined in 'lib/xmpp4r/errors.rb'. Additionally, the Error class which encapsulated the xmpp error response, has been renamed to ErrorResponse to reflect its real usage. This free's up 'Jabber::Error' for use as our base Error class. XMPP4R 0.3.2 (15/10/2007) ========================= * Serious bug involving Ruby threading fixed (caused exceptions with ruby 1.8.6) * vCard helper fixes * Jabber RPC (JEP0009) support * HTTP Binding (JEP0124) support * Publish-Subscribe support * XMPPElement: a framework for classes representing XML elements * Ad-hoc commands support * Improvements to Dataforms: XData, XDataTitle and XDataInstructions XMPP4R 0.3.1 (23/04/2007) ========================= * SASL fixes * Message#x and Presence#x support element selection by namespace * Proper XML entity escaping for REXML text nodes * Improvements to FileTransfer::Helper and SOCKS5BytestreamsServer * Vcard::Helper fixes * Update Digest module usage to reflect recent Ruby versions * More documentation XMPP4R 0.3 (20/07/2006) ======================= * SRV lookup capability in Client#connect * Stringprep support for JIDs * TLS & SASL support * Basic Dataforms support * Multi-User Chat Helpers * Helpers for File-Transfer, SOCKS5 Bytestreams, In-Band Bytestreams * Roster helper has modified subscription-request semantics (see Roster#add_subscription_request_callback) * A lot of features have renamed namespaces (see UPDATING file) XMPP4R 0.2 (20/10/2005) ======================= * Workarounds for REXML bugs. * Presences are now Comparable according to priority or interest * fixed a serious bug in Stream#send which caused some lockups. Reported by chunlinyao@gmail.com. * Moved REXML::Element#add to REXML::Element#typed_add to keep add accessible * Rewritten Roster helper * Added Vcard helper and improved IqVcard * XMLStanza id generator * Support for Roster Item Exchange (JEP-0093 and JEP-0144) * Client#password= to change client's account password * Documentation fixes XMPP4R 0.1 (12/09/2005) ======================= * first public release. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/Rakefile�������������������������������������������������������������������������������0000644�0001750�0001750�00000017230�13647121573�015655� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������require 'rake' require "rake/clean" require 'rake/testtask' require 'rdoc/task' $:.unshift 'lib' require "xmpp4r" ############################################################################## # OPTIONS ############################################################################## PKG_NAME = 'xmpp4r' PKG_VERSION = Jabber::XMPP4R_VERSION AUTHORS = ['Lucas Nussbaum', 'Stephan Maka', 'Glenn Rempe', 'Kaoru Maeda', 'Harisankar P S'] EMAIL = "mailme@hsps.in" HOMEPAGE = "http://xmpp4r.github.io" SUMMARY = "XMPP/Jabber library for ruby" # These are the common rdoc options that are shared between generation of # rdoc files using BOTH 'rake rdoc' and the installation by users of a # RubyGem version which builds rdoc's along with its installation. Any # rdoc options that are ONLY for developers running 'rake rdoc' should be # added in the 'RDoc::Task' block below. RDOC_OPTIONS = [ "--quiet", "--title", SUMMARY, "--opname", "index.html", "--main", "lib/xmpp4r.rb", "--line-numbers", "--inline-source" ] # Extra files outside of the lib dir that should be included with the rdocs. RDOC_FILES = (%w( README.rdoc README_ruby19.txt CHANGELOG LICENSE COPYING )).sort # The full file list used for rdocs, tarballs, gems, and for generating the xmpp4r.gemspec. PKG_FILES = (%w( Rakefile setup.rb xmpp4r.gemspec ) + RDOC_FILES + Dir["{lib,test,data,tools}/**/*"]).sort ############################################################################## # DEFAULT TASK ############################################################################## # The task that will run when a simple 'rake' command is issued. # Default to running the test suite as that's a nice safe command # we should run frequently. task :default => [:test] ############################################################################## # TESTING TASKS ############################################################################## Rake::TestTask.new do |t| t.libs << "test" t.test_files = ['test/ts_xmpp4r.rb'] end ############################################################################## # DOCUMENTATION TASKS ############################################################################## # RDOC ####### RDoc::Task.new do |rd| # which dir should rdoc files be installed in? rd.rdoc_dir = 'rdoc' # the full list of files to be included rd.rdoc_files.include(RDOC_FILES, "lib/**/*.rb") # the full list of options that are common between gem build # and 'rake rdoc' build of docs. rd.options = RDOC_OPTIONS # Devs Only : Uncomment to also document private methods in the rdocs # Please don't check this change in to the source repo. #rd.options << '--all' # Devs Only : Uncomment to generate dot (graphviz) diagrams along with rdocs. # This requires that graphiz (dot) be installed as a local binary and on your path. # See : http://www.graphviz.org/ # Please don't check this change in to the source repo as it introduces a binary dependency. #rd.options << '--diagram' #rd.options << '--fileboxes' end # RCOV ####### # Conditional require rcov/rcovtask if present begin require 'rcov/rcovtask' Rcov::RcovTask.new do |t| t.test_files = ['test/ts_xmpp4r.rb'] t.output_dir = "coverage" end rescue LoadError end # DOT GRAPH ############ desc "Generate requires graph" task :gen_requires_graph do sh %{cd tools; ./gen_requires.bash} end ############################################################################## # SYNTAX CHECKING ############################################################################## desc "Check syntax of all Ruby files." task :check_syntax do `find . -name "*.rb" |xargs -n1 ruby -c |grep -v "Syntax OK"` puts "* Done" end ############################################################################## # PACKAGING & INSTALLATION ############################################################################## # What files/dirs should 'rake clean' remove? CLEAN.include ["*.gem", "pkg", "rdoc", "coverage", "tools/*.png"] begin require 'rubygems/package_task' spec = Gem::Specification.new do |s| s.name = PKG_NAME s.version = PKG_VERSION s.authors = AUTHORS s.email = EMAIL s.homepage = HOMEPAGE s.rubyforge_project = PKG_NAME s.summary = SUMMARY s.description = s.summary s.platform = Gem::Platform::RUBY s.require_path = 'lib' s.executables = [] s.files = PKG_FILES s.test_files = [] s.has_rdoc = true s.extra_rdoc_files = RDOC_FILES s.rdoc_options = RDOC_OPTIONS s.required_ruby_version = ">= 1.8.4" end Gem::PackageTask.new(spec) do |pkg| pkg.gem_spec = spec pkg.need_tar = true pkg.need_zip = true end namespace :gem do desc "Run :package and install the .gem locally" task :install => [:update_gemspec, :package] do sh %{sudo gem install --local pkg/#{PKG_NAME}-#{PKG_VERSION}.gem} end desc "Like gem:install but without ri or rdocs" task :install_fast => [:update_gemspec, :package] do sh %{sudo gem install --local pkg/#{PKG_NAME}-#{PKG_VERSION}.gem --no-rdoc --no-ri} end desc "Run :clean and uninstall the .gem" task :uninstall => :clean do sh %{sudo gem uninstall #{PKG_NAME}} end # Thanks to the Merb project for this code. desc "Update Github Gemspec" task :update_gemspec do skip_fields = %w(new_platform original_platform date cache_dir cache_file loaded) result = "# WARNING : RAKE AUTO-GENERATED FILE. DO NOT MANUALLY EDIT!\n" result << "# RUN : 'rake gem:update_gemspec'\n\n" result << "Gem::Specification.new do |s|\n" spec.instance_variables.sort.each do |ivar| value = spec.instance_variable_get(ivar) name = ivar.to_s.split("@").last next if skip_fields.include?(name) || value.nil? || value == "" || (value.respond_to?(:empty?) && value.empty?) if name == "dependencies" value.each do |d| dep, *ver = d.to_s.split(" ") result << " s.add_dependency #{dep.inspect}, #{ver.join(" ").inspect.gsub(/[()]/, "")}\n" end else case value when Array value = name != "files" ? value.inspect : value.sort.uniq.inspect.split(",").join(",\n") when String, Fixnum, true, false value = value.inspect else value = value.to_s.inspect end result << " s.#{name} = #{value}\n" end end result << "end" File.open(File.join(File.dirname(__FILE__), "#{spec.name}.gemspec"), "w"){|f| f << result} end end # namespace :gem # also keep the gemspec up to date each time we package a tarball or gem task :package => ['gem:update_gemspec'] task :gem => ['gem:update_gemspec'] rescue LoadError puts <<EOF ### Packaging Warning : RubyGems is apparently not installed on this system and any file add/remove/rename will not be auto-updated in the 'xmpp4r.gemspec' when you run any package tasks. All such file changes are recommended to be packaged on a system with RubyGems installed if you intend to push commits to the Git repo so the gemspec will also stay in sync for others. ### EOF end # we are apparently on a system that does not have RubyGems installed. # Lets try to provide only the basic tarball package tasks as a fallback. unless defined? Gem begin require 'rake/packagetask' Rake::PackageTask.new(PKG_NAME, PKG_VERSION) do |p| p.package_files = PKG_FILES p.need_tar = true p.need_zip = true end rescue LoadError puts <<EOF ### Warning : Unable to require the 'rake/packagetask'. Is Rake installed? ### EOF end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/COPYING��������������������������������������������������������������������������������0000644�0001750�0001750�00000043110�13647121573�015237� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/����������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�015164� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_streamSend.rb������������������������������������������������������������������0000755�0001750�0001750�00000002322�13647121573�020306� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'socket' require 'tempfile' require 'io/wait' require 'xmpp4r' include Jabber class StreamSendTest < Test::Unit::TestCase def setup @tmpfile = Tempfile.new("StreamSendTest") @tmpfilepath = @tmpfile.path() @tmpfile.unlink @servlisten = UNIXServer.new(@tmpfilepath) thServer = Thread.new { @server = @servlisten.accept } @iostream = UNIXSocket.new(@tmpfilepath) @stream = Stream.new @stream.start(@iostream) thServer.join end def teardown @stream.close @server.close @servlisten.close end def mysend(s) @stream.send(s) @stream.send("\n") #needed for easy test writing end ## # Tries to send a basic message def test_sendbasic mysend(Message.new) assert_equal("<message/>\n", @server.gets) end def test_sendmessage mysend(Message.new('lucas@linux.ensimag.fr', 'coucou')) assert_equal("<message to='lucas@linux.ensimag.fr'><body>coucou</body></message>\n", @server.gets) end def test_sendpresence mysend(Presence.new) assert_equal("<presence/>\n", @server.gets) end def test_sendiq mysend(Iq.new) assert_equal("<iq/>\n", @server.gets) end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_presence.rb��������������������������������������������������������������������0000755�0001750�0001750�00000011466�13647121573�020016� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'socket' require 'xmpp4r/rexmladdons' require 'xmpp4r/presence' require 'xmpp4r/errors' include Jabber class PresenceTest < Test::Unit::TestCase def test_create x = Presence.new() assert_equal("presence", x.name) assert_equal("jabber:client", x.namespace) assert_equal(nil, x.to) assert_equal(nil, x.show) assert_equal(nil, x.status) assert_equal(nil, x.priority) x = Presence.new(:away, "I am away", 23) assert_equal("presence", x.name) assert_equal(:away, x.show) assert_equal("away", x.show.to_s) assert_equal("I am away", x.status) assert_equal(23, x.priority) end def test_show x = Presence.new() assert_equal(nil, x.show) assert_raise(RuntimeError) { x.show = "a" } assert_equal(nil, x.show) assert_raise(RuntimeError) { x.show = 'away' } assert_equal(nil, x.show) x.show = :away assert_equal(:away, x.show) x.each_element('show') { |e| assert(e.class == REXML::Element, "<show/> is not REXML::Element") } x.show = nil assert_equal(nil, x.show) x.each_element('show') { |e| assert(true, "<show/> exists after 'show=nil'") } x.show = nil assert_equal(nil, x.show) showelement = REXML::Element.new('show') showelement.text = 'chat' x.add(showelement) assert_equal(:chat, x.show) end def test_status x = Presence.new() assert_equal(nil, x.status) x.status = "b" assert_equal("b", x.status) x.each_element('status') { |e| assert(e.class == REXML::Element, "<status/> is not REXML::Element") } x.status = nil assert_equal(nil, x.status) x.each_element('status') { |e| assert(true, "<status/> exists after 'status=nil'") } x.status = nil assert_equal(nil, x.status) end def test_priority x = Presence.new() assert_equal(nil, x.priority) x.priority = 5 assert_equal(5, x.priority) x.each_element('priority') { |e| assert(e.class == REXML::Element, "<priority/> is not REXML::Element") } x.priority = "5" assert_equal(5, x.priority) x.priority = nil assert_equal(nil, x.priority) x.each_element('priority') { |e| assert(true, "<priority/> exists after 'priority=nil'") } end def test_type x = Presence.new() assert_equal(nil, x.type) x.type = :delete assert_equal(nil, x.type) x.type = nil assert_equal(nil, x.type) x.type = nil assert_equal(nil, x.type) [:error, :probe, :subscribe, :subscribed, :unavailable, :unsubscribe, :unsubscribed, nil].each { |type| x.type = type assert_equal(type, x.type) } end def test_chaining x = Presence.new() x.set_show(:xa).set_status("Plundering the fridge.").set_priority(0) assert_equal(:xa, x.show) assert_equal("Plundering the fridge.", x.status) assert_equal(0, x.priority) end def test_error x = Presence.new() e = REXML::Element.new('error') x.add(e) x2 = Presence.new.import(x) # test if, after an import, the error element is successfully changed # into an ErrorResponse object. assert_equal(ErrorResponse, x2.first_element('error').class) end def test_sample x = Presence.new require 'rexml/document' d = REXML::Document.new("<presence from='astro@spaceboyz.net/versionbot' to='astro@spaceboyz.net/edgarr' xmlns='jabber:client'>\n <x from='astro@spaceboyz.net/versionbot' stamp='20050823T02:18:42' xmlns='jabber:x:delay'/><show>xa</show>\n <status>I am the evil fingerprinting robot</status>\n </presence>") x.import(d.root) num = 0 x.each_element('show') { num += 1 } assert_equal(1, num) assert_equal(:xa, x.show) assert_equal('I am the evil fingerprinting robot', x.status) end def test_xpathbug require 'rexml/document' d = REXML::Document.new("<tag1 xmlns='ns1'><tag2 xmlns='ns2'/><tada>xa</tada></tag1>") x = d.root num = 0 x.each_element('tada') { num += 1 } assert_equal(1, num) end def test_compare_prio assert_equal(0, Presence.new(:chat, '', 5) <=> Presence.new(:chat, '', 5)) assert_equal(-1, Presence.new(:chat, '', 4) <=> Presence.new(:chat, '', 5)) assert_equal(1, Presence.new(:chat, '', 4) <=> Presence.new(:chat, '', 3)) assert_equal(-1, Presence.new(:chat, '', nil) <=> Presence.new(:chat, '', 3)) assert_equal(1, Presence.new(:chat, '', 10) <=> Presence.new(:chat, '', nil)) assert_equal(0, Presence.new(:chat, '', nil) <=> Presence.new(:chat, '', nil)) end def test_compare_interest unav = Presence.new.set_type(:unavailable) assert_equal(0, unav.cmp_interest(unav)) assert_equal(1, unav.cmp_interest(Presence.new)) assert_equal(-1, Presence.new.cmp_interest(unav)) assert_equal(1, Presence.new(:chat).cmp_interest(Presence.new)) assert_equal(-1, Presence.new(:away).cmp_interest(Presence.new(:dnd))) end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_iqquery.rb���������������������������������������������������������������������0000755�0001750�0001750�00000001373�13647121573�017705� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/query' include Jabber class IqQueryTest < Test::Unit::TestCase def test_create x = IqQuery.new() assert_equal("query", x.name) assert_equal("<query/>", x.to_s) end def test_import q = IqQuery.new assert_equal(IqQuery, q.class) e = REXML::Element.new('query') e.add_namespace('jabber:iq:roster') # kind_of? only checks that the class belongs to the IqQuery class # hierarchy. See IqQueryRosterTest#test_import for a more strict # check. assert_kind_of(IqQuery, IqQuery.import(e)) # Importing specific derivates is to be tested in the test case of the derivate # (e.g. tc_iqqueryroster.rb) end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_client.rb����������������������������������������������������������������������0000755�0001750�0001750�00000001236�13647121573�017462� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/client' include Jabber class ClientTest < Test::Unit::TestCase def test_client1 =begin c = Client.new(JID.new('client1@localhost/res')) assert_nothing_raised("Couldn't connect") { c.connect } assert_nothing_raised("Couldn't authenticate") { c.auth('pw') } =end end def test_jid_is_jid c1 = Client.new(JID.new('user@host/resource')) assert_kind_of(JID, c1.jid) assert_equal('user@host/resource', c1.jid.to_s) c2 = Client.new('user@host/resource') assert_kind_of(JID, c2.jid) assert_equal('user@host/resource', c2.jid.to_s) end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_rexml.rb�����������������������������������������������������������������������0000755�0001750�0001750�00000010037�13647121573�017332� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../lib' require 'test/unit' require 'xmpp4r/rexmladdons' class REXMLTest < Test::Unit::TestCase def test_simple e = REXML::Element.new('e') assert_kind_of(REXML::Element, e) assert_nil(e.text) assert_nil(e.attributes['x']) end def test_normalize assert_equal('&', REXML::Text::normalize('&')) assert_equal('&amp;', REXML::Text::normalize('&')) assert_equal('&amp;amp;', REXML::Text::normalize('&amp;')) assert_equal('&nbsp;', REXML::Text::normalize(' ')) end def test_unnormalize assert_equal('&', REXML::Text::unnormalize('&')) assert_equal('&', REXML::Text::unnormalize('&amp;')) assert_equal('&amp;', REXML::Text::unnormalize('&amp;amp;')) assert_equal(' ', REXML::Text::unnormalize('&nbsp;')) assert_equal(' ', REXML::Text::unnormalize(' ')) # ? end def test_text_entities e = REXML::Element.new('e') e.text = '&' assert_equal('<e>&</e>', e.to_s) e.text = '&' assert_equal('<e>&amp;</e>', e.to_s) e.text = ' ' assert_equal('<e>&nbsp</e>', e.to_s) e.text = ' ' assert_equal('<e>&nbsp;</e>', e.to_s) e.text = '&<;' assert_equal('<e>&<;</e>', e.to_s) e.text = '<>"\'' assert_equal('<e><>"'</e>', e.to_s) e.text = '<x>&</x>' assert_equal('<e><x>&amp;</x></e>', e.to_s) end def test_attribute_entites e = REXML::Element.new('e') e.attributes['x'] = '&' assert_equal('&', e.attributes['x']) e.attributes['x'] = '&' # bug in REXML 3.1.6 unescaped the ampersand # assert_equal('&', e.attributes['x']) # substituting a test that works with 3.1.5, 3.1.6, and 3.1.7 assert_equal('&amp;', e.attribute('x').to_s) e.attributes['x'] = ' ' assert_equal(' ', e.attributes['x']) e.attributes['x'] = ' ' assert_equal(' ', e.attributes['x']) end # test '==(o)' def test_passing_in_non_rexml_element_as_self o = Object.new assert_not_equal(o, REXML::Element.new('foo')) end def test_passing_in_non_rexml_element_as_comparison_object o = Object.new assert_not_equal(REXML::Element.new('foo'), o) end def test_element_equal_simple assert_equal(REXML::Element.new('foo'), REXML::Element.new('foo')) end def test_element_not_equal_simple assert_not_equal(REXML::Element.new('foo'), REXML::Element.new('bar')) end def test_element_equal_when_all_are_same e1 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e1.attributes['a:bar'] = 'baz' e2 = REXML::Element.new('foo') e2.add_namespace('a', 'urn:test:foo') e2.attributes['a:bar'] = 'baz' assert_equal(e1, e2) end def test_element_not_equal_when_namespace_name_differs e1 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e1.attributes['a:bar'] = 'baz' e2 = REXML::Element.new('foo') e2.add_namespace('b', 'urn:test:foo') e2.attributes['a:bar'] = 'baz' assert_not_equal(e1, e2) end def test_element_not_equal_when_namespace_value_differs e1 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e1.attributes['a:bar'] = 'baz' e2 = REXML::Element.new('foo') e2.add_namespace('a', 'urn:test:bar') e2.attributes['a:bar'] = 'baz' assert_not_equal(e1, e2) end def test_element_not_equal_when_attribute_name_differs e1 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e1.attributes['a:bar'] = 'baz' e2 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e2.attributes['b:bar'] = 'baz' assert_not_equal(e1, e2) end def test_element_not_equal_when_attribute_value_differs e1 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e1.attributes['a:bar'] = 'baz' e2 = REXML::Element.new('foo') e1.add_namespace('a', 'urn:test:foo') e2.attributes['a:bar'] = 'bar' assert_not_equal(e1, e2) end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_streamparser.rb����������������������������������������������������������������0000755�0001750�0001750�00000007103�13647121573�020713� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'tempfile' require 'test/unit' require 'socket' require 'xmpp4r' include Jabber class MockListener attr_reader :received def receive(element) @received = element end def parse_failure(exception) raise exception end end class StreamParserTest < Test::Unit::TestCase STREAM = '<stream:stream xmlns:stream="http://etherx.jabber.org/streams">' def setup @listener = MockListener.new end def teardown @listener = nil end def parse_simple_helper(fixture) parser = StreamParser.new(STREAM + fixture, @listener) begin parser.parse rescue Jabber::ServerDisconnected => e end yield parse_with_rexml(fixture) end def parse_with_rexml(fixture) REXML::Document.new(fixture).root end def test_simple_text parse_simple_helper( "<a>text</a>" ) do |desired| assert_equal desired.name, @listener.received.name assert_equal desired.text, @listener.received.text assert_equal desired.cdatas, @listener.received.cdatas end end def test_simple_cdata parse_simple_helper( "<a><![CDATA[<cdata>]]></a>" ) do |desired| assert_equal desired.name, @listener.received.name assert_equal desired.text, @listener.received.text assert_equal desired.cdatas, @listener.received.cdatas end end def test_composite_text_cdata parse_simple_helper( "<a>text<![CDATA[<cdata>]]></a>" ) do |desired| assert_equal desired.name, @listener.received.name assert_equal desired.text, @listener.received.text assert_equal desired.cdatas, @listener.received.cdatas end end def test_composite_cdata_text parse_simple_helper( "<a><![CDATA[<cdata>]]>text</a>" ) do |desired| assert_equal desired.name, @listener.received.name assert_equal desired.text, @listener.received.text assert_equal desired.cdatas, @listener.received.cdatas end end def test_complex_composite_cdata_text parse_simple_helper( "<a><![CDATA[<cdata>]]>text<![CDATA[<cdata>]]>text</a>" ) do |desired| assert_equal desired.name, @listener.received.name assert_equal desired.text, @listener.received.text assert_equal desired.cdatas, @listener.received.cdatas end end def test_entity_escaping1 parse_simple_helper( "<a>'&"</a>" ) do |desired| assert_equal "'&\"", @listener.received.text assert_equal "<a>'&"</a>", @listener.received.to_s end end def test_entity_escaping2 parse_simple_helper( "<a>&amp;amp;</a>" ) do |desired| assert_equal "&amp;", @listener.received.text assert_equal "<a>&amp;amp;</a>", @listener.received.to_s end end def test_entity_escaping3 parse_simple_helper( "<a href='http://google.com?p=1&p=2'>text</a>" ) do |desired| assert_equal "text", @listener.received.text assert_equal "<a href='http://google.com?p=1&p=2'>text</a>", @listener.received.to_s end end =begin ## # FIXME: # http://www.germane-software.com/projects/rexml/ticket/165 def test_unbound_prefix fixture = "<message><soe:instantMessage/></message>" parser = StreamParser.new(STREAM + fixture, @listener) assert_nothing_raised { parser.parse } end =end def test_stream_restart parser = StreamParser.new(STREAM + "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' to='foobar'>", @listener) begin parser.parse rescue Jabber::ServerDisconnected => e end assert_equal 'foobar', @listener.received.attributes['to'] end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/reliable/�������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016743� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/reliable/tc_listener_mocked_test.rb�����������������������������������������������0000644�0001750�0001750�00000003417�13647121573�024171� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift "#{File.dirname(__FILE__)}/../../lib" require 'test/unit' require 'xmpp4r' # Jabber::debug = true class ReliableListenerTest < Test::Unit::TestCase class TestListener < Jabber::Reliable::Listener attr_accessor :received_messages def on_message(got_message) self.received_messages ||= [] self.received_messages << got_message end end def test_listener listener = TestListener.new("listener1@localhost/hi", "test", {:servers => "127.0.0.1", :presence_message => "hi"}) Jabber::Test::ListenerMocker.mock_out(listener) listener.start message_to_send = Jabber::Message.new message_to_send.to = "listener1@localhost/hi" message_to_send.body = "hi" listener.send_message(message_to_send) assert_equal(1, listener.received_messages.size) first_message = listener.received_messages[0] assert_equal("hi", first_message.body) listener.stop end def test_listener_stop_and_start listener = TestListener.new("listener1@localhost/hi", "test", {:servers => "127.0.0.1", :presence_message => "hi"}) Jabber::Test::ListenerMocker.mock_out(listener) listener.start message_to_send = Jabber::Message.new message_to_send.to = "listener1@localhost/hi" message_to_send.body = "hi" listener.send_message(message_to_send) assert_equal(1, listener.received_messages.size) first_message = listener.received_messages[0] assert_equal("hi", first_message.body) listener.stop assert_raises(ArgumentError){ listener.send_message(message_to_send) } listener.start listener.send_message(message_to_send) assert_equal(2, listener.received_messages.size) listener.stop end end�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/reliable/tc_disconnect_exception.rb�����������������������������������������������0000644�0001750�0001750�00000001371�13647121573�024167� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift "#{File.dirname(__FILE__)}/../../lib" require 'test/unit' require 'xmpp4r' class DisconnectExceptionTest < Test::Unit::TestCase class Listener def receive(element) end end def test_streamparser rd, wr = IO.pipe listener = Listener.new exception_raised = nil Thread.new do begin parser = Jabber::StreamParser.new(rd, listener) parser.parse rescue => e exception_raised = e end end wr.write("<hi/>") wr.close sleep(0.1) assert exception_raised assert exception_raised.is_a?(Jabber::ServerDisconnected), "Expected a Jabber::ServerDisconnected but got #{exception_raised}" # puts exception_raised.inspect end end�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/reliable/tc_reliable_connection.rb������������������������������������������������0000644�0001750�0001750�00000001610�13647121573�023752� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift "#{File.dirname(__FILE__)}/../../lib" require 'test/unit' require 'xmpp4r' # Jabber::debug = true class ReliableConnectionTest < Test::Unit::TestCase def test_connection_retry @created_sockets = [] callback_proc = Proc.new do |socket_init_args| @created_sockets << socket_init_args[0] raise RuntimeError, "Fail to create socket" end Jabber::Test::ListenerMocker.with_socket_mocked(callback_proc) do conn = Jabber::Reliable::Connection.new("listener1@localhost/hi", { :servers => ["server 1", "server 2", "server 3", "server 4"], :port => 12345, :max_retry => 3, #3 retries = 4 total tries :retry_sleep => 0.1}) assert_raises(RuntimeError) do conn.connect end assert_equal(["server 1", "server 2", "server 3", "server 4"], @created_sockets.sort) end end end������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/reliable/tc_disconnect_cleanup.rb�������������������������������������������������0000644�0001750�0001750�00000021037�13647121573�023621� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift "#{File.dirname(__FILE__)}/../../lib" require 'test/unit' require 'xmpp4r' # Jabber::debug = true # Jabber::warnings = true class ClientDisconnectCleanupTest < Test::Unit::TestCase class ControledClient < Jabber::Client attr_accessor :tcpserver, :socket_override def connect begin @port = 1024 + rand(32768 - 1024) @tcpserver = TCPServer.new("127.0.0.1", @port) rescue Errno::EADDRINUSE, Errno::EACCES @tcpserver.close rescue nil retry end super("127.0.0.1", @port) end def start @socket = socket_override super end end def test_regular_stream_end rd, wr = IO.pipe rd.instance_eval do def write(*args) end def flush end end client = ControledClient.new("test@localhost") @exceptions_caught = 0 client.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do client.socket_override = rd client.instance_eval{ @keepalive_interval = 0.1 } client.connect client.auth_nonsasl("password", false) end sleep(0.1) assert client.is_connected? wr.write('<stream:stream xmlns:stream="http://etherx.jabber.org/streams">') wr.write("</stream:stream>") sleep(0.2) assert !client.is_connected? assert client.instance_eval{ @parser_thread.nil? || !@parser_thread.alive? } assert client.instance_eval{ @keepaliveThread.nil? || !@keepaliveThread.alive? } assert @exceptions_caught > 0 end def test_error_on_send rd, wr = IO.pipe rd.instance_eval do def write(*args) end def flush end end client = ControledClient.new("test@localhost") @exceptions_caught = 0 client.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do client.socket_override = rd client.connect client.auth_nonsasl("password", false) end sleep(0.1) assert client.is_connected? rd.instance_eval do def write(*args) raise "No writting for you, you disconnect now" end end wr.write(%Q{<stream:stream from='localhost' id="acecf234be084aecdc16509077573c7d7200912f" version='1.0' xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client"><stream:features><auth xmlns='http://jabber.org/features/iq-auth'/></stream:features> }) sleep(0.1) assert !client.is_connected? assert client.instance_eval{ @parser_thread.nil? || !@parser_thread.alive? } assert client.instance_eval{ @keepaliveThread.nil? || !@keepaliveThread.alive? } assert @exceptions_caught > 0 end def test_client_disconnect rd, wr = IO.pipe rd.instance_eval do def write(*args) end def flush end end client = ControledClient.new("test@localhost") @exceptions_caught = 0 client.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do client.socket_override = rd client.connect client.auth_nonsasl("password", false) end wr.write(%Q{<stream:stream from='localhost' id="acecf234be084aecdc16509077573c7d7200912f" version='1.0' xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client"><stream:features><auth xmlns='http://jabber.org/features/iq-auth'/></stream:features> }) sleep(0.1) assert client.is_connected? assert client.instance_eval{ @parser_thread.alive? } assert client.instance_eval{ @keepaliveThread.alive? } wr.close sleep(0.1) assert !client.is_connected? assert client.instance_eval{ @parser_thread.nil? || !@parser_thread.alive? } assert client.instance_eval{ @keepaliveThread.nil? || !@keepaliveThread.alive? } assert @exceptions_caught > 0 end end class ConnectionDisconnectCleanupTest < Test::Unit::TestCase class PipeConnection < Jabber::Connection attr_accessor :socket_override def connect begin @port = 1024 + rand(32768 - 1024) tcpsocket = TCPServer.new("127.0.0.1", @port) rescue Errno::EADDRINUSE, Errno::EACCES tcpsocket.close rescue nil retry end super("127.0.0.1", @port) end def accept_features #do nothing end def start @socket = self.socket_override super end end def test_cleanup_when_disconnected_during_keepalive rd, wr = IO.pipe conn = PipeConnection.new @exceptions_caught = 0 conn.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do conn.socket_override = rd #this will raise exception in keepalive thread, when attempts to send blank space shortly after connect conn.instance_eval{ @keepalive_interval = 0.1 } conn.connect end sleep(0.2) assert rd.closed? assert !conn.is_connected? assert !conn.instance_eval{ @parser_thread }.alive? assert !conn.instance_eval{ @keepaliveThread }.alive? assert @exceptions_caught > 0 end def test_cleanup_after_stream_close rd, wr = IO.pipe conn = PipeConnection.new @exceptions_caught = 0 conn.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do conn.socket_override = rd conn.connect end wr.write("<hi/>") wr.close sleep(0.1) assert rd.closed? assert !conn.is_connected? assert !conn.instance_eval{ @parser_thread }.alive? assert !conn.instance_eval{ @keepaliveThread }.alive? assert @exceptions_caught > 0 end def test_cleanup_after_stream_end rd, wr = IO.pipe conn = PipeConnection.new @exceptions_caught = 0 conn.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end Thread.new do conn.socket_override = rd conn.connect end wr.write('<stream:stream xmlns:stream="http://etherx.jabber.org/streams">') wr.write("</stream:stream>") # wr.close sleep(0.1) assert rd.closed? assert !conn.is_connected? assert !conn.instance_eval{ @parser_thread }.alive? assert !conn.instance_eval{ @keepaliveThread }.alive? assert @exceptions_caught > 0 end end class StreamDisconnectCleanupTest < Test::Unit::TestCase def test_cleanup_when_errors_on_send rd, wr = IO.pipe stream = Jabber::Stream.new @exceptions_caught = 0 stream.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end assert !stream.is_connected? stream.start(rd) wr.write("<hi/>") assert stream.is_connected? #should raise error trying to write to stream that can't be written to, and catch should close it. begin stream.send("<hi/>") rescue end sleep(0.1) assert rd.closed? assert !stream.is_connected? assert !stream.instance_eval{ @parser_thread }.alive? assert @exceptions_caught > 0 end def test_cleanup_after_stream_close rd, wr = IO.pipe stream = Jabber::Stream.new @exceptions_caught = 0 stream.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end assert !stream.is_connected? stream.start(rd) wr.write("<hi/>") assert stream.is_connected? wr.close sleep(0.1) assert rd.closed? assert !stream.is_connected? assert !stream.instance_eval{ @parser_thread }.alive? assert @exceptions_caught > 0 end def test_cleanup_after_stream_end rd, wr = IO.pipe stream = Jabber::Stream.new @exceptions_caught = 0 stream.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end assert !stream.is_connected? stream.start(rd) wr.write('<stream:stream xmlns:stream="http://etherx.jabber.org/streams">') wr.write("</stream:stream>") assert stream.is_connected? # wr.close sleep(0.1) assert rd.closed? assert !stream.is_connected? assert !stream.instance_eval{ @parser_thread }.alive? assert @exceptions_caught > 0 end def test_cleanup_after_parse_failure rd, wr = IO.pipe stream = Jabber::Stream.new @exceptions_caught = 0 stream.on_exception do |e, connection, where_failed| @exceptions_caught += 1 end assert !stream.is_connected? stream.start(rd) wr.write('<this is bad xml>>') wr.close assert stream.is_connected? sleep(0.1) assert rd.closed? assert !stream.is_connected? assert !stream.instance_eval{ @parser_thread }.alive? assert @exceptions_caught > 0 end end�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/roster/���������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016502� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/roster/tc_xroster.rb��������������������������������������������������������������0000755�0001750�0001750�00000003563�13647121573�021235� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/roster/x/roster' require 'xmpp4r/jid' include Jabber class Roster::XRosterTest < Test::Unit::TestCase def test_create r1 = Roster::XRoster.new assert_equal('x', r1.name) assert_equal('jabber:x:roster', r1.namespace) r2 = Roster::RosterX.new assert_equal('x', r2.name) assert_equal('http://jabber.org/protocol/rosterx', r2.namespace) end def test_import x1 = X.new x1.add_namespace('jabber:x:roster') x2 = X::import(x1) assert_equal(Roster::XRoster, x2.class) assert_equal('jabber:x:roster', x2.namespace) end def test_typed_add x = REXML::Element.new('x') x.add(REXML::Element.new('item')) r = Roster::XRoster.new.import(x) assert_kind_of(Roster::XRosterItem, r.first_element('item')) assert_kind_of(Roster::XRosterItem, r.typed_add(REXML::Element.new('item'))) end def test_items j1 = Roster::XRosterItem.new assert_equal(JID.new(nil), j1.jid) assert_equal(nil, j1.iname) j2 = Roster::XRosterItem.new(JID.new('a@b/c')) assert_equal(JID.new('a@b/c'), j2.jid) assert_equal(nil, j2.iname) j3 = Roster::XRosterItem.new(JID.new('a@b/c'), 'Mr. Abc') assert_equal(JID.new('a@b/c'), j3.jid) assert_equal('Mr. Abc', j3.iname) assert_equal([], j3.groups) j3.groups = ['X', 'Y', 'Z'] assert_equal(['X', 'Y', 'Z'], j3.groups) end def test_actions j = Roster::XRosterItem.new assert_equal(:add, j.action) j.action = :modify assert_equal(:modify, j.action) j.action = :delete assert_equal(:delete, j.action) j.action = :invalid assert_equal(:add, j.action) j.attributes['action'] = 'modify' assert_equal(:modify, j.action) j.attributes['action'] = 'invalid' assert_equal(:add, j.action) end end ���������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/roster/tc_helper.rb���������������������������������������������������������������0000755�0001750�0001750�00000034560�13647121573�021007� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/roster/helper/roster' require 'xmpp4r/semaphore' include Jabber class Roster::HelperTest < Test::Unit::TestCase include ClientTester def test_simple state { |iq| assert_kind_of(Iq, iq) assert_equal(:get, iq.type) assert_nil(iq.to) assert_equal('jabber:iq:roster', iq.queryns) send("<iq type='result'><query xmlns='jabber:iq:version'/></iq>") send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c' subscription='both'/> <item jid='b@b.c' name='b guy' subscription='from' ask='subscribe'/> <item jid='123@xyz' name='123' subscription='to'/> </query> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } h.get_roster wait_state query_waiter.wait assert_equal([nil], h.groups) jids = h.find_by_group(nil).collect { |item| item.jid }.sort assert_equal([JID.new('123@xyz'), JID.new('a@b.c'), JID.new('b@b.c')], jids) assert_equal(1, h.find('123@xyz/res').size) assert_kind_of(Roster::Helper::RosterItem, h['a@b.c']) assert_equal(JID.new('a@b.c'), h['a@b.c'].jid) assert_nil(h['a@b.c'].iname) assert_equal(:both, h['a@b.c'].subscription) assert_nil(h['a@b.c'].ask) assert_kind_of(Roster::Helper::RosterItem, h[JID.new('b@b.c')]) assert_equal(JID.new('b@b.c'), h['b@b.c'].jid) assert_equal('b guy', h['b@b.c'].iname) assert_equal(:from, h['b@b.c'].subscription) assert_equal(:subscribe, h['b@b.c'].ask) assert_kind_of(Roster::Helper::RosterItem, h['123@xyz']) assert_equal(JID.new('123@xyz'), h['123@xyz'].jid) assert_equal('123', h['123@xyz'].iname) assert_equal(:to, h['123@xyz'].subscription) assert_nil(h['123@xyz'].ask) assert_nil(h['c@b.c']) assert_nil(h[JID.new('c@b.c')]) end def test_rosterpush state { |iq| send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'> </query> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } h.get_roster wait_state query_waiter.wait assert_equal([], h.groups) assert_nil(h['a@b.c']) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c'/> </query> </iq>") query_waiter.wait assert_kind_of(Roster::Helper::RosterItem, h['a@b.c']) assert_equal(JID.new('a@b.c'), h['a@b.c'].jid) assert_nil(h['a@b.c'].iname) assert_nil(h['a@b.c'].subscription) assert_nil(h['a@b.c'].ask) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c' subscription='from' name='ABC' ask='subscribe'/> </query> </iq>") query_waiter.wait assert_kind_of(Roster::Helper::RosterItem, h['a@b.c']) assert_equal(JID.new('a@b.c'), h['a@b.c'].jid) assert_equal('ABC', h['a@b.c'].iname) assert_equal(:from, h['a@b.c'].subscription) assert_equal(:subscribe, h['a@b.c'].ask) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c'/> </query> </iq>") query_waiter.wait assert_kind_of(Roster::Helper::RosterItem, h['a@b.c']) assert_equal(JID.new('a@b.c'), h['a@b.c'].jid) assert_nil(h['a@b.c'].iname) assert_nil(h['a@b.c'].subscription) assert_nil(h['a@b.c'].ask) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c' subscription='remove'/> </query> </iq>") query_waiter.wait assert_nil(h['a@b.c']) end def test_presence state { |iq| send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'> <item jid='a@b.c' subscription='both'/> </query> </iq>") } query_waiter = Semaphore.new presence_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } cb_item, cb_op, cb_p = nil, nil, nil h.add_presence_callback { |item,oldpres,pres| # HACK: # if two stanzas are expected for one sent stanza, # race conditions may appear here Thread.pass cb_item, cb_op, cb_p = item, oldpres, pres presence_waiter.run } h.get_roster wait_state query_waiter.wait assert_equal(false, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(0, presences) send("<presence from='a@b.c/r'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_nil(cb_op) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(1, presences) send("<presence from='a@b.c/r2'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_nil(cb_op) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(2, presences) send("<presence from='a@b.c/r'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_kind_of(Presence, cb_op) assert_nil(cb_op.type) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(2, presences) send("<presence from='a@b.c/r' type='error'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_kind_of(Presence, cb_op) assert_nil(cb_op.type) assert_kind_of(Presence, cb_p) assert_equal(:error, cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(2, presences) send("<presence from='a@b.c/r2' type='unavailable'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_kind_of(Presence, cb_op) assert_nil(cb_op.type) assert_kind_of(Presence, cb_p) assert_equal(:unavailable, cb_p.type) assert_equal(false, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(2, presences) send("<presence from='a@b.c/r'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_kind_of(Presence, cb_op) assert_equal(:error, cb_op.type) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(1, presences) send("<presence from='a@b.c/r2'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_nil(cb_op) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(2, presences) send("<presence from='a@b.c' type='error'/>") 2.times { presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_kind_of(Presence, cb_op) assert_kind_of(Presence, cb_p) assert_equal(:error, cb_p.type) } assert_equal(false, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(1, presences) send("<presence from='a@b.c/r'/>") presence_waiter.wait assert_kind_of(Roster::Helper::RosterItem, cb_item) assert_nil(cb_op) assert_kind_of(Presence, cb_p) assert_nil(cb_p.type) assert_equal(true, h['a@b.c'].online?) presences = 0 h['a@b.c'].each_presence { presences += 1 } assert_equal(1, presences) end def test_subscribe state { |iq| send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'/> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } h.get_roster wait_state query_waiter.wait state { |iq| assert_kind_of(Iq, iq) assert_equal('jabber:iq:roster', iq.queryns) assert_equal(JID.new('contact@example.org'), iq.query.first_element('item').jid) assert_equal('MyContact', iq.query.first_element('item').iname) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='contact@example.org' subscription='none' name='MyContact'/> </query> </iq> <iq type='result' id='#{iq.id}'/>") } state { |pres| assert_kind_of(Presence, pres) assert_equal(:subscribe, pres.type) assert_equal(JID.new('contact@example.org'), pres.to) Thread.pass } h.add('contact@example.org', 'MyContact', true) wait_state query_waiter.wait wait_state end def test_accept_subscription state { |iq| send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'/> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } h.get_roster wait_state query_waiter.wait state { |pres| assert_kind_of(Presence, pres) assert_equal(:subscribed, pres.type) } state { |iq| assert_kind_of(Iq, iq) assert_equal(:set, iq.type) send("<iq type='result' id='#{iq.id}'/>") } cb_lock = Semaphore.new h.add_subscription_request_callback { |item,pres| assert_nil(item) assert_kind_of(Presence, pres) h.accept_subscription(pres.from) cb_lock.run } send("<presence type='subscribe' from='contact@example.org' to='user@example.com'/>") skip_state wait_state cb_lock.wait state { |pres| assert_kind_of(Presence, pres) assert_equal(:subscribed, pres.type) assert_equal(JID.new('contact@example.org'), pres.to) } wait_state end def test_decline_subscription query_waiter = Semaphore.new state { |iq| send("<iq type='result' id='#{iq.id}'> <query xmlns='jabber:iq:roster'/> </iq>") } h = Roster::Helper.new(@client, false) h.add_query_callback { |iq| query_waiter.run } h.get_roster wait_state query_waiter.wait state { |pres| assert_kind_of(Presence, pres) assert_equal(:unsubscribed, pres.type) assert_equal(JID.new('contact@example.org'), pres.to) } cb_lock = Semaphore.new h.add_subscription_request_callback { |item,pres| assert_nil(item) assert_kind_of(Presence, pres) h.decline_subscription(pres.from) cb_lock.run } send("<presence type='subscribe' from='contact@example.org' to='user@example.com'/>") wait_state cb_lock.wait end def test_groupset state { |iq| send("<iq type='result'> <query xmlns='jabber:iq:roster'> <item jid='test@test' subscription='both'> <group>One</group> <group>Two</group> </item> </query> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { query_waiter.run } h.get_roster wait_state query_waiter.wait assert_equal(1, h.items.size) assert_equal(%w(One Two).sort, h['test@test'].groups.sort) state { |iq| send("<iq type='result' id='#{iq.id}'>#{iq.query}</iq>") } h['test@test'].groups = %w(One Two Three) h['test@test'].send wait_state query_waiter.wait assert_equal(%w(One Two Three).sort, h['test@test'].groups.sort) end def test_nameset state { |iq| send("<iq type='result'> <query xmlns='jabber:iq:roster'> <item jid='test@test' subscription='both'/> </query> </iq>") } query_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_query_callback { query_waiter.run } h.get_roster wait_state query_waiter.wait assert_equal(1, h.items.size) assert_nil(h['test@test'].iname) state { |iq| send("<iq type='result' id='#{iq.id}'>#{iq.query}</iq>") } h['test@test'].iname = 'Unknown' h['test@test'].send wait_state query_waiter.wait assert_equal('Unknown', h['test@test'].iname) state { |iq| send("<iq type='result' id='#{iq.id}'>#{iq.query}</iq>") } h['test@test'].iname = 'Known' h['test@test'].send wait_state query_waiter.wait assert_equal('Known', h['test@test'].iname) state { |iq| send("<iq type='result' id='#{iq.id}'>#{iq.query}</iq>") } h['test@test'].iname = nil h['test@test'].send wait_state query_waiter.wait assert_nil(h['test@test'].iname) end def test_update_callback update_args = nil update_waiter = Semaphore.new h = Roster::Helper.new(@client, false) h.add_update_callback { |*a| update_args = a update_waiter.run } h.get_roster send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='foo@bar' name='Foo'/> </query> </iq>") update_waiter.wait assert_nil(update_args[0]) assert_equal(JID.new('foo@bar'), update_args[1].jid) assert_equal('Foo', update_args[1].iname) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='foo@bar'/> </query> </iq>") update_waiter.wait assert_equal(JID.new('foo@bar'), update_args[0].jid) assert_equal('Foo', update_args[0].iname) assert_equal(JID.new('foo@bar'), update_args[1].jid) assert_nil(update_args[1].iname) send("<iq type='set'> <query xmlns='jabber:iq:roster'> <item jid='foo@bar' subscription='remove'/> </query> </iq>") update_waiter.wait assert_equal(JID.new('foo@bar'), update_args[0].jid) assert_nil(update_args[0].iname) assert_nil(update_args[1]) end end ������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/roster/tc_iqqueryroster.rb��������������������������������������������������������0000755�0001750�0001750�00000011113�13647121573�022453� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/roster/iq/roster' require 'xmpp4r/jid' require 'xmpp4r/iq' include Jabber class Roster::IqQueryRosterTest < Test::Unit::TestCase def test_create r = Roster::IqQueryRoster.new assert_equal('jabber:iq:roster', r.namespace) assert_equal(r.to_a.size, 0) assert_equal(r.to_a, []) assert_equal(r.to_s, "<query xmlns='jabber:iq:roster'/>") end def test_import iq = Iq.new q = REXML::Element.new('query') q.add_namespace('jabber:iq:roster') iq.add(q) iq2 = Iq.new.import(iq) assert_equal(Roster::IqQueryRoster, iq2.query.class) end def test_answer iq = Iq.new_rosterget assert_equal(:get, iq.type) assert_nil(iq.to) assert_equal('jabber:client', iq.namespace) assert_equal('jabber:iq:roster', iq.queryns) assert_equal(0, iq.query.children.size) iq2 = iq.answer(true) assert_equal(:get, iq2.type) assert_nil(iq2.from) assert_equal('jabber:client', iq2.namespace) assert_equal('jabber:iq:roster', iq2.queryns) assert_equal(0, iq2.query.children.size) end def test_xmlns ri = Roster::RosterItem.new assert_equal('jabber:iq:roster', ri.namespace) assert_equal('jabber:iq:roster', ri.attributes['xmlns']) r = Roster::IqQueryRoster.new assert_equal('jabber:iq:roster', r.namespace) assert_equal('jabber:iq:roster', r.attributes['xmlns']) r.add(ri) assert_equal('jabber:iq:roster', ri.namespace) assert_nil(ri.attributes['xmlns']) end def test_items r = Roster::IqQueryRoster.new r.add(Roster::RosterItem.new) r.add(Roster::RosterItem.new(JID.new('a@b/d'), 'ABC', :none, :subscribe)).groups = ['a'] itemstr = "<item jid='astro@spaceboyz.net' name='Astro' subscribtion='both'>" \ + "<group>SpaceBoyZ</group><group>xmpp4r</group></item>" r.typed_add(REXML::Document.new(itemstr).root) r.each { |item| assert_equal(item, r[item.jid]) } r.to_a.each { |item| assert_equal(item, r[item.jid]) } assert_equal(JID.new, r.to_a[0].jid) assert_equal(nil, r.to_a[0].iname) assert_equal(nil, r.to_a[0].subscription) assert_equal(nil, r.to_a[0].ask) assert_equal(JID.new('a@b/d'), r.to_a[1].jid) assert_equal('ABC', r.to_a[1].iname) assert_equal(:none, r.to_a[1].subscription) assert_equal(:subscribe, r.to_a[1].ask) assert_equal(REXML::Document.new(itemstr).root.to_s, r.to_a[2].to_s) end def test_dupitems r = Roster::IqQueryRoster.new jid = JID.new('a@b') jid2 = JID.new('c@d') ri = Roster::RosterItem.new(jid, 'ab') r.add(ri) assert_equal('ab', ri.iname) assert_equal('ab', r[jid].iname) ri.iname = 'cd' assert_equal('cd', ri.iname) # There are no shallow copies - everything is alright. assert_equal('cd', r[jid].iname) r.add(ri) assert_equal('cd', r[jid].iname) assert_equal(ri, r[jid]) ri.jid = jid2 assert_equal(nil, r[jid]) assert_equal(ri, r[jid2]) assert_equal(2, r.to_a.size) r.each_element('item') { |item| assert_equal(ri, item) assert_equal(ri.jid, item.jid) assert_equal(ri.iname, item.iname) assert_equal(jid2, item.jid) assert_equal('cd', item.iname) } end end class Roster::RosterItemTest < Test::Unit::TestCase def test_create ri = Roster::RosterItem.new assert_equal(JID.new, ri.jid) assert_equal(nil, ri.iname) assert_equal(nil, ri.subscription) assert_equal(nil, ri.ask) ri = Roster::RosterItem.new(JID.new('a@b/c'), 'xyz', :both, nil) assert_equal(JID.new('a@b/c'), ri.jid) assert_equal('xyz', ri.iname) assert_equal(:both, ri.subscription) assert_equal(nil, ri.ask) end def test_modify ri = Roster::RosterItem.new(JID.new('a@b/c'), 'xyz', :both, :subscribe) assert_equal(JID.new('a@b/c'), ri.jid) ri.jid = nil assert_equal(JID.new, ri.jid) assert_equal('xyz', ri.iname) ri.iname = nil assert_equal(nil, ri.iname) assert_equal(:both, ri.subscription) ri.subscription = nil assert_equal(nil, ri.subscription) assert_equal(:subscribe, ri.ask) ri.ask = nil assert_equal(nil, ri.ask) end def test_groupdeletion ri = Roster::RosterItem.new g1 = ['a', 'b', 'c'] ri.groups = g1 assert_equal(g1, ri.groups.sort) g2 = ['c', 'd', 'e'] ri.groups = g2 assert_equal(g2, ri.groups.sort) end def test_dupgroups ri = Roster::RosterItem.new mygroups = ['a', 'a', 'b'] ri.groups = mygroups assert_equal(mygroups.uniq, ri.groups) end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/vcard/����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016263� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/vcard/tc_helper.rb����������������������������������������������������������������0000755�0001750�0001750�00000002434�13647121573�020563� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/vcard/helper/vcard' include Jabber class Vcard::HelperTest < Test::Unit::TestCase include ClientTester def test_create h = Vcard::Helper.new(@client) assert_kind_of(Vcard::Helper, h) end def test_callback # @server.on_exception{|*e| p e} class << @client remove_method(:jid) def jid JID.new('b@b.com/b') end end state { |iq| assert_kind_of(Iq, iq) assert_equal(JID.new('a@b.com'), iq.to) assert_equal(:get, iq.type) assert_nil(iq.queryns) assert_kind_of(Vcard::IqVcard, iq.vcard) children = 0 iq.vcard.each_child { children += 1 } assert_equal(0, children) send("<iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'><vCard xmlns='vcard-temp'><NICKNAME>Mr. B</NICKNAME><PHOTO><TYPE>image/png</TYPE><BINVAL>====</BINVAL></PHOTO></vCard></iq>") } res = Vcard::Helper::get(@client, 'a@b.com') wait_state assert_kind_of(Vcard::IqVcard, res) assert_equal('Mr. B', res['NICKNAME']) assert_equal('image/png', res['PHOTO/TYPE']) assert_equal('====', res['PHOTO/BINVAL']) end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/vcard/tc_iqvcard.rb���������������������������������������������������������������0000755�0001750�0001750�00000002745�13647121573�020742� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/vcard/iq/vcard' include Jabber class IqVcardTest < Test::Unit::TestCase def test_create v = Vcard::IqVcard.new assert_equal([], v.fields) end def test_create_with_fields v = Vcard::IqVcard.new({'FN' => 'B C', 'NICKNAME' => 'D'}) assert_equal(['FN', 'NICKNAME'], v.fields.sort) assert_equal('B C', v['FN']) assert_equal('D', v['NICKNAME']) assert_equal(nil, v['x']) end def test_fields v = Vcard::IqVcard.new f = ['a', 'b', 'c', 'd', 'e'] f.each { |s| v[s.downcase] = s.upcase } assert_equal(f, v.fields.sort) f.each { |s| assert_equal(s.upcase, v[s.downcase]) assert_equal(nil, v[s.upcase]) } end def test_deep v = Vcard::IqVcard.new({ 'FN' => 'John D. Random', 'PHOTO/TYPE' => 'image/png', 'PHOTO/BINVAL' => '===='}) assert_equal(['FN', 'PHOTO/BINVAL', 'PHOTO/TYPE'], v.fields.sort) assert_equal('John D. Random', v['FN']) assert_equal('image/png', v['PHOTO/TYPE']) assert_equal('====', v['PHOTO/BINVAL']) assert_equal(nil, v['PHOTO']) assert_equal(nil, v['NICKNAME']) end def test_photo_binval v = Vcard::IqVcard.new({'PHOTO/BINVAL'=>"SGVsbG8gd29ybGQ=\n"}) assert_equal('Hello world', v.photo_binval) end def test_photo_binval_nil v = Vcard::IqVcard.new({}) assert_nil(v.photo_binval) end end ���������������������������xmpp4r-0.5.6/test/last/�����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016127� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/last/tc_helper.rb�����������������������������������������������������������������0000755�0001750�0001750�00000003563�13647121573�020433� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/last/helper/helper' include Jabber class LastActivity::HelperTest < Test::Unit::TestCase include ClientTester def test_simple_query state { |iq| assert_kind_of(Iq, iq) assert_equal(JID.new('juliet@capulet.com'), iq.to) assert_equal(:get, iq.type) assert_kind_of(LastActivity::IqQueryLastActivity, iq.query) send(" <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'> <query xmlns='jabber:iq:last' seconds='903'/> </iq>") } res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com') wait_state assert_equal(903, res.seconds) assert_nil(res.text) end def test_text_query state { |iq| send(" <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'> <query xmlns='jabber:iq:last' seconds='903'>Heading Home</query> </iq>") } res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com') wait_state assert_equal(903, res.seconds) assert_equal('Heading Home', res.text) end def test_empty_query state { |iq| send(" <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'> <query xmlns='jabber:iq:last'/> </iq>") } res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com') wait_state assert_nil(res.seconds) assert_nil(res.text) end def test_forbidden_query state { |iq| send(" <iq type='error' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>") } assert_raises(ServerError) { LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com') } end end ���������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_jid.rb�������������������������������������������������������������������������0000755�0001750�0001750�00000012712�13647121573�016753� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/jid' require 'xmpp4r/xmppelement' require 'xmpp4r/errors' include Jabber class JIDTest < Test::Unit::TestCase def test_create1 j = JID.new('a', 'b', 'c') assert_equal('a', j.node) assert_equal('b', j.domain) assert_equal('c', j.resource) end def test_create2 j = JID.new('a', 'b', 'c') j2 = JID.new(j) assert_equal('a', j2.node) assert_equal('b', j2.domain) assert_equal('c', j2.resource) assert_equal('a@b/c', j.to_s) end def test_create3 j = JID.new('a@b/c') assert_equal('a', j.node) assert_equal('b', j.domain) assert_equal('c', j.resource) assert_equal('a@b/c', j.to_s) end def test_create4 j = JID.new('a@b') assert_equal('a', j.node) assert_equal('b', j.domain) assert_equal(nil, j.resource) assert_equal('a@b', j.to_s) end def test_create5 j = JID.new assert_equal(nil, j.node) assert_equal(nil, j.domain) assert_equal(nil, j.resource) assert_equal('', j.to_s) end def test_create6 j = JID.new('dom') assert_equal(nil, j.node) assert_equal('dom', j.domain) assert_equal(nil, j.resource) assert_equal('dom', j.to_s) end def test_create7 j = JID.new('dom/res') assert_equal(nil, j.node) assert_equal('dom', j.domain) assert_equal('res', j.resource) assert_equal('dom/res', j.to_s) end def test_create8 j = JID.new('dom/a@b') assert_equal(nil, j.node) assert_equal('dom', j.domain) assert_equal('a@b', j.resource) assert_equal('dom/a@b', j.to_s) end def test_create9 assert_nothing_raised { JID.new("#{'n'*1023}@#{'d'*1023}/#{'r'*1023}") } assert_raises(Jabber::ArgumentError) { JID.new("#{'n'*1024}@#{'d'*1023}/#{'r'*1023}") } assert_raises(Jabber::ArgumentError) { JID.new("#{'n'*1023}@#{'d'*1024}/#{'r'*1023}") } assert_raises(Jabber::ArgumentError) { JID.new("#{'n'*1023}@#{'d'*1023}/#{'r'*1024}") } end def test_create10 j = JID.new('@b/c') assert_equal('', j.node) assert_equal('b', j.domain) assert_equal('c', j.resource) assert_equal('@b/c', j.to_s) end def test_create11 j = JID.new('@b') assert_equal('', j.node) assert_equal('b', j.domain) assert_equal(nil, j.resource) assert_equal('@b', j.to_s) end def test_create12 j = JID.new('@b/') assert_equal('', j.node) assert_equal('b', j.domain) assert_equal('', j.resource) assert_equal('@b/', j.to_s) end def test_create13 j = JID.new('a@b/') assert_equal('a', j.node) assert_equal('b', j.domain) assert_equal('', j.resource) assert_equal('a@b/', j.to_s) end def test_create14 j = JID.new('nOdE@dOmAiN/rEsOuRcE') assert_equal('node', j.node) assert_equal('domain', j.domain) assert_equal('rEsOuRcE', j.resource) assert_equal('node@domain/rEsOuRcE', j.to_s) end def test_tos assert_equal('', JID.new.to_s) assert_equal('domain.fr', JID.new('domain.fr').to_s) assert_equal('l@domain.fr', JID.new('l','domain.fr').to_s) assert_equal('l@domain.fr/res', JID.new('l','domain.fr','res').to_s) assert_equal('domain.fr/res', JID.new(nil,'domain.fr','res').to_s) end def test_equal assert_equal(JID.new('domain.fr'), JID.new('domain.fr')) assert_equal(JID.new('domain.fr'), JID.new(nil, 'domain.fr')) assert_equal(JID.new('l@domain.fr'), JID.new('l@domain.fr')) assert_equal(JID.new('l@domain.fr'), JID.new('l', 'domain.fr')) assert_equal(JID.new('l@domain.fr/res'), JID.new('l@domain.fr/res')) assert_equal(JID.new('l@domain.fr/res'), JID.new('l', 'domain.fr', 'res')) end def test_hash h = {} j = JID.new('l@domain.fr/res') h[j] = 'a' assert_equal(h[j], h[JID.new('l@domain.fr/res')]) end def test_strip assert_equal(JID.new('l@domain.fr'), JID.new('l@domain.fr/res').strip) assert_equal(JID.new('l@domain.fr'), JID.new('l@domain.fr').strip) assert_equal(JID.new('l@domain.fr'), JID.new('l@domain.fr/res').bare) jid = JID.new('l@domain.fr/res') jid.strip! assert_equal(JID.new('l@domain.fr'), jid) jid = JID.new('l@domain.fr/res') jid.bare! assert_equal(JID.new('l@domain.fr'), jid) end def test_change1 j = JID.new('a@b/c') j.node = 'd' assert_equal('d@b/c', j.to_s) j.domain = 'e' assert_equal('d@e/c', j.to_s) j.resource = 'f' assert_equal('d@e/f', j.to_s) end def test_escaping j = JID.new('user1@server1') j2 = JID.new(JID::escape(j), 'server2', 'res2') assert_equal('user1%server1@server2/res2', j2.to_s) end if defined?(libidnbug) # this crashes the interpreter def test_invalidnode # assert_raises(IDN::Stringprep::StringprepError) { JID.new('toto@a/a', 'server', 'res') } assert_raises(IDN::Stringprep::StringprepError) { IDN::Stringprep.nodeprep('toto@a/a') } end end def test_empty assert(JID.new.empty?) assert(!JID.new("test").empty?) end def test_stripped assert(JID.new("node@domain").stripped?) assert(!JID.new("node@domain/res").stripped?) assert(JID.new("node@domain").bared?) assert(!JID.new("node@domain/res").bared?) end def test_sort assert_equal(-1, JID.new('a@b') <=> JID.new('b@b')) assert_equal(0, JID.new('a@b') <=> JID.new('a@b')) assert_equal(1, JID.new('a@b/r') <=> JID.new('a@b')) jids = [JID.new('b@b'), JID.new('a@b/r'), JID.new('a@b')] jids.sort! assert_equal([JID.new('a@b'), JID.new('a@b/r'), JID.new('b@b')], jids) end end ������������������������������������������������������xmpp4r-0.5.6/test/tc_callbacks.rb�������������������������������������������������������������������0000755�0001750�0001750�00000005273�13647121573�020130� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/callbacks' include Jabber class CallbacksTest < Test::Unit::TestCase def test_test1 called = 0 cb = Callback.new(5, "toto", Proc.new { called += 1 }) assert_equal(5, cb.priority) assert_equal("toto", cb.ref) cb.block.call assert_equal(1, called) cb.block.call assert_equal(2, called) end def test_callbacklist1 cbl = CallbackList.new called1 = false called2 = false called3 = false called4 = false cbl.add(5, "ref1") { called1 = true ; true } cbl.add(7, "ref1") { |e| called2 = true ; false} cbl.add(9, "ref1") { called3 = true ;false } cbl.add(11, "ref1") { called4 = true ; false } o = "aaaa" assert(cbl.process(o)) assert(called1) assert(called2) assert(called3) assert(called4) end def test_callbacklist2 cbl = CallbackList.new assert(0, cbl.length) cbl.add(5, "ref1") { called1 = true } assert(1, cbl.length) cbl.add(7, "ref2") { |e| called2 = true ; e.consume } assert(2, cbl.length) cbl.delete("ref2") assert(1, cbl.length) cbl.add(9, "ref3") { called3 = true } assert(2, cbl.length) end def test_callbacklist4 cbl = CallbackList.new cbl.add(5, "ref1") { false } cbl.add(7, "ref1") { false } o = "o" assert(!cbl.process(o)) end def test_callbacklist5 cbl = CallbackList.new cbl.add(5, "ref1") { true } cbl.add(7, "ref1") { false } o = "o" assert(cbl.process(o)) end def test_callbacklist6 cbl = CallbackList.new ok = false c = 'a' d = 'b' cbl.add(5, "ref1") { |a, b| if a == 'a' and b == 'b' ok = true end false } assert(!cbl.process(c, d)) assert(ok) end def test_callbacklist7 cbl = CallbackList.new called1 = false called2 = false called3 = false called4 = false cbl.add(3, "ref1") { called4 = true ; true } cbl.add(5, "ref1") { called1 = true ; true } cbl.add(7, "ref1") { called2 = true ; 'a'} cbl.add(9, "ref1") { called3 = true ;1 } o = "aaaa" assert(cbl.process(o)) assert(called1) assert(called2) assert(called3) assert(!called4) end def test_nested cbl = CallbackList.new called_outer = 0 called_inner = 0 cbl.add(100, nil) { called_outer += 1 if called_outer == 1 cbl.add(200, nil) { called_inner += 1 } end } assert_equal(0, called_inner) assert_equal(0, called_outer) cbl.process assert_equal(0, called_inner) assert_equal(1, called_outer) cbl.process assert_equal(1, called_inner) assert_equal(2, called_outer) end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/caps/�����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016112� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/caps/tc_helper.rb�����������������������������������������������������������������0000755�0001750�0001750�00000013533�13647121573�020414� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# coding: utf-8 $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/discovery' require 'xmpp4r/caps/helper/helper' require 'xmpp4r/dataforms' include Jabber class Caps::HelperTest < Test::Unit::TestCase include ClientTester ## # Walk through the client/ server conversation defined # in http://www.xmpp.org/extensions/xep-0115.html#usecases # and assert conformance. def test_caps_reply # This will be invoked by 'wait_state' below... state { |presence| assert_kind_of(Jabber::Presence, presence) c = presence.first_element('c') assert_kind_of(Jabber::Caps::C, c) # see http://www.xmpp.org/extensions/xep-0115.html#ver assert_equal('SrFo9ar2CCk2EnOH4q4QANeuxLQ=', c.ver) # version 1.5 of xep 0115 indicates that the <c /> stanzq MUST feature a 'hash' attribute assert_equal('sha-1', c.hash) assert_equal("http://home.gna.org/xmpp4r/##{Jabber::XMPP4R_VERSION}", c.node) } # Construct Caps::Helper which will send a <presence> # stanza (with embedded <c/> advert) to the 'server' h = Caps::Helper.new(@client, identities, features) # The 'server' will receive the <presence> stanza and # yield it to the 'state' block above, where an <iq> query # will be sent back to the 'client,' to discover its capabilities. # Wait here until the block has been executed. wait_state # The Caps::Helper will process the <iq> query from the 'server' # and reply with an <iq> result providing the details of its # identities and features. state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:result, iq.type) assert_equal('1867999907', iq.id) assert_equal('new-big-computer.local', iq.to.to_s) assert_kind_of(Jabber::Discovery::IqQueryDiscoInfo, iq.query) assert_equal(iq.query.identities.size, identities.size) get_category_iname_type = lambda{ |i| [i.category,i.iname,i.type] } assert_equal(iq.query.identities.map(&get_category_iname_type).sort!, identities.map(&get_category_iname_type).sort!) assert_equal(3, iq.query.features.size) get_var = lambda { |f| f.var } assert_equal(iq.query.features.sort, features.map(&get_var).sort) } send(iq_discovering_capabilities) # The 'server' will receive the <iq> result from the # 'client' and yield it to the block above. Wait here # until that block exits. wait_state end def test_custom_node client_id='http://new-big-computer.local/client#321' state { |presence| c = presence.first_element('c') assert_kind_of(Jabber::Caps::C, c) assert_equal(client_id, c.node) assert_equal('SrFo9ar2CCk2EnOH4q4QANeuxLQ=', c.ver) } h = Caps::Helper.new(@client, identities, features, client_id) wait_state end def identities [Jabber::Discovery::Identity.new('client', 'Exodus 0.9.1', 'pc')] end def features [Jabber::Discovery::Feature.new("http://jabber.org/protocol/disco#info"), Jabber::Discovery::Feature.new("http://jabber.org/protocol/disco#items"), Jabber::Discovery::Feature.new("http://jabber.org/protocol/muc")] end def iq_discovering_capabilities "<iq from='new-big-computer.local' type='get' to='matt@new-big-computer.local/capable_client' id='1867999907' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>" end ## # http://www.xmpp.org/extensions/xep-0115.html#ver-gen-complex def test_caps_complex form = Dataforms::XData.new(:result) form.add(Dataforms::XDataField.new('FORM_TYPE', :hidden)).value = 'urn:xmpp:dataforms:softwareinfo' form.add(Dataforms::XDataField.new('ip_version')).values = ['ipv4', 'ipv6'] form.add(Dataforms::XDataField.new('software')).value = 'Psi' # re-ordered form.add(Dataforms::XDataField.new('software_version')).value = '0.11' form.add(Dataforms::XDataField.new('os')).value = 'Mac' form.add(Dataforms::XDataField.new('os_version')).value = '10.5.1' ver = Caps::generate_ver([Discovery::Identity.new('client', 'Psi 0.9.1', 'pc').set_xml_lang('en'), Discovery::Identity.new('client', 'Ψ 0.9.1', 'pc').set_xml_lang('el')], [Discovery::Feature.new('http://jabber.org/protocol/muc'), # re-ordered Discovery::Feature.new('http://jabber.org/protocol/disco#info'), Discovery::Feature.new('http://jabber.org/protocol/disco#items')], [form]) assert_equal('8lu+88MRxmKM7yO3MEzY7YmTsWs=', ver) end ## # http://www.xmpp.org/extensions/xep-0115.html#ver-gen-complex def test_caps_complex_imported query = IqQuery::import(REXML::Document.new(<<END).root) <query xmlns='http://jabber.org/protocol/disco#info' node='http://psi-im.org#8lu+88MRxmKM7yO3MEzY7YmTsWs='> <identity xml:lang='en' category='client' name='Psi 0.9.1' type='pc'/> <identity xml:lang='el' category='client' name='Ψ 0.9.1' type='pc'/> <feature var='http://jabber.org/protocol/disco#info'/> <feature var='http://jabber.org/protocol/disco#items'/> <feature var='http://jabber.org/protocol/muc'/> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:dataforms:softwareinfo</value> </field> <field var='ip_version'> <value>ipv4</value> <value>ipv6</value> </field> <field var='os'> <value>Mac</value> </field> <field var='os_version'> <value>10.5.1</value> </field> <field var='software'> <value>Psi</value> </field> <field var='software_version'> <value>0.11</value> </field> </x> </query> END assert_equal('8lu+88MRxmKM7yO3MEzY7YmTsWs=', Caps::generate_ver_from_discoinfo(query)) end end ���������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/delay/����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016262� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/delay/tc_xdelay.rb����������������������������������������������������������������0000755�0001750�0001750�00000002312�13647121573�020564� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/delay/x/delay' include Jabber class XDelayTest < Test::Unit::TestCase def test_create1 d = Delay::XDelay.new(false) assert_equal(nil, d.stamp) assert_equal(nil, d.from) assert_equal('jabber:x:delay', d.namespace) end def test_create2 d = Delay::XDelay.new # Hopefully the seconds don't change here... assert_equal(Time.now.to_s, d.stamp.to_s) assert_equal(nil, d.from) assert_equal('jabber:x:delay', d.namespace) end def test_from d = Delay::XDelay.new assert_equal(nil, d.from) d.from = JID.new('astro@spaceboyz.net') assert_equal(JID.new('astro@spaceboyz.net'), d.from) assert_equal(d, d.set_from(nil)) assert_equal(nil, d.from) end def test_stamp d = Delay::XDelay.new(false) assert_equal(nil, d.stamp) now = Time.now d.stamp = now assert_equal(now.to_s, d.stamp.to_s) assert_equal(d, d.set_stamp(nil)) assert_equal(nil, d.stamp) end def test_import x1 = X.new x1.add_namespace('jabber:x:delay') x2 = X::import(x1) assert_equal(Delay::XDelay, x2.class) end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/bytestreams/����������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�017526� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/bytestreams/tc_socks5bytestreams.rb�����������������������������������������������0000755�0001750�0001750�00000005061�13647121573�024240� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/bytestreams' include Jabber class SOCKS5BytestreamsTest < Test::Unit::TestCase include ClientTester @@server = Bytestreams::SOCKS5BytestreamsServer.new(65005) @@server.add_address('localhost') def create_buffer(size) ([nil] * size).collect { rand(256).chr }.join end def test_server2multi target1 = Bytestreams::SOCKS5BytestreamsTarget.new(@server, '1', '1@a.com/1', '1@a.com/2') target2 = Bytestreams::SOCKS5BytestreamsTarget.new(@server, '2', '2@a.com/1', '2@a.com/2') initiator1 = Bytestreams::SOCKS5BytestreamsInitiator.new(@client, '1', '1@a.com/1', '1@a.com/2') initiator2 = Bytestreams::SOCKS5BytestreamsInitiator.new(@client, '2', '2@a.com/1', '2@a.com/2') initiator1.add_streamhost(@@server) initiator2.add_streamhost(@@server) buf1 = create_buffer(8192) buf2 = create_buffer(8192) Thread.new do target1.accept target1.write(buf1) target1.flush target1.close end Thread.new do target2.accept target2.write(buf2) target2.flush target2.close end target1.accept_wait target2.accept_wait initiator1.open initiator2.open recv1 = '' recv2 = '' while buf = initiator2.read(256) recv2 += buf end while buf = initiator1.read(256) recv1 += buf end initiator1.close initiator2.close assert_equal(buf1, recv1) assert_equal(buf2, recv2) end def test_pingpong target = Bytestreams::SOCKS5BytestreamsTarget.new(@server, '1', '1@a.com/1', '1@a.com/2') initiator = Bytestreams::SOCKS5BytestreamsInitiator.new(@client, '1', '1@a.com/1', '1@a.com/2') initiator.add_streamhost(@@server) Thread.new do target.accept while buf = target.read(256) target.write(buf) target.flush end target.close end target.accept_wait initiator.open 10.times do buf = create_buffer(8192) initiator.write(buf) initiator.flush bufr = '' begin bufr += initiator.read(256) end while bufr.size < buf.size assert_equal(buf, bufr) end initiator.close end def test_timeout target = Bytestreams::SOCKS5BytestreamsTarget.new(@server, '1', '1@a.com/1', '1@a.com/2') assert_nothing_raised do Timeout::timeout(1) do target.connect_timeout = 0.5 assert target.accept == false end end end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/bytestreams/tc_ibb.rb�������������������������������������������������������������0000755�0001750�0001750�00000010411�13647121573�021275� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/bytestreams' require 'xmpp4r/semaphore' include Jabber class IBBTest < Test::Unit::TestCase include ClientTester def create_buffer(size) ([nil] * size).collect { rand(256).chr }.join end def test_ibb_target2initiator target = Bytestreams::IBBTarget.new(@server, '1', nil, '1@a.com/1') initiator = Bytestreams::IBBInitiator.new(@client, '1', nil, '1@a.com/1') buffer = create_buffer(9999) Thread.new do target.accept target.write(buffer) Thread.pass target.close end target.accept_wait initiator.open received = '' while buf = initiator.read received += buf end initiator.close assert_equal(buffer, received) end def test_ibb_initiator2target target = Bytestreams::IBBTarget.new(@server, '1', nil, '1@a.com/1') initiator = Bytestreams::IBBInitiator.new(@client, '1', nil, '1@a.com/1') buffer = create_buffer(9999) Thread.new do target.accept_wait initiator.open initiator.write(buffer) initiator.close end target.accept received = '' while buf = target.read received += buf end target.close assert_equal(buffer, received) end def test_ibb_pingpong ignored_stanzas = 0 wait = Semaphore.new @server.add_message_callback { ignored_stanzas += 1; wait.run } @server.add_iq_callback { ignored_stanzas += 1; wait.run } target = Bytestreams::IBBTarget.new(@server, '1', nil, '1@a.com/1') initiator = Bytestreams::IBBInitiator.new(@client, '1', nil, '1@a.com/1') Thread.new do target.accept while buf = target.read target.write(buf) target.flush end target.close end assert_equal(0, ignored_stanzas) @client.send("<iq from='1@a.com/1' type='set'> <close xmlns='http://jabber.org/protocol/ibb' sid='another session id'/> </iq>") wait.wait assert_equal(1, ignored_stanzas) target.accept_wait initiator.open assert_equal(1, ignored_stanzas) @client.send("<message from='1@a.com/1' type='error'> <data xmlns='http://jabber.org/protocol/ibb' sid='another session id' seq='0'/> </message>") wait.wait assert_equal(2, ignored_stanzas) @client.send("<iq from='1@a.com/1' type='set'> <close xmlns='http://jabber.org/protocol/ibb' sid='another session id'/> </iq>") wait.wait assert_equal(3, ignored_stanzas) 5.times do buf = create_buffer(100) initiator.write(buf) initiator.flush bufr = '' begin bufr += initiator.read end while bufr.size < buf.size assert_equal(buf, bufr) end initiator.close assert_equal(3, ignored_stanzas) end def test_ibb_error target = Bytestreams::IBBTarget.new(@server, '1', nil, '1@a.com/1') initiator = Bytestreams::IBBInitiator.new(@client, '1', nil, '1@a.com/1') Thread.new do target.accept @server.send("<message from='1@a.com/1' type='error'> <data xmlns='http://jabber.org/protocol/ibb' sid='#{target.instance_variable_get(:@session_id)}' seq='0'/> </message>") end target.accept_wait initiator.open assert_nil(initiator.read) initiator.close end def test_ibb_inactive target = Bytestreams::IBBTarget.new(@server, '1', nil, '1@a.com/1') initiator = Bytestreams::IBBInitiator.new(@client, '1', nil, '1@a.com/1') assert_nil(target.read) assert_nil(initiator.read) assert_raise(RuntimeError) { target.write('a' * target.block_size) } assert_raise(RuntimeError) { initiator.write('a' * initiator.block_size) } end def test_ibb_queueitem i1 = Bytestreams::IBBQueueItem.new(:close) assert_equal(:close, i1.type) assert_nil(i1.seq) i2 = Bytestreams::IBBQueueItem.new(:data, 1, ['blah'].pack('m')) assert_equal(:data, i2.type) assert_equal(1, i2.seq) assert_equal('blah', i2.data) assert_raise(RuntimeError) { i3 = Bytestreams::IBBQueueItem.new(:invalid) } end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_iq.rb��������������������������������������������������������������������������0000755�0001750�0001750�00000007036�13647121573�016621� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'socket' require 'xmpp4r/rexmladdons' require 'xmpp4r/iq' require 'xmpp4r/errors' include Jabber require File::dirname(__FILE__) + '/lib/assert_equal_xml' class IqTest < Test::Unit::TestCase def test_create x = Iq.new() assert_equal("iq", x.name) assert_equal("jabber:client", x.namespace) assert_equal_xml("<iq xmlns='jabber:client'/>", x) end def test_iqauth x = Iq.new_authset(JID.new('node@domain/resource'), 'password') assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:auth'><username>node</username><password>password</password><resource>resource</resource></query></iq>", x) end def test_iqauth_digest x = Iq.new_authset_digest(JID.new('node@domain/resource'), '', 'password') assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:auth'><username>node</username><digest>#{Digest::SHA1.hexdigest('password')}</digest><resource>resource</resource></query></iq>", x) end def test_register x1 = Iq.new_register assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:register'/></iq>", x1) x2 = Iq.new_register('node') assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:register'><username>node</username></query></iq>", x2) x3 = Iq.new_register('node', 'password') assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:register'><username>node</username><password>password</password></query></iq>", x3) end def test_rosterget x = Iq.new_rosterget assert_equal_xml("<iq type='get' xmlns='jabber:client'><query xmlns='jabber:iq:roster'/></iq>", x) end def test_rosterset x = Iq.new_rosterset assert_equal_xml("<iq type='set' xmlns='jabber:client'><query xmlns='jabber:iq:roster'/></iq>", x) end def test_browseget x = Iq.new_browseget assert_equal_xml("<iq type='get' xmlns='jabber:client'><query xmlns='jabber:iq:browse'/></iq>", x) end def test_types iq = Iq.new assert_equal(nil, iq.type) iq.type = :get assert_equal(:get, iq.type) iq.type = :set assert_equal(:set, iq.type) iq.type = :result assert_equal(:result, iq.type) iq.type = :error assert_equal(:error, iq.type) iq.type = :invalid assert_equal(nil, iq.type) end def test_query x = Iq.new(:set) assert_equal(nil, x.queryns) query = REXML::Element.new('query') x.add(query) assert_equal('jabber:client', x.queryns) query.add_namespace('jabber:iq:auth') assert_equal(query.to_s, x.query.to_s) assert_equal('jabber:iq:auth', x.queryns) query2 = REXML::Element.new('query') x.query = query2 assert_equal('jabber:client', x.queryns) query2.add_namespace('jabber:iq:register') assert_equal('jabber:iq:register', x.queryns) end def test_vcard x = Iq.new assert_equal(nil, x.vcard) x.add(vcard = REXML::Element.new('vCard')) assert_equal(vcard, x.vcard) end def test_error x = Iq.new(:set) e = REXML::Element.new('error') x.add(e) # test if, after an import, the error element is successfully changed # into an ErrorResponse object. x2 = Iq.new.import(x) assert_equal(ErrorResponse, x2.first_element('error').class) end def test_new_query x = Iq.new_query(:get, JID.new('a@b/c')) assert_equal(:get, x.type) assert_equal(nil, x.from) assert_equal(JID.new('a@b/c'), x.to) assert_kind_of(IqQuery, x.query) assert_equal('jabber:client', x.queryns) end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_xmppstanza.rb������������������������������������������������������������������0000755�0001750�0001750�00000006363�13647121573�020417� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'socket' require 'xmpp4r/rexmladdons' require 'xmpp4r/xmppstanza' require 'xmpp4r/iq' require 'xmpp4r/feature_negotiation' require 'xmpp4r/dataforms' require 'xmpp4r/errors' include Jabber class XMPPStanzaTest < Test::Unit::TestCase ## # Hack: XMPPStanza derives from XMPPElement # which enforces element classes to be named at declaration time class MyXMPPStanza < XMPPStanza name_xmlns 'stanza', 'http://home.gna.org/xmpp4r' end class MyStanza < XMPPStanza end def test_from x = MyXMPPStanza.new assert_equal(nil, x.from) assert_equal(x, x.set_from("blop")) assert_equal("blop", x.from.to_s) x.from = "tada" assert_equal("tada", x.from.to_s) end def test_to x = MyXMPPStanza.new assert_equal(nil, x.to) assert_equal(x, x.set_to("blop")) assert_equal("blop", x.to.to_s) x.to = "tada" assert_equal("tada", x.to.to_s) end def test_id x = MyXMPPStanza.new assert_equal(nil, x.id) assert_equal(x, x.set_id("blop")) assert_equal("blop", x.id) x.id = "tada" assert_equal("tada", x.id) end def test_type x = MyXMPPStanza.new assert_equal(nil, x.type) assert_equal(x, x.set_type("blop")) assert_equal("blop", x.type) x.type = "tada" assert_equal("tada", x.type) end def test_import x = MyXMPPStanza.new x.id = "heya" q = x.add_element("query") q.add_namespace("about:blank") q.add_element("b").text = "I am b" q.add_text("I am text") q.add_element("a").add_attribute("href", "http://home.gna.org/xmpp4r/") x.add_text("yow") x.add_element("query") assert_raise(RuntimeError) { iq = Iq.import(x) } x.name = 'iq' iq = Iq.import(x) assert_equal(x.id, iq.id) assert_equal(q.to_s, iq.query.to_s) assert_equal(x.to_s, iq.to_s) assert_equal(q.namespace, iq.queryns) end def test_import2 feature = FeatureNegotiation::IqFeature.new xdata = feature.add(Dataforms::XData.new(:form)) field = xdata.add(Dataforms::XDataField.new('stream-method', :list_single)) feature2 = FeatureNegotiation::IqFeature.new.import(feature) assert_equal(field.var, feature2.x.fields.first.var) assert_equal(field.type, feature2.x.fields.first.type) end def test_import_xml_entities e = REXML::Element.new('e') e.text = '&' assert_equal('<e>&</e>', e.to_s) e2 = REXML::Element.new('e').import(e) assert_equal('<e>&</e>', e2.to_s) end def test_error x = MyXMPPStanza.new assert_equal(nil, x.error) x.typed_add(REXML::Element.new('error')) assert_equal('<error/>', x.error.to_s) assert_equal(ErrorResponse, x.error.class) end def test_clone_and_dup x = MyXMPPStanza.new x.attributes['xyz'] = '123' x.text = 'abc' assert_equal(x.attributes['xyz'], '123') assert_equal(x.text, 'abc') x2 = x.clone assert_kind_of(MyXMPPStanza, x2) assert_equal('123', x2.attributes['xyz']) assert_nil(x2.text) x3 = x.dup assert_kind_of(MyXMPPStanza, x3) assert_equal('123', x3.attributes['xyz']) assert_equal('abc', x3.text) end def test_raise assert_raises(NoNameXmlnsRegistered) { XMPPStanza.name_xmlns_for_class(MyStanza) } end end �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/discovery/������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�017173� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/discovery/tc_responder.rb���������������������������������������������������������0000755�0001750�0001750�00000006636�13647121573�022225� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/discovery/helper/responder' include Jabber class Discovery::ResponderTest < Test::Unit::TestCase include ClientTester def test_new r = Discovery::Responder.new(@client) assert_nil(r.node) assert_equal([], r.identities) assert_equal([], r.features) assert_equal([], r.forms) assert_equal([], r.items) end def test_new2 r = Discovery::Responder.new(@client, 'xmpp4r', [Discovery::Identity.new('client', 'XMPP4R', 'bot'), Discovery::Identity.new('pubsub', 'Personal events', 'pep')], ['ipv6', Discovery::Feature.new('sslc2s')], [Discovery::Item.new('foo@bar', 'Foo', nil), Discovery::Item.new('bar@baz', 'Bar', 'barbaz')]) assert_equal('xmpp4r', r.node) assert_equal([Discovery::Identity.new('client', 'XMPP4R', 'bot'), Discovery::Identity.new('pubsub', 'Personal events', 'pep')], r.identities) assert_equal([Discovery::Feature.new('ipv6'), Discovery::Feature.new('sslc2s')], r.features) assert_equal([], r.forms) assert_equal([Discovery::Item.new('foo@bar', 'Foo', nil), Discovery::Item.new('bar@baz', 'Bar', 'barbaz')], r.items) end def test_generate_item r = Discovery::Responder.new(@client, nil, [Discovery::Identity.new('client', 'XMPP4R', 'bot')]) assert_equal(Discovery::Item.new(@client.jid, 'XMPP4R'), r.generate_item) end def test_query Discovery::Responder.new(@client, nil, [Discovery::Identity.new('client', 'XMPP4R', 'bot')], ['ipv6'], [Discovery::Item.new('foo@bar', 'Foo', nil)]) iq1 = Iq.new(:get) iq1.add(Discovery::IqQueryDiscoInfo.new) reply1 = @server.send_with_id(iq1) assert_equal(:result, reply1.type) assert_kind_of(Discovery::IqQueryDiscoInfo, reply1.query) assert_nil(reply1.query.node) assert_equal(1, reply1.query.identities.size) assert_equal('XMPP4R', reply1.query.identities[0].iname) assert_equal(['ipv6'], reply1.query.features) iq2 = Iq.new(:get) iq2.add(Discovery::IqQueryDiscoItems.new) reply2 = @server.send_with_id(iq2) assert_equal(:result, reply2.type) assert_kind_of(Discovery::IqQueryDiscoItems, reply2.query) assert_nil(reply2.query.node) assert_equal(1, reply2.query.items.size) assert_equal(JID.new('foo@bar'), reply2.query.items[0].jid) end def test_linked class << @client remove_method(:jid) # avoids warning def jid JID.new('foo@bar/baz') end end r1 = Discovery::Responder.new(@client, 'child', [Discovery::Identity.new('client', 'Child', 'bot')]) r2 = Discovery::Responder.new(@client, nil, [], [], [r1]) iq = Iq.new(:get) iq.add(Discovery::IqQueryDiscoItems.new) reply = @server.send_with_id(iq) assert_kind_of(Discovery::IqQueryDiscoItems, reply.query) assert_nil(reply.query.node) assert_equal(1, reply.query.items.size) assert_equal(JID.new('foo@bar/baz'), reply.query.items[0].jid) assert_equal('Child', reply.query.items[0].iname) assert_equal('child', reply.query.items[0].node) end end ��������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_streamComponent.rb�������������������������������������������������������������0000755�0001750�0001750�00000004224�13647121573�021362� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'tempfile' require 'test/unit' require 'socket' require 'xmpp4r/component' require 'xmpp4r/bytestreams' require 'xmpp4r/semaphore' require 'xmpp4r' include Jabber class StreamComponentTest < Test::Unit::TestCase @@SOCKET_PORT = 65224 STREAM = 'stream:stream xmlns:stream="http://etherx.jabber.org/streams"' def setup servlisten = TCPServer.new(@@SOCKET_PORT) serverwait = Semaphore.new Thread.new do Thread.current.abort_on_exception = true serversock = servlisten.accept servlisten.close serversock.sync = true @server = Stream.new @server.add_xml_callback do |xml| if xml.prefix == 'stream' and xml.name == 'stream' @server.send("<#{STREAM} xmlns='jabber:component:accept'>") true else false end end @server.start(serversock) serverwait.run end @stream = Component.new('test') @stream.connect('localhost', @@SOCKET_PORT) serverwait.wait end def teardown n = 0 while @stream.processing > 0 and n < 1000 Thread::pass n += 1 end n = 0 while @server.processing > 0 and n < 1000 Thread::pass n += 1 end #@stream.close @server.close end def test_process stanzas = 0 message_lock = Semaphore.new iq_lock = Semaphore.new presence_lock = Semaphore.new @stream.add_message_callback { |msg| assert_kind_of(Message, msg) stanzas += 1 message_lock.run } @stream.add_iq_callback { |iq| assert_kind_of(Iq, iq) stanzas += 1 iq_lock.run } @stream.add_presence_callback { |pres| assert_kind_of(Presence, pres) stanzas += 1 presence_lock.run } @server.send('<message/>') @server.send('<iq/>') @server.send('<presence/>') message_lock.wait iq_lock.wait presence_lock.wait assert_equal(3, stanzas) end def test_outgoing received_wait = Semaphore.new @server.add_message_callback { |msg| assert_kind_of(Message, msg) received_wait.run } @stream.send(Message.new) received_wait.wait end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/entity_time/����������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�017516� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/entity_time/tc_responder.rb�������������������������������������������������������0000644�0001750�0001750�00000003631�13647121573�022535� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/entity_time/responder' include Jabber class EntityTime::ResponderTest < Test::Unit::TestCase include ClientTester def a_utc_time Time.utc(2000,"jan",1,20,15,1) # xmlschema = "2000-01-01T20:15:01Z" end def test_entity_time_iq el_time = EntityTime::IqTime.new(a_utc_time) assert_equal('time', el_time.name, "element has wrong name") assert_equal('urn:xmpp:time', el_time.namespace, "element has wrong namespace") assert(el_time.elements["utc"], "element does not have a utc child") assert_equal('2000-01-01T20:15:01Z', el_time.elements["utc"].text, "element/utc has the wrong text") assert(el_time.elements["tzo"], "element does not have a tzo child") assert_equal("+00:00", el_time.elements["tzo"].text, "element/tzo has the wrong time zone offset") end =begin http://xmpp.org/extensions/xep-0202.html <iq type='get' from='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='time_1'> <time xmlns='urn:xmpp:time'/> </iq> =end def test_query EntityTime::Responder.new(@client) iq = Iq.new(:get) t = REXML::Element.new('time') t.add_namespace('urn:xmpp:time') iq.add_element(t) reply = @server.send_with_id(iq) assert_equal(:result, reply.type) assert_equal('time', reply[0].name) assert_equal('urn:xmpp:time', reply[0].namespace) assert(reply[0].elements["utc"].has_text?, "element must have a utc time (actual time should be check here)") tmp = Time.now local_offset = Time.now.utc_offset hour_offset = local_offset / 3600 min_offset = local_offset % 60 offset = "%+03d:%02d"%[hour_offset, min_offset] assert_equal(offset, reply[0].elements["tzo"].text, "element should has an incorrect time zone offset") end end �������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/lib/������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�015732� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/lib/clienttester.rb���������������������������������������������������������������0000755�0001750�0001750�00000007211�13647121573�020770� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io $:.unshift '../lib' require 'xmpp4r' require 'test/unit' require 'socket' require 'xmpp4r/semaphore' # Jabber::debug = true $ctdebug = false # $ctdebug = true # This is sane for tests: Thread::abort_on_exception = true # Turn $VERBOSE off to suppress warnings about redefinition oldverbose = $VERBOSE $VERBOSE = false module Jabber ## # The ClientTester is a mix-in which provides a setup and teardown # method to prepare a Stream object (@client) and the method # interfacing as the "server side": # * send(xml):: Send a stanza to @client # # The server side is a stream, too: add your callbacks to @server # # ClientTester is written to test complex helper classes. module ClientTester @@SOCKET_PORT = 65223 def setup servlisten = TCPServer.new(@@SOCKET_PORT) serverwait = Semaphore.new stream = '<stream:stream xmlns:stream="http://etherx.jabber.org/streams">' @state = 0 @states = [] Thread.new do Thread.current.abort_on_exception = true serversock = servlisten.accept servlisten.close serversock.sync = true @server = Stream.new @server.add_xml_callback do |xml| if xml.prefix == 'stream' and xml.name == 'stream' send(stream) true else false end end @server.start(serversock) serverwait.run end clientsock = TCPSocket.new('localhost', @@SOCKET_PORT) clientsock.sync = true @client = Stream.new #=begin class << @client def jid begin #raise rescue puts $!.backtrace.join("\n") end JID.new('test@test.com/test') end end #=end @client.start(clientsock) @processdone_wait = Semaphore.new @nextstate_wait = Semaphore.new serverwait.wait @server.add_stanza_callback { |stanza| # Client prepares everything, then calls wait_state. Problem: because # of a race condition, it is possible that we receive the stanza before # what to do with it is defined. We busy-wait on @states here. n = 0 while @state >= @states.size and n < 1000 Thread.pass n += 1 end if n == 1000 puts "Unmanaged stanza in state. Maybe processed by helper?" if $ctdebug else begin puts "Calling #{@states[@state]} for #{stanza.to_s}" if $ctdebug @states[@state].call(stanza) rescue Exception => e puts "Exception in state: #{e.class}: #{e}\n#{e.backtrace.join("\n")}" end @state += 1 @nextstate_wait.wait @processdone_wait.run end false } @client.send(stream) { |reply| true } end def teardown # In some cases, we might lost count of some stanzas # (for example, if the handler raises an exception) # so we can't block forever. n = 0 while @client.processing > 0 and n < 1000 Thread::pass n += 1 end n = 0 while @server.processing > 0 and n < 1000 Thread::pass n += 1 end @client.close @server.close end def send(xml) @server.send(xml) end def state(&block) @states << block end def wait_state @nextstate_wait.run @processdone_wait.wait end def skip_state @nextstate_wait.run end end end # Restore the old $VERBOSE setting $VERBOSE = oldverbose ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/lib/assert_equal_xml.rb�����������������������������������������������������������0000644�0001750�0001750�00000000577�13647121573�021640� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������module Test::Unit::Assertions public ## # Like assert_equal, but actual comparision switches parameters to # make use of our attribute order agnostic REXML#== def assert_equal_xml(expected, actual, message=nil) full_message = build_message(message, <<EOT, expected, actual) <?> expected but was <?>. EOT assert_block(full_message) { actual == expected } end end ���������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/xhtml/����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�016320� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/xhtml/tc_html.rb������������������������������������������������������������������0000755�0001750�0001750�00000003102�13647121573�020276� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/xhtml' include Jabber class XHTML::HTMLTest < Test::Unit::TestCase def test_set contents1 = REXML::Element.new('p') contents1.text = 'Hello' html = XHTML::HTML.new(contents1) assert_kind_of(XHTML::Body, html.first_element('body')) assert_equal("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'><p>Hello</p></body></html>", html.to_s) contents2 = REXML::Element.new('a') contents2.attributes['href'] = 'about:blank' contents2.text = 'nothing' html.contents = ["Look at ", contents2] assert_equal("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>Look at <a href='about:blank'>nothing</a></body></html>", html.to_s) end def test_parse html = XHTML::HTML.new('There is a fine <a href="http://home.gna.org/xmpp4r/">library</a>') assert_equal("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>There is a fine <a href='http://home.gna.org/xmpp4r/'>library</a></body></html>", html.to_s) end def test_text a1 = REXML::Element.new('a') a1.attributes['href'] = 'http://www.jabber.org/' a1.text = 'Jabber' a2 = REXML::Element.new('a') a2.attributes['href'] = 'http://home.gna.org/xmpp4r/' a2.text = 'XMPP4R' html = XHTML::HTML.new(["Look at ", a1, " & ", a2]) assert_equal("Look at Jabber & XMPP4R", html.to_text) end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/tc_stream.rb����������������������������������������������������������������������0000755�0001750�0001750�00000006454�13647121573�017506� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' $:.unshift './lib/' require 'tempfile' require 'test/unit' require 'socket' require 'xmpp4r/stream' require 'xmpp4r/semaphore' require 'clienttester' include Jabber # Jabber::debug = true class StreamTest < Test::Unit::TestCase include ClientTester def busywait(&block) n = 0 while not block.call and n < 10000 Thread::pass n += 1 end end ## # tests that connection really waits the call to process() to dispatch # stanzas to filters def test_process called = false @client.add_xml_callback { called = true } assert(!called) @server.send('<iq/>') busywait { called } assert(called) end def test_process20 done = Semaphore.new n = 0 @client.add_message_callback { n += 1 done.run if n % 20 == 0 } 20.times { @server.send('<message/>') } done.wait assert_equal(20, n) @server.send('<message/>' * 20) done.wait assert_equal(40, n) end def test_send sem = Semaphore::new @server.add_xml_callback { |e| @server.send(Iq.new(:result)) sem.run } called = 0 @client.send(Iq.new(:get)) { |reply| called += 1 if reply.kind_of? Iq and reply.type == :result true else false end } sem.wait busywait { called } assert_equal(1, called) end def test_send_nested finished = Semaphore.new id = 0 @server.add_xml_callback do |e| id += 1 if id == 1 @server.send(Iq.new(:result).set_id('1').delete_namespace) elsif id == 2 @server.send(Iq.new(:result).set_id('2').delete_namespace) elsif id == 3 @server.send(Iq.new(:result).set_id('3').delete_namespace) else p e end end called_outer = 0 called_inner = 0 @client.send(Iq.new(:get)) do |reply| called_outer += 1 assert_kind_of(Iq, reply) assert_equal(:result, reply.type) if reply.id == '1' @client.send(Iq.new(:set)) do |reply2| called_inner += 1 assert_kind_of(Iq, reply2) assert_equal(:result, reply2.type) assert_equal('2', reply2.id) @client.send(Iq.new(:get)) true end false elsif reply.id == '3' true else false end end assert_equal(2, called_outer) assert_equal(1, called_inner) end def test_send_in_callback finished = Semaphore.new @client.add_message_callback { @client.send_with_id(Iq.new(:get)) { |reply| assert_equal(:result, reply.type) finished.run } } @server.add_iq_callback { |iq| @server.send(Iq.new(:result).set_id(iq.id)) } @server.send(Message.new) finished.wait end def test_similar_children n = 0 @client.add_message_callback { n += 1 } assert_equal(0, n) @server.send("<message/>") busywait { n == 1 } assert_equal(1, n) @server.send('<message>') assert_equal(1, n) @server.send('<message/>') assert_equal(1, n) @server.send('</message>') busywait { n == 2 } assert_equal(2, n) @server.send("<message><stream:stream><message/></stream:stream>") assert_equal(2, n) @server.send('</message>') busywait { n == 3 } assert_equal(3, n) end end ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/muc/������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13647121573�015750� 5����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xmpp4r-0.5.6/test/muc/tc_mucowner.rb����������������������������������������������������������������0000755�0001750�0001750�00000002542�13647121573�020630� 0����������������������������������������������������������������������������������������������������ustar �debbiecocoa���������������������debbiecocoa������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r' require 'xmpp4r/muc' include Jabber class MUCOwnerTest < Test::Unit::TestCase def test_parse s = <<EOF <iq from='darkcave@macbeth.shakespeare.lit' id='config1' to='crone1@shakespeare.lit/desktop' type='result'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='form'> <title>Configuration for "darkcave" Room Complete this form to make changes to the configuration of your room. http://jabber.org/protocol/muc#roomconfig A Dark Cave
EOF iq = Iq::import(REXML::Document.new(s).root) assert_kind_of(Iq, iq) assert_kind_of(MUC::IqQueryMUCOwner, iq.query) assert_kind_of(Dataforms::XData, iq.query.x) assert_kind_of(Dataforms::XData, iq.query.x('jabber:x:data')) assert_kind_of(Dataforms::XData, iq.query.x(Dataforms::XData)) assert_equal(1, iq.query.x.fields.size) assert_equal('Natural-Language Room Name', iq.query.x.fields[0].label) end end xmpp4r-0.5.6/test/muc/tc_muc_mucclient.rb0000755000175000017500000007500513647121573021624 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/muc' require 'xmpp4r/semaphore' include Jabber class MUCClientTest < Test::Unit::TestCase include ClientTester def test_new1 m = MUC::MUCClient.new(@client) assert_equal(nil, m.jid) assert_equal(nil, m.my_jid) assert_equal({}, m.roster) assert(!m.active?) end # JEP-0045: 6.3 Entering a Room def test_enter_room state { |pres| assert_kind_of(Presence, pres) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch'), pres.to) send("" + "" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch'), pres.to) send("" + "" + "" + "" + "" + "" + "" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert(!m.active?) assert_nil(m.room) assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } wait_state assert(!m.active?) assert_nil(m.room) assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) assert_equal('darkcave', m.room) assert_equal(3, m.roster.size) m.roster.each { |resource,pres| assert_equal(resource, pres.from.resource) assert_equal('darkcave', pres.from.node) assert_equal('macbeth.shakespeare.lit', pres.from.domain) assert_kind_of(String, resource) assert_kind_of(Presence, pres) assert(%w(firstwitch secondwitch thirdwitch).include?(resource)) assert_kind_of(MUC::XMUCUser, pres.x) assert_kind_of(Array, pres.x.items) assert_equal(1, pres.x.items.size) } assert_equal(:owner, m.roster['firstwitch'].x.items[0].affiliation) assert_equal(:moderator, m.roster['firstwitch'].x.items[0].role) assert_equal(:admin, m.roster['secondwitch'].x.items[0].affiliation) assert_equal(:moderator, m.roster['secondwitch'].x.items[0].role) assert_equal(:member, m.roster['thirdwitch'].x.items[0].affiliation) assert_equal(:participant, m.roster['thirdwitch'].x.items[0].role) assert_nil(m.roster['thirdwitch'].x.items[0].jid) send("" + "" + "") n = 0 while m.roster.size != 3 and n < 1000 Thread::pass n += 1 end assert_equal(3, m.roster.size) assert_equal(:none, m.roster['thirdwitch'].x.items[0].affiliation) assert_equal(:participant, m.roster['thirdwitch'].x.items[0].role) assert_equal(JID.new('hag66@shakespeare.lit/pda'), m.roster['thirdwitch'].x.items[0].jid) end def test_enter_room_password state { |pres| assert_kind_of(Presence, pres) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal('cauldron', pres.x.password) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } wait_state assert(!m.active?) assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch', 'cauldron')) wait_state assert(m.active?) end def test_members_only_room state { |pres| assert_kind_of(Presence, pres) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert(!m.active?) wait_state end def test_banned_users state { |pres| assert_kind_of(Presence, pres) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert(!m.active?) wait_state end def test_nickname_conflict state { |pres| assert_kind_of(Presence, pres) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert(!m.active?) wait_state end def test_max_users state { |pres| assert_kind_of(Presence, pres) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert(!m.active?) wait_state end def test_locked_room state { |pres| send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_raises(ServerError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert(!m.active?) wait_state end def test_exit_room state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.type) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(:unavailable, pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_nil(pres.status) send("" + "" + "") send("" + "" + "") } ignored_stanzas = 0 @client.add_stanza_callback { |stanza| ignored_stanzas += 1 } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(0, ignored_stanzas) assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) assert_equal(0, ignored_stanzas) assert_equal(m, m.exit) wait_state assert(!m.active?) assert_equal(1, ignored_stanzas) end def test_custom_exit_message state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.type) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(:unavailable, pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal('gone where the goblins go', pres.status) send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) assert(m.active?) wait_state assert_equal(m, m.exit('gone where the goblins go')) assert(!m.active?) wait_state end def test_joins state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch'), pres.to) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(:unavailable, pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch'), pres.to) assert_nil(pres.status) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/fourthwitch'), pres.to) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(:unavailable, pres.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/fourthwitch'), pres.to) assert_equal(pres.status, 'Exiting one last time') send("" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) assert_raises(RuntimeError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert_raises(RuntimeError) { m.join('darkcave@macbeth.shakespeare.lit/fourthwitch') } assert(m.active?) assert_equal(m, m.exit) wait_state assert(!m.active?) assert_raises(RuntimeError) { m.exit } assert(!m.active?) assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/fourthwitch')) wait_state assert(m.active?) assert_raises(RuntimeError) { m.join('darkcave@macbeth.shakespeare.lit/thirdwitch') } assert_raises(RuntimeError) { m.join('darkcave@macbeth.shakespeare.lit/fourthwitch') } assert(m.active?) assert_equal(m, m.exit('Exiting one last time')) wait_state assert(!m.active?) assert_raises(RuntimeError) { m.exit } assert(!m.active?) end def test_message_callback state { |pres| assert_kind_of(Presence, pres) assert_equal('cauldron', pres.x.password) send("" + "" + "") } message_lock = Semaphore.new messages_client = 0 @client.add_message_callback { |msg| messages_client += 1 message_lock.run } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' messages_muc = 0 m.add_message_callback { |msg| messages_muc += 1 message_lock.run } messages_muc_private = 0 m.add_private_message_callback { |msg| messages_muc_private += 1 message_lock.run } assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch', 'cauldron')) assert(m.active?) assert_equal(0, messages_client) assert_equal(0, messages_muc) assert_equal(0, messages_muc_private) send("Hello") message_lock.wait assert_equal(0, messages_client) assert_equal(1, messages_muc) assert_equal(0, messages_muc_private) send("Hello") message_lock.wait assert_equal(1, messages_client) assert_equal(1, messages_muc) assert_equal(0, messages_muc_private) send("Hello") message_lock.wait assert_equal(1, messages_client) assert_equal(1, messages_muc) assert_equal(1, messages_muc_private) wait_state end def test_presence_callbacks state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.x.password) send("" + "" + "") } presence_lock = Semaphore.new presences_client = 0 @client.add_presence_callback { |pres| presences_client += 1 presence_lock.run } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' presences_join = 0 m.add_join_callback { |pres| presences_join += 1 presence_lock.run } presences_leave = 0 m.add_leave_callback { |pres| presences_leave += 1 presence_lock.run } presences_muc = 0 m.add_presence_callback { |pres| presences_muc += 1 presence_lock.run } assert_equal(0, presences_client) assert_equal(0, presences_join) assert_equal(0, presences_leave) assert_equal(0, presences_muc) assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) assert(m.active?) assert_equal(0, presences_client) assert_equal(0, presences_join) # Joins before own join won't be called back assert_equal(0, presences_leave) assert_equal(0, presences_muc) send("" + "" + "") presence_lock.wait assert_equal(0, presences_client) assert_equal(1, presences_join) assert_equal(0, presences_leave) assert_equal(0, presences_muc) send("" + "chat" + "") presence_lock.wait assert_equal(1, presences_client) assert_equal(1, presences_join) assert_equal(0, presences_leave) assert_equal(0, presences_muc) send("" + "" + "away") presence_lock.wait assert_equal(1, presences_client) assert_equal(1, presences_join) assert_equal(0, presences_leave) assert_equal(1, presences_muc) send("") presence_lock.wait assert_equal(1, presences_client) assert_equal(1, presences_join) assert_equal(1, presences_leave) assert_equal(1, presences_muc) wait_state end def test_send state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.x.password) send("" + "" + "") } state { |stanza| assert_kind_of(Message, stanza) assert(:groupchat, stanza.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), stanza.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit'), stanza.to) assert_equal('First message', stanza.body) } state { |stanza| assert_kind_of(Message, stanza) assert(:chat, stanza.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), stanza.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/secondwitch'), stanza.to) assert_equal('Second message', stanza.body) } state { |stanza| assert_kind_of(Message, stanza) assert(:chat, stanza.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), stanza.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/firstwitch'), stanza.to) assert_equal('Third message', stanza.body) } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) m.send(Jabber::Message.new(nil, 'First message')) wait_state m.send(Jabber::Message.new(nil, 'Second message'), 'secondwitch') wait_state m.send(Jabber::Message.new('secondwitch', 'Third message'), 'firstwitch') wait_state end def test_nick state { |pres| assert_kind_of(Presence, pres) assert_nil(pres.x.password) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/secondwitch'), pres.to) assert_nil(pres.type) send("" + "" + "") } state { |pres| assert_kind_of(Presence, pres) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/oldhag'), pres.to) assert_nil(pres.type) send("" + "" + "" + "" + "" + "" + "" + "" + "") } m = MUC::MUCClient.new(@client) m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) assert_equal('thirdwitch', m.nick) assert_raises(ServerError) { m.nick = 'secondwitch' } wait_state assert(m.active?) assert_equal('thirdwitch', m.nick) m.nick = 'oldhag' wait_state assert(m.active?) assert_equal('oldhag', m.nick) end # JEP-0045: 10.2 Room Configuration def test_configuration room = JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch') jid = JID.new('hag66@shakespeare.lit/pda') state { |pres| send( "" + "" + "" ) } state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(jid, iq.from) assert_equal(room.strip, iq.to) assert_equal(:get, iq.type) assert_kind_of(Jabber::MUC::IqQueryMUCOwner, iq.first_element('query')) send(muc_config_form.sub("id='config1'","id='#{iq.id}'")) } state { |room_config| assert_kind_of(Jabber::Iq, room_config) assert_equal(room.strip, room_config.to) assert_equal(:set, room_config.type) assert_kind_of(Jabber::MUC::IqQueryMUCOwner, room_config.first_element('query')) form = room_config.first_element('query/x') assert_kind_of(Dataforms::XData, form) assert_equal(:submit, form.type) assert_equal(1, form.elements.size) assert_equal('muc#roomconfig_roomname', form.first_element('field').var) assert_equal(['Dunsinane'], form.first_element('field').values) send(muc_config_acknowledgement.sub("id='config1'","id='#{room_config.id}'")) } m = MUC::MUCClient.new(@client) m.my_jid = jid m.join(room) wait_state assert_equal(true, m.owner?) assert_equal(%w{muc#roomconfig_roomname muc#roomconfig_roomdesc muc#roomconfig_enablelogging muc#roomconfig_changesubject muc#roomconfig_allowinvites muc#roomconfig_maxusers muc#roomconfig_presencebroadcast muc#roomconfig_getmemberlist muc#roomconfig_publicroom muc#roomconfig_persistentroom muc#roomconfig_moderatedroom muc#roomconfig_membersonly muc#roomconfig_passwordprotectedroom muc#roomconfig_roomsecret muc#roomconfig_whois muc#roomconfig_roomadmins muc#roomconfig_roomowners}, m.get_room_configuration) wait_state m.submit_room_configuration( 'muc#roomconfig_roomname' => 'Dunsinane' ) wait_state end # example 150 from XEP-0045 def muc_config_form " Configuration for \"darkcave\" Room Complete this form to make changes to the configuration of your room. http://jabber.org/protocol/muc#roomconfig A Dark Cave The place for all good witches! 0 0 0 10 moderator participant visitor moderator participant visitor 0 0 0 0 1 If a password is required to enter this room, you must specify the password below. cauldronburn moderators You may specify additional people who have administrative privileges in the room. Please provide one Jabber ID per line. wiccarocks@shakespeare.lit hecate@shakespeare.lit You may specify additional owners for this room. Please provide one Jabber ID per line. " end def muc_config_acknowledgement " Dunsinane " end end xmpp4r-0.5.6/test/muc/tc_muc_simplemucclient.rb0000755000175000017500000000742613647121573023040 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r/muc' require 'xmpp4r/semaphore' include Jabber class SimpleMUCClientTest < Test::Unit::TestCase include ClientTester def test_new1 m = MUC::SimpleMUCClient.new(@client) assert_equal(nil, m.jid) assert_equal(nil, m.my_jid) assert_equal({}, m.roster) assert(!m.active?) end def test_complex m = MUC::SimpleMUCClient.new(@client) block_args = [] wait = Semaphore.new block = lambda { |*a| block_args = a; wait.run } m.on_room_message(&block) m.on_message(&block) m.on_private_message(&block) m.on_subject(&block) m.on_join(&block) m.on_leave(&block) m.on_self_leave(&block) state { |pres| assert_kind_of(Presence, pres) assert_equal(JID.new('hag66@shakespeare.lit/pda'), pres.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit/thirdwitch'), pres.to) send("" + "" + "" + "" + "" + "" + "" + "" + "") } m.my_jid = 'hag66@shakespeare.lit/pda' assert_equal(m, m.join('darkcave@macbeth.shakespeare.lit/thirdwitch')) wait_state assert(m.active?) assert_equal(3, m.roster.size) state { |msg| assert_kind_of(Message, msg) assert_equal(:groupchat, msg.type) assert_equal(JID.new('hag66@shakespeare.lit/pda'), msg.from) assert_equal(JID.new('darkcave@macbeth.shakespeare.lit'), msg.to) assert_equal('TestCasing room', msg.subject) assert_nil(msg.body) send(msg.set_from('darkcave@macbeth.shakespeare.lit/thirdwitch').set_to('hag66@shakespeare.lit/pda')) } assert_nil(m.subject) wait.wait m.subject = 'TestCasing room' wait_state wait.wait # FIXME : **Intermittently** failing (especially during RCOV run) at this line with: # 1) Failure: # test_complex(SimpleMUCClientTest) [./test/muc/tc_muc_simplemucclient.rb:71]: # <[nil, "thirdwitch", "TestCasing room"]> expected but was # <[nil, "secondwitch"]>. # #assert_equal([nil, 'thirdwitch', 'TestCasing room'], block_args) # FIXME : **Intermittently** failing (especially during RCOV run) at this line with: # 1) Failure: # test_complex(SimpleMUCClientTest) [./test/muc/tc_muc_simplemucclient.rb:80]: # <"TestCasing room"> expected but was # . # #assert_equal('TestCasing room', m.subject) end def test_kick m = MUC::SimpleMUCClient.new(@client) state { |presence| send("") } m.join('test@test/test') wait_state state { |iq| assert_kind_of(Iq, iq) assert_equal('http://jabber.org/protocol/muc#admin', iq.queryns) assert_kind_of(MUC::IqQueryMUCAdmin, iq.query) assert_equal(1, iq.query.items.size) assert_equal('pistol', iq.query.items[0].nick) assert_equal(:none, iq.query.items[0].role) assert_equal('Avaunt, you cullion!', iq.query.items[0].reason) a = iq.answer(false) a.type = :result send(a) } m.kick('pistol', 'Avaunt, you cullion!') wait_state end end xmpp4r-0.5.6/test/rpc/0000755000175000017500000000000013647121573015750 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/test/rpc/tc_helper.rb0000755000175000017500000000423713647121573020253 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/rpc/helper/client' require 'xmpp4r/rpc/helper/server' include Jabber class RPC::HelperTest < Test::Unit::TestCase include ClientTester def give_client_jid! class << @client remove_method(:jid) # avoids warning def jid; Jabber::JID.new('client@test.com/clienttester'); end end end def test_create give_client_jid! cl = RPC::Client.new(@client, 'a@b/c') assert_kind_of(RPC::Client, cl) sv = RPC::Server.new(@server) assert_kind_of(RPC::Server, sv) end def echo(msg = nil) msg end def test_simple give_client_jid! sv = RPC::Server.new(@server) sv.add_handler("echo", &method(:echo)) cl = RPC::Client.new(@client, 'a@b/c') assert_nothing_raised do assert_equal('Test string', cl.call("echo", 'Test string')) end # exception during serialisation bug identified on xmpp4r-devel # https://mail.gna.org/public/xmpp4r-devel/2008-05/msg00010.html assert_raise XMLRPC::FaultException do cl.call("echo") end end def test_introspection give_client_jid! sv = RPC::Server.new(@server) sv.add_introspection cl = RPC::Client.new(@client, 'a@b/c') cl_methods = cl.call("system.listMethods") assert(cl_methods.size > 0) cl_methods.each { |method| assert_kind_of(String, method) assert(method =~ /^system\./) } end def test_multicall give_client_jid! sv = RPC::Server.new(@server) sv.add_multicall sv.add_handler("reverse") do |s| s.reverse end sv.add_handler("upcase") do |s| s.upcase end cl = RPC::Client.new(@client, 'a@b/c') assert_equal(['tseT', 'TEST'], cl.multicall(['reverse', 'Test'], ['upcase', 'Test'])) end def test_10calls give_client_jid! sv = RPC::Server.new(@server) sv.add_handler("add") do |a,b| a+b end cl = RPC::Client.new(@client, 'a@b/c') correct = true 10.times { a, b = rand(1000), rand(1000) correct &&= (cl.call('add', a, b) == a + b) } assert(correct) end end xmpp4r-0.5.6/test/version/0000755000175000017500000000000013647121573016651 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/test/version/tc_helper.rb0000755000175000017500000000323413647121573021150 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/version/helper/responder' require 'xmpp4r/version/helper/simpleresponder' include Jabber class Version::HelperTest < Test::Unit::TestCase include ClientTester def test_create h = Version::Responder.new(@client) assert_kind_of(Version::Responder, h) assert_respond_to(h, :add_version_callback) end def test_callback # Prepare helper h = Version::Responder.new(@client) calls = 0 h.add_version_callback { |iq,responder| calls += 1 assert('jabber:iq:version', iq.queryns) responder.call('Test program', '1.0', 'Ruby Test::Unit') } # Send stanzas which shouldn't match @server.send("") @server.send("") assert_equal(0, calls) # Send a query @server.send("") { |reply| assert_equal('Test program', reply.query.iname) assert_equal('1.0', reply.query.version) assert_equal('Ruby Test::Unit', reply.query.os) true } assert_equal(1, calls) end def test_simple h = Version::SimpleResponder.new(@client, 'Test program', '1.0', 'Ruby Test::Unit') # Send a query @server.send("") { |reply| assert_equal('Test program', reply.query.iname) assert_equal('1.0', reply.query.version) assert_equal('Ruby Test::Unit', reply.query.os) true } end end xmpp4r-0.5.6/test/version/tc_iqqueryversion.rb0000755000175000017500000000502413647121573022775 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/rexmladdons' require 'xmpp4r/iq' require 'xmpp4r/version/iq/version' include Jabber class Version::IqQueryVersionTest < Test::Unit::TestCase def test_create_empty x = Version::IqQueryVersion.new assert_equal('jabber:iq:version', x.namespace) assert_nil(x.iname) assert_nil(x.version) assert_nil(x.os) end def test_create x = Version::IqQueryVersion.new('my test', 'XP') assert_equal('jabber:iq:version', x.namespace) assert_equal('my test', x.iname) assert_equal('XP', x.version) assert_equal(nil, x.os) end def test_create_with_os x = Version::IqQueryVersion.new('superbot', '1.0-final', 'FreeBSD 5.4-RELEASE-p4') assert_equal('jabber:iq:version', x.namespace) assert_equal('superbot', x.iname) assert_equal('1.0-final', x.version) assert_equal('FreeBSD 5.4-RELEASE-p4', x.os) end def test_import1 iq = Iq.new q = REXML::Element.new('query') q.add_namespace('jabber:iq:version') iq.add(q) iq2 = Iq.new.import(iq) assert_equal(Version::IqQueryVersion, iq2.query.class) end def test_import2 iq = Iq.new q = REXML::Element.new('query') q.add_namespace('jabber:iq:version') q.add_element('name').text = 'AstroBot' q.add_element('version').text = 'XP' q.add_element('os').text = 'FreeDOS' iq.add(q) iq = Iq.new.import(iq) assert_equal(Version::IqQueryVersion, iq.query.class) assert_equal('AstroBot', iq.query.iname) assert_equal('XP', iq.query.version) assert_equal('FreeDOS', iq.query.os) end def test_replace x = Version::IqQueryVersion.new('name', 'version', 'os') num = 0 x.each_element('name') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('version') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('os') { |e| num += 1 } assert_equal(1, num) x.set_iname('N').set_version('V').set_os('O') num = 0 x.each_element('name') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('version') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('os') { |e| num += 1 } assert_equal(1, num) x.set_iname(nil).set_version(nil).set_os(nil) num = 0 x.each_element('name') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('version') { |e| num += 1 } assert_equal(1, num) num = 0 x.each_element('os') { |e| num += 1 } assert_equal(0, num) end end xmpp4r-0.5.6/test/dataforms/0000755000175000017500000000000013647121573017144 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/test/dataforms/tc_data.rb0000755000175000017500000000441613647121573021100 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require 'xmpp4r/dataforms' include Jabber class DataFormsTest < Test::Unit::TestCase def test_create_defaults v = Dataforms::XDataTitle.new assert_nil(v.title) assert_equal("", v.to_s) v = Dataforms::XDataInstructions.new assert_nil(v.instructions) assert_equal("", v.to_s) v = Dataforms::XDataField.new assert_nil(v.label) assert_nil(v.var) assert_nil(v.type) assert_equal(false, v.required?) assert_equal([], v.values) assert_equal({}, v.options) v = Dataforms::XData.new assert_equal([], v.fields) assert_nil(v.type) end def test_create v = Dataforms::XDataTitle.new "This is the title" assert_equal("This is the title",v.title) assert_equal("This is the title", v.to_s) v = Dataforms::XDataInstructions.new "Instructions" assert_equal("Instructions",v.instructions) assert_equal("Instructions", v.to_s) f = Dataforms::XDataField.new "botname", :text_single assert_nil(f.label) assert_equal("botname", f.var) assert_equal(:text_single, f.type) assert_equal(false, f.required?) assert_equal([], f.values) assert_equal({}, f.options) f.label = "The name of your bot" assert_equal("The name of your bot", f.label) [:boolean, :fixed, :hidden, :jid_multi, :jid_single, :list_multi, :list_single, :text_multi, :text_private, :text_single].each do |type| f.type = type assert_equal(type, f.type) end f.type = :wrong_type assert_nil(f.type) f.required= true assert_equal(true, f.required?) f.values = ["the value"] assert_equal(["the value"], f.values) f.options = { "option 1" => "Label 1", "option 2" => "Label 2", "option 3" => nil } assert_equal({ "option 1" => "Label 1", "option 2" => "Label 2", "option 3" => nil }, f.options) f = Dataforms::XDataField.new "test", :text_single v = Dataforms::XData.new :form assert_equal([], v.fields) assert_equal(:form, v.type) [:form, :result, :submit, :cancel].each do |type| v.type = type assert_equal(type, v.type) end v.add f assert_equal(f, v.field('test')) assert_nil(v.field('wrong field')) assert_equal([f], v.fields) end end xmpp4r-0.5.6/test/tc_streamError.rb0000755000175000017500000000553513647121573020517 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift "#{File.dirname(__FILE__)}/../lib" require 'test/unit' require 'socket' require 'xmpp4r/client' include Jabber class ConnectionErrorTest < Test::Unit::TestCase @@SOCKET_PORT = 65225 STREAM = '' def setup servlisten = TCPServer.new(@@SOCKET_PORT) serverwait = Semaphore.new @server = nil Thread.new do Thread.current.abort_on_exception = true @server = servlisten.accept servlisten.close @server.sync = true serverwait.run end @conn = TCPSocket.new('localhost', @@SOCKET_PORT) serverwait.wait end def teardown @conn.close if not @conn.closed? @server.close if not @conn.closed? end def test_connectionError_start_withexcblock @stream = Stream.new error = false @stream.on_exception do |e, o, w| # strange exception, it's caused by REXML, actually assert_kind_of(StandardError, e) assert_equal(Jabber::Stream, o.class) assert_equal(:start, w) error = true end assert(!error) begin # wrong port on purpose conn = TCPSocket.new('localhost', 1) rescue end @stream.start(conn) sleep 0.2 assert(error) @server.close @stream.close end def test_connectionError_parse_withexcblock @stream = Stream.new error = false @stream.start(@conn) @stream.on_exception do |e, o, w| if w == :disconnected assert_equal(nil, e) assert_equal(Jabber::Stream, o.class) else assert_equal(REXML::ParseException, e.class) assert_equal(Jabber::Stream, o.class) assert_equal(:parser, w) error = true end end @server.puts(STREAM) @server.flush assert(!error) @server.puts('') @server.flush sleep 0.2 assert(error) @server.close @stream.close end def test_connectionError_send_withexcblock @stream = Stream.new error = 0 @stream.start(@conn) @stream.on_exception do |exc, o, w| case w when :sending assert_equal(IOError, exc.class) assert_equal(Jabber::Stream, o.class) when :disconnected assert_equal(nil, exc) assert_equal(Jabber::Stream, o.class) end error += 1 end @server.puts(STREAM) @server.flush assert_equal(0, error) @server.close sleep 0.1 assert_equal(1, error) @stream.send('') sleep 0.1 @stream.send('') sleep 0.1 assert_equal(3, error) @stream.close end def test_connectionError_send_withoutexcblock @stream = Stream.new @stream.start(@conn) @server.puts(STREAM) @server.flush assert_raise(IOError) do @server.close sleep 0.1 @stream.send('') sleep 0.1 @stream.send('') sleep 0.1 end end end xmpp4r-0.5.6/test/pubsub/0000755000175000017500000000000013647121573016464 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/test/pubsub/tc_nodeconfig.rb0000644000175000017500000000341413647121573021614 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/pubsub/children/node_config' require 'xmpp4r/dataforms' include Jabber # Jabber.debug = true class PubSub::NodeConfigTest < Test::Unit::TestCase include ClientTester def test_create() config = PubSub::NodeConfig.new() assert_nil(config.form) assert_nil(config.node) assert_equal({}, config.options) end def test_create_with_options options = {'pubsub#access_model'=>'open'} config = PubSub::NodeConfig.new(nil, options) assert_kind_of(Jabber::Dataforms::XData, config.form) assert_equal(options, config.options) assert_equal(:submit, config.form.type) assert_equal('http://jabber.org/protocol/pubsub#node_config', config.form.field('FORM_TYPE').values.first) end def test_create_with_options_and_node node = 'mynode' options = {'pubsub#access_model'=>'open'} config = PubSub::NodeConfig.new(node, options) assert_equal(node, config.node) assert_kind_of(Jabber::Dataforms::XData, config.form) assert_equal(options, config.options) assert_equal(:submit, config.form.type) assert_equal('http://jabber.org/protocol/pubsub#node_config', config.form.field('FORM_TYPE').values.first) end def test_set_options options = {'pubsub#access_model'=>'open'} config = PubSub::NodeConfig.new() config.options = options assert_kind_of(Jabber::Dataforms::XData, config.form) assert_equal(options, config.options) end def test_create_with_array_in_options options = {'pubsub#collection'=>['parent1','parent2']} config = PubSub::OwnerNodeConfig.new(nil, options) assert_equal(options, config.options) end end xmpp4r-0.5.6/test/pubsub/tc_helper.rb0000755000175000017500000007325113647121573020771 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/pubsub/helper/servicehelper' include Jabber # Jabber.debug = true class PubSub::ServiceHelperTest < Test::Unit::TestCase include ClientTester ## # subscribe_to # examples 30 and 31 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-subscribe def test_subscribe pubsub = 'pubsub.example.org' h = PubSub::ServiceHelper.new(@client,pubsub) assert_kind_of(Jabber::PubSub::ServiceHelper,h) state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(:set,iq.type) assert_equal(pubsub, iq.to.to_s) assert_equal(@client.jid, iq.from) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(1, iq.pubsub.children.size) assert_equal('subscribe',iq.pubsub.children.first.name) assert_equal('princely_musings',iq.pubsub.children.first.attributes['node']) assert_equal(@client.jid.strip.to_s,iq.pubsub.children.first.attributes['jid']) send(" ") } subscription = h.subscribe_to('princely_musings') assert_kind_of(Jabber::PubSub::Subscription,subscription) assert_equal(@client.jid.strip,subscription.jid) assert_equal('princely_musings',subscription.node) assert_equal('ba49252aaa4f5d320c24d3766f0bdcade78c78d3',subscription.subid) assert_equal(:subscribed,subscription.subscription) wait_state end ## # subscribe error condition # example 44 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-subscribe-configure def test_subscribe_configuration_required h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_kind_of(PubSub::ServiceHelper,h) state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(:set,iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(1, iq.pubsub.children.size) assert_equal('subscribe',iq.pubsub.children.first.name) assert_equal('princely_musings',iq.pubsub.children.first.attributes['node']) assert_equal(@client.jid.strip.to_s,iq.pubsub.children.first.attributes['jid']) send(" ") } subscription = h.subscribe_to('princely_musings') assert_kind_of(Jabber::PubSub::Subscription,subscription) assert_equal(@client.jid.strip,subscription.jid) assert_equal('princely_musings',subscription.node) assert_equal('ba49252aaa4f5d320c24d3766f0bdcade78c78d3',subscription.subid) assert_equal(:unconfigured,subscription.subscription) wait_state end ## # subscribe error condition # example 43 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-subscribe-approval def test_subscribe_approval_required h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_kind_of(PubSub::ServiceHelper,h) state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(:set,iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(1, iq.pubsub.children.size) assert_equal('subscribe',iq.pubsub.children.first.name) assert_equal('princely_musings',iq.pubsub.children.first.attributes['node']) assert_equal(@client.jid.strip.to_s,iq.pubsub.children.first.attributes['jid']) send(" ") } subscription = h.subscribe_to('princely_musings') assert_kind_of(Jabber::PubSub::Subscription,subscription) assert_equal(@client.jid.strip,subscription.jid) assert_equal('princely_musings',subscription.node) assert_equal('ba49252aaa4f5d320c24d3766f0bdcade78c78d3',subscription.subid) assert_equal(:pending,subscription.subscription) assert_equal(true,subscription.need_approval?) wait_state end ## # unsubscribe from # examples 48 and 49 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-unsubscribe-request def test_unsubscribe h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_kind_of(PubSub::ServiceHelper,h) state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(:set,iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(1, iq.pubsub.children.size) assert_equal('unsubscribe',iq.pubsub.children.first.name) assert_equal('princely_musings',iq.pubsub.children.first.attributes['node']) assert_equal(@client.jid.strip.to_s,iq.pubsub.children.first.attributes['jid']) send("") } unsubscribe = h.unsubscribe_from('princely_musings') assert_equal(true, unsubscribe) wait_state end ## # get subscription options # examples 56 and 57 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-configure-request def test_get_subscription_options pubsub = Jabber::JID.new('pubsub.example.org') node = 'princely_musings' jid = Jabber::JID.new('test@test.com/test') h = PubSub::ServiceHelper.new(@client, pubsub) state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(pubsub, iq.to) assert_kind_of(Jabber::PubSub::SubscriptionConfig, iq.pubsub.first_element('options')) assert_equal(node, iq.pubsub.first_element('options').node) assert_equal(jid.strip, iq.pubsub.first_element('options').jid) send( " http://jabber.org/protocol/pubsub#subscribe_options 1 ") } options = h.get_options_from(node, jid) assert_kind_of(Jabber::PubSub::SubscriptionConfig, options) assert_equal({'pubsub#deliver'=>'1'}, options.options) wait_state end ## # set subscription options # examples 65 and 66 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-configure-submit def test_set_subscription_options pubsub = Jabber::JID.new('pubsub.example.org') node = 'princely_musings' jid = Jabber::JID.new('test@test.com/test') options = {'pubsub#deliver' => '0'} h = PubSub::ServiceHelper.new(@client, pubsub) state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(pubsub, iq.to) assert_kind_of(Jabber::PubSub::SubscriptionConfig, iq.pubsub.first_element('options')) assert_equal(node, iq.pubsub.first_element('options').node) assert_equal(jid.strip, iq.pubsub.first_element('options').jid) send( "") } assert_nothing_raised do assert_equal(true, h.set_options_for(node, jid, options) ) end wait_state end ## # create node with default configuration # example 119 and 121 from # http://www.xmpp.org/extensions/xep-0060.html#owner-create-default def test_create h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_kind_of(PubSub::ServiceHelper, h) state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(2, iq.pubsub.children.size) assert_equal('create', iq.pubsub.children.first.name) assert_equal('mynode', iq.pubsub.children.first.attributes['node']) assert_equal('configure', iq.pubsub.children[1].name) assert_equal({}, iq.pubsub.children[1].attributes) assert_equal([], iq.pubsub.children[1].children) send("") } assert_equal('mynode', h.create_node('mynode')) wait_state end ## # create node with configuration # example 123 and 124 from # http://www.xmpp.org/extensions/xep-0060.html#owner-create-and-configure def test_create_configure node = 'mynode' options = {'pubsub#access_model'=>'open'} h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(2, iq.pubsub.children.size) assert_equal('create', iq.pubsub.children.first.name) assert_equal(node, iq.pubsub.children.first.attributes['node']) assert_kind_of(Jabber::PubSub::NodeConfig, iq.pubsub.children[1]) assert_equal(options, iq.pubsub.children[1].options) send("") } assert_nothing_raised do assert_equal(node, h.create_node(node, Jabber::PubSub::NodeConfig.new(node, options))) end wait_state end ## # create node a collection node # example 203 and 204 from # http://www.xmpp.org/extensions/xep-0060.html#collections-createnode def test_create_collection node = 'mynode' h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') required_options = {'pubsub#node_type' => 'collection'} state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal('http://jabber.org/protocol/pubsub', iq.pubsub.namespace) assert_equal(2, iq.pubsub.children.size) assert_equal('create', iq.pubsub.children.first.name) assert_equal(node, iq.pubsub.children.first.attributes['node']) assert_kind_of(Jabber::PubSub::NodeConfig, iq.pubsub.children[1]) assert_equal(required_options, iq.pubsub.children[1].options) send("") } assert_equal('mynode', h.create_collection_node('mynode')) wait_state end ## # delete node # example 144 and 145 from # http://www.xmpp.org/extensions/xep-0060.html#owner-delete-request def test_delete h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal(1, iq.pubsub.children.size) assert_equal('delete', iq.pubsub.children.first.name) assert_equal('mynode', iq.pubsub.children.first.attributes['node']) send("") } h.delete_node('mynode') wait_state end ## # publish to a node # example 88 and 89 from # http://www.xmpp.org/extensions/xep-0060.html#publisher-publish def test_publish node = 'mynode' item1 = Jabber::PubSub::Item.new item1.text = 'foobar' h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal(1, iq.pubsub.children.size) assert_equal('publish', iq.pubsub.children[0].name) assert_equal(node, iq.pubsub.children[0].attributes['node']) assert_equal(1, iq.pubsub.children[0].children.size) assert_equal('item', iq.pubsub.children[0].children[0].name) assert_equal(1, iq.pubsub.children[0].children[0].children.size) assert_equal(item1.children[0].to_s, iq.pubsub.children[0].children[0].children[0].to_s) send("") } assert_nothing_raised { h.publish_item_to(node, item1) } wait_state end ## # publish item with id # example 88 and 89 from # http://www.xmpp.org/extensions/xep-0060.html#publisher-publish def test_publish_pubsub_item_with_id item1 = Jabber::PubSub::Item.new item1.text = 'foobar' h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal(1, iq.pubsub.children.size) assert_equal('publish', iq.pubsub.children[0].name) assert_equal(1, iq.pubsub.children[0].children.size) assert_equal('item', iq.pubsub.children[0].children[0].name) assert_equal('blubb', iq.pubsub.children[0].children[0].attributes['id'] ) assert_equal(1, iq.pubsub.children[0].children[0].children.size) assert_equal(item1.children[0].to_s, iq.pubsub.children[0].children[0].children[0].to_s) send("") } assert_nothing_raised { h.publish_item_with_id_to('mynode', item1,"blubb") } wait_state end ## # publish item and trap client-side error # examples 88 from # http://www.xmpp.org/extensions/xep-0060.html#publisher-publish def test_publish_pubsub_item_with_id_and_produce_a_local_error item1 = 'foobarbaz' h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_raise RuntimeError do h.publish_item_with_id_to('mynode', item1,"blubb") end end ## # publish item and trap server-side error # examples 88 from # http://www.xmpp.org/extensions/xep-0060.html#publisher-publish # and 93 from # http://www.xmpp.org/extensions/xep-0060.html#publisher-publish-error-forbidden def test_publish_pubsub_item_with_id_and_produce_an_error item1 = Jabber::PubSub::Item.new item1.text = "foobarbaz" h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.children.size) assert_equal(1, iq.pubsub.children.size) assert_equal('publish', iq.pubsub.children[0].name) assert_equal(1, iq.pubsub.children[0].children.size) assert_equal('item', iq.pubsub.children[0].children[0].name) assert_equal('blubb', iq.pubsub.children[0].children[0].attributes['id'] ) assert_equal(1, iq.pubsub.children[0].children[0].children.size) assert_equal(item1.children[0].to_s, iq.pubsub.children[0].children[0].children[0].to_s) send(" ") } assert_raise Jabber::ServerError do h.publish_item_with_id_to('mynode', item1,"blubb") end wait_state end ## # retrieve all items # examples 70 and 71 from # http://www.xmpp.org/extensions/xep-0060.html#subscriber-retrieve-returnall def test_items item1 = Jabber::PubSub::Item.new("1") item1.text = 'foobar' item2 = Jabber::PubSub::Item.new("2") item2.text = 'barfoo' h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('items', iq.pubsub.children.first.name) assert_equal('mynode', iq.pubsub.children.first.attributes['node']) send(" #{item1.to_s} #{item2.to_s} ") } items = h.get_items_from('mynode') assert_equal(2, items.size) assert_kind_of(REXML::Text, items['1']) assert_kind_of(REXML::Text, items['2']) assert_equal(item1.children.join, items['1'].to_s) assert_equal(item2.children.join, items['2'].to_s) wait_state end ## # retrieve some items # example 76 from # http://xmpp.org/extensions/xep-0060.html#subscriber-retrieve-requestsome def test_items_with_max_items node_name = "mynode" max_items = 2 h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('items', iq.pubsub.children.first.name) assert_equal(node_name, iq.pubsub.children.first.attributes['node']) assert_equal(max_items.to_s, iq.pubsub.children.first.attributes['max_items']) # response doesn't matter; was previously tested, so send a simple result send("") } h.get_items_from(node_name, max_items) wait_state end ## # get affiliation # example 184 and 185 from # http://www.xmpp.org/extensions/xep-0060.html#owner-affiliations-retrieve-success1 def test_affiliations h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('affiliations', iq.pubsub.children.first.name) send(" ") } a = h.get_affiliations assert_kind_of(Hash, a) assert_equal(4, a.size) assert_equal(:owner, a['node1']) assert_equal(:publisher, a['node2']) assert_equal(:outcast, a['node5']) assert_equal(:owner, a['node6']) wait_state end # http://xmpp.org/extensions/xep-0060.html#owner-affiliations-modify def test_set_affiliations h = PubSub::ServiceHelper.new(@client,'pubsub.shakespeare.lit') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:set, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('affiliations', iq.pubsub.children[0].name) assert_equal('affiliation', iq.pubsub.children[0].children[0].name) assert_equal('bard@shakespeare.lit', iq.pubsub.children[0].children[0].attributes['jid']) assert_equal('publisher', iq.pubsub.children[0].children[0].attributes['affiliation']) send("") } a = h.set_affiliations('princely_musings', 'bard@shakespeare.lit', :publisher) wait_state end ## # get_subscriptions_from # example 171 and 172 from # http://www.xmpp.org/extensions/xep-0060.html#owner-subscriptions-retrieve-request def test_subscriptions h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('subscriptions', iq.pubsub.children.first.name) send(" ") } s = h.get_subscriptions_from('node1') assert_kind_of(Array,s) assert_equal(4,s.size) assert_kind_of(Jabber::PubSub::Subscription,s[0]) assert_kind_of(Jabber::PubSub::Subscription,s[1]) assert_kind_of(Jabber::PubSub::Subscription,s[2]) assert_kind_of(Jabber::PubSub::Subscription,s[3]) assert_equal(:subscribed,s[0].state) assert_equal(:unconfigured,s[1].state) assert_equal(JID.new("hamlet@denmark.lit"),s[0].jid) assert_equal("123-abc",s[2].subid) wait_state end ## # get_subscribers # example 171 and 172 from # http://www.xmpp.org/extensions/xep-0060.html#owner-subscriptions-retrieve def test_subscribers h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('subscriptions', iq.pubsub.children.first.name) send(" ") } s = h.get_subscribers_from('princely_musings') assert_equal(4,s.size) assert_kind_of(Jabber::JID,s[0]) assert_kind_of(Jabber::JID,s[1]) assert_kind_of(Jabber::JID,s[2]) assert_kind_of(Jabber::JID,s[3]) wait_state end ## # get_all_subscriptions def test_get_all_subscriptions h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('subscriptions', iq.pubsub.children.first.name) send(" ") } s = h.get_subscriptions_from_all_nodes assert_kind_of(Array,s) assert_equal(4,s.size) assert_kind_of(Jabber::PubSub::Subscription,s[0]) assert_kind_of(Jabber::PubSub::Subscription,s[1]) assert_kind_of(Jabber::PubSub::Subscription,s[2]) assert_kind_of(Jabber::PubSub::Subscription,s[3]) assert_equal(:subscribed,s[0].state) assert_equal(:unconfigured,s[2].state) assert_equal(:pending,s[3].state) assert_equal(JID.new("francisco@denmark.lit"),s[0].jid) assert_equal("node1",s[0].node) wait_state end ## # get all subscriptions with no subscriptions def test_get_all_subscriptions_with_no_subscriptions h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(1, iq.pubsub.children.size) assert_equal('subscriptions', iq.pubsub.children.first.name) send(" ") } s = h.get_subscriptions_from_all_nodes assert_kind_of(Array,s) assert_equal(0,s.size) wait_state end ## # get configuration for a node # example 125 and 126 from # http://www.xmpp.org/extensions/xep-0060.html#owner-configure-request def test_get_node_config pubsub = 'pubsub.example.org' h = PubSub::ServiceHelper.new(@client, pubsub) state { |iq| assert_kind_of(Jabber::Iq, iq) assert_equal(:get, iq.type) assert_equal(pubsub, iq.to.to_s) assert_kind_of(Jabber::PubSub::OwnerNodeConfig, iq.pubsub.first_element('configure')) send( " http://jabber.org/protocol/pubsub#node_config ") } config = h.get_config_from('princelymusings') assert_kind_of(Jabber::PubSub::OwnerNodeConfig, config) wait_state end ## # owner set configuration for a node # example 133 # http://xmpp.org/extensions/xep-0060.html#owner-configure def test_set_node_config node = 'princely_musings' pubsub = 'pubsub.shakespeare.lit' h = PubSub::ServiceHelper.new(@client,pubsub) state { |iq| assert_kind_of(Jabber::Iq,iq) assert_equal(:set, iq.type) assert_equal(pubsub, iq.to.to_s) config = iq.pubsub.first_element('configure') assert_kind_of(Jabber::PubSub::OwnerNodeConfig, config) assert_kind_of(Jabber::Dataforms::XData, config.form) assert_equal(config.options["pubsub#title"], "Princely Musings (Atom)") assert_equal(config.options["pubsub#deliver_notifications"], "1") assert_equal(config.options["pubsub#deliver_payloads"], "1") assert_equal(config.options["pubsub#persist_items"], "1") assert_equal(config.options["pubsub#max_items"], "10") assert_equal(config.options["pubsub#access_model"], "open") assert_equal(config.options["pubsub#publish_model"], "publishers") assert_equal(config.options["pubsub#send_last_published_item"], "never") assert_equal(config.options["pubsub#presence_based_delivery"], "false") assert_equal(config.options["pubsub#notify_config"], "0") assert_equal(config.options["pubsub#notify_delete"], "0") assert_equal(config.options["pubsub#notify_retract"], "0") assert_equal(config.options["pubsub#notify_sub"], "0") assert_equal(config.options["pubsub#max_payload_size"], "1028") assert_equal(config.options["pubsub#type"], "http://www.w3.org/2005/Atom") assert_equal(config.options["pubsub#body_xslt"], "http://jabxslt.jabberstudio.org/atom_body.xslt") send("") } config = Jabber::PubSub::OwnerNodeConfig.new(node) config.options = { "pubsub#title" => "Princely Musings (Atom)", "pubsub#deliver_notifications" => "1", "pubsub#deliver_payloads" => "1", "pubsub#persist_items" => "1", "pubsub#max_items" => "10", "pubsub#access_model" => "open", "pubsub#publish_model" => "publishers", "pubsub#send_last_published_item" => "never", "pubsub#presence_based_delivery" => "false", "pubsub#notify_config" => "0", "pubsub#notify_delete" => "0", "pubsub#notify_retract" => "0", "pubsub#notify_sub" => "0", "pubsub#max_payload_size" => "1028", "pubsub#type" => "http://www.w3.org/2005/Atom", "pubsub#body_xslt" => "http://jabxslt.jabberstudio.org/atom_body.xslt" } assert_kind_of(Jabber::PubSub::OwnerNodeConfig, config) h.set_config_for(node, config) wait_state end def test_delete_item pubsub = 'pubsub.example.org' h = PubSub::ServiceHelper.new(@client, pubsub) state { |iq| assert_kind_of(Jabber::Iq, iq) assert_kind_of(Jabber::PubSub::IqPubSub, iq.pubsub) assert_kind_of(Jabber::PubSub::Retract, iq.pubsub.first_element('retract')) assert_equal(1, iq.pubsub.first_element('retract').items.size) assert_equal('ae890ac52d0df67ed7cfdf51b644e901', iq.pubsub.first_element('retract').items[0].id) send(iq.answer.set_type(:result)) } h.delete_item_from('princely_musings', 'ae890ac52d0df67ed7cfdf51b644e901') wait_state end def test_to_s h = PubSub::ServiceHelper.new(@client,'pubsub.example.org') assert_equal('pubsub.example.org',h.to_s) end end xmpp4r-0.5.6/test/pubsub/tc_subscriptionconfig.rb0000644000175000017500000000224513647121573023414 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/pubsub/children/subscription_config' require 'xmpp4r/dataforms' include Jabber # Jabber.debug = true class PubSub::SubscriptionConfigTest < Test::Unit::TestCase include ClientTester def test_create() config = PubSub::SubscriptionConfig.new() assert_nil(config.form) assert_nil(config.node) assert_equal({}, config.options) end def test_create_with_options node = 'mynode' jid = 'test@test.com' options = {'pubsub#access_model'=>'open'} subid = '004-yyy' config = PubSub::SubscriptionConfig.new(node, jid, options, subid) assert_equal(node, config.node) assert_equal(subid, config.subid) assert_kind_of(Jabber::JID, config.jid) assert_equal(Jabber::JID.new(jid), config.jid) assert_kind_of(Jabber::Dataforms::XData, config.form) assert_equal(options, config.options) assert_equal(:submit, config.form.type) assert_equal('http://jabber.org/protocol/pubsub#subscribe_options', config.form.field('FORM_TYPE').values.first) end end xmpp4r-0.5.6/test/tc_class_names.rb0000755000175000017500000001265113647121573020477 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r' # No include Jabber, test full namespace class JIDTest < Test::Unit::TestCase def test_base assert_kind_of(Module, Jabber) assert_kind_of(Class, Jabber::JabberError) assert_kind_of(Class, Jabber::ErrorResponse) assert_kind_of(Class, Jabber::ClientAuthenticationFailure) assert_kind_of(Class, Jabber::ComponentAuthenticationFailure) assert_kind_of(Class, Jabber::Client) assert_kind_of(Class, Jabber::Component) assert_kind_of(Class, Jabber::Connection) assert_kind_of(Class, Jabber::ErrorResponse) assert_kind_of(Class, Jabber::ServerError) assert_kind_of(Class, Jabber::IdGenerator) assert_kind_of(Class, Jabber::Iq) assert_kind_of(Class, Jabber::IqQuery) assert_kind_of(Class, Jabber::JID) assert_kind_of(Class, Jabber::Message) assert_kind_of(Class, Jabber::Presence) assert_kind_of(Module, Jabber::SASL) assert_respond_to(Jabber::SASL, :new) assert_kind_of(Class, Jabber::SASL::Base) assert_kind_of(Class, Jabber::SASL::Plain) assert_kind_of(Class, Jabber::SASL::DigestMD5) assert_kind_of(Class, Jabber::Stream) assert_kind_of(Class, Jabber::StreamParser) assert_kind_of(Class, Jabber::X) assert_kind_of(Class, Jabber::XMPPElement) assert_kind_of(Class, Jabber::XMPPStanza) end def test_roster require 'xmpp4r/roster' assert_kind_of(Module, Jabber::Roster) assert_kind_of(Class, Jabber::Roster::Helper) assert_kind_of(Class, Jabber::Roster::Helper::RosterItem) assert_kind_of(Class, Jabber::Roster::RosterItem) assert_kind_of(Class, Jabber::Roster::IqQueryRoster) assert_kind_of(Class, Jabber::Roster::XRoster) assert_kind_of(Class, Jabber::Roster::XRosterItem) end def test_muc require 'xmpp4r/muc' assert_kind_of(Module, Jabber::MUC) assert_kind_of(Class, Jabber::MUC::MUCBrowser) assert_kind_of(Class, Jabber::MUC::MUCClient) assert_kind_of(Class, Jabber::MUC::SimpleMUCClient) assert_kind_of(Class, Jabber::MUC::XMUC) assert_kind_of(Class, Jabber::MUC::XMUCUser) assert_kind_of(Class, Jabber::MUC::XMUCUserInvite) end def test_bytestreams require 'xmpp4r/bytestreams' assert_kind_of(Class, Jabber::SOCKS5Error) assert_kind_of(Module, Jabber::FileTransfer) assert_kind_of(Module, Jabber::FileTransfer::TransferSource) assert_kind_of(Class, Jabber::FileTransfer::FileSource) assert_kind_of(Class, Jabber::FileTransfer::Helper) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5BytestreamsServer) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5BytestreamsServerStreamHost) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5BytestreamsPeer) assert_kind_of(Class, Jabber::Bytestreams::IqQueryBytestreams) assert_kind_of(Class, Jabber::Bytestreams::StreamHost) assert_kind_of(Class, Jabber::Bytestreams::StreamHostUsed) assert_kind_of(Class, Jabber::Bytestreams::IqSi) assert_kind_of(Class, Jabber::Bytestreams::IqSiFile) assert_kind_of(Class, Jabber::Bytestreams::IqSiFileRange) assert_kind_of(Class, Jabber::Bytestreams::IBB) assert_kind_of(Class, Jabber::Bytestreams::IBBQueueItem) assert_kind_of(Class, Jabber::Bytestreams::IBBInitiator) assert_kind_of(Class, Jabber::Bytestreams::IBBTarget) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5Bytestreams) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5BytestreamsInitiator) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5BytestreamsTarget) assert_kind_of(Class, Jabber::Bytestreams::SOCKS5Socket) end def test_dataforms require 'xmpp4r/dataforms' assert_kind_of(Module, Jabber::Dataforms) assert_kind_of(Class, Jabber::Dataforms::XData) assert_kind_of(Class, Jabber::Dataforms::XDataTitle) assert_kind_of(Class, Jabber::Dataforms::XDataInstructions) assert_kind_of(Class, Jabber::Dataforms::XDataField) assert_kind_of(Class, Jabber::Dataforms::XDataReported) end def test_delay require 'xmpp4r/delay' assert_kind_of(Module, Jabber::Delay) assert_kind_of(Class, Jabber::Delay::XDelay) end def test_discovery require 'xmpp4r/discovery' assert_kind_of(Module, Jabber::Discovery) assert_kind_of(Class, Jabber::Discovery::IqQueryDiscoInfo) assert_kind_of(Class, Jabber::Discovery::Identity) assert_kind_of(Class, Jabber::Discovery::Feature) assert_kind_of(Class, Jabber::Discovery::IqQueryDiscoItems) assert_kind_of(Class, Jabber::Discovery::Item) end def test_feature_negotiation require 'xmpp4r/feature_negotiation' assert_kind_of(Module, Jabber::FeatureNegotiation) assert_kind_of(Class, Jabber::FeatureNegotiation::IqFeature) end def test_vcard require 'xmpp4r/vcard' assert_kind_of(Module, Jabber::Vcard) assert_kind_of(Class, Jabber::Vcard::Helper) assert_kind_of(Class, Jabber::Vcard::IqVcard) end def test_version require 'xmpp4r/version' assert_kind_of(Module, Jabber::Version) assert_kind_of(Class, Jabber::Version::Responder) assert_kind_of(Class, Jabber::Version::SimpleResponder) assert_kind_of(Class, Jabber::Version::IqQueryVersion) end def test_rpc require 'xmpp4r/rpc' assert_kind_of(Module, Jabber::RPC) assert_kind_of(Class, Jabber::RPC::Server) assert_kind_of(Class, Jabber::RPC::Client) end def test_pubsub require 'xmpp4r/pubsub' assert_kind_of(Module, Jabber::PubSub) assert_kind_of(Class, Jabber::PubSub::ServiceHelper) end end xmpp4r-0.5.6/test/ts_xmpp4r.rb0000755000175000017500000000253413647121573017460 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby -w $:.unshift File.join(File.dirname(__FILE__), '..', 'lib') $:.unshift File.join(File.dirname(__FILE__), '..', 'test') $:.unshift File.join(File.dirname(__FILE__), 'lib') $:.unshift File.join(File.dirname(__FILE__), 'test') # This is allowed here, to make sure it's enabled in all test. Thread::abort_on_exception = true require 'xmpp4r' require 'find' # List files' basenames, not full path! # EXCLUDED_FILES = [ 'tc_muc_simplemucclient.rb' ] EXCLUDED_FILES = %w[ tc_disconnect_cleanup.rb ./pubsub/tc_helper.rb ./muc/tc_muc_mucclient.rb ./reliable/tc_reliable_connection.rb ./reliable/tc_disconnect_exception.rb ./reliable/tc_listener_mocked_test.rb ./reliable/tc_reliable_connection.rb ./bytestreams/tc_socks5bytestreams.rb ].map {|f| f.gsub(%r[^\.], File.dirname(__FILE__)) } tc_files = [] tc_subdirs = [] Find.find(File.dirname(__FILE__)) do |f| if File::directory?(f) if f == '.' # do nothing elsif File::basename(f) != '.svn' tc_subdirs << f Find.prune end elsif File::basename(f) =~ /^tc.*\.rb$/ tc_files << f end end tc_subdirs.each do |dir| Find.find(dir) do |f| if File::file?(f) and File::basename(f) =~ /^tc.*\.rb$/ tc_files << f end end end tc_files.each do |f| next if EXCLUDED_FILES.include?(File::basename(f)) or EXCLUDED_FILES.include?(f) require f end xmpp4r-0.5.6/test/tc_idgenerator.rb0000755000175000017500000000107713647121573020512 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/idgenerator' include Jabber class IdGeneratorTest < Test::Unit::TestCase def test_instances assert_equal(Jabber::IdGenerator.instance, Jabber::IdGenerator.instance) end def test_unique ids = [] 100.times { ids.push(Jabber::IdGenerator.generate_id) } ok = true ids.each_index { |a| ids.each_index { |b| if a == b ok = false if ids[a] != ids[b] else ok = false if ids[a] == ids[b] end } } assert(ok) end end xmpp4r-0.5.6/test/tune/0000755000175000017500000000000013647121573016137 5ustar debbiecocoadebbiecocoaxmpp4r-0.5.6/test/tune/tc_helper_send.rb0000755000175000017500000000417713647121573021456 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/tune/helper/helper' require 'xmpp4r/tune/tune' include Jabber #Jabber::debug=true class UserTune::HelperTest < Test::Unit::TestCase include ClientTester ## # Test sending 'now playing' notifications # # See http://www.xmpp.org/extensions/xep-0118.html#protocol-transport, # example 1 def test_send_now_playing artist = 'Mike Flowers Pops' title = 'Light My Fire' tune_to_send = UserTune::Tune.new(artist, title) h = UserTune::Helper.new(@client, nil) assert_kind_of(UserTune::Helper, h) state { |now_playing| assert_kind_of(Jabber::Iq, now_playing) assert_equal :set, now_playing.type assert_kind_of(Jabber::PubSub::IqPubSub,now_playing.first_element('pubsub')) assert_equal(Jabber::UserTune::NS_USERTUNE,now_playing.first_element('pubsub').first_element('publish').node) tune=now_playing.first_element('pubsub').first_element('publish').first_element('item').first_element('tune') assert_kind_of Jabber::UserTune::Tune,tune assert_equal true, tune.playing? assert_equal artist,tune.artist assert_equal title,tune.title assert_equal nil,tune.length assert_equal nil,tune.track assert_equal nil,tune.source assert_equal nil,tune.uri send("") } h.now_playing(tune_to_send) wait_state end def test_send_stop_playing h = UserTune::Helper.new(@client, nil) state { |now_playing| tune = now_playing.first_element('pubsub').first_element('publish').first_element('item').first_element('tune') assert_kind_of Jabber::UserTune::Tune, tune assert_equal false, tune.playing? assert_equal nil, tune.artist assert_equal nil, tune.title assert_equal nil,tune.length assert_equal nil,tune.track assert_equal nil,tune.source assert_equal nil,tune.uri send("") } h.stop_playing wait_state end end xmpp4r-0.5.6/test/tune/tc_helper_recv.rb0000755000175000017500000000456613647121573021466 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift File::dirname(__FILE__) + '/../../lib' require 'test/unit' require File::dirname(__FILE__) + '/../lib/clienttester' require 'xmpp4r' require 'xmpp4r/tune/helper/helper' require 'xmpp4r/tune/tune' include Jabber #Jabber::debug=true class UserTune::HelperTest < Test::Unit::TestCase include ClientTester ## # Test receiving 'now playing' notifications # # See http://www.xmpp.org/extensions/xep-0118.html#protocol-transport, # example 1 def test_recv_now_playing state { |presence| send(deliver_usertune) } query_waiter = Semaphore.new h = UserTune::Helper.new(@client, 'stpeter@jabber.org') h.add_usertune_callback do |tune| assert_kind_of Jabber::UserTune::Tune, tune assert_equal true, tune.playing? assert_equal 'Yes', tune.artist assert_equal 686, tune.length assert_equal 'Yessongs', tune.source assert_equal 'Heart of the Sunrise', tune.title assert_equal '3', tune.track assert_equal 'http://www.yesworld.com/lyrics/Fragile.html#9',tune.uri query_waiter.run end @client.send Jabber::Presence.new wait_state query_waiter.wait end # see example 2 from http://www.xmpp.org/extensions/xep-0118.html#protocol-transport def deliver_usertune " Yes 686 Yessongs Heart of the Sunrise 3 http://www.yesworld.com/lyrics/Fragile.html#9 " end # an example from the Wild def psi_usertune " Wes MontgomeryJinglesBags Meets Wes8410" end end xmpp4r-0.5.6/test/tune/tc_tune.rb0000755000175000017500000000337413647121573020137 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r/tune/tune.rb' class Jabber::UserTune::TuneTest < Test::Unit::TestCase def test_create artist='Mike Flowers Pops' title='Light My Fire' length=175 track='4' source='A Groovy Place' uri='http://musicbrainz.org/track/d44110e6-4b20-4d16-9e69-74bf0e4f7106.html' rating=10 t=Jabber::UserTune::Tune.new(artist,title,length,track,source,uri,rating) assert_kind_of Jabber::UserTune::Tune,t assert_equal 7,t.elements.size assert_equal true,t.playing? assert_equal artist,t.artist assert_equal track,t.track assert_equal length,t.length assert_equal track,t.track assert_equal source,t.source assert_equal uri,t.uri assert_equal rating,t.rating end def test_stop_playing t=Jabber::UserTune::Tune.new assert_kind_of Jabber::UserTune::Tune,t assert_equal 0,t.elements.size assert_equal false, t.playing? assert_equal nil,t.artist assert_equal nil,t.track assert_equal nil,t.length assert_equal nil,t.track assert_equal nil,t.source assert_equal nil,t.uri end def test_rating_edgecases assert_equal(0, Jabber::UserTune::Tune.new(artist,title,length,track,source,uri,-1.5).rating) assert_equal(10, Jabber::UserTune::Tune.new(artist,title,length,track,source,uri,11.5).rating) assert_equal(nil, Jabber::UserTune::Tune.new(artist,title,length,track,source,uri,'fantastic').rating) end def artist 'Mike Flowers Pops' end def title 'Light My Fire' end def length 175 end def track '4' end def source 'A Groovy Place' end def uri 'http://musicbrainz.org/track/d44110e6-4b20-4d16-9e69-74bf0e4f7106.html' end def rating 10 end end xmpp4r-0.5.6/test/tc_errors.rb0000755000175000017500000000735113647121573017524 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'xmpp4r' require 'xmpp4r/rexmladdons' require 'xmpp4r/message' include Jabber class ServerErrorTest < Test::Unit::TestCase def test_create_with_empty_error e = ErrorResponse.new() ee = ServerError.new(e) assert_equal(nil, e.error) end def test_create_with_error_code e = ErrorResponse.new('payment-required') ee = ServerError.new(e) assert_equal("payment-required: ", ee.to_s) end def test_create_invalid assert_raise(Jabber::ArgumentError) { e = ErrorResponse.new('invalid error') ee = ServerError.new(e) } end def test_to_s_with_error_code_but_no_text e = ErrorResponse.new('payment-required') ee = ServerError.new(e) assert_equal("payment-required: ", ee.to_s) assert_equal('payment-required', e.error) assert_equal(402, ee.error.code) assert_equal(:auth, ee.error.type) assert_equal(nil, ee.error.text) end def test_to_s_with_error_code_and_text e = ErrorResponse.new('payment-required', 'cuz you are a deadbeat.') ee = ServerError.new(e) assert_equal("payment-required: cuz you are a deadbeat.", ee.to_s) assert_equal('payment-required', e.error) assert_equal(402, ee.error.code) assert_equal(:auth, ee.error.type) assert_equal("cuz you are a deadbeat.", ee.error.text) end end class ErrorTest < Test::Unit::TestCase def test_create e = ErrorResponse.new assert_equal(nil, e.error) assert_equal(nil, e.code) assert_equal(nil, e.type) assert_equal(nil, e.text) end def test_create2 e = ErrorResponse.new('payment-required') assert_equal('payment-required', e.error) assert_equal(402, e.code) assert_equal(:auth, e.type) assert_equal(nil, e.text) end def test_create3 e = ErrorResponse.new('gone', 'User moved to afterlife.gov') assert_equal('gone', e.error) assert_equal(302, e.code) assert_equal(:modify, e.type) assert_equal('User moved to afterlife.gov', e.text) end def test_create_invalid assert_raise(Jabber::ArgumentError) { e = ErrorResponse.new('invalid error') } end def test_type e = ErrorResponse.new assert_nil(e.type) e.type = :auth assert_equal(:auth, e.type) e.type = :cancel assert_equal(:cancel, e.type) e.type = :continue assert_equal(:continue, e.type) e.type = :modify assert_equal(:modify, e.type) e.type = :wait assert_equal(:wait, e.type) e.type = nil assert_nil(e.type) end def test_code e = ErrorResponse.new assert_nil(e.code) e.code = 404 assert_equal(404, e.code) assert_equal("", e.to_s) e.code = nil assert_nil(e.code) end def test_error e = ErrorResponse.new assert_nil(e.error) e.error = 'gone' assert_equal('gone', e.error) assert_raise(RuntimeError) { e.error = nil } end def test_stanzas m = Message.new assert_equal(nil, m.error) m.typed_add(ErrorResponse.new) assert_equal('', m.error.to_s) end def test_sample_normal src = '...' e = ErrorResponse.new.import(REXML::Document.new(src).root) assert_equal(:modify, e.type) assert_equal(302, e.code) assert_equal('gone', e.error) assert_equal('...', e.text) end def test_sample_muc src = 'Please choose a different nickname.' e = ErrorResponse.new.import(REXML::Document.new(src).root) assert_equal(nil, e.type) assert_equal(409, e.code) assert_equal(nil, e.error) assert_equal('Please choose a different nickname.', e.text) end end xmpp4r-0.5.6/test/tc_message.rb0000755000175000017500000001242213647121573017627 0ustar debbiecocoadebbiecocoa#!/usr/bin/ruby $:.unshift '../lib' require 'test/unit' require 'socket' require 'xmpp4r/rexmladdons' require 'xmpp4r/message' require 'xmpp4r/errors' include Jabber class MessageTest < Test::Unit::TestCase def test_create x = Message.new() assert_equal("message", x.name) assert_equal("jabber:client", x.namespace) assert_equal(nil, x.to) assert_equal(nil, x.body) x = Message.new("lucas@linux.ensimag.fr", "coucou") assert_equal("message", x.name) assert_equal("lucas@linux.ensimag.fr", x.to.to_s) assert_equal("coucou", x.body) end def test_import x = Message.new assert_kind_of(REXML::Element, x.typed_add(REXML::Element.new('thread'))) assert_kind_of(X, x.typed_add(REXML::Element.new('x'))) assert_kind_of(X, x.x) end def test_type x = Message.new assert_equal(nil, x.type) x.type = :chat assert_equal(:chat, x.type) assert_equal(x, x.set_type(:error)) assert_equal(:error, x.type) x.type = :groupchat assert_equal(:groupchat, x.type) x.type = :headline assert_equal(:headline, x.type) x.type = :normal assert_equal(:normal, x.type) x.type = :invalid assert_equal(nil, x.type) end def test_should_update_body x = Message.new() assert_equal(nil, x.body) assert_equal(x, x.set_body("trezrze ezfrezr ezr zer ezr ezrezrez ezr z")) assert_equal("trezrze ezfrezr ezr zer ezr ezrezrez ezr z", x.body) x.body = "2" assert_equal("2", x.body) end def test_should_update_xhtml_body x = Message.new() assert_equal(nil, x.xhtml_body) assert_equal(x, x.set_xhtml_body("check this link out")) assert_equal("check this link out", x.xhtml_body) x.xhtml_body = "2" assert_equal("2", x.xhtml_body) end def test_should_get_bodies x = Message.new() x.set_body("check this link out") assert_equal("check this link out", x.body) x.set_xhtml_body("check this link out") assert_equal("check this link out", x.xhtml_body) x.first_element("html").remove assert_equal(nil, x.xhtml_body) # Some clients send markupped body without wrapper, # and we need to be able to deal with this also el = REXML::Element.new("body") el.add_namespace("http://www.w3.org/1999/xhtml") el.add_text("xhtml body without wrapper") x.add_element(el) assert_equal("xhtml body without wrapper", x.xhtml_body) end def test_should_get_xhtml_body_of_new_message x = Message.new() x.set_xhtml_body("check this link out") assert_equal("check this link out", x.xhtml_body) doc = REXML::Document.new x.to_s x2 = Message.new.import doc.root assert_equal(x.to_s, x2.to_s) assert_equal("check this link out", x2.xhtml_body) end def test_should_raise_exception_with_invalid_xhtml_body x = Message.new() assert_raise Jabber::ArgumentError do x.set_xhtml_body("check this link out") end end def test_subject x = Message.new assert_equal(nil, x.subject) subject = REXML::Element.new('subject') subject.text = 'A' x.add(subject) assert_equal('A', x.subject) x.subject = 'Test message' assert_equal('Test message', x.subject) x.each_element('subject') { |s| assert_equal('Test message', s.text) } assert_equal(x, x.set_subject('Breaking news')) assert_equal('Breaking news', x.subject) end def test_thread x = Message.new assert_equal(nil, x.thread) thread = REXML::Element.new('thread') thread.text = '123' x.add(thread) assert_equal('123', x.thread) x.thread = '321' assert_equal('321', x.thread) x.each_element('thread') { |s| assert_equal('321', s.text) } assert_equal(x, x.set_thread('abc')) assert_equal('abc', x.thread) end def test_chat_state x = Message.new assert_equal(nil, x.chat_state) chat_state = REXML::Element.new('active') chat_state.add_namespace('http://jabber.org/protocol/chatstates') x.add(chat_state) assert_equal(:active, x.chat_state) x.chat_state = :gone assert_equal(:gone, x.chat_state) assert_raise(InvalidChatState) do x.chat_state = :some_invalid_state end assert_equal true, x.gone? end def test_error x = Message.new() assert_equal(nil, x.error) e = REXML::Element.new('error') x.add(e) # test if, after an import, the error element is successfully changed # into an ErrorResponse object. x2 = Message.new.import(x) assert_equal(ErrorResponse, x2.first_element('error').class) end def test_answer orig = Message.new orig.from = 'a@b' orig.to = 'b@a' orig.id = '123' orig.type = :chat orig.add(REXML::Element.new('x')) answer = orig.answer assert_equal(JID.new('b@a'), answer.from) assert_equal(JID.new('a@b'), answer.to) assert_equal('123', answer.id) assert_equal(:chat, answer.type) answer.each_element { |e| assert_equal('x', e.name) assert_kind_of(X, e) } end end