Skip to content

[PHP-WASM] Add raw outbound mail capture plumbing - #3956

Closed
bgrgicak wants to merge 3 commits into
trunkfrom
codex/smtp-runtime-integration
Closed

bgrgicak wants to merge 3 commits into
trunkfrom
codex/smtp-runtime-integration

Conversation

@bgrgicak

@bgrgicak bgrgicak commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds raw outbound mail capture plumbing for PHP runtimes without parsing the captured bytes.

Motivation for the change, related issues

This is the first SMTP-stack layer after #3951. Once PHP can route write-side popen() / pclose() through wasm wrappers, Playground can intercept the bytes PHP writes to sendmail. This PR proves that raw stream can be captured, but intentionally does not parse headers, MIME bodies, attachments, or SMTP socket sessions.

Implementation details

  • Adds bounded stdin collection to process handlers via ProcessApi.readStdin().
  • Adds shared byte concatenation helpers used by process stdin buffering.
  • Adds createSendmailCaptureSpawnHandler(), which intercepts sendmail commands and reports raw stdin bytes plus command metadata.
  • Allows the raw capture callback to be async, so later parsed adapters can await parser work before the sendmail process exits.
  • Adds withMailCapture() in @php-wasm/universal and exposes it through the node and web runtime loaders.
  • Adds PHP 8.4 node tests that verify mail() and direct sendmail proc_open() calls can be captured as raw bytes.

This deliberately leaves SMTP protocol handling and MIME parsing to later PRs in the stack.

Testing Instructions

Run the utility tests and raw mail capture runtime test:

npm exec nx test php-wasm-util -- --runInBand
npm exec nx run php-wasm-node:test-group-2-asyncify -- --testFiles=php-mail-capture.spec.ts

@bgrgicak
bgrgicak force-pushed the codex/smtp-runtime-integration branch from c37001e to e9ea3f3 Compare July 8, 2026 09:34
@bgrgicak
bgrgicak changed the base branch from codex/smtp-popen-mail-routing to trunk July 8, 2026 09:34
};
}

function getEnvelopeSender(command: string[]) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add simple docs what this does.

: typeof command === 'string'
? (splitShellCommand(command)[0] ?? '')
: '';
const bin = basename(cmdStr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we could improve the const naming.

Comment thread packages/php-wasm/util/src/lib/create-sendmail-capture-handler.ts Outdated
}

if (loaderOptions.withMailCapture) {
emscriptenOptions = withMailCapture(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets the spawnProcess option, but Playground will probably override it in

and
php.setSpawnHandler((command: string, args: string[]): any =>
.

return;
}

if (stdin.bytes.length > 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be silent or emit an error? Or should we call the fallback even with no bytes?

@bgrgicak

bgrgicak commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

This PR should enable us to start working on a UI for email on the website and it's working, but I'm unhappy with how it overrides the spawn handler.

A alternative to the current approach are creating a stack of spawn handler overrides, but that's too complex for what we need and could be messy to add more spawn handlers. For example if I want to replace the default, or need to ensure that handler B goes after handler A.

A simpler option (I'm exploring it now) would be to always capture email and leave the spawn handler as is.
When email is captured we send a event that PHP-wasm users (developers) or Playground can catch.

@bgrgicak

Copy link
Copy Markdown
Collaborator Author

Superseded by a two-PR stack: #3995 (per-command spawn handlers) and #3996 (sendmail mail capture, stacked on #3995). The combined diff is equivalent to this PR's final state.

@bgrgicak bgrgicak closed this Jul 10, 2026
ashfame added a commit that referenced this pull request Aug 10, 2026
## Summary

Adds shared Base64 helpers to `@php-wasm/util`.

## Motivation for the change, related issues

Playground currently has several small Base64 implementations spread
across packages. The SMTP stack needs byte-oriented Base64 helpers for
protocol-level authentication challenges and responses, so this PR
centralizes those helpers before the SMTP protocol engine lands.

This sits after #3956 in the SMTP review stack. #3956 proves PHP mail
bytes can be captured raw; this PR adds shared Base64 utilities used by
#3953's SMTP AUTH support and existing callers.

## Implementation details

- Moves `decodeBase64ToString`, `decodeBase64ToUint8Array`,
`encodeStringAsBase64`, and `encodeUint8ArrayAsBase64` to
`@php-wasm/util`.
- Updates call sites to import the shared helpers.
- Updates `encodeUint8ArrayAsBase64` to support arrays with more than
65536 arguments without triggering a `RangeError`.

The MIME parser work later in the stack no longer depends on these
helpers; it is delegated to `postal-mime` in #3953.

## Testing Instructions

Run the focused utility tests:

```bash
npm exec nx test php-wasm-util -- --runInBand
```

Co-authored-by: Ashish Kumar <ashfame@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants