imap

    MoonBit IMAP structured UID core with bounded, read-only Maildir archival host

    Download zip
    Version
    0.7.0
    License
    MIT
    Last updated
    5 hours ago
    Downloads
    3

    #可执行 API 示例

    收紧 FETCH sequence-set 语法,支持范围、星号、逗号和 32 位边界。这些例子调用公开 API,并随 moon test 执行。

    ///|
    test "sequence sets reject invalid separators and zero" {
    for s in ["1", "1:3", "3:1", "*", "1:*,7"] {
    assert_true(@imap.valid_sequence_set(s))
    }
    for s in ["0", "01", "1:", ":2", "1,,2", "1:2:3", "4294967296"] {
    assert_true(!@imap.valid_sequence_set(s))
    }
    }

    历史0.4 已提供 TCP/隐式 TLS/STARTTLS、APPEND/IDLE/PLAIN 和 Dovecot/GreenMail 互通。核心升级状态用法见 starttls_test.mbt;仍缺完整 FETCH 语义、更多 SASL 和其它扩展。

    0.5 新增的是Node readOnlySnapshot 编排(tools/snapshot.mjs),不是MoonBit原生网络API。当前独立服务器证据与限制见CAPABILITY-MATRIX.md;旧Dovecot记录未在本轮重跑。

    ImapError

    pub suberror ImapError {
    Invalid(String)
    } derive(
    Debug
    )

    CompletedResponse

    pub struct CompletedResponse {
    // private fields
    }

    Only construction after one matching final tagged OK is allowed. This does not claim every untagged event belongs to the command: unsolicited events remain in wire order and FETCH projections expose their optional UID.

    CompletedResponse::fetch

    FETCH data, including unsolicited flag-only updates. Unknown attributes are retained with structural values. BODY[] alone is the full-body profile; sections, partials and BODYSTRUCTURE stay unknown rather than being guessed.

    CompletedResponse::selection

    fn CompletedResponse::selection(self : CompletedResponse, read_only? : Bool) -> SelectionInfo raise ImapError

    UIDNEXT is an optional server prediction, never a message count/last UID. EXAMINE callers require ReadOnly. Session already deselects on any new SELECT/EXAMINE attempt; this projection never restores a failed old selection.
    fn CompletedResponse::uid_search(self : CompletedResponse) -> UidSet raise ImapError

    Serial UID SEARCH result. Classic SEARCH is rev1 compatibility. ESEARCH requires UID and, when present, this command's TAG; COUNT/ALL must agree. MIN/MAX are checked if supplied; unknown extensions are explicitly rejected by this narrow profile, while Response::parse still preserves their AST.

    Decoder

    pub struct Decoder {
    // private fields
    } derive(
    Debug
    )

    Incremental byte parser with bounded per-response storage; literals never pass through UTF-8.

    Decoder::feed

    fn Decoder::feed(self : Decoder, bytes : Bytes) -> Array[Response] raise ImapError

    Decoder::finish

    fn Decoder::finish(self : Decoder) -> Unit raise ImapError

    Decoder::has_partial

    fn Decoder::has_partial(self : Decoder) -> Bool

    Decoder::new

    fn Decoder::new() -> Decoder

    Decoder::to_repr

    FetchBody

    pub(all) enum FetchBody {
    Missing
    NilBody
    Body(Bytes)
    } derive(Eq,
    Debug
    )

    FetchBody::equal

    fn FetchBody::equal(FetchBody, FetchBody) -> Bool

    FetchBody::not_equal

    fn FetchBody::not_equal(x : FetchBody, y : FetchBody) -> Bool

    FetchInfo

    pub struct FetchInfo {
    sequence : Int64
    uid : Int64?
    size : Int64?
    flags : Array[String]?
    body : FetchBody
    unknown : Array[(String, ImapValue)]
    } derive(Eq,
    Debug
    )

    FetchInfo::equal

    fn FetchInfo::equal(FetchInfo, FetchInfo) -> Bool

    FetchInfo::not_equal

    fn FetchInfo::not_equal(x : FetchInfo, y : FetchInfo) -> Bool

    ImapValue

    pub(all) enum ImapValue {
    Atom(String)
    Quoted(String)
    Nil
    Literal(Bytes)
    List(Array[ImapValue])
    } derive(Eq,
    Debug
    )

    Structural values preserve order and bind each literal to its syntax position. Numeric atoms remain text until a profile checks its exact range. The original Response remains available for lexical spelling and diagnostic text.

    ImapValue::equal

    fn ImapValue::equal(ImapValue, ImapValue) -> Bool

    ImapValue::not_equal

    fn ImapValue::not_equal(x : ImapValue, y : ImapValue) -> Bool

    MailboxAccess

    pub(all) enum MailboxAccess {
    ReadOnly
    ReadWrite
    } derive(Eq,
    Debug
    )

    MailboxAccess::equal

    MailboxAccess::not_equal

    fn MailboxAccess::not_equal(x : MailboxAccess, y : MailboxAccess) -> Bool

    Response

    pub(all) struct Response {
    line : String
    literals : Array[Bytes]
    } derive(Eq,
    Debug
    )

    Response::equal

    fn Response::equal(Response, Response) -> Bool

    Response::not_equal

    fn Response::not_equal(x : Response, y : Response) -> Bool

    Response::parse

    fn Response::parse(self : Response) -> TypedResponse raise ImapError

    Parse already framed response syntax with exact literal association. Limits: 64 KiB syntax, 64 literals/2 MiB total, depth 32, 8192 structural values. Unknown extensions remain structural data; this is not full extension semantics.

    Response::to_repr

    SelectionInfo

    pub struct SelectionInfo {
    uid_validity : Int64
    uid_next : Int64?
    exists : Int64?
    flags : Array[String]
    permanent_flags : Array[String]
    access : MailboxAccess
    } derive(Eq,
    Debug
    )

    SelectionInfo::equal

    SelectionInfo::identity

    fn SelectionInfo::identity(self : SelectionInfo, mailbox : String, uid : Int64) -> UidIdentity raise ImapError

    SelectionInfo::not_equal

    fn SelectionInfo::not_equal(x : SelectionInfo, y : SelectionInfo) -> Bool

    Session

    pub struct Session {
    // private fields
    } derive(
    Debug
    )

    Session::append

    fn Session::append(self : Session, mailbox : String, size : Int, flags? : String) -> String raise ImapError

    Session::authenticate_response

    fn Session::authenticate_response(self : Session, base64 : String) -> String raise ImapError

    A pre-encoded PLAIN response or '*' to cancel. Credentials are encoded by the host.

    Session::capabilities

    fn Session::capabilities(self : Session) -> Array[String]

    Session::command

    fn Session::command(self : Session, command : String, args : Array[String]) -> String raise ImapError

    Session::done

    fn Session::done(self : Session) -> String raise ImapError

    Session::feed

    fn Session::feed(self : Session, bytes : Bytes) -> Array[Response] raise ImapError

    Session::finish

    fn Session::finish(self : Session) -> Unit raise ImapError

    Session::has_pending

    fn Session::has_pending(self : Session) -> Bool

    Session::literal

    fn Session::literal(self : Session, bytes : Bytes) -> Bytes raise ImapError

    Send a synchronizing APPEND literal only after the server's continuation.

    Session::new

    fn Session::new() -> Session

    Session::state

    fn Session::state(self : Session) -> State

    Session::tls_established

    fn Session::tls_established(self : Session) -> Unit raise ImapError

    The transport must verify the certificate and hostname before calling this method.

    Session::to_repr

    State

    pub(all) enum State {
    Greeting
    NotAuthenticated
    Authenticated
    Selected
    Closed
    } derive(Eq,
    Debug
    )

    State::equal

    fn State::equal(State, State) -> Bool

    State::not_equal

    fn State::not_equal(x : State, y : State) -> Bool

    State::to_repr

    TypedResponse

    pub(all) enum TypedResponse {
    Status(String, String, Array[ImapValue], String)
    Data(Array[ImapValue])
    Continuation(String)
    } derive(Eq,
    Debug
    )

    TypedResponse::equal

    TypedResponse::not_equal

    fn TypedResponse::not_equal(x : TypedResponse, y : TypedResponse) -> Bool

    UidIdentity

    pub struct UidIdentity {
    mailbox : String
    uid_validity : Int64
    uid : Int64
    } derive(Eq,
    Debug
    )

    UidIdentity::equal

    fn UidIdentity::equal(UidIdentity, UidIdentity) -> Bool

    UidIdentity::not_equal

    fn UidIdentity::not_equal(x : UidIdentity, y : UidIdentity) -> Bool

    UidRange

    pub struct UidRange {
    first : Int64
    last : Int64
    } derive(Eq,
    Debug
    )

    UidRange::equal

    fn UidRange::equal(UidRange, UidRange) -> Bool

    UidRange::not_equal

    fn UidRange::not_equal(x : UidRange, y : UidRange) -> Bool

    UidRange::to_repr

    UidSet

    pub struct UidSet {
    count : Int64
    // private fields
    } derive(Eq,
    Debug
    )

    Sorted non-overlapping inclusive ranges; no unbounded UID expansion.

    UidSet::equal

    fn UidSet::equal(UidSet, UidSet) -> Bool

    UidSet::expand

    fn UidSet::expand(self : UidSet, limit~ : Int) -> Array[Int64] raise ImapError

    UidSet::not_equal

    fn UidSet::not_equal(x : UidSet, y : UidSet) -> Bool

    UidSet::ranges

    fn UidSet::ranges(self : UidSet) -> Array[UidRange]

    UidSet::to_repr

    completed_response

    fn completed_response(tag : String, responses : Array[Response]) -> CompletedResponse raise ImapError

    Complete an already collected serial-command response (including completion). NO/BAD, mismatched tags, BYE, continuation, trailing data and oversized collections fail before exposing a high-level result. No partial success.

    decode_mailbox

    fn decode_mailbox(encoded : String) -> String raise ImapError

    encode_mailbox

    fn encode_mailbox(name : String) -> String raise ImapError

    RFC 3501 modified UTF-7, for mailbox names before UTF8=ACCEPT negotiation.

    valid_sequence_set

    fn valid_sequence_set(s : String) -> Bool

    Validate RFC 3501 sequence-set syntax without interpreting mailbox contents.