require 'singleton' require 'xmpp4r' require 'xmpp4r/vcard' class TunecoreAnnouncer::XmppConnection include Singleton attr_reader :server attr_reader :name attr_reader :port def initialize @logger = TunecoreAnnouncer::Logger.instance end def connect(opt) @options = opt @server = opt.jabber_server @name = "#{opt.name_prefix}-#{TunecoreAnnouncer::Utils.random_word(2)}" @password = opt.jabber_password admin_jids = opt.admin_jids register @logger.info "Connected to XMPP server" #@jabber.add(admin_jids) add_admins #@logger.debug "Added #{admin_jid} to roster" @logger.debug "Launching listener" admins(opt.hello_mesg) if opt.hello_mesg set_vcard listen end def disconnect admins "Shutting down ... Sayonara!" deregister @jabber.disconnect @listener_thread.join end def admins(mesg) @options.admin_jids.each do |admin_jid| @jabber.deliver admin_jid, mesg end end def status(presence, mesg) pres = Jabber::Presence::new set_avatar(pres) unless @options.avatar_file.nil? pres.set_status mesg @logger.debug "Setting presence: #{pres.to_s}, #{mesg}" @jabber.send!(pres) end private def register connection_string = "#{@name}@#{@server}" @logger.debug "Registering #{connection_string}" begin @jabber = Jabber::Simple.register(connection_string, @password) rescue => e @logger.fatal "XMPP connection refused: #{e.message}\n#{e.backtrace}" puts "XMPP connection refused: #{e.inspect}\n#{e.backtrace}" exit end end def deregister @logger.debug "Removing registration" client = @jabber.client client.remove_registration end def listen @listener_thread = Thread.new{ @logger.debug "Listening for XMPP messages (connected=#{@jabber.connected?})" while @jabber.connected? do #@logger.debug "Polling for Jabber mesg" @jabber.received_messages.each do |message| process_incoming message sleep 1 end end } @listener_thread.run end def process_incoming(message) @logger.debug "Jabber msg from #{message.from}: #{message.body}" case message.body when "uptime" @jabber.deliver(message.from, `uptime`) when "ps" @jabber.deliver(message.from, `ps -ef`) when "hostname" @jabber.deliver(message.from, `hostname`) else @jabber.deliver(message.from, "Sorry, I don't know #{message.body}") end end def set_vcard # set vcard info @avatar_sha1 = nil # this gets used later on vcard = Jabber::Vcard::IqVcard.new vcard["FN"] = @name # full name vcard["NICKNAME"] = @name # nickname # buddy icon stuff unless @options.avatar_file.nil? vcard["PHOTO/TYPE"] = "image/png" # open buddy icon/avatar image file image_file = File.new(@options.avatar_file, "r") # Base64 encode the file contents image_b64 = Base64.b64encode(image_file.read()) # process sha1 hash of photo contents # this is used by the presence setting image_file.rewind # must rewind the file to the beginning @avatar_sha1 = Digest::SHA1.hexdigest(image_file.read()) vcard["PHOTO/BINVAL"] = image_b64 # set the BINVAL to the Base64 encoded contents of our image end begin # create a vcard helper and immediately set the vcard info @logger.info "Setting Vcard" vcard_helper = Jabber::Vcard::Helper.new(@jabber.client).set(vcard) rescue @logger.error "Vcard operation timed out." end return true end def set_avatar(pres) if not @avatar_sha1.nil? # send the sha1 hash of the avatar to the server # as per the RFC http://www.xmpp.org/extensions/xep-0153.html x = REXML::Element::new("x") x.add_namespace('vcard-temp:x:update') photo = REXML::Element::new("photo") # this is the avatar hash as computed in the vcard thread above avatar_hash = REXML::Text.new(@avatar_sha1) # add text to photo photo.add(avatar_hash) # add photo to x x.add(photo) # add x to presence pres.add_element(x) end end def add_admins @options.admin_jids.each do |jid| @jabber.add jid @logger.debug "Added admin #{jid}" end end end