GithubHelp home page GithubHelp logo

ruby / net-imap Goto Github PK

View Code? Open in Web Editor NEW
41.0 34.0 23.0 1.14 MB

Ruby client api for Internet Message Access Protocol

Home Page: https://ruby.github.io/net-imap

License: Other

Ruby 99.91% Makefile 0.07% Shell 0.02%
ruby hacktoberfest

net-imap's Introduction

Net::IMAP

Net::IMAP implements Internet Message Access Protocol (IMAP) client functionality. The protocol is described in IMAP.

Installation

Add this line to your application's Gemfile:

gem 'net-imap'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install net-imap

Usage

Connect with TLS to port 993

imap = Net::IMAP.new('mail.example.com', ssl: true)
imap.port          => 993
imap.tls_verified? => true
case imap.greeting.name
in /OK/i
  # The client is connected in the "Not Authenticated" state.
  imap.authenticate("PLAIN", "joe_user", "joes_password")
in /PREAUTH/i
  # The client is connected in the "Authenticated" state.
end

List sender and subject of all recent messages in the default mailbox

imap.examine('INBOX')
imap.search(["RECENT"]).each do |message_id|
  envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
  puts "#{envelope.from[0].name}: \t#{envelope.subject}"
end

Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"

imap.select('Mail/sent-mail')
if not imap.list('Mail/', 'sent-apr03')
  imap.create('Mail/sent-apr03')
end
imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
  imap.copy(message_id, "Mail/sent-apr03")
  imap.store(message_id, "+FLAGS", [:Deleted])
end
imap.expunge

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bundle exec rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/net-imap.

net-imap's People

Contributors

akr avatar arnt avatar c-leroy avatar debasishbsws avatar dependabot[bot] avatar drbrain avatar gobijan avatar hoffi avatar hsbt avatar jeremyevans avatar k0kubun avatar kamipo avatar knu avatar ko1 avatar mame avatar marcandre avatar mark-young-atg avatar matzbot avatar nevans avatar nobu avatar nurse avatar olleolleolle avatar petergoldstein avatar shugo avatar shyouhei avatar sorah avatar ssunday avatar unak avatar voxik avatar znz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

net-imap's Issues

Possible deadlock with condition variables in Net::IMAP

We're using Net::IMAP via the MailRoom gem, and quite frequently we are seeing issues with the process not terminating even though we attempt to run Thread#join with a 60-second timeout.

A GDB backtrace shows that this is stuck waiting for a conditional variable:

Thread 2 (Thread 0x7f2bf81ff700 (LWP 11)):
#0  futex_wait_cancelable (private=0, expected=0, futex_word=0x7f2bfa3df810) at ../sysdeps/unix/sysv/linux/futex-internal.h:88
#1  __pthread_cond_wait_common (abstime=0x0, mutex=0x7f2bfa3df840, cond=0x7f2bfa3df7e8) at pthread_cond_wait.c:502
#2  __pthread_cond_wait (cond=cond@entry=0x7f2bfa3df7e8, mutex=mutex@entry=0x7f2bfa3df840) at pthread_cond_wait.c:655
#3  0x00007f2bffe343fd in rb_native_cond_wait (mutex=0x7f2bfa3df840, cond=0x7f2bfa3df7e8) at thread_pthread.c:1197
#4  native_cond_sleep (th=th@entry=0x7f2bfa3df780, rel=rel@entry=0x0) at thread_pthread.c:1197
#5  0x00007f2bffe346f4 in native_sleep (th=th@entry=0x7f2bfa3df780, rel=0x0) at thread_pthread.c:2113
#6  0x00007f2bffe376de in sleep_forever (fl=1, th=0x7f2bfa3df780) at thread.c:1252
#7  rb_thread_sleep_deadly_allow_spurious_wakeup () at thread.c:1343
#8  rb_mutex_sleep_forever (time=time@entry=8) at thread_sync.c:451
#9  0x00007f2bffcce134 in rb_ensure (b_proc=b_proc@entry=0x7f2bffe37660 <rb_mutex_sleep_forever>, data1=data1@entry=8, e_proc=e_proc@entry=0x7f2bffe36160 <mutex_lock_uninterruptible>,
    data2=data2@entry=139826840321280) at eval.c:1129
#10 0x00007f2bffe361bd in rb_mutex_sleep (self=139826840321280, timeout=<optimized out>) at thread_sync.c:477
#11 0x00007f2bffe796e1 in vm_call0_cfunc_with_frame (argv=0x7f2bf80fc858, cd=<optimized out>, calling=0x7f2bf80fc700, ec=0x7f2bfa209bd0) at vm_eval.c:91
#12 vm_call0_cfunc (argv=0x7f2bf80fc858, cd=<optimized out>, calling=0x7f2bf80fc700, ec=0x7f2bfa209bd0) at vm_eval.c:105
#13 vm_call0_body (ec=0x7f2bfa209bd0, calling=0x7f2bf80fc700, cd=<optimized out>, argv=0x7f2bf80fc858) at vm_eval.c:140
#14 0x00007f2bffe7bfd8 in rb_funcallv_with_cc (cd=0x7f2bfffa75c0 <rb_funcallv_data>, recv=139826840321280, mid=24193, argc=1, argv=<optimized out>) at vm_core.h:1805
#15 0x00007f2bffcce134 in rb_ensure (b_proc=b_proc@entry=0x7f2bffe2f360 <do_sleep>, data1=data1@entry=139826822105168, e_proc=e_proc@entry=0x7f2bffe2df60 <delete_from_waitq>, data2=data2@entry=139826822105184)
    at eval.c:1129
#16 0x00007f2bffe2f472 in rb_condvar_wait (argc=<optimized out>, argv=0x7f2bf80fcad0, self=139826858411400) at thread_sync.c:1407
#17 0x00007f2bffe796e1 in vm_call0_cfunc_with_frame (argv=0x7f2bf80fcad0, cd=<optimized out>, calling=0x7f2bf80fc980, ec=0x7f2bfa209bd0) at vm_eval.c:91
#18 vm_call0_cfunc (argv=0x7f2bf80fcad0, cd=<optimized out>, calling=0x7f2bf80fc980, ec=0x7f2bfa209bd0) at vm_eval.c:105
#19 vm_call0_body (ec=0x7f2bfa209bd0, calling=0x7f2bf80fc980, cd=<optimized out>, argv=0x7f2bf80fcad0) at vm_eval.c:140
#20 0x00007f2bffe79d5f in rb_vm_call0 (ec=ec@entry=0x7f2bfa209bd0, recv=recv@entry=139826858411400, id=id@entry=24353, argc=<optimized out>, argv=<optimized out>, me=me@entry=0x7f2bfef8b9d0, kw_splat=0)
    at vm_eval.c:52
#21 0x00007f2bffe79ff8 in rb_vm_call_kw (ec=0x7f2bfa209bd0, recv=139826858411400, id=24353, argc=<optimized out>, argc@entry=2, argv=<optimized out>, argv@entry=0x7f2bf80fcab0, me=0x7f2bfef8b9d0,
    kw_splat=<optimized out>) at vm_eval.c:268
#22 0x00007f2bffe7a453 in rb_call0 (ec=<optimized out>, recv=<optimized out>, mid=<optimized out>, argc=argc@entry=2, argv=argv@entry=0x7f2bf80fcab0, call_scope=call_scope@entry=CALL_FCALL,
    self=<optimized out>) at vm_eval.c:392
#23 0x00007f2bffe7ac7e in rb_call (scope=CALL_FCALL, argv=0x7f2bf80fcab0, argc=2, mid=<optimized out>, recv=<optimized out>) at vm_eval.c:718
#24 rb_funcallv (recv=<optimized out>, mid=<optimized out>, argc=argc@entry=2, argv=argv@entry=0x7f2bf80fcad0) at vm_eval.c:958
#25 0x00007f2bff2f62e1 in monitor_wait_for_cond_body (v=v@entry=139826822106112) at monitor.c:152
#26 0x00007f2bffcce134 in rb_ensure (b_proc=b_proc@entry=0x7f2bff2f62a0 <monitor_wait_for_cond_body>, data1=data1@entry=139826822106112, e_proc=e_proc@entry=0x7f2bff2f64a0 <monitor_enter_for_cond>,
    data2=data2@entry=139826822106112) at eval.c:1129
#27 0x00007f2bff2f641e in monitor_wait_for_cond (monitor=139826840321320, cond=<optimized out>, timeout=8) at monitor.c:180
#28 0x00007f2bffe66c59 in vm_call_cfunc_with_frame (empty_kw_splat=<optimized out>, cd=0x7f2bfe9c8b70, calling=<optimized out>, reg_cfp=0x7f2bf81fd268, ec=0x7f2bfa209bd0) at vm_insnhelper.c:2514
#29 vm_call_cfunc (ec=0x7f2bfa209bd0, reg_cfp=0x7f2bf81fd268, calling=<optimized out>, cd=0x7f2bfe9c8b70) at vm_insnhelper.c:2539
#30 0x00007f2bffe72092 in vm_sendish (block_handler=<optimized out>, method_explorer=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, ec=<optimized out>) at vm_insnhelper.c:4023
#31 vm_exec_core (ec=0x7f2bfa3df810, initial=128) at insns.def:801
#32 0x00007f2bffe78365 in rb_vm_exec (ec=0x7f2bfa209bd0, mjit_enable_p=1) at vm.c:1929
#33 0x00007f2bffe82130 in invoke_block (captured=0x7f2bf81fd3d0, opt_pc=<optimized out>, type=<optimized out>, cref=<optimized out>, self=139826840321400, iseq=0x7f2bfef91600, ec=0x7f2bfa209bd0) at vm.c:1044
#34 invoke_iseq_block_from_c (me=0x0, is_lambda=0, cref=<optimized out>, passed_block_handler=<optimized out>, kw_splat=<optimized out>, argv=<optimized out>, argc=0, self=139826840321400,
    captured=0x7f2bf81fd3d0, ec=0x7f2bfa209bd0) at vm.c:1116
#35 invoke_block_from_c_bh (argc=<optimized out>, argv=<optimized out>, kw_splat=<optimized out>, passed_block_handler=<optimized out>, cref=<optimized out>, is_lambda=<optimized out>,
    force_blockarg=<optimized out>, block_handler=<optimized out>, ec=<optimized out>) at vm.c:1134
#36 vm_yield (argc=<optimized out>, argv=<optimized out>, kw_splat=<optimized out>, ec=<optimized out>) at vm.c:1179
#37 rb_yield_0 (argv=<optimized out>, argc=<optimized out>) at vm_eval.c:1227
#38 rb_yield_values (n=<optimized out>) at vm_eval.c:1252
#39 0x00007f2bffe82eef in vm_yield (kw_splat=0, argv=<optimized out>, argc=<optimized out>, ec=<optimized out>) at vm.c:1165
#40 rb_yield_0 (argv=<optimized out>, argc=<optimized out>) at vm_eval.c:1227
#41 rb_yield_values2 (argc=<optimized out>, argv=<optimized out>) at vm_eval.c:1273
#42 0x00007f2bffcce134 in rb_ensure (b_proc=0x7f2bff2f6380 <monitor_sync_body>, data1=139826840321320, e_proc=<optimized out>, data2=<optimized out>) at eval.c:1129
#43 0x00007f2bffe66c59 in vm_call_cfunc_with_frame (empty_kw_splat=<optimized out>, cd=0x7f2bfa4f19e0, calling=<optimized out>, reg_cfp=0x7f2bf81fd380, ec=0x7f2bfa209bd0) at vm_insnhelper.c:2514
#44 vm_call_cfunc (ec=0x7f2bfa209bd0, reg_cfp=0x7f2bf81fd380, calling=<optimized out>, cd=0x7f2bfa4f19e0) at vm_insnhelper.c:2539
#45 0x00007f2bffe72150 in vm_sendish (method_explorer=<optimized out>, block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, ec=<optimized out>) at vm_insnhelper.c:4023
#46 vm_exec_core (ec=0x7f2bfa3df810, initial=128) at insns.def:782
#47 0x00007f2bffe78365 in rb_vm_exec (ec=0x7f2bfa209bd0, mjit_enable_p=1) at vm.c:1929
#48 0x00007f2bffe792bc in invoke_iseq_block_from_c (me=0x0, is_lambda=<optimized out>, cref=0x0, passed_block_handler=0, kw_splat=<optimized out>, argv=<optimized out>, argc=0, self=139826857723800,
    captured=<optimized out>, ec=0x7f2bfa209bd0) at vm.c:1116
#49 invoke_block_from_c_proc (me=0x0, is_lambda=<optimized out>, passed_block_handler=0, kw_splat=<optimized out>, argv=<optimized out>, argc=0, self=139826857723800, proc=0x7f2bfa209bd0, ec=0x7f2bfa209bd0)
    at vm.c:1216
#50 vm_invoke_proc (passed_block_handler=0, kw_splat=<optimized out>, argv=<optimized out>, argc=0, self=139826857723800, proc=0x7f2bfa209bd0, ec=0x7f2bfa209bd0) at vm.c:1238
#51 rb_vm_invoke_proc (ec=0x7f2bfa209bd0, proc=proc@entry=0x7f2bf98a4510, argc=0, argv=<optimized out>, kw_splat=<optimized out>, passed_block_handler=passed_block_handler@entry=0) at vm.c:1259
#52 0x00007f2bffe3646d in thread_do_start (th=0x7f2bfa3df780) at thread.c:697
#53 0x00007f2bffe384ef in thread_start_func_2 (th=0x7f2bfa3df780, stack_start=<optimized out>) at thread.c:745
#54 0x00007f2bffe38a34 in thread_start_func_1 (th_ptr=<optimized out>) at thread_pthread.c:969
#55 0x00007f2bff9c5fa3 in start_thread (arg=<optimized out>) at pthread_create.c:486
#56 0x00007f2bff4574cf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 1 (Thread 0x7f2bff33ff80 (LWP 7)):
#0  0x00007f2bff44c916 in __GI_ppoll (fds=fds@entry=0x7fff52ea2f78, nfds=nfds@entry=1, timeout=<optimized out>, timeout@entry=0x7fff52ea2f80, sigmask=sigmask@entry=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39
#1  0x00007f2bffe33880 in rb_sigwait_sleep (th=th@entry=0x7f2bfee2d000, sigwait_fd=sigwait_fd@entry=3, rel=rel@entry=0x7fff52ea3088) at hrtime.h:148
#2  0x00007f2bffe34589 in native_sleep (th=0x7f2bfee2d000, rel=0x7fff52ea3088) at thread_pthread.c:2099
#3  0x00007f2bffe37b64 in rb_thread_terminate_all () at thread.c:587
#4  0x00007f2bffccfbaa in rb_ec_cleanup (ec=ec@entry=0x7f2bfee09050, ex=<optimized out>) at eval.c:218
#5  0x00007f2bffccfed3 in ruby_run_node (n=0x7f2bfa503a98) at eval.c:336
#6  0x000055dabf0fe10b in main (argc=<optimized out>, argv=<optimized out>) at ./main.c:50

Net::IMAP uses several conditional variables:

  1. idle : https://github.com/ruby/ruby/blob/48f324e92f9b36edc267f9871e35039cbd1c2eb9/lib/net/imap.rb#L965
  2. get_tagged_response: https://github.com/ruby/ruby/blob/48f324e92f9b36edc267f9871e35039cbd1c2eb9/lib/net/imap.rb#L1215
  3. send_literal: https://github.com/ruby/ruby/blob/48f324e92f9b36edc267f9871e35039cbd1c2eb9/lib/net/imap.rb#L1368

We're using a 60-second idle timeout for idle. However, the last two do NOT have a timeout, so it's possible we're getting stuck in one of those cases.

I noticed that send_literal only checks the state of @exception after the wait returns. Do we need to do this?

diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 720acbc86d..2e1fd6e7b9 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -1365,6 +1365,7 @@ def send_literal(str, tag = nil)
         @continued_command_tag = tag
         @continuation_request_exception = nil
         begin
+          raise @exception if @exception
           @continuation_request_arrival.wait
           e = @continuation_request_exception || @exception
           raise e if e

I also wonder if we need a timeout for these other condition variables.

RFC7162 (2014): CONDSTORE (recommended by IMAP4rev2)

Before CONDSTORE can be implemented, RFC4466 extensions should be generally supported:

CONDSTORE makes the following changes to the IMAP4 protocol (from the list at the top of RFC7162 3.1):

  • adds the UNCHANGEDSINCE STORE modifier.
    #237
  • adds the MODIFIED response code that is used with an OK response to the STORE command. (It can also be used in a NO response.)
  • adds a new MODSEQ message data item for use with the FETCH command.
  • adds the CHANGEDSINCE FETCH modifier.
    #132
  • adds a new MODSEQ search criterion.
  • extends the syntax of untagged SEARCH and ESEARCH responses to include mod-sequence.
  • adds new OK untagged responses (HIGHESTMODSEQ and NOMODSEQ) for the SELECT and EXAMINE commands.
  • defines an additional CONDSTORE parameter to SELECT/EXAMINE commands.
    #122
  • adds the HIGHESTMODSEQ status data item to the STATUS command.

Subsections of RFC7162 3.1:

  • 3.1.2. New OK Untagged Responses for SELECT and EXAMINE
    • 3.1.2.1. HIGHESTMODSEQ Response Code
    • 3.1.2.2. NOMODSEQ Response Code
  • 3.1.3. STORE and UID STORE Commands
    #237
  • 3.1.4. FETCH and UID FETCH Commands
    • 3.1.4.1. CHANGEDSINCE FETCH Modifier
      #132
    • 3.1.4.2. MODSEQ Message Data Item in FETCH Command
  • 3.1.5. MODSEQ Search Criterion in SEARCH
    NOTE: This doesn't require explicit support.
  • 3.1.6. Modified SEARCH Untagged Response
  • 3.1.7. HIGHESTMODSEQ Status Data Items
  • 3.1.8. CONDSTORE Parameter to SELECT and EXAMINE
    #122
  • 3.1.9. Interaction with IMAP SORT and THREAD Extensions
  • 3.1.10. Interaction with IMAP ESORT and ESEARCH Extensions

can't install "digest" dependency as an unpriviledged user

This is similar to #24, which failed due to io-wait. In this case ruby/digest#14 prevents installation unless the user has access to update the header install location.

some initial setup to create an unpriviledged user:

$ docker run --rm -it ruby:2.7.4 bash
root@d216e60e0a0c:/# useradd --create-home --shell /bin/bash --user-group user
root@d216e60e0a0c:/# su - user
user@d216e60e0a0c:~$ export GEM_HOME="$HOME/gems/$RUBY_MAJOR"
user@d216e60e0a0c:~$ export GEM_PATH="${GEM_HOME}:${GEM_PATH}"

Now, to prove that the user can install other gems and can compile extensions:

user@d216e60e0a0c:~$ gem install json
Fetching json-2.6.1.gem
Building native extensions. This could take a while...
Successfully installed json-2.6.1
1 gem installed

However, net-imap cannot be installed.

user@d216e60e0a0c:~$ gem install net-imap
Fetching net-imap-0.2.2.gem
Fetching io-wait-0.2.1.gem
Fetching net-protocol-0.1.2.gem
Fetching digest-3.0.0.gem
Building native extensions. This could take a while...
Successfully installed io-wait-0.2.1
Successfully installed net-protocol-0.1.2
Building native extensions. This could take a while...
ERROR:  Error installing net-imap:
        ERROR: Failed to build gem native extension.

    current directory: /home/user/gems/gems/digest-3.0.0/ext/digest
/usr/local/bin/ruby -I /usr/local/lib/ruby/2.7.0 -r ./siteconf20211119-19-e2jvw8.rb extconf.rb
creating Makefile

current directory: /home/user/gems/gems/digest-3.0.0/ext/digest
make "DESTDIR=" clean

current directory: /home/user/gems/gems/digest-3.0.0/ext/digest
make "DESTDIR="
compiling digest.c
linking shared-object digest.so

current directory: /home/user/gems/gems/digest-3.0.0/ext/digest
make "DESTDIR=" install
/usr/bin/install -c -m 0755 digest.so ./.gem.20211119-19-ted4vt
installing digest libraries
/usr/bin/install: cannot remove '/usr/local/include/ruby-2.7.0/ruby/digest.h': Permission denied
make: *** [Makefile:204: /usr/local/include/ruby-2.7.0/ruby/digest.h] Error 1

make install failed, exit code 2

Gem files will remain installed in /home/user/gems/gems/digest-3.0.0 for inspection.
Results logged to /home/user/gems/extensions/x86_64-linux/2.7.0/digest-3.0.0/gem_make.out

And the latest pre-release doesn't fix the issue either:

user@d216e60e0a0c:~$ gem install digest:3.1.0.pre3
Fetching digest-3.1.0.pre3.gem
Building native extensions. This could take a while...
ERROR:  Error installing digest:
        ERROR: Failed to build gem native extension.

    current directory: /home/user/gems/gems/digest-3.1.0.pre3/ext/digest
/usr/local/bin/ruby -I /usr/local/lib/ruby/2.7.0 -r ./siteconf20211119-179-agra2q.rb extconf.rb
creating Makefile

current directory: /home/user/gems/gems/digest-3.1.0.pre3/ext/digest
make "DESTDIR=" clean

current directory: /home/user/gems/gems/digest-3.1.0.pre3/ext/digest
make "DESTDIR="
compiling digest.c
linking shared-object digest.so

current directory: /home/user/gems/gems/digest-3.1.0.pre3/ext/digest
make "DESTDIR=" install
/usr/bin/install -c -m 0755 digest.so ./.gem.20211119-179-904e0d
installing header files
install -c -m 644 ./digest.h /usr/local/include/ruby-2.7.0/ruby
/usr/local/lib/ruby/2.7.0/fileutils.rb:1415:in `initialize': Permission denied @ rb_sysopen - /usr/local/include/ruby-2.7.0/ruby/digest.h (Errno::EACCES)
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1415:in `open'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1415:in `block in copy_file'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1414:in `open'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1414:in `copy_file'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:511:in `copy_file'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:880:in `block in install'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1589:in `block in fu_each_src_dest'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1603:in `fu_each_src_dest0'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:1587:in `fu_each_src_dest'
        from /usr/local/lib/ruby/2.7.0/fileutils.rb:876:in `install'
        from install_headers.rb:9:in `block in <main>'
        from install_headers.rb:8:in `each'
        from install_headers.rb:8:in `<main>'
make: *** [Makefile:283: do-install-headers] Error 1

make install failed, exit code 2

Gem files will remain installed in /home/user/gems/gems/digest-3.1.0.pre3 for inspection.
Results logged to /home/user/gems/extensions/x86_64-linux/2.7.0/digest-3.1.0.pre3/gem_make.out

[DISCUSS] Structural changes to ResponseParser

I would like to split Net::IMAP::ResponseParser into three parts: the parser, the lexer, and a "builder" (so the data structures built by the parser can be more easily customized). Would a patch that does just this (and nothing else) be accepted?

The parser seems to have been based on an obsolete IMAP version. E.g. some of the comments and code around astrings (see fe77596) match the older RFC2060. Although I can continue creating smaller patches to update smaller pieces, would a much larger cleanup project to more closely match RFC3501 be accepted?

And, what do you think about rewriting the parser with racc? The lexer could be converted to StringScanner or to ragel or it could keep the same #next_token it has now. I have some improvements on diagnostics for parse errors that I'd like to add as well.

Would you accept big patches to build out a much larger ResponseParser test suite? This should include benchmark scripts to protect against regressions. This should be a prerequisite for the bigger updates, like racc or StringScanner (or ragel).

My company processes GBs of IMAP every day, and speeding up ResponseParser would be a godsend. CPU is the limiting resource for my IMAP daemons, and I've wanted to rewrite or replace our parser for years now. For our newest project, my team has been using golang instead of ruby, even though the go IMAP library had fewer features and is more difficult to work with. That makes me so sad. :(

So in summary, are PRs welcome to:

  • #10 Split ResponseParser into multiple components:
  • Extract a lexer and "builder" from the existing ResponseParser class..
  • A big update of the existing recursive descent parser to match RFC3501 and RFC4466 (and others).
  • Greatly expand the ResponseParser test suite?.
  • Convert the parser to racc?
  • Convert the lexer to StringScanner?
    • (maybe) Convert the lexer to ragel-generated C code?

Thanks!

cache capabilities and add capability?(name)

The client should keep track of server CAPABILITY responses and response codes. And it should invalidate previously cached values at the appropriate times (e.g. after STARTTLS).

The base spec requires this: (e.g. #32 LOGINDISABLED, #48 AUTH=). And many other extensions require the client to keep track (e.g. #37 LITERAL-). And the parser will also need to be able to "enable" different behavior for some extensions (e.g.
BINARY, UTF8=ACCEPT, IMAPrev2).

`date` dependency requires native compilation

There is a major issue with date: it requires native compilation. My server does not have a compiler installed for security reasons and I cannot install the latest version of net-imap now.

I install Ruby from distro packages and then a set of pure Ruby gems but net-imap is no longer pure. โ˜น๏ธ

Originally posted by @mperham in #66 (comment)

I've been deploying everything in containers for a while now, so I forget about these pain points. It's a common pattern for production Dockerfiles to either uninstall compilers or copy compiled libs over from another image layer.

Unless you are deploying to ruby 2.4 and older, some version of the date gem should be installed already... so if you are willing and able to simply pin the version that matches your distro ruby installation, that should work, I think.

Another option might be to create native gem files, maybe scripted with docker or rake-compiler or maybe rake-compiler-dock, upload them to S3, and configure the server with an S3 gem source.

I wonder if ruby/date would consider a PR for publishing pre-compiled native gems?

And, of course, you could fork net-imap and install from your fork. Or we could drop the explicit dependency. โ˜น๏ธ

@hsbt Any thoughts?

RFC4466 (2006): Collected Extensions to IMAP4 ABNF

Supporting RFC4466 as well as the basic ABNF in RFC3501 will give significant forward compatibility, allowing us to partially parse some other extensions which we don't (yet?) otherwise support. Additionally, RFC4466 support is necessary for RFC9051.

  • Parse unknown or unsupported extensions using RFC4466 tagged-ext syntax
    • Parse tagged-ext-val (done as part of #225)
    • Add ExtensionData (done as part of #225)
    • #127
    • #128
    • #129
      (also usable for non-RFC4466 extension data)
  • RFC4466 2.1: Optional Parameters with the #select/#examine Commands
    • #124
    • Add kwargs to #select/#examine for existing select-param extensions
  • RFC4466 2.2: Extended #create Command
    • #126
    • Add kwargs to #create for existing create-param extensions
  • RFC4466 2.3: Extended #rename Command
  • RFC4466 2.4: Extensions to #fetch and #uid_fetch Commands
    • #133
    • Add kwargs to #fetch/#uid_fetch for existing fetch-modifier extensions
  • RFC4422 2.6: Extensions to #search Command
    • #115
      • Add arbitrary return opts to #search, using search-return-opts syntax (RFC4466 tagged-ext)
    • #121
      • #120
      • Parse all existing search-return-data extensions directly
  • RFC4422 2.7: Extensions to #append Command
    • BINARY
    • LITERAL+
    • MULTIAPPEND

Avoiding "already initialized constant errors" with net/protocol

If I have a gem that depends on net/imap v0.2.1, I see that it conflicts with the Ruby interpreter's version:

$ bundle exec rspec spec
/home/travis/.rvm/rubies/ruby-2.6.6/lib/ruby/2.6.0/net/protocol.rb:66: warning: already initialized constant Net::ProtocRetryError
/home/travis/.rvm/gems/ruby-2.6.6/gems/net-protocol-0.1.0/lib/net/protocol.rb:66: warning: previous definition of ProtocRetryError was here
/home/travis/.rvm/rubies/ruby-2.6.6/lib/ruby/2.6.0/net/protocol.rb:206: warning: already initialized constant Net::BufferedIO::BUFSIZE
/home/travis/.rvm/gems/ruby-2.6.6/gems/net-protocol-0.1.0/lib/net/protocol.rb:206: warning: previous definition of BUFSIZE was here
/home/travis/.rvm/rubies/ruby-2.6.6/lib/ruby/2.6.0/net/protocol.rb:503: warning: already initialized constant Net::NetPrivate::Socket
/home/travis/.rvm/gems/ruby-2.6.6/gems/net-protocol-0.1.0/lib/net/protocol.rb:503: warning: previous definition of Socket was here

Is there a way this updated version of the gem can be used as a separate dependency without generating these warnings? https://stackoverflow.com/a/9117903/1992201 has some suggestions, but these still seem like hacks. Does it make sense to remove net/protocol as a hard dependency? MRI appears to have an exact copy of protocol.rb.

net-imap no longer installs in clean Ruby 2.7 environment

$ docker run -it ruby:2.7 bash
root@ef5efd9831f6:/# gem install net-imap
Fetching net-protocol-0.1.2.gem
Fetching net-imap-0.2.2.gem
Fetching io-wait-0.2.0.gem
Fetching digest-3.0.0.gem
ERROR:  Error installing net-imap:
	The last version of io-wait (>= 0) to support your Ruby & RubyGems was 0.1.0. Try installing it with `gem install io-wait -v 0.1.0` and then running the current command again
	io-wait requires Ruby version >= 3.0.0. The current ruby version is 2.7.4.191.

Since net-imap isn't pinned to a specific net-protocol gem, and net-protocol doesn't pin to a specific io-wait version, ruby/io-wait@6fed3da was picked up, which requires Ruby 3.0.

Using PLAIN is broken in ruby 3.1.0

For years, up to and including ruby 2.7.5, I've been able to authenticate into some IMAP servers (even gmail) like so:

server = Net::IMAP.new(HOST, :port=>993, :ssl=>true)
server.authenticate('PLAIN', USERNAME, PASSWORD)

I've just upgraded to ruby 3.1.0 (from 2.7.5), and now this doesn't work on any of those servers. Instead I get:

lib/ruby/gems/3.1.0/gems/net-imap-0.2.2/lib/net/imap.rb:1252:in `get_tagged_response': Authentication failed. (Net::IMAP::NoResponseError)

If I switch back to ruby 2.7.5 it works again.

I'm not knowledgeable enough to understand why this stopped working.

Split out response parser (etc) into multiple files

I would find it much easier to work with if net/imap.rb were split into several files. Would a patch like that be welcome and accepted? Just let me know, and I'll submit a PR right away (and if it's accepted, I'll rebase all existing PRs on it).

e.g:

  • net/imap/response_parser.rb for Net::IMAP::ResponseParser
  • net/imap/response_data.rb for the response data Structs (e.g. TaggedResponse, FetchData, MailboxList, etc)
  • net/imap/command_data.rb for all of the #send_data/#validate classes (like Atom, Literal, QuotedString, etc)
  • net/imap/authenticators.rb
    • net/imap/authenticators/{cram_md5,login,plain} etc

Newly added date dependency in 0.3.2 breaks JRuby installs

0.3.2 added a dependency on date which breaks installing Rails under JRuby (without some extensions building stuff?) due to its transitive dependency on net-imap and then seems to relate to ruby/date#48

Not sure if there'll be any change here so feel free to close if so, but logging here so people can discover it.

See gocd/gocd#11097 for one such example. One normally doesn't expect to be building native extensions under JRuby.

Interested to see if anyone else is experiencing a strscan dependency problem with 0.2.3

Ruby 3.1.2 still includes strscan as a default gem. The version of strscan is 3.0.1 in Ruby 3.1.2 (latest release).
net-imap is dependent on strscan and according to the Gemspec, just wants the latest version.
strscan, the broken out gem, is now at 3.0.3, but of course is still 3.0.1 in Ruby itself.
This is causing a gem dependency problem when trying to start a Rails app:

You have already activated strscan 3.0.1, but your Gemfile requires strscan 3.0.3

In your opinion, who is to blame here? net-imap? strscan? ruby? The Rails server (passenger, in this case)?

Support for IMAP4rev2 and modern extensions

This is a meta-ticket to track support for IMAP capabilities. I'm also listing capabilities that are already supported by Net::IMAP as well as extensions that are obsolete or unsupported by IMAP servers, so that (when completed) the list will be exhaustive. Later, we can copy the list of supported RFCs and extensions into the rdoc.

This list hasn't been carefully audited yet. Some unchecked items can probably be checked off without any extra effort. As tickets and PRs are created for various features, I'll update this list to include them.

Basic protocol specifications

  • RFC3501 (2003): Internet Message Access Protocol - version 4rev1
    • support all basic IMAP4rev1 commands
    • #50
    • #49
      several extensions require this. e.g. non-synchronizing literals
    • #48
    • #30
      this can be a client option, to preserve backwards compatibility
  • #35
  • RFC9051 (2021): IMAP4rev2
    • Appendix E: Changes since RFC3501
      Only the relevant items are listed below. In some cases, all that we need do for Net::IMAP is add (or link to) documentation.
      • 1. Support for 64-bit message and body part sizes.
        Net::IMAP doesn't validate number or number64 size, and thus automatically supports the larger 64-bit sizes
      • 2. Folded in:
      • 3. Added STATUS SIZE RFC8438 and STATUS DELETED.
        These are both numeric status attributes and thus were already implicitly supported.
        #225 added explicit support and documented both.
      • 4. SEARCH command now requires to return the ESEARCH response (SEARCH response is now deprecated)
        #44
      • 7. Clarified that the COPYUID response code is returned for both MOVE and UID MOVE.
        #65
      • 14. For future extensibility, extended ABNF for tagged-ext-simple to allow for bare number64
        #35
      • 16. Mailbox names and message headers now allow for UTF-8. Support for modified UTF-7 in mailbox names is not required, unless compatibility with IMAP4rev1 is desired.
        #38
      • 20. IDLE command can now return updates not related to the currently selected mailbox state.
        Not applicable: Net::IMAP does not interpret unsolicited responses, and leaves this up to the library users.
      • 22. Clarified that client implementations MUST ignore response codes that they do not recognize. (Changed from a SHOULD to a MUST.)
      • 23. #110
      • 24. #109
      • #118
      • 26. Added warnings about use of ALERT response codes and PREAUTH response.
      • 27. Replaced DIGEST-MD5 SASL mechanism with SCRAM-SHA-256. DIGEST-MD5 was deprecated.
    • Appendix F. Other Recommended IMAP Extensions
  • RFC3501's official "updated by" RFCs:
    • #35
    • RFC4469 (2006): CATENATE
    • RFC4551 (2006): obsoleted by RFC7162 (2014): CONDSTORE
    • RFC5032 (2007): WITHIN
    • #42
    • RFC5738 (2010): obsoleted by RFC6855 (2013): UTF-8 support
    • RFC6186 (2011): SRV Records for Locating Email Services
      • updated by: RFC8314 (2018): Cleartext Obsolete: Use TLS for Email
      • updated by: [RFC8553] (2019): DNS AttrLeaf, fixing underscored node names
    • RFC6858 (2013): Simplified Downgrading for I18n Email
      • values simplicity of implementation over fidelity of representation, since implementing a high-fidelity downgrade algorithm... is likely more work than implementing proper UTF-8 support RFC6855.
    • RFC7817 (2016): Updated TLS Server Identity Check Procedure
    • RFC8314 (2018): Cleartext Obsolete: Use TLS for Email
    • RFC8437 (2018): UNAUTHENTICATE
    • #39

IANA registries

Registry references
Capabilities RFC3501
Mailbox attributes RFC3501
SASL Mechanisms RFC4422
LIST EXTENDED RFC5258
Response codes RFC5530
Keywords RFC5788, RFC8621

Commonly supported extensions

This is not an exhaustive list of all extensions, but an opinionated selection of the most important extensions that Net::IMAP should support. It is partially based on CAPABILITY data for hundreds of thousands of IMAP4 accounts, and partially based on subjective judgement. Additionally, any RFCs we already support, even if only partially, have been promoted to this list. Some extension behaviors are out-of-scope for Net::IMAP, so "support" simply means parsing and adding the necessary documentation for users of net-imap to implement the extension bevavior. Please comment below if you think something should be promoted to this list!

sorted by original RFC year

  • RFC2087 (1997): QUOTA
  • RFC2177 (1997): IDLE (included in IMAP4rev2)
  • RFC2342 (1998): #5 NAMESPACE (included in IMAP4rev2)
  • RFC2971 (2000): #4 ID
  • RFC3348 (2002): CHILDREN
    • MailboxList predicate methods
  • RFC3516 (2003): BINARY
    • #41
    • APPEND command extensions
  • #40
  • RFC4314 (2005): ACL
    • commands: GETACL, SETACL
    • response: ACL
    • commands: DELETEACL, LISTRIGHTS, MYRIGHTS
    • response: LISTRIGHTS, MYRIGHTS
  • #36
    • it is possible to manually extract the raw COPYUID data
  • #35
  • RFC4616 (2006): AUTH=PLAIN
  • RFC5256 (2008): SORT
  • #44
  • #34
  • #46
  • XOAUTH2 (2007): AUTH=XOAUTH2
  • GMAILext (2007): X-GM-EXT-1
    • XLIST deprecated for SPECIAL-USE
    • X-GM-RAW, X-GM-MSGID, X-GM-THRID, X-GM-LABELS
      n.b. GMail's non-standard extensions can almost be obsoleted by a combination of OBJECTID, SORT, THREAD, SEARCH=FUZZY, ESEARCH, ESORT, and AUTH=OAUTHBEARER. But GMail and other large email providers still haven't upgraded to use all of these new standards. Until such time as they are deprecated by GMail (and AUTH=XOAUTH2 is used by many providers), I think it's useful to add these to the standard library instead of installing a separate gem.
  • #33
  • #42
  • RFC5957 (2010): SORT=DISPLAY (only requires documentation)
  • #43
  • RFC5530 (2009): Response Codes (IANA registry) (included in IMAP4rev2)
  • #38
  • RFC5802 (2010): AUTH=SCRAM-*
    #54
  • #45
  • #108
    • Constants for the mailbox name attributes are added by #28
  • RFC6851 (2013): MOVE (included in IMAP4rev2)
  • #106 (obsoletes: RFC4551 2006)
  • #107 (obsoletes: RFC5162 2008)
  • #47
  • RFC7888 (2016): LITERAL+ (obsoletes: RFC2088 1997)
  • #37
  • RFC7889 (2016): APPENDLIMIT
  • RFC8437 (2018): UNAUTHENTICATE
  • RFC8438 (2018): STATUS=SIZE (included in IMAP4rev2)
    • n.b. Net::IMAP imposes no restrictions on STATUS attributes and can parse any atom number extension in the response, so it automatically supports STATUS mbox (SIZE). It is the user's responsibility to check for the capability.
  • RFC8457 (2018): Keyword: $Important
    • n.b. Net::IMAP ignores keyword semantics, leaving that to the user. So "support" simply means that the attributes are parsed correctly.
  • RFC8457 (2018): Special-use attribute: \Important
  • #39
  • RFC8970 (2020): PREVIEW, Message Preview Generation
  • RFC9208 (2022): QUOTA (obsoletes RFC2087)
  • RFC9394 (2023): PARTIAL, for Paged SEARCH and FETCH

Additional standard recommendations

  • RFC2180 - IMAP4 Multi-Accessed Mailbox Practice
  • RFC2595 - Using TLS with IMAP, POP3 and ACAP
  • RFC2683 (1999): IMAP4 Implementation Recommendations
    • mailbox listing, heirarchy, delimiters, NAMESPACE, etc
    • mailbox "\Noselect", "\Noinferiors", "[TRYCREATE]", "[READ-ONLY]"
    • UIDs and UIDVALIDITY
    • FETCH (any order, extra information, unsolicited)
    • "Clients must present ALERT text clearly to the user."
  • RFC4549 (2006): Synchronization Operations for Disconnected IMAP4 Clients
  • RFC5550 (2009): The Internet Email to Support Diverse Service Environments (Lemonade) Profile
    • obsoletes: [RFC4550] (2006)
  • RFC5598 (2009): Internet Email Architecture (Errata Exist)

Other extensions

Some of these are easy to add, and many of them would be very useful. I placed them down here instead of prioritizing them above mostly due to a quick and unscientific sampling of email accounts and server capabilities.

sorted by estimated current server support

By my estimation, the following are currently supported by relatively few email addresses. They are sorted by RFC number.

New specifications

Looking at recent standards and others that are still in draft form (as of 2021-10-19):

IETF "extra" WG (Email mailstore and eXtensions To Revise or Amend)

Deprecations?

We might want to remove obsolete authentication mechanisms from the default set of authenticators. For people who still need them, the code could be preserved in separate files, with explicit requires, e.g:

require "net/imap/sasl/cram-md5_authenticator"
require "net/imap/sasl/digest-md5_authenticator"
require "net/imap/sasl/login_authenticator"
  • #55
  • #62
    • LOGIN (only published as a draft. use AUTH PLAIN or LOGIN instead)
    • AUTH=CRAM-MD5
    • AUTH=DIGEST-MD5 - officially obsoleted by RFC6331 (2011)

Should we create a shared net-sasl gem?

Currently, the following gems duplicate partial support for a small subset of SASL mechanisms:

  • net-imap
  • net-smtp
  • net-pop (it's actually missing, but it should be in there)
  • net-ldap
  • blather (XMPP)
  • memcached
  • dalli (another memcached client)
  • ...and probably many others.

This duplication and incomplete support basically defeats the purpose of SASL. It seems to me that SASL is used in a wide enough number of internet protocols that some level of SASL support should be in stdlib. See e.g. it's in java standard edition

We could start with a very simple API, which only handles client-side authentication and doesn't do much more than Net::IMAP already does. Simply providing a standard for pluggable support is useful. E.g here's a starter proposal:

  • class Net::SASL::Registry to allow non-global config, so e.g. a mechanism could be added to Net::IMAP without affecting other libraries.
    • #add_authenticator(name, mechanism_class)
    • #authenticator(name, *args)
    • both of these methods would also be available from a global registry on Net::SASL
  • Net::SASL::Authenticator interface (can provide a super-class with NotImplementedError on perform):
    • #initialize(*credentials, uri: nil)
      • some mechanisms require the host and port, and supporting that in the generic interface simplifies making clients work properly regardless of which mechanism is selected.
    • #supports_initial_response?
    • #process(challenge) - just as with Net::IMAP. IR sends a nil challenge
    • #done? - for mechanisms implementing a state machine, users of the library can know it is done without needing to call #perform and catch an exception.
    • respond_to?(...)could be used for backwards compatibility with Net::IMAP authenticators which haven't yet been updated to add supports_initial_response? or #done?.
  • utility methods:
    • Net::SASL.saslprep(string) - implements RFC4013, which is required by some mechanisms and recommended for others

And, of course, we could start by adding the existing Net::IMAP mechanisms. But it would be simple and very useful to add OAUTHBEARER and some others as well.

I've marked #22 as a draft, pending some discussion about this. Is this the correct place to discuss this? Should I create a ticket in the ruby issue tracker?

RFC6331: Moving DIGEST-MD5 to Historic

Dear @ruby team,

In first, I wish you a Happy New Year!

20 November 2008: CRAM-MD5 to Historic:

29 June 2017: CRAM-MD5 to Historic:

July 2011: RFC6331: Moving DIGEST-MD5 to Historic:

August 2021: RFC9051: Internet Message Access Protocol (IMAP) - Version 4rev2:
"Replaced DIGEST-MD5 SASL mechanism with SCRAM-SHA-256. DIGEST-MD5 was deprecated."

I add same about SCRAM-MD5.

There are now:

  • July 2010: RFC5802: Salted Challenge Response Authentication Mechanism (SCRAM): SASL and GSS-API Mechanisms: https://tools.ietf.org/html/rfc5802 (SCRAM-SHA-1 and SCRAM-SHA-1-PLUS)
  • July 2010: RFC5803: Lightweight Directory Access Protocol (LDAP) Schema for Storing Salted: Challenge Response Authentication Mechanism (SCRAM) Secrets: https://tools.ietf.org/html/rfc5803
  • November 2015: RFC7677: SCRAM-SHA-256 and SCRAM-SHA-256-PLUS: Simple Authentication and Security Layer (SASL) Mechanisms: https://tools.ietf.org/html/rfc7677

Soon:

SCRAM-SHA-1(-PLUS) + SCRAM-SHA-256(-PLUS) + SCRAM-SHA-512(-PLUS) + SCRAM-SHA3-512(-PLUS) supports

Dear @ruby team,

In first, I wish you a Happy New Year!

Can you add supports of :

  • SCRAM-SHA-1
  • SCRAM-SHA-1-PLUS
  • SCRAM-SHA-256
  • SCRAM-SHA-256-PLUS
  • SCRAM-SHA-512
  • SCRAM-SHA-512-PLUS
  • SCRAM-SHA3-512
  • SCRAM-SHA3-512-PLUS

You can add too:

  • SCRAM-SHA-224
  • SCRAM-SHA-224-PLUS
  • SCRAM-SHA-384
  • SCRAM-SHA-384-PLUS

"When using the SASL SCRAM mechanism, the SCRAM-SHA-256-PLUS variant SHOULD be preferred over the SCRAM-SHA-256 variant, and SHA-256 variants [RFC7677] SHOULD be preferred over SHA-1 variants [RFC5802]".

https://xmpp.org/extensions/inbox/hash-recommendations.html

-PLUS variants:

IMAP:

LDAP:

  • RFC5803: Lightweight Directory Access Protocol (LDAP) Schema for Storing Salted: Challenge Response Authentication Mechanism (SCRAM) Secrets: https://tools.ietf.org/html/rfc5803

HTTP:

2FA:

IANA:

Linked to:

NOOP untagged response sent by Zimbra

Hello,

I quite often encounter a response parsing error while issuing long running requests to our Zimbra server :

Traceback (most recent call last):
	8: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:1120:in `block in initialize'
	7: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:1147:in `receive_responses'
	6: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:1244:in `get_response'
	5: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:2181:in `parse'
	4: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:2255:in `response'
	3: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:2308:in `response_untagged'
	2: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:2879:in `text_response'
	1: from .rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:3347:in `match'
.rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/net/imap.rb:3495:in `parse_error': unexpected token CRLF (expected SPACE) (Net::IMAP::ResponseParseError)

The last time, the server sent a untagged response "* NOOP" while a close request was in progress, after I had flagged lots of mails as Deleted :

[...]
C: RUBY0037 UID STORE 3668787,3668788,3668790,3668791 +FLAGS (\Deleted)
S: * 3480 FETCH (FLAGS (\Deleted) UID 3668787)
S: * 3481 FETCH (FLAGS (\Deleted) UID 3668788)
S: * 3482 FETCH (FLAGS (\Deleted) UID 3668790)
S: * 3483 FETCH (FLAGS (\Deleted) UID 3668791)
S: RUBY0037 OK UID STORE completed
C: RUBY0038 CLOSE
S: * NOOP
@str: "* NOOP\r\n"
@pos: 8
@lex_state: EXPR_BEG
@token.symbol: CRLF
@token.value: "\r\n"
[backtrace]

I did not find in RFC3501 if this was OK for Zimbra to do so, it seems not.

RFC5258 (2008): `LIST-EXTENDED` (mostly included in `IMAP4rev2`)

RFC5258 also established an IANA registy.

  • Add select options to #list, using RFC4466 tagged-ext syntax
  • Add return options to #list, using RDC4466 tagged-ext syntax
  • Parse all RFC9051 mbox-list-extended-item directly
    • TODO: add appropriate tickets here...
  • Parse all other mbox-list-extended-item extensions directly
    • TODO: add appropriate tickets here...
  • Parse unknown mbox-list-extended into TaggedExtensionData

Honoring server-reported and client-enabled capabilities

  • Depends on:
  • Subtasks:
    • Security related (authentication, TLS, etc)
  • Related:
    • refuse LOGIN for cleartext connections
    • refuse AUTHENTICATE for cleartext connections, configurable per authenticator

Using capabilities information

The client should track whenever it is knowingly violating capabilitied, either server reported or client enabled. At the very least, this should include checking capabilities before sending specific commands or command arguments.

Client-enabled capabilties

There will be no API to work around client.enable(capability). Users who are determined to evade this limitation can figure out how cheat with client.instance_variable_set(...).

Server reported capabilities

The server capabilties cache will always be kept up-to-date when capabilities are checked. If the server hasn't sent its capabilities unsolicited, the client will request capabilties whenever capabilities are checked.

If the capabilities check fails:

  • For new commands or new arguments to existing commands.
    • A CapabilityError will be raised without issuing any command to the server,
  • Where there is a security risk.
    • A CapabilityError will be raised without issuing any command to the server,
    • This is backwards incompatible!
  • For other existing commands with existing command arguments.
    • A warning will be printed to $stderr but the command will still be sent

In some future version of Net::IMAP (perhaps the version released with ruby 3.2 or 3.3), the default for existing commands should change to match the behavior for new commands.

Configuration

To opt-out of the new behavior or to opt-in to more strict behavior:

  1. Net::IMAP#enforce_capabilities=
    • nil -- Uses the current default behavior, which can change in future releases.
    • true -- Always raise an exception for any failed capabilities checks. Never knowingly sends unsupported commands or command arguments to the server.
    • :warn -- Prints a warning to $stderr for non-security-related capabilities, regardless of the command or the current Net::IMAP default. Still raises for security-related capabilities checks.
    • false -- Restores old default behavior: client doesn't care about server capabilities. Still warns for security-related capabilities checks.
    • def []: (CapabilityError, Net::IMAP) -> (nil | true | false | :warn), eg. a Proc. May change security-related capabilities handling.. The command, command args, required capabilities, etc will all be available on CapabilityError.
  2. Net::IMAP#initialize(*args, **kwargs, enforce_capabilities: cfg)
    • Shorthand for Net::IMAP.new(...).tap {|client| client.enforce_capabilities = cfg }
  3. Net::IMAP#cmdname(..., enforce_capabilities: cfg)
    • #cmdname represents any command which might check server capability.
    • This kwarg also changes security-related capabilities handling for #starttls, #authenticate, #login.

opting out of security errors

enforce_capabilities: false will still raise a warning for security related violations. These warnings can only be disabled with a proc.

client.authenticate("PLAIN", username, password, enforce_capabilities: false)
# $stderr << "warning: Ignoring server capability \"AUTH=PLAIN\"" unless capability?("AUTH=PLAIN")
# The server might respond with a tagged `NO` => `NoResponseError`
client.login(username, password, enforce_capabilities: false)
# $stderr << "warning: Ignoring server capability \"LOGINDISABLED\"" if capability?("LOGINDISABLED")
# The server might respond with a tagged `NO` => `NoResponseError`

# Always ignore server capabilities. Please don't do this!
client = Net::IMAP.new(..., enforce_capabilities: -> _ { false })
client.enforce_capabilities = -> _ { false }

Allow fine-grained configuration with a Proc

The arguments will be a CapabilityError exception (which can be raised) and the Net::IMAP client object (in case the proc is shared between multiple clients). CapabilityError should have at least one sub-class, SecurityCapabilityError, to represent LOGINDISABLED, AUTH=, STARTTLS capabilities, etc. CapabilityError and its subclasses should allow pattern matching via #deconstruct and/or #deconstruct_keys

This could be used to:

  • add support for capabilities which haven't been added to net-imap yet
  • log unexpectedly missing security related capabilities errors.
  • log all capabilities errors.
client.enforce_capabilities = proc do |error|
  case error
  in AuthCapabilityError{sasl_mechanism: "FOOBAR"}
    raise "Server doesn't support our pretend SASL mechanism"
  in SecurityCapabilityError{command: "AUTHENTICATE" | "STARTTLS"}
    raise SecureAuthUnsupportedError
  in SecurityCapabilityError
    raise error
  in command: "SELECT" | "EXAMINE", capability: "QRESYNC"
    error.run_alternate_commands do
      # a theoretical API for gracefully degrading
    end
  in capability: "X_PLEASE_WARN"
    logger.warn { "X_WARNING: error.warning" }
  in capability: "X_PLEASE_WARN_2"
    # let Net::IMAP handle warning via $stderr
    # just like client.enforce_capabilities = :warn
    :warn
  in capability: "BINARY"
    # let Net::IMAP raise the error
    # just like client.enforce_capabilities = true
    true
  in capability: "X_THIS_CLIENT_ALLOWS_IT"
    # ignore the missing capability
    # just like client.enforce_capabilities = false
    false
  else
    # use the default behavior for this version of Net::IMAP, i.e. warn now, raise later
    # just like client.enforce_capabilities = nil
    nil
  end
end

SASL: Add support for lazy-loaded properties and event callbacks

Lazy loading can be used with:

  • password prompts,
  • web browser interactions to acquire/refresh OAuth2 access tokens,
  • KMS lookups
  • audit logs
  • dynamic values that can't be computed until data from the server has been seen.

Eagerly doing these actions can be wasteful, frustrating to the user, or violate security policies.

Build broken by missing libyaml-dev

Psych doesn't vendor libyaml anymore, so the system needs the header files in order to install it.

See also:

I'm hoping it's fixed in runner-images, because the conversation on setup-ruby was reluctant to fix it there. IMO, setup-ruby should come with everything needed to install any standard gem without installing extra OS packages.

But, just to make the build work, we can probably pin to an older version of psych (temporarily).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.