Skip to content

Basic IMAP Without The Extension

Ed Mozley edited this page Nov 7, 2026 · 1 revision

Basic IMAP without the PHP extension - why, and what was decided

Issues: #257 (Basic IMAP unavailable in the Docker image), #259 (is there a plan for IMAP?), and before them #50 Β· Module: Tickets β†’ Mailboxes Β· From: the release after 4.10.0

This page is the decision record: what the problem was, what was considered, what was chosen and why. If the question "why doesn't FreeITSM just use the imap extension?" comes up again, this is the answer.


In one paragraph

Basic IMAP mailboxes used to read mail through PHP's imap extension. PHP 8.4 removed that extension from PHP itself, and the C library it is built on is no longer packaged by Debian 13 - which is what the Docker image runs. So Basic IMAP did not work in Docker, did not work on Debian 13, and on Windows meant hunting the internet for a DLL. FreeITSM now talks IMAP itself, in plain PHP. Nothing to install, on any platform, and one code path for Docker and everything else.


The problem

What changed in PHP

PHP's imap_* functions were never really PHP's own code. They were a thin wrapper around c-client, a C library from the University of Washington whose last release was in 2007. It had known security problems and nobody to fix them. In PHP 8.4.0 the PHP project removed the extension from PHP and moved it to PECL, the add-on repository, where it is kept alive but not developed.

That is a change to PHP, not to an operating system, so every platform felt it:

Platform What happened to Basic IMAP
The Docker image (php:8.4-apache, Debian 13) Could not work. Debian 13 no longer packages c-client, so the extension cannot even be compiled into the image. docker/php-extensions.txt left it out on purpose (#207)
Debian 13 No php-imap package at all. apt install php-imap - the command our own wiki gave - simply failed
Windows (WAMP / XAMPP) on PHP 8.4 No php_imap.dll in the box. You had to find the right one on PECL - matching the PHP version, thread-safe or not, 64-bit - copy it in and edit php.ini. php.net itself warns that the thread-safe build Apache needs is not supported
Ubuntu 25.10, Debian 12, Ubuntu 24.04 Still worked - Ubuntu packaged the PECL build, older releases are on PHP 8.2/8.3

So the feature worked best on old systems and worst on new ones, and the official Docker image - the easiest way to run FreeITSM - could not use it at all.

Why the wiki fix was not enough

#50 was closed with better documentation: a per-platform table and Windows DLL instructions. That made the problem clear; it did not make it go away. #257 and #259 came back to the same wall from the Docker side - one with a workaround of rebuilding the image on Debian 12, which the reporter themselves called "not a clean solution".


What was considered

Docker Windows Debian 13 For whoever runs it
A. Build the Docker image on Debian 12 and compile the extension βœ… ❌ unchanged ❌ unchanged Fixes one platform; ties the image to an older Debian (security support ends 2028) and to a 2007 C library
B. Ship the extension's binaries with FreeITSM messy one DLL per PHP version Γ— thread-safety Γ— architecture one .so per distribution Still needs a php.ini edit - a web app cannot load an extension itself. A pile of binaries to build, ship and keep secure
C. Use an existing PHP IMAP library (e.g. Webklex PHP-IMAP) βœ… βœ… βœ… Pulls in large dependencies (as far as we know: Carbon, parts of Symfony and Laravel). FreeITSM has no Composer - every library is copied in by hand and tracked in VENDOR.md for security updates
D. FreeITSM speaks IMAP itself, in plain PHP βœ… βœ… βœ… Nothing to install. One code path everywhere

"Bundle the extension" was the first instinct, and it is worth saying why it is not possible. An extension is compiled C code that has to match the exact PHP build it loads into, and it is loaded by PHP at start-up from php.ini - not by the application. FreeITSM cannot carry it and switch it on. Option D is what "bundled" turns out to mean for a PHP application: the capability ships as PHP.

What was chosen: D

FreeITSM has its own small IMAP client, includes/imap_client.php. It is the same decision FreeITSM made long ago for sending: replies already go out through a small built-in SMTP client (imapSmtpSend()), not a mail library or extension. Reading now works the same way.

Why it is the right call for FreeITSM:

  • Nothing to install, anywhere. It uses only what every PHP build has: network sockets, OpenSSL for encryption, base64 and quoted-printable decoding, and mbstring/iconv for character sets. The Docker image, WAMP, Debian, Ubuntu and shared hosting all have those.
  • Docker and non-Docker cannot drift apart. There is one code path, and no part of it depends on the platform. A fix for one is a fix for all.
  • The image stays on current Debian. No downgrade, no 2007 C library.
  • It is the protocol FreeITSM needs, not all of IMAP. Sign in, list folders, open one, find unread mail, read it without marking it read, then mark it read, move it or delete it. About ten commands.
  • Self-hosting is easier. For an open-source product other organisations run themselves, every native extension removed is one less thing that can go wrong on somebody else's server.

The one decision that keeps it small: the server parses the email

The hardest part of reading email is not the network conversation - it is MIME: an email can be nested parts inside parts, HTML with a plain-text alternative, base64 and quoted-printable encodings, a dozen character sets, inline images, attachments with names in any language. Writing a MIME parser is genuinely hard, and the advice when this was researched was "don't reinvent MIME parsing".

FreeITSM doesn't. IMAP servers already parse MIME, and will describe a message's structure and hand over one part at a time:

  • BODYSTRUCTURE - the server returns the message's tree of parts: types, encodings, character sets, filenames, which part is an attachment.
  • BODY.PEEK[1.2] - the server returns just that part (here, the second part inside the first), without marking the message read.
  • ENVELOPE - the server returns the sender, recipients, subject and Message-ID, with addresses already split into name, mailbox and domain.

This is exactly what the old extension relied on - imap_fetchstructure() and imap_fetchbody() were these same two IMAP requests. So the new client is like-for-like: the mail server (Dovecot, Exchange, Gmail, whatever the host runs) does the hard parsing, as it always did. What is left for FreeITSM is small and well-defined: undo base64 or quoted-printable on each part (built into PHP), convert character sets (built into PHP), and decode encoded headers and filenames.

The other way - fetch the whole raw email and parse the MIME locally - would have meant either writing a MIME parser or adding a library. The pure-PHP ones carry several dependencies, and the convenient one (php-mime-mail-parser) needs the mailparse extension - the same problem we were escaping.


The concerns raised, and how each is handled

When this approach was researched, four concerns came up. All four were right to raise.

Concern How it is handled
IMAP is more complicated than it looks - multi-line responses, unsolicited responses, length-prefixed literals, tags that must be matched, continuation requests, servers that differ The client is built around exactly those: every command has a tag and the client reads until that tag comes back; unrequested lines along the way are kept or ignored; literals are read by byte count in both directions; capabilities decide between MOVE and the older COPY + EXPUNGE. All of it is tested against two different real servers - see Testing
Email parsing is the biggest headache The mail server does it (above). Nothing in FreeITSM parses raw MIME
Authentication can get complicated (OAuth2, SASL) Not for this feature. Microsoft 365 and Google mailboxes already connect through their own APIs (Graph, Gmail) - not IMAP - and are unchanged. Basic IMAP exists only for username-and-password hosts, so the client needs plain LOGIN
You become responsible for security Certificates are verified with the same policy as every other outbound connection in FreeITSM. A server that refuses passwords on an unencrypted line (LOGINDISABLED) is never sent one. Every value sent is quoted or length-prefixed, so a password containing a line break cannot become a second command. Reads have timeouts and a size cap. Details in the Developer Guide

The research also suggested testing against Exchange Online, Gmail and Dovecot. Dovecot - what most cPanel hosts run - is in the automated tests, along with GreenMail. Exchange Online is not a target: Microsoft has switched off password sign-in for IMAP, and Microsoft 365 customers use the Graph connector anyway. Gmail works with an app password and is the recommended real-world check before a release.


What changes for people running FreeITSM

  • Nothing to install. Basic IMAP works after upgrading, on Docker, Windows, Debian 13 and everywhere else. If you installed the imap extension for FreeITSM, it is no longer used; you can leave it or remove it.
  • No settings change. Server, port, encryption, username, password, folder and post-import actions mean exactly what they did.
  • Microsoft 365 and Gmail mailboxes are untouched.
  • A few things now work that did not always before - folder names with accents, attachment names in other languages, clearer errors. The full list is in the Developer Guide.

Related

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally