From 0453cf3c9cc59d0e5f96f1232c824e748e0777b1 Mon Sep 17 00:00:00 2001 From: Jason Munro Date: Thu, 24 Jan 2019 18:11:51 -0600 Subject: [PATCH] Add JMAP server management issue #180 --- modules/imap/hm-imap.php | 3 + modules/imap/hm-jmap.php | 36 +++---- modules/imap/modules.php | 215 +++++++++++++++++++++++++++++++++------ modules/imap/setup.php | 8 +- modules/imap/site.css | 2 +- modules/imap/site.js | 5 + 6 files changed, 214 insertions(+), 55 deletions(-) diff --git a/modules/imap/hm-imap.php b/modules/imap/hm-imap.php index b437c13d60..54aa693bb8 100644 --- a/modules/imap/hm-imap.php +++ b/modules/imap/hm-imap.php @@ -122,6 +122,9 @@ class Hm_IMAP extends Hm_IMAP_Cache { /* query the server for it's CAPABILITY response */ public $no_caps = false; + /* server type */ + public $server_type = 'IMAP'; + /* IMAP ID client information */ public $app_name = 'Hm_IMAP'; public $app_version = '3.0'; diff --git a/modules/imap/hm-jmap.php b/modules/imap/hm-jmap.php index 9ad7e07ad9..2aa75bade9 100644 --- a/modules/imap/hm-jmap.php +++ b/modules/imap/hm-jmap.php @@ -65,6 +65,7 @@ class Hm_JMAP { public $folder_state = array(); public $use_cache = true; public $read_only = false; + public $server_type = 'JMAP'; /** * PUBLIC INTERFACE @@ -279,7 +280,7 @@ public function get_state() { public function show_debug() { return array( 'commands' => $this->requests, - 'responses' => $htis->responses + 'responses' => $this->responses ); } @@ -396,7 +397,6 @@ public function connect($cfg) { $cfg['username'], $cfg['password'], $cfg['server'], - $cfg['port'] ); } @@ -413,22 +413,21 @@ public function disconnect() { * @param string $username user to login * @param string $password user password * @param string $url JMAP url - * @param integer $port JMAP port * @return boolean */ - public function authenticate($username, $password, $url, $port) { + public function authenticate($username, $password, $url) { if (is_array($this->session)) { $res = $this->session; } else { - $auth_url = $this->prep_url($url, $port); + $auth_url = $this->prep_url($url); $res = $this->send_command($auth_url, array(), 'GET'); } if (is_array($res) && array_key_exists('apiUrl', $res) && array_key_exists('accounts', $res)) { - $this->init_session($res, $url, $port); + $this->init_session($res, $url); return true; } return false; @@ -835,30 +834,28 @@ private function normalize_headers($msg) { * Start a JMAP session * @param array $data JMAP auth response * @param string $url url to access JMAP - * @param integer $port port to access JMAP * @return void */ - private function init_session($data, $url, $port) { + private function init_session($data, $url) { $this->state = 'authenticated'; $this->session = $data; $this->api_url = sprintf( - '%s:%s%s', + '%s%s', preg_replace("/\/$/", '', $url), - $port, $data['apiUrl'] ); $this->download_url = sprintf( - '%s:%s%s', + '%s%s', preg_replace("/\/$/", '', $url), - $port, $data['downloadUrl'] ); $this->upload_url = sprintf( - '%s:%s%s', + '%s%s', preg_replace("/\/$/", '', $url), - $port, $data['uploadUrl'] ); + /* TODO: get account listed as "primary" */ + /* TODO: support > 1 account from a JMAP source */ $this->account_id = array_keys($data['accounts'])[0]; if (count($this->folder_list) == 0) { $this->reset_folders(); @@ -943,6 +940,9 @@ private function search_response($data, $key_path, $default=false) { $data = $data[$key]; } else { + Hm_Debug::add('Failed to find key path in response'); + Hm_Debug::add('key path: '.print_r($keypath, true)); + Hm_Debug::add('data: '.print_r($data, true)); return $default; } } @@ -995,15 +995,11 @@ private function build_headers($user, $pass) { /** * Prep a URL for JMAP discover * @param string $url JMAP url - * @param integer $port JMAP port * @return string */ - private function prep_url($url, $port) { + private function prep_url($url) { $url = preg_replace("/\/$/", '', $url); - if ($port == 80 || $port == 443) { - return sprintf('%s/.well-known/jmap/', $url); - } - return sprintf('%s:%s/.well-known/jmap/', $url, $port); + return sprintf('%s/.well-known/jmap/', $url); } /** diff --git a/modules/imap/modules.php b/modules/imap/modules.php index 453c7b2e38..c0d5103fed 100644 --- a/modules/imap/modules.php +++ b/modules/imap/modules.php @@ -529,7 +529,6 @@ public function process() { $this->out('imap_expanded_folder_path', $path); return; } - $details = Hm_IMAP_List::dump($form['imap_server_id']); $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); if (imap_authed($imap)) { @@ -543,7 +542,7 @@ public function process() { $this->out('imap_expanded_folder_path', $path); } else { - Hm_Msgs::add('ERRCould not authenticate to the selected IMAP server'); + Hm_Msgs::add(sprintf('ERRCould not authenticate to the selected %s server', $imap->server_type)); } } } @@ -967,6 +966,46 @@ public function process() { } } +/** + * Add a new JMAP server + * @subpackage imap/handler + */ +class Hm_Handler_process_add_jmap_server extends Hm_Handler_Module { + public function process() { + /** + * Used on the servers page to add a new JMAP server + */ + if (isset($this->request->post['submit_jmap_server'])) { + list($success, $form) = $this->process_form(array('new_jmap_name', 'new_jmap_address')); + if (!$success) { + $this->out('old_form', $form); + Hm_Msgs::add('ERRYou must supply a name and a JMAP server URL'); + return; + } + $hidden = false; + if (isset($this->request->post['new_jmap_hidden'])) { + $hidden = true; + } + $parsed = parse_url($form['new_jmap_address']); + if (array_key_exists('host', $parsed) && @get_headers($form['new_jmap_address'])) { + + Hm_IMAP_List::add(array( + 'name' => $form['new_jmap_name'], + 'server' => $form['new_jmap_address'], + 'hide' => $hidden, + 'type' => 'jmap', + 'port' => false, + 'tls' => false)); + Hm_Msgs::add('Added server!'); + $this->session->record_unsaved('JMAP server added'); + } + else { + Hm_Msgs::add('ERRCound not access supplied URL'); + } + } + } +} + /** * Add a new IMAP server * @subpackage imap/handler @@ -1297,10 +1336,10 @@ public function process() { } if ($imap) { if ($imap->get_state() == 'authenticated') { - Hm_Msgs::add("Successfully authenticated to the IMAP server"); + Hm_Msgs::add(sprintf("Successfully authenticated to the %s server", $imap->server_type)); } else { - Hm_Msgs::add("ERRFailed to authenticate to the IMAP server"); + Hm_Msgs::add(sprintf("ERRFailed to authenticate to the %s server", $imap->server_type)); } } else { @@ -1766,7 +1805,9 @@ protected function output() { $res = ''; foreach ($this->get('imap_servers', array()) as $index => $vals) { - $no_edit = false; + if (array_key_exists('type', $vals) && $vals['type'] == 'jmap') { + continue; + } if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) { $disabled = 'disabled="disabled"'; @@ -1792,8 +1833,7 @@ protected function output() { $res .= sprintf('
%s
%s/%d %s
', $this->html_safe($vals['name']), $this->html_safe($vals['server']), $this->html_safe($vals['port']), $vals['tls'] ? 'TLS' : '' ); - $res .= - '
'. + $res .= ''. ''. ' '. ''. @@ -1803,32 +1843,30 @@ protected function output() { ''; - if (!$no_edit) { - if (!isset($vals['user']) || !$vals['user']) { - $res .= ''; - $res .= ''; - } - else { - $res .= ''; - $res .= ''; - $res .= ''; - } - $hidden = false; - if (array_key_exists('hide', $vals) && $vals['hide']) { - $hidden = true; - } - $res .= 'trans('Hide').'" class="hide_imap_connection" />'; - $res .= 'trans('Unhide').'" class="unhide_imap_connection" />'; - $res .= ''; + if (!isset($vals['user']) || !$vals['user']) { + $res .= ''; + $res .= ''; + } + else { + $res .= ''; + $res .= ''; + $res .= ''; + } + $hidden = false; + if (array_key_exists('hide', $vals) && $vals['hide']) { + $hidden = true; + } + $res .= 'trans('Hide').'" class="hide_imap_connection" />'; + $res .= 'trans('Unhide').'" class="unhide_imap_connection" />'; + $res .= ''; $res .= '
'; } $res .= '
'; @@ -1848,7 +1886,7 @@ protected function output() { if ($this->get('single_server_mode')) { return ''; } - $count = count($this->get('imap_servers', array())); + $count = count(array_filter($this->get('imap_servers', array()), function($v) { return !array_key_exists('type', $v) || $v['type'] != 'jmap'; })); $count = sprintf($this->trans('%d configured'), $count); return '
'. ''. @@ -1870,6 +1908,117 @@ protected function output() { } } +/** + * Format the add IMAP server dialog for the servers page + * @subpackage imap/output + */ +class Hm_Output_add_jmap_server_dialog extends Hm_Output_Module { + /** + * Build the HTML for the add server dialog + */ + protected function output() { + if ($this->get('single_server_mode')) { + return ''; + } + $count = count(array_filter($this->get('imap_servers', array()), function($v) { return array_key_exists('type', $v) && $v['type'] == 'jmap';})); + $count = sprintf($this->trans('%d configured'), $count); + return '
'. + ''. + ' '.$this->trans('JMAP Servers').'
'.$count.'
'. + ''. + '
'.$this->trans('Add a JMAP Server').'
'. + ''. + ''. + ''. + ''. + '
'. + '
'. + '
'. + '
'; + } +} + +/** + * Format configured JMAP servers for the servers page + * @subpackage imap/output + */ +class Hm_Output_display_configured_jmap_servers extends Hm_Output_Module { + /** + * Build HTML for configured JMAP servers + */ + protected function output() { + if ($this->get('single_server_mode')) { + return ''; + } + $res = ''; + foreach ($this->get('imap_servers', array()) as $index => $vals) { + + if (!array_key_exists('type', $vals) || $vals['type'] != 'jmap') { + continue; + } + if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) { + $disabled = 'disabled="disabled"'; + $user_pc = $vals['user']; + $pass_pc = $this->trans('[saved]'); + } + elseif (array_key_exists('nopass', $vals)) { + if (array_key_exists('user', $vals)) { + $user_pc = $vals['user']; + } + else { + $user_pc = ''; + } + $pass_pc = $this->trans('Password'); + $disabled = ''; + } + else { + $user_pc = ''; + $pass_pc = $this->trans('Password'); + $disabled = ''; + } + $res .= '
'; + $res .= sprintf('
%s
%s
', + $this->html_safe($vals['name']), $this->html_safe($vals['server'])); + $res .= '
'. + ''. + ' '. + ''. + ''. + ''. + ''; + + if (!isset($vals['user']) || !$vals['user']) { + $res .= ''; + $res .= ''; + } + else { + $res .= ''; + $res .= ''; + $res .= ''; + } + $hidden = false; + if (array_key_exists('hide', $vals) && $vals['hide']) { + $hidden = true; + } + $res .= 'trans('Hide').'" class="hide_imap_connection" />'; + $res .= 'trans('Unhide').'" class="unhide_imap_connection" />'; + $res .= ''; + $res .= '
'; + } + $res .= '
'; + return $res; + } +} /** * Format the IMAP status output on the info page * @subpackage imap/output diff --git a/modules/imap/setup.php b/modules/imap/setup.php index 62eaf26654..6480aa1e03 100644 --- a/modules/imap/setup.php +++ b/modules/imap/setup.php @@ -19,9 +19,12 @@ /* servers page data */ add_handler('servers', 'process_add_imap_server', true, 'imap', 'message_list_type', 'after'); -add_handler('servers', 'save_imap_servers', true, 'imap', 'process_add_imap_server', 'after'); +add_handler('servers', 'process_add_jmap_server', true, 'imap', 'process_add_imap_server', 'after'); +add_handler('servers', 'save_imap_servers', true, 'imap', 'process_add_jmap_server', 'after'); add_output('servers', 'add_imap_server_dialog', true, 'imap', 'server_content_start', 'after'); add_output('servers', 'display_configured_imap_servers', true, 'imap', 'add_imap_server_dialog', 'after'); +add_output('servers', 'add_jmap_server_dialog', true, 'imap', 'display_configured_imap_servers', 'after'); +add_output('servers', 'display_configured_jmap_servers', true, 'imap', 'add_jmap_server_dialog', 'after'); add_output('servers', 'imap_server_ids', true, 'imap', 'page_js', 'before'); /* settings page data */ @@ -315,6 +318,9 @@ 'imap_forget' => FILTER_SANITIZE_STRING, 'imap_save' => FILTER_SANITIZE_STRING, 'submit_imap_server' => FILTER_SANITIZE_STRING, + 'submit_jmap_server' => FILTER_SANITIZE_STRING, + 'new_jmap_address' => FILTER_SANITIZE_URL, + 'new_jmap_name' => FILTER_SANITIZE_STRING, 'new_imap_address' => FILTER_SANITIZE_STRING, 'new_imap_hidden' => FILTER_VALIDATE_BOOLEAN, 'new_imap_port' => FILTER_VALIDATE_INT, diff --git a/modules/imap/site.css b/modules/imap/site.css index 9c13b876f5..92813c24fb 100644 --- a/modules/imap/site.css +++ b/modules/imap/site.css @@ -52,7 +52,7 @@ .online { color: teal; } .down { color: red; } .header_subject .account_icon { vertical-align: 0px; } -.sent_setting, .imap_section { display: none; } +.jmap_section, .sent_setting, .imap_section { display: none; } .imap_server_setup .content_title { cursor: pointer; } .ctr_divider { padding-left: 5px; padding-right: 5px; color: #eee; vertical-align: 2px; font-weight: normal; font-size: 90%; } .move_to_location { text-transform: none; z-index: 1; position: absolute; display: none; background-color: #fff; padding: 10px; border: solid 1px #ede8e6; font-weight: normal; padding-right: 25px; padding-left: 0px; font-size: 80%; top: 34px; min-width: 55px; right: 2px; border-top: none; padding-top: 0px; } diff --git a/modules/imap/site.js b/modules/imap/site.js index 0e261cd66f..8366867f2a 100644 --- a/modules/imap/site.js +++ b/modules/imap/site.js @@ -118,10 +118,15 @@ var imap_setup_server_page = function() { $('.unhide_imap_connection').on('click', imap_unhide); $('.forget_imap_connection').on('click', imap_forget_action); $('.test_imap_connect').on('click', imap_test_action); + var dsp = Hm_Utils.get_from_local_storage('.imap_section'); if (dsp === 'block' || dsp === 'none') { $('.imap_section').css('display', dsp); } + var jdsp = Hm_Utils.get_from_local_storage('.jmap_section'); + if (jdsp === 'block' || jdsp === 'none') { + $('.jmap_section').css('display', jdsp); + } }; var set_message_content = function(path, msg_uid) {