diff --git a/SQL/mysql.20140112.sql b/SQL/mysql.20140112.sql new file mode 100644 index 0000000..46197e4 --- /dev/null +++ b/SQL/mysql.20140112.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS `identities_imap_hosts` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `domain` varchar(255) COLLATE utf8_general_ci NOT NULL, + `host` varchar(255) COLLATE utf8_general_ci DEFAULT NULL, + `ts` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1 ; + +UPDATE `system` SET `value`='initial|20140112' WHERE `name`='myrc_identities_imap'; \ No newline at end of file diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql new file mode 100644 index 0000000..29e430e --- /dev/null +++ b/SQL/mysql.initial.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS `identities_imap` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `iid` int(10) unsigned NOT NULL, + `username` varchar(256) DEFAULT NULL, + `password` text, + `server` varchar(256) DEFAULT NULL, + `enabled` int(1) unsigned NOT NULL DEFAULT '0', + `label` text, + `preferences` text, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `iid` (`iid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; + +CREATE TABLE IF NOT EXISTS `system` ( + `name` varchar(64) NOT NULL, + `value` mediumtext, + PRIMARY KEY(`name`) +); + +INSERT INTO `system` (name, value) VALUES ('myrc_identities_imap', 'initial'); + +ALTER TABLE `identities_imap` + ADD CONSTRAINT `identities_imap_ibfk_2` FOREIGN KEY (`iid`) REFERENCES `identities` (`identity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT `identities_imap_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/SQL/mysql.sql b/SQL/mysql.sql new file mode 100644 index 0000000..8ebbde4 --- /dev/null +++ b/SQL/mysql.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS `identities_imap` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `iid` int(10) unsigned NOT NULL, + `username` varchar(256) DEFAULT NULL, + `password` text, + `server` varchar(256) DEFAULT NULL, + `enabled` int(1) unsigned NOT NULL DEFAULT '0', + `label` text, + `preferences` text, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `iid` (`iid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; + +ALTER TABLE `identities_imap` + ADD CONSTRAINT `identities_imap_ibfk_2` FOREIGN KEY (`iid`) REFERENCES `identities` (`identity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT `identities_imap_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/SQL/pgsql.20140112.sql b/SQL/pgsql.20140112.sql new file mode 100644 index 0000000..659d345 --- /dev/null +++ b/SQL/pgsql.20140112.sql @@ -0,0 +1,8 @@ +CREATE TABLE identities_imap_hosts ( + id serial PRIMARY KEY, + domain varchar(255) NOT NULL, + host varchar(255) DEFAULT NULL, + ts timestamp NOT NULL +); + +UPDATE "system" SET value='initial|20140112' WHERE name='myrc_identities_imap'; diff --git a/SQL/pgsql.initial.sql b/SQL/pgsql.initial.sql new file mode 100644 index 0000000..7df171a --- /dev/null +++ b/SQL/pgsql.initial.sql @@ -0,0 +1,21 @@ +CREATE TABLE identities_imap ( + id serial PRIMARY KEY, + user_id integer NOT NULL REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE, + iid integer NOT NULL REFERENCES identities(identity_id) ON DELETE CASCADE ON UPDATE CASCADE, + username varchar(256) DEFAULT NULL, + password text, + server varchar(256) DEFAULT NULL, + enabled smallint NOT NULL DEFAULT 0, + label text, + preferences text +); + +CREATE TABLE IF NOT EXISTS "system" ( + name varchar(64) NOT NULL PRIMARY KEY, + value text +); + +INSERT INTO "system" (name, value) VALUES ('myrc_identities_imap', 'initial'); + +CREATE INDEX ix_identities_imap_user_id ON identities_imap(user_id); +CREATE INDEX ix_identities_imap_iid ON identities_imap(iid); diff --git a/SQL/postgres.sql b/SQL/postgres.sql new file mode 100644 index 0000000..76c6ebf --- /dev/null +++ b/SQL/postgres.sql @@ -0,0 +1,14 @@ +CREATE TABLE identities_imap ( + id serial PRIMARY KEY, + user_id integer NOT NULL REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE, + iid integer NOT NULL REFERENCES identities(identity_id) ON DELETE CASCADE ON UPDATE CASCADE, + username varchar(256) DEFAULT NULL, + password text, + server varchar(256) DEFAULT NULL, + enabled smallint NOT NULL DEFAULT 0, + label text, + preferences text +); + +CREATE INDEX ix_identities_imap_user_id ON identities_imap(user_id); +CREATE INDEX ix_identities_imap_iid ON identities_imap(iid); diff --git a/SQL/sqlite.20140112.sql b/SQL/sqlite.20140112.sql new file mode 100644 index 0000000..b84c2c4 --- /dev/null +++ b/SQL/sqlite.20140112.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS 'identities_imap_hosts' ( + 'id' INTEGER NOT NULL PRIMARY KEY ASC, + 'domain' varchar(255) NOT NULL, + 'host' varchar(255) DEFAULT NULL, + 'ts' DATETIME NOT NULL +); + +UPDATE 'system' SET value='initial|20140112' WHERE name='myrc_identities_imap'; diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql new file mode 100644 index 0000000..79b8f20 --- /dev/null +++ b/SQL/sqlite.initial.sql @@ -0,0 +1,22 @@ +CREATE TABLE IF NOT EXISTS 'identities_imap' ( + 'id' INTEGER NOT NULL PRIMARY KEY ASC, + 'user_id' int(10) NOT NULL, + 'iid' int(10) NOT NULL, + 'username' varchar(256) DEFAULT NULL, + 'password' text, + 'server' varchar(256) DEFAULT NULL, + 'enabled' int(1) NOT NULL DEFAULT '0', + 'label' text, + 'preferences' text, + CONSTRAINT 'identities_imap_ibfk_1' FOREIGN KEY ('user_id') REFERENCES 'users' + ('user_id') ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE IF NOT EXISTS 'system' ( + name varchar(64) NOT NULL PRIMARY KEY, + value text NOT NULL +); + +INSERT INTO system (name, value) VALUES ('myrc_identities_imap', 'initial'); + +CREATE INDEX identities_imap_iid ON 'identities_imap'('iid'); \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e69de29 diff --git a/config.inc.php b/config.inc.php new file mode 100644 index 0000000..20fc1a6 --- /dev/null +++ b/config.inc.php @@ -0,0 +1,26 @@ + array( + 'host' =>'localhost', + 'delimiter' => '/', + 'readonly' => false, // on match prevent field editing + ), +); +?> diff --git a/config.inc.php.dist b/config.inc.php.dist new file mode 100644 index 0000000..5b89a5c --- /dev/null +++ b/config.inc.php.dist @@ -0,0 +1,44 @@ + array( + 'host' =>'ssl://imap.gmail.com:993', + 'delimiter' => '/', + 'readonly' => true, // on match prevent field editing + ), + 'googlemail.com' => array( + 'host' =>'ssl://imap.gmail.com:993', + 'delimiter' => '/', + 'readonly' => true, // on match prevent field editing + ), + 'freenet.de' => array( + 'host' =>'mx.freenet.de', + 'delimiter' => '.', + 'readonly' => false, // on match prevent field editing + ), +); + +$rcmail_config['identities_imap_internal'] = array( + 'mydomain.tld' => array( + 'host' =>'ssl://imap.mydomain.tld:993', + 'delimiter' => '/', + 'readonly' => true, // on match prevent field editing + ), +); + +/* auto-detect IMAP server */ +$rcmail_config['identities_imap_autodetect'] = false; +?> \ No newline at end of file diff --git a/ident_switch.js b/ident_switch.js new file mode 100644 index 0000000..8620e89 --- /dev/null +++ b/ident_switch.js @@ -0,0 +1,14 @@ +/* + * This is part of identity_imap plugin + */ +$(function() { + if ($('#plugin-ident_switch-account').size() > 0) { + $('#plugin-ident_switch-account').prependTo('.topright'); + $('.topright .username').hide(); + $('#plugin-ident_switch-account').show(); + } +}); + +function plugin_switchIdent_switch(val) { + rcmail.http_post('plugin.ident_switch.switch', { '_ident-id': val }); +} diff --git a/ident_switch.php b/ident_switch.php new file mode 100644 index 0000000..ac0adf3 --- /dev/null +++ b/ident_switch.php @@ -0,0 +1,234 @@ +add_hook('startup', array($this, 'on_startup')); + $this->add_hook('render_page', array($this, 'on_render_page')); +# $this->add_hook('smtp_connect', array($this, 'on_smtp_connect')); + $this->add_hook('identity_form', array($this, 'on_identity_form')); + $this->add_hook('identity_update', array($this, 'on_identity_update')); + $this->add_hook('identity_delete', array($this, 'on_identity_delete')); + + $this->register_action('plugin.ident_switch.switch', array($this, 'on_switch')); + } + + function on_startup($args) + { + $rc = rcmail::get_instance(); + + if (strtolower($rc->user->data['username']) != strtolower($_SESSION['username'])) + { // We are impersonating + $rc->config->set('imap_cache', null); + $rc->config->set('messages_cache', false); + + if ($args['task'] == 'mail') + { + $this->add_texts('localization/'); + $rc->config->set('create_default_folders', false); + } + } + + return $args; + } + + function on_render_page($args) + { + $rc = rcmail::get_instance(); + + // Get list of alternative accounts + $sOpt = ''; + $sql = 'SELECT id, label FROM ' . $rc->db->table_name($this->table) . ' WHERE user_id = ? AND enabled = true'; + $q = $rc->db->query($sql, $rc->user->data['user_id']); + while ($r = $rc->db->fetch_assoc($q)) + { + $sOpt .= html::tag( + 'option', + array('value' => $r['id']), + $r['label'] + ); + } + + // Render UI if user has extra accounts + if (!empty($sOpt)) + { + + // TODO: Add main account + + $this->include_script('ident_switch.js'); + $sw = html::tag( + 'select', + array( + 'id' => 'plugin-ident_switch-account', + 'style' => 'display: none;', + 'onchange' => 'plugin_switchIdent_switch(this.value);', + ), + $sOpt + ); + $rc->output->add_footer($sw); + } + + return $args; + } + + function on_smtp_connect($args) + { + error_log('on_smtp_connect'); + } + + function on_identity_form($args) + { + $rc = rcmail::get_instance(); + $this->add_texts('localization'); + + // Create our field set + $args['form']['ident_switch'] = array( + 'name' => $this->gettext('form.caption'), + 'content' => array( + 'ident_switch.form.enabled' => array('type' => 'checkbox'), + 'ident_switch.form.label' => array('type' => 'text', 'size' => 32), + 'ident_switch.form.host' => array('type' => 'text', 'size' => 64), + 'ident_switch.form.secure' => array('type' => 'checkbox'), + 'ident_switch.form.port' => array('type' => 'text', 'size' => 5), + 'ident_switch.form.username' => array('type' => 'text', 'size' => 64), + 'ident_switch.form.password' => array('type' => 'password', 'size' => 64), + 'ident_switch.form.delimiter' => array('type' => 'text', 'size' => 1), + ), + ); + + // Load data if exists + if (isset($args['record']['identity_id'])) + { + $sql = 'SELECT * FROM ' . $rc->db->table_name($this->table) . ' WHERE iid = ? AND user_id = ?'; + $q = $rc->db->query($sql, $args['record']['identity_id'], $rc->user->ID); + $r = $rc->db->fetch_assoc($q); + if ($r) + { + foreach ($r as $k => $v) + $args['record']['ident_switch.form.' . $k] = $v; + } + } + + return $args; + } + + function on_identity_update($args) + { + $rc = rcmail::get_instance(); + + // TODO: check field values + + $sql = 'SELECT NULL FROM ' . $rc->db->table_name($this->table) . ' WHERE iid = ? AND user_id = ?'; + $q = $rc->db->query($sql, $args['id'], $rc->user->ID); + $r = $rc->db->fetch_assoc($q); + if ($r) + { // Record already exists, will update it + $sql = 'UPDATE ' . + $rc->db->table_name($this->table) . + ' SET enabled = ?, label = ?, host = ?, secure = ?, port = ?, username = ?, password = ?, delimiter = ?, user_id = ?, iid = ?'; + } + else + { // No record exists, create new one + $sql = 'INSERT INTO ' . + $rc->db->table_name($this->table) . + '(enabled, label, host, secure, port, username, password, delimiter, user_id, iid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + } + + ob_start(); + var_dump($args); + error_log(ob_get_contents()); + ob_end_clean(); + + $rc->db->query( + $sql, + get_input_value('_ident_switch_form_enabled', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_label', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_host', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_secure', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_port', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_username', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_password', RCUBE_INPUT_POST), + get_input_value('_ident_switch_form_delimiter', RCUBE_INPUT_POST), + $rc->user->ID, + $args['id'] + ); + + return $args; + } + + function on_identity_delete($args) + { + $rc = rcmail::get_instance(); + + $sql = 'DELETE FROM ' . $rc->db->table_name($this->table) . ' WHERE iid = ? AND user_id = ?'; + $rc->db->query($sql, $args['id'], $rc->user->ID); + + // TODO: Affected rows count for log + + return $args; + } + + function on_switch() + { + $rc = rcmail::get_instance(); + + $identId = rcube_utils::get_input_value('_ident-id', RCUBE_INPUT_POST); + error_log($identId); + + if (-1 == $identId) + { // Switch to main account + } + else + { + $sql = 'SELECT server, username, password FROM ' . $rc->db->table_name($this->table) . ' WHERE id = ? AND user_id = ?'; + $q = $rc->db->query($sql, $identId ,$rc->user->ID); + $r = $rc->db->fetch_assoc($q); + if (is_array($r)) + { + // Parse server URI + $pth = parse_url($r['server']); + $ssl = false; + if ($pth['scheme'] == 'ssl' || $pth['scheme'] == 'tls') + { + $ssl = true; + if (!$pth['port']) + $pth['port'] = 993; // Default SSL/TLS port here! + } + if (!$pth['port']) + $pth['port'] = 143; // Default port here! + + $_SESSION['storage_host'] = $pth['host'] ? $pth['host'] : $pth['path']; + $_SESSION['storage_ssl'] = $ssl; + $_SESSION['storage_port'] = $pth['port']; + $_SESSION['username'] = $r['username']; + $_SESSION['password'] = $r['password']; + + $rc->session->remove('folders'); + $rc->output->redirect( + array( + '_task' => 'mail', + '_mbox' => rcube_utils::get_input_value('_mbox', RCUBE_INPUT_GET), + ) + ); + } + else + { + // TODO: Show message in browser + error_log('Requested account not found!'); + } + } + } +} diff --git a/localization/en_US.inc b/localization/en_US.inc new file mode 100644 index 0000000..387e654 --- /dev/null +++ b/localization/en_US.inc @@ -0,0 +1,26 @@ +