feat: initial version

Signed-off-by: Sebastian Krupinski <root@LAPTOP-7DVOR6NC>
This commit is contained in:
Sebastian Krupinski
2026-02-20 16:41:19 -05:00
commit 7f562d6aba
139 changed files with 11256 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP BODY <string> search criteria.
*/
final readonly class BodyCriteria implements Criteria
{
public function __construct(private string $value) {}
public function __toString(): string
{
return 'BODY "' . str_replace(['"', '\\'], ['\\"', '\\\\'], $this->value) . '"';
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
use Gricob\IMAP\Protocol\Command\Argument\QuotedString;
use Gricob\IMAP\Protocol\Command\Argument\SequenceSet;
/**
* Raw UID COPY command.
*
* gricob does not expose message copying; this thin wrapper fills the gap.
* Accepts a set of UIDs formatted as a comma-separated sequence set.
*
* Example: UID COPY 1,3,7 "INBOX.Archive"
*/
final readonly class CopyCommand extends Command
{
/**
* @param int[] $uids Source message UIDs
* @param string $destination Target mailbox name
*/
public function __construct(array $uids, string $destination)
{
parent::__construct(
'UID COPY',
new SequenceSet(...$uids),
new QuotedString($destination),
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
use Gricob\IMAP\Protocol\Command\Argument\QuotedString;
/**
* Raw IMAP DELETE command for a mailbox.
*
* gricob does not expose mailbox deletion; this thin wrapper fills the gap.
*
* Example: DELETE "INBOX.Trash"
*/
final readonly class DeleteCommand extends Command
{
public function __construct(string $mailbox)
{
parent::__construct('DELETE', new QuotedString($mailbox));
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP FLAGGED search criteria.
*/
final readonly class FlaggedCriteria implements Criteria
{
public function __toString(): string
{
return 'FLAGGED';
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP FROM <string> search criteria.
*/
final readonly class FromCriteria implements Criteria
{
public function __construct(private string $value) {}
public function __toString(): string
{
return 'FROM "' . str_replace(['"', '\\'], ['\\"', '\\\\'], $this->value) . '"';
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP LARGER <n> search criteria (messages larger than n octets).
*/
final readonly class LargerCriteria implements Criteria
{
public function __construct(private int $size) {}
public function __toString(): string
{
return 'LARGER ' . $this->size;
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
use Gricob\IMAP\Protocol\Command\Argument\QuotedString;
/**
* Raw IMAP RENAME command.
*
* gricob does not expose mailbox renaming; this thin wrapper fills the gap.
*
* Example: RENAME "OldName" "NewName"
*/
final readonly class RenameCommand extends Command
{
public function __construct(string $oldName, string $newName)
{
parent::__construct('RENAME', new QuotedString($oldName), new QuotedString($newName));
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP SEEN search criteria.
*/
final readonly class SeenCriteria implements Criteria
{
public function __toString(): string
{
return 'SEEN';
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP SMALLER <n> search criteria (messages smaller than n octets).
*/
final readonly class SmallerCriteria implements Criteria
{
public function __construct(private int $size) {}
public function __toString(): string
{
return 'SMALLER ' . $this->size;
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
/**
* STARTTLS command (RFC 3501 §6.2.1).
*
* Instructs the server to begin TLS negotiation on the current connection.
* After the server responds OK, the client must call upgradeTls() on the
* underlying SocketConnection to complete the handshake.
*/
final readonly class StartTlsCommand extends Command
{
public function __construct()
{
parent::__construct('STARTTLS');
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
use Gricob\IMAP\Protocol\Command\Argument\SequenceSet;
use Gricob\IMAP\Protocol\Command\Argument\Store\Flags;
/**
* Bulk UID STORE command for flag mutations.
*
* A thin ergonomic wrapper around gricob's FetchCommand that accepts an array
* of UIDs and a pre-built Flags argument so callers don't have to construct
* SequenceSet directly.
*
* Example: UID STORE 1,3,7 +FLAGS.SILENT (\Seen)
*/
final readonly class StoreCommand extends Command
{
/**
* @param int[] $uids UIDs to operate on
* @param Flags $flags e.g. new Flags(['\Seen'], '+')
*/
public function __construct(array $uids, Flags $flags)
{
parent::__construct(
'UID STORE',
new SequenceSet(...$uids),
$flags,
);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Command;
use Gricob\IMAP\Protocol\Command\Argument\ParenthesizedList;
use Gricob\IMAP\Protocol\Command\Argument\SequenceSet;
/**
* Streaming single-message fetch command.
*
* Wraps gricob's UID FETCH for one or more UIDs with a configurable item list.
* Used inside ImapClientWrapper::streamMessages() (one UID per call) and
* ImapClientWrapper::fetchMessages() (variadic UIDs for bulk prefetch).
*
* Example: UID FETCH 42 (FLAGS ENVELOPE INTERNALDATE BODYSTRUCTURE BODY[])
*/
final readonly class StreamFetchCommand extends Command
{
/**
* @param int[] $uids One or more UIDs; formatted as "1,3,7" by SequenceSet
* @param string[] $items IMAP fetch data items (e.g. 'FLAGS', 'ENVELOPE', 'BODY[]')
*/
public function __construct(array $uids, array $items)
{
parent::__construct(
'UID FETCH',
new SequenceSet(...$uids),
new ParenthesizedList($items),
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP SUBJECT <string> search criteria.
*/
final readonly class SubjectCriteria implements Criteria
{
public function __construct(private string $value) {}
public function __toString(): string
{
return 'SUBJECT "' . str_replace(['"', '\\'], ['\\"', '\\\\'], $this->value) . '"';
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP TO <string> search criteria.
*/
final readonly class ToCriteria implements Criteria
{
public function __construct(private string $value) {}
public function __toString(): string
{
return 'TO "' . str_replace(['"', '\\'], ['\\"', '\\\\'], $this->value) . '"';
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP UNFLAGGED search criteria.
*/
final readonly class UnflaggedCriteria implements Criteria
{
public function __toString(): string
{
return 'UNFLAGGED';
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXM\ProviderImapMail\Service\Remote\Command;
use Gricob\IMAP\Protocol\Command\Argument\Search\Criteria;
/**
* IMAP UNSEEN search criteria.
*
* gricob does not include an UNSEEN criteria; this thin class fills the gap.
* Used with SearchCommand to count unread messages via SEARCH UNSEEN.
*/
final readonly class UnseenCriteria implements Criteria
{
public function __toString(): string
{
return 'UNSEEN';
}
}