PHP File-Based Mailer
=====================

Requirements
- PHP 8.2+ with OpenSSL, JSON, sockets, and preferably sodium.
- A web server with PHP support.
- A cron job (recommended) or CLI access for the worker.
- SMTP accounts that explicitly permit the intended opt-in bulk-mail use.

No MySQL/MariaDB or database installation is required. State is stored in JSON/CSV files.
This tool is for permission-based mailing lists only. Do not import purchased, scraped,
or otherwise non-consensual lists. Every campaign includes an unsubscribe link.

Quick start
1. Upload this directory outside the public web root if possible. Point the web root to
   this directory (or protect storage/ and config files using the included .htaccess).
2. Ensure PHP can write to storage/.
3. Visit index.php in a browser and create the first administrator.
4. Add SMTP accounts and use "Test connection" before enabling them.
5. Import a CSV with an email column (or a plain TXT file with one address per line).
6. Create a campaign and send a test message.
7. Configure a cron job to run the worker every minute:
   * * * * * /usr/bin/php /path/to/php-mailer/worker.php
   Adjust the PHP path and directory to your hosting environment.
8. Use the dashboard to start/pause campaigns and review logs.

SMTP rotation and limits
- Each newly added SMTP account defaults to 100 messages per rolling hour.
- The worker round-robins across enabled, tested accounts that still have capacity.
- When an account reaches its configured cap, it is skipped until capacity returns.
- This is NOT a way to bypass provider limits. Configure each account at or below the
  provider's permitted rate, and keep the account's own cap no higher than that limit.
- Provider throttling, reputation controls, daily caps, and recipient-domain limits may
  apply independently. "Unlimited" sending cannot be guaranteed by software.

Additional features
- Campaign daily limit defaults to 1,000 and can be set per campaign. It is enforced per UTC calendar day in addition to each SMTP rolling-hour cap.
- Temporary SMTP errors are retried with exponential backoff (1, 2, 4, 8, 16 minutes; maximum five retries after the initial attempt). Permanent 5xx SMTP errors are not retried.
- SMTP performance statistics are shown in Reports (attempts, SMTP-accepted messages, errors, last use).
- A basic visual HTML editor is provided with a source editor. Review the HTML and send a test before launch.
- Bounce/complaint ingestion is supported through bounce.php using a signed, normalized webhook. The expected JSON body is {"email":"person@example.com","event":"bounce"} (event may be bounce, hard_bounce, or complaint). Compute X-Bounce-Signature as `sha256=` plus the lowercase hex HMAC-SHA256 of the exact raw JSON body. The webhook signing secret is HMAC-SHA256 of `bounce-webhook-v1` using the application's secret key in storage/secret.key. Configure your ESP webhook adapter to normalize provider events to this schema. Unsigned requests are rejected. Hard bounces and complaints are added to suppression.

Operational notes
- SMTP acceptance is not proof of inbox delivery. Bounce processing requires configuring your provider webhook (or an adapter) to POST the normalized signed payload described above. Provider-specific webhook payloads are not parsed automatically.
- Use only recipients who gave appropriate consent. Comply with applicable anti-spam,
  privacy, and data-protection laws (including applicable Nigerian and recipient-country
  requirements). Identify the sender accurately and provide a working unsubscribe route.
- Back up storage/ regularly. Keep storage/ outside public web access where possible.
- This is a practical starter application, not a substitute for a security review before
  exposing it to the public internet.

Security
- First-run setup creates the admin password hash.
- SMTP passwords are encrypted at rest using libsodium when available, otherwise OpenSSL.
- Protect storage/ and config.php from web access. The included .htaccess helps on Apache;
  configure equivalent rules on Nginx.
