Search code examples
rubyimap

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('[email protected]', '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('[email protected]', '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.


Solution

  • 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 effectvely 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 creating a new imap object.