From: "Martin Povolný" Date: 2005-09-23T14:56:42+09:00 Subject: patch for imap.rb -- error handling when waiting for continuation request response Hallo, we are using heavily Ruby IMAP library for communication with cyrus imap server. And we have discovered then when [ruby] client waits for continuation request response, it only accepts request for more data and it hangs [infinite wait] when it gets error code. This happends for example if you are trying to store a message in a folder where you don't have sufficient access rights. You send command wint continuation request do the server, tell it which folder you want do write to and wait for server to ask you for data. Cyrus will give you immediately error message. Maybe the server that the library was tested with would accept whole message and then give error so that the library would seem to work right. But giving error code as soon as possible is perfectly valid and is also OK according to the RFC. So following patch agains imap.rb from latest ruby 1.8.3 fixes this behaviour. Please tell me, what you thing. Regards, --- /tmp/imap.rb Wed Sep 21 18:05:28 2005 +++ /tmp/imap_fixed.rb Wed Sep 21 18:05:18 2005 @@ -902,8 +902,8 @@ @responses = Hash.new([].freeze) @tagged_responses = {} @response_handlers = [] - @tagged_response_arrival = new_cond - @continuation_request_arrival = new_cond + @response_arrival = new_cond + @continuation_request_arrival = false @logout_command_tag = nil @debug_output_bol = true @@ -934,7 +934,7 @@ case resp when TaggedResponse @tagged_responses[resp.tag] = resp - @tagged_response_arrival.broadcast + @response_arrival.broadcast if resp.tag == @logout_command_tag return end @@ -949,7 +949,8 @@ raise ByeResponseError, resp.raw_data end when ContinuationRequest - @continuation_request_arrival.signal + @continuation_request_arrival = true + @response_arrival.broadcast end @response_handlers.each do |handler| handler.call(resp) @@ -961,10 +962,7 @@ end end - def get_tagged_response(tag, cmd) - until @tagged_responses.key?(tag) - @tagged_response_arrival.wait - end + def pick_up_tagged_response(tag) resp = @tagged_responses.delete(tag) case resp.name when /\A(?:NO)\z/ni @@ -976,6 +974,13 @@ end end + def get_tagged_response(tag, cmd) + until @tagged_responses.key?(tag) + @response_arrival.wait + end + pick_up_tagged_response(tag) + end + def get_response buff = "" while true @@ -1009,7 +1014,7 @@ put_string(tag + " " + cmd) args.each do |i| put_string(" ") - send_data(i) + send_data(i, tag) end put_string(CRLF) if cmd == "LOGOUT" @@ -1048,32 +1053,51 @@ end end - def send_data(data) + def put_string_with_continuation(str, tag = nil) + # TODO - only one request is allowed to wait for ContinuationRequest at a time. + + @continuation_request_arrival = false + put_string("{" + str.length.to_s + "}" + CRLF) + + until (tag and @tagged_responses.key?(tag)) or @continuation_request_arrival + @response_arrival.wait + end + + unless @continuation_request_arrival + # processing Errors + pick_up_tagged_response(tag) + parse_error('expected continuation request response') + end + + put_string(str) + end + + def send_data(data, tag = nil) case data when nil put_string("NIL") when String - send_string_data(data) + send_string_data(data, tag) when Integer send_number_data(data) when Array - send_list_data(data) + send_list_data(data, tag) when Time send_time_data(data) when Symbol send_symbol_data(data) else - data.send_data(self) + data.send_data(self, tag) end end - def send_string_data(str) + def send_string_data(str, tag = nil) case str when "" put_string('""') when /[\x80-\xff\r\n]/n # literal - send_literal(str) + send_literal(str, tag) when /[(){ \x00-\x1f\x7f%*"\\]/n # quoted string send_quoted_string(str) @@ -1086,10 +1110,8 @@ put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') end - def send_literal(str) - put_string("{" + str.length.to_s + "}" + CRLF) - @continuation_request_arrival.wait - put_string(str) + def send_literal(str, tag = nil) + put_string_with_continuation(str, tag) end def send_number_data(num) @@ -1099,7 +1121,7 @@ put_string(num.to_s) end - def send_list_data(list) + def send_list_data(list, tag = nil) put_string("(") first = true list.each do |i| @@ -1108,7 +1130,7 @@ else put_string(" ") end - send_data(i) + send_data(i, tag) end put_string(")") end @@ -1324,7 +1346,7 @@ private_class_method :u8tou16 class RawData # :nodoc: - def send_data(imap) + def send_data(imap, tag = nil) imap.send(:put_string, @data) end @@ -1336,7 +1358,7 @@ end class Atom # :nodoc: - def send_data(imap) + def send_data(imap, tag = nil) imap.send(:put_string, @data) end @@ -1348,7 +1370,7 @@ end class QuotedString # :nodoc: - def send_data(imap) + def send_data(imap, tag = nil) imap.send(:send_quoted_string, @data) end @@ -1360,8 +1382,8 @@ end class Literal # :nodoc: - def send_data(imap) - imap.send(:send_literal, @data) + def send_data(imap, tag = nil) + imap.send(:send_literal, @data, tag) end private @@ -1372,7 +1394,7 @@ end class MessageSet # :nodoc: - def send_data(imap) + def send_data(imap, tag = nil) imap.send(:put_string, format_internal(@data)) end -- Mgr. Martin Povoln�, soLNet, s.r.o. Technick� podpora telefon: +420/549131233, +420/737743587