Tomalla Tomalla - 4 months ago 41
Ruby Question

Cannot logout from IMAP server (Ruby)

Using Ruby I'm writing a daemon which synchronizes mails from an IMAP server. I have multiple accounts which I log into one after another and download unread messages. However I'm unable to reuse the existing connection to the IMAP server.

Here's a rough example of what I want to do:

require 'net/imap'

imap = Net::IMAP.new('imap.example.com')

imap.login('login@example.com', 'R2D2C3PO')
imap.select('INBOX')

# interacting with the server, like fetching the messages etc.
# for example:
puts imap.fetch(3, "BODYSTRUCTURE")

# now I want to log out of the account and log in with another
imap.logout

imap.login('anotherlogin@example.com', 'R2D2C3PO')
imap.select('INBOX')

# again - interacting with the server
puts imap.fetch(3, "BODYSTRUCTURE")

imap.disconnect


However an exception is raised when calling the
logout
method:

/usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:110:in `sleep': No live threads left. Deadlock? (fatal)
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:110:in `wait'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:110:in `wait'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/imap.rb:1166:in `get_tagged_response'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/imap.rb:1225:in `block in send_command'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/imap.rb:1207:in `send_command'
from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/imap.rb:435:in `login'
from test.rb:13:in `<main>'


I'm completely baffled by a threading exception, since the daemon is only single-threaded. Under the hood the process is hang on the conditional variable while waiting for the server response. Why it wasn't a problem when calling previous methods (
LOGIN
,
SELECT
and other commands) and it becomes a problem when sending the
LOGOUT
command?




Edit:



I somehow completely missed the fact, that the
LOGOUT
command renders the connection to the IMAP server no longer usable (as @HolgerJust points out). Although my idea to reuse the connection has failed miserably, the main point still stands: the exception is raised when sending the
LOGOUT
command, which at this point is still a legitimate action and should not cause any trouble.

Answer

Once you have logged out from an IMAP session, you have to create a new connection to execute any further commands. After a LOGOUT, the connection is not allowed to be further re-used.

To quote from RFC 3501:

The LOGOUT command informs the server that the client is done with the connection. The server MUST send a BYE untagged response before the (tagged) OK response, and then close the network connection.

In Ruby code, this effectively means that after you have called logout on your imap object, you can't do anything useful with it anymore.

Instead, create a new connection by either creating a new imap object or at least a new connection.

The simplest solution is probably to just call imap.disconnect after logout so that the old connection is closed. The imap object will then automatically create a new connection next time you send a command.