mirror of
https://github.com/vrana/adminer.git
synced 2025-08-07 23:27:17 +02:00
277 lines
7.7 KiB
PHP
277 lines
7.7 KiB
PHP
<?php
|
|
/** Experimental driver for IMAP created just for fun. Features:
|
|
* - list mailboxes with number of messages (Rows) and unread messages (Data Free)
|
|
* - creating and dropping mailboxes work, truncate does expunge on all mailboxes
|
|
* - list messages in each mailbox - limit and offset works but there's no search and order
|
|
* - for each message, there's subject, from, to, date and some flags
|
|
* - editing the message shows some other information
|
|
* - deleting marks the message for deletion but doesn't expunge the mailbox
|
|
* - inserting or updating the message does nothing
|
|
* @link https://www.adminer.org/static/plugins/imap.png
|
|
*/
|
|
|
|
namespace Adminer;
|
|
|
|
add_driver("imap", "IMAP");
|
|
|
|
if (isset($_GET["imap"])) {
|
|
define('Adminer\DRIVER', "imap");
|
|
|
|
if (extension_loaded("imap")) {
|
|
class Db extends SqlDb {
|
|
public $extension = "IMAP";
|
|
public $server_info = "?"; // imap_mailboxmsginfo() or imap_check() don't return anything useful
|
|
private $mailbox;
|
|
private $imap;
|
|
|
|
function attach($server, $username, $password): string {
|
|
$this->mailbox = "{" . "$server:993/ssl}"; // Adminer disallows specifying privileged port in server name
|
|
$this->imap = @imap_open($this->mailbox, $username, $password, OP_HALFOPEN, 1);
|
|
return ($this->imap ? '' : imap_last_error());
|
|
}
|
|
|
|
function select_db($database) {
|
|
return ($database == "mail");
|
|
}
|
|
|
|
function query($query, $unbuffered = false) {
|
|
if (preg_match('~DELETE FROM "(.+?)"~', $query)) {
|
|
preg_match_all('~"uid" = (\d+)~', $query, $matches);
|
|
return imap_delete($this->imap, implode(",", $matches[1]), FT_UID);
|
|
} elseif (preg_match('~^SELECT COUNT\(\*\)\sFROM "(.+?)"~s', $query, $match)) {
|
|
$status = table_status1($match[1]);
|
|
return new Result(array(array($status["Rows"])));
|
|
} elseif (preg_match('~^SELECT (.+)\sFROM "(.+?)"(?:\sWHERE "uid" = (\d+))?.*?(?:\sLIMIT (\d+)(?:\sOFFSET (\d+))?)?~s', $query, $match)) {
|
|
list(, $columns, $table, $uid, $limit, $offset) = $match;
|
|
imap_reopen($this->imap, $this->mailbox . $table);
|
|
if ($uid) {
|
|
$return = array((array) imap_fetchstructure($this->imap, $uid, FT_UID));
|
|
} else {
|
|
$count = imap_num_msg($this->imap);
|
|
$range = ($offset + 1) . ":" . ($limit ? min($count, $offset + $limit) : $count);
|
|
$return = array();
|
|
$fields = fields($table);
|
|
$columns = ($columns == "*" ? $fields : array_flip(explode(", ", $columns)));
|
|
$empty = array_fill_keys(array_keys($fields), null);
|
|
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_intersect_key(array_merge($empty, (array) $row), $columns);
|
|
}
|
|
}
|
|
return new Result($return);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function quote($string): 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_length" => $return->messages, // this is used on database overview
|
|
"Data_free" => $return->unseen,
|
|
);
|
|
}
|
|
|
|
function create($name) {
|
|
return imap_createmailbox($this->imap, $this->mailbox . $name);
|
|
}
|
|
|
|
function drop($name) {
|
|
return imap_deletemailbox($this->imap, $this->mailbox . $name);
|
|
}
|
|
|
|
function expunge() {
|
|
return imap_expunge($this->imap);
|
|
}
|
|
}
|
|
|
|
class Result {
|
|
public $num_rows;
|
|
private $result;
|
|
private $fields;
|
|
|
|
function __construct($result) {
|
|
$this->result = $result;
|
|
$this->num_rows = count($result);
|
|
$this->fields = array_keys(idx($result, 0, array()));
|
|
}
|
|
|
|
function fetch_assoc() {
|
|
$row = current($this->result);
|
|
next($this->result);
|
|
return $row;
|
|
}
|
|
|
|
function fetch_row() {
|
|
$row = $this->fetch_assoc();
|
|
return ($row ? array_values($row) : false);
|
|
}
|
|
|
|
function fetch_field(): \stdClass {
|
|
$field = current($this->fields);
|
|
next($this->fields);
|
|
return ($field != '' ? (object) array('name' => $field, 'type' => 15, 'charsetnr' => 0) : false);
|
|
}
|
|
}
|
|
}
|
|
|
|
class Driver extends SqlDriver {
|
|
static $extensions = array("imap");
|
|
static $jush = "imap";
|
|
public $insertFunctions = array("json");
|
|
}
|
|
|
|
function logged_user() {
|
|
return $_GET["username"];
|
|
}
|
|
|
|
function get_databases($flush) {
|
|
return array("mail");
|
|
}
|
|
|
|
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 ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
|
|
}
|
|
|
|
function idf_escape($idf) {
|
|
return '"' . str_replace('"', '""', $idf) . '"';
|
|
}
|
|
|
|
function table($idf) {
|
|
return idf_escape($idf);
|
|
}
|
|
|
|
function foreign_keys($table) {
|
|
return array();
|
|
}
|
|
|
|
function tables_list() {
|
|
return connection()->tables_list();
|
|
}
|
|
|
|
function table_status($name = "", $fast = false) {
|
|
$return = array();
|
|
foreach (($name != "" ? array($name => 1) : 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() {
|
|
return h(connection()->error);
|
|
}
|
|
|
|
function is_view($table_status) {
|
|
return false;
|
|
}
|
|
|
|
function found_rows($table_status, $where) {
|
|
return $table_status["Rows"];
|
|
}
|
|
|
|
function fk_support($table_status) {
|
|
}
|
|
|
|
function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
|
|
return connection()->create($name);
|
|
}
|
|
|
|
function drop_tables($tables) {
|
|
$return = true;
|
|
foreach ($tables as $name) {
|
|
$return = $return && connection()->drop($name);
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
function truncate_tables($tables) {
|
|
return connection()->expunge();
|
|
}
|
|
|
|
function support($feature) {
|
|
return false;
|
|
}
|
|
}
|