-
Notifications
You must be signed in to change notification settings - Fork 333
Support for rfc5465: The IMAP NOTIFY Extension #718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v2
Are you sure you want to change the base?
Conversation
|
v2: removed a very long example from the docs. It's very easy for this to fall out of sync and not really useful. |
|
Tests with dovecot failed. When writing these tests I didn't realise that they also run with another server in CI. I lean towards dropping the third commit— the one which implements the scaffold for NOTIFY in imapmemserver (along with the test which run server+client). Thoughts? |
|
We can also drop the incomplete imapmemserver support and enable the client-server NOTIFY tests only for dovecot. I see the I'll wait for feedback before advancing this this in any way. |
|
v3: added test for how disconnections are handled (both with and without NOTIFY). Fix |
06a1f88 to
2a6eb8e
Compare
|
I've re-written the client test to run only with dovecot, which has real NOTIFY support. Those test will be skipped with the imapmemserver, but are a lot more meaningful than before. I've ripped out the scaffold for the imapmemserver's NOTIFY support. Such broken support was useless beyond these tests. |
emersion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the patch! Here are a few comments. I've only read the client part for now, not the tests nor server.
imapclient/notify.go
Outdated
| // Validate the item before encoding | ||
| if item.MailboxSpec == "" && len(item.Mailboxes) == 0 { | ||
| // Skip invalid items - this shouldn't happen with properly constructed NotifyOptions | ||
| continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably return an error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find any other method which uses an encoder and returns an error.
Is it fine to leave the command half-written in this case? (this indicates a programmer error anyway).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some commands we panic when fed an invalid state (e.g. see GetMetadata). For some commands we validate upfront and we return a command struct already completed with an error (e.g. see Enable). Latter is more complicated and better suited for cases where the failure doesn't indicate a programmer error (Enable is probably a bad usage for this).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you prefer for this to panic or return an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to not return an error so that callers can chain .Wait() like other short-running commands. I don't have strong opinions about returning a command struct already completed with an error vs. a panic. Simplest would probably be to panic. (We can change it later if it turns out to be an annoyance.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this comment has been addressed?
|
I missed the review above. Using UnilateralDataHandler is much cleaner! |
91cebce to
f59d635
Compare
|
All comments addressed; ready for a second round of review. |
| Session | ||
|
|
||
| // Authenticated state | ||
| Notify(w *UpdateWriter, options *imap.NotifyOptions) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about the implications of passing an UpdateWriter to the backend here. Up until now backends would only use UpdateWriter synchronously in handlers. Here the backend would need to use it after Notify returns. We have conn.encMutex to avoid stepping on our own toes when sending an update, but that still leaves considerations such as sequence number issues with EXPUNGE notifications while another command is running, see section 5.5.
Maybe there is a way to expose a safer interface to backends. Maybe we should just leave it up to the backend and document the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you prefer that I drop the imapserver support from this PR and focus on the client part first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I only wrote some of the server portions to test the client, but eventually moved on to just testing with dovecot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving the server parts to a separate PR would allow the client part to be merged first, which sounds like a good idea.
Extracted from emersion#718
984125a to
c2f95df
Compare
Client–server tests run only with dovecot; the imapmemserver doesn't support NOTIFY.
c2f95df to
58ef58f
Compare
| "github.com/emersion/go-imap/v2/internal/imapwire" | ||
| ) | ||
|
|
||
| func encodeToString(options *imap.NotifyOptions) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using table-driven tests should allow us to cut down the boiler plate here I believe: https://go.dev/wiki/TableDrivenTests
| if err != nil { | ||
| t.Fatalf("encodeToString() error = %v", err) | ||
| } | ||
| expected := " SET ((INBOX \"Foo Bar\" \"Test&-Mailbox\") (MessageNew))\r\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backtick strings can improve readability here
| ) | ||
|
|
||
| func TestClient_Notify(t *testing.T) { | ||
| if os.Getenv("GOIMAP_TEST_DOVECOT") != "1" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check whether the extension is supported instead of hardcoding "Dovecot == supported"? See e.g. TestACL.
| t.Fatalf("Notify() = %v", err) | ||
| } | ||
|
|
||
| if cmd == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we wait for the command to complete in all tests?
| conn, server := newDovecotClientServerPair(t) | ||
| defer server.Close() | ||
|
|
||
| options := &imapclient.Options{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, do we need to pass custom UnilateralDataHandler to the client, but newClientServerPair doesn't support this?
This patch series implements support for the NOTIFY extension.
The first commit implements support in imapclient, the main focus of my work. I've been using this on a client to monitor changes with success.
The second commit implements support in imapserver.
The third commit adds a minimal scaffold implementation in imapmemserver. This implementation simply rejects any request (which it technically allows as per the spec). It's not an actually useful implementation, and is mostly there so we can have minimal client-server unit tests. Implementing proper NOTIFY support for imapmemserver likely requires substantial changes which fall beyond the scope of this series.
This is likely easiest to review on a per-commit basis, and about half of the LoC are just unit tests for encoding/decoding.