Skip to content

Mailbox Providers Developer Guide

Ed Mozley edited this page Oct 25, 2026 · 1 revision

Mailbox providers - Developer Guide

Added in 4.0.0 Β· includes/mailbox_provider.php Β· user pages: Mailbox authentication, Basic IMAP mailboxes

A ticket mailbox gets its mail from one of three places: Microsoft 365 (Graph), Google Workspace (Gmail API) or Basic IMAP / SMTP. Until 4.0 these were the strings 'microsoft', 'google' and 'imap', spelled out in a dozen files, each with its own ?? 'microsoft' default, and the Mailboxes tab drew its coloured badge with a JavaScript if/else that repeated what the PHP knew. Now there is one enum:

enum MailboxProvider: string
{
    case Microsoft = 'microsoft';
    case Google    = 'google';
    case Imap      = 'imap';
}

The value is what target_mailboxes.provider stores, so a case's value must never change.

Using it

$provider = MailboxProvider::fromMailbox($mailbox);   // a target_mailboxes row

if ($provider === MailboxProvider::Imap) {
    // username and password - imap_* / SMTP
} elseif ($provider === MailboxProvider::Google) {
    // Gmail API
} else {
    // Microsoft Graph (delegated or app-only)
}
Method Gives
fromMailbox(?array $row) The row's provider. None saved, or one the code does not know, is Microsoft - exactly what every old ?? 'microsoft' said
fromValue($value) The same, from a bare value - a fetchColumn() result, which may be false
usesOAuth() Microsoft and Google sign in with OAuth (a token to refresh); IMAP uses a password
supportsAppOnly() Only Microsoft has app-only (client credentials) mode
label(), badgeColours() The Mailboxes tab's badge - a product name (not translated) and fixed colours, because a badge is data and means the same in light and dark
forJs() Every provider as value => {label, background, color, oauth}, for the browser

πŸ”΄ TRAP: never compare a stored string with a case

if ($mailbox['provider'] === MailboxProvider::Imap)            // ALWAYS false - a string is never a case
if (MailboxProvider::fromMailbox($mailbox) === MailboxProvider::Imap)   // right

PHP does not complain about the first line - it is simply false, every time, and the IMAP branch never runs. Turn the value into a case first, then compare cases. handleEmailAfterProcessing() (api/tickets/check_mailbox_email.php) takes a MailboxProvider parameter for this reason: a string can no longer reach its comparisons.

Where it is used

Reading a mailbox (check_mailbox_email.php), sending (send_email.php, includes/mailbox_send.php, includes/template_email.php, includes/self_service_email.php, the workflow engine's send step), checking a folder (verify_mailbox_folder.php), Graph helpers (includes/mailbox_graph.php), listing (get_mailboxes.php), OAuth start (mailbox_oauth_start.php) and password-reset email (api/auth/request_password_reset.php).

Saving (api/tickets/save_mailbox.php) uses tryFrom() and refuses an unknown provider - until 4.0 any text was stored as given. The value written is $provider->value.

The Mailboxes tab (Tickets β†’ Settings) builds its provider dropdown from MailboxProvider::cases() - each option's label is the translation tickets.settings.modals.mailbox.provider_<value> - and its badge from MAILBOX_PROVIDERS, the page's json_encode(MailboxProvider::forJs()).

Loading it

includes/functions.php requires includes/mailbox_provider.php, so every page and endpoint has it. ⚠️ The enum is in its own file on purpose: functions.php must stay readable by an old PHP so the version guard at its top can speak (Contributing), and an enum is PHP 8.1 syntax. Never move the enum into functions.php.

Adding a provider

  1. Add the case, and answer it in label() and badgeColours() - match will not let you forget.
  2. Add the translation tickets.settings.modals.mailbox.provider_<value> to lang/en/tickets.php; the dropdown picks it up.
  3. Go through the files above and decide what the new provider does at each branch - most are if / elseif / else with Microsoft as the else, so a new case would otherwise quietly be treated as Microsoft.
  4. Its sign-in, if it is OAuth: see Mailbox authentication.

Not this

Calendar sync has its own, different list of providers (microsoft, caldav) in includes/calendar_sync/ - a calendar connection, not a mailbox. A $current['provider'] === 'microsoft' there is about a calendar and is correct as it is.

Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally