libetpan

repository·master·Indexed 20 days ago

https://github.com/dinhvh/libetpan

A portable C library providing a framework for mail access protocols including IMAP, SMTP, POP, and NNTP, along with MIME parsing and support for storage formats such as Maildir, mbox, and MH. It offers both low-level functions for specific mail access and high-level driver-based APIs for a consistent interface.

Tokens
12.2K
Snippets
24
Records
49
Agent score
70%

What's inside libetpan

  1. Overview of LibEtPan features

    master

    LibEtPan is a portable and efficient C library designed for various mail access protocols and message handling. It supports the following protocols and formats:

    • Protocols: IMAP, SMTP, POP, and NNTP.
    • Message Handling: RFC822/MIME message building and parsing.
    • Storage Formats: Maildir, mbox, and MH.
  2. RFC 2231 MIME Parameter Support Plan

    master

    This document outlines the plan to add support for RFC 2231 MIME parameters to libetpan. The goal is to enable the decoding of extended single parameters (e.g., filename*=utf-8''%E2%82%AC.txt) and the joining of continued parameters (e.g., filename*0="long"; filename*1=".txt") while maintaining the existing public ABI.

    Key objectives include:

    • Decoding extended single parameters.
    • Joining and decoding extended continued parameters.
    • Applying decoded results to existing helper surfaces like mailmime_content_param_get(), mailmime_single_fields_init(), and Content-Disposition filename extraction.
    • Keeping struct mailmime_parameter unchanged to preserve ABI compatibility.
  3. How RFC 2231 parameter normalization works

    master

    To support RFC 2231 without breaking the public API, libetpan implements an internal normalization process.

    1. Parsing: The raw parser treats RFC 2231 names (like name*0*) as generic attributes.
    2. Normalization: An internal helper (proposed as mailmime_parameters_rfc2231_normalize) scans the parsed parameter list. It identifies fragments such as:
      • name* (single extended parameter)
      • name*0, name*1 (continuations)
      • name*0*, name*1* (encoded continuations)
    3. Decoding:
      • Percent-decodes valid %HH triples.
      • Converts from the declared charset to UTF-8 using existing libetpan charset conversion helpers.
      • Falls back to percent-decoded bytes if charset conversion fails.
    4. Joining: For continuations, segment 0 is required. Segments are appended in numeric order. If segment 0 is extended, the charset/language is parsed from it.
    5. Replacement: The normalized, single struct mailmime_parameter replaces the fragments in the public list, so callers using mailmime_content_param_get() see the final decoded value (e.g., filename) instead of the fragments.
  4. Understand the ActiveSync WBXML Encoding/Decoding Plan

    master

    The ActiveSync WBXML codec is a specialized, low-level implementation designed to handle the WAP Binary XML (WBXML) format used by the ActiveSync protocol. It is decoupled from HTTP and specific ActiveSync commands to allow for independent testing via byte fixtures.

    Key characteristics:

    • Scope: Primarily supports mail-oriented commands (FolderSync, Sync, ItemOperations, SendMail, MoveItems, Ping).
    • Protocol Support: Focuses on a subset of WBXML, specifically handling code page switching, inline strings (STR_I), and opaque values (OPAQUE).
    • Constraints: It does not support arbitrary XML features like DTDs, processing instructions, or attributes, as these are not required by the ActiveSync command scope. It prioritizes structural correctness (nesting, root elements, code page validity) over general XML compliance.
  5. Understand the ActiveSync HTTP Transport Model

    master

    The ActiveSync implementation uses a transport abstraction to decouple the command logic from the HTTP backend (libcurl). This allows for easy testing via fake transports.

    Core Data Structures:

    • struct mailactivesync_http_header: Represents a single HTTP header with name and value.
    • struct mailactivesync_http_request: Contains the method, url, headers (a clist of headers), body, body_len, and timeout.
    • struct mailactivesync_http_response: Contains the status_code, headers, body, and body_len.
    • struct mailactivesync_http_transport: An interface containing a context pointer and function pointers for perform (to execute the request) and free (to clean up the transport).
    struct mailactivesync_http_header {
      char * name;
      char * value;
    };
    
    struct mailactivesync_http_request {
      char * method;
      char * url;
      clist * headers; /* struct mailactivesync_http_header * */
      unsigned char * body;
      size_t body_len;
      time_t timeout;
    };
    
    struct mailactivesync_http_response {
      int status_code;
      clist * headers; /* struct mailactivesync_http_header * */
      unsigned char * body;
      size_t body_len;
    };
    
    struct mailactivesync_http_transport {
      void * context;
      int (*perform)(struct mailactivesync_http_transport * transport,
          struct mailactivesync_http_request * request,
          struct mailactivesync_http_response ** response);
      void (*free)(struct mailactivesync_http_transport * transport);
    };
  6. Authenticate ActiveSync sessions via OAuth2 or Basic Auth

    master

    LibEtPan supports two primary authentication methods for ActiveSync:

    1. OAuth2 (Recommended): Use mailactivesync_login_oauth2() to provide an already acquired access token. The library consumes the token but does not handle the OAuth device code, refresh token, or browser-based flows. This is the required method for Outlook.com and Exchange Online.
    2. Basic Authentication: Use mailactivesync_login() for private or on-premises Exchange servers. Note that if Outlook.com or Exchange Online returns an HTTP 401, the library will surface MAILACTIVESYNC_ERROR_UNAUTHORIZED to indicate that OAuth2 is required.

    Security Note: The library is designed to redact Authorization headers (both Bearer and Basic) to prevent credentials from being leaked in logs.

  7. ActiveSync HTTP Command Implementation Phases

    master

    The ActiveSync HTTP implementation in libEtPan follows a phased approach to build up from core transport to high-level mail operations.

    Core Transport & Setup

    • Phase 1 & 2: Establishes the HTTP request/response core using libcurl. This provides HTTPS, redirects, proxies, TLS verification, and timeouts. If built without HAVE_CURL, the library returns MAILACTIVESYNC_ERROR_HTTP_UNAVAILABLE.
    • Phase 3: Implements mailactivesync_options() to negotiate protocol versions (defaulting to 16.1) and commands.
    • Phase 3.5: Handles Provisioning Policy. If a server requires it, mailactivesync_provision() is used to store a policy key, which is then sent via the X-MS-PolicyKey header in subsequent requests.

    Mail Operations

    • Phase 4 (FolderSync): Uses a sync key to fetch folder changes (adds, updates, deletes) into a mailactivesync_folder_sync_result.
    • Phase 5 (Sync): Performs the main synchronization of collections (e.g., Inbox) using a SyncKey. It parses message changes, read/flag states, and MIME bodies into a mailactivesync_sync_result.
    • Phase 6 (ItemOperations Fetch): Retrieves specific items using CollectionId and ServerId.
    • Phase 7 (SendMail): Sends mail via POST, supporting ClientId and SaveInSentItems.
    • Phase 8 (MoveItems): Moves items between collections.
    • Phase 9 (SmartReply/SmartForward): Specialized reply/forward operations.
    • Phase 10 (Ping): A heartbeat mechanism with longer timeouts to detect changes in watched collections.
  8. Understand the Plain Text Rendering Pipeline

    master

    The plain text rendering in this project follows a specific pipeline to ensure consistency with HTML-first rendering strategies. Instead of a direct MIME-to-text conversion, the process follows these steps:

    1. MIME Parsing: The message is parsed into a MIME/message tree using mailmime_parse().
    2. HTML Rendering: The full message is rendered as HTML using the default message HTML renderer.
      • text/plain parts are converted into escaped HTML with line breaks (<br/>) and quote block handling (<blockquote type="cite">).
      • text/html parts are cleaned and inserted as HTML.
      • Part Selection Logic:
        • For multipart/alternative: The last part containing text/html is chosen; if none, the last part containing text/plain; otherwise, the first child.
        • For multipart/related: The first child is the root body; remaining children are related attachments.
        • For multipart/mixed: Children are rendered in order.
        • For message/rfc822: The nested body is wrapped with an embedded-message header template.
    3. HTML Flattening: The final HTML document is flattened into plain text (using logic similar to String::flattenHTML()).
    4. Normalization: Lines beginning with Date: are normalized by replacing " at " with " " to ensure deterministic comparison.
  9. Authenticate with Outlook.com using OAuth2

    master

    Since Microsoft has deprecated Basic authentication for Outlook.com and Exchange Online, you should use OAuth2. The recommended flow is to acquire an OAuth2 access token outside of libEtPan and then pass it to the library using mailactivesync_login_oauth2().

    Steps for Outlook.com:

    1. Register a test application in Microsoft Entra ID.
    2. Configure it as a public/native client with a local development redirect URI.
    3. Request delegated mail scopes (e.g., for mail access and sending).
    4. Obtain the OAuth2 access token externally.
    5. Use mailactivesync_login_oauth2() with the token.
    6. Connect to https://outlook.office365.com/Microsoft-Server-ActiveSync.
    7. Perform OPTIONS to select protocol version 16.1, then run FolderSync and Sync commands.
    r = mailactivesync_login_oauth2(as, "user@outlook.com", access_token);
  10. Test the RFC 2231 implementation

    master

    To verify RFC 2231 support, run the following commands to execute the unit tests and the mime-parser end-to-end tests:

    cd unittest/mime && make && ./mime
    cd ../mime-parser && make && ./mime-parser

    If your build setup allows, you can also run the broader test suite using:

    make check
    cd unittest/mime && make && ./mime
    cd ../mime-parser && make && ./mime-parser
    
    # Or broader tests
    make check
  11. ActiveSync Logging and Diagnostics Best Practices

    master

    When debugging ActiveSync implementations, use the provided logging hooks.

    Safe to log:

    • Request method, path, and query (ensure bearer tokens are stripped).
    • HTTP status codes.
    • ActiveSync command status.
    • Selected protocol version.
    • Sync keys (only if explicitly enabled, as they represent account state).

    DO NOT log:

    • OAuth tokens.
    • Basic authentication headers.
    • Raw MIME bodies.
    • WBXML bodies (by default).
  12. Run Plain Text Rendering Tests

    master

    To verify the plain text rendering implementation, you can run the focused test suite or the full project check suite using make.

    Run focused tests

    1. Build the specific test binary:
    make -C unittest plaintext-rendering/plaintext_rendering_test
    1. Execute the test:
    cd unittest/plaintext-rendering
    ./plaintext_rendering_test

    Run full test suite

    To run all tests in the project:

    make -C unittest check

    Clean up

    To remove generated test binaries:

    make -C unittest clean
    # Build the focused test
    make -C unittest plaintext-rendering/plaintext_rendering_test
    
    # Run the focused test
    cd unittest/plaintext-rendering
    ./plaintext_rendering_test
    
    # Run the full suite
    make -C unittest check
    
    # Clean generated test binaries
    make -C unittest clean