-
-
Notifications
You must be signed in to change notification settings - Fork 25
[2.x] Add incremental mailbox synchronization #185
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
Open
stevebauman
wants to merge
39
commits into
v2.0
Choose a base branch
from
feature/v2-incremental-sync
base: v2.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 19 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
4364fad
Add incremental mailbox synchronization and extensible fetched data
stevebauman 61c0b43
Support incremental synchronization through fetch modifiers
stevebauman 95a67d8
Remove redundant connection FETCH shortcuts
stevebauman e5a2034
Unify IMAP operation identifiers and STORE modifiers
stevebauman b9f3499
Align connection parameters with IMAP command syntax
stevebauman 67b5640
Send SORT charsets unquoted for server compatibility
stevebauman b9a7101
Adjust formatting
stevebauman 4fd92fb
Spacing
stevebauman ba8ce90
Address incremental synchronization review feedback
stevebauman a4c13d0
Adjust param order
stevebauman ab63e17
Rename sequence set parser to fromSequenceSet
stevebauman 0844b7b
Harden synchronization results and mailbox reconnects
stevebauman b5dda15
Extract SASL authentication exchange
stevebauman 68e6ab6
Add fake logger assertions
stevebauman 007487b
Refine capability and selection APIs
stevebauman cea1083
Fix code style
stevebauman cba52f6
Make mailbox capabilities immutable snapshots
stevebauman 9494048
Handle selection capability and state edge cases
stevebauman fdafa28
Improve tokenizer method and enablement interface naming
stevebauman 81822a9
Handle authentication replies within the exchange
stevebauman 5889554
Sort cases
stevebauman 8dcaf34
Adjust comments and spacing
stevebauman 7fc93bf
Construct fake mailboxes through a typed factory
stevebauman 58a3e0e
Validate IMAP arguments and centralize mailbox reset
stevebauman d396273
Refine FETCH and STORE response handling
stevebauman 3fc9e70
Fix code style
stevebauman 3c3e379
Send atom-safe IMAP charsets without quotes
stevebauman 2636658
Merge branch 'feature/v2-incremental-sync' of https://github.com/Dire…
stevebauman d5bbe4a
Sort match asc
stevebauman c4e730d
Potential fix for pull request finding 'Require 64-bit PHP for 63-bit…
stevebauman 2e34fd6
Potential fix for pull request finding 'Normalize case-insensitive VA…
stevebauman cf94985
Simplify fetched response handling
stevebauman 1c03e60
Fix code style
stevebauman 35ea32d
Adjust bool prop name
stevebauman d8cf4b4
Validate IMAP command tokens
stevebauman 91d49c3
Potential fix for pull request finding 'Validate LIST selection optio…
stevebauman 25b493b
Mirror selection capability checks in fake mailbox
stevebauman 9702316
Track examined folders in fake mailbox
stevebauman 089f1dd
Preserve fetched message identifiers
stevebauman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| <?php | ||
|
|
||
| namespace DirectoryTree\ImapEngine; | ||
|
|
||
| use DirectoryTree\ImapEngine\Connection\ConnectionInterface; | ||
| use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse; | ||
| use Throwable; | ||
|
|
||
| class Authentication | ||
| { | ||
| /** | ||
| * Constructor. | ||
| */ | ||
| public function __construct( | ||
| protected ConnectionInterface $connection, | ||
| protected AuthenticatorInterface $authenticator, | ||
| ) {} | ||
|
|
||
| /** | ||
| * Authenticate the connection. | ||
| */ | ||
| public function authenticate(bool $initial = false): TaggedResponse | ||
| { | ||
| $response = $this->authenticator->initial(); | ||
| $sent = $initial && $response !== null; | ||
|
|
||
| $exchange = $this->connection->authenticate( | ||
| $this->authenticator->mechanism(), | ||
| $sent ? $response : null, | ||
| ); | ||
|
|
||
| foreach ($exchange as $challenge) { | ||
| try { | ||
| if (! $sent && $response !== null) { | ||
| $answer = $response; | ||
| $sent = true; | ||
| } else { | ||
| $answer = $this->authenticator->respond($challenge); | ||
| } | ||
| } catch (Throwable $e) { | ||
| $this->connection->disconnect(); | ||
|
|
||
| throw $e; | ||
| } | ||
|
|
||
| $this->connection->respond($answer); | ||
| } | ||
|
|
||
| return $exchange->getReturn(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| <?php | ||
|
|
||
| namespace DirectoryTree\ImapEngine\Authentication; | ||
|
|
||
| use DirectoryTree\ImapEngine\AuthenticatorInterface; | ||
|
|
||
| class XOAuth2 implements AuthenticatorInterface | ||
| { | ||
| /** | ||
| * Constructor. | ||
| */ | ||
| public function __construct( | ||
| protected string $user, | ||
| protected string $token, | ||
| ) {} | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| public function mechanism(): string | ||
| { | ||
| return 'XOAUTH2'; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| public function initial(): string | ||
| { | ||
| return "user=$this->user\1auth=Bearer $this->token\1\1"; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| public function respond(string $challenge): string | ||
| { | ||
| return ''; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?php | ||
|
|
||
| namespace DirectoryTree\ImapEngine; | ||
|
|
||
| interface AuthenticatorInterface | ||
| { | ||
| /** | ||
| * Get the SASL mechanism name. | ||
| */ | ||
| public function mechanism(): string; | ||
|
|
||
| /** | ||
| * Get the unencoded initial data, or null to await a challenge. | ||
| */ | ||
| public function initial(): ?string; | ||
|
|
||
| /** | ||
| * Respond to a decoded challenge. Return null to cancel authentication. | ||
| */ | ||
| public function respond(string $challenge): ?string; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| <?php | ||
|
|
||
| namespace DirectoryTree\ImapEngine; | ||
|
|
||
| readonly class Capabilities | ||
| { | ||
| /** | ||
| * The mailbox capabilities. | ||
| * | ||
| * @var array<string, Capability> | ||
| */ | ||
| protected array $items; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * | ||
| * @param array<string, Capability> $items | ||
| */ | ||
| protected function __construct(array $items) | ||
| { | ||
| $this->items = $items; | ||
| } | ||
|
|
||
| /** | ||
| * Create a capability collection from the given items. | ||
| */ | ||
| public static function from(Capability ...$capabilities): static | ||
| { | ||
| $items = []; | ||
|
|
||
| foreach ($capabilities as $capability) { | ||
| $items[$capability->name()] = $capability; | ||
| } | ||
|
|
||
| return new static($items); | ||
| } | ||
|
|
||
| /** | ||
| * Get the capability items. | ||
| * | ||
| * @return array<string, Capability> | ||
| */ | ||
| public function items(): array | ||
| { | ||
| return $this->items; | ||
| } | ||
|
|
||
| /** | ||
| * Get all supported capabilities. | ||
| */ | ||
| public function all(): array | ||
| { | ||
| return array_keys($this->items); | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the exact capability exists. | ||
| */ | ||
| public function has(string $capability): bool | ||
| { | ||
| return isset($this->items[strtoupper($capability)]); | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the capability is supported. | ||
| */ | ||
| public function supports(string $capability): bool | ||
| { | ||
| return (bool) $this->find($capability); | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the capability is enabled. | ||
| */ | ||
| public function enabled(string $capability): bool | ||
| { | ||
| return ($this->items[strtoupper($capability)] ?? null)?->enabled() ?? false; | ||
| } | ||
|
|
||
| /** | ||
| * Find a supported capability. | ||
| */ | ||
| protected function find(string $capability): ?Capability | ||
| { | ||
| foreach ($this->items as $item) { | ||
| if ($item->matches($capability)) { | ||
| return $item; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
|
|
||
| namespace DirectoryTree\ImapEngine; | ||
|
|
||
| readonly class Capability | ||
| { | ||
| /** | ||
| * Constructor. | ||
| */ | ||
| protected function __construct( | ||
| protected string $name, | ||
| protected bool $enabled = false, | ||
| ) {} | ||
|
|
||
| /** | ||
| * Make a new capability instance. | ||
| */ | ||
| public static function make(string $name, bool $enabled = false): static | ||
| { | ||
| return new static(strtoupper($name), $enabled); | ||
| } | ||
|
|
||
| /** | ||
| * Get the capability name. | ||
| */ | ||
| public function name(): string | ||
| { | ||
| return $this->name; | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the capability matches the given name. | ||
| */ | ||
| public function matches(string $capability): bool | ||
| { | ||
| $capability = strtoupper($capability); | ||
|
|
||
| return $this->name === $capability | ||
| || str_starts_with($this->name, "{$capability}="); | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the capability is enabled. | ||
| */ | ||
| public function enabled(): bool | ||
| { | ||
| return $this->enabled; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.