require 'time' # For Time#xmlschema module Blather class Stanza class Iq # # Si Stanza # # [XEP-0096: SI File Transfer](http://xmpp.org/extensions/xep-0096.html) # # This is a base class for any si based Iq stanzas. It provides a base set # of methods for working with si stanzas # # @example Basic file transfer acceptance # client.register_handler :file_transfer do |iq| # transfer = Blather::FileTransfer.new(client, iq) # transfer.accept(Blather::FileTransfer::SimpleFileReceiver, "/path/to/#{iq.si.file["name"]}", iq.si.file["size"].to_i) # end # # @example Basic file transfer refusal # client.register_handler :file_transfer do |iq| # transfer = Blather::FileTransfer.new(client, iq) # transfer.decline # end # # @example File transfer acceptance by in-band bytestreams with custom handler # client.register_handler :file_transfer do |iq| # transfer = Blather::FileTransfer.new(client, iq) # transfer.allow_ibb = true # transfer.allow_s5b = false # transfer.accept(MyFileReceiver, iq) # end # # @handler :file_transfer class Si < Iq # @private NS_SI = 'http://jabber.org/protocol/si' register :file_transfer, :si, NS_SI # Overrides the parent method to ensure a si node is created # # @see Blather::Stanza::Iq.new def self.new(type = :set) node = super node.si node end # Overrides the parent method to ensure the current si node is destroyed # # @see Blather::Stanza::Iq#inherit def inherit(node) si.remove super end # Find or create si node # # @return [Si::Si] def si Si.find_or_create self end # Replaces si node # # @param [Si::Si, XML::Node] node the stanza's new si node # # @return [Si::Si] def si=(node) si.remove self << node Si.find_or_create self end # Overrides the parent method to ensure the current si node is destroyed # # @see Blather::Stanza#reply def reply reply = Stanza::Iq::Si.import super reply.si.remove reply end # Si stanza fragment class Si < XMPPNode # Create a new Si::Si object # # @param [XML::Node, nil] node a node to inherit from # # @return [Si::Si] def self.new(node = nil) new_node = super :si new_node.namespace = NS_SI new_node.inherit node if node new_node end # Find or create si node in Si Iq and converts it to Si::Si # # @param [Si] parent a Si Iq where to find or create si # # @return [Si::Si] def self.find_or_create(parent) if found_si = parent.find_first('//ns:si', :ns => NS_SI) si = self.new found_si found_si.remove else si = self.new end parent << si si end # Get the id of the stream # # @return [String, nil] def id read_attr :id end # Set the id # # @param [String, nil] id the id of the stream def id=(id) write_attr :id, id end # Get the MIME type of the stream # # @return [String, nil] def mime_type read_attr 'mime-type' end # Set the MIME type # # @param [String, nil] type the MIME type of the stream def mime_type=(type) write_attr 'mime-type', type end # Get the profile of the stream # # @return [String, nil] def profile read_attr :profile end # Set the profile # # @param [String, nil] profile the profile of the stream def profile=(profile) write_attr :profile, profile end # Find or create file node # # @return [Si::Si::File] def file File.find_or_create self end # Find or create feature node # # @return [Si::Si::Feature] def feature Feature.find_or_create self end # Feature stanza fragment class Feature < XMPPNode register :feature, 'http://jabber.org/protocol/feature-neg' # Create a new Si::Si::Feature object # # @param [XML::Node, nil] node a node to inherit from # # @return [Si::Si::Feature] def self.new(node = nil) new_node = super :feature new_node.namespace = self.registered_ns new_node.inherit node if node new_node end # Find or create feature node in si node and converts it to Si::Si::Feature # # @param [Si::Si] parent a si node where to find or create feature # # @return [Si::Si::Feature] def self.find_or_create(parent) if found_feature = parent.find_first('//ns:feature', :ns => self.registered_ns) feature = self.new found_feature found_feature.remove else feature = self.new end parent << feature feature end # Find or create x node # # @return [Stanza::X] def x Stanza::X.find_or_create self end end # File stanza fragment class File < XMPPNode register :file, 'http://jabber.org/protocol/si/profile/file-transfer' # Create a new Si::Si::File object # # @param [XML::Node, nil] node a node to inherit from # # @return [Si::Si::File] def self.new(name = nil, size = nil) new_node = super :file case name when Nokogiri::XML::Node new_node.inherit name else new_node.name = name new_node.size = size end new_node end # Find or create file node in si node and converts it to Si::Si::File # # @param [Si::Si] parent a si node where to find or create file # # @return [Si::Si::File] def self.find_or_create(parent) if found_file = parent.find_first('//ns:file', :ns => self.registered_ns) file = self.new found_file found_file.remove else file = self.new end parent << file file end # Get the filename # # @return [String, nil] def name read_attr :name end # Set the filename # # @param [String, nil] name the name of the file def name=(name) write_attr :name, name end # Get the hash # # @return [String, nil] def hash read_attr :hash end # Set the hash # # @param [String, nil] hash the MD5 hash of the file def hash=(hash) write_attr :hash, hash end # Get the date # # @return [Time, nil] def date begin Time.xmlschema(read_attr(:date)) rescue ArgumentError nil end end # Set the date # # @param [Time, nil] date the last modification time of the file def date=(date) write_attr :date, (date ? date.xmlschema : nil) end # Get the size # # @return [Fixnum, nil] def size if (s = read_attr(:size)) && (s =~ /^\d+$/) s.to_i else nil end end # Set the size # # @param [Fixnum, nil] size the size, in bytes, of the file def size=(size) write_attr :size, size end # Get the desc # # @return [String, nil] def desc content_from 'ns:desc', :ns => self.class.registered_ns end # Set the desc # # @param [String, nil] desc the description of the file def desc=(desc) set_content_for :desc, desc end # Find or create range node # # @return [Si::Si::File::Range] def range Range.find_or_create self end end # Range stanza fragment class Range < XMPPNode register :range, 'http://jabber.org/protocol/si/profile/file-transfer' # Create a new Si::Si::File::Range object # # @param [XML::Node, nil] node a node to inherit from # # @return [Si::Si::File::Range] def self.new(offset = nil, length = nil) new_node = super :range case offset when Nokogiri::XML::Node new_node.inherit offset else new_node.offset = offset new_node.length = length end new_node end # Find or create range node in file node and converts it to Si::Si::File::Range # # @param [Si::Si::File] parent a file node where to find or create range # # @return [Si::Si::File::Range] def self.find_or_create(parent) if found_range = parent.find_first('//ns:range', :ns => self.registered_ns) range = self.new found_range found_range.remove else range = self.new end parent << range range end # Get the offset # # @return [Fixnum, nil] def offset if (o = read_attr(:offset)) && (o =~ /^\d+$/) o.to_i else nil end end # Set the offset # # @param [Fixnum, nil] offset the position, in bytes, to start transferring the file data from def offset=(offset) write_attr :offset, offset end # Get the length # # @return [Fixnum, nil] def length if (l = read_attr(:length)) && (l =~ /^\d+$/) l.to_i else nil end end # Set the length # # @param [Fixnum, nil] length the number of bytes to retrieve starting at offset def length=(length) write_attr :length, length end end end end end end end