From ebb31bdac808693f436aadb8dc40be3a0945be7c Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Tue, 18 Mar 2025 11:28:20 +0100 Subject: [PATCH] IMAP: New driver created just for fun --- README.md | 2 +- plugins/drivers/imap.php | 235 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 plugins/drivers/imap.php diff --git a/README.md b/README.md index a56cc2fe..62f31c6b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ to the target server. **Adminer Editor** offers data manipulation for end-users. https://www.adminer.org/ - **Supports:** MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MS SQL, Oracle -- **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse +- **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse, IMAP - **Requirements:** PHP 5.3+ ## Screenshot diff --git a/plugins/drivers/imap.php b/plugins/drivers/imap.php new file mode 100644 index 00000000..832938e9 --- /dev/null +++ b/plugins/drivers/imap.php @@ -0,0 +1,235 @@ +mailbox = "{" . "$server:993/ssl}"; // Adminer disallows specifying privileged port in server name + $this->imap = @imap_open($this->mailbox, $username, $password, 0, 1); + if (!$this->imap) { + $this->error = imap_last_error(); + } + return $this->imap; + } + + function select_db($database) { + return ($database == "imap"); + } + + function query($query, $unbuffered = false) { + preg_match('~\sFROM (\w+).*?(?:\sWHERE uid = (\d+))?.*?(?:\sLIMIT (\d+)(?:\sOFFSET (\d+))?)?~s', $query, $match); + list(, $table, $uid, $limit, $offset) = $match; + if ($uid) { + $return = array((array) imap_fetchstructure($this->imap, $uid, FT_UID)); + } else { + imap_reopen($this->imap, "$this->mailbox$table"); + $check = imap_check($this->imap); + $range = ($offset + 1) . ":" . ($limit ? min($check->Nmsgs, $offset + $limit) : $check->Nmsgs); + $return = array(); + foreach (imap_fetch_overview($this->imap, $range) as $row) { + // imap_utf8 doesn't work with some strings + $row->subject = iconv_mime_decode($row->subject, 2, "utf-8"); + $row->from = iconv_mime_decode($row->from, 2, "utf-8"); + $row->to = iconv_mime_decode($row->to, 2, "utf-8"); + $row->udate = gmdate("Y-m-d H:i:s", $row->udate); + $return[] = array_merge(array_fill_keys(array_keys(fields($table)), null), (array) $row); + } + } + return new Result($return); + } + + function quote($string) { + return $string; + } + + function tables_list() { + static $return; + if ($return === null) { + $return = array(); + foreach (imap_list($this->imap, $this->mailbox, "*") as $val) { + $return[substr($val, strlen($this->mailbox))] = "table"; + } + } + return array_reverse($return); + } + + function table_status($name, $fast) { + if ($fast) { + return array("Name" => $name); + } + $return = imap_status($this->imap, $this->mailbox . $name, SA_ALL); + return array( + "Name" => $name, + "Rows" => $return->messages, + "Auto_increment" => $return->uidnext, + "Data_free" => $return->unseen, + ); + } + } + + class Result { + public $num_rows; + private $result; + + function __construct($result) { + $this->result = $result; + $this->num_rows = count($result); + } + + function fetch_assoc() { + $row = current($this->result); + next($this->result); + return $row; + } + } + } + + class Driver extends SqlDriver { + static $possibleDrivers = array("imap"); + static $jush = "imap"; + public $editFunctions = array(array("json")); + } + + function logged_user() { + return $_GET["username"]; + } + + function get_databases($flush) { + return array("imap"); + } + + function collations() { + return array(); + } + + function db_collation($db, $collations) { + } + + function information_schema($db) { + } + + function indexes($table, $connection2 = null) { + return array(array("type" => "PRIMARY", "columns" => array("uid"))); + } + + function fields($table) { + $return = array(); + foreach ( + array( // taken from imap_fetch_overview + 'subject' => 'the messages subject', + 'from' => 'who sent it', + 'to' => 'recipient', + 'date' => 'when was it sent', + 'message_id' => 'Message-ID', + 'references' => 'is a reference to this message id', + 'in_reply_to' => 'is a reply to this message id', + 'size' => 'size in bytes', + 'uid' => 'UID the message has in the mailbox', + 'msgno' => 'message sequence number in the mailbox', + 'recent' => 'flagged as recent', + 'flagged' => 'flagged', + 'answered' => 'flagged as answered', + 'deleted' => 'flagged for deletion', + 'seen' => 'flagged as already read', + 'draft' => 'flagged as being a draft', + 'udate' => 'the GMT time of the arrival date', + ) as $name => $comment + ) { + $return[$name] = array( + "field" => $name, + "type" => (preg_match('~^(size|uid|msgno)$~', $name) ? "int" : ""), + "privileges" => array("select" => 1), + "comment" => $comment, + ); + } + return $return; + } + + function convert_field($field) { + } + + function unconvert_field($field, $return) { + return $return; + } + + function limit($query, $where, $limit, $offset = 0, $separator = " ") { + return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : ""); + } + + function idf_escape($idf) { + return $idf; //! maybe {} + } + + function table($idf) { + return idf_escape($idf); + } + + function foreign_keys($table) { + return array(); + } + + function tables_list() { + global $connection; + return $connection->tables_list(); + } + + function table_status($name = "", $fast = false) { + global $connection; + if ($name != "") { + return $connection->table_status($name, $fast); + } + $return = array(); + foreach (tables_list() as $table => $type) { + $return[$table] = $connection->table_status($table, $fast); + } + return $return; + } + + function count_tables($databases) { + return array(reset($databases) => count(tables_list())); + } + + function error() { + global $connection; + return h($connection->error); + } + + function is_view($table_status) { + return false; + } + + function found_rows($table_status, $where) { + return $table_status["Rows"]; + } + + function connect($credentials) { + $connection = new Db; + if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) { + return $connection; + } + return $connection->error; + } + + function support($feature) { + return preg_match("~^()$~", $feature); + } +}