Permalink
Browse files

Import more of the command formatting from Net::IMAP

  • Loading branch information...
1 parent fae44bc commit 02e063e1b74bd5c6110f35e363346ba62724cca1 @ConradIrwin committed Jul 2, 2011
Showing with 198 additions and 0 deletions.
  1. +1 −0 lib/em-imap.rb
  2. +124 −0 lib/em-imap/formatter.rb
  3. +73 −0 spec/formatter_spec.rb
View
@@ -8,6 +8,7 @@
$:.unshift File.dirname( __FILE__ )
require 'em-imap/listener'
require 'em-imap/continuation_synchronisation'
+require 'em-imap/formatter'
require 'em-imap/command_sender'
require 'em-imap/response_parser'
require 'em-imap/connection'
View
@@ -0,0 +1,124 @@
+module EventMachine
+ module IMAP
+ class Formatter
+
+ # A placeholder so that the command sender knows to treat literal strings specially
+ class Literal < Struct.new(:str); end
+
+ # Format the data to be sent into strings and literals, and call the block
+ # for each token to be sent.
+ #
+ # @param data The data to format,
+ # @param &block The callback, which will be called with a number of strings and
+ # EM::IMAP::Formatter::Literal instances.
+ #
+ # NOTE: The block is responsible for handling any network-level concerns, such
+ # as sending literals only with permission.
+ #
+ def self.format(data, &block)
+ new(&block).send_data(data)
+ end
+
+ def initialize(&block)
+ @block = block
+ end
+
+ def put_string(str)
+ @block.call str
+ end
+
+ def send_literal(str)
+ @block.call Literal.new(str)
+ end
+
+ # The remainder of the code in this file is directly from Net::IMAP.
+ # Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
+ def send_data(data)
+ case data
+ when nil
+ put_string("NIL")
+ when String
+ send_string_data(data)
+ when Integer
+ send_number_data(data)
+ when Array
+ send_list_data(data)
+ when Time
+ send_time_data(data)
+ when Symbol
+ send_symbol_data(data)
+ when EM::IMAP::Command
+ send_command(data)
+ else
+ data.send_data(self)
+ end
+ end
+
+ def send_command(cmd)
+ put_string cmd.tag
+ put_string " "
+ put_string cmd.cmd
+ cmd.args.each do |i|
+ put_string " "
+ send_data(i)
+ end
+ put_string "\r\n"
+ end
+
+ def send_string_data(str)
+ case str
+ when ""
+ put_string('""')
+ when /[\x80-\xff\r\n]/n
+ # literal
+ send_literal(str)
+ when /[(){ \x00-\x1f\x7f%*"\\]/n
+ # quoted string
+ send_quoted_string(str)
+ else
+ put_string(str)
+ end
+ end
+
+ def send_quoted_string(str)
+ put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
+ end
+
+ def send_number_data(num)
+ if num < 0 || num >= 4294967296
+ raise Net::IMAP::DataFormatError, num.to_s
+ end
+ put_string(num.to_s)
+ end
+
+ def send_list_data(list)
+ put_string("(")
+ first = true
+ list.each do |i|
+ if first
+ first = false
+ else
+ put_string(" ")
+ end
+ send_data(i)
+ end
+ put_string(")")
+ end
+
+ DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
+
+ def send_time_data(time)
+ t = time.dup.gmtime
+ s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
+ t.day, DATE_MONTH[t.month - 1], t.year,
+ t.hour, t.min, t.sec)
+ put_string(s)
+ end
+
+ def send_symbol_data(symbol)
+ put_string("\\" + symbol.to_s)
+ end
+
+ end
+ end
+end
View
@@ -0,0 +1,73 @@
+require 'spec_helper'
+describe EM::IMAP::Formatter do
+
+ before do
+ @result = []
+ @formatter = EM::IMAP::Formatter.new do |thing|
+ if thing.is_a?(String) && @result.last.is_a?(String)
+ @result[-1] += thing
+ else
+ @result << thing
+ end
+ end
+
+ @format = lambda { |data| @result.tap{ @formatter.send_data data } }
+ end
+
+ it "should format nils" do
+ @format.call(nil).should == ["NIL"]
+ end
+
+ it "should format simple strings with no quotes" do
+ @format.call("FETCH").should == ["FETCH"]
+ end
+
+ it "should quote the empty string" do
+ @format.call("").should == ['""']
+ end
+
+ it "should quote strings with spaces" do
+ @format.call("hello world").should == ['"hello world"']
+ end
+
+ it "should make strings that contain newlines into literals" do
+ @format.call("good\nmorning").should == [EM::IMAP::Formatter::Literal.new("good\nmorning")]
+ end
+
+ it "should raise an error on out-of-range ints" do
+ lambda{ @format.call(2 ** 64) }.should raise_error Net::IMAP::DataFormatError
+ lambda{ @format.call(-1) }.should raise_error Net::IMAP::DataFormatError
+ end
+
+ it "should be able to format in-range ints" do
+ @format.call(123).should == ['123']
+ end
+
+ it "should format dates with a leading space" do
+ @format.call(Time.gm(2011, 1, 1, 10, 10, 10)).should == ['" 1-Jan-2011 10:10:10 +0000"']
+ end
+
+ it "should format times in the 24 hour clock" do
+ @format.call(Time.gm(2011, 10, 10, 19, 10, 10)).should == ['"10-Oct-2011 19:10:10 +0000"']
+ end
+
+ it "should format lists correctly" do
+ @format.call([1,"",nil, "three"]).should == ['(1 "" NIL three)']
+ end
+
+ it "should allow for literals within lists" do
+ @format.call(["oh yes", "oh\nno"]).should == ['("oh yes" ', EM::IMAP::Formatter::Literal.new("oh\nno"), ')']
+ end
+
+ it "should format symbols correctly" do
+ @format.call(:hi).should == ["\\hi"]
+ end
+
+ it "should format commands correctly" do
+ @format.call(EM::IMAP::Command.new('RUBY0001', 'SELECT', ['Inbox'])).should == ["RUBY0001 SELECT Inbox\r\n"]
+ end
+
+ it "should format complex commands correctly" do
+ @format.call(EM::IMAP::Command.new('RUBY1234', 'FETCH', [[Net::IMAP::MessageSet.new([1,2,3])], 'BODY'])).should == ["RUBY1234 FETCH (1,2,3) BODY\r\n"]
+ end
+end

0 comments on commit 02e063e

Please sign in to comment.