Copy plugins into 0.4.1 release branch
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Additional Message Headers |
| | | * |
| | | * Very simple plugin which will add additional headers |
| | | * to or remove them from outgoing messages. |
| | | * |
| | | * Enable the plugin in config/main.inc.php and add your desired headers: |
| | | * $rcmail_config['additional_message_headers'] = array('User-Agent'); |
| | | * |
| | | * @version @package_version@ |
| | | * @author Ziba Scott |
| | | * @website http://roundcube.net |
| | | */ |
| | | class additional_message_headers extends rcube_plugin |
| | | { |
| | | public $task = 'mail'; |
| | | |
| | | function init() |
| | | { |
| | | $this->add_hook('message_outgoing_headers', array($this, 'message_headers')); |
| | | } |
| | | |
| | | function message_headers($args) |
| | | { |
| | | $this->load_config(); |
| | | |
| | | // additional email headers |
| | | $additional_headers = rcmail::get_instance()->config->get('additional_message_headers',array()); |
| | | foreach($additional_headers as $header=>$value){ |
| | | if (null === $value) { |
| | | unset($args['headers'][$header]); |
| | | } else { |
| | | $args['headers'][$header] = $value; |
| | | } |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 |
| | | http://pear.php.net/dtd/tasks-1.0.xsd |
| | | http://pear.php.net/dtd/package-2.0 |
| | | http://pear.php.net/dtd/package-2.0.xsd"> |
| | | <name>additional_message_headers</name> |
| | | <channel>pear.roundcube.net</channel> |
| | | <summary>Additional message headers for Roundcube</summary> |
| | | <description>Very simple plugin which will add additional headers to or remove them from outgoing messages.</description> |
| | | <lead> |
| | | <name>Ziba Scott</name> |
| | | <user>ziba</user> |
| | | <email>email@example.org</email> |
| | | <active>yes</active> |
| | | </lead> |
| | | <date>2010-01-16</date> |
| | | <time>18:19:33</time> |
| | | <version> |
| | | <release>1.1.0</release> |
| | | <api>1.1.0</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPL v2</license> |
| | | <notes>-</notes> |
| | | <contents> |
| | | <dir baseinstalldir="/" name="/"> |
| | | <file name="additional_message_headers.php" role="php"> |
| | | <tasks:replace from="@name@" to="name" type="package-info" /> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info" /> |
| | | </file> |
| | | </dir> <!-- / --> |
| | | </contents> |
| | | <dependencies> |
| | | <required> |
| | | <php> |
| | | <min>5.2.1</min> |
| | | </php> |
| | | <pearinstaller> |
| | | <min>1.7.0</min> |
| | | </pearinstaller> |
| | | </required> |
| | | </dependencies> |
| | | <phprelease /> |
| | | </package> |
New file |
| | |
| | | /* |
| | | * Archive plugin script |
| | | * @version @package_version@ |
| | | */ |
| | | |
| | | function rcmail_archive(prop) |
| | | { |
| | | if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length)) |
| | | return; |
| | | |
| | | var uids = rcmail.env.uid ? rcmail.env.uid : rcmail.message_list.get_selection().join(','); |
| | | |
| | | rcmail.set_busy(true, 'loading'); |
| | | rcmail.http_post('plugin.archive', '_uid='+uids+'&_mbox='+urlencode(rcmail.env.mailbox), true); |
| | | } |
| | | |
| | | // callback for app-onload event |
| | | if (window.rcmail) { |
| | | rcmail.addEventListener('init', function(evt) { |
| | | |
| | | // register command (directly enable in message view mode) |
| | | rcmail.register_command('plugin.archive', rcmail_archive, (rcmail.env.uid && rcmail.env.mailbox != rcmail.env.archive_folder)); |
| | | |
| | | // add event-listener to message list |
| | | if (rcmail.message_list) |
| | | rcmail.message_list.addEventListener('select', function(list){ |
| | | rcmail.enable_command('plugin.archive', (list.get_selection().length > 0 && rcmail.env.mailbox != rcmail.env.archive_folder)); |
| | | }); |
| | | |
| | | // set css style for archive folder |
| | | var li; |
| | | if (rcmail.env.archive_folder && rcmail.env.archive_folder_icon && (li = rcmail.get_folder_li(rcmail.env.archive_folder))) |
| | | $(li).css('background-image', 'url(' + rcmail.env.archive_folder_icon + ')'); |
| | | }) |
| | | } |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Archive |
| | | * |
| | | * Plugin that adds a new button to the mailbox toolbar |
| | | * to move messages to a (user selectable) archive folder. |
| | | * |
| | | * @version @package_version@ |
| | | * @author Andre Rodier, Thomas Bruederli |
| | | */ |
| | | class archive extends rcube_plugin |
| | | { |
| | | public $task = 'mail|settings'; |
| | | |
| | | function init() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $this->register_action('plugin.archive', array($this, 'request_action')); |
| | | |
| | | // There is no "Archived flags" |
| | | // $GLOBALS['IMAP_FLAGS']['ARCHIVED'] = 'Archive'; |
| | | if ($rcmail->task == 'mail' && ($rcmail->action == '' || $rcmail->action == 'show') |
| | | && ($archive_folder = $rcmail->config->get('archive_mbox'))) { |
| | | $skin_path = $this->local_skin_path(); |
| | | |
| | | $this->include_script('archive.js'); |
| | | $this->add_texts('localization', true); |
| | | $this->add_button( |
| | | array( |
| | | 'command' => 'plugin.archive', |
| | | 'imagepas' => $skin_path.'/archive_pas.png', |
| | | 'imageact' => $skin_path.'/archive_act.png', |
| | | 'title' => 'buttontitle', |
| | | 'domain' => $this->ID, |
| | | ), |
| | | 'toolbar'); |
| | | |
| | | // register hook to localize the archive folder |
| | | $this->add_hook('render_mailboxlist', array($this, 'render_mailboxlist')); |
| | | |
| | | // set env variable for client |
| | | $rcmail->output->set_env('archive_folder', $archive_folder); |
| | | $rcmail->output->set_env('archive_folder_icon', $this->url($skin_path.'/foldericon.png')); |
| | | |
| | | // add archive folder to the list of default mailboxes |
| | | if (($default_folders = $rcmail->config->get('default_imap_folders')) && !in_array($archive_folder, $default_folders)) { |
| | | $default_folders[] = $archive_folder; |
| | | $rcmail->config->set('default_imap_folders', $default_folders); |
| | | } |
| | | } |
| | | else if ($rcmail->task == 'settings') { |
| | | $dont_override = $rcmail->config->get('dont_override', array()); |
| | | if (!in_array('archive_mbox', $dont_override)) { |
| | | $this->add_hook('preferences_list', array($this, 'prefs_table')); |
| | | $this->add_hook('preferences_save', array($this, 'save_prefs')); |
| | | } |
| | | } |
| | | } |
| | | |
| | | function render_mailboxlist($p) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $archive_folder = $rcmail->config->get('archive_mbox'); |
| | | |
| | | // set localized name for the configured archive folder |
| | | if ($archive_folder) { |
| | | if (isset($p['list'][$archive_folder])) |
| | | $p['list'][$archive_folder]['name'] = $this->gettext('archivefolder'); |
| | | else // search in subfolders |
| | | $this->_mod_folder_name($p['list'], $archive_folder, $this->gettext('archivefolder')); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | function _mod_folder_name(&$list, $folder, $new_name) |
| | | { |
| | | foreach ($list as $idx => $item) { |
| | | if ($item['id'] == $folder) { |
| | | $list[$idx]['name'] = $new_name; |
| | | return true; |
| | | } else if (!empty($item['folders'])) |
| | | if ($this->_mod_folder_name($list[$idx]['folders'], $folder, $new_name)) |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | function request_action() |
| | | { |
| | | $this->add_texts('localization'); |
| | | |
| | | $uids = get_input_value('_uid', RCUBE_INPUT_POST); |
| | | $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // There is no "Archive flags", but I left this line in case it may be useful |
| | | // $rcmail->imap->set_flag($uids, 'ARCHIVE'); |
| | | |
| | | if (($archive_mbox = $rcmail->config->get('archive_mbox')) && $mbox != $archive_mbox) { |
| | | $rcmail->output->command('move_messages', $archive_mbox); |
| | | $rcmail->output->command('display_message', $this->gettext('archived'), 'confirmation'); |
| | | } |
| | | |
| | | $rcmail->output->send(); |
| | | } |
| | | |
| | | function prefs_table($args) |
| | | { |
| | | global $CURR_SECTION; |
| | | |
| | | if ($args['section'] == 'folders') { |
| | | $this->add_texts('localization'); |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // load folders list when needed |
| | | if ($CURR_SECTION) |
| | | $select = rcmail_mailbox_select(array('noselection' => '---', 'realnames' => true, |
| | | 'maxlength' => 30, 'exceptions' => array('INBOX'))); |
| | | else |
| | | $select = new html_select(); |
| | | |
| | | $args['blocks']['main']['options']['archive_mbox'] = array( |
| | | 'title' => $this->gettext('archivefolder'), |
| | | 'content' => $select->show($rcmail->config->get('archive_mbox'), array('name' => "_archive_mbox")) |
| | | ); |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | function save_prefs($args) |
| | | { |
| | | if ($args['section'] == 'folders') { |
| | | $args['prefs']['archive_mbox'] = get_input_value('_archive_mbox', RCUBE_INPUT_POST); |
| | | return $args; |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | language/cs_CZ/labels.inc | |
| | | | | |
| | | | Language file of the Roundcube archive plugin | |
| | | | Copyright (C) 2005-2009, Roundcube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Milan Kozak <hodza@hodza.net> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | @version $Id: labels.inc 2993 2009-09-26 18:32:07Z alec $ |
| | | |
| | | */ |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archivovat zprávu'; |
| | | $labels['archived'] = 'Úspěšně vloženo do archivu'; |
| | | $labels['archivefolder'] = 'Archiv'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Nachricht archivieren'; |
| | | $labels['archived'] = 'Nachricht erfolgreich archiviert'; |
| | | $labels['archivefolder'] = 'Archiv'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Nachricht archivieren'; |
| | | $labels['archived'] = 'Nachricht erfolgreich archiviert'; |
| | | $labels['archivefolder'] = 'Archiv'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archive this message'; |
| | | $labels['archived'] = 'Successfully archived'; |
| | | $labels['archivefolder'] = 'Archive'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // MPBAUPGRADE |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archivar este mensaje'; |
| | | $labels['archived'] = 'Mensaje Archivado'; |
| | | $labels['archivefolder'] = 'Archivo'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // MPBAUPGRADE |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archivar este mensaje'; |
| | | $labels['archived'] = 'Mensaje Archivado'; |
| | | $labels['archivefolder'] = 'Archivo'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Arhiveeri see kiri'; |
| | | $labels['archived'] = 'Edukalt arhiveeritud'; |
| | | $labels['archivefolder'] = 'Arhiveeri'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archiver ce message'; |
| | | $labels['archived'] = 'Message archivé avec success'; |
| | | $labels['archivefolder'] = 'Archive'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // EN-Revision: 3891 |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'このメッセージのアーカイブ'; |
| | | $labels['archived'] = 'アーカイブに成功しました。'; |
| | | $labels['archivefolder'] = 'アーカイブ'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Archiveer dit bericht'; |
| | | $labels['archived'] = 'Succesvol gearchiveerd'; |
| | | $labels['archivefolder'] = 'Archief'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Przenieś do archiwum'; |
| | | $labels['archived'] = 'Pomyślnie zarchiwizowano'; |
| | | $labels['archivefolder'] = 'Archiwum'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Переместить выбранное в архив'; |
| | | $labels['archived'] = 'Перенесено в Архив'; |
| | | $labels['archivefolder'] = 'Архив'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Arkivera meddelande'; |
| | | $labels['archived'] = 'Meddelandet är arkiverat'; |
| | | $labels['archivefolder'] = 'Arkiv'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = '封存此信件'; |
| | | $labels['archived'] = '已成功封存'; |
| | | $labels['archivefolder'] = '封存'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 |
| | | http://pear.php.net/dtd/tasks-1.0.xsd |
| | | http://pear.php.net/dtd/package-2.0 |
| | | http://pear.php.net/dtd/package-2.0.xsd"> |
| | | <name>archive</name> |
| | | <channel>pear.roundcube.net</channel> |
| | | <summary>Archive feature for Roundcube</summary> |
| | | <description>This adds a button to move the selected messages to an archive folder. The folder can be selected in the settings panel.</description> |
| | | <lead> |
| | | <name>Thomas Bruederli</name> |
| | | <user>thomasb</user> |
| | | <email>roundcube@gmail.com</email> |
| | | <active>yes</active> |
| | | </lead> |
| | | <date>2010-02-06</date> |
| | | <time>12:12:00</time> |
| | | <version> |
| | | <release>1.4</release> |
| | | <api>1.4</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license> |
| | | <notes>-</notes> |
| | | <contents> |
| | | <dir baseinstalldir="/" name="/"> |
| | | <file name="archive.php" role="php"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <file name="archive.js" role="data"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <file name="localization/en_US.inc" role="data"></file> |
| | | <file name="localization/cs_CZ.inc" role="data"></file> |
| | | <file name="localization/de_CH.inc" role="data"></file> |
| | | <file name="localization/de_DE.inc" role="data"></file> |
| | | <file name="localization/et_EE.inc" role="data"></file> |
| | | <file name="localization/fr_FR.inc" role="data"></file> |
| | | <file name="localization/pl_PL.inc" role="data"></file> |
| | | <file name="localization/ru_RU.inc" role="data"></file> |
| | | <file name="localization/zh_TW.inc" role="data"></file> |
| | | <file name="skins/default/archive_act.png" role="data"></file> |
| | | <file name="skins/default/archive_pas.png" role="data"></file> |
| | | <file name="skins/default/foldericon.png" role="data"></file> |
| | | </dir> |
| | | <!-- / --> |
| | | </contents> |
| | | <dependencies> |
| | | <required> |
| | | <php> |
| | | <min>5.2.1</min> |
| | | </php> |
| | | <pearinstaller> |
| | | <min>1.7.0</min> |
| | | </pearinstaller> |
| | | </required> |
| | | </dependencies> |
| | | <phprelease/> |
| | | </package> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Sample plugin to try out some hooks. |
| | | * This performs an automatic login if accessed from localhost |
| | | */ |
| | | class autologon extends rcube_plugin |
| | | { |
| | | public $task = 'login'; |
| | | |
| | | function init() |
| | | { |
| | | $this->add_hook('startup', array($this, 'startup')); |
| | | $this->add_hook('authenticate', array($this, 'authenticate')); |
| | | } |
| | | |
| | | function startup($args) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // change action to login |
| | | if (empty($_SESSION['user_id']) && !empty($_GET['_autologin']) && $this->is_localhost()) |
| | | $args['action'] = 'login'; |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | function authenticate($args) |
| | | { |
| | | if (!empty($_GET['_autologin']) && $this->is_localhost()) { |
| | | $args['user'] = 'me'; |
| | | $args['pass'] = '******'; |
| | | $args['host'] = 'localhost'; |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | function is_localhost() |
| | | { |
| | | return $_SERVER['REMOTE_ADDR'] == '::1' || $_SERVER['REMOTE_ADDR'] == '127.0.0.1'; |
| | | } |
| | | |
| | | } |
| | | |
New file |
| | |
| | | <?php |
| | | /** |
| | | * Filesystem Attachments |
| | | * |
| | | * This plugin which provides database backed storage for temporary |
| | | * attachment file handling. The primary advantage of this plugin |
| | | * is its compatibility with round-robin dns multi-server roundcube |
| | | * installations. |
| | | * |
| | | * This plugin relies on the core filesystem_attachments plugin |
| | | * |
| | | * @author Ziba Scott <ziba@umich.edu> |
| | | * |
| | | */ |
| | | require_once('plugins/filesystem_attachments/filesystem_attachments.php'); |
| | | class database_attachments extends filesystem_attachments |
| | | { |
| | | |
| | | // A prefix for the cache key used in the session and in the key field of the cache table |
| | | private $cache_prefix = "db_attach"; |
| | | |
| | | /** |
| | | * Helper method to generate a unique key for the given attachment file |
| | | */ |
| | | private function _key($filepath) |
| | | { |
| | | return $this->cache_prefix.md5(mktime().$filepath.$_SESSION['user_id']); |
| | | } |
| | | |
| | | /** |
| | | * Save a newly uploaded attachment |
| | | */ |
| | | function upload($args) |
| | | { |
| | | $args['status'] = false; |
| | | $rcmail = rcmail::get_instance(); |
| | | $key = $this->_key($args['path']); |
| | | $data = base64_encode(file_get_contents($args['path'])); |
| | | |
| | | $status = $rcmail->db->query( |
| | | "INSERT INTO ".get_table_name('cache')." |
| | | (created, user_id, cache_key, data) |
| | | VALUES (".$rcmail->db->now().", ?, ?, ?)", |
| | | $_SESSION['user_id'], |
| | | $key, |
| | | $data); |
| | | |
| | | if ($status) { |
| | | $args['id'] = $key; |
| | | $args['status'] = true; |
| | | unset($args['path']); |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Save an attachment from a non-upload source (draft or forward) |
| | | */ |
| | | function save($args) |
| | | { |
| | | $args['status'] = false; |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $key = $this->_key($args['name']); |
| | | |
| | | if ($args['path']) |
| | | $args['data'] = file_get_contents($args['path']); |
| | | |
| | | $data = base64_encode($args['data']); |
| | | |
| | | $status = $rcmail->db->query( |
| | | "INSERT INTO ".get_table_name('cache')." |
| | | (created, user_id, cache_key, data) |
| | | VALUES (".$rcmail->db->now().", ?, ?, ?)", |
| | | $_SESSION['user_id'], |
| | | $key, |
| | | $data); |
| | | |
| | | if ($status) { |
| | | $args['id'] = $key; |
| | | $args['status'] = true; |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Remove an attachment from storage |
| | | * This is triggered by the remove attachment button on the compose screen |
| | | */ |
| | | function remove($args) |
| | | { |
| | | $args['status'] = false; |
| | | $rcmail = rcmail::get_instance(); |
| | | $status = $rcmail->db->query( |
| | | "DELETE FROM ".get_table_name('cache')." |
| | | WHERE user_id=? |
| | | AND cache_key=?", |
| | | $_SESSION['user_id'], |
| | | $args['id']); |
| | | |
| | | if ($status) { |
| | | $args['status'] = true; |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * When composing an html message, image attachments may be shown |
| | | * For this plugin, $this->get() will check the file and |
| | | * return it's contents |
| | | */ |
| | | function display($args) |
| | | { |
| | | return $this->get($args); |
| | | } |
| | | |
| | | /** |
| | | * When displaying or sending the attachment the file contents are fetched |
| | | * using this method. This is also called by the attachment_display hook. |
| | | */ |
| | | function get($args) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $sql_result = $rcmail->db->query( |
| | | "SELECT cache_id, data |
| | | FROM ".get_table_name('cache')." |
| | | WHERE user_id=? |
| | | AND cache_key=?", |
| | | $_SESSION['user_id'], |
| | | $args['id']); |
| | | |
| | | if ($sql_arr = $rcmail->db->fetch_assoc($sql_result)) { |
| | | $args['data'] = base64_decode($sql_arr['data']); |
| | | $args['status'] = true; |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Delete all temp files associated with this user |
| | | */ |
| | | function cleanup($args) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail->db->query( |
| | | "DELETE FROM ".get_table_name('cache')." |
| | | WHERE user_id=? |
| | | AND cache_key like '{$this->cache_prefix}%'", |
| | | $_SESSION['user_id']); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Debug Logger |
| | | * |
| | | * Enhanced logging for debugging purposes. It is not recommened |
| | | * to be enabled on production systems without testing because of |
| | | * the somewhat increased memory, cpu and disk i/o overhead. |
| | | * |
| | | * Debug Logger listens for existing console("message") calls and |
| | | * introduces start and end tags as well as free form tagging |
| | | * which can redirect messages to files. The resulting log files |
| | | * provide timing and tag quantity results. |
| | | * |
| | | * Enable the plugin in config/main.inc.php and add your desired |
| | | * log types and files. |
| | | * |
| | | * @version 1.0 |
| | | * @author Ziba Scott |
| | | * @website http://roundcube.net |
| | | * |
| | | * Example: |
| | | * |
| | | * config/main.inc.php: |
| | | * |
| | | * // $rcmail_config['debug_logger'][type of logging] = name of file in log_dir |
| | | * // The 'master' log includes timing information |
| | | * $rcmail_config['debug_logger']['master'] = 'master'; |
| | | * // If you want sql messages to also go into a separate file |
| | | * $rcmail_config['debug_logger']['sql'] = 'sql'; |
| | | * |
| | | * index.php (just after $RCMAIL->plugins->init()): |
| | | * |
| | | * console("my test","start"); |
| | | * console("my message"); |
| | | * console("my sql calls","start"); |
| | | * console("cp -r * /dev/null","shell exec"); |
| | | * console("select * from example","sql"); |
| | | * console("select * from example","sql"); |
| | | * console("select * from example","sql"); |
| | | * console("end"); |
| | | * console("end"); |
| | | * |
| | | * |
| | | * logs/master (after reloading the main page): |
| | | * |
| | | * [17-Feb-2009 16:51:37 -0500] start: Task: mail. |
| | | * [17-Feb-2009 16:51:37 -0500] start: my test |
| | | * [17-Feb-2009 16:51:37 -0500] my message |
| | | * [17-Feb-2009 16:51:37 -0500] shell exec: cp -r * /dev/null |
| | | * [17-Feb-2009 16:51:37 -0500] start: my sql calls |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | * [17-Feb-2009 16:51:37 -0500] end: my sql calls - 0.0018 seconds shell exec: 1, sql: 3, |
| | | * [17-Feb-2009 16:51:37 -0500] end: my test - 0.0055 seconds shell exec: 1, sql: 3, |
| | | * [17-Feb-2009 16:51:38 -0500] end: Task: mail. - 0.8854 seconds shell exec: 1, sql: 3, |
| | | * |
| | | * logs/sql (after reloading the main page): |
| | | * |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | * [17-Feb-2009 16:51:37 -0500] sql: select * from example |
| | | */ |
| | | class debug_logger extends rcube_plugin |
| | | { |
| | | function init() |
| | | { |
| | | require_once(dirname(__FILE__).'/runlog/runlog.php'); |
| | | $this->runlog = new runlog(); |
| | | |
| | | if(!rcmail::get_instance()->config->get('log_dir')){ |
| | | rcmail::get_instance()->config->set('log_dir',INSTALL_PATH.'logs'); |
| | | } |
| | | |
| | | $log_config = rcmail::get_instance()->config->get('debug_logger',array()); |
| | | |
| | | foreach($log_config as $type=>$file){ |
| | | $this->runlog->set_file(rcmail::get_instance()->config->get('log_dir').'/'.$file, $type); |
| | | } |
| | | |
| | | $start_string = ""; |
| | | $action = rcmail::get_instance()->action; |
| | | $task = rcmail::get_instance()->task; |
| | | if($action){ |
| | | $start_string .= "Action: ".$action.". "; |
| | | } |
| | | if($task){ |
| | | $start_string .= "Task: ".$task.". "; |
| | | } |
| | | $this->runlog->start($start_string); |
| | | |
| | | $this->add_hook('console', array($this, 'console')); |
| | | $this->add_hook('authenticate', array($this, 'authenticate')); |
| | | } |
| | | |
| | | function authenticate($args){ |
| | | $this->runlog->note('Authenticating '.$args['user'].'@'.$args['host']); |
| | | return $args; |
| | | } |
| | | |
| | | function console($args){ |
| | | $note = $args[0]; |
| | | $type = $args[1]; |
| | | |
| | | |
| | | if(!isset($args[1])){ |
| | | // This could be extended to detect types based on the |
| | | // file which called console. For now only rcube_imap.inc is supported |
| | | $bt = debug_backtrace(); |
| | | $file = $bt[3]['file']; |
| | | switch(basename($file)){ |
| | | case 'rcube_imap.php': |
| | | $type = 'imap'; |
| | | break; |
| | | default: |
| | | $type = FALSE; |
| | | break; |
| | | } |
| | | } |
| | | switch($note){ |
| | | case 'end': |
| | | $type = 'end'; |
| | | break; |
| | | } |
| | | |
| | | |
| | | switch($type){ |
| | | case 'start': |
| | | $this->runlog->start($note); |
| | | break; |
| | | case 'end': |
| | | $this->runlog->end(); |
| | | break; |
| | | default: |
| | | $this->runlog->note($note, $type); |
| | | break; |
| | | } |
| | | return $args; |
| | | } |
| | | |
| | | function __destruct(){ |
| | | $this->runlog->end(); |
| | | } |
| | | } |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * runlog |
| | | * |
| | | * @author Ziba Scott <ziba@umich.edu> |
| | | */ |
| | | class runlog { |
| | | |
| | | private $start_time = FALSE; |
| | | |
| | | private $parent_stack = array(); |
| | | |
| | | public $print_to_console = FALSE; |
| | | |
| | | private $file_handles = array(); |
| | | |
| | | private $indent = 0; |
| | | |
| | | public $threshold = 0; |
| | | |
| | | public $tag_count = array(); |
| | | |
| | | public $timestamp = "d-M-Y H:i:s O"; |
| | | |
| | | public $max_line_size = 150; |
| | | |
| | | private $run_log = array(); |
| | | |
| | | function runlog() |
| | | { |
| | | $this->start_time = microtime( TRUE ); |
| | | } |
| | | |
| | | public function start( $name, $tag = FALSE ) |
| | | { |
| | | $this->run_log[] = array( 'type' => 'start', |
| | | 'tag' => $tag, |
| | | 'index' => count($this->run_log), |
| | | 'value' => $name, |
| | | 'time' => microtime( TRUE ), |
| | | 'parents' => $this->parent_stack, |
| | | 'ended' => false, |
| | | ); |
| | | $this->parent_stack[] = $name; |
| | | |
| | | $this->print_to_console("start: ".$name, $tag, 'start'); |
| | | $this->print_to_file("start: ".$name, $tag, 'start'); |
| | | $this->indent++; |
| | | } |
| | | |
| | | public function end() |
| | | { |
| | | $name = array_pop( $this->parent_stack ); |
| | | foreach ( $this->run_log as $k => $entry ) { |
| | | if ( $entry['value'] == $name && $entry['type'] == 'start' && $entry['ended'] == false) { |
| | | $lastk = $k; |
| | | } |
| | | } |
| | | $start = $this->run_log[$lastk]['time']; |
| | | $this->run_log[$lastk]['duration'] = microtime( TRUE ) - $start; |
| | | $this->run_log[$lastk]['ended'] = true; |
| | | |
| | | $this->run_log[] = array( 'type' => 'end', |
| | | 'tag' => $this->run_log[$lastk]['tag'], |
| | | 'index' => $lastk, |
| | | 'value' => $name, |
| | | 'time' => microtime( TRUE ), |
| | | 'duration' => microtime( TRUE ) - $start, |
| | | 'parents' => $this->parent_stack, |
| | | ); |
| | | $this->indent--; |
| | | if($this->run_log[$lastk]['duration'] >= $this->threshold){ |
| | | $tag_report = ""; |
| | | foreach($this->tag_count as $tag=>$count){ |
| | | $tag_report .= "$tag: $count, "; |
| | | } |
| | | if(!empty($tag_report)){ |
| | | // $tag_report = "\n$tag_report\n"; |
| | | } |
| | | $end_txt = sprintf("end: $name - %0.4f seconds $tag_report", $this->run_log[$lastk]['duration'] ); |
| | | $this->print_to_console($end_txt, $this->run_log[$lastk]['tag'] , 'end'); |
| | | $this->print_to_file($end_txt, $this->run_log[$lastk]['tag'], 'end'); |
| | | } |
| | | } |
| | | |
| | | public function increase_tag_count($tag){ |
| | | if(!isset($this->tag_count[$tag])){ |
| | | $this->tag_count[$tag] = 0; |
| | | } |
| | | $this->tag_count[$tag]++; |
| | | } |
| | | |
| | | public function get_text(){ |
| | | $text = ""; |
| | | foreach($this->run_log as $entry){ |
| | | $text .= str_repeat(" ",count($entry['parents'])); |
| | | if($entry['tag'] != 'text'){ |
| | | $text .= $entry['tag'].': '; |
| | | } |
| | | $text .= $entry['value']; |
| | | |
| | | if($entry['tag'] == 'end'){ |
| | | $text .= sprintf(" - %0.4f seconds", $entry['duration'] ); |
| | | } |
| | | |
| | | $text .= "\n"; |
| | | } |
| | | return $text; |
| | | } |
| | | |
| | | public function set_file($filename, $tag = 'master'){ |
| | | if(!isset($this->file_handle[$tag])){ |
| | | $this->file_handles[$tag] = fopen($filename, 'a'); |
| | | if(!$this->file_handles[$tag]){ |
| | | trigger_error('Could not open file for writing: '.$filename); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public function note( $msg, $tag = FALSE ) |
| | | { |
| | | if($tag){ |
| | | $this->increase_tag_count($tag); |
| | | } |
| | | if ( is_array( $msg )) { |
| | | $msg = '<pre>' . print_r( $msg, TRUE ) . '</pre>'; |
| | | } |
| | | $this->debug_messages[] = $msg; |
| | | $this->run_log[] = array( 'type' => 'note', |
| | | 'tag' => $tag ? $tag:"text", |
| | | 'value' => htmlentities($msg), |
| | | 'time' => microtime( TRUE ), |
| | | 'parents' => $this->parent_stack, |
| | | ); |
| | | |
| | | $this->print_to_file($msg, $tag); |
| | | $this->print_to_console($msg, $tag); |
| | | |
| | | } |
| | | |
| | | public function print_to_file($msg, $tag = FALSE, $type = FALSE){ |
| | | if(!$tag){ |
| | | $file_handle_tag = 'master'; |
| | | } |
| | | else{ |
| | | $file_handle_tag = $tag; |
| | | } |
| | | if($file_handle_tag != 'master' && isset($this->file_handles[$file_handle_tag])){ |
| | | $buffer = $this->get_indent(); |
| | | $buffer .= "$msg\n"; |
| | | if(!empty($this->timestamp)){ |
| | | $buffer = sprintf("[%s] %s",date($this->timestamp, mktime()), $buffer); |
| | | } |
| | | fwrite($this->file_handles[$file_handle_tag], wordwrap($buffer, $this->max_line_size, "\n ")); |
| | | } |
| | | if(isset($this->file_handles['master']) && $this->file_handles['master']){ |
| | | $buffer = $this->get_indent(); |
| | | if($tag){ |
| | | $buffer .= "$tag: "; |
| | | } |
| | | $msg = str_replace("\n","",$msg); |
| | | $buffer .= "$msg"; |
| | | if(!empty($this->timestamp)){ |
| | | $buffer = sprintf("[%s] %s",date($this->timestamp, mktime()), $buffer); |
| | | } |
| | | if(strlen($buffer) > $this->max_line_size){ |
| | | $buffer = substr($buffer,0,$this->max_line_size - 3)."..."; |
| | | } |
| | | fwrite($this->file_handles['master'], $buffer."\n"); |
| | | } |
| | | } |
| | | |
| | | public function print_to_console($msg, $tag=FALSE){ |
| | | if($this->print_to_console){ |
| | | if(is_array($this->print_to_console)){ |
| | | if(in_array($tag, $this->print_to_console)){ |
| | | echo $this->get_indent(); |
| | | if($tag){ |
| | | echo "$tag: "; |
| | | } |
| | | echo "$msg\n"; |
| | | } |
| | | } |
| | | else{ |
| | | echo $this->get_indent(); |
| | | if($tag){ |
| | | echo "$tag: "; |
| | | } |
| | | echo "$msg\n"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | public function print_totals(){ |
| | | $totals = array(); |
| | | foreach ( $this->run_log as $k => $entry ) { |
| | | if ( $entry['type'] == 'start' && $entry['ended'] == true) { |
| | | $totals[$entry['value']]['duration'] += $entry['duration']; |
| | | $totals[$entry['value']]['count'] += 1; |
| | | } |
| | | } |
| | | if($this->file_handle){ |
| | | foreach($totals as $name=>$details){ |
| | | fwrite($this->file_handle,$name.": ".number_format($details['duration'],4)."sec, ".$details['count']." calls \n"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private function get_indent(){ |
| | | $buf = ""; |
| | | for($i = 0; $i < $this->indent; $i++){ |
| | | $buf .= " "; |
| | | } |
| | | return $buf; |
| | | } |
| | | |
| | | |
| | | function __destruct(){ |
| | | foreach($this->file_handles as $handle){ |
| | | fclose($handle); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Display Emoticons |
| | | * |
| | | * Sample plugin to replace emoticons in plain text message body with real icons |
| | | * |
| | | * @version 1.1.0 |
| | | * @author Thomas Bruederli |
| | | * @author Aleksander Machniak |
| | | * @website http://roundcube.net |
| | | */ |
| | | class emoticons extends rcube_plugin |
| | | { |
| | | public $task = 'mail'; |
| | | private $map; |
| | | |
| | | function init() |
| | | { |
| | | $this->task = 'mail'; |
| | | $this->add_hook('message_part_after', array($this, 'replace')); |
| | | |
| | | $this->map = array( |
| | | '/:\)/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-smile.gif', 'title' => ':)')), |
| | | '/:-\)/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-smile.gif', 'title' => ':-)')), |
| | | '/(?<!mailto):D/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-laughing.gif', 'title' => ':D')), |
| | | '/:-D/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-laughing.gif', 'title' => ':-D')), |
| | | '/;\)/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-wink.gif', 'title' => ';)')), |
| | | '/;-\)/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-wink.gif', 'title' => ';-)')), |
| | | '/:\(/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-frown.gif', 'title' => ':(')), |
| | | '/:-\(/' => html::img(array('src' => './program/js/tiny_mce/plugins/emotions/img/smiley-frown.gif', 'title' => ':-(')), |
| | | ); |
| | | } |
| | | |
| | | function replace($args) |
| | | { |
| | | if ($args['type'] == 'plain') { |
| | | $args['body'] = preg_replace( |
| | | array_keys($this->map), array_values($this->map), $args['body']); |
| | | } |
| | | return $args; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | ------------------------------------------------------------------ |
| | | THIS IS NOT EVEN AN "ALPHA" STATE. USE ONLY FOR DEVELOPMENT!!!!!!! |
| | | ------------------------------------------------------------------ |
| | | |
| | | Enigma Plugin Status: |
| | | |
| | | * DONE: |
| | | |
| | | - PGP signed messages verification |
| | | - Handling of PGP keys files attached to incoming messages |
| | | - PGP encrypted messages decryption (started) |
| | | - PGP keys management UI (started) |
| | | |
| | | * TODO (must have): |
| | | |
| | | - Parsing of decrypted messages into array (see rcube_mime_struct) and then into rcube_message_part structure |
| | | (create core class rcube_mime_parser or take over PEAR::Mail_mimeDecode package and improve it) |
| | | - Sending encrypted/signed messages (probably some changes in core will be needed) |
| | | - Per-Identity settings (including keys/certs) (+ split Identities details page into tabs) |
| | | - Handling big messages with temp files (including changes in Roundcube core) |
| | | - Performance improvements (some caching, code review) |
| | | - better (and more) icons |
| | | |
| | | * TODO (later): |
| | | |
| | | - Keys generation |
| | | - Certs generation |
| | | - Keys/Certs info in Contacts details page (+ split Contact details page into tabs) |
| | | - Key server support |
| | | - S/MIME signed messages verification |
| | | - S/MIME encrypted messages decryption |
| | | - Handling of S/MIME certs files attached to incoming messages |
| | | - SSL (S/MIME) Certs management |
New file |
| | |
| | | <?php |
| | | |
| | | // Enigma Plugin options |
| | | // -------------------- |
| | | |
| | | // A driver to use for PGP. Default: "gnupg". |
| | | $rcmail_config['enigma_pgp_driver'] = 'gnupg'; |
| | | |
| | | // A driver to use for S/MIME. Default: "phpssl". |
| | | $rcmail_config['enigma_smime_driver'] = 'phpssl'; |
| | | |
| | | // Keys directory for all users. Default 'enigma/home'. |
| | | // Must be writeable by PHP process |
| | | $rcmail_config['enigma_pgp_homedir'] = null; |
New file |
| | |
| | | /* Enigma Plugin */ |
| | | |
| | | if (window.rcmail) |
| | | { |
| | | rcmail.addEventListener('init', function(evt) |
| | | { |
| | | if (rcmail.env.task == 'settings') { |
| | | rcmail.register_command('plugin.enigma', function() { rcmail.goto_url('plugin.enigma') }, true); |
| | | rcmail.register_command('plugin.enigma-key-import', function() { rcmail.enigma_key_import() }, true); |
| | | rcmail.register_command('plugin.enigma-key-export', function() { rcmail.enigma_key_export() }, true); |
| | | |
| | | if (rcmail.gui_objects.keyslist) |
| | | { |
| | | var p = rcmail; |
| | | rcmail.keys_list = new rcube_list_widget(rcmail.gui_objects.keyslist, |
| | | {multiselect:false, draggable:false, keyboard:false}); |
| | | rcmail.keys_list.addEventListener('select', function(o){ p.enigma_key_select(o); }); |
| | | rcmail.keys_list.init(); |
| | | rcmail.keys_list.focus(); |
| | | |
| | | rcmail.enigma_list(); |
| | | |
| | | rcmail.register_command('firstpage', function(props) {return rcmail.enigma_list_page('first'); }); |
| | | rcmail.register_command('previouspage', function(props) {return rcmail.enigma_list_page('previous'); }); |
| | | rcmail.register_command('nextpage', function(props) {return rcmail.enigma_list_page('next'); }); |
| | | rcmail.register_command('lastpage', function(props) {return rcmail.enigma_list_page('last'); }); |
| | | } |
| | | |
| | | if (rcmail.env.action == 'edit-prefs') { |
| | | rcmail.register_command('search', function(props) {return rcmail.enigma_search(props); }, true); |
| | | rcmail.register_command('reset-search', function(props) {return rcmail.enigma_search_reset(props); }, true); |
| | | } |
| | | else if (rcmail.env.action == 'plugin.enigma') { |
| | | rcmail.register_command('plugin.enigma-import', function() { rcmail.enigma_import() }, true); |
| | | rcmail.register_command('plugin.enigma-export', function() { rcmail.enigma_export() }, true); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /*********************************************************/ |
| | | /********* Enigma Settings/Keys/Certs UI *********/ |
| | | /*********************************************************/ |
| | | |
| | | // Display key(s) import form |
| | | rcube_webmail.prototype.enigma_key_import = function() |
| | | { |
| | | this.enigma_loadframe(null, '&_a=keyimport'); |
| | | }; |
| | | |
| | | // Submit key(s) form |
| | | rcube_webmail.prototype.enigma_import = function() |
| | | { |
| | | var form, file; |
| | | if (form = this.gui_objects.importform) { |
| | | file = document.getElementById('rcmimportfile'); |
| | | if (file && !file.value) { |
| | | alert(this.get_label('selectimportfile')); |
| | | return; |
| | | } |
| | | form.submit(); |
| | | this.set_busy(true, 'importwait'); |
| | | this.lock_form(form, true); |
| | | } |
| | | }; |
| | | |
| | | // list row selection handler |
| | | rcube_webmail.prototype.enigma_key_select = function(list) |
| | | { |
| | | var id; |
| | | if (id = list.get_single_selection()) |
| | | this.enigma_loadframe(id); |
| | | }; |
| | | |
| | | // load key frame |
| | | rcube_webmail.prototype.enigma_loadframe = function(id, url) |
| | | { |
| | | var frm, win; |
| | | if (this.env.contentframe && window.frames && (frm = window.frames[this.env.contentframe])) { |
| | | if (!id && !url && (win = window.frames[this.env.contentframe])) { |
| | | if (win.location && win.location.href.indexOf(this.env.blankpage)<0) |
| | | win.location.href = this.env.blankpage; |
| | | return; |
| | | } |
| | | this.set_busy(true); |
| | | if (!url) |
| | | url = '&_a=keyinfo&_id='+id; |
| | | frm.location.href = this.env.comm_path+'&_action=plugin.enigma&_framed=1' + url; |
| | | } |
| | | }; |
| | | |
| | | // Search keys/certs |
| | | rcube_webmail.prototype.enigma_search = function(props) |
| | | { |
| | | if (!props && this.gui_objects.qsearchbox) |
| | | props = this.gui_objects.qsearchbox.value; |
| | | |
| | | if (props || this.env.search_request) { |
| | | var params = {'_a': 'keysearch', '_q': urlencode(props)}; |
| | | // if (this.gui_objects.search_filter) |
| | | // addurl += '&_filter=' + this.gui_objects.search_filter.value; |
| | | this.env.current_page = 1; |
| | | this.set_busy(true, 'searching'); |
| | | |
| | | this.enigma_loadframe(); |
| | | this.enigma_clear_list(); |
| | | this.http_post('plugin.enigma', params, true); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // Reset search filter and the list |
| | | rcube_webmail.prototype.enigma_search_reset = function(props) |
| | | { |
| | | var s = this.env.search_request; |
| | | this.reset_qsearch(); |
| | | |
| | | if (s) { |
| | | this.enigma_loadframe(); |
| | | this.enigma_clear_list(); |
| | | |
| | | // refresh the list |
| | | this.enigma_list(); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // Keys/certs listing |
| | | rcube_webmail.prototype.enigma_list = function(page) |
| | | { |
| | | var params = {'_a': 'keylist'}; |
| | | |
| | | this.env.current_page = page ? page : 1; |
| | | this.set_busy(true, 'loading'); |
| | | |
| | | if (this.env.search_request) |
| | | params._q = this.env.search_request; |
| | | if (page) |
| | | params._p = page; |
| | | |
| | | this.enigma_clear_list(); |
| | | this.http_post('plugin.enigma', params, true); |
| | | } |
| | | |
| | | // Change list page |
| | | rcube_webmail.prototype.enigma_list_page = function(page) |
| | | { |
| | | if (page == 'next') |
| | | page = this.env.current_page + 1; |
| | | else if (page == 'last') |
| | | page = this.env.pagecount; |
| | | else if (page == 'prev' && this.env.current_page > 1) |
| | | page = this.env.current_page - 1; |
| | | else if (page == 'first' && this.env.current_page > 1) |
| | | page = 1; |
| | | |
| | | this.enigma_list(page); |
| | | } |
| | | |
| | | // Remove list rows |
| | | rcube_webmail.prototype.enigma_clear_list = function() |
| | | { |
| | | this.enigma_loadframe(); |
| | | if (this.keys_list) |
| | | this.keys_list.clear(true); |
| | | } |
| | | |
| | | // Adds a row to the list |
| | | rcube_webmail.prototype.enigma_add_list_row = function(r) |
| | | { |
| | | if (!this.gui_objects.keyslist || !this.keys_list) |
| | | return false; |
| | | |
| | | var list = this.keys_list, |
| | | tbody = this.gui_objects.keyslist.tBodies[0], |
| | | rowcount = tbody.rows.length, |
| | | even = rowcount%2, |
| | | css_class = 'message' |
| | | + (even ? ' even' : ' odd'), |
| | | // for performance use DOM instead of jQuery here |
| | | row = document.createElement('tr'), |
| | | col = document.createElement('td'); |
| | | |
| | | row.id = 'rcmrow' + r.id; |
| | | row.className = css_class; |
| | | |
| | | col.innerHTML = r.name; |
| | | row.appendChild(col); |
| | | list.insert_row(row); |
| | | } |
| | | |
| | | /*********************************************************/ |
| | | /********* Enigma Message methods *********/ |
| | | /*********************************************************/ |
| | | |
| | | // Import attached keys/certs file |
| | | rcube_webmail.prototype.enigma_import_attachment = function(mime_id) |
| | | { |
| | | this.set_busy(true, 'loading'); |
| | | this.http_post('plugin.enigmaimport', '_uid='+this.env.uid+'&_mbox=' |
| | | +urlencode(this.env.mailbox)+'&_part='+urlencode(mime_id), true); |
| | | |
| | | return false; |
| | | }; |
| | | |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Enigma Plugin for Roundcube | |
| | | | Version 0.1 | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | /* |
| | | This class contains only hooks and action handlers. |
| | | Most plugin logic is placed in enigma_engine and enigma_ui classes. |
| | | */ |
| | | |
| | | class enigma extends rcube_plugin |
| | | { |
| | | public $task = 'mail|settings'; |
| | | public $rc; |
| | | public $engine; |
| | | |
| | | private $env_loaded; |
| | | private $message; |
| | | private $keys_parts = array(); |
| | | private $keys_bodies = array(); |
| | | |
| | | |
| | | /** |
| | | * Plugin initialization. |
| | | */ |
| | | function init() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $this->rc = $rcmail; |
| | | |
| | | if ($this->rc->task == 'mail') { |
| | | // message parse/display hooks |
| | | $this->add_hook('message_part_structure', array($this, 'parse_structure')); |
| | | $this->add_hook('message_body_prefix', array($this, 'status_message')); |
| | | |
| | | // message sending hooks |
| | | //$this->add_hook('outgoing_message_body', array($this, 'msg_encode')); |
| | | //$this->add_hook('outgoing_message_body', array($this, 'msg_sign')); |
| | | |
| | | if ($rcmail->action == 'show' || $rcmail->action == 'preview') { |
| | | $this->add_hook('message_load', array($this, 'message_load')); |
| | | $this->add_hook('template_object_messagebody', array($this, 'message_output')); |
| | | } |
| | | |
| | | $this->register_action('plugin.enigmaimport', array($this, 'import_file')); |
| | | } |
| | | else if ($this->rc->task == 'settings') { |
| | | // add hooks for Enigma settings |
| | | $this->add_hook('preferences_sections_list', array($this, 'preferences_section')); |
| | | $this->add_hook('preferences_list', array($this, 'preferences_list')); |
| | | $this->add_hook('preferences_save', array($this, 'preferences_save')); |
| | | |
| | | // register handler for keys/certs management |
| | | $this->register_action('plugin.enigma', array($this, 'preferences_ui')); |
| | | |
| | | // grab keys/certs management iframe requests |
| | | $section = get_input_value('_section', RCUBE_INPUT_GET); |
| | | if ($this->rc->action == 'edit-prefs' && preg_match('/^enigma(certs|keys)/', $section)) { |
| | | $this->load_ui(); |
| | | $this->ui->init($section); |
| | | } |
| | | |
| | | // include main js script |
| | | // $this->include_script('enigma.js'); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Plugin environment initialization. |
| | | */ |
| | | function load_env() |
| | | { |
| | | if ($this->env_loaded) |
| | | return; |
| | | |
| | | $this->env_loaded = true; |
| | | |
| | | // Add include path for Enigma classes and drivers |
| | | $include_path = $this->home . '/lib' . PATH_SEPARATOR; |
| | | $include_path .= ini_get('include_path'); |
| | | set_include_path($include_path); |
| | | |
| | | // load the Enigma plugin configuration |
| | | $this->load_config(); |
| | | |
| | | // include localization (if wasn't included before) |
| | | $this->add_texts('localization/'); |
| | | } |
| | | |
| | | /** |
| | | * Plugin UI initialization. |
| | | */ |
| | | function load_ui() |
| | | { |
| | | if ($this->ui) |
| | | return; |
| | | |
| | | // load config/localization |
| | | $this->load_env(); |
| | | |
| | | // Load UI |
| | | $this->ui = new enigma_ui($this, $this->home); |
| | | } |
| | | |
| | | /** |
| | | * Plugin engine initialization. |
| | | */ |
| | | function load_engine() |
| | | { |
| | | if ($this->engine) |
| | | return; |
| | | |
| | | // load config/localization |
| | | $this->load_env(); |
| | | |
| | | $this->engine = new enigma_engine($this); |
| | | } |
| | | |
| | | /** |
| | | * Handler for message_part_structure hook. |
| | | * Called for every part of the message. |
| | | * |
| | | * @param array Original parameters |
| | | * |
| | | * @return array Modified parameters |
| | | */ |
| | | function parse_structure($p) |
| | | { |
| | | $struct = $p['structure']; |
| | | |
| | | if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') { |
| | | $this->parse_plain($p); |
| | | } |
| | | else if ($p['mimetype'] == 'multipart/signed') { |
| | | $this->parse_signed($p); |
| | | } |
| | | else if ($p['mimetype'] == 'multipart/encrypted') { |
| | | $this->parse_encrypted($p); |
| | | } |
| | | else if ($p['mimetype'] == 'application/pkcs7-mime') { |
| | | $this->parse_encrypted($p); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for preferences_sections_list hook. |
| | | * Adds Enigma settings sections into preferences sections list. |
| | | * |
| | | * @param array Original parameters |
| | | * |
| | | * @return array Modified parameters |
| | | */ |
| | | function preferences_section($p) |
| | | { |
| | | // add labels |
| | | $this->add_texts('localization/'); |
| | | |
| | | $p['list']['enigmasettings'] = array( |
| | | 'id' => 'enigmasettings', 'section' => $this->gettext('enigmasettings'), |
| | | ); |
| | | $p['list']['enigmacerts'] = array( |
| | | 'id' => 'enigmacerts', 'section' => $this->gettext('enigmacerts'), |
| | | ); |
| | | $p['list']['enigmakeys'] = array( |
| | | 'id' => 'enigmakeys', 'section' => $this->gettext('enigmakeys'), |
| | | ); |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for preferences_list hook. |
| | | * Adds options blocks into Enigma settings sections in Preferences. |
| | | * |
| | | * @param array Original parameters |
| | | * |
| | | * @return array Modified parameters |
| | | */ |
| | | function preferences_list($p) |
| | | { |
| | | if ($p['section'] == 'enigmasettings') { |
| | | // This makes that section is not removed from the list |
| | | $p['blocks']['dummy']['options']['dummy'] = array(); |
| | | } |
| | | else if ($p['section'] == 'enigmacerts') { |
| | | // This makes that section is not removed from the list |
| | | $p['blocks']['dummy']['options']['dummy'] = array(); |
| | | } |
| | | else if ($p['section'] == 'enigmakeys') { |
| | | // This makes that section is not removed from the list |
| | | $p['blocks']['dummy']['options']['dummy'] = array(); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for preferences_save hook. |
| | | * Executed on Enigma settings form submit. |
| | | * |
| | | * @param array Original parameters |
| | | * |
| | | * @return array Modified parameters |
| | | */ |
| | | function preferences_save($p) |
| | | { |
| | | if ($p['section'] == 'enigmasettings') { |
| | | $a['prefs'] = array( |
| | | // 'dummy' => get_input_value('_dummy', RCUBE_INPUT_POST), |
| | | ); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for keys/certs management UI template. |
| | | */ |
| | | function preferences_ui() |
| | | { |
| | | $this->load_ui(); |
| | | $this->ui->init(); |
| | | } |
| | | |
| | | /** |
| | | * Handler for message_body_prefix hook. |
| | | * Called for every displayed (content) part of the message. |
| | | * Adds infobox about signature verification and/or decryption |
| | | * status above the body. |
| | | * |
| | | * @param array Original parameters |
| | | * |
| | | * @return array Modified parameters |
| | | */ |
| | | function status_message($p) |
| | | { |
| | | $part_id = $p['part']->mime_id; |
| | | |
| | | // skip: not a message part |
| | | if ($p['part'] instanceof rcube_message) |
| | | return $p; |
| | | |
| | | // skip: message has no signed/encoded content |
| | | if (!$this->engine) |
| | | return $p; |
| | | |
| | | // Decryption status |
| | | if (isset($this->engine->decryptions[$part_id])) { |
| | | |
| | | // get decryption status |
| | | $status = $this->engine->decryptions[$part_id]; |
| | | |
| | | // Load UI and add css script |
| | | $this->load_ui(); |
| | | $this->ui->add_css(); |
| | | |
| | | // display status info |
| | | $attrib['id'] = 'enigma-message'; |
| | | |
| | | if ($status instanceof enigma_error) { |
| | | $attrib['class'] = 'enigmaerror'; |
| | | $code = $status->getCode(); |
| | | if ($code == enigma_error::E_KEYNOTFOUND) |
| | | $msg = Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')), |
| | | $this->gettext('decryptnokey'))); |
| | | else if ($code == enigma_error::E_BADPASS) |
| | | $msg = Q($this->gettext('decryptbadpass')); |
| | | else |
| | | $msg = Q($this->gettext('decrypterror')); |
| | | } |
| | | else { |
| | | $attrib['class'] = 'enigmanotice'; |
| | | $msg = Q($this->gettext('decryptok')); |
| | | } |
| | | |
| | | $p['prefix'] .= html::div($attrib, $msg); |
| | | } |
| | | |
| | | // Signature verification status |
| | | if (isset($this->engine->signed_parts[$part_id]) |
| | | && ($sig = $this->engine->signatures[$this->engine->signed_parts[$part_id]]) |
| | | ) { |
| | | // add css script |
| | | $this->load_ui(); |
| | | $this->ui->add_css(); |
| | | |
| | | // display status info |
| | | $attrib['id'] = 'enigma-message'; |
| | | |
| | | if ($sig instanceof enigma_signature) { |
| | | if ($sig->valid) { |
| | | $attrib['class'] = 'enigmanotice'; |
| | | $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>'; |
| | | $msg = Q(str_replace('$sender', $sender, $this->gettext('sigvalid'))); |
| | | } |
| | | else { |
| | | $attrib['class'] = 'enigmawarning'; |
| | | $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>'; |
| | | $msg = Q(str_replace('$sender', $sender, $this->gettext('siginvalid'))); |
| | | } |
| | | } |
| | | else if ($sig->getCode() == enigma_error::E_KEYNOTFOUND) { |
| | | $attrib['class'] = 'enigmawarning'; |
| | | $msg = Q(str_replace('$keyid', enigma_key::format_id($sig->getData('id')), |
| | | $this->gettext('signokey'))); |
| | | } |
| | | else { |
| | | $attrib['class'] = 'enigmaerror'; |
| | | $msg = Q($this->gettext('sigerror')); |
| | | } |
| | | /* |
| | | $msg .= ' ' . html::a(array('href' => "#sigdetails", |
| | | 'onclick' => JS_OBJECT_NAME.".command('enigma-sig-details')"), |
| | | Q($this->gettext('showdetails'))); |
| | | */ |
| | | // test |
| | | // $msg .= '<br /><pre>'.$sig->body.'</pre>'; |
| | | |
| | | $p['prefix'] .= html::div($attrib, $msg); |
| | | |
| | | // Display each signature message only once |
| | | unset($this->engine->signatures[$this->engine->signed_parts[$part_id]]); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for plain/text message. |
| | | * |
| | | * @param array Reference to hook's parameters (see enigma::parse_structure()) |
| | | */ |
| | | private function parse_plain(&$p) |
| | | { |
| | | $this->load_engine(); |
| | | $this->engine->parse_plain($p); |
| | | } |
| | | |
| | | /** |
| | | * Handler for multipart/signed message. |
| | | * Verifies signature. |
| | | * |
| | | * @param array Reference to hook's parameters (see enigma::parse_structure()) |
| | | */ |
| | | private function parse_signed(&$p) |
| | | { |
| | | $this->load_engine(); |
| | | $this->engine->parse_signed($p); |
| | | } |
| | | |
| | | /** |
| | | * Handler for multipart/encrypted and application/pkcs7-mime message. |
| | | * |
| | | * @param array Reference to hook's parameters (see enigma::parse_structure()) |
| | | */ |
| | | private function parse_encrypted(&$p) |
| | | { |
| | | $this->load_engine(); |
| | | $this->engine->parse_encrypted($p); |
| | | } |
| | | |
| | | /** |
| | | * Handler for message_load hook. |
| | | * Check message bodies and attachments for keys/certs. |
| | | */ |
| | | function message_load($p) |
| | | { |
| | | $this->message = $p['object']; |
| | | |
| | | // handle attachments vcard attachments |
| | | foreach ((array)$this->message->attachments as $attachment) { |
| | | if ($this->is_keys_part($attachment)) { |
| | | $this->keys_parts[] = $attachment->mime_id; |
| | | } |
| | | } |
| | | // the same with message bodies |
| | | foreach ((array)$this->message->parts as $idx => $part) { |
| | | if ($this->is_keys_part($part)) { |
| | | $this->keys_parts[] = $part->mime_id; |
| | | $this->keys_bodies[] = $part->mime_id; |
| | | } |
| | | } |
| | | // @TODO: inline PGP keys |
| | | |
| | | if ($this->keys_parts) { |
| | | $this->add_texts('localization'); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for template_object_messagebody hook. |
| | | * This callback function adds a box below the message content |
| | | * if there is a key/cert attachment available |
| | | */ |
| | | function message_output($p) |
| | | { |
| | | $attach_script = false; |
| | | |
| | | foreach ($this->keys_parts as $part) { |
| | | |
| | | // remove part's body |
| | | if (in_array($part, $this->keys_bodies)) |
| | | $p['content'] = ''; |
| | | |
| | | $style = "margin:0 1em; padding:0.2em 0.5em; border:1px solid #999; width: auto" |
| | | ." border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px"; |
| | | |
| | | // add box below messsage body |
| | | $p['content'] .= html::p(array('style' => $style), |
| | | html::a(array( |
| | | 'href' => "#", |
| | | 'onclick' => "return ".JS_OBJECT_NAME.".enigma_import_attachment('".JQ($part)."')", |
| | | 'title' => $this->gettext('keyattimport')), |
| | | html::img(array('src' => $this->url('skins/default/key_add.png'), 'style' => "vertical-align:middle"))) |
| | | . ' ' . html::span(null, $this->gettext('keyattfound'))); |
| | | |
| | | $attach_script = true; |
| | | } |
| | | |
| | | if ($attach_script) { |
| | | $this->include_script('enigma.js'); |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | /** |
| | | * Handler for attached keys/certs import |
| | | */ |
| | | function import_file() |
| | | { |
| | | $this->load_engine(); |
| | | $this->engine->import_file(); |
| | | } |
| | | |
| | | /** |
| | | * Checks if specified message part is a PGP-key or S/MIME cert data |
| | | * |
| | | * @param rcube_message_part Part object |
| | | * |
| | | * @return boolean True if part is a key/cert |
| | | */ |
| | | private function is_keys_part($part) |
| | | { |
| | | // @TODO: S/MIME |
| | | return ( |
| | | // Content-Type: application/pgp-keys |
| | | $part->mimetype == 'application/pgp-keys' |
| | | ); |
| | | } |
| | | } |
New file |
| | |
| | | Order allow,deny |
| | | Deny from all |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Crypt_GPG is a package to use GPG from PHP |
| | | * |
| | | * This package provides an object oriented interface to GNU Privacy |
| | | * Guard (GPG). It requires the GPG executable to be on the system. |
| | | * |
| | | * Though GPG can support symmetric-key cryptography, this package is intended |
| | | * only to facilitate public-key cryptography. |
| | | * |
| | | * This file contains the main GPG class. The class in this file lets you |
| | | * encrypt, decrypt, sign and verify data; import and delete keys; and perform |
| | | * other useful GPG tasks. |
| | | * |
| | | * Example usage: |
| | | * <code> |
| | | * <?php |
| | | * // encrypt some data |
| | | * $gpg = new Crypt_GPG(); |
| | | * $gpg->addEncryptKey($mySecretKeyId); |
| | | * $encryptedData = $gpg->encrypt($data); |
| | | * ?> |
| | | * </code> |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: GPG.php 302814 2010-08-26 15:43:07Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | |
| | | /** |
| | | * Signature handler class |
| | | */ |
| | | require_once 'Crypt/GPG/VerifyStatusHandler.php'; |
| | | |
| | | /** |
| | | * Decryption handler class |
| | | */ |
| | | require_once 'Crypt/GPG/DecryptStatusHandler.php'; |
| | | |
| | | /** |
| | | * GPG key class |
| | | */ |
| | | require_once 'Crypt/GPG/Key.php'; |
| | | |
| | | /** |
| | | * GPG sub-key class |
| | | */ |
| | | require_once 'Crypt/GPG/SubKey.php'; |
| | | |
| | | /** |
| | | * GPG user id class |
| | | */ |
| | | require_once 'Crypt/GPG/UserId.php'; |
| | | |
| | | /** |
| | | * GPG process and I/O engine class |
| | | */ |
| | | require_once 'Crypt/GPG/Engine.php'; |
| | | |
| | | /** |
| | | * GPG exception classes |
| | | */ |
| | | require_once 'Crypt/GPG/Exceptions.php'; |
| | | |
| | | // {{{ class Crypt_GPG |
| | | |
| | | /** |
| | | * A class to use GPG from PHP |
| | | * |
| | | * This class provides an object oriented interface to GNU Privacy Guard (GPG). |
| | | * |
| | | * Though GPG can support symmetric-key cryptography, this class is intended |
| | | * only to facilitate public-key cryptography. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | class Crypt_GPG |
| | | { |
| | | // {{{ class error constants |
| | | |
| | | /** |
| | | * Error code returned when there is no error. |
| | | */ |
| | | const ERROR_NONE = 0; |
| | | |
| | | /** |
| | | * Error code returned when an unknown or unhandled error occurs. |
| | | */ |
| | | const ERROR_UNKNOWN = 1; |
| | | |
| | | /** |
| | | * Error code returned when a bad passphrase is used. |
| | | */ |
| | | const ERROR_BAD_PASSPHRASE = 2; |
| | | |
| | | /** |
| | | * Error code returned when a required passphrase is missing. |
| | | */ |
| | | const ERROR_MISSING_PASSPHRASE = 3; |
| | | |
| | | /** |
| | | * Error code returned when a key that is already in the keyring is |
| | | * imported. |
| | | */ |
| | | const ERROR_DUPLICATE_KEY = 4; |
| | | |
| | | /** |
| | | * Error code returned the required data is missing for an operation. |
| | | * |
| | | * This could be missing key data, missing encrypted data or missing |
| | | * signature data. |
| | | */ |
| | | const ERROR_NO_DATA = 5; |
| | | |
| | | /** |
| | | * Error code returned when an unsigned key is used. |
| | | */ |
| | | const ERROR_UNSIGNED_KEY = 6; |
| | | |
| | | /** |
| | | * Error code returned when a key that is not self-signed is used. |
| | | */ |
| | | const ERROR_NOT_SELF_SIGNED = 7; |
| | | |
| | | /** |
| | | * Error code returned when a public or private key that is not in the |
| | | * keyring is used. |
| | | */ |
| | | const ERROR_KEY_NOT_FOUND = 8; |
| | | |
| | | /** |
| | | * Error code returned when an attempt to delete public key having a |
| | | * private key is made. |
| | | */ |
| | | const ERROR_DELETE_PRIVATE_KEY = 9; |
| | | |
| | | /** |
| | | * Error code returned when one or more bad signatures are detected. |
| | | */ |
| | | const ERROR_BAD_SIGNATURE = 10; |
| | | |
| | | /** |
| | | * Error code returned when there is a problem reading GnuPG data files. |
| | | */ |
| | | const ERROR_FILE_PERMISSIONS = 11; |
| | | |
| | | // }}} |
| | | // {{{ class constants for data signing modes |
| | | |
| | | /** |
| | | * Signing mode for normal signing of data. The signed message will not |
| | | * be readable without special software. |
| | | * |
| | | * This is the default signing mode. |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | * @see Crypt_GPG::signFile() |
| | | */ |
| | | const SIGN_MODE_NORMAL = 1; |
| | | |
| | | /** |
| | | * Signing mode for clearsigning data. Clearsigned signatures are ASCII |
| | | * armored data and are readable without special software. If the signed |
| | | * message is unencrypted, the message will still be readable. The message |
| | | * text will be in the original encoding. |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | * @see Crypt_GPG::signFile() |
| | | */ |
| | | const SIGN_MODE_CLEAR = 2; |
| | | |
| | | /** |
| | | * Signing mode for creating a detached signature. When using detached |
| | | * signatures, only the signature data is returned. The original message |
| | | * text may be distributed separately from the signature data. This is |
| | | * useful for miltipart/signed email messages as per |
| | | * {@link http://www.ietf.org/rfc/rfc3156.txt RFC 3156}. |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | * @see Crypt_GPG::signFile() |
| | | */ |
| | | const SIGN_MODE_DETACHED = 3; |
| | | |
| | | // }}} |
| | | // {{{ class constants for fingerprint formats |
| | | |
| | | /** |
| | | * No formatting is performed. |
| | | * |
| | | * Example: C3BC615AD9C766E5A85C1F2716D27458B1BBA1C4 |
| | | * |
| | | * @see Crypt_GPG::getFingerprint() |
| | | */ |
| | | const FORMAT_NONE = 1; |
| | | |
| | | /** |
| | | * Fingerprint is formatted in the format used by the GnuPG gpg command's |
| | | * default output. |
| | | * |
| | | * Example: C3BC 615A D9C7 66E5 A85C 1F27 16D2 7458 B1BB A1C4 |
| | | * |
| | | * @see Crypt_GPG::getFingerprint() |
| | | */ |
| | | const FORMAT_CANONICAL = 2; |
| | | |
| | | /** |
| | | * Fingerprint is formatted in the format used when displaying X.509 |
| | | * certificates |
| | | * |
| | | * Example: C3:BC:61:5A:D9:C7:66:E5:A8:5C:1F:27:16:D2:74:58:B1:BB:A1:C4 |
| | | * |
| | | * @see Crypt_GPG::getFingerprint() |
| | | */ |
| | | const FORMAT_X509 = 3; |
| | | |
| | | // }}} |
| | | // {{{ other class constants |
| | | |
| | | /** |
| | | * URI at which package bugs may be reported. |
| | | */ |
| | | const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'; |
| | | |
| | | // }}} |
| | | // {{{ protected class properties |
| | | |
| | | /** |
| | | * Engine used to control the GPG subprocess |
| | | * |
| | | * @var Crypt_GPG_Engine |
| | | * |
| | | * @see Crypt_GPG::setEngine() |
| | | */ |
| | | protected $engine = null; |
| | | |
| | | /** |
| | | * Keys used to encrypt |
| | | * |
| | | * The array is of the form: |
| | | * <code> |
| | | * array( |
| | | * $key_id => array( |
| | | * 'fingerprint' => $fingerprint, |
| | | * 'passphrase' => null |
| | | * ) |
| | | * ); |
| | | * </code> |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG::addEncryptKey() |
| | | * @see Crypt_GPG::clearEncryptKeys() |
| | | */ |
| | | protected $encryptKeys = array(); |
| | | |
| | | /** |
| | | * Keys used to decrypt |
| | | * |
| | | * The array is of the form: |
| | | * <code> |
| | | * array( |
| | | * $key_id => array( |
| | | * 'fingerprint' => $fingerprint, |
| | | * 'passphrase' => $passphrase |
| | | * ) |
| | | * ); |
| | | * </code> |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG::addSignKey() |
| | | * @see Crypt_GPG::clearSignKeys() |
| | | */ |
| | | protected $signKeys = array(); |
| | | |
| | | /** |
| | | * Keys used to sign |
| | | * |
| | | * The array is of the form: |
| | | * <code> |
| | | * array( |
| | | * $key_id => array( |
| | | * 'fingerprint' => $fingerprint, |
| | | * 'passphrase' => $passphrase |
| | | * ) |
| | | * ); |
| | | * </code> |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG::addDecryptKey() |
| | | * @see Crypt_GPG::clearDecryptKeys() |
| | | */ |
| | | protected $decryptKeys = array(); |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new GPG object |
| | | * |
| | | * Available options are: |
| | | * |
| | | * - <kbd>string homedir</kbd> - the directory where the GPG |
| | | * keyring files are stored. If not |
| | | * specified, Crypt_GPG uses the |
| | | * default of <kbd>~/.gnupg</kbd>. |
| | | * - <kbd>string publicKeyring</kbd> - the file path of the public |
| | | * keyring. Use this if the public |
| | | * keyring is not in the homedir, or |
| | | * if the keyring is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * keyring with this option |
| | | * (/foo/bar/pubring.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string privateKeyring</kbd> - the file path of the private |
| | | * keyring. Use this if the private |
| | | * keyring is not in the homedir, or |
| | | * if the keyring is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * keyring with this option |
| | | * (/foo/bar/secring.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string trustDb</kbd> - the file path of the web-of-trust |
| | | * database. Use this if the trust |
| | | * database is not in the homedir, or |
| | | * if the database is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * trust database with this option |
| | | * (/foo/bar/trustdb.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string binary</kbd> - the location of the GPG binary. If |
| | | * not specified, the driver attempts |
| | | * to auto-detect the GPG binary |
| | | * location using a list of known |
| | | * default locations for the current |
| | | * operating system. The option |
| | | * <kbd>gpgBinary</kbd> is a |
| | | * deprecated alias for this option. |
| | | * - <kbd>boolean debug</kbd> - whether or not to use debug mode. |
| | | * When debug mode is on, all |
| | | * communication to and from the GPG |
| | | * subprocess is logged. This can be |
| | | * |
| | | * @param array $options optional. An array of options used to create the |
| | | * GPG object. All options are optional and are |
| | | * represented as key-value pairs. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist |
| | | * and cannot be created. This can happen if <kbd>homedir</kbd> is |
| | | * not specified, Crypt_GPG is run as the web user, and the web |
| | | * user has no home directory. This exception is also thrown if any |
| | | * of the options <kbd>publicKeyring</kbd>, |
| | | * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are |
| | | * specified but the files do not exist or are are not readable. |
| | | * This can happen if the user running the Crypt_GPG process (for |
| | | * example, the Apache user) does not have permission to read the |
| | | * files. |
| | | * |
| | | * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or |
| | | * if no <kbd>binary</kbd> is provided and no suitable binary could |
| | | * be found. |
| | | */ |
| | | public function __construct(array $options = array()) |
| | | { |
| | | $this->setEngine(new Crypt_GPG_Engine($options)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ importKey() |
| | | |
| | | /** |
| | | * Imports a public or private key into the keyring |
| | | * |
| | | * Keys may be removed from the keyring using |
| | | * {@link Crypt_GPG::deletePublicKey()} or |
| | | * {@link Crypt_GPG::deletePrivateKey()}. |
| | | * |
| | | * @param string $data the key data to be imported. |
| | | * |
| | | * @return array an associative array containing the following elements: |
| | | * - <kbd>fingerprint</kbd> - the fingerprint of the |
| | | * imported key, |
| | | * - <kbd>public_imported</kbd> - the number of public |
| | | * keys imported, |
| | | * - <kbd>public_unchanged</kbd> - the number of unchanged |
| | | * public keys, |
| | | * - <kbd>private_imported</kbd> - the number of private |
| | | * keys imported, |
| | | * - <kbd>private_unchanged</kbd> - the number of unchanged |
| | | * private keys. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the key data is missing or if the |
| | | * data is is not valid key data. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function importKey($data) |
| | | { |
| | | return $this->_importKey($data, false); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ importKeyFile() |
| | | |
| | | /** |
| | | * Imports a public or private key file into the keyring |
| | | * |
| | | * Keys may be removed from the keyring using |
| | | * {@link Crypt_GPG::deletePublicKey()} or |
| | | * {@link Crypt_GPG::deletePrivateKey()}. |
| | | * |
| | | * @param string $filename the key file to be imported. |
| | | * |
| | | * @return array an associative array containing the following elements: |
| | | * - <kbd>fingerprint</kbd> - the fingerprint of the |
| | | * imported key, |
| | | * - <kbd>public_imported</kbd> - the number of public |
| | | * keys imported, |
| | | * - <kbd>public_unchanged</kbd> - the number of unchanged |
| | | * public keys, |
| | | * - <kbd>private_imported</kbd> - the number of private |
| | | * keys imported, |
| | | * - <kbd>private_unchanged</kbd> - the number of unchanged |
| | | * private keys. |
| | | * private keys. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the key data is missing or if the |
| | | * data is is not valid key data. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the key file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function importKeyFile($filename) |
| | | { |
| | | return $this->_importKey($filename, true); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ exportPublicKey() |
| | | |
| | | /** |
| | | * Exports a public key from the keyring |
| | | * |
| | | * The exported key remains on the keyring. To delete the public key, use |
| | | * {@link Crypt_GPG::deletePublicKey()}. |
| | | * |
| | | * If more than one key fingerprint is available for the specified |
| | | * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the |
| | | * first public key is exported. |
| | | * |
| | | * @param string $keyId either the full uid of the public key, the email |
| | | * part of the uid of the public key or the key id of |
| | | * the public key. For example, |
| | | * "Test User (example) <test@example.com>", |
| | | * "test@example.com" or a hexadecimal string. |
| | | * @param boolean $armor optional. If true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. Defaults to |
| | | * true. |
| | | * |
| | | * @return string the public key data. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if a public key with the given |
| | | * <kbd>$keyId</kbd> is not found. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function exportPublicKey($keyId, $armor = true) |
| | | { |
| | | $fingerprint = $this->getFingerprint($keyId); |
| | | |
| | | if ($fingerprint === null) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Public key not found: ' . $keyId, |
| | | Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId); |
| | | } |
| | | |
| | | $keyData = ''; |
| | | $operation = '--export ' . escapeshellarg($fingerprint); |
| | | $arguments = ($armor) ? array('--armor') : array(); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOutput($keyData); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | if ($code !== Crypt_GPG::ERROR_NONE) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error exporting public key. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | return $keyData; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ deletePublicKey() |
| | | |
| | | /** |
| | | * Deletes a public key from the keyring |
| | | * |
| | | * If more than one key fingerprint is available for the specified |
| | | * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the |
| | | * first public key is deleted. |
| | | * |
| | | * The private key must be deleted first or an exception will be thrown. |
| | | * See {@link Crypt_GPG::deletePrivateKey()}. |
| | | * |
| | | * @param string $keyId either the full uid of the public key, the email |
| | | * part of the uid of the public key or the key id of |
| | | * the public key. For example, |
| | | * "Test User (example) <test@example.com>", |
| | | * "test@example.com" or a hexadecimal string. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if a public key with the given |
| | | * <kbd>$keyId</kbd> is not found. |
| | | * |
| | | * @throws Crypt_GPG_DeletePrivateKeyException if the specified public key |
| | | * has an associated private key on the keyring. The private key |
| | | * must be deleted first. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function deletePublicKey($keyId) |
| | | { |
| | | $fingerprint = $this->getFingerprint($keyId); |
| | | |
| | | if ($fingerprint === null) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Public key not found: ' . $keyId, |
| | | Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId); |
| | | } |
| | | |
| | | $operation = '--delete-key ' . escapeshellarg($fingerprint); |
| | | $arguments = array( |
| | | '--batch', |
| | | '--yes' |
| | | ); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | break; |
| | | case Crypt_GPG::ERROR_DELETE_PRIVATE_KEY: |
| | | throw new Crypt_GPG_DeletePrivateKeyException( |
| | | 'Private key must be deleted before public key can be ' . |
| | | 'deleted.', $code, $keyId); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error deleting public key. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ deletePrivateKey() |
| | | |
| | | /** |
| | | * Deletes a private key from the keyring |
| | | * |
| | | * If more than one key fingerprint is available for the specified |
| | | * <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the |
| | | * first private key is deleted. |
| | | * |
| | | * Calls GPG with the <kbd>--delete-secret-key</kbd> command. |
| | | * |
| | | * @param string $keyId either the full uid of the private key, the email |
| | | * part of the uid of the private key or the key id of |
| | | * the private key. For example, |
| | | * "Test User (example) <test@example.com>", |
| | | * "test@example.com" or a hexadecimal string. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if a private key with the given |
| | | * <kbd>$keyId</kbd> is not found. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function deletePrivateKey($keyId) |
| | | { |
| | | $fingerprint = $this->getFingerprint($keyId); |
| | | |
| | | if ($fingerprint === null) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Private key not found: ' . $keyId, |
| | | Crypt_GPG::ERROR_KEY_NOT_FOUND, $keyId); |
| | | } |
| | | |
| | | $operation = '--delete-secret-key ' . escapeshellarg($fingerprint); |
| | | $arguments = array( |
| | | '--batch', |
| | | '--yes' |
| | | ); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | break; |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Private key not found: ' . $keyId, |
| | | $code, $keyId); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error deleting private key. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getKeys() |
| | | |
| | | /** |
| | | * Gets the available keys in the keyring |
| | | * |
| | | * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See |
| | | * the first section of <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG package} for a detailed |
| | | * description of how the GPG command output is parsed. |
| | | * |
| | | * @param string $keyId optional. Only keys with that match the specified |
| | | * pattern are returned. The pattern may be part of |
| | | * a user id, a key id or a key fingerprint. If not |
| | | * specified, all keys are returned. |
| | | * |
| | | * @return array an array of {@link Crypt_GPG_Key} objects. If no keys |
| | | * match the specified <kbd>$keyId</kbd> an empty array is |
| | | * returned. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG_Key |
| | | */ |
| | | public function getKeys($keyId = '') |
| | | { |
| | | // get private key fingerprints |
| | | if ($keyId == '') { |
| | | $operation = '--list-secret-keys'; |
| | | } else { |
| | | $operation = '--list-secret-keys ' . escapeshellarg($keyId); |
| | | } |
| | | |
| | | // According to The file 'doc/DETAILS' in the GnuPG distribution, using |
| | | // double '--with-fingerprint' also prints the fingerprint for subkeys. |
| | | $arguments = array( |
| | | '--with-colons', |
| | | '--with-fingerprint', |
| | | '--with-fingerprint', |
| | | '--fixed-list-mode' |
| | | ); |
| | | |
| | | $output = ''; |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | // ignore not found key errors |
| | | break; |
| | | case Crypt_GPG::ERROR_FILE_PERMISSIONS: |
| | | $filename = $this->engine->getErrorFilename(); |
| | | if ($filename) { |
| | | throw new Crypt_GPG_FileException(sprintf( |
| | | 'Error reading GnuPG data file \'%s\'. Check to make ' . |
| | | 'sure it is readable by the current user.', $filename), |
| | | $code, $filename); |
| | | } |
| | | throw new Crypt_GPG_FileException( |
| | | 'Error reading GnuPG data file. Check to make GnuPG data ' . |
| | | 'files are readable by the current user.', $code); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error getting keys. Please use the \'debug\' option ' . |
| | | 'when creating the Crypt_GPG object, and file a bug report ' . |
| | | 'at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | $privateKeyFingerprints = array(); |
| | | |
| | | $lines = explode(PHP_EOL, $output); |
| | | foreach ($lines as $line) { |
| | | $lineExp = explode(':', $line); |
| | | if ($lineExp[0] == 'fpr') { |
| | | $privateKeyFingerprints[] = $lineExp[9]; |
| | | } |
| | | } |
| | | |
| | | // get public keys |
| | | if ($keyId == '') { |
| | | $operation = '--list-public-keys'; |
| | | } else { |
| | | $operation = '--list-public-keys ' . escapeshellarg($keyId); |
| | | } |
| | | |
| | | $output = ''; |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | // ignore not found key errors |
| | | break; |
| | | case Crypt_GPG::ERROR_FILE_PERMISSIONS: |
| | | $filename = $this->engine->getErrorFilename(); |
| | | if ($filename) { |
| | | throw new Crypt_GPG_FileException(sprintf( |
| | | 'Error reading GnuPG data file \'%s\'. Check to make ' . |
| | | 'sure it is readable by the current user.', $filename), |
| | | $code, $filename); |
| | | } |
| | | throw new Crypt_GPG_FileException( |
| | | 'Error reading GnuPG data file. Check to make GnuPG data ' . |
| | | 'files are readable by the current user.', $code); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error getting keys. Please use the \'debug\' option ' . |
| | | 'when creating the Crypt_GPG object, and file a bug report ' . |
| | | 'at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | $keys = array(); |
| | | |
| | | $key = null; // current key |
| | | $subKey = null; // current sub-key |
| | | |
| | | $lines = explode(PHP_EOL, $output); |
| | | foreach ($lines as $line) { |
| | | $lineExp = explode(':', $line); |
| | | |
| | | if ($lineExp[0] == 'pub') { |
| | | |
| | | // new primary key means last key should be added to the array |
| | | if ($key !== null) { |
| | | $keys[] = $key; |
| | | } |
| | | |
| | | $key = new Crypt_GPG_Key(); |
| | | |
| | | $subKey = Crypt_GPG_SubKey::parse($line); |
| | | $key->addSubKey($subKey); |
| | | |
| | | } elseif ($lineExp[0] == 'sub') { |
| | | |
| | | $subKey = Crypt_GPG_SubKey::parse($line); |
| | | $key->addSubKey($subKey); |
| | | |
| | | } elseif ($lineExp[0] == 'fpr') { |
| | | |
| | | $fingerprint = $lineExp[9]; |
| | | |
| | | // set current sub-key fingerprint |
| | | $subKey->setFingerprint($fingerprint); |
| | | |
| | | // if private key exists, set has private to true |
| | | if (in_array($fingerprint, $privateKeyFingerprints)) { |
| | | $subKey->setHasPrivate(true); |
| | | } |
| | | |
| | | } elseif ($lineExp[0] == 'uid') { |
| | | |
| | | $string = stripcslashes($lineExp[9]); // as per documentation |
| | | $userId = new Crypt_GPG_UserId($string); |
| | | |
| | | if ($lineExp[1] == 'r') { |
| | | $userId->setRevoked(true); |
| | | } |
| | | |
| | | $key->addUserId($userId); |
| | | |
| | | } |
| | | } |
| | | |
| | | // add last key |
| | | if ($key !== null) { |
| | | $keys[] = $key; |
| | | } |
| | | |
| | | return $keys; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getFingerprint() |
| | | |
| | | /** |
| | | * Gets a key fingerprint from the keyring |
| | | * |
| | | * If more than one key fingerprint is available (for example, if you use |
| | | * a non-unique user id) only the first key fingerprint is returned. |
| | | * |
| | | * Calls the GPG <kbd>--list-keys</kbd> command with the |
| | | * <kbd>--with-fingerprint</kbd> option to retrieve a public key |
| | | * fingerprint. |
| | | * |
| | | * @param string $keyId either the full user id of the key, the email |
| | | * part of the user id of the key, or the key id of |
| | | * the key. For example, |
| | | * "Test User (example) <test@example.com>", |
| | | * "test@example.com" or a hexadecimal string. |
| | | * @param integer $format optional. How the fingerprint should be formatted. |
| | | * Use {@link Crypt_GPG::FORMAT_X509} for X.509 |
| | | * certificate format, |
| | | * {@link Crypt_GPG::FORMAT_CANONICAL} for the format |
| | | * used by GnuPG output and |
| | | * {@link Crypt_GPG::FORMAT_NONE} for no formatting. |
| | | * Defaults to <code>Crypt_GPG::FORMAT_NONE</code>. |
| | | * |
| | | * @return string the fingerprint of the key, or null if no fingerprint |
| | | * is found for the given <kbd>$keyId</kbd>. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function getFingerprint($keyId, $format = Crypt_GPG::FORMAT_NONE) |
| | | { |
| | | $output = ''; |
| | | $operation = '--list-keys ' . escapeshellarg($keyId); |
| | | $arguments = array( |
| | | '--with-colons', |
| | | '--with-fingerprint' |
| | | ); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | // ignore not found key errors |
| | | break; |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error getting key fingerprint. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | $fingerprint = null; |
| | | |
| | | $lines = explode(PHP_EOL, $output); |
| | | foreach ($lines as $line) { |
| | | if (substr($line, 0, 3) == 'fpr') { |
| | | $lineExp = explode(':', $line); |
| | | $fingerprint = $lineExp[9]; |
| | | |
| | | switch ($format) { |
| | | case Crypt_GPG::FORMAT_CANONICAL: |
| | | $fingerprintExp = str_split($fingerprint, 4); |
| | | $format = '%s %s %s %s %s %s %s %s %s %s'; |
| | | $fingerprint = vsprintf($format, $fingerprintExp); |
| | | break; |
| | | |
| | | case Crypt_GPG::FORMAT_X509: |
| | | $fingerprintExp = str_split($fingerprint, 2); |
| | | $fingerprint = implode(':', $fingerprintExp); |
| | | break; |
| | | } |
| | | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return $fingerprint; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ encrypt() |
| | | |
| | | /** |
| | | * Encrypts string data |
| | | * |
| | | * Data is ASCII armored by default but may optionally be returned as |
| | | * binary. |
| | | * |
| | | * @param string $data the data to be encrypted. |
| | | * @param boolean $armor optional. If true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. Defaults to |
| | | * true. |
| | | * |
| | | * @return string the encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified. |
| | | * See {@link Crypt_GPG::addEncryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @sensitive $data |
| | | */ |
| | | public function encrypt($data, $armor = true) |
| | | { |
| | | return $this->_encrypt($data, false, null, $armor); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ encryptFile() |
| | | |
| | | /** |
| | | * Encrypts a file |
| | | * |
| | | * Encrypted data is ASCII armored by default but may optionally be saved |
| | | * as binary. |
| | | * |
| | | * @param string $filename the filename of the file to encrypt. |
| | | * @param string $encryptedFile optional. The filename of the file in |
| | | * which to store the encrypted data. If null |
| | | * or unspecified, the encrypted data is |
| | | * returned as a string. |
| | | * @param boolean $armor optional. If true, ASCII armored data is |
| | | * returned; otherwise, binary data is |
| | | * returned. Defaults to true. |
| | | * |
| | | * @return void|string if the <kbd>$encryptedFile</kbd> parameter is null, |
| | | * a string containing the encrypted data is returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified. |
| | | * See {@link Crypt_GPG::addEncryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function encryptFile($filename, $encryptedFile = null, $armor = true) |
| | | { |
| | | return $this->_encrypt($filename, true, $encryptedFile, $armor); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ encryptAndSign() |
| | | |
| | | /** |
| | | * Encrypts and signs data |
| | | * |
| | | * Data is encrypted and signed in a single pass. |
| | | * |
| | | * NOTE: Until GnuPG version 1.4.10, it was not possible to verify |
| | | * encrypted-signed data without decrypting it at the same time. If you try |
| | | * to use {@link Crypt_GPG::verify()} method on encrypted-signed data with |
| | | * earlier GnuPG versions, you will get an error. Please use |
| | | * {@link Crypt_GPG::decryptAndVerify()} to verify encrypted-signed data. |
| | | * |
| | | * @param string $data the data to be encrypted and signed. |
| | | * @param boolean $armor optional. If true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. Defaults to |
| | | * true. |
| | | * |
| | | * @return string the encrypted signed data. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified |
| | | * or if no signing key is specified. See |
| | | * {@link Crypt_GPG::addEncryptKey()} and |
| | | * {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG::decryptAndVerify() |
| | | */ |
| | | public function encryptAndSign($data, $armor = true) |
| | | { |
| | | return $this->_encryptAndSign($data, false, null, $armor); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ encryptAndSignFile() |
| | | |
| | | /** |
| | | * Encrypts and signs a file |
| | | * |
| | | * The file is encrypted and signed in a single pass. |
| | | * |
| | | * NOTE: Until GnuPG version 1.4.10, it was not possible to verify |
| | | * encrypted-signed files without decrypting them at the same time. If you |
| | | * try to use {@link Crypt_GPG::verify()} method on encrypted-signed files |
| | | * with earlier GnuPG versions, you will get an error. Please use |
| | | * {@link Crypt_GPG::decryptAndVerifyFile()} to verify encrypted-signed |
| | | * files. |
| | | * |
| | | * @param string $filename the name of the file containing the data to |
| | | * be encrypted and signed. |
| | | * @param string $signedFile optional. The name of the file in which the |
| | | * encrypted, signed data should be stored. If |
| | | * null or unspecified, the encrypted, signed |
| | | * data is returned as a string. |
| | | * @param boolean $armor optional. If true, ASCII armored data is |
| | | * returned; otherwise, binary data is returned. |
| | | * Defaults to true. |
| | | * |
| | | * @return void|string if the <kbd>$signedFile</kbd> parameter is null, a |
| | | * string containing the encrypted, signed data is |
| | | * returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified |
| | | * or if no signing key is specified. See |
| | | * {@link Crypt_GPG::addEncryptKey()} and |
| | | * {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG::decryptAndVerifyFile() |
| | | */ |
| | | public function encryptAndSignFile($filename, $signedFile = null, |
| | | $armor = true |
| | | ) { |
| | | return $this->_encryptAndSign($filename, true, $signedFile, $armor); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ decrypt() |
| | | |
| | | /** |
| | | * Decrypts string data |
| | | * |
| | | * This method assumes the required private key is available in the keyring |
| | | * and throws an exception if the private key is not available. To add a |
| | | * private key to the keyring, use the {@link Crypt_GPG::importKey()} or |
| | | * {@link Crypt_GPG::importKeyFile()} methods. |
| | | * |
| | | * @param string $encryptedData the data to be decrypted. |
| | | * |
| | | * @return string the decrypted data. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function decrypt($encryptedData) |
| | | { |
| | | return $this->_decrypt($encryptedData, false, null); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ decryptFile() |
| | | |
| | | /** |
| | | * Decrypts a file |
| | | * |
| | | * This method assumes the required private key is available in the keyring |
| | | * and throws an exception if the private key is not available. To add a |
| | | * private key to the keyring, use the {@link Crypt_GPG::importKey()} or |
| | | * {@link Crypt_GPG::importKeyFile()} methods. |
| | | * |
| | | * @param string $encryptedFile the name of the encrypted file data to |
| | | * decrypt. |
| | | * @param string $decryptedFile optional. The name of the file to which the |
| | | * decrypted data should be written. If null |
| | | * or unspecified, the decrypted data is |
| | | * returned as a string. |
| | | * |
| | | * @return void|string if the <kbd>$decryptedFile</kbd> parameter is null, |
| | | * a string containing the decrypted data is returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function decryptFile($encryptedFile, $decryptedFile = null) |
| | | { |
| | | return $this->_decrypt($encryptedFile, true, $decryptedFile); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ decryptAndVerify() |
| | | |
| | | /** |
| | | * Decrypts and verifies string data |
| | | * |
| | | * This method assumes the required private key is available in the keyring |
| | | * and throws an exception if the private key is not available. To add a |
| | | * private key to the keyring, use the {@link Crypt_GPG::importKey()} or |
| | | * {@link Crypt_GPG::importKeyFile()} methods. |
| | | * |
| | | * @param string $encryptedData the encrypted, signed data to be decrypted |
| | | * and verified. |
| | | * |
| | | * @return array two element array. The array has an element 'data' |
| | | * containing the decrypted data and an element |
| | | * 'signatures' containing an array of |
| | | * {@link Crypt_GPG_Signature} objects for the signed data. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function decryptAndVerify($encryptedData) |
| | | { |
| | | return $this->_decryptAndVerify($encryptedData, false, null); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ decryptAndVerifyFile() |
| | | |
| | | /** |
| | | * Decrypts and verifies a signed, encrypted file |
| | | * |
| | | * This method assumes the required private key is available in the keyring |
| | | * and throws an exception if the private key is not available. To add a |
| | | * private key to the keyring, use the {@link Crypt_GPG::importKey()} or |
| | | * {@link Crypt_GPG::importKeyFile()} methods. |
| | | * |
| | | * @param string $encryptedFile the name of the signed, encrypted file to |
| | | * to decrypt and verify. |
| | | * @param string $decryptedFile optional. The name of the file to which the |
| | | * decrypted data should be written. If null |
| | | * or unspecified, the decrypted data is |
| | | * returned in the results array. |
| | | * |
| | | * @return array two element array. The array has an element 'data' |
| | | * containing the decrypted data and an element |
| | | * 'signatures' containing an array of |
| | | * {@link Crypt_GPG_Signature} objects for the signed data. |
| | | * If the decrypted data is written to a file, the 'data' |
| | | * element is null. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function decryptAndVerifyFile($encryptedFile, $decryptedFile = null) |
| | | { |
| | | return $this->_decryptAndVerify($encryptedFile, true, $decryptedFile); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ sign() |
| | | |
| | | /** |
| | | * Signs data |
| | | * |
| | | * Data may be signed using any one of the three available signing modes: |
| | | * - {@link Crypt_GPG::SIGN_MODE_NORMAL} |
| | | * - {@link Crypt_GPG::SIGN_MODE_CLEAR} |
| | | * - {@link Crypt_GPG::SIGN_MODE_DETACHED} |
| | | * |
| | | * @param string $data the data to be signed. |
| | | * @param boolean $mode optional. The data signing mode to use. Should |
| | | * be one of {@link Crypt_GPG::SIGN_MODE_NORMAL}, |
| | | * {@link Crypt_GPG::SIGN_MODE_CLEAR} or |
| | | * {@link Crypt_GPG::SIGN_MODE_DETACHED}. If not |
| | | * specified, defaults to |
| | | * <kbd>Crypt_GPG::SIGN_MODE_NORMAL</kbd>. |
| | | * @param boolean $armor optional. If true, ASCII armored data is |
| | | * returned; otherwise, binary data is returned. |
| | | * Defaults to true. This has no effect if the |
| | | * mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used. |
| | | * @param boolean $textmode optional. If true, line-breaks in signed data |
| | | * are normalized. Use this option when signing |
| | | * e-mail, or for greater compatibility between |
| | | * systems with different line-break formats. |
| | | * Defaults to false. This has no effect if the |
| | | * mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used as clear-signing always uses textmode. |
| | | * |
| | | * @return string the signed data, or the signature data if a detached |
| | | * signature is requested. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified. |
| | | * See {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function sign($data, $mode = Crypt_GPG::SIGN_MODE_NORMAL, |
| | | $armor = true, $textmode = false |
| | | ) { |
| | | return $this->_sign($data, false, null, $mode, $armor, $textmode); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ signFile() |
| | | |
| | | /** |
| | | * Signs a file |
| | | * |
| | | * The file may be signed using any one of the three available signing |
| | | * modes: |
| | | * - {@link Crypt_GPG::SIGN_MODE_NORMAL} |
| | | * - {@link Crypt_GPG::SIGN_MODE_CLEAR} |
| | | * - {@link Crypt_GPG::SIGN_MODE_DETACHED} |
| | | * |
| | | * @param string $filename the name of the file containing the data to |
| | | * be signed. |
| | | * @param string $signedFile optional. The name of the file in which the |
| | | * signed data should be stored. If null or |
| | | * unspecified, the signed data is returned as a |
| | | * string. |
| | | * @param boolean $mode optional. The data signing mode to use. Should |
| | | * be one of {@link Crypt_GPG::SIGN_MODE_NORMAL}, |
| | | * {@link Crypt_GPG::SIGN_MODE_CLEAR} or |
| | | * {@link Crypt_GPG::SIGN_MODE_DETACHED}. If not |
| | | * specified, defaults to |
| | | * <kbd>Crypt_GPG::SIGN_MODE_NORMAL</kbd>. |
| | | * @param boolean $armor optional. If true, ASCII armored data is |
| | | * returned; otherwise, binary data is returned. |
| | | * Defaults to true. This has no effect if the |
| | | * mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used. |
| | | * @param boolean $textmode optional. If true, line-breaks in signed data |
| | | * are normalized. Use this option when signing |
| | | * e-mail, or for greater compatibility between |
| | | * systems with different line-break formats. |
| | | * Defaults to false. This has no effect if the |
| | | * mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used as clear-signing always uses textmode. |
| | | * |
| | | * @return void|string if the <kbd>$signedFile</kbd> parameter is null, a |
| | | * string containing the signed data (or the signature |
| | | * data if a detached signature is requested) is |
| | | * returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified. |
| | | * See {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function signFile($filename, $signedFile = null, |
| | | $mode = Crypt_GPG::SIGN_MODE_NORMAL, $armor = true, $textmode = false |
| | | ) { |
| | | return $this->_sign( |
| | | $filename, |
| | | true, |
| | | $signedFile, |
| | | $mode, |
| | | $armor, |
| | | $textmode |
| | | ); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ verify() |
| | | |
| | | /** |
| | | * Verifies signed data |
| | | * |
| | | * The {@link Crypt_GPG::decrypt()} method may be used to get the original |
| | | * message if the signed data is not clearsigned and does not use a |
| | | * detached signature. |
| | | * |
| | | * @param string $signedData the signed data to be verified. |
| | | * @param string $signature optional. If verifying data signed using a |
| | | * detached signature, this must be the detached |
| | | * signature data. The data that was signed is |
| | | * specified in <kbd>$signedData</kbd>. |
| | | * |
| | | * @return array an array of {@link Crypt_GPG_Signature} objects for the |
| | | * signed data. For each signature that is valid, the |
| | | * {@link Crypt_GPG_Signature::isValid()} will return true. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the provided data is not signed |
| | | * data. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG_Signature |
| | | */ |
| | | public function verify($signedData, $signature = '') |
| | | { |
| | | return $this->_verify($signedData, false, $signature); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ verifyFile() |
| | | |
| | | /** |
| | | * Verifies a signed file |
| | | * |
| | | * The {@link Crypt_GPG::decryptFile()} method may be used to get the |
| | | * original message if the signed data is not clearsigned and does not use |
| | | * a detached signature. |
| | | * |
| | | * @param string $filename the signed file to be verified. |
| | | * @param string $signature optional. If verifying a file signed using a |
| | | * detached signature, this must be the detached |
| | | * signature data. The file that was signed is |
| | | * specified in <kbd>$filename</kbd>. |
| | | * |
| | | * @return array an array of {@link Crypt_GPG_Signature} objects for the |
| | | * signed data. For each signature that is valid, the |
| | | * {@link Crypt_GPG_Signature::isValid()} will return true. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the provided data is not signed |
| | | * data. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG_Signature |
| | | */ |
| | | public function verifyFile($filename, $signature = '') |
| | | { |
| | | return $this->_verify($filename, true, $signature); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addDecryptKey() |
| | | |
| | | /** |
| | | * Adds a key to use for decryption |
| | | * |
| | | * @param mixed $key the key to use. This may be a key identifier, |
| | | * user id, fingerprint, {@link Crypt_GPG_Key} or |
| | | * {@link Crypt_GPG_SubKey}. The key must be able |
| | | * to encrypt. |
| | | * @param string $passphrase optional. The passphrase of the key required |
| | | * for decryption. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::decrypt() |
| | | * @see Crypt_GPG::decryptFile() |
| | | * @see Crypt_GPG::clearDecryptKeys() |
| | | * @see Crypt_GPG::_addKey() |
| | | * @see Crypt_GPG_DecryptStatusHandler |
| | | * |
| | | * @sensitive $passphrase |
| | | */ |
| | | public function addDecryptKey($key, $passphrase = null) |
| | | { |
| | | $this->_addKey($this->decryptKeys, true, false, $key, $passphrase); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addEncryptKey() |
| | | |
| | | /** |
| | | * Adds a key to use for encryption |
| | | * |
| | | * @param mixed $key the key to use. This may be a key identifier, user id |
| | | * user id, fingerprint, {@link Crypt_GPG_Key} or |
| | | * {@link Crypt_GPG_SubKey}. The key must be able to |
| | | * encrypt. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::encrypt() |
| | | * @see Crypt_GPG::encryptFile() |
| | | * @see Crypt_GPG::clearEncryptKeys() |
| | | * @see Crypt_GPG::_addKey() |
| | | */ |
| | | public function addEncryptKey($key) |
| | | { |
| | | $this->_addKey($this->encryptKeys, true, false, $key); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addSignKey() |
| | | |
| | | /** |
| | | * Adds a key to use for signing |
| | | * |
| | | * @param mixed $key the key to use. This may be a key identifier, |
| | | * user id, fingerprint, {@link Crypt_GPG_Key} or |
| | | * {@link Crypt_GPG_SubKey}. The key must be able |
| | | * to sign. |
| | | * @param string $passphrase optional. The passphrase of the key required |
| | | * for signing. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | * @see Crypt_GPG::signFile() |
| | | * @see Crypt_GPG::clearSignKeys() |
| | | * @see Crypt_GPG::handleSignStatus() |
| | | * @see Crypt_GPG::_addKey() |
| | | * |
| | | * @sensitive $passphrase |
| | | */ |
| | | public function addSignKey($key, $passphrase = null) |
| | | { |
| | | $this->_addKey($this->signKeys, false, true, $key, $passphrase); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ clearDecryptKeys() |
| | | |
| | | /** |
| | | * Clears all decryption keys |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::decrypt() |
| | | * @see Crypt_GPG::addDecryptKey() |
| | | */ |
| | | public function clearDecryptKeys() |
| | | { |
| | | $this->decryptKeys = array(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ clearEncryptKeys() |
| | | |
| | | /** |
| | | * Clears all encryption keys |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::encrypt() |
| | | * @see Crypt_GPG::addEncryptKey() |
| | | */ |
| | | public function clearEncryptKeys() |
| | | { |
| | | $this->encryptKeys = array(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ clearSignKeys() |
| | | |
| | | /** |
| | | * Clears all signing keys |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | * @see Crypt_GPG::addSignKey() |
| | | */ |
| | | public function clearSignKeys() |
| | | { |
| | | $this->signKeys = array(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ handleSignStatus() |
| | | |
| | | /** |
| | | * Handles the status output from GPG for the sign operation |
| | | * |
| | | * This method is responsible for sending the passphrase commands when |
| | | * required by the {@link Crypt_GPG::sign()} method. See <b>doc/DETAILS</b> |
| | | * in the {@link http://www.gnupg.org/download/ GPG distribution} for |
| | | * detailed information on GPG's status output. |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::sign() |
| | | */ |
| | | public function handleSignStatus($line) |
| | | { |
| | | $tokens = explode(' ', $line); |
| | | switch ($tokens[0]) { |
| | | case 'NEED_PASSPHRASE': |
| | | $subKeyId = $tokens[1]; |
| | | if (array_key_exists($subKeyId, $this->signKeys)) { |
| | | $passphrase = $this->signKeys[$subKeyId]['passphrase']; |
| | | $this->engine->sendCommand($passphrase); |
| | | } else { |
| | | $this->engine->sendCommand(''); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ handleImportKeyStatus() |
| | | |
| | | /** |
| | | * Handles the status output from GPG for the import operation |
| | | * |
| | | * This method is responsible for building the result array that is |
| | | * returned from the {@link Crypt_GPG::importKey()} method. See |
| | | * <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG distribution} for detailed |
| | | * information on GPG's status output. |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * @param array &$result the current result array being processed. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG::importKey() |
| | | * @see Crypt_GPG::importKeyFile() |
| | | * @see Crypt_GPG_Engine::addStatusHandler() |
| | | */ |
| | | public function handleImportKeyStatus($line, array &$result) |
| | | { |
| | | $tokens = explode(' ', $line); |
| | | switch ($tokens[0]) { |
| | | case 'IMPORT_OK': |
| | | $result['fingerprint'] = $tokens[2]; |
| | | break; |
| | | |
| | | case 'IMPORT_RES': |
| | | $result['public_imported'] = intval($tokens[3]); |
| | | $result['public_unchanged'] = intval($tokens[5]); |
| | | $result['private_imported'] = intval($tokens[11]); |
| | | $result['private_unchanged'] = intval($tokens[12]); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setEngine() |
| | | |
| | | /** |
| | | * Sets the I/O engine to use for GnuPG operations |
| | | * |
| | | * Normally this method does not need to be used. It provides a means for |
| | | * dependency injection. |
| | | * |
| | | * @param Crypt_GPG_Engine $engine the engine to use. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function setEngine(Crypt_GPG_Engine $engine) |
| | | { |
| | | $this->engine = $engine; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _addKey() |
| | | |
| | | /** |
| | | * Adds a key to one of the internal key arrays |
| | | * |
| | | * This handles resolving full key objects from the provided |
| | | * <kbd>$key</kbd> value. |
| | | * |
| | | * @param array &$array the array to which the key should be added. |
| | | * @param boolean $encrypt whether or not the key must be able to |
| | | * encrypt. |
| | | * @param boolean $sign whether or not the key must be able to sign. |
| | | * @param mixed $key the key to add. This may be a key identifier, |
| | | * user id, fingerprint, {@link Crypt_GPG_Key} or |
| | | * {@link Crypt_GPG_SubKey}. |
| | | * @param string $passphrase optional. The passphrase associated with the |
| | | * key. |
| | | * |
| | | * @return void |
| | | * |
| | | * @sensitive $passphrase |
| | | */ |
| | | private function _addKey(array &$array, $encrypt, $sign, $key, |
| | | $passphrase = null |
| | | ) { |
| | | $subKeys = array(); |
| | | |
| | | if (is_scalar($key)) { |
| | | $keys = $this->getKeys($key); |
| | | if (count($keys) == 0) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Key "' . $key . '" not found.', 0, $key); |
| | | } |
| | | $key = $keys[0]; |
| | | } |
| | | |
| | | if ($key instanceof Crypt_GPG_Key) { |
| | | if ($encrypt && !$key->canEncrypt()) { |
| | | throw new InvalidArgumentException( |
| | | 'Key "' . $key . '" cannot encrypt.'); |
| | | } |
| | | |
| | | if ($sign && !$key->canSign()) { |
| | | throw new InvalidArgumentException( |
| | | 'Key "' . $key . '" cannot sign.'); |
| | | } |
| | | |
| | | foreach ($key->getSubKeys() as $subKey) { |
| | | $canEncrypt = $subKey->canEncrypt(); |
| | | $canSign = $subKey->canSign(); |
| | | if ( ($encrypt && $sign && $canEncrypt && $canSign) |
| | | || ($encrypt && !$sign && $canEncrypt) |
| | | || (!$encrypt && $sign && $canSign) |
| | | ) { |
| | | // We add all subkeys that meet the requirements because we |
| | | // were not told which subkey is required. |
| | | $subKeys[] = $subKey; |
| | | } |
| | | } |
| | | } elseif ($key instanceof Crypt_GPG_SubKey) { |
| | | $subKeys[] = $key; |
| | | } |
| | | |
| | | if (count($subKeys) === 0) { |
| | | throw new InvalidArgumentException( |
| | | 'Key "' . $key . '" is not in a recognized format.'); |
| | | } |
| | | |
| | | foreach ($subKeys as $subKey) { |
| | | if ($encrypt && !$subKey->canEncrypt()) { |
| | | throw new InvalidArgumentException( |
| | | 'Key "' . $key . '" cannot encrypt.'); |
| | | } |
| | | |
| | | if ($sign && !$subKey->canSign()) { |
| | | throw new InvalidArgumentException( |
| | | 'Key "' . $key . '" cannot sign.'); |
| | | } |
| | | |
| | | $array[$subKey->getId()] = array( |
| | | 'fingerprint' => $subKey->getFingerprint(), |
| | | 'passphrase' => $passphrase |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _importKey() |
| | | |
| | | /** |
| | | * Imports a public or private key into the keyring |
| | | * |
| | | * @param string $key the key to be imported. |
| | | * @param boolean $isFile whether or not the input is a filename. |
| | | * |
| | | * @return array an associative array containing the following elements: |
| | | * - <kbd>fingerprint</kbd> - the fingerprint of the |
| | | * imported key, |
| | | * - <kbd>public_imported</kbd> - the number of public |
| | | * keys imported, |
| | | * - <kbd>public_unchanged</kbd> - the number of unchanged |
| | | * public keys, |
| | | * - <kbd>private_imported</kbd> - the number of private |
| | | * keys imported, |
| | | * - <kbd>private_unchanged</kbd> - the number of unchanged |
| | | * private keys. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the key data is missing or if the |
| | | * data is is not valid key data. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the key file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | private function _importKey($key, $isFile) |
| | | { |
| | | $result = array(); |
| | | |
| | | if ($isFile) { |
| | | $input = @fopen($key, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open key file "' . |
| | | $key . '" for importing.', 0, $key); |
| | | } |
| | | } else { |
| | | $input = strval($key); |
| | | if ($input == '') { |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'No valid GPG key data found.', Crypt_GPG::ERROR_NO_DATA); |
| | | } |
| | | } |
| | | |
| | | $arguments = array(); |
| | | $version = $this->engine->getVersion(); |
| | | |
| | | if ( version_compare($version, '1.0.5', 'ge') |
| | | && version_compare($version, '1.0.7', 'lt') |
| | | ) { |
| | | $arguments[] = '--allow-secret-key-import'; |
| | | } |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler( |
| | | array($this, 'handleImportKeyStatus'), |
| | | array(&$result) |
| | | ); |
| | | |
| | | $this->engine->setOperation('--import', $arguments); |
| | | $this->engine->setInput($input); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_DUPLICATE_KEY: |
| | | case Crypt_GPG::ERROR_NONE: |
| | | // ignore duplicate key import errors |
| | | break; |
| | | case Crypt_GPG::ERROR_NO_DATA: |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'No valid GPG key data found.', $code); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error importing GPG key. Please use the \'debug\' ' . |
| | | 'option when creating the Crypt_GPG object, and file a bug ' . |
| | | 'report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _encrypt() |
| | | |
| | | /** |
| | | * Encrypts data |
| | | * |
| | | * @param string $data the data to encrypt. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $outputFile the filename of the file in which to store |
| | | * the encrypted data. If null, the encrypted |
| | | * data is returned as a string. |
| | | * @param boolean $armor if true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. |
| | | * |
| | | * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a |
| | | * string containing the encrypted data is returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified. |
| | | * See {@link Crypt_GPG::addEncryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | private function _encrypt($data, $isFile, $outputFile, $armor) |
| | | { |
| | | if (count($this->encryptKeys) === 0) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'No encryption keys specified.'); |
| | | } |
| | | |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input file "' . |
| | | $data . '" for encryption.', 0, $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $output = ''; |
| | | } else { |
| | | $output = @fopen($outputFile, 'wb'); |
| | | if ($output === false) { |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | throw new Crypt_GPG_FileException('Could not open output ' . |
| | | 'file "' . $outputFile . '" for storing encrypted data.', |
| | | 0, $outputFile); |
| | | } |
| | | } |
| | | |
| | | $arguments = ($armor) ? array('--armor') : array(); |
| | | foreach ($this->encryptKeys as $key) { |
| | | $arguments[] = '--recipient ' . escapeshellarg($key['fingerprint']); |
| | | } |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->setInput($input); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation('--encrypt', $arguments); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | if ($outputFile !== null) { |
| | | fclose($output); |
| | | } |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | if ($code !== Crypt_GPG::ERROR_NONE) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error encrypting data. Please use the \'debug\' ' . |
| | | 'option when creating the Crypt_GPG object, and file a bug ' . |
| | | 'report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | return $output; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _decrypt() |
| | | |
| | | /** |
| | | * Decrypts data |
| | | * |
| | | * @param string $data the data to be decrypted. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $outputFile the name of the file to which the decrypted |
| | | * data should be written. If null, the decrypted |
| | | * data is returned as a string. |
| | | * |
| | | * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a |
| | | * string containing the decrypted data is returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | private function _decrypt($data, $isFile, $outputFile) |
| | | { |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input file "' . |
| | | $data . '" for decryption.', 0, $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | if ($input == '') { |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'Cannot decrypt data. No PGP encrypted data was found in '. |
| | | 'the provided data.', Crypt_GPG::ERROR_NO_DATA); |
| | | } |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $output = ''; |
| | | } else { |
| | | $output = @fopen($outputFile, 'wb'); |
| | | if ($output === false) { |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | throw new Crypt_GPG_FileException('Could not open output ' . |
| | | 'file "' . $outputFile . '" for storing decrypted data.', |
| | | 0, $outputFile); |
| | | } |
| | | } |
| | | |
| | | $handler = new Crypt_GPG_DecryptStatusHandler($this->engine, |
| | | $this->decryptKeys); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler(array($handler, 'handle')); |
| | | $this->engine->setOperation('--decrypt'); |
| | | $this->engine->setInput($input); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | if ($outputFile !== null) { |
| | | fclose($output); |
| | | } |
| | | |
| | | // if there was any problem decrypting the data, the handler will |
| | | // deal with it here. |
| | | $handler->throwException(); |
| | | |
| | | if ($outputFile === null) { |
| | | return $output; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _sign() |
| | | |
| | | /** |
| | | * Signs data |
| | | * |
| | | * @param string $data the data to be signed. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $outputFile the name of the file in which the signed data |
| | | * should be stored. If null, the signed data is |
| | | * returned as a string. |
| | | * @param boolean $mode the data signing mode to use. Should be one of |
| | | * {@link Crypt_GPG::SIGN_MODE_NORMAL}, |
| | | * {@link Crypt_GPG::SIGN_MODE_CLEAR} or |
| | | * {@link Crypt_GPG::SIGN_MODE_DETACHED}. |
| | | * @param boolean $armor if true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. This has |
| | | * no effect if the mode |
| | | * <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used. |
| | | * @param boolean $textmode if true, line-breaks in signed data be |
| | | * normalized. Use this option when signing |
| | | * e-mail, or for greater compatibility between |
| | | * systems with different line-break formats. |
| | | * Defaults to false. This has no effect if the |
| | | * mode <kbd>Crypt_GPG::SIGN_MODE_CLEAR</kbd> is |
| | | * used as clear-signing always uses textmode. |
| | | * |
| | | * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a |
| | | * string containing the signed data (or the signature |
| | | * data if a detached signature is requested) is |
| | | * returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no signing key is specified. |
| | | * See {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | private function _sign($data, $isFile, $outputFile, $mode, $armor, |
| | | $textmode |
| | | ) { |
| | | if (count($this->signKeys) === 0) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'No signing keys specified.'); |
| | | } |
| | | |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input ' . |
| | | 'file "' . $data . '" for signing.', 0, $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $output = ''; |
| | | } else { |
| | | $output = @fopen($outputFile, 'wb'); |
| | | if ($output === false) { |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | throw new Crypt_GPG_FileException('Could not open output ' . |
| | | 'file "' . $outputFile . '" for storing signed ' . |
| | | 'data.', 0, $outputFile); |
| | | } |
| | | } |
| | | |
| | | switch ($mode) { |
| | | case Crypt_GPG::SIGN_MODE_DETACHED: |
| | | $operation = '--detach-sign'; |
| | | break; |
| | | case Crypt_GPG::SIGN_MODE_CLEAR: |
| | | $operation = '--clearsign'; |
| | | break; |
| | | case Crypt_GPG::SIGN_MODE_NORMAL: |
| | | default: |
| | | $operation = '--sign'; |
| | | break; |
| | | } |
| | | |
| | | $arguments = array(); |
| | | |
| | | if ($armor) { |
| | | $arguments[] = '--armor'; |
| | | } |
| | | if ($textmode) { |
| | | $arguments[] = '--textmode'; |
| | | } |
| | | |
| | | foreach ($this->signKeys as $key) { |
| | | $arguments[] = '--local-user ' . |
| | | escapeshellarg($key['fingerprint']); |
| | | } |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler(array($this, 'handleSignStatus')); |
| | | $this->engine->setInput($input); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | if ($outputFile !== null) { |
| | | fclose($output); |
| | | } |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | break; |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Cannot sign data. Private key not found. Import the '. |
| | | 'private key before trying to sign data.', $code, |
| | | $this->engine->getErrorKeyId()); |
| | | case Crypt_GPG::ERROR_BAD_PASSPHRASE: |
| | | throw new Crypt_GPG_BadPassphraseException( |
| | | 'Cannot sign data. Incorrect passphrase provided.', $code); |
| | | case Crypt_GPG::ERROR_MISSING_PASSPHRASE: |
| | | throw new Crypt_GPG_BadPassphraseException( |
| | | 'Cannot sign data. No passphrase provided.', $code); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error signing data. Please use the \'debug\' option ' . |
| | | 'when creating the Crypt_GPG object, and file a bug report ' . |
| | | 'at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | return $output; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _encryptAndSign() |
| | | |
| | | /** |
| | | * Encrypts and signs data |
| | | * |
| | | * @param string $data the data to be encrypted and signed. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $outputFile the name of the file in which the encrypted, |
| | | * signed data should be stored. If null, the |
| | | * encrypted, signed data is returned as a |
| | | * string. |
| | | * @param boolean $armor if true, ASCII armored data is returned; |
| | | * otherwise, binary data is returned. |
| | | * |
| | | * @return void|string if the <kbd>$outputFile</kbd> parameter is null, a |
| | | * string containing the encrypted, signed data is |
| | | * returned. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if no encryption key is specified |
| | | * or if no signing key is specified. See |
| | | * {@link Crypt_GPG::addEncryptKey()} and |
| | | * {@link Crypt_GPG::addSignKey()}. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a specified passphrase is |
| | | * incorrect or if a required passphrase is not specified. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | private function _encryptAndSign($data, $isFile, $outputFile, $armor) |
| | | { |
| | | if (count($this->signKeys) === 0) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'No signing keys specified.'); |
| | | } |
| | | |
| | | if (count($this->encryptKeys) === 0) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'No encryption keys specified.'); |
| | | } |
| | | |
| | | |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input ' . |
| | | 'file "' . $data . '" for encrypting and signing.', 0, |
| | | $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $output = ''; |
| | | } else { |
| | | $output = @fopen($outputFile, 'wb'); |
| | | if ($output === false) { |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | throw new Crypt_GPG_FileException('Could not open output ' . |
| | | 'file "' . $outputFile . '" for storing encrypted, ' . |
| | | 'signed data.', 0, $outputFile); |
| | | } |
| | | } |
| | | |
| | | $arguments = ($armor) ? array('--armor') : array(); |
| | | |
| | | foreach ($this->signKeys as $key) { |
| | | $arguments[] = '--local-user ' . |
| | | escapeshellarg($key['fingerprint']); |
| | | } |
| | | |
| | | foreach ($this->encryptKeys as $key) { |
| | | $arguments[] = '--recipient ' . escapeshellarg($key['fingerprint']); |
| | | } |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler(array($this, 'handleSignStatus')); |
| | | $this->engine->setInput($input); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation('--encrypt --sign', $arguments); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | if ($outputFile !== null) { |
| | | fclose($output); |
| | | } |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | break; |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Cannot sign encrypted data. Private key not found. Import '. |
| | | 'the private key before trying to sign the encrypted data.', |
| | | $code, $this->engine->getErrorKeyId()); |
| | | case Crypt_GPG::ERROR_BAD_PASSPHRASE: |
| | | throw new Crypt_GPG_BadPassphraseException( |
| | | 'Cannot sign encrypted data. Incorrect passphrase provided.', |
| | | $code); |
| | | case Crypt_GPG::ERROR_MISSING_PASSPHRASE: |
| | | throw new Crypt_GPG_BadPassphraseException( |
| | | 'Cannot sign encrypted data. No passphrase provided.', $code); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error encrypting and signing data. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | return $output; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _verify() |
| | | |
| | | /** |
| | | * Verifies data |
| | | * |
| | | * @param string $data the signed data to be verified. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $signature if verifying a file signed using a detached |
| | | * signature, this must be the detached signature |
| | | * data. Otherwise, specify ''. |
| | | * |
| | | * @return array an array of {@link Crypt_GPG_Signature} objects for the |
| | | * signed data. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if the provided data is not signed |
| | | * data. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG_Signature |
| | | */ |
| | | private function _verify($data, $isFile, $signature) |
| | | { |
| | | if ($signature == '') { |
| | | $operation = '--verify'; |
| | | $arguments = array(); |
| | | } else { |
| | | // Signed data goes in FD_MESSAGE, detached signature data goes in |
| | | // FD_INPUT. |
| | | $operation = '--verify - "-&' . Crypt_GPG_Engine::FD_MESSAGE. '"'; |
| | | $arguments = array('--enable-special-filenames'); |
| | | } |
| | | |
| | | $handler = new Crypt_GPG_VerifyStatusHandler(); |
| | | |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input ' . |
| | | 'file "' . $data . '" for verifying.', 0, $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | if ($input == '') { |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'No valid signature data found.', Crypt_GPG::ERROR_NO_DATA); |
| | | } |
| | | } |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler(array($handler, 'handle')); |
| | | |
| | | if ($signature == '') { |
| | | // signed or clearsigned data |
| | | $this->engine->setInput($input); |
| | | } else { |
| | | // detached signature |
| | | $this->engine->setInput($signature); |
| | | $this->engine->setMessage($input); |
| | | } |
| | | |
| | | $this->engine->setOperation($operation, $arguments); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | $code = $this->engine->getErrorCode(); |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | case Crypt_GPG::ERROR_BAD_SIGNATURE: |
| | | break; |
| | | case Crypt_GPG::ERROR_NO_DATA: |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'No valid signature data found.', $code); |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Public key required for data verification not in keyring.', |
| | | $code, $this->engine->getErrorKeyId()); |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error validating signature details. Please use the ' . |
| | | '\'debug\' option when creating the Crypt_GPG object, and ' . |
| | | 'file a bug report at ' . self::BUG_URI, $code); |
| | | } |
| | | |
| | | return $handler->getSignatures(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _decryptAndVerify() |
| | | |
| | | /** |
| | | * Decrypts and verifies encrypted, signed data |
| | | * |
| | | * @param string $data the encrypted signed data to be decrypted and |
| | | * verified. |
| | | * @param boolean $isFile whether or not the data is a filename. |
| | | * @param string $outputFile the name of the file to which the decrypted |
| | | * data should be written. If null, the decrypted |
| | | * data is returned in the results array. |
| | | * |
| | | * @return array two element array. The array has an element 'data' |
| | | * containing the decrypted data and an element |
| | | * 'signatures' containing an array of |
| | | * {@link Crypt_GPG_Signature} objects for the signed data. |
| | | * If the decrypted data is written to a file, the 'data' |
| | | * element is null. |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring or it the public |
| | | * key needed for verification is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG signed, encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the output file is not writeable or |
| | | * if the input file is not readable. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @see Crypt_GPG_Signature |
| | | */ |
| | | private function _decryptAndVerify($data, $isFile, $outputFile) |
| | | { |
| | | if ($isFile) { |
| | | $input = @fopen($data, 'rb'); |
| | | if ($input === false) { |
| | | throw new Crypt_GPG_FileException('Could not open input ' . |
| | | 'file "' . $data . '" for decrypting and verifying.', 0, |
| | | $data); |
| | | } |
| | | } else { |
| | | $input = strval($data); |
| | | if ($input == '') { |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'No valid encrypted signed data found.', |
| | | Crypt_GPG::ERROR_NO_DATA); |
| | | } |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $output = ''; |
| | | } else { |
| | | $output = @fopen($outputFile, 'wb'); |
| | | if ($output === false) { |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | throw new Crypt_GPG_FileException('Could not open output ' . |
| | | 'file "' . $outputFile . '" for storing decrypted data.', |
| | | 0, $outputFile); |
| | | } |
| | | } |
| | | |
| | | $verifyHandler = new Crypt_GPG_VerifyStatusHandler(); |
| | | |
| | | $decryptHandler = new Crypt_GPG_DecryptStatusHandler($this->engine, |
| | | $this->decryptKeys); |
| | | |
| | | $this->engine->reset(); |
| | | $this->engine->addStatusHandler(array($verifyHandler, 'handle')); |
| | | $this->engine->addStatusHandler(array($decryptHandler, 'handle')); |
| | | $this->engine->setInput($input); |
| | | $this->engine->setOutput($output); |
| | | $this->engine->setOperation('--decrypt'); |
| | | $this->engine->run(); |
| | | |
| | | if ($isFile) { |
| | | fclose($input); |
| | | } |
| | | |
| | | if ($outputFile !== null) { |
| | | fclose($output); |
| | | } |
| | | |
| | | $return = array( |
| | | 'data' => null, |
| | | 'signatures' => $verifyHandler->getSignatures() |
| | | ); |
| | | |
| | | // if there was any problem decrypting the data, the handler will |
| | | // deal with it here. |
| | | try { |
| | | $decryptHandler->throwException(); |
| | | } catch (Exception $e) { |
| | | if ($e instanceof Crypt_GPG_KeyNotFoundException) { |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Public key required for data verification not in ', |
| | | 'the keyring. Either no suitable private decryption key ' . |
| | | 'is in the keyring or the public key required for data ' . |
| | | 'verification is not in the keyring. Import a suitable ' . |
| | | 'key before trying to decrypt and verify this data.', |
| | | self::ERROR_KEY_NOT_FOUND, $this->engine->getErrorKeyId()); |
| | | } |
| | | |
| | | if ($e instanceof Crypt_GPG_NoDataException) { |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'Cannot decrypt and verify data. No PGP encrypted data ' . |
| | | 'was found in the provided data.', self::ERROR_NO_DATA); |
| | | } |
| | | |
| | | throw $e; |
| | | } |
| | | |
| | | if ($outputFile === null) { |
| | | $return['data'] = $output; |
| | | } |
| | | |
| | | return $return; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Crypt_GPG is a package to use GPG from PHP |
| | | * |
| | | * This file contains an object that handles GPG's status output for the |
| | | * decrypt operation. |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008-2009 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: DecryptStatusHandler.php 302814 2010-08-26 15:43:07Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | |
| | | /** |
| | | * Crypt_GPG base class |
| | | */ |
| | | require_once 'Crypt/GPG.php'; |
| | | |
| | | /** |
| | | * GPG exception classes |
| | | */ |
| | | require_once 'Crypt/GPG/Exceptions.php'; |
| | | |
| | | |
| | | /** |
| | | * Status line handler for the decrypt operation |
| | | * |
| | | * This class is used internally by Crypt_GPG and does not need be used |
| | | * directly. See the {@link Crypt_GPG} class for end-user API. |
| | | * |
| | | * This class is responsible for sending the passphrase commands when required |
| | | * by the {@link Crypt_GPG::decrypt()} method. See <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG distribution} for detailed |
| | | * information on GPG's status output for the decrypt operation. |
| | | * |
| | | * This class is also responsible for parsing error status and throwing a |
| | | * meaningful exception in the event that decryption fails. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | class Crypt_GPG_DecryptStatusHandler |
| | | { |
| | | // {{{ protected properties |
| | | |
| | | /** |
| | | * Keys used to decrypt |
| | | * |
| | | * The array is of the form: |
| | | * <code> |
| | | * array( |
| | | * $key_id => array( |
| | | * 'fingerprint' => $fingerprint, |
| | | * 'passphrase' => $passphrase |
| | | * ) |
| | | * ); |
| | | * </code> |
| | | * |
| | | * @var array |
| | | */ |
| | | protected $keys = array(); |
| | | |
| | | /** |
| | | * Engine used to which passphrases are passed |
| | | * |
| | | * @var Crypt_GPG_Engine |
| | | */ |
| | | protected $engine = null; |
| | | |
| | | /** |
| | | * The id of the current sub-key used for decryption |
| | | * |
| | | * @var string |
| | | */ |
| | | protected $currentSubKey = ''; |
| | | |
| | | /** |
| | | * Whether or not decryption succeeded |
| | | * |
| | | * If the message is only signed (compressed) and not encrypted, this is |
| | | * always true. If the message is encrypted, this flag is set to false |
| | | * until we know the decryption succeeded. |
| | | * |
| | | * @var boolean |
| | | */ |
| | | protected $decryptionOkay = true; |
| | | |
| | | /** |
| | | * Whether or not there was no data for decryption |
| | | * |
| | | * @var boolean |
| | | */ |
| | | protected $noData = false; |
| | | |
| | | /** |
| | | * Keys for which the passhprase is missing |
| | | * |
| | | * This contains primary user ids indexed by sub-key id and is used to |
| | | * create helpful exception messages. |
| | | * |
| | | * @var array |
| | | */ |
| | | protected $missingPassphrases = array(); |
| | | |
| | | /** |
| | | * Keys for which the passhprase is incorrect |
| | | * |
| | | * This contains primary user ids indexed by sub-key id and is used to |
| | | * create helpful exception messages. |
| | | * |
| | | * @var array |
| | | */ |
| | | protected $badPassphrases = array(); |
| | | |
| | | /** |
| | | * Keys that can be used to decrypt the data but are missing from the |
| | | * keychain |
| | | * |
| | | * This is an array with both the key and value being the sub-key id of |
| | | * the missing keys. |
| | | * |
| | | * @var array |
| | | */ |
| | | protected $missingKeys = array(); |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new decryption status handler |
| | | * |
| | | * @param Crypt_GPG_Engine $engine the GPG engine to which passphrases are |
| | | * passed. |
| | | * @param array $keys the decryption keys to use. |
| | | */ |
| | | public function __construct(Crypt_GPG_Engine $engine, array $keys) |
| | | { |
| | | $this->engine = $engine; |
| | | $this->keys = $keys; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ handle() |
| | | |
| | | /** |
| | | * Handles a status line |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function handle($line) |
| | | { |
| | | $tokens = explode(' ', $line); |
| | | switch ($tokens[0]) { |
| | | case 'ENC_TO': |
| | | // Now we know the message is encrypted. Set flag to check if |
| | | // decryption succeeded. |
| | | $this->decryptionOkay = false; |
| | | |
| | | // this is the new key message |
| | | $this->currentSubKeyId = $tokens[1]; |
| | | break; |
| | | |
| | | case 'NEED_PASSPHRASE': |
| | | // send passphrase to the GPG engine |
| | | $subKeyId = $tokens[1]; |
| | | if (array_key_exists($subKeyId, $this->keys)) { |
| | | $passphrase = $this->keys[$subKeyId]['passphrase']; |
| | | $this->engine->sendCommand($passphrase); |
| | | } else { |
| | | $this->engine->sendCommand(''); |
| | | } |
| | | break; |
| | | |
| | | case 'USERID_HINT': |
| | | // remember the user id for pretty exception messages |
| | | $this->badPassphrases[$tokens[1]] |
| | | = implode(' ', array_splice($tokens, 2)); |
| | | |
| | | break; |
| | | |
| | | case 'GOOD_PASSPHRASE': |
| | | // if we got a good passphrase, remove the key from the list of |
| | | // bad passphrases. |
| | | unset($this->badPassphrases[$this->currentSubKeyId]); |
| | | break; |
| | | |
| | | case 'MISSING_PASSPHRASE': |
| | | $this->missingPassphrases[$this->currentSubKeyId] |
| | | = $this->currentSubKeyId; |
| | | |
| | | break; |
| | | |
| | | case 'NO_SECKEY': |
| | | // note: this message is also received if there are multiple |
| | | // recipients and a previous key had a correct passphrase. |
| | | $this->missingKeys[$tokens[1]] = $tokens[1]; |
| | | break; |
| | | |
| | | case 'NODATA': |
| | | $this->noData = true; |
| | | break; |
| | | |
| | | case 'DECRYPTION_OKAY': |
| | | // If the message is encrypted, this is the all-clear signal. |
| | | $this->decryptionOkay = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ throwException() |
| | | |
| | | /** |
| | | * Takes the final status of the decrypt operation and throws an |
| | | * appropriate exception |
| | | * |
| | | * If decryption was successful, no exception is thrown. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_KeyNotFoundException if the private key needed to |
| | | * decrypt the data is not in the user's keyring. |
| | | * |
| | | * @throws Crypt_GPG_NoDataException if specified data does not contain |
| | | * GPG encrypted data. |
| | | * |
| | | * @throws Crypt_GPG_BadPassphraseException if a required passphrase is |
| | | * incorrect or if a required passphrase is not specified. See |
| | | * {@link Crypt_GPG::addDecryptKey()}. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <i>debug</i> option and file a bug report if these |
| | | * exceptions occur. |
| | | */ |
| | | public function throwException() |
| | | { |
| | | $code = Crypt_GPG::ERROR_NONE; |
| | | |
| | | if (!$this->decryptionOkay) { |
| | | if (count($this->badPassphrases) > 0) { |
| | | $code = Crypt_GPG::ERROR_BAD_PASSPHRASE; |
| | | } elseif (count($this->missingKeys) > 0) { |
| | | $code = Crypt_GPG::ERROR_KEY_NOT_FOUND; |
| | | } else { |
| | | $code = Crypt_GPG::ERROR_UNKNOWN; |
| | | } |
| | | } elseif ($this->noData) { |
| | | $code = Crypt_GPG::ERROR_NO_DATA; |
| | | } |
| | | |
| | | switch ($code) { |
| | | case Crypt_GPG::ERROR_NONE: |
| | | break; |
| | | |
| | | case Crypt_GPG::ERROR_KEY_NOT_FOUND: |
| | | if (count($this->missingKeys) > 0) { |
| | | $keyId = reset($this->missingKeys); |
| | | } else { |
| | | $keyId = ''; |
| | | } |
| | | throw new Crypt_GPG_KeyNotFoundException( |
| | | 'Cannot decrypt data. No suitable private key is in the ' . |
| | | 'keyring. Import a suitable private key before trying to ' . |
| | | 'decrypt this data.', $code, $keyId); |
| | | |
| | | case Crypt_GPG::ERROR_BAD_PASSPHRASE: |
| | | $badPassphrases = array_diff_key( |
| | | $this->badPassphrases, |
| | | $this->missingPassphrases |
| | | ); |
| | | |
| | | $missingPassphrases = array_intersect_key( |
| | | $this->badPassphrases, |
| | | $this->missingPassphrases |
| | | ); |
| | | |
| | | $message = 'Cannot decrypt data.'; |
| | | if (count($badPassphrases) > 0) { |
| | | $message = ' Incorrect passphrase provided for keys: "' . |
| | | implode('", "', $badPassphrases) . '".'; |
| | | } |
| | | if (count($missingPassphrases) > 0) { |
| | | $message = ' No passphrase provided for keys: "' . |
| | | implode('", "', $badPassphrases) . '".'; |
| | | } |
| | | |
| | | throw new Crypt_GPG_BadPassphraseException($message, $code, |
| | | $badPassphrases, $missingPassphrases); |
| | | |
| | | case Crypt_GPG::ERROR_NO_DATA: |
| | | throw new Crypt_GPG_NoDataException( |
| | | 'Cannot decrypt data. No PGP encrypted data was found in '. |
| | | 'the provided data.', $code); |
| | | |
| | | default: |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error decrypting data.', $code); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Crypt_GPG is a package to use GPG from PHP |
| | | * |
| | | * This file contains an engine that handles GPG subprocess control and I/O. |
| | | * PHP's process manipulation functions are used to handle the GPG subprocess. |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: Engine.php 302822 2010-08-26 17:30:57Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | |
| | | /** |
| | | * Crypt_GPG base class. |
| | | */ |
| | | require_once 'Crypt/GPG.php'; |
| | | |
| | | /** |
| | | * GPG exception classes. |
| | | */ |
| | | require_once 'Crypt/GPG/Exceptions.php'; |
| | | |
| | | /** |
| | | * Standard PEAR exception is used if GPG binary is not found. |
| | | */ |
| | | require_once 'PEAR/Exception.php'; |
| | | |
| | | // {{{ class Crypt_GPG_Engine |
| | | |
| | | /** |
| | | * Native PHP Crypt_GPG I/O engine |
| | | * |
| | | * This class is used internally by Crypt_GPG and does not need be used |
| | | * directly. See the {@link Crypt_GPG} class for end-user API. |
| | | * |
| | | * This engine uses PHP's native process control functions to directly control |
| | | * the GPG process. The GPG executable is required to be on the system. |
| | | * |
| | | * All data is passed to the GPG subprocess using file descriptors. This is the |
| | | * most secure method of passing data to the GPG subprocess. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | class Crypt_GPG_Engine |
| | | { |
| | | // {{{ constants |
| | | |
| | | /** |
| | | * Size of data chunks that are sent to and retrieved from the IPC pipes. |
| | | * |
| | | * PHP reads 8192 bytes. If this is set to less than 8192, PHP reads 8192 |
| | | * and buffers the rest so we might as well just read 8192. |
| | | * |
| | | * Using values other than 8192 also triggers PHP bugs. |
| | | * |
| | | * @see http://bugs.php.net/bug.php?id=35224 |
| | | */ |
| | | const CHUNK_SIZE = 8192; |
| | | |
| | | /** |
| | | * Standard input file descriptor. This is used to pass data to the GPG |
| | | * process. |
| | | */ |
| | | const FD_INPUT = 0; |
| | | |
| | | /** |
| | | * Standard output file descriptor. This is used to receive normal output |
| | | * from the GPG process. |
| | | */ |
| | | const FD_OUTPUT = 1; |
| | | |
| | | /** |
| | | * Standard output file descriptor. This is used to receive error output |
| | | * from the GPG process. |
| | | */ |
| | | const FD_ERROR = 2; |
| | | |
| | | /** |
| | | * GPG status output file descriptor. The status file descriptor outputs |
| | | * detailed information for many GPG commands. See the second section of |
| | | * the file <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG package} for a detailed |
| | | * description of GPG's status output. |
| | | */ |
| | | const FD_STATUS = 3; |
| | | |
| | | /** |
| | | * Command input file descriptor. This is used for methods requiring |
| | | * passphrases. |
| | | */ |
| | | const FD_COMMAND = 4; |
| | | |
| | | /** |
| | | * Extra message input file descriptor. This is used for passing signed |
| | | * data when verifying a detached signature. |
| | | */ |
| | | const FD_MESSAGE = 5; |
| | | |
| | | /** |
| | | * Minimum version of GnuPG that is supported. |
| | | */ |
| | | const MIN_VERSION = '1.0.2'; |
| | | |
| | | // }}} |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * Whether or not to use debugging mode |
| | | * |
| | | * When set to true, every GPG command is echoed before it is run. Sensitive |
| | | * data is always handled using pipes and is not specified as part of the |
| | | * command. As a result, sensitive data is never displayed when debug is |
| | | * enabled. Sensitive data includes private key data and passphrases. |
| | | * |
| | | * Debugging is off by default. |
| | | * |
| | | * @var boolean |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | */ |
| | | private $_debug = false; |
| | | |
| | | /** |
| | | * Location of GPG binary |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | * @see Crypt_GPG_Engine::_getBinary() |
| | | */ |
| | | private $_binary = ''; |
| | | |
| | | /** |
| | | * Directory containing the GPG key files |
| | | * |
| | | * This property only contains the path when the <i>homedir</i> option |
| | | * is specified in the constructor. |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | */ |
| | | private $_homedir = ''; |
| | | |
| | | /** |
| | | * File path of the public keyring |
| | | * |
| | | * This property only contains the file path when the <i>public_keyring</i> |
| | | * option is specified in the constructor. |
| | | * |
| | | * If the specified file path starts with <kbd>~/</kbd>, the path is |
| | | * relative to the <i>homedir</i> if specified, otherwise to |
| | | * <kbd>~/.gnupg</kbd>. |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | */ |
| | | private $_publicKeyring = ''; |
| | | |
| | | /** |
| | | * File path of the private (secret) keyring |
| | | * |
| | | * This property only contains the file path when the <i>private_keyring</i> |
| | | * option is specified in the constructor. |
| | | * |
| | | * If the specified file path starts with <kbd>~/</kbd>, the path is |
| | | * relative to the <i>homedir</i> if specified, otherwise to |
| | | * <kbd>~/.gnupg</kbd>. |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | */ |
| | | private $_privateKeyring = ''; |
| | | |
| | | /** |
| | | * File path of the trust database |
| | | * |
| | | * This property only contains the file path when the <i>trust_db</i> |
| | | * option is specified in the constructor. |
| | | * |
| | | * If the specified file path starts with <kbd>~/</kbd>, the path is |
| | | * relative to the <i>homedir</i> if specified, otherwise to |
| | | * <kbd>~/.gnupg</kbd>. |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::__construct() |
| | | */ |
| | | private $_trustDb = ''; |
| | | |
| | | /** |
| | | * Array of pipes used for communication with the GPG binary |
| | | * |
| | | * This is an array of file descriptor resources. |
| | | * |
| | | * @var array |
| | | */ |
| | | private $_pipes = array(); |
| | | |
| | | /** |
| | | * Array of currently opened pipes |
| | | * |
| | | * This array is used to keep track of remaining opened pipes so they can |
| | | * be closed when the GPG subprocess is finished. This array is a subset of |
| | | * the {@link Crypt_GPG_Engine::$_pipes} array and contains opened file |
| | | * descriptor resources. |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG_Engine::_closePipe() |
| | | */ |
| | | private $_openPipes = array(); |
| | | |
| | | /** |
| | | * A handle for the GPG process |
| | | * |
| | | * @var resource |
| | | */ |
| | | private $_process = null; |
| | | |
| | | /** |
| | | * Whether or not the operating system is Darwin (OS X) |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_isDarwin = false; |
| | | |
| | | /** |
| | | * Commands to be sent to GPG's command input stream |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::sendCommand() |
| | | */ |
| | | private $_commandBuffer = ''; |
| | | |
| | | /** |
| | | * Array of status line handlers |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG_Engine::addStatusHandler() |
| | | */ |
| | | private $_statusHandlers = array(); |
| | | |
| | | /** |
| | | * Array of error line handlers |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG_Engine::addErrorHandler() |
| | | */ |
| | | private $_errorHandlers = array(); |
| | | |
| | | /** |
| | | * The error code of the current operation |
| | | * |
| | | * @var integer |
| | | * @see Crypt_GPG_Engine::getErrorCode() |
| | | */ |
| | | private $_errorCode = Crypt_GPG::ERROR_NONE; |
| | | |
| | | /** |
| | | * File related to the error code of the current operation |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::getErrorFilename() |
| | | */ |
| | | private $_errorFilename = ''; |
| | | |
| | | /** |
| | | * Key id related to the error code of the current operation |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::getErrorKeyId() |
| | | */ |
| | | private $_errorkeyId = ''; |
| | | |
| | | /** |
| | | * The number of currently needed passphrases |
| | | * |
| | | * If this is not zero when the GPG command is completed, the error code is |
| | | * set to {@link Crypt_GPG::ERROR_MISSING_PASSPHRASE}. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_needPassphrase = 0; |
| | | |
| | | /** |
| | | * The input source |
| | | * |
| | | * This is data to send to GPG. Either a string or a stream resource. |
| | | * |
| | | * @var string|resource |
| | | * @see Crypt_GPG_Engine::setInput() |
| | | */ |
| | | private $_input = null; |
| | | |
| | | /** |
| | | * The extra message input source |
| | | * |
| | | * Either a string or a stream resource. |
| | | * |
| | | * @var string|resource |
| | | * @see Crypt_GPG_Engine::setMessage() |
| | | */ |
| | | private $_message = null; |
| | | |
| | | /** |
| | | * The output location |
| | | * |
| | | * This is where the output from GPG is sent. Either a string or a stream |
| | | * resource. |
| | | * |
| | | * @var string|resource |
| | | * @see Crypt_GPG_Engine::setOutput() |
| | | */ |
| | | private $_output = ''; |
| | | |
| | | /** |
| | | * The GPG operation to execute |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::setOperation() |
| | | */ |
| | | private $_operation; |
| | | |
| | | /** |
| | | * Arguments for the current operation |
| | | * |
| | | * @var array |
| | | * @see Crypt_GPG_Engine::setOperation() |
| | | */ |
| | | private $_arguments = array(); |
| | | |
| | | /** |
| | | * The version number of the GPG binary |
| | | * |
| | | * @var string |
| | | * @see Crypt_GPG_Engine::getVersion() |
| | | */ |
| | | private $_version = ''; |
| | | |
| | | /** |
| | | * Cached value indicating whether or not mbstring function overloading is |
| | | * on for strlen |
| | | * |
| | | * This is cached for optimal performance inside the I/O loop. |
| | | * |
| | | * @var boolean |
| | | * @see Crypt_GPG_Engine::_byteLength() |
| | | * @see Crypt_GPG_Engine::_byteSubstring() |
| | | */ |
| | | private static $_mbStringOverload = null; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new GPG engine |
| | | * |
| | | * Available options are: |
| | | * |
| | | * - <kbd>string homedir</kbd> - the directory where the GPG |
| | | * keyring files are stored. If not |
| | | * specified, Crypt_GPG uses the |
| | | * default of <kbd>~/.gnupg</kbd>. |
| | | * - <kbd>string publicKeyring</kbd> - the file path of the public |
| | | * keyring. Use this if the public |
| | | * keyring is not in the homedir, or |
| | | * if the keyring is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * keyring with this option |
| | | * (/foo/bar/pubring.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string privateKeyring</kbd> - the file path of the private |
| | | * keyring. Use this if the private |
| | | * keyring is not in the homedir, or |
| | | * if the keyring is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * keyring with this option |
| | | * (/foo/bar/secring.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string trustDb</kbd> - the file path of the web-of-trust |
| | | * database. Use this if the trust |
| | | * database is not in the homedir, or |
| | | * if the database is in a directory |
| | | * not writable by the process |
| | | * invoking GPG (like Apache). Then |
| | | * you can specify the path to the |
| | | * trust database with this option |
| | | * (/foo/bar/trustdb.gpg), and specify |
| | | * a writable directory (like /tmp) |
| | | * using the <i>homedir</i> option. |
| | | * - <kbd>string binary</kbd> - the location of the GPG binary. If |
| | | * not specified, the driver attempts |
| | | * to auto-detect the GPG binary |
| | | * location using a list of known |
| | | * default locations for the current |
| | | * operating system. The option |
| | | * <kbd>gpgBinary</kbd> is a |
| | | * deprecated alias for this option. |
| | | * - <kbd>boolean debug</kbd> - whether or not to use debug mode. |
| | | * When debug mode is on, all |
| | | * communication to and from the GPG |
| | | * subprocess is logged. This can be |
| | | * useful to diagnose errors when |
| | | * using Crypt_GPG. |
| | | * |
| | | * @param array $options optional. An array of options used to create the |
| | | * GPG object. All options are optional and are |
| | | * represented as key-value pairs. |
| | | * |
| | | * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist |
| | | * and cannot be created. This can happen if <kbd>homedir</kbd> is |
| | | * not specified, Crypt_GPG is run as the web user, and the web |
| | | * user has no home directory. This exception is also thrown if any |
| | | * of the options <kbd>publicKeyring</kbd>, |
| | | * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are |
| | | * specified but the files do not exist or are are not readable. |
| | | * This can happen if the user running the Crypt_GPG process (for |
| | | * example, the Apache user) does not have permission to read the |
| | | * files. |
| | | * |
| | | * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or |
| | | * if no <kbd>binary</kbd> is provided and no suitable binary could |
| | | * be found. |
| | | */ |
| | | public function __construct(array $options = array()) |
| | | { |
| | | $this->_isDarwin = (strncmp(strtoupper(PHP_OS), 'DARWIN', 6) === 0); |
| | | |
| | | // populate mbstring overloading cache if not set |
| | | if (self::$_mbStringOverload === null) { |
| | | self::$_mbStringOverload = (extension_loaded('mbstring') |
| | | && (ini_get('mbstring.func_overload') & 0x02) === 0x02); |
| | | } |
| | | |
| | | // get homedir |
| | | if (array_key_exists('homedir', $options)) { |
| | | $this->_homedir = (string)$options['homedir']; |
| | | } else { |
| | | // note: this requires the package OS dep exclude 'windows' |
| | | $info = posix_getpwuid(posix_getuid()); |
| | | $this->_homedir = $info['dir'].'/.gnupg'; |
| | | } |
| | | |
| | | // attempt to create homedir if it does not exist |
| | | if (!is_dir($this->_homedir)) { |
| | | if (@mkdir($this->_homedir, 0777, true)) { |
| | | // Set permissions on homedir. Parent directories are created |
| | | // with 0777, homedir is set to 0700. |
| | | chmod($this->_homedir, 0700); |
| | | } else { |
| | | throw new Crypt_GPG_FileException('The \'homedir\' "' . |
| | | $this->_homedir . '" is not readable or does not exist '. |
| | | 'and cannot be created. This can happen if \'homedir\' '. |
| | | 'is not specified in the Crypt_GPG options, Crypt_GPG is '. |
| | | 'run as the web user, and the web user has no home '. |
| | | 'directory.', |
| | | 0, $this->_homedir); |
| | | } |
| | | } |
| | | |
| | | // get binary |
| | | if (array_key_exists('binary', $options)) { |
| | | $this->_binary = (string)$options['binary']; |
| | | } elseif (array_key_exists('gpgBinary', $options)) { |
| | | // deprecated alias |
| | | $this->_binary = (string)$options['gpgBinary']; |
| | | } else { |
| | | $this->_binary = $this->_getBinary(); |
| | | } |
| | | |
| | | if ($this->_binary == '' || !is_executable($this->_binary)) { |
| | | throw new PEAR_Exception('GPG binary not found. If you are sure '. |
| | | 'the GPG binary is installed, please specify the location of '. |
| | | 'the GPG binary using the \'binary\' driver option.'); |
| | | } |
| | | |
| | | /* |
| | | * Note: |
| | | * |
| | | * Normally, GnuPG expects keyrings to be in the homedir and expects |
| | | * to be able to write temporary files in the homedir. Sometimes, |
| | | * keyrings are not in the homedir, or location of the keyrings does |
| | | * not allow writing temporary files. In this case, the <i>homedir</i> |
| | | * option by itself is not enough to specify the keyrings because GnuPG |
| | | * can not write required temporary files. Additional options are |
| | | * provided so you can specify the location of the keyrings separately |
| | | * from the homedir. |
| | | */ |
| | | |
| | | // get public keyring |
| | | if (array_key_exists('publicKeyring', $options)) { |
| | | $this->_publicKeyring = (string)$options['publicKeyring']; |
| | | if (!is_readable($this->_publicKeyring)) { |
| | | throw new Crypt_GPG_FileException('The \'publicKeyring\' "' . |
| | | $this->_publicKeyring . '" does not exist or is ' . |
| | | 'not readable. Check the location and ensure the file ' . |
| | | 'permissions are correct.', 0, $this->_publicKeyring); |
| | | } |
| | | } |
| | | |
| | | // get private keyring |
| | | if (array_key_exists('privateKeyring', $options)) { |
| | | $this->_privateKeyring = (string)$options['privateKeyring']; |
| | | if (!is_readable($this->_privateKeyring)) { |
| | | throw new Crypt_GPG_FileException('The \'privateKeyring\' "' . |
| | | $this->_privateKeyring . '" does not exist or is ' . |
| | | 'not readable. Check the location and ensure the file ' . |
| | | 'permissions are correct.', 0, $this->_privateKeyring); |
| | | } |
| | | } |
| | | |
| | | // get trust database |
| | | if (array_key_exists('trustDb', $options)) { |
| | | $this->_trustDb = (string)$options['trustDb']; |
| | | if (!is_readable($this->_trustDb)) { |
| | | throw new Crypt_GPG_FileException('The \'trustDb\' "' . |
| | | $this->_trustDb . '" does not exist or is not readable. ' . |
| | | 'Check the location and ensure the file permissions are ' . |
| | | 'correct.', 0, $this->_trustDb); |
| | | } |
| | | } |
| | | |
| | | if (array_key_exists('debug', $options)) { |
| | | $this->_debug = (boolean)$options['debug']; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ __destruct() |
| | | |
| | | /** |
| | | * Closes open GPG subprocesses when this object is destroyed |
| | | * |
| | | * Subprocesses should never be left open by this class unless there is |
| | | * an unknown error and unexpected script termination occurs. |
| | | */ |
| | | public function __destruct() |
| | | { |
| | | $this->_closeSubprocess(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addErrorHandler() |
| | | |
| | | /** |
| | | * Adds an error handler method |
| | | * |
| | | * The method is run every time a new error line is received from the GPG |
| | | * subprocess. The handler method must accept the error line to be handled |
| | | * as its first parameter. |
| | | * |
| | | * @param callback $callback the callback method to use. |
| | | * @param array $args optional. Additional arguments to pass as |
| | | * parameters to the callback method. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function addErrorHandler($callback, array $args = array()) |
| | | { |
| | | $this->_errorHandlers[] = array( |
| | | 'callback' => $callback, |
| | | 'args' => $args |
| | | ); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addStatusHandler() |
| | | |
| | | /** |
| | | * Adds a status handler method |
| | | * |
| | | * The method is run every time a new status line is received from the |
| | | * GPG subprocess. The handler method must accept the status line to be |
| | | * handled as its first parameter. |
| | | * |
| | | * @param callback $callback the callback method to use. |
| | | * @param array $args optional. Additional arguments to pass as |
| | | * parameters to the callback method. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function addStatusHandler($callback, array $args = array()) |
| | | { |
| | | $this->_statusHandlers[] = array( |
| | | 'callback' => $callback, |
| | | 'args' => $args |
| | | ); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ sendCommand() |
| | | |
| | | /** |
| | | * Sends a command to the GPG subprocess over the command file-descriptor |
| | | * pipe |
| | | * |
| | | * @param string $command the command to send. |
| | | * |
| | | * @return void |
| | | * |
| | | * @sensitive $command |
| | | */ |
| | | public function sendCommand($command) |
| | | { |
| | | if (array_key_exists(self::FD_COMMAND, $this->_openPipes)) { |
| | | $this->_commandBuffer .= $command . PHP_EOL; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ reset() |
| | | |
| | | /** |
| | | * Resets the GPG engine, preparing it for a new operation |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG_Engine::run() |
| | | * @see Crypt_GPG_Engine::setOperation() |
| | | */ |
| | | public function reset() |
| | | { |
| | | $this->_operation = ''; |
| | | $this->_arguments = array(); |
| | | $this->_input = null; |
| | | $this->_message = null; |
| | | $this->_output = ''; |
| | | $this->_errorCode = Crypt_GPG::ERROR_NONE; |
| | | $this->_needPassphrase = 0; |
| | | $this->_commandBuffer = ''; |
| | | |
| | | $this->_statusHandlers = array(); |
| | | $this->_errorHandlers = array(); |
| | | |
| | | $this->addStatusHandler(array($this, '_handleErrorStatus')); |
| | | $this->addErrorHandler(array($this, '_handleErrorError')); |
| | | |
| | | if ($this->_debug) { |
| | | $this->addStatusHandler(array($this, '_handleDebugStatus')); |
| | | $this->addErrorHandler(array($this, '_handleDebugError')); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ run() |
| | | |
| | | /** |
| | | * Runs the current GPG operation |
| | | * |
| | | * This creates and manages the GPG subprocess. |
| | | * |
| | | * The operation must be set with {@link Crypt_GPG_Engine::setOperation()} |
| | | * before this method is called. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_InvalidOperationException if no operation is specified. |
| | | * |
| | | * @see Crypt_GPG_Engine::reset() |
| | | * @see Crypt_GPG_Engine::setOperation() |
| | | */ |
| | | public function run() |
| | | { |
| | | if ($this->_operation === '') { |
| | | throw new Crypt_GPG_InvalidOperationException('No GPG operation ' . |
| | | 'specified. Use Crypt_GPG_Engine::setOperation() before ' . |
| | | 'calling Crypt_GPG_Engine::run().'); |
| | | } |
| | | |
| | | $this->_openSubprocess(); |
| | | $this->_process(); |
| | | $this->_closeSubprocess(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getErrorCode() |
| | | |
| | | /** |
| | | * Gets the error code of the last executed operation |
| | | * |
| | | * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has |
| | | * been executed. |
| | | * |
| | | * @return integer the error code of the last executed operation. |
| | | */ |
| | | public function getErrorCode() |
| | | { |
| | | return $this->_errorCode; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getErrorFilename() |
| | | |
| | | /** |
| | | * Gets the file related to the error code of the last executed operation |
| | | * |
| | | * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has |
| | | * been executed. If there is no file related to the error, an empty string |
| | | * is returned. |
| | | * |
| | | * @return string the file related to the error code of the last executed |
| | | * operation. |
| | | */ |
| | | public function getErrorFilename() |
| | | { |
| | | return $this->_errorFilename; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getErrorKeyId() |
| | | |
| | | /** |
| | | * Gets the key id related to the error code of the last executed operation |
| | | * |
| | | * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has |
| | | * been executed. If there is no key id related to the error, an empty |
| | | * string is returned. |
| | | * |
| | | * @return string the key id related to the error code of the last executed |
| | | * operation. |
| | | */ |
| | | public function getErrorKeyId() |
| | | { |
| | | return $this->_errorKeyId; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setInput() |
| | | |
| | | /** |
| | | * Sets the input source for the current GPG operation |
| | | * |
| | | * @param string|resource &$input either a reference to the string |
| | | * containing the input data or an open |
| | | * stream resource containing the input |
| | | * data. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function setInput(&$input) |
| | | { |
| | | $this->_input =& $input; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setMessage() |
| | | |
| | | /** |
| | | * Sets the message source for the current GPG operation |
| | | * |
| | | * Detached signature data should be specified here. |
| | | * |
| | | * @param string|resource &$message either a reference to the string |
| | | * containing the message data or an open |
| | | * stream resource containing the message |
| | | * data. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function setMessage(&$message) |
| | | { |
| | | $this->_message =& $message; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setOutput() |
| | | |
| | | /** |
| | | * Sets the output destination for the current GPG operation |
| | | * |
| | | * @param string|resource &$output either a reference to the string in |
| | | * which to store GPG output or an open |
| | | * stream resource to which the output data |
| | | * should be written. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function setOutput(&$output) |
| | | { |
| | | $this->_output =& $output; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setOperation() |
| | | |
| | | /** |
| | | * Sets the operation to perform |
| | | * |
| | | * @param string $operation the operation to perform. This should be one |
| | | * of GPG's operations. For example, |
| | | * <kbd>--encrypt</kbd>, <kbd>--decrypt</kbd>, |
| | | * <kbd>--sign</kbd>, etc. |
| | | * @param array $arguments optional. Additional arguments for the GPG |
| | | * subprocess. See the GPG manual for specific |
| | | * values. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG_Engine::reset() |
| | | * @see Crypt_GPG_Engine::run() |
| | | */ |
| | | public function setOperation($operation, array $arguments = array()) |
| | | { |
| | | $this->_operation = $operation; |
| | | $this->_arguments = $arguments; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getVersion() |
| | | |
| | | /** |
| | | * Gets the version of the GnuPG binary |
| | | * |
| | | * @return string a version number string containing the version of GnuPG |
| | | * being used. This value is suitable to use with PHP's |
| | | * version_compare() function. |
| | | * |
| | | * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. |
| | | * Use the <kbd>debug</kbd> option and file a bug report if these |
| | | * exceptions occur. |
| | | * |
| | | * @throws Crypt_GPG_UnsupportedException if the provided binary is not |
| | | * GnuPG or if the GnuPG version is less than 1.0.2. |
| | | */ |
| | | public function getVersion() |
| | | { |
| | | if ($this->_version == '') { |
| | | |
| | | $options = array( |
| | | 'homedir' => $this->_homedir, |
| | | 'binary' => $this->_binary, |
| | | 'debug' => $this->_debug |
| | | ); |
| | | |
| | | $engine = new self($options); |
| | | $info = ''; |
| | | |
| | | // Set a garbage version so we do not end up looking up the version |
| | | // recursively. |
| | | $engine->_version = '1.0.0'; |
| | | |
| | | $engine->reset(); |
| | | $engine->setOutput($info); |
| | | $engine->setOperation('--version'); |
| | | $engine->run(); |
| | | |
| | | $code = $this->getErrorCode(); |
| | | |
| | | if ($code !== Crypt_GPG::ERROR_NONE) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'Unknown error getting GnuPG version information. Please ' . |
| | | 'use the \'debug\' option when creating the Crypt_GPG ' . |
| | | 'object, and file a bug report at ' . Crypt_GPG::BUG_URI, |
| | | $code); |
| | | } |
| | | |
| | | $matches = array(); |
| | | $expression = '/gpg \(GnuPG\) (\S+)/'; |
| | | |
| | | if (preg_match($expression, $info, $matches) === 1) { |
| | | $this->_version = $matches[1]; |
| | | } else { |
| | | throw new Crypt_GPG_Exception( |
| | | 'No GnuPG version information provided by the binary "' . |
| | | $this->_binary . '". Are you sure it is GnuPG?'); |
| | | } |
| | | |
| | | if (version_compare($this->_version, self::MIN_VERSION, 'lt')) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'The version of GnuPG being used (' . $this->_version . |
| | | ') is not supported by Crypt_GPG. The minimum version ' . |
| | | 'required by Crypt_GPG is ' . self::MIN_VERSION); |
| | | } |
| | | } |
| | | |
| | | |
| | | return $this->_version; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _handleErrorStatus() |
| | | |
| | | /** |
| | | * Handles error values in the status output from GPG |
| | | * |
| | | * This method is responsible for setting the |
| | | * {@link Crypt_GPG_Engine::$_errorCode}. See <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG distribution} for detailed |
| | | * information on GPG's status output. |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _handleErrorStatus($line) |
| | | { |
| | | $tokens = explode(' ', $line); |
| | | switch ($tokens[0]) { |
| | | case 'BAD_PASSPHRASE': |
| | | $this->_errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE; |
| | | break; |
| | | |
| | | case 'MISSING_PASSPHRASE': |
| | | $this->_errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE; |
| | | break; |
| | | |
| | | case 'NODATA': |
| | | $this->_errorCode = Crypt_GPG::ERROR_NO_DATA; |
| | | break; |
| | | |
| | | case 'DELETE_PROBLEM': |
| | | if ($tokens[1] == '1') { |
| | | $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; |
| | | break; |
| | | } elseif ($tokens[1] == '2') { |
| | | $this->_errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY; |
| | | break; |
| | | } |
| | | break; |
| | | |
| | | case 'IMPORT_RES': |
| | | if ($tokens[12] > 0) { |
| | | $this->_errorCode = Crypt_GPG::ERROR_DUPLICATE_KEY; |
| | | } |
| | | break; |
| | | |
| | | case 'NO_PUBKEY': |
| | | case 'NO_SECKEY': |
| | | $this->_errorKeyId = $tokens[1]; |
| | | $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; |
| | | break; |
| | | |
| | | case 'NEED_PASSPHRASE': |
| | | $this->_needPassphrase++; |
| | | break; |
| | | |
| | | case 'GOOD_PASSPHRASE': |
| | | $this->_needPassphrase--; |
| | | break; |
| | | |
| | | case 'EXPSIG': |
| | | case 'EXPKEYSIG': |
| | | case 'REVKEYSIG': |
| | | case 'BADSIG': |
| | | $this->_errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE; |
| | | break; |
| | | |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _handleErrorError() |
| | | |
| | | /** |
| | | * Handles error values in the error output from GPG |
| | | * |
| | | * This method is responsible for setting the |
| | | * {@link Crypt_GPG_Engine::$_errorCode}. |
| | | * |
| | | * @param string $line the error line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _handleErrorError($line) |
| | | { |
| | | if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { |
| | | $pattern = '/no valid OpenPGP data found/'; |
| | | if (preg_match($pattern, $line) === 1) { |
| | | $this->_errorCode = Crypt_GPG::ERROR_NO_DATA; |
| | | } |
| | | } |
| | | |
| | | if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { |
| | | $pattern = '/No secret key|secret key not available/'; |
| | | if (preg_match($pattern, $line) === 1) { |
| | | $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; |
| | | } |
| | | } |
| | | |
| | | if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { |
| | | $pattern = '/No public key|public key not found/'; |
| | | if (preg_match($pattern, $line) === 1) { |
| | | $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; |
| | | } |
| | | } |
| | | |
| | | if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { |
| | | $matches = array(); |
| | | $pattern = '/can\'t (?:access|open) `(.*?)\'/'; |
| | | if (preg_match($pattern, $line, $matches) === 1) { |
| | | $this->_errorFilename = $matches[1]; |
| | | $this->_errorCode = Crypt_GPG::ERROR_FILE_PERMISSIONS; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _handleDebugStatus() |
| | | |
| | | /** |
| | | * Displays debug output for status lines |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _handleDebugStatus($line) |
| | | { |
| | | $this->_debug('STATUS: ' . $line); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _handleDebugError() |
| | | |
| | | /** |
| | | * Displays debug output for error lines |
| | | * |
| | | * @param string $line the error line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _handleDebugError($line) |
| | | { |
| | | $this->_debug('ERROR: ' . $line); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _process() |
| | | |
| | | /** |
| | | * Performs internal streaming operations for the subprocess using either |
| | | * strings or streams as input / output points |
| | | * |
| | | * This is the main I/O loop for streaming to and from the GPG subprocess. |
| | | * |
| | | * The implementation of this method is verbose mainly for performance |
| | | * reasons. Adding streams to a lookup array and looping the array inside |
| | | * the main I/O loop would be siginficantly slower for large streams. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_Exception if there is an error selecting streams for |
| | | * reading or writing. If this occurs, please file a bug report at |
| | | * http://pear.php.net/bugs/report.php?package=Crypt_GPG. |
| | | */ |
| | | private function _process() |
| | | { |
| | | $this->_debug('BEGIN PROCESSING'); |
| | | |
| | | $this->_commandBuffer = ''; // buffers input to GPG |
| | | $messageBuffer = ''; // buffers input to GPG |
| | | $inputBuffer = ''; // buffers input to GPG |
| | | $outputBuffer = ''; // buffers output from GPG |
| | | $statusBuffer = ''; // buffers output from GPG |
| | | $errorBuffer = ''; // buffers output from GPG |
| | | $inputComplete = false; // input stream is completely buffered |
| | | $messageComplete = false; // message stream is completely buffered |
| | | |
| | | if (is_string($this->_input)) { |
| | | $inputBuffer = $this->_input; |
| | | $inputComplete = true; |
| | | } |
| | | |
| | | if (is_string($this->_message)) { |
| | | $messageBuffer = $this->_message; |
| | | $messageComplete = true; |
| | | } |
| | | |
| | | if (is_string($this->_output)) { |
| | | $outputBuffer =& $this->_output; |
| | | } |
| | | |
| | | // convenience variables |
| | | $fdInput = $this->_pipes[self::FD_INPUT]; |
| | | $fdOutput = $this->_pipes[self::FD_OUTPUT]; |
| | | $fdError = $this->_pipes[self::FD_ERROR]; |
| | | $fdStatus = $this->_pipes[self::FD_STATUS]; |
| | | $fdCommand = $this->_pipes[self::FD_COMMAND]; |
| | | $fdMessage = $this->_pipes[self::FD_MESSAGE]; |
| | | |
| | | while (true) { |
| | | |
| | | $inputStreams = array(); |
| | | $outputStreams = array(); |
| | | $exceptionStreams = array(); |
| | | |
| | | // set up input streams |
| | | if (is_resource($this->_input) && !$inputComplete) { |
| | | if (feof($this->_input)) { |
| | | $inputComplete = true; |
| | | } else { |
| | | $inputStreams[] = $this->_input; |
| | | } |
| | | } |
| | | |
| | | // close GPG input pipe if there is no more data |
| | | if ($inputBuffer == '' && $inputComplete) { |
| | | $this->_debug('=> closing GPG input pipe'); |
| | | $this->_closePipe(self::FD_INPUT); |
| | | } |
| | | |
| | | if (is_resource($this->_message) && !$messageComplete) { |
| | | if (feof($this->_message)) { |
| | | $messageComplete = true; |
| | | } else { |
| | | $inputStreams[] = $this->_message; |
| | | } |
| | | } |
| | | |
| | | // close GPG message pipe if there is no more data |
| | | if ($messageBuffer == '' && $messageComplete) { |
| | | $this->_debug('=> closing GPG message pipe'); |
| | | $this->_closePipe(self::FD_MESSAGE); |
| | | } |
| | | |
| | | if (!feof($fdOutput)) { |
| | | $inputStreams[] = $fdOutput; |
| | | } |
| | | |
| | | if (!feof($fdStatus)) { |
| | | $inputStreams[] = $fdStatus; |
| | | } |
| | | |
| | | if (!feof($fdError)) { |
| | | $inputStreams[] = $fdError; |
| | | } |
| | | |
| | | // set up output streams |
| | | if ($outputBuffer != '' && is_resource($this->_output)) { |
| | | $outputStreams[] = $this->_output; |
| | | } |
| | | |
| | | if ($this->_commandBuffer != '') { |
| | | $outputStreams[] = $fdCommand; |
| | | } |
| | | |
| | | if ($messageBuffer != '') { |
| | | $outputStreams[] = $fdMessage; |
| | | } |
| | | |
| | | if ($inputBuffer != '') { |
| | | $outputStreams[] = $fdInput; |
| | | } |
| | | |
| | | // no streams left to read or write, we're all done |
| | | if (count($inputStreams) === 0 && count($outputStreams) === 0) { |
| | | break; |
| | | } |
| | | |
| | | $this->_debug('selecting streams'); |
| | | |
| | | $ready = stream_select( |
| | | $inputStreams, |
| | | $outputStreams, |
| | | $exceptionStreams, |
| | | null |
| | | ); |
| | | |
| | | $this->_debug('=> got ' . $ready); |
| | | |
| | | if ($ready === false) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'Error selecting stream for communication with GPG ' . |
| | | 'subprocess. Please file a bug report at: ' . |
| | | 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'); |
| | | } |
| | | |
| | | if ($ready === 0) { |
| | | throw new Crypt_GPG_Exception( |
| | | 'stream_select() returned 0. This can not happen! Please ' . |
| | | 'file a bug report at: ' . |
| | | 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'); |
| | | } |
| | | |
| | | // write input (to GPG) |
| | | if (in_array($fdInput, $outputStreams)) { |
| | | $this->_debug('GPG is ready for input'); |
| | | |
| | | $chunk = self::_byteSubstring( |
| | | $inputBuffer, |
| | | 0, |
| | | self::CHUNK_SIZE |
| | | ); |
| | | |
| | | $length = self::_byteLength($chunk); |
| | | |
| | | $this->_debug( |
| | | '=> about to write ' . $length . ' bytes to GPG input' |
| | | ); |
| | | |
| | | $length = fwrite($fdInput, $chunk, $length); |
| | | |
| | | $this->_debug('=> wrote ' . $length . ' bytes'); |
| | | |
| | | $inputBuffer = self::_byteSubstring( |
| | | $inputBuffer, |
| | | $length |
| | | ); |
| | | } |
| | | |
| | | // read input (from PHP stream) |
| | | if (in_array($this->_input, $inputStreams)) { |
| | | $this->_debug('input stream is ready for reading'); |
| | | $this->_debug( |
| | | '=> about to read ' . self::CHUNK_SIZE . |
| | | ' bytes from input stream' |
| | | ); |
| | | |
| | | $chunk = fread($this->_input, self::CHUNK_SIZE); |
| | | $length = self::_byteLength($chunk); |
| | | $inputBuffer .= $chunk; |
| | | |
| | | $this->_debug('=> read ' . $length . ' bytes'); |
| | | } |
| | | |
| | | // write message (to GPG) |
| | | if (in_array($fdMessage, $outputStreams)) { |
| | | $this->_debug('GPG is ready for message data'); |
| | | |
| | | $chunk = self::_byteSubstring( |
| | | $messageBuffer, |
| | | 0, |
| | | self::CHUNK_SIZE |
| | | ); |
| | | |
| | | $length = self::_byteLength($chunk); |
| | | |
| | | $this->_debug( |
| | | '=> about to write ' . $length . ' bytes to GPG message' |
| | | ); |
| | | |
| | | $length = fwrite($fdMessage, $chunk, $length); |
| | | $this->_debug('=> wrote ' . $length . ' bytes'); |
| | | |
| | | $messageBuffer = self::_byteSubstring($messageBuffer, $length); |
| | | } |
| | | |
| | | // read message (from PHP stream) |
| | | if (in_array($this->_message, $inputStreams)) { |
| | | $this->_debug('message stream is ready for reading'); |
| | | $this->_debug( |
| | | '=> about to read ' . self::CHUNK_SIZE . |
| | | ' bytes from message stream' |
| | | ); |
| | | |
| | | $chunk = fread($this->_message, self::CHUNK_SIZE); |
| | | $length = self::_byteLength($chunk); |
| | | $messageBuffer .= $chunk; |
| | | |
| | | $this->_debug('=> read ' . $length . ' bytes'); |
| | | } |
| | | |
| | | // read output (from GPG) |
| | | if (in_array($fdOutput, $inputStreams)) { |
| | | $this->_debug('GPG output stream ready for reading'); |
| | | $this->_debug( |
| | | '=> about to read ' . self::CHUNK_SIZE . |
| | | ' bytes from GPG output' |
| | | ); |
| | | |
| | | $chunk = fread($fdOutput, self::CHUNK_SIZE); |
| | | $length = self::_byteLength($chunk); |
| | | $outputBuffer .= $chunk; |
| | | |
| | | $this->_debug('=> read ' . $length . ' bytes'); |
| | | } |
| | | |
| | | // write output (to PHP stream) |
| | | if (in_array($this->_output, $outputStreams)) { |
| | | $this->_debug('output stream is ready for data'); |
| | | |
| | | $chunk = self::_byteSubstring( |
| | | $outputBuffer, |
| | | 0, |
| | | self::CHUNK_SIZE |
| | | ); |
| | | |
| | | $length = self::_byteLength($chunk); |
| | | |
| | | $this->_debug( |
| | | '=> about to write ' . $length . ' bytes to output stream' |
| | | ); |
| | | |
| | | $length = fwrite($this->_output, $chunk, $length); |
| | | |
| | | $this->_debug('=> wrote ' . $length . ' bytes'); |
| | | |
| | | $outputBuffer = self::_byteSubstring($outputBuffer, $length); |
| | | } |
| | | |
| | | // read error (from GPG) |
| | | if (in_array($fdError, $inputStreams)) { |
| | | $this->_debug('GPG error stream ready for reading'); |
| | | $this->_debug( |
| | | '=> about to read ' . self::CHUNK_SIZE . |
| | | ' bytes from GPG error' |
| | | ); |
| | | |
| | | $chunk = fread($fdError, self::CHUNK_SIZE); |
| | | $length = self::_byteLength($chunk); |
| | | $errorBuffer .= $chunk; |
| | | |
| | | $this->_debug('=> read ' . $length . ' bytes'); |
| | | |
| | | // pass lines to error handlers |
| | | while (($pos = strpos($errorBuffer, PHP_EOL)) !== false) { |
| | | $line = self::_byteSubstring($errorBuffer, 0, $pos); |
| | | foreach ($this->_errorHandlers as $handler) { |
| | | array_unshift($handler['args'], $line); |
| | | call_user_func_array( |
| | | $handler['callback'], |
| | | $handler['args'] |
| | | ); |
| | | |
| | | array_shift($handler['args']); |
| | | } |
| | | $errorBuffer = self::_byteSubString( |
| | | $errorBuffer, |
| | | $pos + self::_byteLength(PHP_EOL) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // read status (from GPG) |
| | | if (in_array($fdStatus, $inputStreams)) { |
| | | $this->_debug('GPG status stream ready for reading'); |
| | | $this->_debug( |
| | | '=> about to read ' . self::CHUNK_SIZE . |
| | | ' bytes from GPG status' |
| | | ); |
| | | |
| | | $chunk = fread($fdStatus, self::CHUNK_SIZE); |
| | | $length = self::_byteLength($chunk); |
| | | $statusBuffer .= $chunk; |
| | | |
| | | $this->_debug('=> read ' . $length . ' bytes'); |
| | | |
| | | // pass lines to status handlers |
| | | while (($pos = strpos($statusBuffer, PHP_EOL)) !== false) { |
| | | $line = self::_byteSubstring($statusBuffer, 0, $pos); |
| | | // only pass lines beginning with magic prefix |
| | | if (self::_byteSubstring($line, 0, 9) == '[GNUPG:] ') { |
| | | $line = self::_byteSubstring($line, 9); |
| | | foreach ($this->_statusHandlers as $handler) { |
| | | array_unshift($handler['args'], $line); |
| | | call_user_func_array( |
| | | $handler['callback'], |
| | | $handler['args'] |
| | | ); |
| | | |
| | | array_shift($handler['args']); |
| | | } |
| | | } |
| | | $statusBuffer = self::_byteSubString( |
| | | $statusBuffer, |
| | | $pos + self::_byteLength(PHP_EOL) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // write command (to GPG) |
| | | if (in_array($fdCommand, $outputStreams)) { |
| | | $this->_debug('GPG is ready for command data'); |
| | | |
| | | // send commands |
| | | $chunk = self::_byteSubstring( |
| | | $this->_commandBuffer, |
| | | 0, |
| | | self::CHUNK_SIZE |
| | | ); |
| | | |
| | | $length = self::_byteLength($chunk); |
| | | |
| | | $this->_debug( |
| | | '=> about to write ' . $length . ' bytes to GPG command' |
| | | ); |
| | | |
| | | $length = fwrite($fdCommand, $chunk, $length); |
| | | |
| | | $this->_debug('=> wrote ' . $length); |
| | | |
| | | $this->_commandBuffer = self::_byteSubstring( |
| | | $this->_commandBuffer, |
| | | $length |
| | | ); |
| | | } |
| | | |
| | | } // end loop while streams are open |
| | | |
| | | $this->_debug('END PROCESSING'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _openSubprocess() |
| | | |
| | | /** |
| | | * Opens an internal GPG subprocess for the current operation |
| | | * |
| | | * Opens a GPG subprocess, then connects the subprocess to some pipes. Sets |
| | | * the private class property {@link Crypt_GPG_Engine::$_process} to |
| | | * the new subprocess. |
| | | * |
| | | * @return void |
| | | * |
| | | * @throws Crypt_GPG_OpenSubprocessException if the subprocess could not be |
| | | * opened. |
| | | * |
| | | * @see Crypt_GPG_Engine::setOperation() |
| | | * @see Crypt_GPG_Engine::_closeSubprocess() |
| | | * @see Crypt_GPG_Engine::$_process |
| | | */ |
| | | private function _openSubprocess() |
| | | { |
| | | $version = $this->getVersion(); |
| | | |
| | | $env = $_ENV; |
| | | |
| | | // Newer versions of GnuPG return localized results. Crypt_GPG only |
| | | // works with English, so set the locale to 'C' for the subprocess. |
| | | $env['LC_ALL'] = 'C'; |
| | | |
| | | $commandLine = $this->_binary; |
| | | |
| | | $defaultArguments = array( |
| | | '--status-fd ' . escapeshellarg(self::FD_STATUS), |
| | | '--command-fd ' . escapeshellarg(self::FD_COMMAND), |
| | | '--no-secmem-warning', |
| | | '--no-tty', |
| | | '--no-default-keyring', // ignored if keying files are not specified |
| | | '--no-options' // prevent creation of ~/.gnupg directory |
| | | ); |
| | | |
| | | if (version_compare($version, '1.0.7', 'ge')) { |
| | | if (version_compare($version, '2.0.0', 'lt')) { |
| | | $defaultArguments[] = '--no-use-agent'; |
| | | } |
| | | $defaultArguments[] = '--no-permission-warning'; |
| | | } |
| | | |
| | | if (version_compare($version, '1.4.2', 'ge')) { |
| | | $defaultArguments[] = '--exit-on-status-write-error'; |
| | | } |
| | | |
| | | if (version_compare($version, '1.3.2', 'ge')) { |
| | | $defaultArguments[] = '--trust-model always'; |
| | | } else { |
| | | $defaultArguments[] = '--always-trust'; |
| | | } |
| | | |
| | | $arguments = array_merge($defaultArguments, $this->_arguments); |
| | | |
| | | if ($this->_homedir) { |
| | | $arguments[] = '--homedir ' . escapeshellarg($this->_homedir); |
| | | |
| | | // the random seed file makes subsequent actions faster so only |
| | | // disable it if we have to. |
| | | if (!is_writeable($this->_homedir)) { |
| | | $arguments[] = '--no-random-seed-file'; |
| | | } |
| | | } |
| | | |
| | | if ($this->_publicKeyring) { |
| | | $arguments[] = '--keyring ' . escapeshellarg($this->_publicKeyring); |
| | | } |
| | | |
| | | if ($this->_privateKeyring) { |
| | | $arguments[] = '--secret-keyring ' . |
| | | escapeshellarg($this->_privateKeyring); |
| | | } |
| | | |
| | | if ($this->_trustDb) { |
| | | $arguments[] = '--trustdb-name ' . escapeshellarg($this->_trustDb); |
| | | } |
| | | |
| | | $commandLine .= ' ' . implode(' ', $arguments) . ' ' . |
| | | $this->_operation; |
| | | |
| | | // Binary operations will not work on Windows with PHP < 5.2.6. This is |
| | | // in case stream_select() ever works on Windows. |
| | | $rb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'r' : 'rb'; |
| | | $wb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'w' : 'wb'; |
| | | |
| | | $descriptorSpec = array( |
| | | self::FD_INPUT => array('pipe', $rb), // stdin |
| | | self::FD_OUTPUT => array('pipe', $wb), // stdout |
| | | self::FD_ERROR => array('pipe', $wb), // stderr |
| | | self::FD_STATUS => array('pipe', $wb), // status |
| | | self::FD_COMMAND => array('pipe', $rb), // command |
| | | self::FD_MESSAGE => array('pipe', $rb) // message |
| | | ); |
| | | |
| | | $this->_debug('OPENING SUBPROCESS WITH THE FOLLOWING COMMAND:'); |
| | | $this->_debug($commandLine); |
| | | |
| | | $this->_process = proc_open( |
| | | $commandLine, |
| | | $descriptorSpec, |
| | | $this->_pipes, |
| | | null, |
| | | $env, |
| | | array('binary_pipes' => true) |
| | | ); |
| | | |
| | | if (!is_resource($this->_process)) { |
| | | throw new Crypt_GPG_OpenSubprocessException( |
| | | 'Unable to open GPG subprocess.', 0, $commandLine); |
| | | } |
| | | |
| | | $this->_openPipes = $this->_pipes; |
| | | $this->_errorCode = Crypt_GPG::ERROR_NONE; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _closeSubprocess() |
| | | |
| | | /** |
| | | * Closes a the internal GPG subprocess |
| | | * |
| | | * Closes the internal GPG subprocess. Sets the private class property |
| | | * {@link Crypt_GPG_Engine::$_process} to null. |
| | | * |
| | | * @return void |
| | | * |
| | | * @see Crypt_GPG_Engine::_openSubprocess() |
| | | * @see Crypt_GPG_Engine::$_process |
| | | */ |
| | | private function _closeSubprocess() |
| | | { |
| | | if (is_resource($this->_process)) { |
| | | $this->_debug('CLOSING SUBPROCESS'); |
| | | |
| | | // close remaining open pipes |
| | | foreach (array_keys($this->_openPipes) as $pipeNumber) { |
| | | $this->_closePipe($pipeNumber); |
| | | } |
| | | |
| | | $exitCode = proc_close($this->_process); |
| | | |
| | | if ($exitCode != 0) { |
| | | $this->_debug( |
| | | '=> subprocess returned an unexpected exit code: ' . |
| | | $exitCode |
| | | ); |
| | | |
| | | if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { |
| | | if ($this->_needPassphrase > 0) { |
| | | $this->_errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE; |
| | | } else { |
| | | $this->_errorCode = Crypt_GPG::ERROR_UNKNOWN; |
| | | } |
| | | } |
| | | } |
| | | |
| | | $this->_process = null; |
| | | $this->_pipes = array(); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _closePipe() |
| | | |
| | | /** |
| | | * Closes an opened pipe used to communicate with the GPG subprocess |
| | | * |
| | | * If the pipe is already closed, it is ignored. If the pipe is open, it |
| | | * is flushed and then closed. |
| | | * |
| | | * @param integer $pipeNumber the file descriptor number of the pipe to |
| | | * close. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _closePipe($pipeNumber) |
| | | { |
| | | $pipeNumber = intval($pipeNumber); |
| | | if (array_key_exists($pipeNumber, $this->_openPipes)) { |
| | | fflush($this->_openPipes[$pipeNumber]); |
| | | fclose($this->_openPipes[$pipeNumber]); |
| | | unset($this->_openPipes[$pipeNumber]); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _getBinary() |
| | | |
| | | /** |
| | | * Gets the name of the GPG binary for the current operating system |
| | | * |
| | | * This method is called if the '<kbd>binary</kbd>' option is <i>not</i> |
| | | * specified when creating this driver. |
| | | * |
| | | * @return string the name of the GPG binary for the current operating |
| | | * system. If no suitable binary could be found, an empty |
| | | * string is returned. |
| | | */ |
| | | private function _getBinary() |
| | | { |
| | | $binary = ''; |
| | | |
| | | if ($this->_isDarwin) { |
| | | $binaryFiles = array( |
| | | '/opt/local/bin/gpg', // MacPorts |
| | | '/usr/local/bin/gpg', // Mac GPG |
| | | '/sw/bin/gpg', // Fink |
| | | '/usr/bin/gpg' |
| | | ); |
| | | } else { |
| | | $binaryFiles = array( |
| | | '/usr/bin/gpg', |
| | | '/usr/local/bin/gpg' |
| | | ); |
| | | } |
| | | |
| | | foreach ($binaryFiles as $binaryFile) { |
| | | if (is_executable($binaryFile)) { |
| | | $binary = $binaryFile; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return $binary; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _debug() |
| | | |
| | | /** |
| | | * Displays debug text if debugging is turned on |
| | | * |
| | | * Debugging text is prepended with a debug identifier and echoed to stdout. |
| | | * |
| | | * @param string $text the debugging text to display. |
| | | * |
| | | * @return void |
| | | */ |
| | | private function _debug($text) |
| | | { |
| | | if ($this->_debug) { |
| | | if (array_key_exists('SHELL', $_ENV)) { |
| | | foreach (explode(PHP_EOL, $text) as $line) { |
| | | echo "Crypt_GPG DEBUG: ", $line, PHP_EOL; |
| | | } |
| | | } else { |
| | | // running on a web server, format debug output nicely |
| | | foreach (explode(PHP_EOL, $text) as $line) { |
| | | echo "Crypt_GPG DEBUG: <strong>", $line, |
| | | '</strong><br />', PHP_EOL; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _byteLength() |
| | | |
| | | /** |
| | | * Gets the length of a string in bytes even if mbstring function |
| | | * overloading is turned on |
| | | * |
| | | * This is used for stream-based communication with the GPG subprocess. |
| | | * |
| | | * @param string $string the string for which to get the length. |
| | | * |
| | | * @return integer the length of the string in bytes. |
| | | * |
| | | * @see Crypt_GPG_Engine::$_mbStringOverload |
| | | */ |
| | | private static function _byteLength($string) |
| | | { |
| | | if (self::$_mbStringOverload) { |
| | | return mb_strlen($string, '8bit'); |
| | | } |
| | | |
| | | return strlen((binary)$string); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _byteSubstring() |
| | | |
| | | /** |
| | | * Gets the substring of a string in bytes even if mbstring function |
| | | * overloading is turned on |
| | | * |
| | | * This is used for stream-based communication with the GPG subprocess. |
| | | * |
| | | * @param string $string the input string. |
| | | * @param integer $start the starting point at which to get the substring. |
| | | * @param integer $length optional. The length of the substring. |
| | | * |
| | | * @return string the extracted part of the string. Unlike the default PHP |
| | | * <kbd>substr()</kbd> function, the returned value is |
| | | * always a string and never false. |
| | | * |
| | | * @see Crypt_GPG_Engine::$_mbStringOverload |
| | | */ |
| | | private static function _byteSubstring($string, $start, $length = null) |
| | | { |
| | | if (self::$_mbStringOverload) { |
| | | if ($length === null) { |
| | | return mb_substr( |
| | | $string, |
| | | $start, |
| | | self::_byteLength($string) - $start, '8bit' |
| | | ); |
| | | } |
| | | |
| | | return mb_substr($string, $start, $length, '8bit'); |
| | | } |
| | | |
| | | if ($length === null) { |
| | | return (string)substr((binary)$string, $start); |
| | | } |
| | | |
| | | return (string)substr((binary)$string, $start, $length); |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Various exception handling classes for Crypt_GPG |
| | | * |
| | | * Crypt_GPG provides an object oriented interface to GNU Privacy |
| | | * Guard (GPG). It requires the GPG executable to be on the system. |
| | | * |
| | | * This file contains various exception classes used by the Crypt_GPG package. |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: Exceptions.php 273745 2009-01-18 05:24:25Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | |
| | | /** |
| | | * PEAR Exception handler and base class |
| | | */ |
| | | require_once 'PEAR/Exception.php'; |
| | | |
| | | // {{{ class Crypt_GPG_Exception |
| | | |
| | | /** |
| | | * An exception thrown by the Crypt_GPG package |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_Exception extends PEAR_Exception |
| | | { |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_FileException |
| | | |
| | | /** |
| | | * An exception thrown when a file is used in ways it cannot be used |
| | | * |
| | | * For example, if an output file is specified and the file is not writeable, or |
| | | * if an input file is specified and the file is not readable, this exception |
| | | * is thrown. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2007-2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_FileException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * The name of the file that caused this exception |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_filename = ''; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_FileException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $filename the name of the file that caused this exception. |
| | | */ |
| | | public function __construct($message, $code = 0, $filename = '') |
| | | { |
| | | $this->_filename = $filename; |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getFilename() |
| | | |
| | | /** |
| | | * Returns the filename of the file that caused this exception |
| | | * |
| | | * @return string the filename of the file that caused this exception. |
| | | * |
| | | * @see Crypt_GPG_FileException::$_filename |
| | | */ |
| | | public function getFilename() |
| | | { |
| | | return $this->_filename; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_OpenSubprocessException |
| | | |
| | | /** |
| | | * An exception thrown when the GPG subprocess cannot be opened |
| | | * |
| | | * This exception is thrown when the {@link Crypt_GPG_Engine} tries to open a |
| | | * new subprocess and fails. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_OpenSubprocessException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * The command used to try to open the subprocess |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_command = ''; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_OpenSubprocessException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $command the command that was called to open the |
| | | * new subprocess. |
| | | * |
| | | * @see Crypt_GPG::_openSubprocess() |
| | | */ |
| | | public function __construct($message, $code = 0, $command = '') |
| | | { |
| | | $this->_command = $command; |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getCommand() |
| | | |
| | | /** |
| | | * Returns the contents of the internal _command property |
| | | * |
| | | * @return string the command used to open the subprocess. |
| | | * |
| | | * @see Crypt_GPG_OpenSubprocessException::$_command |
| | | */ |
| | | public function getCommand() |
| | | { |
| | | return $this->_command; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_InvalidOperationException |
| | | |
| | | /** |
| | | * An exception thrown when an invalid GPG operation is attempted |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_InvalidOperationException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * The attempted operation |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_operation = ''; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_OpenSubprocessException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $operation the operation. |
| | | */ |
| | | public function __construct($message, $code = 0, $operation = '') |
| | | { |
| | | $this->_operation = $operation; |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getOperation() |
| | | |
| | | /** |
| | | * Returns the contents of the internal _operation property |
| | | * |
| | | * @return string the attempted operation. |
| | | * |
| | | * @see Crypt_GPG_InvalidOperationException::$_operation |
| | | */ |
| | | public function getOperation() |
| | | { |
| | | return $this->_operation; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_KeyNotFoundException |
| | | |
| | | /** |
| | | * An exception thrown when Crypt_GPG fails to find the key for various |
| | | * operations |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_KeyNotFoundException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * The key identifier that was searched for |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_keyId = ''; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_KeyNotFoundException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $keyId the key identifier of the key. |
| | | */ |
| | | public function __construct($message, $code = 0, $keyId= '') |
| | | { |
| | | $this->_keyId = $keyId; |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getKeyId() |
| | | |
| | | /** |
| | | * Gets the key identifier of the key that was not found |
| | | * |
| | | * @return string the key identifier of the key that was not found. |
| | | */ |
| | | public function getKeyId() |
| | | { |
| | | return $this->_keyId; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_NoDataException |
| | | |
| | | /** |
| | | * An exception thrown when Crypt_GPG cannot find valid data for various |
| | | * operations |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2006 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_NoDataException extends Crypt_GPG_Exception |
| | | { |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_BadPassphraseException |
| | | |
| | | /** |
| | | * An exception thrown when a required passphrase is incorrect or missing |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2006-2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_BadPassphraseException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * Keys for which the passhprase is missing |
| | | * |
| | | * This contains primary user ids indexed by sub-key id. |
| | | * |
| | | * @var array |
| | | */ |
| | | private $_missingPassphrases = array(); |
| | | |
| | | /** |
| | | * Keys for which the passhprase is incorrect |
| | | * |
| | | * This contains primary user ids indexed by sub-key id. |
| | | * |
| | | * @var array |
| | | */ |
| | | private $_badPassphrases = array(); |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_BadPassphraseException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $badPassphrases an array containing user ids of keys |
| | | * for which the passphrase is incorrect. |
| | | * @param string $missingPassphrases an array containing user ids of keys |
| | | * for which the passphrase is missing. |
| | | */ |
| | | public function __construct($message, $code = 0, |
| | | array $badPassphrases = array(), array $missingPassphrases = array() |
| | | ) { |
| | | $this->_badPassphrases = $badPassphrases; |
| | | $this->_missingPassphrases = $missingPassphrases; |
| | | |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getBadPassphrases() |
| | | |
| | | /** |
| | | * Gets keys for which the passhprase is incorrect |
| | | * |
| | | * @return array an array of keys for which the passphrase is incorrect. |
| | | * The array contains primary user ids indexed by the sub-key |
| | | * id. |
| | | */ |
| | | public function getBadPassphrases() |
| | | { |
| | | return $this->_badPassphrases; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getMissingPassphrases() |
| | | |
| | | /** |
| | | * Gets keys for which the passhprase is missing |
| | | * |
| | | * @return array an array of keys for which the passphrase is missing. |
| | | * The array contains primary user ids indexed by the sub-key |
| | | * id. |
| | | */ |
| | | public function getMissingPassphrases() |
| | | { |
| | | return $this->_missingPassphrases; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class Crypt_GPG_DeletePrivateKeyException |
| | | |
| | | /** |
| | | * An exception thrown when an attempt is made to delete public key that has an |
| | | * associated private key on the keyring |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | class Crypt_GPG_DeletePrivateKeyException extends Crypt_GPG_Exception |
| | | { |
| | | // {{{ private class properties |
| | | |
| | | /** |
| | | * The key identifier the deletion attempt was made upon |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_keyId = ''; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new Crypt_GPG_DeletePrivateKeyException |
| | | * |
| | | * @param string $message an error message. |
| | | * @param integer $code a user defined error code. |
| | | * @param string $keyId the key identifier of the public key that was |
| | | * attempted to delete. |
| | | * |
| | | * @see Crypt_GPG::deletePublicKey() |
| | | */ |
| | | public function __construct($message, $code = 0, $keyId = '') |
| | | { |
| | | $this->_keyId = $keyId; |
| | | parent::__construct($message, $code); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getKeyId() |
| | | |
| | | /** |
| | | * Gets the key identifier of the key that was not found |
| | | * |
| | | * @return string the key identifier of the key that was not found. |
| | | */ |
| | | public function getKeyId() |
| | | { |
| | | return $this->_keyId; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Contains a class representing GPG keys |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: Key.php 295621 2010-03-01 04:18:54Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | |
| | | /** |
| | | * Sub-key class definition |
| | | */ |
| | | require_once 'Crypt/GPG/SubKey.php'; |
| | | |
| | | /** |
| | | * User id class definition |
| | | */ |
| | | require_once 'Crypt/GPG/UserId.php'; |
| | | |
| | | // {{{ class Crypt_GPG_Key |
| | | |
| | | /** |
| | | * A data class for GPG key information |
| | | * |
| | | * This class is used to store the results of the {@link Crypt_GPG::getKeys()} |
| | | * method. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @see Crypt_GPG::getKeys() |
| | | */ |
| | | class Crypt_GPG_Key |
| | | { |
| | | // {{{ class properties |
| | | |
| | | /** |
| | | * The user ids associated with this key |
| | | * |
| | | * This is an array of {@link Crypt_GPG_UserId} objects. |
| | | * |
| | | * @var array |
| | | * |
| | | * @see Crypt_GPG_Key::addUserId() |
| | | * @see Crypt_GPG_Key::getUserIds() |
| | | */ |
| | | private $_userIds = array(); |
| | | |
| | | /** |
| | | * The subkeys of this key |
| | | * |
| | | * This is an array of {@link Crypt_GPG_SubKey} objects. |
| | | * |
| | | * @var array |
| | | * |
| | | * @see Crypt_GPG_Key::addSubKey() |
| | | * @see Crypt_GPG_Key::getSubKeys() |
| | | */ |
| | | private $_subKeys = array(); |
| | | |
| | | // }}} |
| | | // {{{ getSubKeys() |
| | | |
| | | /** |
| | | * Gets the sub-keys of this key |
| | | * |
| | | * @return array the sub-keys of this key. |
| | | * |
| | | * @see Crypt_GPG_Key::addSubKey() |
| | | */ |
| | | public function getSubKeys() |
| | | { |
| | | return $this->_subKeys; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getUserIds() |
| | | |
| | | /** |
| | | * Gets the user ids of this key |
| | | * |
| | | * @return array the user ids of this key. |
| | | * |
| | | * @see Crypt_GPG_Key::addUserId() |
| | | */ |
| | | public function getUserIds() |
| | | { |
| | | return $this->_userIds; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getPrimaryKey() |
| | | |
| | | /** |
| | | * Gets the primary sub-key of this key |
| | | * |
| | | * The primary key is the first added sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the primary sub-key of this key. |
| | | */ |
| | | public function getPrimaryKey() |
| | | { |
| | | $primary_key = null; |
| | | if (count($this->_subKeys) > 0) { |
| | | $primary_key = $this->_subKeys[0]; |
| | | } |
| | | return $primary_key; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ canSign() |
| | | |
| | | /** |
| | | * Gets whether or not this key can sign data |
| | | * |
| | | * This key can sign data if any sub-key of this key can sign data. |
| | | * |
| | | * @return boolean true if this key can sign data and false if this key |
| | | * cannot sign data. |
| | | */ |
| | | public function canSign() |
| | | { |
| | | $canSign = false; |
| | | foreach ($this->_subKeys as $subKey) { |
| | | if ($subKey->canSign()) { |
| | | $canSign = true; |
| | | break; |
| | | } |
| | | } |
| | | return $canSign; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ canEncrypt() |
| | | |
| | | /** |
| | | * Gets whether or not this key can encrypt data |
| | | * |
| | | * This key can encrypt data if any sub-key of this key can encrypt data. |
| | | * |
| | | * @return boolean true if this key can encrypt data and false if this |
| | | * key cannot encrypt data. |
| | | */ |
| | | public function canEncrypt() |
| | | { |
| | | $canEncrypt = false; |
| | | foreach ($this->_subKeys as $subKey) { |
| | | if ($subKey->canEncrypt()) { |
| | | $canEncrypt = true; |
| | | break; |
| | | } |
| | | } |
| | | return $canEncrypt; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addSubKey() |
| | | |
| | | /** |
| | | * Adds a sub-key to this key |
| | | * |
| | | * The first added sub-key will be the primary key of this key. |
| | | * |
| | | * @param Crypt_GPG_SubKey $subKey the sub-key to add. |
| | | * |
| | | * @return Crypt_GPG_Key the current object, for fluent interface. |
| | | */ |
| | | public function addSubKey(Crypt_GPG_SubKey $subKey) |
| | | { |
| | | $this->_subKeys[] = $subKey; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ addUserId() |
| | | |
| | | /** |
| | | * Adds a user id to this key |
| | | * |
| | | * @param Crypt_GPG_UserId $userId the user id to add. |
| | | * |
| | | * @return Crypt_GPG_Key the current object, for fluent interface. |
| | | */ |
| | | public function addUserId(Crypt_GPG_UserId $userId) |
| | | { |
| | | $this->_userIds[] = $userId; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * A class representing GPG signatures |
| | | * |
| | | * This file contains a data class representing a GPG signature. |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: Signature.php 302773 2010-08-25 14:16:28Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | |
| | | /** |
| | | * User id class definition |
| | | */ |
| | | require_once 'Crypt/GPG/UserId.php'; |
| | | |
| | | // {{{ class Crypt_GPG_Signature |
| | | |
| | | /** |
| | | * A class for GPG signature information |
| | | * |
| | | * This class is used to store the results of the Crypt_GPG::verify() method. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @see Crypt_GPG::verify() |
| | | */ |
| | | class Crypt_GPG_Signature |
| | | { |
| | | // {{{ class properties |
| | | |
| | | /** |
| | | * A base64-encoded string containing a unique id for this signature if |
| | | * this signature has been verified as ok |
| | | * |
| | | * This id is used to prevent replay attacks and is not present for all |
| | | * types of signatures. |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_id = ''; |
| | | |
| | | /** |
| | | * The fingerprint of the key used to create the signature |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_keyFingerprint = ''; |
| | | |
| | | /** |
| | | * The id of the key used to create the signature |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_keyId = ''; |
| | | |
| | | /** |
| | | * The creation date of this signature |
| | | * |
| | | * This is a Unix timestamp. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_creationDate = 0; |
| | | |
| | | /** |
| | | * The expiration date of the signature |
| | | * |
| | | * This is a Unix timestamp. If this signature does not expire, this will |
| | | * be zero. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_expirationDate = 0; |
| | | |
| | | /** |
| | | * The user id associated with this signature |
| | | * |
| | | * @var Crypt_GPG_UserId |
| | | */ |
| | | private $_userId = null; |
| | | |
| | | /** |
| | | * Whether or not this signature is valid |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_isValid = false; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new signature |
| | | * |
| | | * Signatures can be initialized from an array of named values. Available |
| | | * names are: |
| | | * |
| | | * - <kbd>string id</kbd> - the unique id of this signature. |
| | | * - <kbd>string fingerprint</kbd> - the fingerprint of the key used to |
| | | * create the signature. The fingerprint |
| | | * should not contain formatting |
| | | * characters. |
| | | * - <kbd>string keyId</kbd> - the id of the key used to create the |
| | | * the signature. |
| | | * - <kbd>integer creation</kbd> - the date the signature was created. |
| | | * This is a UNIX timestamp. |
| | | * - <kbd>integer expiration</kbd> - the date the signature expired. This |
| | | * is a UNIX timestamp. If the signature |
| | | * does not expire, use 0. |
| | | * - <kbd>boolean valid</kbd> - whether or not the signature is valid. |
| | | * - <kbd>string userId</kbd> - the user id associated with the |
| | | * signature. This may also be a |
| | | * {@link Crypt_GPG_UserId} object. |
| | | * |
| | | * @param Crypt_GPG_Signature|array $signature optional. Either an existing |
| | | * signature object, which is copied; or an array of initial values. |
| | | */ |
| | | public function __construct($signature = null) |
| | | { |
| | | // copy from object |
| | | if ($signature instanceof Crypt_GPG_Signature) { |
| | | $this->_id = $signature->_id; |
| | | $this->_keyFingerprint = $signature->_keyFingerprint; |
| | | $this->_keyId = $signature->_keyId; |
| | | $this->_creationDate = $signature->_creationDate; |
| | | $this->_expirationDate = $signature->_expirationDate; |
| | | $this->_isValid = $signature->_isValid; |
| | | |
| | | if ($signature->_userId instanceof Crypt_GPG_UserId) { |
| | | $this->_userId = clone $signature->_userId; |
| | | } else { |
| | | $this->_userId = $signature->_userId; |
| | | } |
| | | } |
| | | |
| | | // initialize from array |
| | | if (is_array($signature)) { |
| | | if (array_key_exists('id', $signature)) { |
| | | $this->setId($signature['id']); |
| | | } |
| | | |
| | | if (array_key_exists('fingerprint', $signature)) { |
| | | $this->setKeyFingerprint($signature['fingerprint']); |
| | | } |
| | | |
| | | if (array_key_exists('keyId', $signature)) { |
| | | $this->setKeyId($signature['keyId']); |
| | | } |
| | | |
| | | if (array_key_exists('creation', $signature)) { |
| | | $this->setCreationDate($signature['creation']); |
| | | } |
| | | |
| | | if (array_key_exists('expiration', $signature)) { |
| | | $this->setExpirationDate($signature['expiration']); |
| | | } |
| | | |
| | | if (array_key_exists('valid', $signature)) { |
| | | $this->setValid($signature['valid']); |
| | | } |
| | | |
| | | if (array_key_exists('userId', $signature)) { |
| | | $userId = new Crypt_GPG_UserId($signature['userId']); |
| | | $this->setUserId($userId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getId() |
| | | |
| | | /** |
| | | * Gets the id of this signature |
| | | * |
| | | * @return string a base64-encoded string containing a unique id for this |
| | | * signature. This id is used to prevent replay attacks and |
| | | * is not present for all types of signatures. |
| | | */ |
| | | public function getId() |
| | | { |
| | | return $this->_id; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getKeyFingerprint() |
| | | |
| | | /** |
| | | * Gets the fingerprint of the key used to create this signature |
| | | * |
| | | * @return string the fingerprint of the key used to create this signature. |
| | | */ |
| | | public function getKeyFingerprint() |
| | | { |
| | | return $this->_keyFingerprint; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getKeyId() |
| | | |
| | | /** |
| | | * Gets the id of the key used to create this signature |
| | | * |
| | | * Whereas the fingerprint of the signing key may not always be available |
| | | * (for example if the signature is bad), the id should always be |
| | | * available. |
| | | * |
| | | * @return string the id of the key used to create this signature. |
| | | */ |
| | | public function getKeyId() |
| | | { |
| | | return $this->_keyId; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getCreationDate() |
| | | |
| | | /** |
| | | * Gets the creation date of this signature |
| | | * |
| | | * @return integer the creation date of this signature. This is a Unix |
| | | * timestamp. |
| | | */ |
| | | public function getCreationDate() |
| | | { |
| | | return $this->_creationDate; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getExpirationDate() |
| | | |
| | | /** |
| | | * Gets the expiration date of the signature |
| | | * |
| | | * @return integer the expiration date of this signature. This is a Unix |
| | | * timestamp. If this signature does not expire, this will |
| | | * be zero. |
| | | */ |
| | | public function getExpirationDate() |
| | | { |
| | | return $this->_expirationDate; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getUserId() |
| | | |
| | | /** |
| | | * Gets the user id associated with this signature |
| | | * |
| | | * @return Crypt_GPG_UserId the user id associated with this signature. |
| | | */ |
| | | public function getUserId() |
| | | { |
| | | return $this->_userId; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isValid() |
| | | |
| | | /** |
| | | * Gets whether or no this signature is valid |
| | | * |
| | | * @return boolean true if this signature is valid and false if it is not. |
| | | */ |
| | | public function isValid() |
| | | { |
| | | return $this->_isValid; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setId() |
| | | |
| | | /** |
| | | * Sets the id of this signature |
| | | * |
| | | * @param string $id a base64-encoded string containing a unique id for |
| | | * this signature. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | * |
| | | * @see Crypt_GPG_Signature::getId() |
| | | */ |
| | | public function setId($id) |
| | | { |
| | | $this->_id = strval($id); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setKeyFingerprint() |
| | | |
| | | /** |
| | | * Sets the key fingerprint of this signature |
| | | * |
| | | * @param string $fingerprint the key fingerprint of this signature. This |
| | | * is the fingerprint of the primary key used to |
| | | * create this signature. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setKeyFingerprint($fingerprint) |
| | | { |
| | | $this->_keyFingerprint = strval($fingerprint); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setKeyId() |
| | | |
| | | /** |
| | | * Sets the key id of this signature |
| | | * |
| | | * @param string $id the key id of this signature. This is the id of the |
| | | * primary key used to create this signature. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setKeyId($id) |
| | | { |
| | | $this->_keyId = strval($id); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setCreationDate() |
| | | |
| | | /** |
| | | * Sets the creation date of this signature |
| | | * |
| | | * @param integer $creationDate the creation date of this signature. This |
| | | * is a Unix timestamp. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setCreationDate($creationDate) |
| | | { |
| | | $this->_creationDate = intval($creationDate); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setExpirationDate() |
| | | |
| | | /** |
| | | * Sets the expiration date of this signature |
| | | * |
| | | * @param integer $expirationDate the expiration date of this signature. |
| | | * This is a Unix timestamp. Specify zero if |
| | | * this signature does not expire. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setExpirationDate($expirationDate) |
| | | { |
| | | $this->_expirationDate = intval($expirationDate); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setUserId() |
| | | |
| | | /** |
| | | * Sets the user id associated with this signature |
| | | * |
| | | * @param Crypt_GPG_UserId $userId the user id associated with this |
| | | * signature. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setUserId(Crypt_GPG_UserId $userId) |
| | | { |
| | | $this->_userId = $userId; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setValid() |
| | | |
| | | /** |
| | | * Sets whether or not this signature is valid |
| | | * |
| | | * @param boolean $isValid true if this signature is valid and false if it |
| | | * is not. |
| | | * |
| | | * @return Crypt_GPG_Signature the current object, for fluent interface. |
| | | */ |
| | | public function setValid($isValid) |
| | | { |
| | | $this->_isValid = ($isValid) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Contains a class representing GPG sub-keys and constants for GPG algorithms |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: SubKey.php 302768 2010-08-25 13:45:52Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | |
| | | // {{{ class Crypt_GPG_SubKey |
| | | |
| | | /** |
| | | * A class for GPG sub-key information |
| | | * |
| | | * This class is used to store the results of the {@link Crypt_GPG::getKeys()} |
| | | * method. Sub-key objects are members of a {@link Crypt_GPG_Key} object. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @author Nathan Fredrickson <nathan@silverorange.com> |
| | | * @copyright 2005-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @see Crypt_GPG::getKeys() |
| | | * @see Crypt_GPG_Key::getSubKeys() |
| | | */ |
| | | class Crypt_GPG_SubKey |
| | | { |
| | | // {{{ class constants |
| | | |
| | | /** |
| | | * RSA encryption algorithm. |
| | | */ |
| | | const ALGORITHM_RSA = 1; |
| | | |
| | | /** |
| | | * Elgamal encryption algorithm (encryption only). |
| | | */ |
| | | const ALGORITHM_ELGAMAL_ENC = 16; |
| | | |
| | | /** |
| | | * DSA encryption algorithm (sometimes called DH, sign only). |
| | | */ |
| | | const ALGORITHM_DSA = 17; |
| | | |
| | | /** |
| | | * Elgamal encryption algorithm (signage and encryption - should not be |
| | | * used). |
| | | */ |
| | | const ALGORITHM_ELGAMAL_ENC_SGN = 20; |
| | | |
| | | // }}} |
| | | // {{{ class properties |
| | | |
| | | /** |
| | | * The id of this sub-key |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_id = ''; |
| | | |
| | | /** |
| | | * The algorithm used to create this sub-key |
| | | * |
| | | * The value is one of the Crypt_GPG_SubKey::ALGORITHM_* constants. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_algorithm = 0; |
| | | |
| | | /** |
| | | * The fingerprint of this sub-key |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_fingerprint = ''; |
| | | |
| | | /** |
| | | * Length of this sub-key in bits |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_length = 0; |
| | | |
| | | /** |
| | | * Date this sub-key was created |
| | | * |
| | | * This is a Unix timestamp. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_creationDate = 0; |
| | | |
| | | /** |
| | | * Date this sub-key expires |
| | | * |
| | | * This is a Unix timestamp. If this sub-key does not expire, this will be |
| | | * zero. |
| | | * |
| | | * @var integer |
| | | */ |
| | | private $_expirationDate = 0; |
| | | |
| | | /** |
| | | * Whether or not this sub-key can sign data |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_canSign = false; |
| | | |
| | | /** |
| | | * Whether or not this sub-key can encrypt data |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_canEncrypt = false; |
| | | |
| | | /** |
| | | * Whether or not the private key for this sub-key exists in the keyring |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_hasPrivate = false; |
| | | |
| | | /** |
| | | * Whether or not this sub-key is revoked |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_isRevoked = false; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new sub-key object |
| | | * |
| | | * Sub-keys can be initialized from an array of named values. Available |
| | | * names are: |
| | | * |
| | | * - <kbd>string id</kbd> - the key id of the sub-key. |
| | | * - <kbd>integer algorithm</kbd> - the encryption algorithm of the |
| | | * sub-key. |
| | | * - <kbd>string fingerprint</kbd> - the fingerprint of the sub-key. The |
| | | * fingerprint should not contain |
| | | * formatting characters. |
| | | * - <kbd>integer length</kbd> - the length of the sub-key in bits. |
| | | * - <kbd>integer creation</kbd> - the date the sub-key was created. |
| | | * This is a UNIX timestamp. |
| | | * - <kbd>integer expiration</kbd> - the date the sub-key expires. This |
| | | * is a UNIX timestamp. If the sub-key |
| | | * does not expire, use 0. |
| | | * - <kbd>boolean canSign</kbd> - whether or not the sub-key can be |
| | | * used to sign data. |
| | | * - <kbd>boolean canEncrypt</kbd> - whether or not the sub-key can be |
| | | * used to encrypt data. |
| | | * - <kbd>boolean hasPrivate</kbd> - whether or not the private key for |
| | | * the sub-key exists in the keyring. |
| | | * - <kbd>boolean isRevoked</kbd> - whether or not this sub-key is |
| | | * revoked. |
| | | * |
| | | * @param Crypt_GPG_SubKey|string|array $key optional. Either an existing |
| | | * sub-key object, which is copied; a sub-key string, which is |
| | | * parsed; or an array of initial values. |
| | | */ |
| | | public function __construct($key = null) |
| | | { |
| | | // parse from string |
| | | if (is_string($key)) { |
| | | $key = self::parse($key); |
| | | } |
| | | |
| | | // copy from object |
| | | if ($key instanceof Crypt_GPG_SubKey) { |
| | | $this->_id = $key->_id; |
| | | $this->_algorithm = $key->_algorithm; |
| | | $this->_fingerprint = $key->_fingerprint; |
| | | $this->_length = $key->_length; |
| | | $this->_creationDate = $key->_creationDate; |
| | | $this->_expirationDate = $key->_expirationDate; |
| | | $this->_canSign = $key->_canSign; |
| | | $this->_canEncrypt = $key->_canEncrypt; |
| | | $this->_hasPrivate = $key->_hasPrivate; |
| | | $this->_isRevoked = $key->_isRevoked; |
| | | } |
| | | |
| | | // initialize from array |
| | | if (is_array($key)) { |
| | | if (array_key_exists('id', $key)) { |
| | | $this->setId($key['id']); |
| | | } |
| | | |
| | | if (array_key_exists('algorithm', $key)) { |
| | | $this->setAlgorithm($key['algorithm']); |
| | | } |
| | | |
| | | if (array_key_exists('fingerprint', $key)) { |
| | | $this->setFingerprint($key['fingerprint']); |
| | | } |
| | | |
| | | if (array_key_exists('length', $key)) { |
| | | $this->setLength($key['length']); |
| | | } |
| | | |
| | | if (array_key_exists('creation', $key)) { |
| | | $this->setCreationDate($key['creation']); |
| | | } |
| | | |
| | | if (array_key_exists('expiration', $key)) { |
| | | $this->setExpirationDate($key['expiration']); |
| | | } |
| | | |
| | | if (array_key_exists('canSign', $key)) { |
| | | $this->setCanSign($key['canSign']); |
| | | } |
| | | |
| | | if (array_key_exists('canEncrypt', $key)) { |
| | | $this->setCanEncrypt($key['canEncrypt']); |
| | | } |
| | | |
| | | if (array_key_exists('hasPrivate', $key)) { |
| | | $this->setHasPrivate($key['hasPrivate']); |
| | | } |
| | | |
| | | if (array_key_exists('isRevoked', $key)) { |
| | | $this->setRevoked($key['isRevoked']); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getId() |
| | | |
| | | /** |
| | | * Gets the id of this sub-key |
| | | * |
| | | * @return string the id of this sub-key. |
| | | */ |
| | | public function getId() |
| | | { |
| | | return $this->_id; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getAlgorithm() |
| | | |
| | | /** |
| | | * Gets the algorithm used by this sub-key |
| | | * |
| | | * The algorithm should be one of the Crypt_GPG_SubKey::ALGORITHM_* |
| | | * constants. |
| | | * |
| | | * @return integer the algorithm used by this sub-key. |
| | | */ |
| | | public function getAlgorithm() |
| | | { |
| | | return $this->_algorithm; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getCreationDate() |
| | | |
| | | /** |
| | | * Gets the creation date of this sub-key |
| | | * |
| | | * This is a Unix timestamp. |
| | | * |
| | | * @return integer the creation date of this sub-key. |
| | | */ |
| | | public function getCreationDate() |
| | | { |
| | | return $this->_creationDate; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getExpirationDate() |
| | | |
| | | /** |
| | | * Gets the date this sub-key expires |
| | | * |
| | | * This is a Unix timestamp. If this sub-key does not expire, this will be |
| | | * zero. |
| | | * |
| | | * @return integer the date this sub-key expires. |
| | | */ |
| | | public function getExpirationDate() |
| | | { |
| | | return $this->_expirationDate; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getFingerprint() |
| | | |
| | | /** |
| | | * Gets the fingerprint of this sub-key |
| | | * |
| | | * @return string the fingerprint of this sub-key. |
| | | */ |
| | | public function getFingerprint() |
| | | { |
| | | return $this->_fingerprint; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getLength() |
| | | |
| | | /** |
| | | * Gets the length of this sub-key in bits |
| | | * |
| | | * @return integer the length of this sub-key in bits. |
| | | */ |
| | | public function getLength() |
| | | { |
| | | return $this->_length; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ canSign() |
| | | |
| | | /** |
| | | * Gets whether or not this sub-key can sign data |
| | | * |
| | | * @return boolean true if this sub-key can sign data and false if this |
| | | * sub-key can not sign data. |
| | | */ |
| | | public function canSign() |
| | | { |
| | | return $this->_canSign; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ canEncrypt() |
| | | |
| | | /** |
| | | * Gets whether or not this sub-key can encrypt data |
| | | * |
| | | * @return boolean true if this sub-key can encrypt data and false if this |
| | | * sub-key can not encrypt data. |
| | | */ |
| | | public function canEncrypt() |
| | | { |
| | | return $this->_canEncrypt; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ hasPrivate() |
| | | |
| | | /** |
| | | * Gets whether or not the private key for this sub-key exists in the |
| | | * keyring |
| | | * |
| | | * @return boolean true the private key for this sub-key exists in the |
| | | * keyring and false if it does not. |
| | | */ |
| | | public function hasPrivate() |
| | | { |
| | | return $this->_hasPrivate; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isRevoked() |
| | | |
| | | /** |
| | | * Gets whether or not this sub-key is revoked |
| | | * |
| | | * @return boolean true if this sub-key is revoked and false if it is not. |
| | | */ |
| | | public function isRevoked() |
| | | { |
| | | return $this->_isRevoked; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setCreationDate() |
| | | |
| | | /** |
| | | * Sets the creation date of this sub-key |
| | | * |
| | | * The creation date is a Unix timestamp. |
| | | * |
| | | * @param integer $creationDate the creation date of this sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setCreationDate($creationDate) |
| | | { |
| | | $this->_creationDate = intval($creationDate); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setExpirationDate() |
| | | |
| | | /** |
| | | * Sets the expiration date of this sub-key |
| | | * |
| | | * The expiration date is a Unix timestamp. Specify zero if this sub-key |
| | | * does not expire. |
| | | * |
| | | * @param integer $expirationDate the expiration date of this sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setExpirationDate($expirationDate) |
| | | { |
| | | $this->_expirationDate = intval($expirationDate); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setId() |
| | | |
| | | /** |
| | | * Sets the id of this sub-key |
| | | * |
| | | * @param string $id the id of this sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setId($id) |
| | | { |
| | | $this->_id = strval($id); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setAlgorithm() |
| | | |
| | | /** |
| | | * Sets the algorithm used by this sub-key |
| | | * |
| | | * @param integer $algorithm the algorithm used by this sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setAlgorithm($algorithm) |
| | | { |
| | | $this->_algorithm = intval($algorithm); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setFingerprint() |
| | | |
| | | /** |
| | | * Sets the fingerprint of this sub-key |
| | | * |
| | | * @param string $fingerprint the fingerprint of this sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setFingerprint($fingerprint) |
| | | { |
| | | $this->_fingerprint = strval($fingerprint); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setLength() |
| | | |
| | | /** |
| | | * Sets the length of this sub-key in bits |
| | | * |
| | | * @param integer $length the length of this sub-key in bits. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setLength($length) |
| | | { |
| | | $this->_length = intval($length); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setCanSign() |
| | | |
| | | /** |
| | | * Sets whether of not this sub-key can sign data |
| | | * |
| | | * @param boolean $canSign true if this sub-key can sign data and false if |
| | | * it can not. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setCanSign($canSign) |
| | | { |
| | | $this->_canSign = ($canSign) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setCanEncrypt() |
| | | |
| | | /** |
| | | * Sets whether of not this sub-key can encrypt data |
| | | * |
| | | * @param boolean $canEncrypt true if this sub-key can encrypt data and |
| | | * false if it can not. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setCanEncrypt($canEncrypt) |
| | | { |
| | | $this->_canEncrypt = ($canEncrypt) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setHasPrivate() |
| | | |
| | | /** |
| | | * Sets whether of not the private key for this sub-key exists in the |
| | | * keyring |
| | | * |
| | | * @param boolean $hasPrivate true if the private key for this sub-key |
| | | * exists in the keyring and false if it does |
| | | * not. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setHasPrivate($hasPrivate) |
| | | { |
| | | $this->_hasPrivate = ($hasPrivate) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setRevoked() |
| | | |
| | | /** |
| | | * Sets whether or not this sub-key is revoked |
| | | * |
| | | * @param boolean $isRevoked whether or not this sub-key is revoked. |
| | | * |
| | | * @return Crypt_GPG_SubKey the current object, for fluent interface. |
| | | */ |
| | | public function setRevoked($isRevoked) |
| | | { |
| | | $this->_isRevoked = ($isRevoked) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ parse() |
| | | |
| | | /** |
| | | * Parses a sub-key object from a sub-key string |
| | | * |
| | | * See <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG distribution} for information |
| | | * on how the sub-key string is parsed. |
| | | * |
| | | * @param string $string the string containing the sub-key. |
| | | * |
| | | * @return Crypt_GPG_SubKey the sub-key object parsed from the string. |
| | | */ |
| | | public static function parse($string) |
| | | { |
| | | $tokens = explode(':', $string); |
| | | |
| | | $subKey = new Crypt_GPG_SubKey(); |
| | | |
| | | $subKey->setId($tokens[4]); |
| | | $subKey->setLength($tokens[2]); |
| | | $subKey->setAlgorithm($tokens[3]); |
| | | $subKey->setCreationDate(self::_parseDate($tokens[5])); |
| | | $subKey->setExpirationDate(self::_parseDate($tokens[6])); |
| | | |
| | | if ($tokens[1] == 'r') { |
| | | $subKey->setRevoked(true); |
| | | } |
| | | |
| | | if (strpos($tokens[11], 's') !== false) { |
| | | $subKey->setCanSign(true); |
| | | } |
| | | |
| | | if (strpos($tokens[11], 'e') !== false) { |
| | | $subKey->setCanEncrypt(true); |
| | | } |
| | | |
| | | return $subKey; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _parseDate() |
| | | |
| | | /** |
| | | * Parses a date string as provided by GPG into a UNIX timestamp |
| | | * |
| | | * @param string $string the date string. |
| | | * |
| | | * @return integer the UNIX timestamp corresponding to the provided date |
| | | * string. |
| | | */ |
| | | private static function _parseDate($string) |
| | | { |
| | | if ($string == '') { |
| | | $timestamp = 0; |
| | | } else { |
| | | // all times are in UTC according to GPG documentation |
| | | $timeZone = new DateTimeZone('UTC'); |
| | | |
| | | if (strpos($string, 'T') === false) { |
| | | // interpret as UNIX timestamp |
| | | $string = '@' . $string; |
| | | } |
| | | |
| | | $date = new DateTime($string, $timeZone); |
| | | |
| | | // convert to UNIX timestamp |
| | | $timestamp = intval($date->format('U')); |
| | | } |
| | | |
| | | return $timestamp; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Contains a data class representing a GPG user id |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: UserId.php 295621 2010-03-01 04:18:54Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | */ |
| | | |
| | | // {{{ class Crypt_GPG_UserId |
| | | |
| | | /** |
| | | * A class for GPG user id information |
| | | * |
| | | * This class is used to store the results of the {@link Crypt_GPG::getKeys()} |
| | | * method. User id objects are members of a {@link Crypt_GPG_Key} object. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008-2010 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @see Crypt_GPG::getKeys() |
| | | * @see Crypt_GPG_Key::getUserIds() |
| | | */ |
| | | class Crypt_GPG_UserId |
| | | { |
| | | // {{{ class properties |
| | | |
| | | /** |
| | | * The name field of this user id |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_name = ''; |
| | | |
| | | /** |
| | | * The comment field of this user id |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_comment = ''; |
| | | |
| | | /** |
| | | * The email field of this user id |
| | | * |
| | | * @var string |
| | | */ |
| | | private $_email = ''; |
| | | |
| | | /** |
| | | * Whether or not this user id is revoked |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_isRevoked = false; |
| | | |
| | | /** |
| | | * Whether or not this user id is valid |
| | | * |
| | | * @var boolean |
| | | */ |
| | | private $_isValid = true; |
| | | |
| | | // }}} |
| | | // {{{ __construct() |
| | | |
| | | /** |
| | | * Creates a new user id |
| | | * |
| | | * User ids can be initialized from an array of named values. Available |
| | | * names are: |
| | | * |
| | | * - <kbd>string name</kbd> - the name field of the user id. |
| | | * - <kbd>string comment</kbd> - the comment field of the user id. |
| | | * - <kbd>string email</kbd> - the email field of the user id. |
| | | * - <kbd>boolean valid</kbd> - whether or not the user id is valid. |
| | | * - <kbd>boolean revoked</kbd> - whether or not the user id is revoked. |
| | | * |
| | | * @param Crypt_GPG_UserId|string|array $userId optional. Either an |
| | | * existing user id object, which is copied; a user id string, which |
| | | * is parsed; or an array of initial values. |
| | | */ |
| | | public function __construct($userId = null) |
| | | { |
| | | // parse from string |
| | | if (is_string($userId)) { |
| | | $userId = self::parse($userId); |
| | | } |
| | | |
| | | // copy from object |
| | | if ($userId instanceof Crypt_GPG_UserId) { |
| | | $this->_name = $userId->_name; |
| | | $this->_comment = $userId->_comment; |
| | | $this->_email = $userId->_email; |
| | | $this->_isRevoked = $userId->_isRevoked; |
| | | $this->_isValid = $userId->_isValid; |
| | | } |
| | | |
| | | // initialize from array |
| | | if (is_array($userId)) { |
| | | if (array_key_exists('name', $userId)) { |
| | | $this->setName($userId['name']); |
| | | } |
| | | |
| | | if (array_key_exists('comment', $userId)) { |
| | | $this->setComment($userId['comment']); |
| | | } |
| | | |
| | | if (array_key_exists('email', $userId)) { |
| | | $this->setEmail($userId['email']); |
| | | } |
| | | |
| | | if (array_key_exists('revoked', $userId)) { |
| | | $this->setRevoked($userId['revoked']); |
| | | } |
| | | |
| | | if (array_key_exists('valid', $userId)) { |
| | | $this->setValid($userId['valid']); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getName() |
| | | |
| | | /** |
| | | * Gets the name field of this user id |
| | | * |
| | | * @return string the name field of this user id. |
| | | */ |
| | | public function getName() |
| | | { |
| | | return $this->_name; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getComment() |
| | | |
| | | /** |
| | | * Gets the comments field of this user id |
| | | * |
| | | * @return string the comments field of this user id. |
| | | */ |
| | | public function getComment() |
| | | { |
| | | return $this->_comment; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getEmail() |
| | | |
| | | /** |
| | | * Gets the email field of this user id |
| | | * |
| | | * @return string the email field of this user id. |
| | | */ |
| | | public function getEmail() |
| | | { |
| | | return $this->_email; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isRevoked() |
| | | |
| | | /** |
| | | * Gets whether or not this user id is revoked |
| | | * |
| | | * @return boolean true if this user id is revoked and false if it is not. |
| | | */ |
| | | public function isRevoked() |
| | | { |
| | | return $this->_isRevoked; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isValid() |
| | | |
| | | /** |
| | | * Gets whether or not this user id is valid |
| | | * |
| | | * @return boolean true if this user id is valid and false if it is not. |
| | | */ |
| | | public function isValid() |
| | | { |
| | | return $this->_isValid; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ __toString() |
| | | |
| | | /** |
| | | * Gets a string representation of this user id |
| | | * |
| | | * The string is formatted as: |
| | | * <b><kbd>name (comment) <email-address></kbd></b>. |
| | | * |
| | | * @return string a string representation of this user id. |
| | | */ |
| | | public function __toString() |
| | | { |
| | | $components = array(); |
| | | |
| | | if (strlen($this->_name) > 0) { |
| | | $components[] = $this->_name; |
| | | } |
| | | |
| | | if (strlen($this->_comment) > 0) { |
| | | $components[] = '(' . $this->_comment . ')'; |
| | | } |
| | | |
| | | if (strlen($this->_email) > 0) { |
| | | $components[] = '<' . $this->_email. '>'; |
| | | } |
| | | |
| | | return implode(' ', $components); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setName() |
| | | |
| | | /** |
| | | * Sets the name field of this user id |
| | | * |
| | | * @param string $name the name field of this user id. |
| | | * |
| | | * @return Crypt_GPG_UserId the current object, for fluent interface. |
| | | */ |
| | | public function setName($name) |
| | | { |
| | | $this->_name = strval($name); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setComment() |
| | | |
| | | /** |
| | | * Sets the comment field of this user id |
| | | * |
| | | * @param string $comment the comment field of this user id. |
| | | * |
| | | * @return Crypt_GPG_UserId the current object, for fluent interface. |
| | | */ |
| | | public function setComment($comment) |
| | | { |
| | | $this->_comment = strval($comment); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setEmail() |
| | | |
| | | /** |
| | | * Sets the email field of this user id |
| | | * |
| | | * @param string $email the email field of this user id. |
| | | * |
| | | * @return Crypt_GPG_UserId the current object, for fluent interface. |
| | | */ |
| | | public function setEmail($email) |
| | | { |
| | | $this->_email = strval($email); |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setRevoked() |
| | | |
| | | /** |
| | | * Sets whether or not this user id is revoked |
| | | * |
| | | * @param boolean $isRevoked whether or not this user id is revoked. |
| | | * |
| | | * @return Crypt_GPG_UserId the current object, for fluent interface. |
| | | */ |
| | | public function setRevoked($isRevoked) |
| | | { |
| | | $this->_isRevoked = ($isRevoked) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setValid() |
| | | |
| | | /** |
| | | * Sets whether or not this user id is valid |
| | | * |
| | | * @param boolean $isValid whether or not this user id is valid. |
| | | * |
| | | * @return Crypt_GPG_UserId the current object, for fluent interface. |
| | | */ |
| | | public function setValid($isValid) |
| | | { |
| | | $this->_isValid = ($isValid) ? true : false; |
| | | return $this; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ parse() |
| | | |
| | | /** |
| | | * Parses a user id object from a user id string |
| | | * |
| | | * A user id string is of the form: |
| | | * <b><kbd>name (comment) <email-address></kbd></b> with the <i>comment</i> |
| | | * and <i>email-address</i> fields being optional. |
| | | * |
| | | * @param string $string the user id string to parse. |
| | | * |
| | | * @return Crypt_GPG_UserId the user id object parsed from the string. |
| | | */ |
| | | public static function parse($string) |
| | | { |
| | | $userId = new Crypt_GPG_UserId(); |
| | | $email = ''; |
| | | $comment = ''; |
| | | |
| | | // get email address from end of string if it exists |
| | | $matches = array(); |
| | | if (preg_match('/^(.+?) <([^>]+)>$/', $string, $matches) === 1) { |
| | | $string = $matches[1]; |
| | | $email = $matches[2]; |
| | | } |
| | | |
| | | // get comment from end of string if it exists |
| | | $matches = array(); |
| | | if (preg_match('/^(.+?) \(([^\)]+)\)$/', $string, $matches) === 1) { |
| | | $string = $matches[1]; |
| | | $comment = $matches[2]; |
| | | } |
| | | |
| | | $name = $string; |
| | | |
| | | $userId->setName($name); |
| | | $userId->setComment($comment); |
| | | $userId->setEmail($email); |
| | | |
| | | return $userId; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Crypt_GPG is a package to use GPG from PHP |
| | | * |
| | | * This file contains an object that handles GPG's status output for the verify |
| | | * operation. |
| | | * |
| | | * PHP version 5 |
| | | * |
| | | * LICENSE: |
| | | * |
| | | * This library is free software; you can redistribute it and/or modify |
| | | * it under the terms of the GNU Lesser General Public License as |
| | | * published by the Free Software Foundation; either version 2.1 of the |
| | | * License, or (at your option) any later version. |
| | | * |
| | | * This library is distributed in the hope that it will be useful, |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | | * Lesser General Public License for more details. |
| | | * |
| | | * You should have received a copy of the GNU Lesser General Public |
| | | * License along with this library; if not, write to the Free Software |
| | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @version CVS: $Id: VerifyStatusHandler.php 302908 2010-08-31 03:56:54Z gauthierm $ |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | |
| | | /** |
| | | * Signature object class definition |
| | | */ |
| | | require_once 'Crypt/GPG/Signature.php'; |
| | | |
| | | /** |
| | | * Status line handler for the verify operation |
| | | * |
| | | * This class is used internally by Crypt_GPG and does not need be used |
| | | * directly. See the {@link Crypt_GPG} class for end-user API. |
| | | * |
| | | * This class is responsible for building signature objects that are returned |
| | | * by the {@link Crypt_GPG::verify()} method. See <b>doc/DETAILS</b> in the |
| | | * {@link http://www.gnupg.org/download/ GPG distribution} for detailed |
| | | * information on GPG's status output for the verify operation. |
| | | * |
| | | * @category Encryption |
| | | * @package Crypt_GPG |
| | | * @author Michael Gauthier <mike@silverorange.com> |
| | | * @copyright 2008 silverorange |
| | | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
| | | * @link http://pear.php.net/package/Crypt_GPG |
| | | * @link http://www.gnupg.org/ |
| | | */ |
| | | class Crypt_GPG_VerifyStatusHandler |
| | | { |
| | | // {{{ protected properties |
| | | |
| | | /** |
| | | * The current signature id |
| | | * |
| | | * Ths signature id is emitted by GPG before the new signature line so we |
| | | * must remember it temporarily. |
| | | * |
| | | * @var string |
| | | */ |
| | | protected $signatureId = ''; |
| | | |
| | | /** |
| | | * List of parsed {@link Crypt_GPG_Signature} objects |
| | | * |
| | | * @var array |
| | | */ |
| | | protected $signatures = array(); |
| | | |
| | | /** |
| | | * Array index of the current signature |
| | | * |
| | | * @var integer |
| | | */ |
| | | protected $index = -1; |
| | | |
| | | // }}} |
| | | // {{{ handle() |
| | | |
| | | /** |
| | | * Handles a status line |
| | | * |
| | | * @param string $line the status line to handle. |
| | | * |
| | | * @return void |
| | | */ |
| | | public function handle($line) |
| | | { |
| | | $tokens = explode(' ', $line); |
| | | switch ($tokens[0]) { |
| | | case 'GOODSIG': |
| | | case 'EXPSIG': |
| | | case 'EXPKEYSIG': |
| | | case 'REVKEYSIG': |
| | | case 'BADSIG': |
| | | $signature = new Crypt_GPG_Signature(); |
| | | |
| | | // if there was a signature id, set it on the new signature |
| | | if ($this->signatureId != '') { |
| | | $signature->setId($this->signatureId); |
| | | $this->signatureId = ''; |
| | | } |
| | | |
| | | // Detect whether fingerprint or key id was returned and set |
| | | // signature values appropriately. Key ids are strings of either |
| | | // 16 or 8 hexadecimal characters. Fingerprints are strings of 40 |
| | | // hexadecimal characters. The key id is the last 16 characters of |
| | | // the key fingerprint. |
| | | if (strlen($tokens[1]) > 16) { |
| | | $signature->setKeyFingerprint($tokens[1]); |
| | | $signature->setKeyId(substr($tokens[1], -16)); |
| | | } else { |
| | | $signature->setKeyId($tokens[1]); |
| | | } |
| | | |
| | | // get user id string |
| | | $string = implode(' ', array_splice($tokens, 2)); |
| | | $string = rawurldecode($string); |
| | | |
| | | $signature->setUserId(Crypt_GPG_UserId::parse($string)); |
| | | |
| | | $this->index++; |
| | | $this->signatures[$this->index] = $signature; |
| | | break; |
| | | |
| | | case 'ERRSIG': |
| | | $signature = new Crypt_GPG_Signature(); |
| | | |
| | | // if there was a signature id, set it on the new signature |
| | | if ($this->signatureId != '') { |
| | | $signature->setId($this->signatureId); |
| | | $this->signatureId = ''; |
| | | } |
| | | |
| | | // Detect whether fingerprint or key id was returned and set |
| | | // signature values appropriately. Key ids are strings of either |
| | | // 16 or 8 hexadecimal characters. Fingerprints are strings of 40 |
| | | // hexadecimal characters. The key id is the last 16 characters of |
| | | // the key fingerprint. |
| | | if (strlen($tokens[1]) > 16) { |
| | | $signature->setKeyFingerprint($tokens[1]); |
| | | $signature->setKeyId(substr($tokens[1], -16)); |
| | | } else { |
| | | $signature->setKeyId($tokens[1]); |
| | | } |
| | | |
| | | $this->index++; |
| | | $this->signatures[$this->index] = $signature; |
| | | |
| | | break; |
| | | |
| | | case 'VALIDSIG': |
| | | if (!array_key_exists($this->index, $this->signatures)) { |
| | | break; |
| | | } |
| | | |
| | | $signature = $this->signatures[$this->index]; |
| | | |
| | | $signature->setValid(true); |
| | | $signature->setKeyFingerprint($tokens[1]); |
| | | |
| | | if (strpos($tokens[3], 'T') === false) { |
| | | $signature->setCreationDate($tokens[3]); |
| | | } else { |
| | | $signature->setCreationDate(strtotime($tokens[3])); |
| | | } |
| | | |
| | | if (array_key_exists(4, $tokens)) { |
| | | if (strpos($tokens[4], 'T') === false) { |
| | | $signature->setExpirationDate($tokens[4]); |
| | | } else { |
| | | $signature->setExpirationDate(strtotime($tokens[4])); |
| | | } |
| | | } |
| | | |
| | | break; |
| | | |
| | | case 'SIG_ID': |
| | | // note: signature id comes before new signature line and may not |
| | | // exist for some signature types |
| | | $this->signatureId = $tokens[1]; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSignatures() |
| | | |
| | | /** |
| | | * Gets the {@link Crypt_GPG_Signature} objects parsed by this handler |
| | | * |
| | | * @return array the signature objects parsed by this handler. |
| | | */ |
| | | public function getSignatures() |
| | | { |
| | | return $this->signatures; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Abstract driver for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | abstract class enigma_driver |
| | | { |
| | | /** |
| | | * Class constructor. |
| | | * |
| | | * @param string User name (email address) |
| | | */ |
| | | abstract function __construct($user); |
| | | |
| | | /** |
| | | * Driver initialization. |
| | | * |
| | | * @return mixed NULL on success, enigma_error on failure |
| | | */ |
| | | abstract function init(); |
| | | |
| | | /** |
| | | * Encryption. |
| | | */ |
| | | abstract function encrypt($text, $keys); |
| | | |
| | | /** |
| | | * Decryption.. |
| | | */ |
| | | abstract function decrypt($text, $key, $passwd); |
| | | |
| | | /** |
| | | * Signing. |
| | | */ |
| | | abstract function sign($text, $key, $passwd); |
| | | |
| | | /** |
| | | * Signature verification. |
| | | * |
| | | * @param string Message body |
| | | * @param string Signature, if message is of type PGP/MIME and body doesn't contain it |
| | | * |
| | | * @return mixed Signature information (enigma_signature) or enigma_error |
| | | */ |
| | | abstract function verify($text, $signature); |
| | | |
| | | /** |
| | | * Key/Cert file import. |
| | | * |
| | | * @param string File name or file content |
| | | * @param bollean True if first argument is a filename |
| | | * |
| | | * @return mixed Import status array or enigma_error |
| | | */ |
| | | abstract function import($content, $isfile=false); |
| | | |
| | | /** |
| | | * Keys listing. |
| | | * |
| | | * @param string Optional pattern for key ID, user ID or fingerprint |
| | | * |
| | | * @return mixed Array of enigma_key objects or enigma_error |
| | | */ |
| | | abstract function list_keys($pattern=''); |
| | | |
| | | /** |
| | | * Single key information. |
| | | * |
| | | * @param string Key ID, user ID or fingerprint |
| | | * |
| | | * @return mixed Key (enigma_key) object or enigma_error |
| | | */ |
| | | abstract function get_key($keyid); |
| | | |
| | | /** |
| | | * Key pair generation. |
| | | * |
| | | * @param array Key/User data |
| | | * |
| | | * @return mixed Key (enigma_key) object or enigma_error |
| | | */ |
| | | abstract function gen_key($data); |
| | | |
| | | /** |
| | | * Key deletion. |
| | | */ |
| | | abstract function del_key($keyid); |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | GnuPG (PGP) driver for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | require_once 'Crypt/GPG.php'; |
| | | |
| | | class enigma_driver_gnupg extends enigma_driver |
| | | { |
| | | private $rc; |
| | | private $gpg; |
| | | private $homedir; |
| | | private $user; |
| | | |
| | | function __construct($user) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $this->rc = $rcmail; |
| | | $this->user = $user; |
| | | } |
| | | |
| | | /** |
| | | * Driver initialization and environment checking. |
| | | * Should only return critical errors. |
| | | * |
| | | * @return mixed NULL on success, enigma_error on failure |
| | | */ |
| | | function init() |
| | | { |
| | | $homedir = $this->rc->config->get('enigma_pgp_homedir', INSTALL_PATH . '/plugins/enigma/home'); |
| | | |
| | | if (!$homedir) |
| | | return new enigma_error(enigma_error::E_INTERNAL, |
| | | "Option 'enigma_pgp_homedir' not specified"); |
| | | |
| | | // check if homedir exists (create it if not) and is readable |
| | | if (!file_exists($homedir)) |
| | | return new enigma_error(enigma_error::E_INTERNAL, |
| | | "Keys directory doesn't exists: $homedir"); |
| | | if (!is_writable($homedir)) |
| | | return new enigma_error(enigma_error::E_INTERNAL, |
| | | "Keys directory isn't writeable: $homedir"); |
| | | |
| | | $homedir = $homedir . '/' . $this->user; |
| | | |
| | | // check if user's homedir exists (create it if not) and is readable |
| | | if (!file_exists($homedir)) |
| | | mkdir($homedir, 0700); |
| | | |
| | | if (!file_exists($homedir)) |
| | | return new enigma_error(enigma_error::E_INTERNAL, |
| | | "Unable to create keys directory: $homedir"); |
| | | if (!is_writable($homedir)) |
| | | return new enigma_error(enigma_error::E_INTERNAL, |
| | | "Unable to write to keys directory: $homedir"); |
| | | |
| | | $this->homedir = $homedir; |
| | | |
| | | // Create Crypt_GPG object |
| | | try { |
| | | $this->gpg = new Crypt_GPG(array( |
| | | 'homedir' => $this->homedir, |
| | | // 'debug' => true, |
| | | )); |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | function encrypt($text, $keys) |
| | | { |
| | | /* |
| | | foreach ($keys as $key) { |
| | | $this->gpg->addEncryptKey($key); |
| | | } |
| | | $enc = $this->gpg->encrypt($text); |
| | | return $enc; |
| | | */ |
| | | } |
| | | |
| | | function decrypt($text, $key, $passwd) |
| | | { |
| | | // $this->gpg->addDecryptKey($key, $passwd); |
| | | try { |
| | | $dec = $this->gpg->decrypt($text); |
| | | return $dec; |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | function sign($text, $key, $passwd) |
| | | { |
| | | /* |
| | | $this->gpg->addSignKey($key, $passwd); |
| | | $signed = $this->gpg->sign($text, Crypt_GPG::SIGN_MODE_DETACHED); |
| | | return $signed; |
| | | */ |
| | | } |
| | | |
| | | function verify($text, $signature) |
| | | { |
| | | try { |
| | | $verified = $this->gpg->verify($text, $signature); |
| | | return $this->parse_signature($verified[0]); |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | public function import($content, $isfile=false) |
| | | { |
| | | try { |
| | | if ($isfile) |
| | | return $this->gpg->importKeyFile($content); |
| | | else |
| | | return $this->gpg->importKey($content); |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | public function list_keys($pattern='') |
| | | { |
| | | try { |
| | | $keys = $this->gpg->getKeys($pattern); |
| | | $result = array(); |
| | | //print_r($keys); |
| | | foreach ($keys as $idx => $key) { |
| | | $result[] = $this->parse_key($key); |
| | | unset($keys[$idx]); |
| | | } |
| | | //print_r($result); |
| | | return $result; |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | public function get_key($keyid) |
| | | { |
| | | $list = $this->list_keys($keyid); |
| | | |
| | | if (is_array($list)) |
| | | return array_shift($list); |
| | | |
| | | // error |
| | | return $list; |
| | | } |
| | | |
| | | public function gen_key($data) |
| | | { |
| | | } |
| | | |
| | | public function del_key($keyid) |
| | | { |
| | | // $this->get_key($keyid); |
| | | |
| | | |
| | | } |
| | | |
| | | public function del_privkey($keyid) |
| | | { |
| | | try { |
| | | $this->gpg->deletePrivateKey($keyid); |
| | | return true; |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | public function del_pubkey($keyid) |
| | | { |
| | | try { |
| | | $this->gpg->deletePublicKey($keyid); |
| | | return true; |
| | | } |
| | | catch (Exception $e) { |
| | | return $this->get_error_from_exception($e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Converts Crypt_GPG exception into Enigma's error object |
| | | * |
| | | * @param mixed Exception object |
| | | * |
| | | * @return enigma_error Error object |
| | | */ |
| | | private function get_error_from_exception($e) |
| | | { |
| | | $data = array(); |
| | | |
| | | if ($e instanceof Crypt_GPG_KeyNotFoundException) { |
| | | $error = enigma_error::E_KEYNOTFOUND; |
| | | $data['id'] = $e->getKeyId(); |
| | | } |
| | | else if ($e instanceof Crypt_GPG_BadPassphraseException) { |
| | | $error = enigma_error::E_BADPASS; |
| | | $data['bad'] = $e->getBadPassphrases(); |
| | | $data['missing'] = $e->getMissingPassphrases(); |
| | | } |
| | | else if ($e instanceof Crypt_GPG_NoDataException) |
| | | $error = enigma_error::E_NODATA; |
| | | else if ($e instanceof Crypt_GPG_DeletePrivateKeyException) |
| | | $error = enigma_error::E_DELKEY; |
| | | else |
| | | $error = enigma_error::E_INTERNAL; |
| | | |
| | | $msg = $e->getMessage(); |
| | | |
| | | return new enigma_error($error, $msg, $data); |
| | | } |
| | | |
| | | /** |
| | | * Converts Crypt_GPG_Signature object into Enigma's signature object |
| | | * |
| | | * @param Crypt_GPG_Signature Signature object |
| | | * |
| | | * @return enigma_signature Signature object |
| | | */ |
| | | private function parse_signature($sig) |
| | | { |
| | | $user = $sig->getUserId(); |
| | | |
| | | $data = new enigma_signature(); |
| | | $data->id = $sig->getId(); |
| | | $data->valid = $sig->isValid(); |
| | | $data->fingerprint = $sig->getKeyFingerprint(); |
| | | $data->created = $sig->getCreationDate(); |
| | | $data->expires = $sig->getExpirationDate(); |
| | | $data->name = $user->getName(); |
| | | $data->comment = $user->getComment(); |
| | | $data->email = $user->getEmail(); |
| | | |
| | | return $data; |
| | | } |
| | | |
| | | /** |
| | | * Converts Crypt_GPG_Key object into Enigma's key object |
| | | * |
| | | * @param Crypt_GPG_Key Key object |
| | | * |
| | | * @return enigma_key Key object |
| | | */ |
| | | private function parse_key($key) |
| | | { |
| | | $ekey = new enigma_key(); |
| | | |
| | | foreach ($key->getUserIds() as $idx => $user) { |
| | | $id = new enigma_userid(); |
| | | $id->name = $user->getName(); |
| | | $id->comment = $user->getComment(); |
| | | $id->email = $user->getEmail(); |
| | | $id->valid = $user->isValid(); |
| | | $id->revoked = $user->isRevoked(); |
| | | |
| | | $ekey->users[$idx] = $id; |
| | | } |
| | | |
| | | $ekey->name = trim($ekey->users[0]->name . ' <' . $ekey->users[0]->email . '>'); |
| | | |
| | | foreach ($key->getSubKeys() as $idx => $subkey) { |
| | | $skey = new enigma_subkey(); |
| | | $skey->id = $subkey->getId(); |
| | | $skey->revoked = $subkey->isRevoked(); |
| | | $skey->created = $subkey->getCreationDate(); |
| | | $skey->expires = $subkey->getExpirationDate(); |
| | | $skey->fingerprint = $subkey->getFingerprint(); |
| | | $skey->has_private = $subkey->hasPrivate(); |
| | | $skey->can_sign = $subkey->canSign(); |
| | | $skey->can_encrypt = $subkey->canEncrypt(); |
| | | |
| | | $ekey->subkeys[$idx] = $skey; |
| | | }; |
| | | |
| | | $ekey->id = $ekey->subkeys[0]->id; |
| | | |
| | | return $ekey; |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Engine of the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | |
| | | */ |
| | | |
| | | /* |
| | | RFC2440: OpenPGP Message Format |
| | | RFC3156: MIME Security with OpenPGP |
| | | RFC3851: S/MIME |
| | | */ |
| | | |
| | | class enigma_engine |
| | | { |
| | | private $rc; |
| | | private $enigma; |
| | | private $pgp_driver; |
| | | private $smime_driver; |
| | | |
| | | public $decryptions = array(); |
| | | public $signatures = array(); |
| | | public $signed_parts = array(); |
| | | |
| | | |
| | | /** |
| | | * Plugin initialization. |
| | | */ |
| | | function __construct($enigma) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $this->rc = $rcmail; |
| | | $this->enigma = $enigma; |
| | | } |
| | | |
| | | /** |
| | | * PGP driver initialization. |
| | | */ |
| | | function load_pgp_driver() |
| | | { |
| | | if ($this->pgp_driver) |
| | | return; |
| | | |
| | | $driver = 'enigma_driver_' . $this->rc->config->get('enigma_pgp_driver', 'gnupg'); |
| | | $username = $this->rc->user->get_username(); |
| | | |
| | | // Load driver |
| | | $this->pgp_driver = new $driver($username); |
| | | |
| | | if (!$this->pgp_driver) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: Unable to load PGP driver: $driver" |
| | | ), true, true); |
| | | } |
| | | |
| | | // Initialise driver |
| | | $result = $this->pgp_driver->init(); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: ".$result->getMessage() |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * S/MIME driver initialization. |
| | | */ |
| | | function load_smime_driver() |
| | | { |
| | | if ($this->smime_driver) |
| | | return; |
| | | |
| | | // NOT IMPLEMENTED! |
| | | return; |
| | | |
| | | $driver = 'enigma_driver_' . $this->rc->config->get('enigma_smime_driver', 'phpssl'); |
| | | $username = $this->rc->user->get_username(); |
| | | |
| | | // Load driver |
| | | $this->smime_driver = new $driver($username); |
| | | |
| | | if (!$this->smime_driver) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: Unable to load S/MIME driver: $driver" |
| | | ), true, true); |
| | | } |
| | | |
| | | // Initialise driver |
| | | $result = $this->smime_driver->init(); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: ".$result->getMessage() |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for plain/text message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | function parse_plain(&$p) |
| | | { |
| | | $part = $p['structure']; |
| | | |
| | | // Get message body from IMAP server |
| | | $this->set_part_body($part, $p['object']->uid); |
| | | |
| | | // @TODO: big message body can be a file resource |
| | | // PGP signed message |
| | | if (preg_match('/^-----BEGIN PGP SIGNED MESSAGE-----/', $part->body)) { |
| | | $this->parse_plain_signed($p); |
| | | } |
| | | // PGP encrypted message |
| | | else if (preg_match('/^-----BEGIN PGP MESSAGE-----/', $part->body)) { |
| | | $this->parse_plain_encrypted($p); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for multipart/signed message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | function parse_signed(&$p) |
| | | { |
| | | $struct = $p['structure']; |
| | | |
| | | // S/MIME |
| | | if ($struct->parts[1] && $struct->parts[1]->mimetype == 'application/pkcs7-signature') { |
| | | $this->parse_smime_signed($p); |
| | | } |
| | | // PGP/MIME: |
| | | // The multipart/signed body MUST consist of exactly two parts. |
| | | // The first part contains the signed data in MIME canonical format, |
| | | // including a set of appropriate content headers describing the data. |
| | | // The second body MUST contain the PGP digital signature. It MUST be |
| | | // labeled with a content type of "application/pgp-signature". |
| | | else if ($struct->parts[1] && $struct->parts[1]->mimetype == 'application/pgp-signature') { |
| | | $this->parse_pgp_signed($p); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for multipart/encrypted message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | function parse_encrypted(&$p) |
| | | { |
| | | $struct = $p['structure']; |
| | | |
| | | // S/MIME |
| | | if ($struct->mimetype == 'application/pkcs7-mime') { |
| | | $this->parse_smime_encrypted($p); |
| | | } |
| | | // PGP/MIME: |
| | | // The multipart/encrypted MUST consist of exactly two parts. The first |
| | | // MIME body part must have a content type of "application/pgp-encrypted". |
| | | // This body contains the control information. |
| | | // The second MIME body part MUST contain the actual encrypted data. It |
| | | // must be labeled with a content type of "application/octet-stream". |
| | | else if ($struct->parts[0] && $struct->parts[0]->mimetype == 'application/pgp-encrypted' && |
| | | $struct->parts[1] && $struct->parts[1]->mimetype == 'application/octet-stream' |
| | | ) { |
| | | $this->parse_pgp_encrypted($p); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for plain signed message. |
| | | * Excludes message and signature bodies and verifies signature. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_plain_signed(&$p) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $part = $p['structure']; |
| | | |
| | | // Verify signature |
| | | if ($this->rc->action == 'show' || $this->rc->action == 'preview') { |
| | | $sig = $this->pgp_verify($part->body); |
| | | } |
| | | |
| | | // @TODO: Handle big bodies using (temp) files |
| | | |
| | | // In this way we can use fgets on string as on file handle |
| | | $fh = fopen('php://memory', 'br+'); |
| | | // @TODO: fopen/fwrite errors handling |
| | | if ($fh) { |
| | | fwrite($fh, $part->body); |
| | | rewind($fh); |
| | | } |
| | | $part->body = null; |
| | | |
| | | // Extract body (and signature?) |
| | | while (!feof($fh)) { |
| | | $line = fgets($fh, 1024); |
| | | |
| | | if ($part->body === null) |
| | | $part->body = ''; |
| | | else if (preg_match('/^-----BEGIN PGP SIGNATURE-----/', $line)) |
| | | break; |
| | | else |
| | | $part->body .= $line; |
| | | } |
| | | |
| | | // Remove "Hash" Armor Headers |
| | | $part->body = preg_replace('/^.*\r*\n\r*\n/', '', $part->body); |
| | | // de-Dash-Escape (RFC2440) |
| | | $part->body = preg_replace('/(^|\n)- -/', '\\1-', $part->body); |
| | | |
| | | // Store signature data for display |
| | | if (!empty($sig)) { |
| | | $this->signed_parts[$part->mime_id] = $part->mime_id; |
| | | $this->signatures[$part->mime_id] = $sig; |
| | | } |
| | | |
| | | fclose($fh); |
| | | } |
| | | |
| | | /** |
| | | * Handler for PGP/MIME signed message. |
| | | * Verifies signature. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_pgp_signed(&$p) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $struct = $p['structure']; |
| | | |
| | | // Verify signature |
| | | if ($this->rc->action == 'show' || $this->rc->action == 'preview') { |
| | | $msg_part = $struct->parts[0]; |
| | | $sig_part = $struct->parts[1]; |
| | | |
| | | // Get bodies |
| | | $this->set_part_body($msg_part, $p['object']->uid); |
| | | $this->set_part_body($sig_part, $p['object']->uid); |
| | | |
| | | // Verify |
| | | $sig = $this->pgp_verify($msg_part->body, $sig_part->body); |
| | | |
| | | // Store signature data for display |
| | | $this->signatures[$struct->mime_id] = $sig; |
| | | |
| | | // Message can be multipart (assign signature to each subpart) |
| | | if (!empty($msg_part->parts)) { |
| | | foreach ($msg_part->parts as $part) |
| | | $this->signed_parts[$part->mime_id] = $struct->mime_id; |
| | | } |
| | | else |
| | | $this->signed_parts[$msg_part->mime_id] = $struct->mime_id; |
| | | |
| | | // Remove signature file from attachments list |
| | | unset($struct->parts[1]); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for S/MIME signed message. |
| | | * Verifies signature. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_smime_signed(&$p) |
| | | { |
| | | $this->load_smime_driver(); |
| | | } |
| | | |
| | | /** |
| | | * Handler for plain encrypted message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_plain_encrypted(&$p) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $part = $p['structure']; |
| | | |
| | | // Get body |
| | | $this->set_part_body($part, $p['object']->uid); |
| | | |
| | | // Decrypt |
| | | $result = $this->pgp_decrypt($part->body); |
| | | |
| | | // Store decryption status |
| | | $this->decryptions[$part->mime_id] = $result; |
| | | |
| | | // Parse decrypted message |
| | | if ($result === true) { |
| | | // @TODO |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for PGP/MIME encrypted message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_pgp_encrypted(&$p) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $struct = $p['structure']; |
| | | $part = $struct->parts[1]; |
| | | |
| | | // Get body |
| | | $this->set_part_body($part, $p['object']->uid); |
| | | |
| | | // Decrypt |
| | | $result = $this->pgp_decrypt($part->body); |
| | | |
| | | $this->decryptions[$part->mime_id] = $result; |
| | | //print_r($part); |
| | | // Parse decrypted message |
| | | if ($result === true) { |
| | | // @TODO |
| | | } |
| | | else { |
| | | // Make sure decryption status message will be displayed |
| | | $part->type = 'content'; |
| | | $p['object']->parts[] = $part; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for S/MIME encrypted message. |
| | | * |
| | | * @param array Reference to hook's parameters |
| | | */ |
| | | private function parse_smime_encrypted(&$p) |
| | | { |
| | | $this->load_smime_driver(); |
| | | } |
| | | |
| | | /** |
| | | * PGP signature verification. |
| | | * |
| | | * @param mixed Message body |
| | | * @param mixed Signature body (for MIME messages) |
| | | * |
| | | * @return mixed enigma_signature or enigma_error |
| | | */ |
| | | private function pgp_verify(&$msg_body, $sig_body=null) |
| | | { |
| | | // @TODO: Handle big bodies using (temp) files |
| | | // @TODO: caching of verification result |
| | | |
| | | $sig = $this->pgp_driver->verify($msg_body, $sig_body); |
| | | |
| | | if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::E_KEYNOTFOUND) |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: " . $error->getMessage() |
| | | ), true, false); |
| | | |
| | | //print_r($sig); |
| | | return $sig; |
| | | } |
| | | |
| | | /** |
| | | * PGP message decryption. |
| | | * |
| | | * @param mixed Message body |
| | | * |
| | | * @return mixed True or enigma_error |
| | | */ |
| | | private function pgp_decrypt(&$msg_body) |
| | | { |
| | | // @TODO: Handle big bodies using (temp) files |
| | | // @TODO: caching of verification result |
| | | |
| | | $result = $this->pgp_driver->decrypt($msg_body, $key, $pass); |
| | | |
| | | //print_r($result); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | $err_code = $result->getCode(); |
| | | if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS))) |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: " . $result->getMessage() |
| | | ), true, false); |
| | | return $result; |
| | | } |
| | | |
| | | // $msg_body = $result; |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * PGP keys listing. |
| | | * |
| | | * @param mixed Key ID/Name pattern |
| | | * |
| | | * @return mixed Array of keys or enigma_error |
| | | */ |
| | | function list_keys($pattern='') |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $result = $this->pgp_driver->list_keys($pattern); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: " . $result->getMessage() |
| | | ), true, false); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * PGP key details. |
| | | * |
| | | * @param mixed Key ID |
| | | * |
| | | * @return mixed enigma_key or enigma_error |
| | | */ |
| | | function get_key($keyid) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $result = $this->pgp_driver->get_key($keyid); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: " . $result->getMessage() |
| | | ), true, false); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * PGP keys/certs importing. |
| | | * |
| | | * @param mixed Import file name or content |
| | | * @param boolean True if first argument is a filename |
| | | * |
| | | * @return mixed Import status data array or enigma_error |
| | | */ |
| | | function import_key($content, $isfile=false) |
| | | { |
| | | $this->load_pgp_driver(); |
| | | $result = $this->pgp_driver->import($content, $isfile); |
| | | |
| | | if ($result instanceof enigma_error) { |
| | | raise_error(array( |
| | | 'code' => 600, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Enigma plugin: " . $result->getMessage() |
| | | ), true, false); |
| | | } |
| | | else { |
| | | $result['imported'] = $result['public_imported'] + $result['private_imported']; |
| | | $result['unchanged'] = $result['public_unchanged'] + $result['private_unchanged']; |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Handler for keys/certs import request action |
| | | */ |
| | | function import_file() |
| | | { |
| | | $uid = get_input_value('_uid', RCUBE_INPUT_POST); |
| | | $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); |
| | | $mime_id = get_input_value('_part', RCUBE_INPUT_POST); |
| | | |
| | | if ($uid && $mime_id) { |
| | | $part = $this->rc->imap->get_message_part($uid, $mime_id); |
| | | } |
| | | |
| | | if ($part && is_array($result = $this->import_key($part))) { |
| | | $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation', |
| | | array('new' => $result['imported'], 'old' => $result['unchanged'])); |
| | | } |
| | | else |
| | | $this->rc->output->show_message('enigma.keysimportfailed', 'error'); |
| | | |
| | | $this->rc->output->send(); |
| | | } |
| | | |
| | | /** |
| | | * Checks if specified message part contains body data. |
| | | * If body is not set it will be fetched from IMAP server. |
| | | * |
| | | * @param rcube_message_part Message part object |
| | | * @param integer Message UID |
| | | */ |
| | | private function set_part_body($part, $uid) |
| | | { |
| | | // @TODO: Create such function in core |
| | | // @TODO: Handle big bodies using file handles |
| | | if (!isset($part->body)) { |
| | | $part->body = $this->rc->imap->get_message_part( |
| | | $uid, $part->mime_id, $part); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Adds CSS style file to the page header. |
| | | */ |
| | | private function add_css() |
| | | { |
| | | $skin = $this->rc->config->get('skin'); |
| | | if (!file_exists($this->home . "/skins/$skin/enigma.css")) |
| | | $skin = 'default'; |
| | | |
| | | $this->include_stylesheet("skins/$skin/enigma.css"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Error class for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_error |
| | | { |
| | | private $code; |
| | | private $message; |
| | | private $data = array(); |
| | | |
| | | // error codes |
| | | const E_OK = 0; |
| | | const E_INTERNAL = 1; |
| | | const E_NODATA = 2; |
| | | const E_KEYNOTFOUND = 3; |
| | | const E_DELKEY = 4; |
| | | const E_BADPASS = 5; |
| | | |
| | | function __construct($code = null, $message = '', $data = array()) |
| | | { |
| | | $this->code = $code; |
| | | $this->message = $message; |
| | | $this->data = $data; |
| | | } |
| | | |
| | | function getCode() |
| | | { |
| | | return $this->code; |
| | | } |
| | | |
| | | function getMessage() |
| | | { |
| | | return $this->message; |
| | | } |
| | | |
| | | function getData($name) |
| | | { |
| | | if ($name) |
| | | return $this->data[$name]; |
| | | else |
| | | return $this->data; |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Key class for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_key |
| | | { |
| | | public $id; |
| | | public $name; |
| | | public $users = array(); |
| | | public $subkeys = array(); |
| | | |
| | | const TYPE_UNKNOWN = 0; |
| | | const TYPE_KEYPAIR = 1; |
| | | const TYPE_PUBLIC = 2; |
| | | |
| | | /** |
| | | * Keys list sorting callback for usort() |
| | | */ |
| | | static function cmp($a, $b) |
| | | { |
| | | return strcmp($a->name, $b->name); |
| | | } |
| | | |
| | | /** |
| | | * Returns key type |
| | | */ |
| | | function get_type() |
| | | { |
| | | if ($this->subkeys[0]->has_private) |
| | | return enigma_key::TYPE_KEYPAIR; |
| | | else if (!empty($this->subkeys[0])) |
| | | return enigma_key::TYPE_PUBLIC; |
| | | |
| | | return enigma_key::TYPE_UNKNOWN; |
| | | } |
| | | |
| | | /** |
| | | * Returns true if all user IDs are revoked |
| | | */ |
| | | function is_revoked() |
| | | { |
| | | foreach ($this->subkeys as $subkey) |
| | | if (!$subkey->revoked) |
| | | return false; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns true if any user ID is valid |
| | | */ |
| | | function is_valid() |
| | | { |
| | | foreach ($this->users as $user) |
| | | if ($user->valid) |
| | | return true; |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns true if any of subkeys is not expired |
| | | */ |
| | | function is_expired() |
| | | { |
| | | $now = time(); |
| | | |
| | | foreach ($this->subkeys as $subkey) |
| | | if (!$subkey->expires || $subkey->expires > $now) |
| | | return true; |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Converts long ID or Fingerprint to short ID |
| | | * Crypt_GPG uses internal, but e.g. Thunderbird's Enigmail displays short ID |
| | | * |
| | | * @param string Key ID or fingerprint |
| | | * @return string Key short ID |
| | | */ |
| | | static function format_id($id) |
| | | { |
| | | // E.g. 04622F2089E037A5 => 89E037A5 |
| | | |
| | | return substr($id, -8); |
| | | } |
| | | |
| | | /** |
| | | * Formats fingerprint string |
| | | * |
| | | * @param string Key fingerprint |
| | | * |
| | | * @return string Formatted fingerprint (with spaces) |
| | | */ |
| | | static function format_fingerprint($fingerprint) |
| | | { |
| | | if (!$fingerprint) |
| | | return ''; |
| | | |
| | | $result = ''; |
| | | for ($i=0; $i<40; $i++) { |
| | | if ($i % 4 == 0) |
| | | $result .= ' '; |
| | | $result .= $fingerprint[$i]; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Signature class for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_signature |
| | | { |
| | | public $id; |
| | | public $valid; |
| | | public $fingerprint; |
| | | public $created; |
| | | public $expires; |
| | | public $name; |
| | | public $comment; |
| | | public $email; |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | SubKey class for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_subkey |
| | | { |
| | | public $id; |
| | | public $fingerprint; |
| | | public $expires; |
| | | public $created; |
| | | public $revoked; |
| | | public $has_private; |
| | | public $can_sign; |
| | | public $can_encrypt; |
| | | |
| | | /** |
| | | * Converts internal ID to short ID |
| | | * Crypt_GPG uses internal, but e.g. Thunderbird's Enigmail displays short ID |
| | | * |
| | | * @return string Key ID |
| | | */ |
| | | function get_short_id() |
| | | { |
| | | // E.g. 04622F2089E037A5 => 89E037A5 |
| | | return enigma_key::format_id($this->id); |
| | | } |
| | | |
| | | /** |
| | | * Getter for formatted fingerprint |
| | | * |
| | | * @return string Formatted fingerprint |
| | | */ |
| | | function get_fingerprint() |
| | | { |
| | | return enigma_key::format_fingerprint($this->fingerprint); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | User Interface for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_ui |
| | | { |
| | | private $rc; |
| | | private $enigma; |
| | | private $home; |
| | | private $css_added; |
| | | private $data; |
| | | |
| | | |
| | | function __construct($enigma_plugin, $home='') |
| | | { |
| | | $this->enigma = $enigma_plugin; |
| | | $this->rc = $enigma_plugin->rc; |
| | | // we cannot use $enigma_plugin->home here |
| | | $this->home = $home; |
| | | } |
| | | |
| | | /** |
| | | * UI initialization and requests handlers. |
| | | * |
| | | * @param string Preferences section |
| | | */ |
| | | function init($section='') |
| | | { |
| | | $this->enigma->include_script('enigma.js'); |
| | | |
| | | // Enigma actions |
| | | if ($this->rc->action == 'plugin.enigma') { |
| | | $action = get_input_value('_a', RCUBE_INPUT_GPC); |
| | | |
| | | switch ($action) { |
| | | case 'keyedit': |
| | | $this->key_edit(); |
| | | break; |
| | | case 'keyimport': |
| | | $this->key_import(); |
| | | break; |
| | | case 'keysearch': |
| | | case 'keylist': |
| | | $this->key_list(); |
| | | break; |
| | | case 'keyinfo': |
| | | default: |
| | | $this->key_info(); |
| | | } |
| | | } |
| | | // Preferences UI |
| | | else { // if ($this->rc->action == 'edit-prefs') { |
| | | if ($section == 'enigmacerts') { |
| | | $this->rc->output->add_handlers(array( |
| | | 'keyslist' => array($this, 'tpl_certs_list'), |
| | | 'keyframe' => array($this, 'tpl_cert_frame'), |
| | | 'countdisplay' => array($this, 'tpl_certs_rowcount'), |
| | | 'searchform' => array($this->rc->output, 'search_form'), |
| | | )); |
| | | $this->rc->output->set_pagetitle($this->enigma->gettext('enigmacerts')); |
| | | $this->rc->output->send('enigma.certs'); |
| | | } |
| | | else { |
| | | $this->rc->output->add_handlers(array( |
| | | 'keyslist' => array($this, 'tpl_keys_list'), |
| | | 'keyframe' => array($this, 'tpl_key_frame'), |
| | | 'countdisplay' => array($this, 'tpl_keys_rowcount'), |
| | | 'searchform' => array($this->rc->output, 'search_form'), |
| | | )); |
| | | $this->rc->output->set_pagetitle($this->enigma->gettext('enigmakeys')); |
| | | $this->rc->output->send('enigma.keys'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Adds CSS style file to the page header. |
| | | */ |
| | | function add_css() |
| | | { |
| | | if ($this->css_loaded) |
| | | return; |
| | | |
| | | $skin = $this->rc->config->get('skin'); |
| | | if (!file_exists($this->home . "/skins/$skin/enigma.css")) |
| | | $skin = 'default'; |
| | | |
| | | $this->enigma->include_stylesheet("skins/$skin/enigma.css"); |
| | | $this->css_added = true; |
| | | } |
| | | |
| | | /** |
| | | * Template object for key info/edit frame. |
| | | * |
| | | * @param array Object attributes |
| | | * |
| | | * @return string HTML output |
| | | */ |
| | | function tpl_key_frame($attrib) |
| | | { |
| | | if (!$attrib['id']) { |
| | | $attrib['id'] = 'rcmkeysframe'; |
| | | } |
| | | |
| | | $attrib['name'] = $attrib['id']; |
| | | |
| | | $this->rc->output->set_env('contentframe', $attrib['name']); |
| | | $this->rc->output->set_env('blankpage', $attrib['src'] ? |
| | | $this->rc->output->abs_url($attrib['src']) : 'program/blank.gif'); |
| | | |
| | | return html::tag('iframe', $attrib); |
| | | } |
| | | |
| | | /** |
| | | * Template object for list of keys. |
| | | * |
| | | * @param array Object attributes |
| | | * |
| | | * @return string HTML content |
| | | */ |
| | | function tpl_keys_list($attrib) |
| | | { |
| | | // add id to message list table if not specified |
| | | if (!strlen($attrib['id'])) { |
| | | $attrib['id'] = 'rcmenigmakeyslist'; |
| | | } |
| | | |
| | | // define list of cols to be displayed |
| | | $a_show_cols = array('name'); |
| | | |
| | | // create XHTML table |
| | | $out = rcube_table_output($attrib, array(), $a_show_cols, 'id'); |
| | | |
| | | // set client env |
| | | $this->rc->output->add_gui_object('keyslist', $attrib['id']); |
| | | $this->rc->output->include_script('list.js'); |
| | | |
| | | // add some labels to client |
| | | $this->rc->output->add_label('enigma.keyconfirmdelete'); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | /** |
| | | * Key listing (and searching) request handler |
| | | */ |
| | | private function key_list() |
| | | { |
| | | $this->enigma->load_engine(); |
| | | |
| | | $pagesize = $this->rc->config->get('pagesize', 100); |
| | | $page = max(intval(get_input_value('_p', RCUBE_INPUT_GPC)), 1); |
| | | $search = get_input_value('_q', RCUBE_INPUT_GPC); |
| | | |
| | | // define list of cols to be displayed |
| | | $a_show_cols = array('name'); |
| | | $result = array(); |
| | | |
| | | // Get the list |
| | | $list = $this->enigma->engine->list_keys($search); |
| | | |
| | | if ($list && ($list instanceof enigma_error)) |
| | | $this->rc->output->show_message('enigma.keylisterror', 'error'); |
| | | else if (empty($list)) |
| | | $this->rc->output->show_message('enigma.nokeysfound', 'notice'); |
| | | else { |
| | | if (is_array($list)) { |
| | | // Save the size |
| | | $listsize = count($list); |
| | | |
| | | // Sort the list by key (user) name |
| | | usort($list, array('enigma_key', 'cmp')); |
| | | |
| | | // Slice current page |
| | | $list = array_slice($list, ($page - 1) * $pagesize, $pagesize); |
| | | |
| | | $size = count($list); |
| | | |
| | | // Add rows |
| | | foreach($list as $idx => $key) { |
| | | $this->rc->output->command('enigma_add_list_row', |
| | | array('name' => Q($key->name), 'id' => $key->id)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | $this->rc->output->set_env('search_request', $search); |
| | | $this->rc->output->set_env('pagecount', ceil($listsize/$pagesize)); |
| | | $this->rc->output->set_env('current_page', $page); |
| | | $this->rc->output->command('set_rowcount', |
| | | $this->get_rowcount_text($listsize, $size, $page)); |
| | | |
| | | $this->rc->output->send(); |
| | | } |
| | | |
| | | /** |
| | | * Template object for list records counter. |
| | | * |
| | | * @param array Object attributes |
| | | * |
| | | * @return string HTML output |
| | | */ |
| | | function tpl_keys_rowcount($attrib) |
| | | { |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmcountdisplay'; |
| | | |
| | | $this->rc->output->add_gui_object('countdisplay', $attrib['id']); |
| | | |
| | | return html::span($attrib, $this->get_rowcount_text()); |
| | | } |
| | | |
| | | /** |
| | | * Returns text representation of list records counter |
| | | */ |
| | | private function get_rowcount_text($all=0, $curr_count=0, $page=1) |
| | | { |
| | | if (!$curr_count) |
| | | $out = $this->enigma->gettext('nokeysfound'); |
| | | else { |
| | | $pagesize = $this->rc->config->get('pagesize', 100); |
| | | $first = ($page - 1) * $pagesize; |
| | | |
| | | $out = $this->enigma->gettext(array( |
| | | 'name' => 'keysfromto', |
| | | 'vars' => array( |
| | | 'from' => $first + 1, |
| | | 'to' => $first + $curr_count, |
| | | 'count' => $all) |
| | | )); |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | /** |
| | | * Key information page handler |
| | | */ |
| | | private function key_info() |
| | | { |
| | | $id = get_input_value('_id', RCUBE_INPUT_GET); |
| | | |
| | | $this->enigma->load_engine(); |
| | | $res = $this->enigma->engine->get_key($id); |
| | | |
| | | if ($res instanceof enigma_key) |
| | | $this->data = $res; |
| | | else { // error |
| | | $this->rc->output->show_message('enigma.keyopenerror', 'error'); |
| | | $this->rc->output->command('parent.enigma_loadframe'); |
| | | $this->rc->output->send('iframe'); |
| | | } |
| | | |
| | | $this->rc->output->add_handlers(array( |
| | | 'keyname' => array($this, 'tpl_key_name'), |
| | | 'keydata' => array($this, 'tpl_key_data'), |
| | | )); |
| | | |
| | | $this->rc->output->set_pagetitle($this->enigma->gettext('keyinfo')); |
| | | $this->rc->output->send('enigma.keyinfo'); |
| | | } |
| | | |
| | | /** |
| | | * Template object for key name |
| | | */ |
| | | function tpl_key_name($attrib) |
| | | { |
| | | return Q($this->data->name); |
| | | } |
| | | |
| | | /** |
| | | * Template object for key information page content |
| | | */ |
| | | function tpl_key_data($attrib) |
| | | { |
| | | $out = ''; |
| | | $table = new html_table(array('cols' => 2)); |
| | | |
| | | // Key user ID |
| | | $table->add('title', $this->enigma->gettext('keyuserid')); |
| | | $table->add(null, Q($this->data->name)); |
| | | // Key ID |
| | | $table->add('title', $this->enigma->gettext('keyid')); |
| | | $table->add(null, $this->data->subkeys[0]->get_short_id()); |
| | | // Key type |
| | | $keytype = $this->data->get_type(); |
| | | if ($keytype == enigma_key::TYPE_KEYPAIR) |
| | | $type = $this->enigma->gettext('typekeypair'); |
| | | else if ($keytype == enigma_key::TYPE_PUBLIC) |
| | | $type = $this->enigma->gettext('typepublickey'); |
| | | $table->add('title', $this->enigma->gettext('keytype')); |
| | | $table->add(null, $type); |
| | | // Key fingerprint |
| | | $table->add('title', $this->enigma->gettext('fingerprint')); |
| | | $table->add(null, $this->data->subkeys[0]->get_fingerprint()); |
| | | |
| | | $out .= html::tag('fieldset', null, |
| | | html::tag('legend', null, |
| | | $this->enigma->gettext('basicinfo')) . $table->show($attrib)); |
| | | |
| | | // Subkeys |
| | | $table = new html_table(array('cols' => 6)); |
| | | // Columns: Type, ID, Algorithm, Size, Created, Expires |
| | | |
| | | $out .= html::tag('fieldset', null, |
| | | html::tag('legend', null, |
| | | $this->enigma->gettext('subkeys')) . $table->show($attrib)); |
| | | |
| | | // Additional user IDs |
| | | $table = new html_table(array('cols' => 2)); |
| | | // Columns: User ID, Validity |
| | | |
| | | $out .= html::tag('fieldset', null, |
| | | html::tag('legend', null, |
| | | $this->enigma->gettext('userids')) . $table->show($attrib)); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | /** |
| | | * Key import page handler |
| | | */ |
| | | private function key_import() |
| | | { |
| | | // Import process |
| | | if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name'])) { |
| | | $this->enigma->load_engine(); |
| | | $result = $this->enigma->engine->import_key($_FILES['_file']['tmp_name'], true); |
| | | |
| | | if (is_array($result)) { |
| | | // reload list if any keys has been added |
| | | if ($result['imported']) { |
| | | $this->rc->output->command('parent.enigma_list', 1); |
| | | } |
| | | else |
| | | $this->rc->output->command('parent.enigma_loadframe'); |
| | | |
| | | $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation', |
| | | array('new' => $result['imported'], 'old' => $result['unchanged'])); |
| | | |
| | | $this->rc->output->send('iframe'); |
| | | } |
| | | else |
| | | $this->rc->output->show_message('enigma.keysimportfailed', 'error'); |
| | | } |
| | | else if ($err = $_FILES['_file']['error']) { |
| | | if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { |
| | | $this->rc->output->show_message('filesizeerror', 'error', |
| | | array('size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))); |
| | | } else { |
| | | $this->rc->output->show_message('fileuploaderror', 'error'); |
| | | } |
| | | } |
| | | |
| | | $this->rc->output->add_handlers(array( |
| | | 'importform' => array($this, 'tpl_key_import_form'), |
| | | )); |
| | | |
| | | $this->rc->output->set_pagetitle($this->enigma->gettext('keyimport')); |
| | | $this->rc->output->send('enigma.keyimport'); |
| | | } |
| | | |
| | | /** |
| | | * Template object for key import (upload) form |
| | | */ |
| | | function tpl_key_import_form($attrib) |
| | | { |
| | | $attrib += array('id' => 'rcmKeyImportForm'); |
| | | |
| | | $upload = new html_inputfield(array('type' => 'file', 'name' => '_file', |
| | | 'id' => 'rcmimportfile', 'size' => 30)); |
| | | |
| | | $form = html::p(null, |
| | | Q($this->enigma->gettext('keyimporttext'), 'show') |
| | | . html::br() . html::br() . $upload->show() |
| | | ); |
| | | |
| | | $this->rc->output->add_label('selectimportfile', 'importwait'); |
| | | $this->rc->output->add_gui_object('importform', $attrib['id']); |
| | | |
| | | $out = $this->rc->output->form_tag(array( |
| | | 'action' => $this->rc->url(array('action' => 'plugin.enigma', 'a' => 'keyimport')), |
| | | 'method' => 'post', |
| | | 'enctype' => 'multipart/form-data') + $attrib, |
| | | $form); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | User ID class for the Enigma Plugin | |
| | | | | |
| | | | This program is free software; you can redistribute it and/or modify | |
| | | | it under the terms of the GNU General Public License version 2 | |
| | | | as published by the Free Software Foundation. | |
| | | | | |
| | | | This program is distributed in the hope that it will be useful, | |
| | | | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | GNU General Public License for more details. | |
| | | | | |
| | | | You should have received a copy of the GNU General Public License along | |
| | | | with this program; if not, write to the Free Software Foundation, Inc., | |
| | | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| | | | | |
| | | +-------------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-------------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | class enigma_userid |
| | | { |
| | | public $revoked; |
| | | public $valid; |
| | | public $name; |
| | | public $comment; |
| | | public $email; |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['enigmasettings'] = 'Enigma: Settings'; |
| | | $labels['enigmacerts'] = 'Enigma: Certificates (S/MIME)'; |
| | | $labels['enigmakeys'] = 'Enigma: Keys (PGP)'; |
| | | $labels['keysfromto'] = 'Keys $from to $to of $count'; |
| | | $labels['keyname'] = 'Name'; |
| | | $labels['keyid'] = 'Key ID'; |
| | | $labels['keyuserid'] = 'User ID'; |
| | | $labels['keytype'] = 'Key type'; |
| | | $labels['fingerprint'] = 'Fingerprint'; |
| | | $labels['subkeys'] = 'Subkeys'; |
| | | $labels['basicinfo'] = 'Basic Information'; |
| | | $labels['userids'] = 'Additional User IDs'; |
| | | $labels['typepublickey'] = 'public key'; |
| | | $labels['typekeypair'] = 'key pair'; |
| | | $labels['keyattfound'] = 'This message contains attached PGP key(s).'; |
| | | $labels['keyattimport'] = 'Import key(s)'; |
| | | |
| | | $labels['createkeys'] = 'Create a new key pair'; |
| | | $labels['importkeys'] = 'Import key(s)'; |
| | | $labels['exportkeys'] = 'Export key(s)'; |
| | | $labels['deletekeys'] = 'Delete key(s)'; |
| | | $labels['keyactions'] = 'Key actions...'; |
| | | $labels['keydisable'] = 'Disable key'; |
| | | $labels['keyrevoke'] = 'Revoke key'; |
| | | $labels['keysend'] = 'Send public key in a message'; |
| | | $labels['keychpass'] = 'Change password'; |
| | | |
| | | $messages = array(); |
| | | $messages['sigvalid'] = 'Verified signature from $sender.'; |
| | | $messages['siginvalid'] = 'Invalid signature from $sender.'; |
| | | $messages['signokey'] = 'Unverified signature. Public key not found. Key ID: $keyid.'; |
| | | $messages['sigerror'] = 'Unverified signature. Internal error.'; |
| | | $messages['decryptok'] = 'Message decrypted.'; |
| | | $messages['decrypterror'] = 'Decryption failed.'; |
| | | $messages['decryptnokey'] = 'Decryption failed. Private key not found. Key ID: $keyid.'; |
| | | $messages['decryptbadpass'] = 'Decryption failed. Bad password.'; |
| | | $messages['nokeysfound'] = 'No keys found'; |
| | | $messages['keyopenerror'] = 'Unable to get key information! Internal error.'; |
| | | $messages['keylisterror'] = 'Unable to list keys! Internal error.'; |
| | | $messages['keysimportfailed'] = 'Unable to import key(s)! Internal error.'; |
| | | $messages['keysimportsuccess'] = 'Key(s) imported successfully. Imported: $new, unchanged: $old.'; |
| | | $messages['keyconfirmdelete'] = 'Are you sure, you want to delete selected key(s)?'; |
| | | $messages['keyimporttext'] = 'You can import private and public key(s) or revocation signatures in ASCII-Armor format.'; |
| | | |
| | | ?> |
New file |
| | |
| | | /*** Style for Enigma plugin ***/ |
| | | |
| | | /***** Messages displaying *****/ |
| | | |
| | | #enigma-message, |
| | | /* fixes border-top */ |
| | | #messagebody div #enigma-message |
| | | { |
| | | margin: 0; |
| | | margin-bottom: 5px; |
| | | min-height: 20px; |
| | | padding: 10px 10px 6px 46px; |
| | | } |
| | | |
| | | div.enigmaerror, |
| | | /* fixes border-top */ |
| | | #messagebody div.enigmaerror |
| | | { |
| | | background: url(enigma_error.png) 6px 1px no-repeat; |
| | | background-color: #EF9398; |
| | | border: 1px solid #DC5757; |
| | | } |
| | | |
| | | div.enigmanotice, |
| | | /* fixes border-top */ |
| | | #messagebody div.enigmanotice |
| | | { |
| | | background: url(enigma.png) 6px 1px no-repeat; |
| | | background-color: #A6EF7B; |
| | | border: 1px solid #76C83F; |
| | | } |
| | | |
| | | div.enigmawarning, |
| | | /* fixes border-top */ |
| | | #messagebody div.enigmawarning |
| | | { |
| | | background: url(enigma.png) 6px 1px no-repeat; |
| | | background-color: #F7FDCB; |
| | | border: 1px solid #C2D071; |
| | | } |
| | | |
| | | #enigma-message a |
| | | { |
| | | color: #666666; |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | #enigma-message a:hover |
| | | { |
| | | color: #333333; |
| | | } |
| | | |
| | | /***** Keys/Certs Management *****/ |
| | | |
| | | div.enigmascreen |
| | | { |
| | | position: absolute; |
| | | top: 65px; |
| | | right: 10px; |
| | | bottom: 10px; |
| | | left: 10px; |
| | | } |
| | | |
| | | #enigmacontent-box |
| | | { |
| | | position: absolute; |
| | | top: 0px; |
| | | left: 290px; |
| | | right: 0px; |
| | | bottom: 0px; |
| | | border: 1px solid #999999; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | #enigmakeyslist |
| | | { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | border: 1px solid #999999; |
| | | background-color: #F9F9F9; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | #keylistcountbar |
| | | { |
| | | margin-top: 4px; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | #keys-table |
| | | { |
| | | width: 100%; |
| | | table-layout: fixed; |
| | | } |
| | | |
| | | #keys-table td |
| | | { |
| | | cursor: default; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | } |
| | | |
| | | #key-details table td.title |
| | | { |
| | | font-weight: bold; |
| | | text-align: right; |
| | | } |
| | | |
| | | #keystoolbar |
| | | { |
| | | position: absolute; |
| | | top: 30px; |
| | | left: 10px; |
| | | height: 35px; |
| | | } |
| | | |
| | | #keystoolbar a |
| | | { |
| | | padding-right: 10px; |
| | | } |
| | | |
| | | #keystoolbar a.button, |
| | | #keystoolbar a.buttonPas, |
| | | #keystoolbar span.separator { |
| | | display: block; |
| | | float: left; |
| | | width: 32px; |
| | | height: 32px; |
| | | padding: 0; |
| | | margin-right: 10px; |
| | | overflow: hidden; |
| | | background: url(keys_toolbar.png) 0 0 no-repeat transparent; |
| | | opacity: 0.99; /* this is needed to make buttons appear correctly in Chrome */ |
| | | } |
| | | |
| | | #keystoolbar a.buttonPas { |
| | | opacity: 0.35; |
| | | } |
| | | |
| | | #keystoolbar a.createSel { |
| | | background-position: 0 -32px; |
| | | } |
| | | |
| | | #keystoolbar a.create { |
| | | background-position: 0 0; |
| | | } |
| | | |
| | | #keystoolbar a.deleteSel { |
| | | background-position: -32px -32px; |
| | | } |
| | | |
| | | #keystoolbar a.delete { |
| | | background-position: -32px 0; |
| | | } |
| | | |
| | | #keystoolbar a.importSel { |
| | | background-position: -64px -32px; |
| | | } |
| | | |
| | | #keystoolbar a.import { |
| | | background-position: -64px 0; |
| | | } |
| | | |
| | | #keystoolbar a.exportSel { |
| | | background-position: -96px -32px; |
| | | } |
| | | |
| | | #keystoolbar a.export { |
| | | background-position: -96px 0; |
| | | } |
| | | |
| | | #keystoolbar a.keymenu { |
| | | background-position: -128px 0; |
| | | width: 36px; |
| | | } |
| | | |
| | | #keystoolbar span.separator { |
| | | width: 5px; |
| | | background-position: -166px 0; |
| | | } |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/enigma.css" /> |
| | | </head> |
| | | <body class="iframe"> |
| | | |
| | | <div id="keyimport-title" class="boxtitle"><roundcube:label name="enigma.importkeys" /></div> |
| | | |
| | | <div id="import-form" class="boxcontent"> |
| | | <roundcube:object name="importform" /> |
| | | <p> |
| | | <br /><roundcube:button command="plugin.enigma-import" type="input" class="button mainaction" label="import" /> |
| | | </p> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/enigma.css" /> |
| | | </head> |
| | | <body class="iframe"> |
| | | |
| | | <div id="keyinfo-title" class="boxtitle"><roundcube:object name="keyname" part="name" /></div> |
| | | |
| | | <div id="key-details" class="boxcontent"> |
| | | <roundcube:object name="keydata" /> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/enigma.css" /> |
| | | <script type="text/javascript" src="/functions.js"></script> |
| | | <script type="text/javascript" src="/splitter.js"></script> |
| | | <style type="text/css"> |
| | | #enigmakeyslist { width: <roundcube:exp expression="!empty(cookie:enigmaviewsplitter) ? cookie:enigmaviewsplitter-5 : 210" />px; } |
| | | #enigmacontent-box { left: <roundcube:exp expression="!empty(cookie:enigmaviewsplitter) ? cookie:enigmaviewsplitter+5 : 220" />px; |
| | | <roundcube:exp expression="browser:ie ? ('width:expression((parseInt(this.parentNode.offsetWidth)-'.(!empty(cookie:enigmaeviewsplitter) ? cookie:enigmaviewsplitter+5 : 220).')+\\'px\\');') : ''" /> |
| | | } |
| | | </style> |
| | | </head> |
| | | <body class="iframe" onload="rcube_init_mail_ui()"> |
| | | |
| | | <div id="prefs-title" class="boxtitle"><roundcube:label name="enigma.enigmakeys" /></div> |
| | | <div id="prefs-details" class="boxcontent"> |
| | | |
| | | <div id="keystoolbar"> |
| | | <roundcube:button command="plugin.enigma-key-create" type="link" class="buttonPas create" classAct="button create" classSel="button createSel" title="enigma.createkeys" content=" " /> |
| | | <roundcube:button command="plugin.enigma-key-delete" type="link" class="buttonPas delete" classAct="button delete" classSel="button deleteSel" title="enigma.deletekeys" content=" " /> |
| | | <span class="separator"> </span> |
| | | <roundcube:button command="plugin.enigma-key-import" type="link" class="buttonPas import" classAct="button import" classSel="button importSel" title="enigma.importkeys" content=" " /> |
| | | <roundcube:button command="plugin.enigma-key-export" type="link" class="buttonPas export" classAct="button export" classSel="button exportSel" title="enigma.exportkeys" content=" " /> |
| | | <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button keymenu" title="enigma.keyactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " /> |
| | | </div> |
| | | |
| | | <div id="quicksearchbar" style="top: 35px; right: 10px;"> |
| | | <roundcube:button name="searchmenulink" id="searchmenulink" image="/images/icons/glass.png" /> |
| | | <roundcube:object name="searchform" id="quicksearchbox" /> |
| | | <roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" /> |
| | | </div> |
| | | |
| | | <div class="enigmascreen"> |
| | | |
| | | <div id="enigmakeyslist"> |
| | | <div class="boxtitle"><roundcube:label name="enigma.keyname" /></div> |
| | | <div class="boxlistcontent"> |
| | | <roundcube:object name="keyslist" id="keys-table" class="records-table" cellspacing="0" noheader="true" /> |
| | | </div> |
| | | <div class="boxfooter"> |
| | | <div id="keylistcountbar" class="pagenav"> |
| | | <roundcube:button command="firstpage" type="link" class="buttonPas firstpage" classAct="button firstpage" classSel="button firstpageSel" title="firstpage" content=" " /> |
| | | <roundcube:button command="previouspage" type="link" class="buttonPas prevpage" classAct="button prevpage" classSel="button prevpageSel" title="previouspage" content=" " /> |
| | | <roundcube:object name="countdisplay" style="padding:0 .5em; float:left" /> |
| | | <roundcube:button command="nextpage" type="link" class="buttonPas nextpage" classAct="button nextpage" classSel="button nextpageSel" title="nextpage" content=" " /> |
| | | <roundcube:button command="lastpage" type="link" class="buttonPas lastpage" classAct="button lastpage" classSel="button lastpageSel" title="lastpage" content=" " /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <script type="text/javascript"> |
| | | var enigmaviewsplit = new rcube_splitter({id:'enigmaviewsplitter', p1: 'enigmakeyslist', p2: 'enigmacontent-box', orientation: 'v', relative: true, start: 215}); |
| | | rcmail.add_onload('enigmaviewsplit.init()'); |
| | | </script> |
| | | |
| | | <div id="enigmacontent-box"> |
| | | <roundcube:object name="keyframe" id="keyframe" width="100%" height="100%" frameborder="0" src="/watermark.html" /> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | |
| | | <div id="messagemenu" class="popupmenu"> |
| | | <ul class="toolbarmenu"> |
| | | <li><roundcube:button class="disablelink" command="enigma.key-disable" label="enigma.keydisable" target="_blank" classAct="disablelink active" /></li> |
| | | <li><roundcube:button class="revokelink" command="enigma.key-revoke" label="enigma.keyrevoke" classAct="revokelink active" /></li> |
| | | <li class="separator_below"><roundcube:button class="sendlink" command="enigma.key-send" label="enigma.keysend" classAct="sendlink active" /></li> |
| | | <li><roundcube:button class="chpasslink" command="enigma.key-chpass" label="enigma.keychpass" classAct="chpasslink active" /></li> |
| | | </ul> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <?php |
| | | |
| | | require_once(dirname(__FILE__) . '/example_addressbook_backend.php'); |
| | | |
| | | /** |
| | | * Sample plugin to add a new address book |
| | | * with just a static list of contacts |
| | | */ |
| | | class example_addressbook extends rcube_plugin |
| | | { |
| | | private $abook_id = 'static'; |
| | | |
| | | public function init() |
| | | { |
| | | $this->add_hook('addressbooks_list', array($this, 'address_sources')); |
| | | $this->add_hook('addressbook_get', array($this, 'get_address_book')); |
| | | |
| | | // use this address book for autocompletion queries |
| | | // (maybe this should be configurable by the user?) |
| | | $config = rcmail::get_instance()->config; |
| | | $sources = (array) $config->get('autocomplete_addressbooks', array('sql')); |
| | | if (!in_array($this->abook_id, $sources)) { |
| | | $sources[] = $this->abook_id; |
| | | $config->set('autocomplete_addressbooks', $sources); |
| | | } |
| | | } |
| | | |
| | | public function address_sources($p) |
| | | { |
| | | $abook = new example_addressbook_backend; |
| | | $p['sources'][$this->abook_id] = array( |
| | | 'id' => $this->abook_id, |
| | | 'name' => 'Static List', |
| | | 'readonly' => $abook->readonly, |
| | | 'groups' => $abook->groups, |
| | | ); |
| | | return $p; |
| | | } |
| | | |
| | | public function get_address_book($p) |
| | | { |
| | | if ($p['id'] === $this->abook_id) { |
| | | $p['instance'] = new example_addressbook_backend; |
| | | } |
| | | |
| | | return $p; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Example backend class for a custom address book |
| | | * |
| | | * This one just holds a static list of address records |
| | | * |
| | | * @author Thomas Bruederli |
| | | */ |
| | | class example_addressbook_backend extends rcube_addressbook |
| | | { |
| | | public $primary_key = 'ID'; |
| | | public $readonly = true; |
| | | public $groups = true; |
| | | |
| | | private $filter; |
| | | private $result; |
| | | |
| | | public function __construct() |
| | | { |
| | | $this->ready = true; |
| | | } |
| | | |
| | | public function set_search_set($filter) |
| | | { |
| | | $this->filter = $filter; |
| | | } |
| | | |
| | | public function get_search_set() |
| | | { |
| | | return $this->filter; |
| | | } |
| | | |
| | | public function reset() |
| | | { |
| | | $this->result = null; |
| | | $this->filter = null; |
| | | } |
| | | |
| | | function list_groups($search = null) |
| | | { |
| | | return array( |
| | | array('ID' => 'testgroup1', 'name' => "Testgroup"), |
| | | array('ID' => 'testgroup2', 'name' => "Sample Group"), |
| | | ); |
| | | } |
| | | |
| | | public function list_records($cols=null, $subset=0) |
| | | { |
| | | $this->result = $this->count(); |
| | | $this->result->add(array('ID' => '111', 'name' => "Example Contact", 'firstname' => "Example", 'surname' => "Contact", 'email' => "example@roundcube.net")); |
| | | |
| | | return $this->result; |
| | | } |
| | | |
| | | public function search($fields, $value, $strict=false, $select=true) |
| | | { |
| | | // no search implemented, just list all records |
| | | return $this->list_records(); |
| | | } |
| | | |
| | | public function count() |
| | | { |
| | | return new rcube_result_set(1, ($this->list_page-1) * $this->page_size); |
| | | } |
| | | |
| | | public function get_result() |
| | | { |
| | | return $this->result; |
| | | } |
| | | |
| | | public function get_record($id, $assoc=false) |
| | | { |
| | | $this->list_records(); |
| | | $first = $this->result->first(); |
| | | $sql_arr = $first['ID'] == $id ? $first : null; |
| | | |
| | | return $assoc && $sql_arr ? $sql_arr : $this->result; |
| | | } |
| | | |
| | | |
| | | function create_group($name) |
| | | { |
| | | $result = false; |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | function delete_group($gid) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | function rename_group($gid, $newname) |
| | | { |
| | | return $newname; |
| | | } |
| | | |
| | | function add_to_group($group_id, $ids) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | function remove_from_group($group_id, $ids) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | /** |
| | | * Filesystem Attachments |
| | | * |
| | | * This is a core plugin which provides basic, filesystem based |
| | | * attachment temporary file handling. This includes storing |
| | | * attachments of messages currently being composed, writing attachments |
| | | * to disk when drafts with attachments are re-opened and writing |
| | | * attachments to disk for inline display in current html compositions. |
| | | * |
| | | * Developers may wish to extend this class when creating attachment |
| | | * handler plugins: |
| | | * require_once('plugins/filesystem_attachments/filesystem_attachments.php'); |
| | | * class myCustom_attachments extends filesystem_attachments |
| | | * |
| | | * @author Ziba Scott <ziba@umich.edu> |
| | | * @author Thomas Bruederli <roundcube@gmail.com> |
| | | * |
| | | */ |
| | | class filesystem_attachments extends rcube_plugin |
| | | { |
| | | public $task = 'mail'; |
| | | |
| | | function init() |
| | | { |
| | | // Save a newly uploaded attachment |
| | | $this->add_hook('attachment_upload', array($this, 'upload')); |
| | | |
| | | // Save an attachment from a non-upload source (draft or forward) |
| | | $this->add_hook('attachment_save', array($this, 'save')); |
| | | |
| | | // Remove an attachment from storage |
| | | $this->add_hook('attachment_delete', array($this, 'remove')); |
| | | |
| | | // When composing an html message, image attachments may be shown |
| | | $this->add_hook('attachment_display', array($this, 'display')); |
| | | |
| | | // Get the attachment from storage and place it on disk to be sent |
| | | $this->add_hook('attachment_get', array($this, 'get')); |
| | | |
| | | // Delete all temp files associated with this user |
| | | $this->add_hook('attachments_cleanup', array($this, 'cleanup')); |
| | | $this->add_hook('session_destroy', array($this, 'cleanup')); |
| | | } |
| | | |
| | | /** |
| | | * Save a newly uploaded attachment |
| | | */ |
| | | function upload($args) |
| | | { |
| | | $args['status'] = false; |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // use common temp dir for file uploads |
| | | $temp_dir = $rcmail->config->get('temp_dir'); |
| | | $tmpfname = tempnam($temp_dir, 'rcmAttmnt'); |
| | | |
| | | if (move_uploaded_file($args['path'], $tmpfname) && file_exists($tmpfname)) { |
| | | $args['id'] = $this->file_id(); |
| | | $args['path'] = $tmpfname; |
| | | $args['status'] = true; |
| | | |
| | | // Note the file for later cleanup |
| | | $_SESSION['plugins']['filesystem_attachments']['tmp_files'][] = $tmpfname; |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Save an attachment from a non-upload source (draft or forward) |
| | | */ |
| | | function save($args) |
| | | { |
| | | $args['status'] = false; |
| | | |
| | | if (!$args['path']) { |
| | | $rcmail = rcmail::get_instance(); |
| | | $temp_dir = $rcmail->config->get('temp_dir'); |
| | | $tmp_path = tempnam($temp_dir, 'rcmAttmnt'); |
| | | |
| | | if ($fp = fopen($tmp_path, 'w')) { |
| | | fwrite($fp, $args['data']); |
| | | fclose($fp); |
| | | $args['path'] = $tmp_path; |
| | | } else |
| | | return $args; |
| | | } |
| | | |
| | | $args['id'] = $this->file_id(); |
| | | $args['status'] = true; |
| | | |
| | | // Note the file for later cleanup |
| | | $_SESSION['plugins']['filesystem_attachments']['tmp_files'][] = $args['path']; |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Remove an attachment from storage |
| | | * This is triggered by the remove attachment button on the compose screen |
| | | */ |
| | | function remove($args) |
| | | { |
| | | $args['status'] = @unlink($args['path']); |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * When composing an html message, image attachments may be shown |
| | | * For this plugin, the file is already in place, just check for |
| | | * the existance of the proper metadata |
| | | */ |
| | | function display($args) |
| | | { |
| | | $args['status'] = file_exists($args['path']); |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * This attachment plugin doesn't require any steps to put the file |
| | | * on disk for use. This stub function is kept here to make this |
| | | * class handy as a parent class for other plugins which may need it. |
| | | */ |
| | | function get($args) |
| | | { |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Delete all temp files associated with this user |
| | | */ |
| | | function cleanup($args) |
| | | { |
| | | // $_SESSION['compose']['attachments'] is not a complete record of |
| | | // temporary files because loading a draft or starting a forward copies |
| | | // the file to disk, but does not make an entry in that array |
| | | if (is_array($_SESSION['plugins']['filesystem_attachments']['tmp_files'])){ |
| | | foreach ($_SESSION['plugins']['filesystem_attachments']['tmp_files'] as $filename){ |
| | | if(file_exists($filename)){ |
| | | unlink($filename); |
| | | } |
| | | } |
| | | unset($_SESSION['plugins']['filesystem_attachments']['tmp_files']); |
| | | } |
| | | return $args; |
| | | } |
| | | |
| | | function file_id() |
| | | { |
| | | $userid = rcmail::get_instance()->user->ID; |
| | | list($usec, $sec) = explode(' ', microtime()); |
| | | return preg_replace('/[^0-9]/', '', $userid . $sec . $usec); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | // Help content iframe source |
| | | // $rcmail_config['help_source'] = 'http://trac.roundcube.net/wiki'; |
| | | $rcmail_config['help_source'] = ''; |
New file |
| | |
| | | <div id="helpabout"> |
| | | <h3 align="center">Copyright © 2005-2010, The Roundcube Dev Team</h3> |
| | | |
| | | <p>This program is free software; you can redistribute it and/or modify |
| | | it under the terms of the GNU General Public License version 2 |
| | | as published by the Free Software Foundation. |
| | | </p> |
| | | <p> |
| | | This program is distributed in the hope that it will be useful, |
| | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | | GNU General Public License for more details. |
| | | </p> |
| | | <p> |
| | | You should have received a copy of the GNU General Public License along |
| | | with this program; if not, write to the Free Software Foundation, Inc., |
| | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| | | </p> |
| | | |
| | | <div align="center"> |
| | | <h3>Project management and administration</h3> |
| | | <b>Thomas Bruederli (thomasb)</b> - Project leader and head developer<br /> |
| | | <b>Till Klampäckel (till)</b> - Co-leader<br /> |
| | | <b>Brett Patterson</b> - Forum administrator<br /> |
| | | <b>Adam Grelck</b> - Trac administrator<br /> |
| | | <b>Jason Fesler</b> - Mailing list administrator<br /> |
| | | <b>Brennan Stehling</b> - Mentor, Coordinator |
| | | |
| | | <h3>Developers</h3> |
| | | <b>Eric Stadtherr (estadtherr)</b><br /> |
| | | <b>Robin Elfrink (robin, wobin)</b><br /> |
| | | <b>Rich Sandberg (richs)</b><br /> |
| | | <b>Tomasz Pajor (tomekp)</b><br /> |
| | | <b>Fourat Zouari (fourat.zouari)</b><br /> |
| | | <b>Aleksander Machniak (alec)</b> |
| | | |
| | | <p><br/>Website: <a href="http://roundcube.net">roundcube.net</a></p> |
| | | </div> |
| | | </div> |
New file |
| | |
| | | <div id="helplicense"> |
| | | <h3>GNU GENERAL PUBLIC LICENSE</h3> |
| | | <p> |
| | | Version 2, June 1991 |
| | | </p> |
| | | |
| | | <pre> |
| | | Copyright (C) 1989, 1991 Free Software Foundation, Inc. |
| | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| | | |
| | | Everyone is permitted to copy and distribute verbatim copies |
| | | of this license document, but changing it is not allowed. |
| | | </pre> |
| | | |
| | | <h3>Preamble</h3> |
| | | |
| | | <p> |
| | | The licenses for most software are designed to take away your |
| | | freedom to share and change it. By contrast, the GNU General Public |
| | | License is intended to guarantee your freedom to share and change free |
| | | software--to make sure the software is free for all its users. This |
| | | General Public License applies to most of the Free Software |
| | | Foundation's software and to any other program whose authors commit to |
| | | using it. (Some other Free Software Foundation software is covered by |
| | | the GNU Lesser General Public License instead.) You can apply it to |
| | | your programs, too. |
| | | </p> |
| | | |
| | | <p> |
| | | When we speak of free software, we are referring to freedom, not |
| | | price. Our General Public Licenses are designed to make sure that you |
| | | have the freedom to distribute copies of free software (and charge for |
| | | this service if you wish), that you receive source code or can get it |
| | | if you want it, that you can change the software or use pieces of it |
| | | in new free programs; and that you know you can do these things. |
| | | </p> |
| | | |
| | | <p> |
| | | To protect your rights, we need to make restrictions that forbid |
| | | anyone to deny you these rights or to ask you to surrender the rights. |
| | | These restrictions translate to certain responsibilities for you if you |
| | | distribute copies of the software, or if you modify it. |
| | | </p> |
| | | |
| | | <p> |
| | | For example, if you distribute copies of such a program, whether |
| | | gratis or for a fee, you must give the recipients all the rights that |
| | | you have. You must make sure that they, too, receive or can get the |
| | | source code. And you must show them these terms so they know their |
| | | rights. |
| | | </p> |
| | | |
| | | <p> |
| | | We protect your rights with two steps: (1) copyright the software, and |
| | | (2) offer you this license which gives you legal permission to copy, |
| | | distribute and/or modify the software. |
| | | </p> |
| | | |
| | | <p> |
| | | Also, for each author's protection and ours, we want to make certain |
| | | that everyone understands that there is no warranty for this free |
| | | software. If the software is modified by someone else and passed on, we |
| | | want its recipients to know that what they have is not the original, so |
| | | that any problems introduced by others will not reflect on the original |
| | | authors' reputations. |
| | | </p> |
| | | |
| | | <p> |
| | | Finally, any free program is threatened constantly by software |
| | | patents. We wish to avoid the danger that redistributors of a free |
| | | program will individually obtain patent licenses, in effect making the |
| | | program proprietary. To prevent this, we have made it clear that any |
| | | patent must be licensed for everyone's free use or not licensed at all. |
| | | </p> |
| | | |
| | | <p> |
| | | The precise terms and conditions for copying, distribution and |
| | | modification follow. |
| | | </p> |
| | | |
| | | |
| | | <h3>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</h3> |
| | | |
| | | |
| | | <p> |
| | | <strong>0.</strong> |
| | | This License applies to any program or other work which contains |
| | | a notice placed by the copyright holder saying it may be distributed |
| | | under the terms of this General Public License. The "Program", below, |
| | | refers to any such program or work, and a "work based on the Program" |
| | | means either the Program or any derivative work under copyright law: |
| | | that is to say, a work containing the Program or a portion of it, |
| | | either verbatim or with modifications and/or translated into another |
| | | language. (Hereinafter, translation is included without limitation in |
| | | the term "modification".) Each licensee is addressed as "you". |
| | | </p> |
| | | |
| | | <p> |
| | | Activities other than copying, distribution and modification are not |
| | | covered by this License; they are outside its scope. The act of |
| | | running the Program is not restricted, and the output from the Program |
| | | is covered only if its contents constitute a work based on the |
| | | Program (independent of having been made by running the Program). |
| | | Whether that is true depends on what the Program does. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>1.</strong> |
| | | You may copy and distribute verbatim copies of the Program's |
| | | source code as you receive it, in any medium, provided that you |
| | | conspicuously and appropriately publish on each copy an appropriate |
| | | copyright notice and disclaimer of warranty; keep intact all the |
| | | notices that refer to this License and to the absence of any warranty; |
| | | and give any other recipients of the Program a copy of this License |
| | | along with the Program. |
| | | </p> |
| | | |
| | | <p> |
| | | You may charge a fee for the physical act of transferring a copy, and |
| | | you may at your option offer warranty protection in exchange for a fee. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>2.</strong> |
| | | You may modify your copy or copies of the Program or any portion |
| | | of it, thus forming a work based on the Program, and copy and |
| | | distribute such modifications or work under the terms of Section 1 |
| | | above, provided that you also meet all of these conditions: |
| | | </p> |
| | | |
| | | <dl> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>a)</strong> |
| | | You must cause the modified files to carry prominent notices |
| | | stating that you changed the files and the date of any change. |
| | | </dd> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>b)</strong> |
| | | You must cause any work that you distribute or publish, that in |
| | | whole or in part contains or is derived from the Program or any |
| | | part thereof, to be licensed as a whole at no charge to all third |
| | | parties under the terms of this License. |
| | | </dd> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>c)</strong> |
| | | If the modified program normally reads commands interactively |
| | | when run, you must cause it, when started running for such |
| | | interactive use in the most ordinary way, to print or display an |
| | | announcement including an appropriate copyright notice and a |
| | | notice that there is no warranty (or else, saying that you provide |
| | | a warranty) and that users may redistribute the program under |
| | | these conditions, and telling the user how to view a copy of this |
| | | License. (Exception: if the Program itself is interactive but |
| | | does not normally print such an announcement, your work based on |
| | | the Program is not required to print an announcement.) |
| | | </dd> |
| | | </dl> |
| | | |
| | | <p> |
| | | These requirements apply to the modified work as a whole. If |
| | | identifiable sections of that work are not derived from the Program, |
| | | and can be reasonably considered independent and separate works in |
| | | themselves, then this License, and its terms, do not apply to those |
| | | sections when you distribute them as separate works. But when you |
| | | distribute the same sections as part of a whole which is a work based |
| | | on the Program, the distribution of the whole must be on the terms of |
| | | this License, whose permissions for other licensees extend to the |
| | | entire whole, and thus to each and every part regardless of who wrote it. |
| | | </p> |
| | | |
| | | <p> |
| | | Thus, it is not the intent of this section to claim rights or contest |
| | | your rights to work written entirely by you; rather, the intent is to |
| | | exercise the right to control the distribution of derivative or |
| | | collective works based on the Program. |
| | | </p> |
| | | |
| | | <p> |
| | | In addition, mere aggregation of another work not based on the Program |
| | | with the Program (or with a work based on the Program) on a volume of |
| | | a storage or distribution medium does not bring the other work under |
| | | the scope of this License. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>3.</strong> |
| | | You may copy and distribute the Program (or a work based on it, |
| | | under Section 2) in object code or executable form under the terms of |
| | | Sections 1 and 2 above provided that you also do one of the following: |
| | | </p> |
| | | |
| | | <dl> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>a)</strong> |
| | | Accompany it with the complete corresponding machine-readable |
| | | source code, which must be distributed under the terms of Sections |
| | | 1 and 2 above on a medium customarily used for software interchange; or, |
| | | </dd> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>b)</strong> |
| | | Accompany it with a written offer, valid for at least three |
| | | years, to give any third party, for a charge no more than your |
| | | cost of physically performing source distribution, a complete |
| | | machine-readable copy of the corresponding source code, to be |
| | | distributed under the terms of Sections 1 and 2 above on a medium |
| | | customarily used for software interchange; or, |
| | | </dd> |
| | | <dt></dt> |
| | | <dd> |
| | | <strong>c)</strong> |
| | | Accompany it with the information you received as to the offer |
| | | to distribute corresponding source code. (This alternative is |
| | | allowed only for noncommercial distribution and only if you |
| | | received the program in object code or executable form with such |
| | | an offer, in accord with Subsection b above.) |
| | | </dd> |
| | | </dl> |
| | | |
| | | <p> |
| | | The source code for a work means the preferred form of the work for |
| | | making modifications to it. For an executable work, complete source |
| | | code means all the source code for all modules it contains, plus any |
| | | associated interface definition files, plus the scripts used to |
| | | control compilation and installation of the executable. However, as a |
| | | special exception, the source code distributed need not include |
| | | anything that is normally distributed (in either source or binary |
| | | form) with the major components (compiler, kernel, and so on) of the |
| | | operating system on which the executable runs, unless that component |
| | | itself accompanies the executable. |
| | | </p> |
| | | |
| | | <p> |
| | | If distribution of executable or object code is made by offering |
| | | access to copy from a designated place, then offering equivalent |
| | | access to copy the source code from the same place counts as |
| | | distribution of the source code, even though third parties are not |
| | | compelled to copy the source along with the object code. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>4.</strong> |
| | | You may not copy, modify, sublicense, or distribute the Program |
| | | except as expressly provided under this License. Any attempt |
| | | otherwise to copy, modify, sublicense or distribute the Program is |
| | | void, and will automatically terminate your rights under this License. |
| | | However, parties who have received copies, or rights, from you under |
| | | this License will not have their licenses terminated so long as such |
| | | parties remain in full compliance. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>5.</strong> |
| | | You are not required to accept this License, since you have not |
| | | signed it. However, nothing else grants you permission to modify or |
| | | distribute the Program or its derivative works. These actions are |
| | | prohibited by law if you do not accept this License. Therefore, by |
| | | modifying or distributing the Program (or any work based on the |
| | | Program), you indicate your acceptance of this License to do so, and |
| | | all its terms and conditions for copying, distributing or modifying |
| | | the Program or works based on it. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>6.</strong> |
| | | Each time you redistribute the Program (or any work based on the |
| | | Program), the recipient automatically receives a license from the |
| | | original licensor to copy, distribute or modify the Program subject to |
| | | these terms and conditions. You may not impose any further |
| | | restrictions on the recipients' exercise of the rights granted herein. |
| | | You are not responsible for enforcing compliance by third parties to |
| | | this License. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>7.</strong> |
| | | If, as a consequence of a court judgment or allegation of patent |
| | | infringement or for any other reason (not limited to patent issues), |
| | | conditions are imposed on you (whether by court order, agreement or |
| | | otherwise) that contradict the conditions of this License, they do not |
| | | excuse you from the conditions of this License. If you cannot |
| | | distribute so as to satisfy simultaneously your obligations under this |
| | | License and any other pertinent obligations, then as a consequence you |
| | | may not distribute the Program at all. For example, if a patent |
| | | license would not permit royalty-free redistribution of the Program by |
| | | all those who receive copies directly or indirectly through you, then |
| | | the only way you could satisfy both it and this License would be to |
| | | refrain entirely from distribution of the Program. |
| | | </p> |
| | | |
| | | <p> |
| | | If any portion of this section is held invalid or unenforceable under |
| | | any particular circumstance, the balance of the section is intended to |
| | | apply and the section as a whole is intended to apply in other |
| | | circumstances. |
| | | </p> |
| | | |
| | | <p> |
| | | It is not the purpose of this section to induce you to infringe any |
| | | patents or other property right claims or to contest validity of any |
| | | such claims; this section has the sole purpose of protecting the |
| | | integrity of the free software distribution system, which is |
| | | implemented by public license practices. Many people have made |
| | | generous contributions to the wide range of software distributed |
| | | through that system in reliance on consistent application of that |
| | | system; it is up to the author/donor to decide if he or she is willing |
| | | to distribute software through any other system and a licensee cannot |
| | | impose that choice. |
| | | </p> |
| | | |
| | | <p> |
| | | This section is intended to make thoroughly clear what is believed to |
| | | be a consequence of the rest of this License. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>8.</strong> |
| | | If the distribution and/or use of the Program is restricted in |
| | | certain countries either by patents or by copyrighted interfaces, the |
| | | original copyright holder who places the Program under this License |
| | | may add an explicit geographical distribution limitation excluding |
| | | those countries, so that distribution is permitted only in or among |
| | | countries not thus excluded. In such case, this License incorporates |
| | | the limitation as if written in the body of this License. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>9.</strong> |
| | | The Free Software Foundation may publish revised and/or new versions |
| | | of the General Public License from time to time. Such new versions will |
| | | be similar in spirit to the present version, but may differ in detail to |
| | | address new problems or concerns. |
| | | </p> |
| | | |
| | | <p> |
| | | Each version is given a distinguishing version number. If the Program |
| | | specifies a version number of this License which applies to it and "any |
| | | later version", you have the option of following the terms and conditions |
| | | either of that version or of any later version published by the Free |
| | | Software Foundation. If the Program does not specify a version number of |
| | | this License, you may choose any version ever published by the Free Software |
| | | Foundation. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>10.</strong> |
| | | If you wish to incorporate parts of the Program into other free |
| | | programs whose distribution conditions are different, write to the author |
| | | to ask for permission. For software which is copyrighted by the Free |
| | | Software Foundation, write to the Free Software Foundation; we sometimes |
| | | make exceptions for this. Our decision will be guided by the two goals |
| | | of preserving the free status of all derivatives of our free software and |
| | | of promoting the sharing and reuse of software generally. |
| | | </p> |
| | | |
| | | <p><strong>NO WARRANTY</strong></p> |
| | | |
| | | <p> |
| | | <strong>11.</strong> |
| | | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
| | | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
| | | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
| | | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
| | | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| | | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
| | | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
| | | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
| | | REPAIR OR CORRECTION. |
| | | </p> |
| | | |
| | | <p> |
| | | <strong>12.</strong> |
| | | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
| | | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
| | | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
| | | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
| | | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
| | | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
| | | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
| | | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
| | | POSSIBILITY OF SUCH DAMAGES. |
| | | </p> |
| | | </div> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Help Plugin |
| | | * |
| | | * @author Aleksander 'A.L.E.C' Machniak |
| | | * @licence GNU GPL |
| | | * |
| | | * Configuration (see config.inc.php.dist) |
| | | * |
| | | **/ |
| | | |
| | | class help extends rcube_plugin |
| | | { |
| | | // all task excluding 'login' and 'logout' |
| | | public $task = '?(?!login|logout).*'; |
| | | |
| | | function init() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $this->add_texts('localization/', false); |
| | | |
| | | // register actions |
| | | $this->register_action('plugin.help', array($this, 'action')); |
| | | $this->register_action('plugin.helpabout', array($this, 'action')); |
| | | $this->register_action('plugin.helplicense', array($this, 'action')); |
| | | |
| | | // add taskbar button |
| | | $this->add_button(array( |
| | | 'name' => 'helptask', |
| | | 'class' => 'button-help', |
| | | 'label' => 'help.help', |
| | | 'href' => './?_task=dummy&_action=plugin.help', |
| | | ), 'taskbar'); |
| | | |
| | | $skin = $rcmail->config->get('skin'); |
| | | if (!file_exists($this->home."/skins/$skin/help.css")) |
| | | $skin = 'default'; |
| | | |
| | | // add style for taskbar button (must be here) and Help UI |
| | | $this->include_stylesheet("skins/$skin/help.css"); |
| | | } |
| | | |
| | | function action() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $this->load_config(); |
| | | |
| | | // register UI objects |
| | | $rcmail->output->add_handlers(array( |
| | | 'helpcontent' => array($this, 'content'), |
| | | )); |
| | | |
| | | if ($rcmail->action == 'plugin.helpabout') |
| | | $rcmail->output->set_pagetitle($this->gettext('about')); |
| | | else if ($rcmail->action == 'plugin.helplicense') |
| | | $rcmail->output->set_pagetitle($this->gettext('license')); |
| | | else |
| | | $rcmail->output->set_pagetitle($this->gettext('help')); |
| | | |
| | | $rcmail->output->send('help.help'); |
| | | } |
| | | |
| | | function content($attrib) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | if ($rcmail->action == 'plugin.helpabout') { |
| | | return @file_get_contents($this->home.'/content/about.html'); |
| | | } |
| | | else if ($rcmail->action == 'plugin.helplicense') { |
| | | return @file_get_contents($this->home.'/content/license.html'); |
| | | } |
| | | |
| | | // default content: iframe |
| | | |
| | | if ($src = $rcmail->config->get('help_source')) |
| | | $attrib['src'] = $src; |
| | | |
| | | if (empty($attrib['id'])) |
| | | $attrib['id'] = 'rcmailhelpcontent'; |
| | | |
| | | // allow the following attributes to be added to the <iframe> tag |
| | | $attrib_str = create_attrib_string($attrib, array( |
| | | 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder')); |
| | | |
| | | $out = sprintf('<iframe name="%s"%s></iframe>'."\n", $attrib['id'], $attrib_str); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | language/cs_CZ/labels.inc | |
| | | | | |
| | | | Language file of the Roundcube help plugin | |
| | | | Copyright (C) 2005-2009, Roundcube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Milan Kozak <hodza@hodza.net> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | @version $Id: labels.inc 2993 2009-09-26 18:32:07Z alec $ |
| | | |
| | | */ |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Nápověda'; |
| | | $labels['about'] = 'O aplikaci'; |
| | | $labels['license'] = 'Licence'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | // translation done by Ulli Heist - http://heist.hobby-site.org/ |
| | | $labels = array(); |
| | | $labels['help'] = 'Hilfe'; |
| | | $labels['about'] = 'Über'; |
| | | $labels['license'] = 'Lizenz'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Help'; |
| | | $labels['about'] = 'About'; |
| | | $labels['license'] = 'License'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Help'; |
| | | $labels['about'] = 'About'; |
| | | $labels['license'] = 'License'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Ayuda'; |
| | | $labels['about'] = 'Acerca de'; |
| | | $labels['license'] = 'Licencia'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Abi'; |
| | | $labels['about'] = 'Roundcube info'; |
| | | $labels['license'] = 'Litsents'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Segítség'; |
| | | $labels['about'] = 'Névjegy'; |
| | | $labels['license'] = 'Licenc'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // EN-Revision: 3891 |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'ヘルプ'; |
| | | $labels['about'] = '紹介'; |
| | | $labels['license'] = 'ライセンス'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Pomoc'; |
| | | $labels['about'] = 'O programie'; |
| | | $labels['license'] = 'Licencja'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | plugins/help/localization/ru_RU.inc | |
| | | | | |
| | | | Language file of the Roundcube help plugin | |
| | | | Copyright (C) 2005-2010, Roundcube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Sergey Dukachev <iam@dukess.ru> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | */ |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Помощь'; |
| | | $labels['about'] = 'О программе'; |
| | | $labels['license'] = 'Лицензия'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = 'Hjälp'; |
| | | $labels['about'] = 'Om'; |
| | | $labels['license'] = 'Licens'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['help'] = '說明'; |
| | | $labels['about'] = '關於'; |
| | | $labels['license'] = '許可證'; |
| | | |
| | | ?> |
New file |
| | |
| | | /***** Roundcube|Mail Help task styles *****/ |
| | | |
| | | #taskbar a.button-help |
| | | { |
| | | background-image: url('help.gif'); |
| | | } |
| | | |
| | | .help-box |
| | | { |
| | | overflow: auto; |
| | | background-color: #F2F2F2; |
| | | } |
| | | |
| | | #helplicense, #helpabout |
| | | { |
| | | width: 46em; |
| | | padding: 1em 2em; |
| | | } |
| | | |
| | | #helplicense a, #helpabout a |
| | | { |
| | | color: #900; |
| | | } |
| | | |
| | | #helpabout |
| | | { |
| | | margin: 0 auto; |
| | | } |
| | | |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/help.css" /> |
| | | <link rel="stylesheet" type="text/css" href="/settings.css" /> |
| | | <script type="text/javascript"> |
| | | function help_init_settings_tabs() |
| | | { |
| | | var tab = '#helptabdefault'; |
| | | if (window.rcmail && rcmail.env.action) { |
| | | var action = rcmail.env.action.replace(/^plugin\.help/, ''); |
| | | tab = '#helptab' + (action ? action : 'default'); |
| | | } |
| | | $(tab).addClass('tablink-selected'); |
| | | } |
| | | </script> |
| | | </head> |
| | | <body> |
| | | |
| | | <roundcube:include file="/includes/taskbar.html" /> |
| | | <roundcube:include file="/includes/header.html" /> |
| | | |
| | | <div id="tabsbar"> |
| | | <span id="helptabdefault" class="tablink"><roundcube:button name="helpdefault" href="?_task=dummy&_action=plugin.help" type="link" label="help.help" title="help.help" /></span> |
| | | <span id="helptababout" class="tablink"><roundcube:button name="helpabout" href="?_task=dummy&_action=plugin.helpabout" type="link" label="help.about" title="help.about" class="tablink" /></span> |
| | | <span id="helptablicense" class="tablink"><roundcube:button name="helplicense" href="?_task=dummy&_action=plugin.helplicense" type="link" label="help.license" title="help.license" class="tablink" /></span> |
| | | <roundcube:container name="helptabs" id="helptabsbar" /> |
| | | <script type="text/javascript"> if (window.rcmail) rcmail.add_onload(help_init_settings_tabs);</script> |
| | | </div> |
| | | |
| | | <div id="mainscreen" class="box help-box"> |
| | | <roundcube:object name="helpcontent" id="helpcontentframe" width="100%" height="100%" frameborder="0" src="/watermark.html" /> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * HTTP Basic Authentication |
| | | * |
| | | * Make use of an existing HTTP authentication and perform login with the existing user credentials |
| | | * |
| | | * @version 1.1 |
| | | * @author Thomas Bruederli |
| | | */ |
| | | class http_authentication extends rcube_plugin |
| | | { |
| | | public $task = 'login'; |
| | | |
| | | function init() |
| | | { |
| | | $this->add_hook('startup', array($this, 'startup')); |
| | | $this->add_hook('authenticate', array($this, 'authenticate')); |
| | | } |
| | | |
| | | function startup($args) |
| | | { |
| | | // change action to login |
| | | if (empty($args['action']) && empty($_SESSION['user_id']) |
| | | && !empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) |
| | | $args['action'] = 'login'; |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | function authenticate($args) |
| | | { |
| | | if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) { |
| | | $args['user'] = $_SERVER['PHP_AUTH_USER']; |
| | | $args['pass'] = $_SERVER['PHP_AUTH_PW']; |
| | | } |
| | | |
| | | $args['cookiecheck'] = false; |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | } |
| | | |
New file |
| | |
| | | - Fixed import from Avelsieve |
| | | - Use localized size units (#1486976) |
| | | |
| | | * version 2.9 [2010-08-02] |
| | | ----------------------------------------------------------- |
| | | - Fixed vacation parameters parsing (#1486883) |
| | | |
| | | * version 2.8 [2010-07-08] |
| | | ----------------------------------------------------------- |
| | | - Added managesieve_auth_type option (#1486731) |
| | | |
| | | * version 2.7 [2010-07-06] |
| | | ----------------------------------------------------------- |
| | | - Update Net_Sieve to version 1.3.0 (fixes LOGIN athentication) |
| | | - Added support for copying and copy sending of messages (COPY extension) |
| | | |
| | | * version 2.6 [2010-06-03] |
| | | ----------------------------------------------------------- |
| | | - Support %n and %d variables in managesieve_host option |
| | | |
| | | * version 2.5 [2010-05-04] |
| | | ----------------------------------------------------------- |
| | | - Fix filters set label after activation |
| | | - Fix filters set activation, add possibility to deactivate sets (#1486699) |
| | | - Fix download button state when sets list is empty |
| | | - Fix errors when sets list is empty |
| | | |
| | | * version 2.4 [2010-04-01] |
| | | ----------------------------------------------------------- |
| | | - Fixed bug in DIGEST-MD5 authentication (http://pear.php.net/bugs/bug.php?id=17285) |
| | | - Fixed disabling rules with many tests |
| | | - Small css unification with core |
| | | - Scripts import/export |
| | | |
| | | * version 2.3 [2010-03-18] |
| | | ----------------------------------------------------------- |
| | | - Added import from Horde-INGO |
| | | - Support for more than one match using if+stop instead of if+elsif structures (#1486078) |
| | | - Support for selectively disabling rules within a single sieve script (#1485882) |
| | | - Added vertical splitter |
| | | |
| | | * version 2.2 [2010-02-06] |
| | | ----------------------------------------------------------- |
| | | - Fix handling of "<>" characters in filter names (#1486477) |
| | | |
| | | * version 2.1 [2010-01-12] |
| | | ----------------------------------------------------------- |
| | | - Fix "require" structure generation when many modules are used |
| | | - Fix problem with '<' and '>' characters in header tests |
| | | |
| | | * version 2.0 [2009-11-02] |
| | | ----------------------------------------------------------- |
| | | - Added 'managesieve_debug' option |
| | | - Added multi-script support |
| | | - Small css improvements + sprite image buttons |
| | | - PEAR::NetSieve 1.2.0b1 |
| | | |
| | | * version 1.7 [2009-09-20] |
| | | ----------------------------------------------------------- |
| | | - Support multiple managesieve hosts using %h variable |
| | | in managesieve_host option |
| | | - Fix first rule deleting (#1486140) |
| | | |
| | | * version 1.6 [2009-09-08] |
| | | ----------------------------------------------------------- |
| | | - Fix warning when importing squirrelmail rules |
| | | - Fix handling of "true" as "anyof (true)" test |
| | | |
| | | * version 1.5 [2009-09-04] |
| | | ----------------------------------------------------------- |
| | | - Added es_ES, ua_UA localizations |
| | | - Added 'managesieve_mbox_encoding' option |
| | | |
| | | * version 1.4 [2009-07-29] |
| | | ----------------------------------------------------------- |
| | | - Updated PEAR::Net_Sieve to 1.1.7 |
| | | |
| | | * version 1.3 [2009-07-24] |
| | | ----------------------------------------------------------- |
| | | - support more languages |
| | | - support config.inc.php file |
| | | |
| | | * version 1.2 [2009-06-28] |
| | | ----------------------------------------------------------- |
| | | - Support IMAP namespaces in fileinto (#1485943) |
| | | - Added it_IT localization |
| | | |
| | | * version 1.1 [2009-05-27] |
| | | ----------------------------------------------------------- |
| | | - Added new icons |
| | | - Added support for headers lists (coma-separated) in rules |
| | | - Added de_CH localization |
| | | |
| | | * version 1.0 [2009-05-21] |
| | | ----------------------------------------------------------- |
| | | - Rewritten using plugin API |
| | | - Added hu_HU localization (Tamas Tevesz) |
| | | |
| | | * version beta7 (svn-r2300) [2009-03-01] |
| | | ----------------------------------------------------------- |
| | | - Added SquirrelMail script auto-import (Jonathan Ernst) |
| | | - Added 'vacation' support (Jonathan Ernst & alec) |
| | | - Added 'stop' support (Jonathan Ernst) |
| | | - Added option for extensions disabling (Jonathan Ernst & alec) |
| | | - Added fi_FI, nl_NL, bg_BG localization |
| | | - Small style fixes |
| | | |
| | | * version 0.2-stable1 (svn-r2205) [2009-01-03] |
| | | ----------------------------------------------------------- |
| | | - Fix moving down filter row |
| | | - Fixes for compressed js files in stable release package |
| | | - Created patch for svn version r2205 |
| | | |
| | | * version 0.2-stable [2008-12-31] |
| | | ----------------------------------------------------------- |
| | | - Added ru_RU, fr_FR, zh_CN translation |
| | | - Fixes for Roundcube 0.2-stable |
| | | |
| | | * version rc0.2beta [2008-09-21] |
| | | ----------------------------------------------------------- |
| | | - Small css fixes for IE |
| | | - Fixes for Roundcube 0.2-beta |
| | | |
| | | * version beta6 [2008-08-08] |
| | | ----------------------------------------------------------- |
| | | - Added de_DE translation |
| | | - Fix for Roundcube r1634 |
| | | |
| | | * version beta5 [2008-06-10] |
| | | ----------------------------------------------------------- |
| | | - Fixed 'exists' operators |
| | | - Fixed 'not*' operators for custom headers |
| | | - Fixed filters deleting |
| | | |
| | | * version beta4 [2008-06-09] |
| | | ----------------------------------------------------------- |
| | | - Fix for Roundcube r1490 |
| | | |
| | | * version beta3 [2008-05-22] |
| | | ----------------------------------------------------------- |
| | | - Fixed textarea error class setting |
| | | - Added pagetitle setting |
| | | - Added option 'managesieve_replace_delimiter' |
| | | - Fixed errors on IE (still need some css fixes) |
| | | |
| | | * version beta2 [2008-05-20] |
| | | ----------------------------------------------------------- |
| | | - Use 'if' only for first filter and 'elsif' for the rest |
| | | |
| | | * version beta1 [2008-05-15] |
| | | ----------------------------------------------------------- |
| | | - Initial version for Roundcube r1388. |
New file |
| | |
| | | <?php |
| | | |
| | | // managesieve server port |
| | | $rcmail_config['managesieve_port'] = 2000; |
| | | |
| | | // managesieve server address, default is localhost. |
| | | // Replacement variables supported in host name: |
| | | // %h - user's IMAP hostname |
| | | // %n - http hostname ($_SERVER['SERVER_NAME']) |
| | | // %d - domain (http hostname without the first part) |
| | | // For example %n = mail.domain.tld, %d = domain.tld |
| | | $rcmail_config['managesieve_host'] = 'localhost'; |
| | | |
| | | // authentication method. Can be CRAM-MD5, DIGEST-MD5, PLAIN, LOGIN, EXTERNAL |
| | | // or none. Optional, defaults to best method supported by server. |
| | | $rcmail_config['managesieve_auth_type'] = null; |
| | | |
| | | // use or not TLS for managesieve server connection |
| | | // it's because I've problems with TLS and dovecot's managesieve plugin |
| | | // and it's not needed on localhost |
| | | $rcmail_config['managesieve_usetls'] = false; |
| | | |
| | | // default contents of filters script (eg. default spam filter) |
| | | $rcmail_config['managesieve_default'] = '/etc/dovecot/sieve/global'; |
| | | |
| | | // Sieve RFC says that we should use UTF-8 endcoding for mailbox names, |
| | | // but some implementations does not covert UTF-8 to modified UTF-7. |
| | | // Defaults to UTF7-IMAP |
| | | $rcmail_config['managesieve_mbox_encoding'] = 'UTF-8'; |
| | | |
| | | // I need this because my dovecot (with listescape plugin) uses |
| | | // ':' delimiter, but creates folders with dot delimiter |
| | | $rcmail_config['managesieve_replace_delimiter'] = ''; |
| | | |
| | | // disabled sieve extensions (body, copy, date, editheader, encoded-character, |
| | | // envelope, environment, ereject, fileinto, ihave, imap4flags, index, |
| | | // mailbox, mboxmetadata, regex, reject, relational, servermetadata, |
| | | // spamtest, spamtestplus, subaddress, vacation, variables, virustest, etc. |
| | | // Note: not all extensions are implemented |
| | | $rcmail_config['managesieve_disabled_extensions'] = array(); |
| | | |
| | | // Enables debugging of conversation with sieve server. Logs it into <log_dir>/sieve |
| | | $rcmail_config['managesieve_debug'] = false; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | /** |
| | | * This file contains the Net_Sieve class. |
| | | * |
| | | * PHP version 4 |
| | | * |
| | | * +-----------------------------------------------------------------------+ |
| | | * | All rights reserved. | |
| | | * | | |
| | | * | Redistribution and use in source and binary forms, with or without | |
| | | * | modification, are permitted provided that the following conditions | |
| | | * | are met: | |
| | | * | | |
| | | * | o Redistributions of source code must retain the above copyright | |
| | | * | notice, this list of conditions and the following disclaimer. | |
| | | * | o Redistributions in binary form must reproduce the above copyright | |
| | | * | notice, this list of conditions and the following disclaimer in the | |
| | | * | documentation and/or other materials provided with the distribution.| |
| | | * | | |
| | | * | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| | | * | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| | | * | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| | | * | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| | | * | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| | | * | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| | | * | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| | | * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| | | * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| | | * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| | | * | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| | | * +-----------------------------------------------------------------------+ |
| | | * |
| | | * @category Networking |
| | | * @package Net_Sieve |
| | | * @author Richard Heyes <richard@phpguru.org> |
| | | * @author Damian Fernandez Sosa <damlists@cnba.uba.ar> |
| | | * @author Anish Mistry <amistry@am-productions.biz> |
| | | * @author Jan Schneider <jan@horde.org> |
| | | * @copyright 2002-2003 Richard Heyes |
| | | * @copyright 2006-2008 Anish Mistry |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD |
| | | * @version SVN: $Id: Sieve.php 300898 2010-07-01 09:49:02Z yunosh $ |
| | | * @link http://pear.php.net/package/Net_Sieve |
| | | */ |
| | | |
| | | require_once 'PEAR.php'; |
| | | require_once 'Net/Socket.php'; |
| | | |
| | | /** |
| | | * TODO |
| | | * |
| | | * o supportsAuthMech() |
| | | */ |
| | | |
| | | /** |
| | | * Disconnected state |
| | | * @const NET_SIEVE_STATE_DISCONNECTED |
| | | */ |
| | | define('NET_SIEVE_STATE_DISCONNECTED', 1, true); |
| | | |
| | | /** |
| | | * Authorisation state |
| | | * @const NET_SIEVE_STATE_AUTHORISATION |
| | | */ |
| | | define('NET_SIEVE_STATE_AUTHORISATION', 2, true); |
| | | |
| | | /** |
| | | * Transaction state |
| | | * @const NET_SIEVE_STATE_TRANSACTION |
| | | */ |
| | | define('NET_SIEVE_STATE_TRANSACTION', 3, true); |
| | | |
| | | |
| | | /** |
| | | * A class for talking to the timsieved server which comes with Cyrus IMAP. |
| | | * |
| | | * @category Networking |
| | | * @package Net_Sieve |
| | | * @author Richard Heyes <richard@phpguru.org> |
| | | * @author Damian Fernandez Sosa <damlists@cnba.uba.ar> |
| | | * @author Anish Mistry <amistry@am-productions.biz> |
| | | * @author Jan Schneider <jan@horde.org> |
| | | * @copyright 2002-2003 Richard Heyes |
| | | * @copyright 2006-2008 Anish Mistry |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD |
| | | * @version Release: 1.3.0 |
| | | * @link http://pear.php.net/package/Net_Sieve |
| | | * @link http://www.ietf.org/rfc/rfc3028.txt RFC 3028 (Sieve: A Mail |
| | | * Filtering Language) |
| | | * @link http://tools.ietf.org/html/draft-ietf-sieve-managesieve A |
| | | * Protocol for Remotely Managing Sieve Scripts |
| | | */ |
| | | class Net_Sieve |
| | | { |
| | | /** |
| | | * The authentication methods this class supports. |
| | | * |
| | | * Can be overwritten if having problems with certain methods. |
| | | * |
| | | * @var array |
| | | */ |
| | | var $supportedAuthMethods = array('DIGEST-MD5', 'CRAM-MD5', 'EXTERNAL', |
| | | 'PLAIN' , 'LOGIN'); |
| | | |
| | | /** |
| | | * SASL authentication methods that require Auth_SASL. |
| | | * |
| | | * @var array |
| | | */ |
| | | var $supportedSASLAuthMethods = array('DIGEST-MD5', 'CRAM-MD5'); |
| | | |
| | | /** |
| | | * The socket handle. |
| | | * |
| | | * @var resource |
| | | */ |
| | | var $_sock; |
| | | |
| | | /** |
| | | * Parameters and connection information. |
| | | * |
| | | * @var array |
| | | */ |
| | | var $_data; |
| | | |
| | | /** |
| | | * Current state of the connection. |
| | | * |
| | | * One of the NET_SIEVE_STATE_* constants. |
| | | * |
| | | * @var integer |
| | | */ |
| | | var $_state; |
| | | |
| | | /** |
| | | * Constructor error. |
| | | * |
| | | * @var PEAR_Error |
| | | */ |
| | | var $_error; |
| | | |
| | | /** |
| | | * Whether to enable debugging. |
| | | * |
| | | * @var boolean |
| | | */ |
| | | var $_debug = false; |
| | | |
| | | /** |
| | | * Debug output handler. |
| | | * |
| | | * This has to be a valid callback. |
| | | * |
| | | * @var string|array |
| | | */ |
| | | var $_debug_handler = null; |
| | | |
| | | /** |
| | | * Whether to pick up an already established connection. |
| | | * |
| | | * @var boolean |
| | | */ |
| | | var $_bypassAuth = false; |
| | | |
| | | /** |
| | | * Whether to use TLS if available. |
| | | * |
| | | * @var boolean |
| | | */ |
| | | var $_useTLS = true; |
| | | |
| | | /** |
| | | * Additional options for stream_context_create(). |
| | | * |
| | | * @var array |
| | | */ |
| | | var $_options = null; |
| | | |
| | | /** |
| | | * Maximum number of referral loops |
| | | * |
| | | * @var array |
| | | */ |
| | | var $_maxReferralCount = 15; |
| | | |
| | | /** |
| | | * Constructor. |
| | | * |
| | | * Sets up the object, connects to the server and logs in. Stores any |
| | | * generated error in $this->_error, which can be retrieved using the |
| | | * getError() method. |
| | | * |
| | | * @param string $user Login username. |
| | | * @param string $pass Login password. |
| | | * @param string $host Hostname of server. |
| | | * @param string $port Port of server. |
| | | * @param string $logintype Type of login to perform (see |
| | | * $supportedAuthMethods). |
| | | * @param string $euser Effective user. If authenticating as an |
| | | * administrator, login as this user. |
| | | * @param boolean $debug Whether to enable debugging (@see setDebug()). |
| | | * @param string $bypassAuth Skip the authentication phase. Useful if the |
| | | * socket is already open. |
| | | * @param boolean $useTLS Use TLS if available. |
| | | * @param array $options Additional options for |
| | | * stream_context_create(). |
| | | * @param mixed $handler A callback handler for the debug output. |
| | | */ |
| | | function Net_Sieve($user = null, $pass = null, $host = 'localhost', |
| | | $port = 2000, $logintype = '', $euser = '', |
| | | $debug = false, $bypassAuth = false, $useTLS = true, |
| | | $options = null, $handler = null) |
| | | { |
| | | $this->_state = NET_SIEVE_STATE_DISCONNECTED; |
| | | $this->_data['user'] = $user; |
| | | $this->_data['pass'] = $pass; |
| | | $this->_data['host'] = $host; |
| | | $this->_data['port'] = $port; |
| | | $this->_data['logintype'] = $logintype; |
| | | $this->_data['euser'] = $euser; |
| | | $this->_sock = new Net_Socket(); |
| | | $this->_bypassAuth = $bypassAuth; |
| | | $this->_useTLS = $useTLS; |
| | | $this->_options = $options; |
| | | $this->setDebug($debug, $handler); |
| | | |
| | | /* Try to include the Auth_SASL package. If the package is not |
| | | * available, we disable the authentication methods that depend upon |
| | | * it. */ |
| | | if ((@include_once 'Auth/SASL.php') === false) { |
| | | $this->_debug('Auth_SASL not present'); |
| | | foreach ($this->supportedSASLAuthMethods as $SASLMethod) { |
| | | $pos = array_search($SASLMethod, $this->supportedAuthMethods); |
| | | $this->_debug('Disabling method ' . $SASLMethod); |
| | | unset($this->supportedAuthMethods[$pos]); |
| | | } |
| | | } |
| | | |
| | | if (strlen($user) && strlen($pass)) { |
| | | $this->_error = $this->_handleConnectAndLogin(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns any error that may have been generated in the constructor. |
| | | * |
| | | * @return boolean|PEAR_Error False if no error, PEAR_Error otherwise. |
| | | */ |
| | | function getError() |
| | | { |
| | | return PEAR::isError($this->_error) ? $this->_error : false; |
| | | } |
| | | |
| | | /** |
| | | * Sets the debug state and handler function. |
| | | * |
| | | * @param boolean $debug Whether to enable debugging. |
| | | * @param string $handler A custom debug handler. Must be a valid callback. |
| | | * |
| | | * @return void |
| | | */ |
| | | function setDebug($debug = true, $handler = null) |
| | | { |
| | | $this->_debug = $debug; |
| | | $this->_debug_handler = $handler; |
| | | } |
| | | |
| | | /** |
| | | * Connects to the server and logs in. |
| | | * |
| | | * @return boolean True on success, PEAR_Error on failure. |
| | | */ |
| | | function _handleConnectAndLogin() |
| | | { |
| | | if (PEAR::isError($res = $this->connect($this->_data['host'], $this->_data['port'], $this->_options, $this->_useTLS))) { |
| | | return $res; |
| | | } |
| | | if ($this->_bypassAuth === false) { |
| | | if (PEAR::isError($res = $this->login($this->_data['user'], $this->_data['pass'], $this->_data['logintype'], $this->_data['euser'], $this->_bypassAuth))) { |
| | | return $res; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Handles connecting to the server and checks the response validity. |
| | | * |
| | | * @param string $host Hostname of server. |
| | | * @param string $port Port of server. |
| | | * @param array $options List of options to pass to |
| | | * stream_context_create(). |
| | | * @param boolean $useTLS Use TLS if available. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function connect($host, $port, $options = null, $useTLS = true) |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) { |
| | | return PEAR::raiseError('Not currently in DISCONNECTED state', 1); |
| | | } |
| | | |
| | | if (PEAR::isError($res = $this->_sock->connect($host, $port, false, 5, $options))) { |
| | | return $res; |
| | | } |
| | | |
| | | if ($this->_bypassAuth) { |
| | | $this->_state = NET_SIEVE_STATE_TRANSACTION; |
| | | } else { |
| | | $this->_state = NET_SIEVE_STATE_AUTHORISATION; |
| | | if (PEAR::isError($res = $this->_doCmd())) { |
| | | return $res; |
| | | } |
| | | } |
| | | |
| | | // Explicitly ask for the capabilities in case the connection is |
| | | // picked up from an existing connection. |
| | | if (PEAR::isError($res = $this->_cmdCapability())) { |
| | | return PEAR::raiseError( |
| | | 'Failed to connect, server said: ' . $res->getMessage(), 2 |
| | | ); |
| | | } |
| | | |
| | | // Check if we can enable TLS via STARTTLS. |
| | | if ($useTLS && !empty($this->_capability['starttls']) |
| | | && function_exists('stream_socket_enable_crypto') |
| | | ) { |
| | | if (PEAR::isError($res = $this->_startTLS())) { |
| | | return $res; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Disconnect from the Sieve server. |
| | | * |
| | | * @param boolean $sendLogoutCMD Whether to send LOGOUT command before |
| | | * disconnecting. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function disconnect($sendLogoutCMD = true) |
| | | { |
| | | return $this->_cmdLogout($sendLogoutCMD); |
| | | } |
| | | |
| | | /** |
| | | * Logs into server. |
| | | * |
| | | * @param string $user Login username. |
| | | * @param string $pass Login password. |
| | | * @param string $logintype Type of login method to use. |
| | | * @param string $euser Effective UID (perform on behalf of $euser). |
| | | * @param boolean $bypassAuth Do not perform authentication. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function login($user, $pass, $logintype = null, $euser = '', $bypassAuth = false) |
| | | { |
| | | if (NET_SIEVE_STATE_AUTHORISATION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | |
| | | if (!$bypassAuth ) { |
| | | if (PEAR::isError($res = $this->_cmdAuthenticate($user, $pass, $logintype, $euser))) { |
| | | return $res; |
| | | } |
| | | } |
| | | $this->_state = NET_SIEVE_STATE_TRANSACTION; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns an indexed array of scripts currently on the server. |
| | | * |
| | | * @return array Indexed array of scriptnames. |
| | | */ |
| | | function listScripts() |
| | | { |
| | | if (is_array($scripts = $this->_cmdListScripts())) { |
| | | $this->_active = $scripts[1]; |
| | | return $scripts[0]; |
| | | } else { |
| | | return $scripts; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the active script. |
| | | * |
| | | * @return string The active scriptname. |
| | | */ |
| | | function getActive() |
| | | { |
| | | if (!empty($this->_active)) { |
| | | return $this->_active; |
| | | } |
| | | if (is_array($scripts = $this->_cmdListScripts())) { |
| | | $this->_active = $scripts[1]; |
| | | return $scripts[1]; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Sets the active script. |
| | | * |
| | | * @param string $scriptname The name of the script to be set as active. |
| | | * |
| | | * @return boolean True on success, PEAR_Error on failure. |
| | | */ |
| | | function setActive($scriptname) |
| | | { |
| | | return $this->_cmdSetActive($scriptname); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves a script. |
| | | * |
| | | * @param string $scriptname The name of the script to be retrieved. |
| | | * |
| | | * @return string The script on success, PEAR_Error on failure. |
| | | */ |
| | | function getScript($scriptname) |
| | | { |
| | | return $this->_cmdGetScript($scriptname); |
| | | } |
| | | |
| | | /** |
| | | * Adds a script to the server. |
| | | * |
| | | * @param string $scriptname Name of the script. |
| | | * @param string $script The script content. |
| | | * @param boolean $makeactive Whether to make this the active script. |
| | | * |
| | | * @return boolean True on success, PEAR_Error on failure. |
| | | */ |
| | | function installScript($scriptname, $script, $makeactive = false) |
| | | { |
| | | if (PEAR::isError($res = $this->_cmdPutScript($scriptname, $script))) { |
| | | return $res; |
| | | } |
| | | if ($makeactive) { |
| | | return $this->_cmdSetActive($scriptname); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Removes a script from the server. |
| | | * |
| | | * @param string $scriptname Name of the script. |
| | | * |
| | | * @return boolean True on success, PEAR_Error on failure. |
| | | */ |
| | | function removeScript($scriptname) |
| | | { |
| | | return $this->_cmdDeleteScript($scriptname); |
| | | } |
| | | |
| | | /** |
| | | * Checks if the server has space to store the script by the server. |
| | | * |
| | | * @param string $scriptname The name of the script to mark as active. |
| | | * @param integer $size The size of the script. |
| | | * |
| | | * @return boolean|PEAR_Error True if there is space, PEAR_Error otherwise. |
| | | * |
| | | * @todo Rename to hasSpace() |
| | | */ |
| | | function haveSpace($scriptname, $size) |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in TRANSACTION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE "%s" %d', $scriptname, $size)))) { |
| | | return $res; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of extensions the server supports. |
| | | * |
| | | * @return array List of extensions or PEAR_Error on failure. |
| | | */ |
| | | function getExtensions() |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 7); |
| | | } |
| | | return $this->_capability['extensions']; |
| | | } |
| | | |
| | | /** |
| | | * Returns whether the server supports an extension. |
| | | * |
| | | * @param string $extension The extension to check. |
| | | * |
| | | * @return boolean Whether the extension is supported or PEAR_Error on |
| | | * failure. |
| | | */ |
| | | function hasExtension($extension) |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 7); |
| | | } |
| | | |
| | | $extension = trim($this->_toUpper($extension)); |
| | | if (is_array($this->_capability['extensions'])) { |
| | | foreach ($this->_capability['extensions'] as $ext) { |
| | | if ($ext == $extension) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of authentication methods the server supports. |
| | | * |
| | | * @return array List of authentication methods or PEAR_Error on failure. |
| | | */ |
| | | function getAuthMechs() |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 7); |
| | | } |
| | | return $this->_capability['sasl']; |
| | | } |
| | | |
| | | /** |
| | | * Returns whether the server supports an authentication method. |
| | | * |
| | | * @param string $method The method to check. |
| | | * |
| | | * @return boolean Whether the method is supported or PEAR_Error on |
| | | * failure. |
| | | */ |
| | | function hasAuthMech($method) |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 7); |
| | | } |
| | | |
| | | $method = trim($this->_toUpper($method)); |
| | | if (is_array($this->_capability['sasl'])) { |
| | | foreach ($this->_capability['sasl'] as $sasl) { |
| | | if ($sasl == $method) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Handles the authentication using any known method. |
| | | * |
| | | * @param string $uid The userid to authenticate as. |
| | | * @param string $pwd The password to authenticate with. |
| | | * @param string $userMethod The method to use. If empty, the class chooses |
| | | * the best (strongest) available method. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _cmdAuthenticate($uid, $pwd, $userMethod = null, $euser = '') |
| | | { |
| | | if (PEAR::isError($method = $this->_getBestAuthMethod($userMethod))) { |
| | | return $method; |
| | | } |
| | | switch ($method) { |
| | | case 'DIGEST-MD5': |
| | | return $this->_authDigestMD5($uid, $pwd, $euser); |
| | | case 'CRAM-MD5': |
| | | $result = $this->_authCRAMMD5($uid, $pwd, $euser); |
| | | break; |
| | | case 'LOGIN': |
| | | $result = $this->_authLOGIN($uid, $pwd, $euser); |
| | | break; |
| | | case 'PLAIN': |
| | | $result = $this->_authPLAIN($uid, $pwd, $euser); |
| | | break; |
| | | case 'EXTERNAL': |
| | | $result = $this->_authEXTERNAL($uid, $pwd, $euser); |
| | | break; |
| | | default : |
| | | $result = PEAR::raiseError( |
| | | $method . ' is not a supported authentication method' |
| | | ); |
| | | break; |
| | | } |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd())) { |
| | | return $res; |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Authenticates the user using the PLAIN method. |
| | | * |
| | | * @param string $user The userid to authenticate as. |
| | | * @param string $pass The password to authenticate with. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _authPLAIN($user, $pass, $euser) |
| | | { |
| | | return $this->_sendCmd( |
| | | sprintf( |
| | | 'AUTHENTICATE "PLAIN" "%s"', |
| | | base64_encode($euser . chr(0) . $user . chr(0) . $pass) |
| | | ) |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Authenticates the user using the LOGIN method. |
| | | * |
| | | * @param string $user The userid to authenticate as. |
| | | * @param string $pass The password to authenticate with. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _authLOGIN($user, $pass, $euser) |
| | | { |
| | | if (PEAR::isError($result = $this->_sendCmd('AUTHENTICATE "LOGIN"'))) { |
| | | return $result; |
| | | } |
| | | if (PEAR::isError($result = $this->_doCmd('"' . base64_encode($user) . '"', true))) { |
| | | return $result; |
| | | } |
| | | return $this->_doCmd('"' . base64_encode($pass) . '"', true); |
| | | } |
| | | |
| | | /** |
| | | * Authenticates the user using the CRAM-MD5 method. |
| | | * |
| | | * @param string $user The userid to authenticate as. |
| | | * @param string $pass The password to authenticate with. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _authCRAMMD5($user, $pass, $euser) |
| | | { |
| | | if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "CRAM-MD5"', true))) { |
| | | return $challenge; |
| | | } |
| | | |
| | | $challenge = base64_decode(trim($challenge)); |
| | | $cram = Auth_SASL::factory('crammd5'); |
| | | if (PEAR::isError($response = $cram->getResponse($user, $pass, $challenge))) { |
| | | return $response; |
| | | } |
| | | |
| | | return $this->_sendStringResponse(base64_encode($response)); |
| | | } |
| | | |
| | | /** |
| | | * Authenticates the user using the DIGEST-MD5 method. |
| | | * |
| | | * @param string $user The userid to authenticate as. |
| | | * @param string $pass The password to authenticate with. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _authDigestMD5($user, $pass, $euser) |
| | | { |
| | | if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"', true))) { |
| | | return $challenge; |
| | | } |
| | | |
| | | $challenge = base64_decode(trim($challenge)); |
| | | $digest = Auth_SASL::factory('digestmd5'); |
| | | // @todo Really 'localhost'? |
| | | if (PEAR::isError($response = $digest->getResponse($user, $pass, $challenge, 'localhost', 'sieve', $euser))) { |
| | | return $response; |
| | | } |
| | | |
| | | if (PEAR::isError($result = $this->_sendStringResponse(base64_encode($response)))) { |
| | | return $result; |
| | | } |
| | | if (PEAR::isError($result = $this->_doCmd('', true))) { |
| | | return $result; |
| | | } |
| | | if ($this->_toUpper(substr($result, 0, 2)) == 'OK') { |
| | | return; |
| | | } |
| | | |
| | | /* We don't use the protocol's third step because SIEVE doesn't allow |
| | | * subsequent authentication, so we just silently ignore it. */ |
| | | if (PEAR::isError($result = $this->_sendStringResponse(''))) { |
| | | return $result; |
| | | } |
| | | |
| | | return $this->_doCmd(); |
| | | } |
| | | |
| | | /** |
| | | * Authenticates the user using the EXTERNAL method. |
| | | * |
| | | * @param string $user The userid to authenticate as. |
| | | * @param string $pass The password to authenticate with. |
| | | * @param string $euser The effective uid to authenticate as. |
| | | * |
| | | * @return void |
| | | * |
| | | * @since 1.1.7 |
| | | */ |
| | | function _authEXTERNAL($user, $pass, $euser) |
| | | { |
| | | $cmd = sprintf( |
| | | 'AUTHENTICATE "EXTERNAL" "%s"', |
| | | base64_encode(strlen($euser) ? $euser : $user) |
| | | ); |
| | | return $this->_sendCmd($cmd); |
| | | } |
| | | |
| | | /** |
| | | * Removes a script from the server. |
| | | * |
| | | * @param string $scriptname Name of the script to delete. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdDeleteScript($scriptname) |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('DELETESCRIPT "%s"', $scriptname)))) { |
| | | return $res; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the contents of the named script. |
| | | * |
| | | * @param string $scriptname Name of the script to retrieve. |
| | | * |
| | | * @return string The script if successful, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdGetScript($scriptname) |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('GETSCRIPT "%s"', $scriptname)))) { |
| | | return $res; |
| | | } |
| | | |
| | | return preg_replace('/{[0-9]+}\r\n/', '', $res); |
| | | } |
| | | |
| | | /** |
| | | * Sets the active script, i.e. the one that gets run on new mail by the |
| | | * server. |
| | | * |
| | | * @param string $scriptname The name of the script to mark as active. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdSetActive($scriptname) |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('SETACTIVE "%s"', $scriptname)))) { |
| | | return $res; |
| | | } |
| | | $this->_activeScript = $scriptname; |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of scripts on the server. |
| | | * |
| | | * @return array An array with the list of scripts in the first element |
| | | * and the active script in the second element on success, |
| | | * PEAR_Error otherwise. |
| | | */ |
| | | function _cmdListScripts() |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd('LISTSCRIPTS'))) { |
| | | return $res; |
| | | } |
| | | |
| | | $scripts = array(); |
| | | $activescript = null; |
| | | $res = explode("\r\n", $res); |
| | | foreach ($res as $value) { |
| | | if (preg_match('/^"(.*)"( ACTIVE)?$/i', $value, $matches)) { |
| | | $scripts[] = $matches[1]; |
| | | if (!empty($matches[2])) { |
| | | $activescript = $matches[1]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return array($scripts, $activescript); |
| | | } |
| | | |
| | | /** |
| | | * Adds a script to the server. |
| | | * |
| | | * @param string $scriptname Name of the new script. |
| | | * @param string $scriptdata The new script. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdPutScript($scriptname, $scriptdata) |
| | | { |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | |
| | | $stringLength = $this->_getLineLength($scriptdata); |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf("PUTSCRIPT \"%s\" {%d+}\r\n%s", $scriptname, $stringLength, $scriptdata)))) { |
| | | return $res; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Logs out of the server and terminates the connection. |
| | | * |
| | | * @param boolean $sendLogoutCMD Whether to send LOGOUT command before |
| | | * disconnecting. |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdLogout($sendLogoutCMD = true) |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 1); |
| | | } |
| | | |
| | | if ($sendLogoutCMD) { |
| | | if (PEAR::isError($res = $this->_doCmd('LOGOUT'))) { |
| | | return $res; |
| | | } |
| | | } |
| | | |
| | | $this->_sock->disconnect(); |
| | | $this->_state = NET_SIEVE_STATE_DISCONNECTED; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Sends the CAPABILITY command |
| | | * |
| | | * @return boolean True on success, PEAR_Error otherwise. |
| | | */ |
| | | function _cmdCapability() |
| | | { |
| | | if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { |
| | | return PEAR::raiseError('Not currently connected', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd('CAPABILITY'))) { |
| | | return $res; |
| | | } |
| | | $this->_parseCapability($res); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Parses the response from the CAPABILITY command and stores the result |
| | | * in $_capability. |
| | | * |
| | | * @param string $data The response from the capability command. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _parseCapability($data) |
| | | { |
| | | // Clear the cached capabilities. |
| | | $this->_capability = array('sasl' => array(), |
| | | 'extensions' => array()); |
| | | |
| | | $data = preg_split('/\r?\n/', $this->_toUpper($data), -1, PREG_SPLIT_NO_EMPTY); |
| | | |
| | | for ($i = 0; $i < count($data); $i++) { |
| | | if (!preg_match('/^"([A-Z]+)"( "(.*)")?$/', $data[$i], $matches)) { |
| | | continue; |
| | | } |
| | | switch ($matches[1]) { |
| | | case 'IMPLEMENTATION': |
| | | $this->_capability['implementation'] = $matches[3]; |
| | | break; |
| | | |
| | | case 'SASL': |
| | | $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]); |
| | | break; |
| | | |
| | | case 'SIEVE': |
| | | $this->_capability['extensions'] = preg_split('/\s+/', $matches[3]); |
| | | break; |
| | | |
| | | case 'STARTTLS': |
| | | $this->_capability['starttls'] = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Sends a command to the server |
| | | * |
| | | * @param string $cmd The command to send. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _sendCmd($cmd) |
| | | { |
| | | $status = $this->_sock->getStatus(); |
| | | if (PEAR::isError($status) || $status['eof']) { |
| | | return PEAR::raiseError('Failed to write to socket: connection lost'); |
| | | } |
| | | if (PEAR::isError($error = $this->_sock->write($cmd . "\r\n"))) { |
| | | return PEAR::raiseError( |
| | | 'Failed to write to socket: ' . $error->getMessage() |
| | | ); |
| | | } |
| | | $this->_debug("C: $cmd"); |
| | | } |
| | | |
| | | /** |
| | | * Sends a string response to the server. |
| | | * |
| | | * @param string $str The string to send. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _sendStringResponse($str) |
| | | { |
| | | return $this->_sendCmd('{' . $this->_getLineLength($str) . "+}\r\n" . $str); |
| | | } |
| | | |
| | | /** |
| | | * Receives a single line from the server. |
| | | * |
| | | * @return string The server response line. |
| | | */ |
| | | function _recvLn() |
| | | { |
| | | if (PEAR::isError($lastline = $this->_sock->gets(8192))) { |
| | | return PEAR::raiseError( |
| | | 'Failed to read from socket: ' . $lastline->getMessage() |
| | | ); |
| | | } |
| | | |
| | | $lastline = rtrim($lastline); |
| | | $this->_debug("S: $lastline"); |
| | | |
| | | if ($lastline === '') { |
| | | return PEAR::raiseError('Failed to read from socket'); |
| | | } |
| | | |
| | | return $lastline; |
| | | } |
| | | |
| | | /** |
| | | * Send a command and retrieves a response from the server. |
| | | * |
| | | * @param string $cmd The command to send. |
| | | * @param boolean $auth Whether this is an authentication command. |
| | | * |
| | | * @return string|PEAR_Error Reponse string if an OK response, PEAR_Error |
| | | * if a NO response. |
| | | */ |
| | | function _doCmd($cmd = '', $auth = false) |
| | | { |
| | | $referralCount = 0; |
| | | while ($referralCount < $this->_maxReferralCount) { |
| | | if (strlen($cmd)) { |
| | | if (PEAR::isError($error = $this->_sendCmd($cmd))) { |
| | | return $error; |
| | | } |
| | | } |
| | | |
| | | $response = ''; |
| | | while (true) { |
| | | if (PEAR::isError($line = $this->_recvLn())) { |
| | | return $line; |
| | | } |
| | | $uc_line = $this->_toUpper($line); |
| | | |
| | | if ('OK' == substr($uc_line, 0, 2)) { |
| | | $response .= $line; |
| | | return rtrim($response); |
| | | } |
| | | |
| | | if ('NO' == substr($uc_line, 0, 2)) { |
| | | // Check for string literal error message. |
| | | if (preg_match('/^no {([0-9]+)\+?}/i', $line, $matches)) { |
| | | $line .= str_replace( |
| | | "\r\n", ' ', $this->_sock->read($matches[1] + 2) |
| | | ); |
| | | $this->_debug("S: $line"); |
| | | } |
| | | return PEAR::raiseError(trim($response . substr($line, 2)), 3); |
| | | } |
| | | |
| | | if ('BYE' == substr($uc_line, 0, 3)) { |
| | | if (PEAR::isError($error = $this->disconnect(false))) { |
| | | return PEAR::raiseError( |
| | | 'Cannot handle BYE, the error was: ' |
| | | . $error->getMessage(), |
| | | 4 |
| | | ); |
| | | } |
| | | // Check for referral, then follow it. Otherwise, carp an |
| | | // error. |
| | | if (preg_match('/^bye \(referral "(sieve:\/\/)?([^"]+)/i', $line, $matches)) { |
| | | // Replace the old host with the referral host |
| | | // preserving any protocol prefix. |
| | | $this->_data['host'] = preg_replace( |
| | | '/\w+(?!(\w|\:\/\/)).*/', $matches[2], |
| | | $this->_data['host'] |
| | | ); |
| | | if (PEAR::isError($error = $this->_handleConnectAndLogin())) { |
| | | return PEAR::raiseError( |
| | | 'Cannot follow referral to ' |
| | | . $this->_data['host'] . ', the error was: ' |
| | | . $error->getMessage(), |
| | | 5 |
| | | ); |
| | | } |
| | | break; |
| | | } |
| | | return PEAR::raiseError(trim($response . $line), 6); |
| | | } |
| | | |
| | | if (preg_match('/^{([0-9]+)\+?}/i', $line, $matches)) { |
| | | // Matches String Responses. |
| | | $str_size = $matches[1] + 2; |
| | | $line = ''; |
| | | $line_length = 0; |
| | | while ($line_length < $str_size) { |
| | | $line .= $this->_sock->read($str_size - $line_length); |
| | | $line_length = $this->_getLineLength($line); |
| | | } |
| | | $this->_debug("S: $line"); |
| | | |
| | | if (!$auth) { |
| | | // Receive the pending OK only if we aren't |
| | | // authenticating since string responses during |
| | | // authentication don't need an OK. |
| | | $this->_recvLn(); |
| | | } |
| | | return $line; |
| | | } |
| | | |
| | | if ($auth) { |
| | | // String responses during authentication don't need an |
| | | // OK. |
| | | $response .= $line; |
| | | return rtrim($response); |
| | | } |
| | | |
| | | $response .= $line . "\r\n"; |
| | | $referralCount++; |
| | | } |
| | | } |
| | | |
| | | return PEAR::raiseError('Max referral count (' . $referralCount . ') reached. Cyrus murder loop error?', 7); |
| | | } |
| | | |
| | | /** |
| | | * Returns the name of the best authentication method that the server |
| | | * has advertised. |
| | | * |
| | | * @param string $userMethod Only consider this method as available. |
| | | * |
| | | * @return string The name of the best supported authentication method or |
| | | * a PEAR_Error object on failure. |
| | | */ |
| | | function _getBestAuthMethod($userMethod = null) |
| | | { |
| | | if (!isset($this->_capability['sasl'])) { |
| | | return PEAR::raiseError('This server doesn\'t support any authentication methods. SASL problem?'); |
| | | } |
| | | if (!$this->_capability['sasl']) { |
| | | return PEAR::raiseError('This server doesn\'t support any authentication methods.'); |
| | | } |
| | | |
| | | if ($userMethod) { |
| | | if (in_array($userMethod, $this->_capability['sasl'])) { |
| | | return $userMethod; |
| | | } |
| | | return PEAR::raiseError( |
| | | sprintf('No supported authentication method found. The server supports these methods: %s, but we want to use: %s', |
| | | implode(', ', $this->_capability['sasl']), |
| | | $userMethod)); |
| | | } |
| | | |
| | | foreach ($this->supportedAuthMethods as $method) { |
| | | if (in_array($method, $this->_capability['sasl'])) { |
| | | return $method; |
| | | } |
| | | } |
| | | |
| | | return PEAR::raiseError( |
| | | sprintf('No supported authentication method found. The server supports these methods: %s, but we only support: %s', |
| | | implode(', ', $this->_capability['sasl']), |
| | | implode(', ', $this->supportedAuthMethods))); |
| | | } |
| | | |
| | | /** |
| | | * Starts a TLS connection. |
| | | * |
| | | * @return boolean True on success, PEAR_Error on failure. |
| | | */ |
| | | function _startTLS() |
| | | { |
| | | if (PEAR::isError($res = $this->_doCmd('STARTTLS'))) { |
| | | return $res; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->_sock->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | return PEAR::raiseError('Failed to establish TLS connection', 2); |
| | | } |
| | | |
| | | $this->_debug('STARTTLS negotiation successful'); |
| | | |
| | | // The server should be sending a CAPABILITY response after |
| | | // negotiating TLS. Read it, and ignore if it doesn't. |
| | | $this->_doCmd(); |
| | | |
| | | // RFC says we need to query the server capabilities again now that we |
| | | // are under encryption. |
| | | if (PEAR::isError($res = $this->_cmdCapability())) { |
| | | return PEAR::raiseError( |
| | | 'Failed to connect, server said: ' . $res->getMessage(), 2 |
| | | ); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Returns the length of a string. |
| | | * |
| | | * @param string $string A string. |
| | | * |
| | | * @return integer The length of the string. |
| | | */ |
| | | function _getLineLength($string) |
| | | { |
| | | if (extension_loaded('mbstring')) { |
| | | return mb_strlen($string, 'latin1'); |
| | | } else { |
| | | return strlen($string); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Locale independant strtoupper() implementation. |
| | | * |
| | | * @param string $string The string to convert to lowercase. |
| | | * |
| | | * @return string The lowercased string, based on ASCII encoding. |
| | | */ |
| | | function _toUpper($string) |
| | | { |
| | | $language = setlocale(LC_CTYPE, 0); |
| | | setlocale(LC_CTYPE, 'C'); |
| | | $string = strtoupper($string); |
| | | setlocale(LC_CTYPE, $language); |
| | | return $string; |
| | | } |
| | | |
| | | /** |
| | | * Write debug text to the current debug output handler. |
| | | * |
| | | * @param string $message Debug message text. |
| | | * |
| | | * @return void |
| | | */ |
| | | function _debug($message) |
| | | { |
| | | if ($this->_debug) { |
| | | if ($this->_debug_handler) { |
| | | call_user_func_array($this->_debug_handler, array(&$this, $message)); |
| | | } else { |
| | | echo "$message\n"; |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | Classes for managesieve operations (using PEAR::Net_Sieve) |
| | | |
| | | Author: Aleksander Machniak <alec@alec.pl> |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | // Sieve Language Basics: http://www.ietf.org/rfc/rfc5228.txt |
| | | |
| | | define('SIEVE_ERROR_CONNECTION', 1); |
| | | define('SIEVE_ERROR_LOGIN', 2); |
| | | define('SIEVE_ERROR_NOT_EXISTS', 3); // script not exists |
| | | define('SIEVE_ERROR_INSTALL', 4); // script installation |
| | | define('SIEVE_ERROR_ACTIVATE', 5); // script activation |
| | | define('SIEVE_ERROR_DELETE', 6); // script deletion |
| | | define('SIEVE_ERROR_INTERNAL', 7); // internal error |
| | | define('SIEVE_ERROR_DEACTIVATE', 8); // script activation |
| | | define('SIEVE_ERROR_OTHER', 255); // other/unknown error |
| | | |
| | | |
| | | class rcube_sieve |
| | | { |
| | | private $sieve; // Net_Sieve object |
| | | private $error = false; // error flag |
| | | private $list = array(); // scripts list |
| | | |
| | | public $script; // rcube_sieve_script object |
| | | public $current; // name of currently loaded script |
| | | private $disabled; // array of disabled extensions |
| | | |
| | | |
| | | /** |
| | | * Object constructor |
| | | * |
| | | * @param string Username (for managesieve login) |
| | | * @param string Password (for managesieve login) |
| | | * @param string Managesieve server hostname/address |
| | | * @param string Managesieve server port number |
| | | * @param string Managesieve authentication method |
| | | * @param boolean Enable/disable TLS use |
| | | * @param array Disabled extensions |
| | | * @param boolean Enable/disable debugging |
| | | */ |
| | | public function __construct($username, $password='', $host='localhost', $port=2000, |
| | | $auth_type=null, $usetls=true, $disabled=array(), $debug=false) |
| | | { |
| | | $this->sieve = new Net_Sieve(); |
| | | |
| | | if ($debug) { |
| | | $this->sieve->setDebug(true, array($this, 'debug_handler')); |
| | | } |
| | | if (PEAR::isError($this->sieve->connect($host, $port, NULL, $usetls))) { |
| | | return $this->_set_error(SIEVE_ERROR_CONNECTION); |
| | | } |
| | | if (PEAR::isError($this->sieve->login($username, $password, |
| | | $auth_type ? strtoupper($auth_type) : null)) |
| | | ) { |
| | | return $this->_set_error(SIEVE_ERROR_LOGIN); |
| | | } |
| | | $this->disabled = $disabled; |
| | | } |
| | | |
| | | public function __destruct() { |
| | | $this->sieve->disconnect(); |
| | | } |
| | | |
| | | /** |
| | | * Getter for error code |
| | | */ |
| | | public function error() |
| | | { |
| | | return $this->error ? $this->error : false; |
| | | } |
| | | |
| | | /** |
| | | * Saves current script into server |
| | | */ |
| | | public function save($name = null) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (!$this->script) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (!$name) |
| | | $name = $this->current; |
| | | |
| | | $script = $this->script->as_text(); |
| | | |
| | | if (!$script) |
| | | $script = '/* empty script */'; |
| | | |
| | | if (PEAR::isError($this->sieve->installScript($name, $script))) |
| | | return $this->_set_error(SIEVE_ERROR_INSTALL); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Saves text script into server |
| | | */ |
| | | public function save_script($name, $content = null) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (!$content) |
| | | $content = '/* empty script */'; |
| | | |
| | | if (PEAR::isError($this->sieve->installScript($name, $content))) |
| | | return $this->_set_error(SIEVE_ERROR_INSTALL); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Activates specified script |
| | | */ |
| | | public function activate($name = null) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (!$name) |
| | | $name = $this->current; |
| | | |
| | | if (PEAR::isError($this->sieve->setActive($name))) |
| | | return $this->_set_error(SIEVE_ERROR_ACTIVATE); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * De-activates specified script |
| | | */ |
| | | public function deactivate() |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (PEAR::isError($this->sieve->setActive(''))) |
| | | return $this->_set_error(SIEVE_ERROR_DEACTIVATE); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Removes specified script |
| | | */ |
| | | public function remove($name = null) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if (!$name) |
| | | $name = $this->current; |
| | | |
| | | // script must be deactivated first |
| | | if ($name == $this->sieve->getActive()) |
| | | if (PEAR::isError($this->sieve->setActive(''))) |
| | | return $this->_set_error(SIEVE_ERROR_DELETE); |
| | | |
| | | if (PEAR::isError($this->sieve->removeScript($name))) |
| | | return $this->_set_error(SIEVE_ERROR_DELETE); |
| | | |
| | | if ($name == $this->current) |
| | | $this->current = null; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Gets list of supported by server Sieve extensions |
| | | */ |
| | | public function get_extensions() |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | $ext = $this->sieve->getExtensions(); |
| | | // we're working on lower-cased names |
| | | $ext = array_map('strtolower', (array) $ext); |
| | | |
| | | if ($this->script) { |
| | | $supported = $this->script->get_extensions(); |
| | | foreach ($ext as $idx => $ext_name) |
| | | if (!in_array($ext_name, $supported)) |
| | | unset($ext[$idx]); |
| | | } |
| | | |
| | | return array_values($ext); |
| | | } |
| | | |
| | | /** |
| | | * Gets list of scripts from server |
| | | */ |
| | | public function get_scripts() |
| | | { |
| | | if (!$this->list) { |
| | | |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | $this->list = $this->sieve->listScripts(); |
| | | |
| | | if (PEAR::isError($this->list)) |
| | | return $this->_set_error(SIEVE_ERROR_OTHER); |
| | | } |
| | | |
| | | return $this->list; |
| | | } |
| | | |
| | | /** |
| | | * Returns active script name |
| | | */ |
| | | public function get_active() |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | return $this->sieve->getActive(); |
| | | } |
| | | |
| | | /** |
| | | * Loads script by name |
| | | */ |
| | | public function load($name) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if ($this->current == $name) |
| | | return true; |
| | | |
| | | $script = $this->sieve->getScript($name); |
| | | |
| | | if (PEAR::isError($script)) |
| | | return $this->_set_error(SIEVE_ERROR_OTHER); |
| | | |
| | | // try to parse from Roundcube format |
| | | $this->script = $this->_parse($script); |
| | | |
| | | $this->current = $name; |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Loads script from text content |
| | | */ |
| | | public function load_script($script) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | // try to parse from Roundcube format |
| | | $this->script = $this->_parse($script); |
| | | } |
| | | |
| | | /** |
| | | * Creates rcube_sieve_script object from text script |
| | | */ |
| | | private function _parse($txt) |
| | | { |
| | | // try to parse from Roundcube format |
| | | $script = new rcube_sieve_script($txt, $this->disabled); |
| | | |
| | | // ... else try to import from different formats |
| | | if (empty($script->content)) { |
| | | $script = $this->_import_rules($txt); |
| | | $script = new rcube_sieve_script($script, $this->disabled); |
| | | } |
| | | |
| | | // replace all elsif with if+stop, we support only ifs |
| | | foreach ($script->content as $idx => $rule) { |
| | | if (!isset($script->content[$idx+1]) |
| | | || preg_match('/^else|elsif$/', $script->content[$idx+1]['type'])) { |
| | | // 'stop' not found? |
| | | if (!preg_match('/^(stop|vacation)$/', $rule['actions'][count($rule['actions'])-1]['type'])) { |
| | | $script->content[$idx]['actions'][] = array( |
| | | 'type' => 'stop' |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $script; |
| | | } |
| | | |
| | | /** |
| | | * Gets specified script as text |
| | | */ |
| | | public function get_script($name) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | $content = $this->sieve->getScript($name); |
| | | |
| | | if (PEAR::isError($content)) |
| | | return $this->_set_error(SIEVE_ERROR_OTHER); |
| | | |
| | | return $content; |
| | | } |
| | | |
| | | /** |
| | | * Creates empty script or copy of other script |
| | | */ |
| | | public function copy($name, $copy) |
| | | { |
| | | if (!$this->sieve) |
| | | return $this->_set_error(SIEVE_ERROR_INTERNAL); |
| | | |
| | | if ($copy) { |
| | | $content = $this->sieve->getScript($copy); |
| | | |
| | | if (PEAR::isError($content)) |
| | | return $this->_set_error(SIEVE_ERROR_OTHER); |
| | | } |
| | | |
| | | return $this->save_script($name, $content); |
| | | } |
| | | |
| | | private function _import_rules($script) |
| | | { |
| | | $i = 0; |
| | | $name = array(); |
| | | |
| | | // Squirrelmail (Avelsieve) |
| | | if ($tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { |
| | | foreach($tokens as $token) { |
| | | if (preg_match('/^#START_SIEVE_RULE.*/', $token, $matches)) { |
| | | $name[$i] = "unnamed rule ".($i+1); |
| | | $content .= "# rule:[".$name[$i]."]\n"; |
| | | } |
| | | elseif (isset($name[$i])) { |
| | | // This preg_replace is added because I've found some Avelsieve scripts |
| | | // with rules containing "if" here. I'm not sure it was working |
| | | // before without this or not. |
| | | $token = preg_replace('/^if\s+/', '', trim($token)); |
| | | $content .= "if $token\n"; |
| | | $i++; |
| | | } |
| | | } |
| | | } |
| | | // Horde (INGO) |
| | | else if ($tokens = preg_split('/(# .+)\r?\n/i', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { |
| | | foreach($tokens as $token) { |
| | | if (preg_match('/^# (.+)/i', $token, $matches)) { |
| | | $name[$i] = $matches[1]; |
| | | $content .= "# rule:[" . $name[$i] . "]\n"; |
| | | } |
| | | elseif (isset($name[$i])) { |
| | | $token = str_replace(":comparator \"i;ascii-casemap\" ", "", $token); |
| | | $content .= $token . "\n"; |
| | | $i++; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $content; |
| | | } |
| | | |
| | | private function _set_error($error) |
| | | { |
| | | $this->error = $error; |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * This is our own debug handler for connection |
| | | */ |
| | | public function debug_handler(&$sieve, $message) |
| | | { |
| | | write_log('sieve', preg_replace('/\r\n$/', '', $message)); |
| | | } |
| | | } |
| | | |
| | | |
| | | class rcube_sieve_script |
| | | { |
| | | public $content = array(); // script rules array |
| | | |
| | | private $supported = array( // extensions supported by class |
| | | 'fileinto', |
| | | 'reject', |
| | | 'ereject', |
| | | 'copy', // RFC3894 |
| | | 'vacation', // RFC5230 |
| | | // TODO: (most wanted first) body, imapflags, notify, regex |
| | | ); |
| | | |
| | | /** |
| | | * Object constructor |
| | | * |
| | | * @param string Script's text content |
| | | * @param array Disabled extensions |
| | | */ |
| | | public function __construct($script, $disabled=NULL) |
| | | { |
| | | if (!empty($disabled)) |
| | | foreach ($disabled as $ext) |
| | | if (($idx = array_search($ext, $this->supported)) !== false) |
| | | unset($this->supported[$idx]); |
| | | |
| | | $this->content = $this->_parse_text($script); |
| | | } |
| | | |
| | | /** |
| | | * Adds script contents as text to the script array (at the end) |
| | | * |
| | | * @param string Text script contents |
| | | */ |
| | | public function add_text($script) |
| | | { |
| | | $content = $this->_parse_text($script); |
| | | $result = false; |
| | | |
| | | // check existsing script rules names |
| | | foreach ($this->content as $idx => $elem) { |
| | | $names[$elem['name']] = $idx; |
| | | } |
| | | |
| | | foreach ($content as $elem) { |
| | | if (!isset($names[$elem['name']])) { |
| | | array_push($this->content, $elem); |
| | | $result = true; |
| | | } |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Adds rule to the script (at the end) |
| | | * |
| | | * @param string Rule name |
| | | * @param array Rule content (as array) |
| | | */ |
| | | public function add_rule($content) |
| | | { |
| | | // TODO: check this->supported |
| | | array_push($this->content, $content); |
| | | return sizeof($this->content)-1; |
| | | } |
| | | |
| | | public function delete_rule($index) |
| | | { |
| | | if(isset($this->content[$index])) { |
| | | unset($this->content[$index]); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | public function size() |
| | | { |
| | | return sizeof($this->content); |
| | | } |
| | | |
| | | public function update_rule($index, $content) |
| | | { |
| | | // TODO: check this->supported |
| | | if ($this->content[$index]) { |
| | | $this->content[$index] = $content; |
| | | return $index; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns script as text |
| | | */ |
| | | public function as_text() |
| | | { |
| | | $script = ''; |
| | | $exts = array(); |
| | | $idx = 0; |
| | | |
| | | // rules |
| | | foreach ($this->content as $rule) { |
| | | $extension = ''; |
| | | $tests = array(); |
| | | $i = 0; |
| | | |
| | | // header |
| | | $script .= '# rule:[' . $rule['name'] . "]\n"; |
| | | |
| | | // constraints expressions |
| | | foreach ($rule['tests'] as $test) { |
| | | $tests[$i] = ''; |
| | | switch ($test['test']) { |
| | | case 'size': |
| | | $tests[$i] .= ($test['not'] ? 'not ' : ''); |
| | | $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; |
| | | break; |
| | | case 'true': |
| | | $tests[$i] .= ($test['not'] ? 'not true' : 'true'); |
| | | break; |
| | | case 'exists': |
| | | $tests[$i] .= ($test['not'] ? 'not ' : ''); |
| | | if (is_array($test['arg'])) |
| | | $tests[$i] .= 'exists ["' . implode('", "', $this->_escape_string($test['arg'])) . '"]'; |
| | | else |
| | | $tests[$i] .= 'exists "' . $this->_escape_string($test['arg']) . '"'; |
| | | break; |
| | | case 'header': |
| | | $tests[$i] .= ($test['not'] ? 'not ' : ''); |
| | | $tests[$i] .= 'header :' . $test['type']; |
| | | if (is_array($test['arg1'])) |
| | | $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg1'])) . '"]'; |
| | | else |
| | | $tests[$i] .= ' "' . $this->_escape_string($test['arg1']) . '"'; |
| | | if (is_array($test['arg2'])) |
| | | $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg2'])) . '"]'; |
| | | else |
| | | $tests[$i] .= ' "' . $this->_escape_string($test['arg2']) . '"'; |
| | | break; |
| | | } |
| | | $i++; |
| | | } |
| | | |
| | | // $script .= ($idx>0 ? 'els' : '').($rule['join'] ? 'if allof (' : 'if anyof ('); |
| | | // disabled rule: if false #.... |
| | | $script .= 'if' . ($rule['disabled'] ? ' false #' : ''); |
| | | $script .= $rule['join'] ? ' allof (' : ' anyof ('; |
| | | if (sizeof($tests) > 1) |
| | | $script .= implode(", ", $tests); |
| | | else if (sizeof($tests)) |
| | | $script .= $tests[0]; |
| | | else |
| | | $script .= 'true'; |
| | | $script .= ")\n{\n"; |
| | | |
| | | // action(s) |
| | | foreach ($rule['actions'] as $action) { |
| | | switch ($action['type']) { |
| | | case 'fileinto': |
| | | array_push($exts, 'fileinto'); |
| | | $script .= "\tfileinto "; |
| | | if ($action['copy']) { |
| | | $script .= ':copy '; |
| | | array_push($exts, 'copy'); |
| | | } |
| | | $script .= "\"" . $this->_escape_string($action['target']) . "\";\n"; |
| | | break; |
| | | case 'redirect': |
| | | $script .= "\tredirect "; |
| | | if ($action['copy']) { |
| | | $script .= ':copy '; |
| | | array_push($exts, 'copy'); |
| | | } |
| | | $script .= "\"" . $this->_escape_string($action['target']) . "\";\n"; |
| | | break; |
| | | case 'reject': |
| | | case 'ereject': |
| | | array_push($exts, $action['type']); |
| | | if (strpos($action['target'], "\n")!==false) |
| | | $script .= "\t".$action['type']." text:\n" . $action['target'] . "\n.\n;\n"; |
| | | else |
| | | $script .= "\t".$action['type']." \"" . $this->_escape_string($action['target']) . "\";\n"; |
| | | break; |
| | | case 'keep': |
| | | case 'discard': |
| | | case 'stop': |
| | | $script .= "\t" . $action['type'] .";\n"; |
| | | break; |
| | | case 'vacation': |
| | | array_push($exts, 'vacation'); |
| | | $script .= "\tvacation"; |
| | | if ($action['days']) |
| | | $script .= " :days " . $action['days']; |
| | | if ($action['addresses']) |
| | | $script .= " :addresses " . $this->_print_list($action['addresses']); |
| | | if ($action['subject']) |
| | | $script .= " :subject \"" . $this->_escape_string($action['subject']) . "\""; |
| | | if ($action['handle']) |
| | | $script .= " :handle \"" . $this->_escape_string($action['handle']) . "\""; |
| | | if ($action['from']) |
| | | $script .= " :from \"" . $this->_escape_string($action['from']) . "\""; |
| | | if ($action['mime']) |
| | | $script .= " :mime"; |
| | | if (strpos($action['reason'], "\n")!==false) |
| | | $script .= " text:\n" . $action['reason'] . "\n.\n;\n"; |
| | | else |
| | | $script .= " \"" . $this->_escape_string($action['reason']) . "\";\n"; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | $script .= "}\n"; |
| | | $idx++; |
| | | } |
| | | |
| | | // requires |
| | | if (!empty($exts)) |
| | | $script = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $script; |
| | | |
| | | return $script; |
| | | } |
| | | |
| | | /** |
| | | * Returns script object |
| | | * |
| | | */ |
| | | public function as_array() |
| | | { |
| | | return $this->content; |
| | | } |
| | | |
| | | /** |
| | | * Returns array of supported extensions |
| | | * |
| | | */ |
| | | public function get_extensions() |
| | | { |
| | | return array_values($this->supported); |
| | | } |
| | | |
| | | /** |
| | | * Converts text script to rules array |
| | | * |
| | | * @param string Text script |
| | | */ |
| | | private function _parse_text($script) |
| | | { |
| | | $i = 0; |
| | | $content = array(); |
| | | |
| | | // remove C comments |
| | | $script = preg_replace('|/\*.*?\*/|sm', '', $script); |
| | | |
| | | // tokenize rules |
| | | if ($tokens = preg_split('/(# rule:\[.*\])\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { |
| | | foreach($tokens as $token) { |
| | | if (preg_match('/^# rule:\[(.*)\]/', $token, $matches)) { |
| | | $content[$i]['name'] = $matches[1]; |
| | | } |
| | | else if (isset($content[$i]['name']) && sizeof($content[$i]) == 1) { |
| | | if ($rule = $this->_tokenize_rule($token)) { |
| | | $content[$i] = array_merge($content[$i], $rule); |
| | | $i++; |
| | | } |
| | | else // unknown rule format |
| | | unset($content[$i]); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $content; |
| | | } |
| | | |
| | | /** |
| | | * Convert text script fragment to rule object |
| | | * |
| | | * @param string Text rule |
| | | */ |
| | | private function _tokenize_rule($content) |
| | | { |
| | | $result = NULL; |
| | | |
| | | if (preg_match('/^(if|elsif|else)\s+((true|false|not\s+true|allof|anyof|exists|header|not|size)(.*))\s+\{(.*)\}$/sm', |
| | | trim($content), $matches)) { |
| | | |
| | | $tests = trim($matches[2]); |
| | | |
| | | // disabled rule (false + comment): if false #..... |
| | | if ($matches[3] == 'false') { |
| | | $tests = preg_replace('/^false\s+#\s+/', '', $tests); |
| | | $disabled = true; |
| | | } |
| | | else |
| | | $disabled = false; |
| | | |
| | | list($tests, $join) = $this->_parse_tests($tests); |
| | | $actions = $this->_parse_actions(trim($matches[5])); |
| | | |
| | | if ($tests && $actions) |
| | | $result = array( |
| | | 'type' => $matches[1], |
| | | 'tests' => $tests, |
| | | 'actions' => $actions, |
| | | 'join' => $join, |
| | | 'disabled' => $disabled, |
| | | ); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Parse body of actions section |
| | | * |
| | | * @param string Text body |
| | | * @return array Array of parsed action type/target pairs |
| | | */ |
| | | private function _parse_actions($content) |
| | | { |
| | | $result = NULL; |
| | | |
| | | // supported actions |
| | | $patterns[] = '^\s*discard;'; |
| | | $patterns[] = '^\s*keep;'; |
| | | $patterns[] = '^\s*stop;'; |
| | | $patterns[] = '^\s*redirect\s+(.*?[^\\\]);'; |
| | | if (in_array('fileinto', $this->supported)) |
| | | $patterns[] = '^\s*fileinto\s+(.*?[^\\\]);'; |
| | | if (in_array('reject', $this->supported)) { |
| | | $patterns[] = '^\s*reject\s+text:(.*)\n\.\n;'; |
| | | $patterns[] = '^\s*reject\s+(.*?[^\\\]);'; |
| | | $patterns[] = '^\s*ereject\s+text:(.*)\n\.\n;'; |
| | | $patterns[] = '^\s*ereject\s+(.*?[^\\\]);'; |
| | | } |
| | | if (in_array('vacation', $this->supported)) |
| | | $patterns[] = '^\s*vacation\s+(.*?[^\\\]);'; |
| | | |
| | | $pattern = '/(' . implode('$)|(', $patterns) . '$)/ms'; |
| | | |
| | | // parse actions body |
| | | if (preg_match_all($pattern, $content, $mm, PREG_SET_ORDER)) { |
| | | foreach ($mm as $m) { |
| | | $content = trim($m[0]); |
| | | |
| | | if(preg_match('/^(discard|keep|stop)/', $content, $matches)) { |
| | | $result[] = array('type' => $matches[1]); |
| | | } |
| | | else if(preg_match('/^fileinto/', $content)) { |
| | | $target = $m[sizeof($m)-1]; |
| | | $copy = false; |
| | | if (preg_match('/^:copy\s+/', $target)) { |
| | | $target = preg_replace('/^:copy\s+/', '', $target); |
| | | $copy = true; |
| | | } |
| | | $result[] = array('type' => 'fileinto', 'copy' => $copy, |
| | | 'target' => $this->_parse_string($target)); |
| | | } |
| | | else if(preg_match('/^redirect/', $content)) { |
| | | $target = $m[sizeof($m)-1]; |
| | | $copy = false; |
| | | if (preg_match('/^:copy\s+/', $target)) { |
| | | $target = preg_replace('/^:copy\s+/', '', $target); |
| | | $copy = true; |
| | | } |
| | | $result[] = array('type' => 'redirect', 'copy' => $copy, |
| | | 'target' => $this->_parse_string($target)); |
| | | } |
| | | else if(preg_match('/^(reject|ereject)\s+(.*);$/sm', $content, $matches)) { |
| | | $result[] = array('type' => $matches[1], 'target' => $this->_parse_string($matches[2])); |
| | | } |
| | | else if(preg_match('/^vacation\s+(.*);$/sm', $content, $matches)) { |
| | | $vacation = array('type' => 'vacation'); |
| | | |
| | | if (preg_match('/:days\s+([0-9]+)/', $content, $vm)) { |
| | | $vacation['days'] = $vm[1]; |
| | | $content = preg_replace('/:days\s+([0-9]+)/', '', $content); |
| | | } |
| | | if (preg_match('/:subject\s+"(.*?[^\\\])"/', $content, $vm)) { |
| | | $vacation['subject'] = $vm[1]; |
| | | $content = preg_replace('/:subject\s+"(.*?[^\\\])"/', '', $content); |
| | | } |
| | | if (preg_match('/:addresses\s+\[(.*?[^\\\])\]/', $content, $vm)) { |
| | | $vacation['addresses'] = $this->_parse_list($vm[1]); |
| | | $content = preg_replace('/:addresses\s+\[(.*?[^\\\])\]/', '', $content); |
| | | } |
| | | if (preg_match('/:handle\s+"(.*?[^\\\])"/', $content, $vm)) { |
| | | $vacation['handle'] = $vm[1]; |
| | | $content = preg_replace('/:handle\s+"(.*?[^\\\])"/', '', $content); |
| | | } |
| | | if (preg_match('/:from\s+"(.*?[^\\\])"/', $content, $vm)) { |
| | | $vacation['from'] = $vm[1]; |
| | | $content = preg_replace('/:from\s+"(.*?[^\\\])"/', '', $content); |
| | | } |
| | | |
| | | $content = preg_replace('/^vacation/', '', $content); |
| | | $content = preg_replace('/;$/', '', $content); |
| | | $content = trim($content); |
| | | |
| | | if (preg_match('/^:mime/', $content, $vm)) { |
| | | $vacation['mime'] = true; |
| | | $content = preg_replace('/^:mime/', '', $content); |
| | | } |
| | | |
| | | $vacation['reason'] = $this->_parse_string($content); |
| | | |
| | | $result[] = $vacation; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Parse test/conditions section |
| | | * |
| | | * @param string Text |
| | | */ |
| | | private function _parse_tests($content) |
| | | { |
| | | $result = NULL; |
| | | |
| | | // lists |
| | | if (preg_match('/^(allof|anyof)\s+\((.*)\)$/sm', $content, $matches)) { |
| | | $content = $matches[2]; |
| | | $join = $matches[1]=='allof' ? true : false; |
| | | } |
| | | else |
| | | $join = false; |
| | | |
| | | // supported tests regular expressions |
| | | // TODO: comparators, envelope |
| | | $patterns[] = '(not\s+)?(exists)\s+\[(.*?[^\\\])\]'; |
| | | $patterns[] = '(not\s+)?(exists)\s+(".*?[^\\\]")'; |
| | | $patterns[] = '(not\s+)?(true)'; |
| | | $patterns[] = '(not\s+)?(size)\s+:(under|over)\s+([0-9]+[KGM]{0,1})'; |
| | | $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+\[(.*?[^\\\]")\]'; |
| | | $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+(".*?[^\\\]")'; |
| | | $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+(".*?[^\\\]")'; |
| | | $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+\[(.*?[^\\\]")\]'; |
| | | |
| | | // join patterns... |
| | | $pattern = '/(' . implode(')|(', $patterns) . ')/'; |
| | | |
| | | // ...and parse tests list |
| | | if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) { |
| | | foreach ($matches as $match) { |
| | | $size = sizeof($match); |
| | | |
| | | if (preg_match('/^(not\s+)?size/', $match[0])) { |
| | | $result[] = array( |
| | | 'test' => 'size', |
| | | 'not' => $match[$size-4] ? true : false, |
| | | 'type' => $match[$size-2], // under/over |
| | | 'arg' => $match[$size-1], // value |
| | | ); |
| | | } |
| | | else if (preg_match('/^(not\s+)?header/', $match[0])) { |
| | | $result[] = array( |
| | | 'test' => 'header', |
| | | 'not' => $match[$size-5] ? true : false, |
| | | 'type' => $match[$size-3], // is/contains/matches |
| | | 'arg1' => $this->_parse_list($match[$size-2]), // header(s) |
| | | 'arg2' => $this->_parse_list($match[$size-1]), // string(s) |
| | | ); |
| | | } |
| | | else if (preg_match('/^(not\s+)?exists/', $match[0])) { |
| | | $result[] = array( |
| | | 'test' => 'exists', |
| | | 'not' => $match[$size-3] ? true : false, |
| | | 'arg' => $this->_parse_list($match[$size-1]), // header(s) |
| | | ); |
| | | } |
| | | else if (preg_match('/^(not\s+)?true/', $match[0])) { |
| | | $result[] = array( |
| | | 'test' => 'true', |
| | | 'not' => $match[$size-2] ? true : false, |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return array($result, $join); |
| | | } |
| | | |
| | | /** |
| | | * Parse string value |
| | | * |
| | | * @param string Text |
| | | */ |
| | | private function _parse_string($content) |
| | | { |
| | | $text = ''; |
| | | $content = trim($content); |
| | | |
| | | if (preg_match('/^text:(.*)\.$/sm', $content, $matches)) |
| | | $text = trim($matches[1]); |
| | | else if (preg_match('/^"(.*)"$/', $content, $matches)) |
| | | $text = str_replace('\"', '"', $matches[1]); |
| | | |
| | | return $text; |
| | | } |
| | | |
| | | /** |
| | | * Escape special chars in string value |
| | | * |
| | | * @param string Text |
| | | */ |
| | | private function _escape_string($content) |
| | | { |
| | | $replace['/"/'] = '\\"'; |
| | | |
| | | if (is_array($content)) { |
| | | for ($x=0, $y=sizeof($content); $x<$y; $x++) |
| | | $content[$x] = preg_replace(array_keys($replace), |
| | | array_values($replace), $content[$x]); |
| | | |
| | | return $content; |
| | | } |
| | | else |
| | | return preg_replace(array_keys($replace), array_values($replace), $content); |
| | | } |
| | | |
| | | /** |
| | | * Parse string or list of strings to string or array of strings |
| | | * |
| | | * @param string Text |
| | | */ |
| | | private function _parse_list($content) |
| | | { |
| | | $result = array(); |
| | | |
| | | for ($x=0, $len=strlen($content); $x<$len; $x++) { |
| | | switch ($content[$x]) { |
| | | case '\\': |
| | | $str .= $content[++$x]; |
| | | break; |
| | | case '"': |
| | | if (isset($str)) { |
| | | $result[] = $str; |
| | | unset($str); |
| | | } |
| | | else |
| | | $str = ''; |
| | | break; |
| | | default: |
| | | if(isset($str)) |
| | | $str .= $content[$x]; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (sizeof($result)>1) |
| | | return $result; |
| | | else if (sizeof($result) == 1) |
| | | return $result[0]; |
| | | else |
| | | return NULL; |
| | | } |
| | | |
| | | /** |
| | | * Convert array of elements to list of strings |
| | | * |
| | | * @param string Text |
| | | */ |
| | | private function _print_list($list) |
| | | { |
| | | $list = (array) $list; |
| | | foreach($list as $idx => $val) |
| | | $list[$idx] = $this->_escape_string($val); |
| | | |
| | | return '["' . implode('","', $list) . '"]'; |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Филтри'; |
| | | $labels['managefilters'] = 'Управление на филтри за входяща поща'; |
| | | $labels['filtername'] = 'Име на филтър'; |
| | | $labels['newfilter'] = 'Нов филтър'; |
| | | $labels['filteradd'] = 'Добавяне на филтър'; |
| | | $labels['filterdel'] = 'Изтриване на филтър'; |
| | | $labels['moveup'] = 'Преместване нагоре'; |
| | | $labels['movedown'] = 'Преместване надолу'; |
| | | $labels['filterallof'] = 'съвпадение на всички следващи правила'; |
| | | $labels['filteranyof'] = 'съвпадение на някое от следните правила'; |
| | | $labels['filterany'] = 'всички съобщения'; |
| | | $labels['filtercontains'] = 'съдържа'; |
| | | $labels['filternotcontains'] = 'не съдържа'; |
| | | $labels['filteris'] = 'е равно на'; |
| | | $labels['filterisnot'] = 'не е равно на'; |
| | | $labels['filterexists'] = 'съществува'; |
| | | $labels['filternotexists'] = 'не съществува'; |
| | | $labels['filterunder'] = 'под'; |
| | | $labels['filterover'] = 'над'; |
| | | $labels['addrule'] = 'Добавяне на правило'; |
| | | $labels['delrule'] = 'Изтриване на правило'; |
| | | $labels['messagemoveto'] = 'Преместване на съобщението в'; |
| | | $labels['messageredirect'] = 'Пренасочване на съобщението до'; |
| | | $labels['messagereply'] = 'Отговор със съобщение'; |
| | | $labels['messagedelete'] = 'Изтриване на съобщение'; |
| | | $labels['messagediscard'] = 'Отхвърляне със съобщение'; |
| | | $labels['messagesrules'] = 'За входящата поща:'; |
| | | $labels['messagesactions'] = '...изпълнение на следните действия'; |
| | | $labels['add'] = 'Добавяне'; |
| | | $labels['del'] = 'Изтриване'; |
| | | $labels['sender'] = 'Подател'; |
| | | $labels['recipient'] = 'Получател'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Неизвестна грешка на сървъра'; |
| | | $messages['filterconnerror'] = 'Невъзможност за свързване с managesieve сървъра '; |
| | | $messages['filterdeleteerror'] = 'Невъзможност за изтриване на филтър. Сървър грешка'; |
| | | $messages['filterdeleted'] = 'Филтърът е изтрит успешно'; |
| | | $messages['filterdeleteconfirm'] = 'Наистина ли искате да изтриете избрания филтър?'; |
| | | $messages['filtersaved'] = 'Филтърът е записан успешно'; |
| | | $messages['filtersaveerror'] = 'Филтърът не може да бъде записан. Сървър грешка.'; |
| | | $messages['ruledeleteconfirm'] = 'Сигурни ли сте, че искате да изтриете избраното правило?'; |
| | | $messages['actiondeleteconfirm'] = 'Сигурни ли сте, че искате да изтриете избраното действие?'; |
| | | $messages['forbiddenchars'] = 'Забранени символи в полето'; |
| | | $messages['cannotbeempty'] = 'Полето не може да бъде празно'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Czech translation for Roundcube managesieve plugin |
| | | * |
| | | * @version 1.0 (2009-09-29) |
| | | * @author Daniel Kolar <kolar@g2n.cz> |
| | | * |
| | | */ |
| | | |
| | | $labels['filters'] = 'Filtry'; |
| | | $labels['managefilters'] = 'Nastavení filtrů'; |
| | | $labels['filtername'] = 'Název filtru'; |
| | | $labels['newfilter'] = 'Nový filtr'; |
| | | $labels['filteradd'] = 'Přidej filtr'; |
| | | $labels['filterdel'] = 'Smaž filtr'; |
| | | $labels['moveup'] = 'Posunout nahoru'; |
| | | $labels['movedown'] = 'Posunout dolů'; |
| | | $labels['filterallof'] = 'Odpovídají všechny pravidla'; |
| | | $labels['filteranyof'] = 'Odpovídá kterékoliv pravidlo'; |
| | | $labels['filterany'] = 'Všechny zprávy'; |
| | | $labels['filtercontains'] = 'obsahuje'; |
| | | $labels['filternotcontains'] = 'neobsahuje'; |
| | | $labels['filteris'] = 'odpovídá'; |
| | | $labels['filterisnot'] = 'neodpovídá'; |
| | | $labels['filterexists'] = 'existuje'; |
| | | $labels['filternotexists'] = 'neexistuje'; |
| | | $labels['filterunder'] = 'pod'; |
| | | $labels['filterover'] = 'nad'; |
| | | $labels['addrule'] = 'Přidej pravidlo'; |
| | | $labels['delrule'] = 'Smaž pravidlo'; |
| | | $labels['messagemoveto'] = 'Přesuň zprávu do'; |
| | | $labels['messageredirect'] = 'Přeposlat zprávu na'; |
| | | $labels['messagereply'] = 'Odpovědět se zprávou'; |
| | | $labels['messagedelete'] = 'Smazat zprávu'; |
| | | $labels['messagediscard'] = 'Smazat se zprávou'; |
| | | $labels['messagesrules'] = 'Pravidla pro příchozí zprávu:'; |
| | | $labels['messagesactions'] = '...vykonej následující akce:'; |
| | | $labels['add'] = 'Přidej'; |
| | | $labels['del'] = 'Smaž'; |
| | | $labels['sender'] = 'Odesílatel'; |
| | | $labels['recipient'] = 'Příjemce'; |
| | | $labels['vacationaddresses'] = 'Seznam příjemců, kterým nebude zpráva odeslána (oddělené čárkou):'; |
| | | $labels['vacationdays'] = 'Počet dnů mezi automatickými odpověďmi:'; |
| | | $labels['vacationreason'] = 'Zpráva (Důvod nepřítomnosti):'; |
| | | $labels['rulestop'] = 'Zastavit pravidla'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Neznámá chyba serveru'; |
| | | $messages['filterconnerror'] = 'Nebylo možné se připojit k sieve serveru'; |
| | | $messages['filterdeleteerror'] = 'Nebylo možné smazat filtr. Server nahlásil chybu'; |
| | | $messages['filterdeleted'] = 'Filtr byl smazán'; |
| | | $messages['filterdeleteconfirm'] = 'Opravdu chcete smazat vybraný filtr?'; |
| | | $messages['filtersaved'] = 'Filtr byl uložen'; |
| | | $messages['filtersaveerror'] = 'Nebylo možné uložit filtr. Server nahlásil chybu.'; |
| | | $messages['ruledeleteconfirm'] = 'Jste si jisti, že chcete smazat vybrané pravidlo?'; |
| | | $messages['actiondeleteconfirm'] = 'Jste si jisti, že chcete smazat vybranou akci?'; |
| | | $messages['forbiddenchars'] = 'Zakázané znaky v poli'; |
| | | $messages['cannotbeempty'] = 'Pole nemůže být prázdné'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filter'; |
| | | $labels['managefilters'] = 'Verwalte eingehende Nachrichtenfilter'; |
| | | $labels['filtername'] = 'Filtername'; |
| | | $labels['newfilter'] = 'Neuer Filter'; |
| | | $labels['filteradd'] = 'Filter hinzufügen'; |
| | | $labels['filterdel'] = 'Filter löschen'; |
| | | $labels['moveup'] = 'Nach oben'; |
| | | $labels['movedown'] = 'Nach unten'; |
| | | $labels['filterallof'] = 'UND (alle Regeln müssen zutreffen)'; |
| | | $labels['filteranyof'] = 'ODER (eine der Regeln muss zutreffen'; |
| | | $labels['filterany'] = 'Für alle Nachrichten'; |
| | | $labels['filtercontains'] = 'enthält'; |
| | | $labels['filternotcontains'] = 'enthält nicht'; |
| | | $labels['filteris'] = 'ist gleich'; |
| | | $labels['filterisnot'] = 'ist ungleich'; |
| | | $labels['filterexists'] = 'ist vorhanden'; |
| | | $labels['filternotexists'] = 'nicht vorhanden'; |
| | | $labels['filterunder'] = 'unter'; |
| | | $labels['filterover'] = 'über'; |
| | | $labels['addrule'] = 'Regel hinzufügen'; |
| | | $labels['delrule'] = 'Regel löschen'; |
| | | $labels['messagemoveto'] = 'Verschiebe Nachricht nach'; |
| | | $labels['messageredirect'] = 'Leite Nachricht um nach'; |
| | | $labels['messagereply'] = 'Antworte mit Nachricht'; |
| | | $labels['messagedelete'] = 'Nachricht löschen'; |
| | | $labels['messagediscard'] = 'Discard with message'; |
| | | $labels['messagesrules'] = 'Für eingehende Nachrichten:'; |
| | | $labels['messagesactions'] = 'Führe folgende Aktionen aus:'; |
| | | $labels['add'] = 'Hinzufügen'; |
| | | $labels['del'] = 'Löschen'; |
| | | $labels['sender'] = 'Absender'; |
| | | $labels['recipient'] = 'Empfänger'; |
| | | $labels['vacationaddresses'] = 'Zusätzliche Liste von Empfängern (Komma getrennt):'; |
| | | $labels['vacationdays'] = 'Antwort wird erneut gesendet nach (in Tagen):'; |
| | | $labels['vacationreason'] = 'Inhalt der Nachricht (Abwesenheitsgrund):'; |
| | | $labels['rulestop'] = 'Regelauswertung anhalten'; |
| | | |
| | | $messages['filterunknownerror'] = 'Unbekannter Serverfehler'; |
| | | $messages['filterconnerror'] = 'Kann nicht zum Sieve-Server verbinden'; |
| | | $messages['filterdeleteerror'] = 'Fehler beim des löschen Filters. Serverfehler'; |
| | | $messages['filterdeleted'] = 'Filter erfolgreich gelöscht'; |
| | | $messages['filterdeleteconfirm'] = 'Möchten Sie den Filter löschen ?'; |
| | | $messages['filtersaved'] = 'Filter gespeichert'; |
| | | $messages['filtersaveerror'] = 'Serverfehler, konnte den Filter nicht speichern.'; |
| | | $messages['ruledeleteconfirm'] = 'Sicher, dass Sie die Regel löschen wollen?'; |
| | | $messages['actiondeleteconfirm'] = 'Sicher, dass Sie die ausgewaehlte Aktion löschen wollen?'; |
| | | $messages['forbiddenchars'] = 'Unerlaubte Zeichen im Feld'; |
| | | $messages['cannotbeempty'] = 'Feld darf nicht leer sein'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filter'; |
| | | $labels['managefilters'] = 'Verwalte eingehende Nachrichtenfilter'; |
| | | $labels['filtername'] = 'Filtername'; |
| | | $labels['newfilter'] = 'Neuer Filter'; |
| | | $labels['filteradd'] = 'Filter hinzufügen'; |
| | | $labels['filterdel'] = 'Filter löschen'; |
| | | $labels['moveup'] = 'Nach oben'; |
| | | $labels['movedown'] = 'Nach unten'; |
| | | $labels['filterallof'] = 'trifft auf alle folgenden Regeln zu'; |
| | | $labels['filteranyof'] = 'trifft auf eine der folgenden Regeln zu'; |
| | | $labels['filterany'] = 'alle Nachrichten'; |
| | | $labels['filtercontains'] = 'enthält'; |
| | | $labels['filternotcontains'] = 'enthält nicht'; |
| | | $labels['filteris'] = 'ist gleich'; |
| | | $labels['filterisnot'] = 'ist ungleich'; |
| | | $labels['filterexists'] = 'vorhanden'; |
| | | $labels['filternotexists'] = 'nicht vorhanden'; |
| | | $labels['filterunder'] = 'unter'; |
| | | $labels['filterover'] = 'über'; |
| | | $labels['addrule'] = 'Regel hinzufügen'; |
| | | $labels['delrule'] = 'Regel löschen'; |
| | | $labels['messagemoveto'] = 'Verschiebe Nachricht nach'; |
| | | $labels['messageredirect'] = 'Leite Nachricht um an'; |
| | | $labels['messagecopyto'] = 'Kopiere Nachricht nach'; |
| | | $labels['messagesendcopy'] = 'Sende Kopie an'; |
| | | $labels['messagereply'] = 'Antworte mit Nachricht'; |
| | | $labels['messagedelete'] = 'Nachricht löschen'; |
| | | $labels['messagediscard'] = 'Weise ab mit Nachricht'; |
| | | $labels['messagesrules'] = 'Für eingehende Nachrichten:'; |
| | | $labels['messagesactions'] = '...führe folgende Aktionen aus:'; |
| | | $labels['add'] = 'Hinzufügen'; |
| | | $labels['del'] = 'Löschen'; |
| | | $labels['sender'] = 'Absender'; |
| | | $labels['recipient'] = 'Empfänger'; |
| | | $labels['vacationaddresses'] = 'Zusätzliche Liste von eMail-Empfängern (Komma getrennt):'; |
| | | $labels['vacationdays'] = 'Wie oft sollen Nachrichten gesendet werden (in Tagen):'; |
| | | $labels['vacationreason'] = 'Inhalt der Nachricht (Abwesenheitsgrund):'; |
| | | $labels['rulestop'] = 'Regelauswertung anhalten'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Unbekannter Serverfehler'; |
| | | $messages['filterconnerror'] = 'Kann nicht zum Sieve-Server verbinden'; |
| | | $messages['filterdeleteerror'] = 'Fehler beim Löschen des Filters. Serverfehler'; |
| | | $messages['filterdeleted'] = 'Filter erfolgreich gelöscht'; |
| | | $messages['filterdeleteconfirm'] = 'Möchten Sie den Filter löschen?'; |
| | | $messages['filtersaved'] = 'Filter gespeichert'; |
| | | $messages['filtersaveerror'] = 'Serverfehler, konnte den Filter nicht speichern.'; |
| | | $messages['ruledeleteconfirm'] = 'Sicher, dass Sie die Regel löschen wollen?'; |
| | | $messages['actiondeleteconfirm'] = 'Sicher, dass Sie die ausgewählte Aktion löschen wollen?'; |
| | | $messages['forbiddenchars'] = 'Unerlaubte Zeichen im Feld'; |
| | | $messages['cannotbeempty'] = 'Feld darf nicht leer sein'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // Antonis Kanouras (2009-10-22) |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Φίλτρα'; |
| | | $labels['managefilters'] = 'Διαχείριση φίλτρων εισερχόμενων'; |
| | | $labels['filtername'] = 'Ονομασία φίλτρου'; |
| | | $labels['newfilter'] = 'Δημιουργία φίλτρου'; |
| | | $labels['filteradd'] = 'Προσθήκη φίλτρου'; |
| | | $labels['filterdel'] = 'Διαγραφή φίλτρου'; |
| | | $labels['moveup'] = 'Μετακίνηση πάνω'; |
| | | $labels['movedown'] = 'Μετακίνηση κάτω'; |
| | | $labels['filterallof'] = 'ταιριάζουν με όλους τους παρακάτω κανόνες'; |
| | | $labels['filteranyof'] = 'ταιριάζουν με οποιονδήποτε από τους παρακάτω κανόνες'; |
| | | $labels['filterany'] = 'όλα τα μηνύματα'; |
| | | $labels['filtercontains'] = 'περιέχει'; |
| | | $labels['filternotcontains'] = 'δεν περιέχει'; |
| | | $labels['filteris'] = 'είναι ίσο με'; |
| | | $labels['filterisnot'] = 'δεν είναι ίσο με'; |
| | | $labels['filterexists'] = 'υπάρχει'; |
| | | $labels['filternotexists'] = 'δεν υπάρχει'; |
| | | $labels['filterunder'] = 'κάτω'; |
| | | $labels['filterover'] = 'πάνω'; |
| | | $labels['addrule'] = 'Προσθήκη κανόνα'; |
| | | $labels['delrule'] = 'Διαγραφή κανόνα'; |
| | | $labels['messagemoveto'] = 'Μετακίνηση μηνύματος στο'; |
| | | $labels['messageredirect'] = 'Προώθηση μηνύματος στο'; |
| | | $labels['messagereply'] = 'Απάντηση με μήνυμα'; |
| | | $labels['messagedelete'] = 'Διαγραφή μηνύματος'; |
| | | $labels['messagediscard'] = 'Απόρριψη με μήνυμα'; |
| | | $labels['messagesrules'] = 'Για εισερχόμενα μηνύματα που:'; |
| | | $labels['messagesactions'] = '...εκτέλεση των παρακάτω ενεργειών:'; |
| | | $labels['add'] = 'Προσθήκη'; |
| | | $labels['del'] = 'Διαγραφή'; |
| | | $labels['sender'] = 'Αποστολέας'; |
| | | $labels['recipient'] = 'Παραλήπτης'; |
| | | $labels['vacationaddresses'] = 'Πρόσθετη λίστα email παραληπτών (διαχωρισμένη με κόμματα):'; |
| | | $labels['vacationdays'] = 'Συχνότητα αποστολής μηνυμάτων (σε ημέρες):'; |
| | | $labels['vacationreason'] = 'Σώμα μηνύματος (λόγος απουσίας):'; |
| | | $labels['rulestop'] = 'Παύση επαλήθευσης κανόνων'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Άγνωστο σφάλμα διακομιστή'; |
| | | $messages['filterconnerror'] = 'Αδυναμία σύνδεσης στον διακομιστή managesieve'; |
| | | $messages['filterdeleteerror'] = 'Αδυναμία διαγραφής φίλτρου. Προέκυψε σφάλμα στον διακομιστή'; |
| | | $messages['filterdeleted'] = 'Το φίλτρο διαγράφηκε επιτυχώς'; |
| | | $messages['filterconfirmdelete'] = 'Θέλετε όντως να διαγράψετε το επιλεγμένο φίλτρο;'; |
| | | $messages['filtersaved'] = 'Το φίλτρο αποθηκεύτηκε επιτυχώς'; |
| | | $messages['filtersaveerror'] = 'Αδυναμία αποθήκευσης φίλτρου. Προέκυψε σφάλμα στον διακομιστή'; |
| | | $messages['ruledeleteconfirm'] = 'Θέλετε όντως να διαγράψετε τον επιλεγμένο κανόνα;'; |
| | | $messages['actiondeleteconfirm'] = 'Θέλετε όντως να διαγράψετε την επιλεγμένη ενέργεια;'; |
| | | $messages['forbiddenchars'] = 'Μη επιτρεπτοί χαρακτήρες στο πεδίο'; |
| | | $messages['cannotbeempty'] = 'Το πεδίο δεν μπορεί να είναι κενό'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filters'; |
| | | $labels['managefilters'] = 'Manage incoming mail filters'; |
| | | $labels['filtername'] = 'Filter name'; |
| | | $labels['newfilter'] = 'New filter'; |
| | | $labels['filteradd'] = 'Add filter'; |
| | | $labels['filterdel'] = 'Delete filter'; |
| | | $labels['moveup'] = 'Move up'; |
| | | $labels['movedown'] = 'Move down'; |
| | | $labels['filterallof'] = 'matching all of the following rules'; |
| | | $labels['filteranyof'] = 'matching any of the following rules'; |
| | | $labels['filterany'] = 'all messages'; |
| | | $labels['filtercontains'] = 'contains'; |
| | | $labels['filternotcontains'] = 'not contains'; |
| | | $labels['filteris'] = 'is equal to'; |
| | | $labels['filterisnot'] = 'is not equal to'; |
| | | $labels['filterexists'] = 'exists'; |
| | | $labels['filternotexists'] = 'not exists'; |
| | | $labels['filterunder'] = 'under'; |
| | | $labels['filterover'] = 'over'; |
| | | $labels['addrule'] = 'Add rule'; |
| | | $labels['delrule'] = 'Delete rule'; |
| | | $labels['messagemoveto'] = 'Move message to'; |
| | | $labels['messageredirect'] = 'Redirect message to'; |
| | | $labels['messagereply'] = 'Reply with message'; |
| | | $labels['messagedelete'] = 'Delete message'; |
| | | $labels['messagediscard'] = 'Discard with message'; |
| | | $labels['messagesrules'] = 'For incoming mail:'; |
| | | $labels['messagesactions'] = '...execute the following actions:'; |
| | | $labels['add'] = 'Add'; |
| | | $labels['del'] = 'Delete'; |
| | | $labels['sender'] = 'Sender'; |
| | | $labels['recipient'] = 'Recipient'; |
| | | $labels['vacationaddresses'] = 'Additional list of recipient e-mails (comma separated):'; |
| | | $labels['vacationdays'] = 'How often send messages (in days):'; |
| | | $labels['vacationreason'] = 'Message body (vacation reason):'; |
| | | $labels['rulestop'] = 'Stop evaluating rules'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Unknown server error'; |
| | | $messages['filterconnerror'] = 'Unable to connect to managesieve server'; |
| | | $messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured'; |
| | | $messages['filterdeleted'] = 'Filter deleted successfully'; |
| | | $messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?'; |
| | | $messages['filtersaved'] = 'Filter saved successfully'; |
| | | $messages['filtersaveerror'] = 'Unable to save filter. Server error occured.'; |
| | | $messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?'; |
| | | $messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?'; |
| | | $messages['forbiddenchars'] = 'Forbidden characters in field'; |
| | | $messages['cannotbeempty'] = 'Field cannot be empty'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filters'; |
| | | $labels['managefilters'] = 'Manage incoming mail filters'; |
| | | $labels['filtername'] = 'Filter name'; |
| | | $labels['newfilter'] = 'New filter'; |
| | | $labels['filteradd'] = 'Add filter'; |
| | | $labels['filterdel'] = 'Delete filter'; |
| | | $labels['moveup'] = 'Move up'; |
| | | $labels['movedown'] = 'Move down'; |
| | | $labels['filterallof'] = 'matching all of the following rules'; |
| | | $labels['filteranyof'] = 'matching any of the following rules'; |
| | | $labels['filterany'] = 'all messages'; |
| | | $labels['filtercontains'] = 'contains'; |
| | | $labels['filternotcontains'] = 'not contains'; |
| | | $labels['filteris'] = 'is equal to'; |
| | | $labels['filterisnot'] = 'is not equal to'; |
| | | $labels['filterexists'] = 'exists'; |
| | | $labels['filternotexists'] = 'not exists'; |
| | | $labels['filterunder'] = 'under'; |
| | | $labels['filterover'] = 'over'; |
| | | $labels['addrule'] = 'Add rule'; |
| | | $labels['delrule'] = 'Delete rule'; |
| | | $labels['messagemoveto'] = 'Move message to'; |
| | | $labels['messageredirect'] = 'Redirect message to'; |
| | | $labels['messagecopyto'] = 'Copy message to'; |
| | | $labels['messagesendcopy'] = 'Send message copy to'; |
| | | $labels['messagereply'] = 'Reply with message'; |
| | | $labels['messagedelete'] = 'Delete message'; |
| | | $labels['messagediscard'] = 'Discard with message'; |
| | | $labels['messagesrules'] = 'For incoming mail:'; |
| | | $labels['messagesactions'] = '...execute the following actions:'; |
| | | $labels['add'] = 'Add'; |
| | | $labels['del'] = 'Delete'; |
| | | $labels['sender'] = 'Sender'; |
| | | $labels['recipient'] = 'Recipient'; |
| | | $labels['vacationaddresses'] = 'Additional list of recipient e-mails (comma separated):'; |
| | | $labels['vacationdays'] = 'How often send messages (in days):'; |
| | | $labels['vacationreason'] = 'Message body (vacation reason):'; |
| | | $labels['rulestop'] = 'Stop evaluating rules'; |
| | | $labels['filterset'] = 'Filters set'; |
| | | $labels['filtersetadd'] = 'Add filters set'; |
| | | $labels['filtersetdel'] = 'Delete current filters set'; |
| | | $labels['filtersetact'] = 'Activate current filters set'; |
| | | $labels['filtersetdeact'] = 'Deactivate current filters set'; |
| | | $labels['filtersetget'] = 'Download filters set in text format'; |
| | | $labels['filterdef'] = 'Filter definition'; |
| | | $labels['filtersetname'] = 'Filters set name'; |
| | | $labels['newfilterset'] = 'New filters set'; |
| | | $labels['active'] = 'active'; |
| | | $labels['none'] = 'none'; |
| | | $labels['fromset'] = 'from set'; |
| | | $labels['fromfile'] = 'from file'; |
| | | $labels['filterdisabled'] = 'Filter disabled'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Unknown server error'; |
| | | $messages['filterconnerror'] = 'Unable to connect to managesieve server'; |
| | | $messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured'; |
| | | $messages['filterdeleted'] = 'Filter deleted successfully'; |
| | | $messages['filtersaved'] = 'Filter saved successfully'; |
| | | $messages['filtersaveerror'] = 'Unable to save filter. Server error occured'; |
| | | $messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?'; |
| | | $messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?'; |
| | | $messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?'; |
| | | $messages['forbiddenchars'] = 'Forbidden characters in field'; |
| | | $messages['cannotbeempty'] = 'Field cannot be empty'; |
| | | $messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured'; |
| | | $messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured'; |
| | | $messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured'; |
| | | $messages['setactivated'] = 'Filters set activated successfully'; |
| | | $messages['setdeactivated'] = 'Filters set deactivated successfully'; |
| | | $messages['setdeleted'] = 'Filters set deleted successfully'; |
| | | $messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?'; |
| | | $messages['setcreateerror'] = 'Unable to create filters set. Server error occured'; |
| | | $messages['setcreated'] = 'Filters set created successfully'; |
| | | $messages['emptyname'] = 'Unable to create filters set. Empty set name'; |
| | | $messages['nametoolong'] = 'Unable to create filters set. Name too long' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filtros'; |
| | | $labels['managefilters'] = 'Administrar filtros de correo entrante'; |
| | | $labels['filtername'] = 'Nombre del filtro'; |
| | | $labels['newfilter'] = 'Nuevo filtro'; |
| | | $labels['filteradd'] = 'Agregar filtro'; |
| | | $labels['filterdel'] = 'Eliminar filtro'; |
| | | $labels['moveup'] = 'Mover arriba'; |
| | | $labels['movedown'] = 'Mover abajo'; |
| | | $labels['filterallof'] = 'coinidir con todas las reglas siguientes'; |
| | | $labels['filteranyof'] = 'coincidir con alguna de las reglas siguientes'; |
| | | $labels['filterany'] = 'todos los mensajes'; |
| | | $labels['filtercontains'] = 'contiene'; |
| | | $labels['filternotcontains'] = 'no contiene'; |
| | | $labels['filteris'] = 'es igual a'; |
| | | $labels['filterisnot'] = 'no es igual a'; |
| | | $labels['filterexists'] = 'existe'; |
| | | $labels['filternotexists'] = 'no existe'; |
| | | $labels['filterunder'] = 'bajo'; |
| | | $labels['filterover'] = 'sobre'; |
| | | $labels['addrule'] = 'Agregar regla'; |
| | | $labels['delrule'] = 'Eliminar regla'; |
| | | $labels['messagemoveto'] = 'Mover mensaje a'; |
| | | $labels['messageredirect'] = 'Redirigir mensaje a'; |
| | | $labels['messagecopyto'] = 'Copiar mensaje a'; |
| | | $labels['messagesendcopy'] = 'Enviar copia del mensaje a'; |
| | | $labels['messagereply'] = 'Responder con un mensaje'; |
| | | $labels['messagedelete'] = 'Eliminar mensaje'; |
| | | $labels['messagediscard'] = 'Descartar con un mensaje'; |
| | | $labels['messagesrules'] = 'Para el correo entrante:'; |
| | | $labels['messagesactions'] = '... ejecutar las siguientes acciones:'; |
| | | $labels['add'] = 'Agregar'; |
| | | $labels['del'] = 'Eliminar'; |
| | | $labels['sender'] = 'Remitente'; |
| | | $labels['recipient'] = 'Destinatario'; |
| | | $labels['vacationaddresses'] = 'Lista de direcciones de correo de destinatarios adicionales (separados por comas):'; |
| | | $labels['vacationdays'] = 'Cada cuanto enviar mensajes (en días):'; |
| | | $labels['vacationreason'] = 'Cuerpo del mensaje (razón de vacaciones):'; |
| | | $labels['rulestop'] = 'Parar de evaluar reglas'; |
| | | $labels['filterset'] = 'Conjunto de filtros'; |
| | | $labels['filtersetadd'] = 'Agregar conjunto de filtros'; |
| | | $labels['filtersetdel'] = 'Eliminar conjunto de filtros'; |
| | | $labels['filtersetact'] = 'Activar conjunto de filtros'; |
| | | $labels['filtersetdeact'] = 'Deactivar conjunto de filtros'; |
| | | $labels['filtersetget'] = 'Descargar conjunto de filtros en archivo de texto'; |
| | | $labels['filterdef'] = 'Definicion del conjunto de filtros'; |
| | | $labels['filtersetname'] = 'Nombre del conjunto de filtros'; |
| | | $labels['newfilterset'] = 'Nuevo conjunto de filtros'; |
| | | $labels['active'] = 'Activar'; |
| | | $labels['none'] = 'none'; |
| | | $labels['fromset'] = 'desde conjunto'; |
| | | $labels['fromfile'] = 'desde archivo'; |
| | | $labels['filterdisabled'] = 'Filtro deshabilitado'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Error desconocido de servidor'; |
| | | $messages['filterconnerror'] = 'Imposible conectar con el servidor managesieve'; |
| | | $messages['filterdeleteerror'] = 'Imposible borrar filtro. Ha ocurrido un error en el servidor'; |
| | | $messages['filterdeleted'] = 'Filtro borrado satisfactoriamente'; |
| | | $messages['filterdeleteconfirm'] = '¿Realmente desea borrar el filtro seleccionado?'; |
| | | $messages['filtersaved'] = 'Filtro guardado satisfactoriamente'; |
| | | $messages['filtersaveerror'] = 'Imposible guardar ell filtro. Ha ocurrido un error en el servidor'; |
| | | $messages['ruledeleteconfirm'] = '¿Está seguro de que desea borrar la regla seleccionada?'; |
| | | $messages['actiondeleteconfirm'] = '¿Está seguro de que desea borrar la acción seleccionada?'; |
| | | $messages['forbiddenchars'] = 'Caracteres prohibidos en el campo'; |
| | | $messages['cannotbeempty'] = 'El campo no puede estar vacío'; |
| | | $messages['setactivateerror'] = 'Imposible activar el conjunto de filtros. Error en el servidor.'; |
| | | $messages['setdeactivateerror'] = 'Imposible desactivar el conjunto de filtros. Error en el servidor.'; |
| | | $messages['setdeleteerror'] = 'Imposible eliminar el conjunto de filtros. Error en el servidor.'; |
| | | $messages['setactivated'] = 'Conjunto de filtros activados correctamente'; |
| | | $messages['setdeactivated'] = 'Conjunto de filtros desactivados correctamente'; |
| | | $messages['setdeleted'] = 'Conjunto de filtros eliminados correctamente'; |
| | | $messages['setdeleteconfirm'] = '¿Esta seguro, que quiere eliminar el conjunto de filtros seleccionado?'; |
| | | $messages['setcreateerror'] = 'Imposible crear el conjunto de filtros. Error en el servidor.'; |
| | | $messages['setcreated'] = 'Conjunto de filtros creados correctamente'; |
| | | $messages['emptyname'] = 'Imposible crear el conjunto de filtros. Nombre del conjunto de filtros vacio'; |
| | | $messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre del conjunto de filtros muy largo'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filtros'; |
| | | $labels['managefilters'] = 'Administrar filtros de correo entrante'; |
| | | $labels['filtername'] = 'Nombre del filtro'; |
| | | $labels['newfilter'] = 'Nuevo filtro'; |
| | | $labels['filteradd'] = 'Agregar filtro'; |
| | | $labels['filterdel'] = 'Eliminar filtro'; |
| | | $labels['moveup'] = 'Mover arriba'; |
| | | $labels['movedown'] = 'Mover abajo'; |
| | | $labels['filterallof'] = 'coinidir con todas las reglas siguientes'; |
| | | $labels['filteranyof'] = 'coincidir con alguna de las reglas siguientes'; |
| | | $labels['filterany'] = 'todos los mensajes'; |
| | | $labels['filtercontains'] = 'contiene'; |
| | | $labels['filternotcontains'] = 'no contiene'; |
| | | $labels['filteris'] = 'es igual a'; |
| | | $labels['filterisnot'] = 'no es igual a'; |
| | | $labels['filterexists'] = 'existe'; |
| | | $labels['filternotexists'] = 'no existe'; |
| | | $labels['filterunder'] = 'bajo'; |
| | | $labels['filterover'] = 'sobre'; |
| | | $labels['addrule'] = 'Agregar regla'; |
| | | $labels['delrule'] = 'Eliminar regla'; |
| | | $labels['messagemoveto'] = 'Mover mensaje a'; |
| | | $labels['messageredirect'] = 'Redirigir mensaje a'; |
| | | $labels['messagecopyto'] = 'Copiar mensaje a'; |
| | | $labels['messagesendcopy'] = 'Enviar copia del mensaje a'; |
| | | $labels['messagereply'] = 'Responder con un mensaje'; |
| | | $labels['messagedelete'] = 'Eliminar mensaje'; |
| | | $labels['messagediscard'] = 'Descartar con un mensaje'; |
| | | $labels['messagesrules'] = 'Para el correo entrante:'; |
| | | $labels['messagesactions'] = '... ejecutar las siguientes acciones:'; |
| | | $labels['add'] = 'Agregar'; |
| | | $labels['del'] = 'Eliminar'; |
| | | $labels['sender'] = 'Remitente'; |
| | | $labels['recipient'] = 'Destinatario'; |
| | | $labels['vacationaddresses'] = 'Lista de direcciones de correo de destinatarios adicionales (separados por comas):'; |
| | | $labels['vacationdays'] = 'Cada cuanto enviar mensajes (en días):'; |
| | | $labels['vacationreason'] = 'Cuerpo del mensaje (razón de vacaciones):'; |
| | | $labels['rulestop'] = 'Parar de evaluar reglas'; |
| | | $labels['filterset'] = 'Conjunto de filtros'; |
| | | $labels['filtersetadd'] = 'Agregar conjunto de filtros'; |
| | | $labels['filtersetdel'] = 'Eliminar conjunto de filtros actual'; |
| | | $labels['filtersetact'] = 'Activar conjunto de filtros actual'; |
| | | $labels['filtersetdeact'] = 'Desactivar conjunto de filtros actual'; |
| | | $labels['filtersetget'] = 'Descargar conjunto de filtros en formato de texto'; |
| | | $labels['filterdef'] = 'Definición de filtros'; |
| | | $labels['filtersetname'] = 'Nombre del conjunto de filtros'; |
| | | $labels['newfilterset'] = 'Nuevo conjunto de filtros'; |
| | | $labels['active'] = 'activo'; |
| | | $labels['none'] = 'ninguno'; |
| | | $labels['fromset'] = 'de conjunto '; |
| | | $labels['fromfile'] = 'de archivo'; |
| | | $labels['filterdisabled'] = 'Filtro desactivado'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Error desconocido de servidor'; |
| | | $messages['filterconnerror'] = 'Imposible conectar con el servidor managesieve'; |
| | | $messages['filterdeleteerror'] = 'Imposible borrar filtro. Ha ocurrido un error en el servidor'; |
| | | $messages['filterdeleted'] = 'Filtro borrado satisfactoriamente'; |
| | | $messages['filtersaved'] = 'Filtro guardado satisfactoriamente'; |
| | | $messages['filtersaveerror'] = 'Imposible guardar el filtro. Ha ocurrido un error en el servidor'; |
| | | $messages['filterdeleteconfirm'] = '¿Realmente desea borrar el filtro seleccionado?'; |
| | | $messages['ruledeleteconfirm'] = '¿Está seguro de que desea borrar la regla seleccionada?'; |
| | | $messages['actiondeleteconfirm'] = '¿Está seguro de que desea borrar la acción seleccionada?'; |
| | | $messages['forbiddenchars'] = 'Caracteres prohibidos en el campo'; |
| | | $messages['cannotbeempty'] = 'El campo no puede estar vacío'; |
| | | $messages['setactivateerror'] = 'Imposible activar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor'; |
| | | $messages['setdeactivateerror'] = 'Imposible desactivar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor'; |
| | | $messages['setdeleteerror'] = 'Imposible borrar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor'; |
| | | $messages['setactivated'] = 'Conjunto de filtros activado satisfactoriamente'; |
| | | $messages['setdeactivated'] = 'Conjunto de filtros desactivado satisfactoriamente'; |
| | | $messages['setdeleted'] = 'Conjunto de filtros borrado satisfactoriamente'; |
| | | $messages['setdeleteconfirm'] = '¿Está seguro de que desea borrar el conjunto de filtros seleccionado?'; |
| | | $messages['setcreateerror'] = 'Imposible crear el conjunto de filtros. Ha ocurrido un error en el servidor'; |
| | | $messages['setcreated'] = 'Conjunto de filtros creado satisfactoriamente'; |
| | | $messages['emptyname'] = 'Imposible crear el conjunto de filtros. Sin nombre'; |
| | | $messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre demasiado largo' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filtrid'; |
| | | $labels['managefilters'] = 'Halda sisenevate kirjade filtreid'; |
| | | $labels['filtername'] = 'Filtri nimi'; |
| | | $labels['newfilter'] = 'Uus filter'; |
| | | $labels['filteradd'] = 'Lisa filter'; |
| | | $labels['filterdel'] = 'Kustuta filter'; |
| | | $labels['moveup'] = 'Liiguta üles'; |
| | | $labels['movedown'] = 'Liiguta alla'; |
| | | $labels['filterallof'] = 'vastab kõikidele järgnevatele reeglitele'; |
| | | $labels['filteranyof'] = 'vastab mõnele järgnevatest reeglitest'; |
| | | $labels['filterany'] = 'kõik kirjad'; |
| | | $labels['filtercontains'] = 'sisaldab'; |
| | | $labels['filternotcontains'] = 'ei sisalda'; |
| | | $labels['filteris'] = 'on võrdne kui'; |
| | | $labels['filterisnot'] = 'ei ole võrdne kui'; |
| | | $labels['filterexists'] = 'on olemas'; |
| | | $labels['filternotexists'] = 'pole olemas'; |
| | | $labels['filterunder'] = 'alt'; |
| | | $labels['filterover'] = 'üle'; |
| | | $labels['addrule'] = 'Lisa reegel'; |
| | | $labels['delrule'] = 'Kustuta reegel'; |
| | | $labels['messagemoveto'] = 'Liiguta kiri'; |
| | | $labels['messageredirect'] = 'Suuna kiri ümber'; |
| | | $labels['messagereply'] = 'Vasta kirjaga'; |
| | | $labels['messagedelete'] = 'Kustuta kiri'; |
| | | $labels['messagediscard'] = 'Viska ära teatega'; |
| | | $labels['messagesrules'] = 'Siseneva kirja puhul, mis:'; |
| | | $labels['messagesactions'] = '...käivita järgnevad tegevused:'; |
| | | $labels['add'] = 'Lisa'; |
| | | $labels['del'] = 'Kustuta'; |
| | | $labels['sender'] = 'Saatja'; |
| | | $labels['recipient'] = 'Saaja'; |
| | | $labels['vacationaddresses'] = 'Lisanimekiri saaja e-posti aadressidest (komadega eraldatud):'; |
| | | $labels['vacationdays'] = 'Kui tihti kirju saata (päevades):'; |
| | | $labels['vacationreason'] = 'Kirja sisu (puhkuse põhjus):'; |
| | | $labels['rulestop'] = 'Peata reeglite otsimine'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Tundmatu serveri tõrge'; |
| | | $messages['filterconnerror'] = 'Managesieve serveriga ühendumine nurjus'; |
| | | $messages['filterdeleteerror'] = 'Filtri kustutamine nurjus. Ilmnes serveri tõrge.'; |
| | | $messages['filterdeleted'] = 'Filter edukalt kustutatud'; |
| | | $messages['filterdeleteconfirm'] = 'Soovid valitud filtri kustutada?'; |
| | | $messages['filtersaved'] = 'Filter edukalt salvestatud'; |
| | | $messages['filtersaveerror'] = 'Filtri salvestamine nurjus. Ilmnes serveri tõrge.'; |
| | | $messages['ruledeleteconfirm'] = 'Soovid valitud reegli kustutada?'; |
| | | $messages['actiondeleteconfirm'] = 'Soovid valitud tegevuse kustutada?'; |
| | | $messages['forbiddenchars'] = 'Väljal on lubamatu märk'; |
| | | $messages['cannotbeempty'] = 'Väli ei või tühi olla'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Suodattimet'; |
| | | $labels['managefilters'] = 'Muokkaa saapuvan sähköpostin suodattimia'; |
| | | $labels['filtername'] = 'Suodattimen nimi'; |
| | | $labels['newfilter'] = 'Uusi suodatin'; |
| | | $labels['filteradd'] = 'Lisää suodatin'; |
| | | $labels['filterdel'] = 'Poista suodatin'; |
| | | $labels['moveup'] = 'Siirrä ylös'; |
| | | $labels['movedown'] = 'Siirrä alas'; |
| | | $labels['filterallof'] = 'Täsmää kaikkien sääntöjen mukaan'; |
| | | $labels['filteranyof'] = 'Täsmää minkä tahansa sääntöjen mukaan'; |
| | | $labels['filterany'] = 'Kaikki viestit'; |
| | | $labels['filtercontains'] = 'Sisältää'; |
| | | $labels['filternotcontains'] = 'Ei Sisällä'; |
| | | $labels['filteris'] = 'on samanlainen kuin'; |
| | | $labels['filterisnot'] = 'ei ole samanlainen kuin'; |
| | | $labels['filterexists'] = 'on olemassa'; |
| | | $labels['filternotexists'] = 'ei ole olemassa'; |
| | | $labels['filterunder'] = 'alla'; |
| | | $labels['filterover'] = 'yli'; |
| | | $labels['addrule'] = 'Lisää sääntö'; |
| | | $labels['delrule'] = 'Poista sääntö'; |
| | | $labels['messagemoveto'] = 'Siirrä viesti'; |
| | | $labels['messageredirect'] = 'Uudelleen ohjaa viesti'; |
| | | $labels['messagereply'] = 'Vastaa viestin kanssa'; |
| | | $labels['messagedelete'] = 'Poista viesti'; |
| | | $labels['messagediscard'] = 'Hylkää viesti'; |
| | | $labels['messagesrules'] = 'Saapuva sähköposti'; |
| | | $labels['messagesactions'] = 'Suorita seuraavat tapahtumat'; |
| | | $labels['add'] = 'Lisää'; |
| | | $labels['del'] = 'Poista'; |
| | | $labels['sender'] = 'Lähettäjä'; |
| | | $labels['recipient'] = 'Vastaanottaja'; |
| | | $labels['vacationaddresses'] = 'Lähetä viesti myös seuraaviin osotteisiin (erottele pilkulla):'; |
| | | $labels['vacationdays'] = 'Kuinka monen päivän välein lähetetään uusi vastaus:'; |
| | | $labels['vacationreason'] = 'Viesti:'; |
| | | $labels['rulestop'] = 'Viimeinen sääntö'; |
| | | $labels['filterset'] = 'Suodatinlista'; |
| | | $labels['filtersetadd'] = 'Lisää suodatinlista'; |
| | | $labels['filtersetdel'] = 'Poista valittu suodatinlista'; |
| | | $labels['filtersetact'] = 'Aktivoi valittu suodatinlista'; |
| | | $labels['filtersetget'] = 'Lataa valittu suodatinlista tekstimuodossa'; |
| | | $labels['filterdef'] = 'Suodatinmääritykset'; |
| | | $labels['filtersetname'] = 'Suodatinlistan nimi'; |
| | | $labels['newfilterset'] = 'Uusi suodatinlista'; |
| | | $labels['active'] = 'aktiivinen'; |
| | | $labels['none'] = 'ei mitään'; |
| | | $labels['fromset'] = 'listasta'; |
| | | $labels['fromfile'] = 'tiedostosta'; |
| | | $labels['filterdisabled'] = 'Suodatin on poistettu käytöstä'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Tuntematon palvelin virhe'; |
| | | $messages['filterconnerror'] = 'Yhdistäminen palvelimeen epäonnistui'; |
| | | $messages['filterdeleteerror'] = 'Suodattimen poistaminen epäonnistui. Palvelin virhe'; |
| | | $messages['filterdeleted'] = 'Suodatin poistettu'; |
| | | $messages['filterdeleteconfirm'] = 'Haluatko varmasti poistaa valitut suodattimet?'; |
| | | $messages['filtersaved'] = 'Suodatin tallennettu'; |
| | | $messages['filtersaveerror'] = 'Suodattimen tallennus epäonnistui. Palvelin virhe'; |
| | | $messages['ruledeleteconfirm'] = 'Haluatko poistaa valitut säännöt?'; |
| | | $messages['actiondeleteconfirm'] = 'Haluatko poistaa valitut tapahtumat?'; |
| | | $messages['forbiddenchars'] = 'Sisältää kiellettyjä kirjaimia'; |
| | | $messages['cannotbeempty'] = 'Kenttä ei voi olla tyhjä'; |
| | | |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filtres'; |
| | | $labels['managefilters'] = 'Gestion des filtres sur les mails entrants'; |
| | | $labels['filtername'] = 'Nom du filtre'; |
| | | $labels['newfilter'] = 'Nouveau filtre'; |
| | | $labels['filteradd'] = 'Ajouter un filtre'; |
| | | $labels['filterdel'] = 'Supprimer un filtre'; |
| | | $labels['moveup'] = 'Monter'; |
| | | $labels['movedown'] = 'Descendre'; |
| | | $labels['filterallof'] = 'valident toutes les conditions suivantes'; |
| | | $labels['filteranyof'] = 'valident au moins une des conditions suivantes'; |
| | | $labels['filterany'] = 'tous les messages'; |
| | | $labels['filtercontains'] = 'contient'; |
| | | $labels['filternotcontains'] = 'ne contient pas'; |
| | | $labels['filteris'] = 'est '; |
| | | $labels['filterisnot'] = 'n\'est pas'; |
| | | $labels['filterexists'] = 'existe'; |
| | | $labels['filternotexists'] = 'n\'existe pas'; |
| | | $labels['filterunder'] = 'est plus petit que'; |
| | | $labels['filterover'] = 'est plus grand que'; |
| | | $labels['addrule'] = 'Ajouter une règle'; |
| | | $labels['delrule'] = 'Supprimer une règle'; |
| | | $labels['messagemoveto'] = 'Déplacer le message vers'; |
| | | $labels['messageredirect'] = 'Transférer le message à'; |
| | | $labels['messagereply'] = 'Répondre avec le message'; |
| | | $labels['messagedelete'] = 'Supprimer le message'; |
| | | $labels['messagediscard'] = 'Rejeter avec le message'; |
| | | $labels['messagesrules'] = 'Pour les mails entrants:'; |
| | | $labels['messagesactions'] = '...exécuter les actions suivantes:'; |
| | | $labels['add'] = 'Ajouter'; |
| | | $labels['del'] = 'Supprimer'; |
| | | $labels['sender'] = 'Expéditeur'; |
| | | $labels['recipient'] = 'Destinataire'; |
| | | $labels['vacationaddresses'] = 'Liste des destinataires (séparés par une virgule) :'; |
| | | $labels['vacationdays'] = 'Ne pas renvoyer un message avant (jours) :'; |
| | | $labels['vacationreason'] = 'Corps du message (raison de l\'absence) :'; |
| | | $labels['rulestop'] = 'Arrêter d\'évaluer les prochaines règles'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Erreur du serveur inconnue'; |
| | | $messages['filterconnerror'] = 'Connexion au serveur Managesieve impossible'; |
| | | $messages['filterdeleteerror'] = 'Suppression du filtre impossible. Le serveur à produit une erreur'; |
| | | $messages['filterdeleted'] = 'Le filtre a bien été supprimé'; |
| | | $messages['filterdeleteconfirm'] = 'Voulez-vous vraiment supprimer le filtre sélectionné?'; |
| | | $messages['filtersaved'] = 'Le filtre a bien été enregistré'; |
| | | $messages['filtersaveerror'] = 'Enregistrement du filtre impossibe. Le serveur à produit une erreur'; |
| | | $messages['ruledeleteconfirm'] = 'Voulez-vous vraiment supprimer la règle sélectionnée?'; |
| | | $messages['actiondeleteconfirm'] = 'Voulez-vous vraiment supprimer l\'action sélectionnée?'; |
| | | $messages['forbiddenchars'] = 'Caractères interdits dans le champ'; |
| | | $messages['cannotbeempty'] = 'Le champ ne peut pas être vide'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Üzenetszűrők'; |
| | | $labels['managefilters'] = 'Bejövő üzenetek szűrői'; |
| | | $labels['filtername'] = 'Szűrő neve'; |
| | | $labels['newfilter'] = 'Új szűrő'; |
| | | $labels['filteradd'] = 'Szűrő hozzáadása'; |
| | | $labels['filterdel'] = 'Szűrő törlése'; |
| | | $labels['moveup'] = 'Mozgatás felfelé'; |
| | | $labels['movedown'] = 'Mozgatás lefelé'; |
| | | $labels['filterallof'] = 'A következők mind illeszkedjenek'; |
| | | $labels['filteranyof'] = 'A következők bármelyike illeszkedjen'; |
| | | $labels['filterany'] = 'Minden üzenet illeszkedjen'; |
| | | $labels['filtercontains'] = 'tartalmazza'; |
| | | $labels['filternotcontains'] = 'nem tartalmazza'; |
| | | $labels['filteris'] = 'megegyezik'; |
| | | $labels['filterisnot'] = 'nem egyezik meg'; |
| | | $labels['filterexists'] = 'létezik'; |
| | | $labels['filternotexists'] = 'nem létezik'; |
| | | $labels['filterunder'] = 'alatta'; |
| | | $labels['filterover'] = 'felette'; |
| | | $labels['addrule'] = 'Szabály hozzáadása'; |
| | | $labels['delrule'] = 'Szabály törlése'; |
| | | $labels['messagemoveto'] = 'Üzenet áthelyezése ide:'; |
| | | $labels['messageredirect'] = 'Üzenet továbbítása ide:'; |
| | | $labels['messagereply'] = 'Válaszüzenet küldése (autoreply)'; |
| | | $labels['messagedelete'] = 'Üzenet törlése'; |
| | | $labels['messagediscard'] = 'Válaszüzenet küldése, a levél törlése'; |
| | | $labels['messagesrules'] = 'Az adott tulajdonságú beérkezett üzenetekre:'; |
| | | $labels['messagesactions'] = '... a következő műveletek végrehajtása:'; |
| | | $labels['add'] = 'Hozzáadás'; |
| | | $labels['del'] = 'Törlés'; |
| | | $labels['sender'] = 'Feladó'; |
| | | $labels['recipient'] = 'Címzett'; |
| | | $labels['vacationaddresses'] = 'További címzettek (vesszővel elválasztva):'; |
| | | $labels['vacationdays'] = 'Válaszüzenet küldése ennyi naponként:'; |
| | | $labels['vacationreason'] = 'Levél szövege (automatikus válasz):'; |
| | | $labels['rulestop'] = 'Műveletek végrehajtásának befejezése'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Ismeretlen szerverhiba'; |
| | | $messages['filterconnerror'] = 'Nem tudok a szűrőszerverhez kapcsolódni'; |
| | | $messages['filterdeleteerror'] = 'A szűrőt nem lehet törölni, szerverhiba történt'; |
| | | $messages['filterdeleted'] = 'A szűrő törlése sikeres'; |
| | | $messages['filterdeleteconfirm'] = 'Biztosan törli ezt a szűrőt?'; |
| | | $messages['filtersaved'] = 'A szűrő mentése sikeres'; |
| | | $messages['filtersaveerror'] = 'A szűrő mentése sikertelen, szerverhiba történt'; |
| | | $messages['ruledeleteconfirm'] = 'Biztosan törli ezt a szabályt?'; |
| | | $messages['actiondeleteconfirm'] = 'Biztosan törli ezt a műveletet?'; |
| | | $messages['forbiddenchars'] = 'Érvénytelen karakter a mezőben'; |
| | | $messages['cannotbeempty'] = 'A mező nem lehet üres'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filtri'; |
| | | $labels['managefilters'] = 'Gestione dei filtri per la posta in arrivo'; |
| | | $labels['filtername'] = 'Nome del filtro'; |
| | | $labels['newfilter'] = 'Nuovo filtro'; |
| | | $labels['filteradd'] = 'Aggiungi filtro'; |
| | | $labels['filterdel'] = 'Elimina filtro'; |
| | | $labels['moveup'] = 'Sposta sopra'; |
| | | $labels['movedown'] = 'Sposta sotto'; |
| | | $labels['filterallof'] = 'che soddisfa tutte le regole seguenti'; |
| | | $labels['filteranyof'] = 'che soddisfa una qualsiasi delle regole seguenti'; |
| | | $labels['filterany'] = 'tutti i messaggi'; |
| | | $labels['filtercontains'] = 'contiene'; |
| | | $labels['filternotcontains'] = 'non contiene'; |
| | | $labels['filteris'] = 'è uguale a'; |
| | | $labels['filterisnot'] = 'è diverso da'; |
| | | $labels['filterexists'] = 'esiste'; |
| | | $labels['filternotexists'] = 'non esiste'; |
| | | $labels['filterunder'] = 'sotto'; |
| | | $labels['filterover'] = 'sopra'; |
| | | $labels['addrule'] = 'Aggiungi regola'; |
| | | $labels['delrule'] = 'Elimina regola'; |
| | | $labels['messagemoveto'] = 'Sposta il messaggio in'; |
| | | $labels['messageredirect'] = 'Inoltra il messaggio a'; |
| | | $labels['messagereply'] = 'Rispondi con il messaggio'; |
| | | $labels['messagedelete'] = 'Elimina il messaggio'; |
| | | $labels['messagediscard'] = 'Rifiuta con messaggio'; |
| | | $labels['messagesrules'] = 'Per la posta in arrivo'; |
| | | $labels['messagesactions'] = '...esegui le seguenti azioni:'; |
| | | $labels['add'] = 'Aggiungi'; |
| | | $labels['del'] = 'Elimina'; |
| | | $labels['sender'] = 'Mittente'; |
| | | $labels['recipient'] = 'Destinatario'; |
| | | $labels['vacationaddresses'] = 'Lista di indirizzi e-mail di destinatari addizionali (separati da virgola):'; |
| | | $labels['vacationdays'] = 'Ogni quanti giorni ribadire il messaggio allo stesso mittente'; |
| | | $labels['vacationreason'] = 'Corpo del messaggio (dettagli relativi all\'assenza):'; |
| | | $labels['rulestop'] = 'Non valutare le regole successive'; |
| | | $labels['filterset'] = 'Gruppi di filtri'; |
| | | $labels['filtersetadd'] = 'Aggiungi gruppo'; |
| | | $labels['filtersetdel'] = 'Cancella gruppo selezionato'; |
| | | $labels['filtersetact'] = 'Attiva gruppo selezionato'; |
| | | $labels['filtersetdeact'] = 'Disattiva gruppo selezionato'; |
| | | $labels['filtersetget'] = 'Scarica filtri come testo'; |
| | | $labels['filterdef'] = 'Definizione del filtro'; |
| | | $labels['filtersetname'] = 'Nome del Gruppo di filtri'; |
| | | $labels['newfilterset'] = 'Nuovo gruppo di filri'; |
| | | $labels['active'] = 'attivo'; |
| | | $labels['none'] = 'nessuno'; |
| | | $labels['fromset'] = 'dal set'; |
| | | $labels['fromfile'] = 'dal file'; |
| | | $labels['filterdisabled'] = 'Filtro disabilitato'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Errore sconosciuto del server'; |
| | | $messages['filterconnerror'] = 'Collegamento al server managesieve fallito'; |
| | | $messages['filterdeleteerror'] = 'Eliminazione del filtro fallita. Si è verificato un errore nel server'; |
| | | $messages['filterdeleted'] = 'Filtro eliminato con successo'; |
| | | $messages['filterdeleteconfirm'] = 'Vuoi veramente eliminare il filtro selezionato?'; |
| | | $messages['filtersaved'] = 'Filtro salvato con successo'; |
| | | $messages['filtersaveerror'] = 'Salvataggio del filtro fallito. Si è verificato un errore nel server'; |
| | | $messages['ruledeleteconfirm'] = 'Sei sicuro di voler eliminare la regola selezionata?'; |
| | | $messages['actiondeleteconfirm'] = 'Sei sicuro di voler eliminare l\'azione selezionata?'; |
| | | $messages['forbiddenchars'] = 'Caratteri non consentiti nel campo'; |
| | | $messages['cannotbeempty'] = 'Il campo non può essere vuoto'; |
| | | $messages['setactivateerror'] = 'Impossibile attivare il filtro. Errore del server'; |
| | | $messages['setdeactivateerror'] = 'Impossibile disattivare il filtro. Errore del server'; |
| | | $messages['setdeleteerror'] = 'Impossibile cancellare il filtro. Errore del server'; |
| | | $messages['setactivated'] = 'Filtro attivato'; |
| | | $messages['setdeactivated'] = 'Filtro disattivato'; |
| | | $messages['setdeleted'] = 'Filtro cancellato'; |
| | | $messages['setdeleteconfirm'] = 'Sei sicuro di voler cancellare il gruppo di filtri'; |
| | | $messages['setcreateerror'] = 'Impossibile creare il gruppo. Errore del server'; |
| | | $messages['setcreated'] = 'Gruppo di filtri creato'; |
| | | $messages['emptyname'] = 'Impossibile creare il gruppo: Inserire un nome'; |
| | | $messages['nametoolong'] = 'Impossibile creare il gruppo: Nome troppo lungo'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // EN-Revision: 3891 |
| | | |
| | | $labels['filters'] = 'フィルター'; |
| | | $labels['managefilters'] = '受信メールのフィルターの管理'; |
| | | $labels['filtername'] = 'フィルターの名前'; |
| | | $labels['newfilter'] = '新規フィルター'; |
| | | $labels['filteradd'] = 'フィルターの追加'; |
| | | $labels['filterdel'] = 'フィルターの削除'; |
| | | $labels['moveup'] = '上に移動'; |
| | | $labels['movedown'] = '下に移動'; |
| | | $labels['filterallof'] = '次のルールすべてに一致'; |
| | | $labels['filteranyof'] = '次のルールのどれかに一致'; |
| | | $labels['filterany'] = '全メッセージ'; |
| | | $labels['filtercontains'] = '含む'; |
| | | $labels['filternotcontains'] = '含まない'; |
| | | $labels['filteris'] = '次と一致する'; |
| | | $labels['filterisnot'] = '次と一致しない'; |
| | | $labels['filterexists'] = '存在する'; |
| | | $labels['filternotexists'] = '存在しない'; |
| | | $labels['filterunder'] = 'より上'; |
| | | $labels['filterover'] = 'より下'; |
| | | $labels['addrule'] = 'ルールの追加'; |
| | | $labels['delrule'] = 'ルールの削除'; |
| | | $labels['messagemoveto'] = '次にメッセージを移動する'; |
| | | $labels['messageredirect'] = '次のメールアドレスに転送 (リダイレクト)'; |
| | | $labels['messagecopyto'] = '次にメッセージをコピーする'; |
| | | $labels['messagesendcopy'] = '次にメッセージのコピーを送信する'; |
| | | $labels['messagereply'] = 'メッセージに返信する'; |
| | | $labels['messagedelete'] = 'メッセージを削除する'; |
| | | $labels['messagediscard'] = 'メッセージを破棄する'; |
| | | $labels['messagesrules'] = '受信メールへの処理:'; |
| | | $labels['messagesactions'] = '…次の操作の実行:'; |
| | | $labels['add'] = '追加'; |
| | | $labels['del'] = '削除'; |
| | | $labels['sender'] = '送信者'; |
| | | $labels['recipient'] = '受信者'; |
| | | $labels['vacationaddresses'] = '電子メール受信者の一覧を追加する (カンマ区切り):'; |
| | | $labels['vacationdays'] = 'どれ位頻繁にメッセージの送信をするか (1 日あたり):'; |
| | | $labels['vacationreason'] = 'メッセージ本文 (vacation reason):'; |
| | | $labels['rulestop'] = 'ルール評価の停止'; |
| | | $labels['filterset'] = 'フィルター セット'; |
| | | $labels['filtersetadd'] = 'フィルター セットの追加'; |
| | | $labels['filtersetdel'] = '現在のルールセット の削除'; |
| | | $labels['filtersetact'] = '現在のフィルター セットを有効にする'; |
| | | $labels['filtersetdeact'] = '現在のフィルター セットを無効にする'; |
| | | $labels['filtersetget'] = 'テキスト形式でフィルター セットをダウンロードする'; |
| | | $labels['filterdef'] = 'フィルターの定義'; |
| | | $labels['filtersetname'] = 'フィルター セットの名前'; |
| | | $labels['newfilterset'] = '新規フィルター セット'; |
| | | $labels['active'] = '有効'; |
| | | $labels['none'] = 'なし'; |
| | | $labels['fromset'] = 'セットから'; |
| | | $labels['fromfile'] = 'ファイルから'; |
| | | $labels['filterdisabled'] = 'フィルターを無効にしました。'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = '不明なサーバーのエラーです'; |
| | | $messages['filterconnerror'] = 'managesieve サーバーに接続できません。'; |
| | | $messages['filterdeleteerror'] = 'フィルターを削除できませんでした。サーバーでエラーが発生しました。'; |
| | | $messages['filterdeleted'] = 'フィルターの削除に成功しました。'; |
| | | $messages['filtersaved'] = 'フィルターの保存に成功しました。'; |
| | | $messages['filtersaveerror'] = 'フィルターの保存に失敗しました。サーバーでエラーが発生しました。'; |
| | | $messages['filterdeleteconfirm'] = '本当に選択したフィルターを削除しますか?'; |
| | | $messages['ruledeleteconfirm'] = '本当に選択したルールを削除しますか?'; |
| | | $messages['actiondeleteconfirm'] = '本当に選択した操作を削除しますか?'; |
| | | $messages['forbiddenchars'] = '項目に禁止文字があります。'; |
| | | $messages['cannotbeempty'] = '空にできませんでした'; |
| | | $messages['setactivateerror'] = '選択したフィルター セットの有効化に失敗しました。サーバーでエラーが発生しました。'; |
| | | $messages['setdeactivateerror'] = '選択したフィルター セットの無効化に失敗しました。サーバーでエラーが発生しました。'; |
| | | $messages['setdeleteerror'] = '選択したフィルター セットの削除に失敗しました。サーバーでエラーが発生しました。'; |
| | | $messages['setactivated'] = 'フィルターセットの有効化に成功しました。'; |
| | | $messages['setdeactivated'] = 'フィルターセットの無効化に成功しました。'; |
| | | $messages['setdeleted'] = 'フィルターセットの削除に成功しました。'; |
| | | $messages['setdeleteconfirm'] = '本当に選択したフィルター セットを削除しますか?'; |
| | | $messages['setcreateerror'] = 'フィルター セットの作成に失敗しました。サーバーでエラーが発生しました。'; |
| | | $messages['setcreated'] = 'フィルター セットの作成に成功しました。'; |
| | | $messages['emptyname'] = 'フィルター セットの作成に失敗しました。名前が空です。'; |
| | | $messages['nametoolong'] = 'フィルター セットの作成に失敗しました。名前が長すぎます。'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filtre'; |
| | | $labels['managefilters'] = 'Rediger filter for innkommende e-post'; |
| | | $labels['filtername'] = 'Filternavn'; |
| | | $labels['newfilter'] = 'Nytt filter'; |
| | | $labels['filteradd'] = 'Legg til filter'; |
| | | $labels['filterdel'] = 'Slett filter'; |
| | | $labels['moveup'] = 'Flytt opp'; |
| | | $labels['movedown'] = 'Flytt ned'; |
| | | $labels['filterallof'] = 'som treffer alle følgende regler'; |
| | | $labels['filteranyof'] = 'som treffer en av følgende regler'; |
| | | $labels['filterany'] = 'og alle meldinger'; |
| | | $labels['filtercontains'] = 'inneholder'; |
| | | $labels['filternotcontains'] = 'ikke innehold'; |
| | | $labels['filteris'] = 'er'; |
| | | $labels['filterisnot'] = 'ikke er'; |
| | | $labels['filterexists'] = 'eksisterer'; |
| | | $labels['filternotexists'] = 'ikke eksisterer'; |
| | | $labels['filterunder'] = 'under'; |
| | | $labels['filterover'] = 'over'; |
| | | $labels['addrule'] = 'Legg til regel'; |
| | | $labels['delrule'] = 'Slett regel'; |
| | | $labels['messagemoveto'] = 'Flytt meldingen til'; |
| | | $labels['messageredirect'] = 'Videresend meldingen til'; |
| | | $labels['messagereply'] = 'Svar med melding'; |
| | | $labels['messagedelete'] = 'Slett melding'; |
| | | $labels['messagediscard'] = 'Avvis med melding'; |
| | | $labels['messagesrules'] = 'For innkommende e-post'; |
| | | $labels['messagesactions'] = '...gjør følgende'; |
| | | $labels['add'] = 'Legg til'; |
| | | $labels['del'] = 'Slett'; |
| | | $labels['sender'] = 'Avsender'; |
| | | $labels['recipient'] = 'Mottaker'; |
| | | $labels['vacationaddresses'] = 'Liste med mottakeradresser (adskilt med komma):'; |
| | | $labels['vacationdays'] = 'Periode mellom meldinger (i dager):'; |
| | | $labels['vacationreason'] = 'Innhold (begrunnelse for fravær)'; |
| | | $labels['rulestop'] = 'Stopp evaluering av regler'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Ukjent problem med tjener'; |
| | | $messages['filterconnerror'] = 'Kunne ikke koble til MANAGESIEVE-tjener'; |
| | | $messages['filterdeleteerror'] = 'Kunne ikke slette filter. Det dukket opp en feil på tjeneren.'; |
| | | $messages['filterdeleted'] = 'Filteret er blitt slettet'; |
| | | $messages['filterconfirmdelete'] = 'Er du sikker på at du vil slette følgende filter?'; |
| | | $messages['filtersaved'] = 'Filter er blitt lagret'; |
| | | $messages['filtersaveerror'] = 'Kunne ikke lagre filteret. Det dukket opp en feil på tjeneren.'; |
| | | $messages['ruledeleteconfirm'] = 'Er du sikker på at du vil slette valgte regel?'; |
| | | $messages['actiondeleteconfirm'] = 'Er du sikker på at du vil slette valgte hendelse?'; |
| | | $messages['forbiddenchars'] = 'Ugyldige tegn i felt'; |
| | | $messages['cannotbeempty'] = 'Feltet kan ikke stå tomt'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filters'; |
| | | $labels['managefilters'] = 'Beheer inkomende mail filters'; |
| | | $labels['filtername'] = 'Filternaam'; |
| | | $labels['newfilter'] = 'Nieuw filter'; |
| | | $labels['filteradd'] = 'Filter toevoegen'; |
| | | $labels['filterdel'] = 'Filter verwijderen'; |
| | | $labels['moveup'] = 'Omhoog'; |
| | | $labels['movedown'] = 'Omlaag'; |
| | | $labels['filterallof'] = 'die voldoen aan alle volgende regels'; |
| | | $labels['filteranyof'] = 'die voldoen aan een van de volgende regels'; |
| | | $labels['filterany'] = 'alle berichten'; |
| | | $labels['filtercontains'] = 'bevat'; |
| | | $labels['filternotcontains'] = 'bevat niet'; |
| | | $labels['filteris'] = 'is gelijk aan'; |
| | | $labels['filterisnot'] = 'is niet gelijk aan'; |
| | | $labels['filterexists'] = 'bestaat'; |
| | | $labels['filternotexists'] = 'bestaat niet'; |
| | | $labels['filterunder'] = 'onder'; |
| | | $labels['filterover'] = 'over'; |
| | | $labels['addrule'] = 'Regel toevoegen'; |
| | | $labels['delrule'] = 'Regel verwijderen'; |
| | | $labels['messagemoveto'] = 'Verplaats bericht naar'; |
| | | $labels['messageredirect'] = 'Redirect bericht naar'; |
| | | $labels['messagereply'] = 'Beantwoord met bericht'; |
| | | $labels['messagedelete'] = 'Verwijder bericht'; |
| | | $labels['messagediscard'] = 'Wijs af met bericht'; |
| | | $labels['messagesrules'] = 'Voor binnenkomende e-mail'; |
| | | $labels['messagesactions'] = '...voer de volgende acties uit'; |
| | | $labels['add'] = 'Toevoegen'; |
| | | $labels['del'] = 'Verwijderen'; |
| | | $labels['sender'] = 'Afzender'; |
| | | $labels['recipient'] = 'Ontvanger'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Onbekende fout'; |
| | | $messages['filterconnerror'] = 'Kan geen verbinding maken met de managesieve server'; |
| | | $messages['filterdeleteerror'] = 'Kan filter niet verwijderen. Er is een fout opgetreden'; |
| | | $messages['filterdeleted'] = 'Filter succesvol verwijderd'; |
| | | $messages['filterdeleteconfirm'] = 'Weet je zeker dat je het geselecteerde filter wilt verwijderen?'; |
| | | $messages['filtersaved'] = 'Filter succesvol opgeslagen'; |
| | | $messages['filtersaveerror'] = 'Kan filter niet opslaan. Er is een fout opgetreden.'; |
| | | $messages['ruledeleteconfirm'] = 'Weet je zeker dat je de geselecteerde regel wilt verwijderen?'; |
| | | $messages['actiondeleteconfirm'] = 'Weet je zeker dat je de geselecteerde actie wilt verwijderen?'; |
| | | $messages['forbiddenchars'] = 'Verboden karakters in het veld'; |
| | | $messages['cannotbeempty'] = 'Veld mag niet leeg zijn'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filtry'; |
| | | $labels['managefilters'] = 'Zarządzaj filtrami wiadomości przychodzących'; |
| | | $labels['filtername'] = 'Nazwa filtru'; |
| | | $labels['newfilter'] = 'Nowy filtr'; |
| | | $labels['filteradd'] = 'Dodaj filtr'; |
| | | $labels['filterdel'] = 'Usuń filtr'; |
| | | $labels['moveup'] = 'Przenieś wyżej'; |
| | | $labels['movedown'] = 'Przenieś niżej'; |
| | | $labels['filterallof'] = 'spełniające wszystkie poniższe kryteria'; |
| | | $labels['filteranyof'] = 'spełniające dowolne z poniższych kryteriów'; |
| | | $labels['filterany'] = 'wszystkich'; |
| | | $labels['filtercontains'] = 'zawiera'; |
| | | $labels['filternotcontains'] = 'nie zawiera'; |
| | | $labels['filteris'] = 'jest równe'; |
| | | $labels['filterisnot'] = 'nie jest równe'; |
| | | $labels['filterexists'] = 'istnieje'; |
| | | $labels['filternotexists'] = 'nie istnieje'; |
| | | $labels['filterunder'] = 'poniżej'; |
| | | $labels['filterover'] = 'ponad'; |
| | | $labels['addrule'] = 'Dodaj regułę'; |
| | | $labels['delrule'] = 'Usuń regułę'; |
| | | $labels['messagemoveto'] = 'Przenieś wiadomość do'; |
| | | $labels['messageredirect'] = 'Przekaż wiadomość na konto'; |
| | | $labels['messagereply'] = 'Odpowiedz wiadomością o treści'; |
| | | $labels['messagecopyto'] = 'Skopiuj wiadomość do'; |
| | | $labels['messagesendcopy'] = 'Wyślij kopię do'; |
| | | $labels['messagedelete'] = 'Usuń wiadomość'; |
| | | $labels['messagediscard'] = 'Odrzuć z komunikatem'; |
| | | $labels['messagesrules'] = 'W stosunku do przychodzących wiadomości:'; |
| | | $labels['messagesactions'] = '...wykonaj następujące czynności:'; |
| | | $labels['add'] = 'Dodaj'; |
| | | $labels['del'] = 'Usuń'; |
| | | $labels['sender'] = 'Nadawca'; |
| | | $labels['recipient'] = 'Odbiorca'; |
| | | $labels['rulestop'] = 'Przerwij przetwarzanie reguł'; |
| | | $labels['vacationdays'] = 'Częstotliwość wysyłania wiadomości (w dniach):'; |
| | | $labels['vacationaddresses'] = 'Lista dodatkowych adresów odbiorców (oddzielonych przecinkami):'; |
| | | $labels['vacationreason'] = 'Treść (przyczyna nieobecności):'; |
| | | $labels['filterset'] = 'Zbiór filtrów'; |
| | | $labels['filtersetadd'] = 'Dodaj zbiór filtrów'; |
| | | $labels['filtersetdel'] = 'Usuń bierzący zbiór filtrów'; |
| | | $labels['filtersetact'] = 'Aktywuj bierzący zbiór filtrów'; |
| | | $labels['filtersetdeact'] = 'Deaktywuj bierzący zbiór filtrów'; |
| | | $labels['filtersetget'] = 'Pobierz bierzący zbiór filtrów w formacie tekstowym'; |
| | | $labels['filterdef'] = 'Definicja filtra'; |
| | | $labels['filtersetname'] = 'Nazwa zbioru'; |
| | | $labels['newfilterset'] = 'Nowy zbiór filtrów'; |
| | | $labels['active'] = 'aktywny'; |
| | | $labels['none'] = 'brak'; |
| | | $labels['fromset'] = 'ze zbioru'; |
| | | $labels['fromfile'] = 'z pliku'; |
| | | $labels['filterdisabled'] = 'Filtr wyłączony'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Nieznany błąd serwera'; |
| | | $messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem managesieve'; |
| | | $messages['filterdeleteerror'] = 'Nie można usunąć filtra. Wystąpił błąd serwera'; |
| | | $messages['filterdeleted'] = 'Filtr został usunięty pomyślnie'; |
| | | $messages['filterdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany filtr?'; |
| | | $messages['filtersaved'] = 'Filtr został zapisany pomyślnie'; |
| | | $messages['filtersaveerror'] = 'Nie można zapisać filtra. Wystąpił błąd serwera.'; |
| | | $messages['ruledeleteconfirm'] = 'Czy na pewno chcesz usunąć wybraną regułę?'; |
| | | $messages['actiondeleteconfirm'] = 'Czy na pewno usunąć wybraną akcję?'; |
| | | $messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki'; |
| | | $messages['cannotbeempty'] = 'Pole nie może być puste'; |
| | | $messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera'; |
| | | $messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera'; |
| | | $messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera'; |
| | | $messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie'; |
| | | $messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie'; |
| | | $messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie'; |
| | | $messages['setdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany zbiór filtrów?'; |
| | | $messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera'; |
| | | $messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie'; |
| | | $messages['emptyname'] = 'Nie można utworzyć zbioru filtrów. Pusta nazwa zbioru'; |
| | | $messages['nametoolong'] = 'Nie można utworzyć zbioru filtrów. Nazwa zbyt długa' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filtros'; |
| | | $labels['managefilters'] = 'Gerenciar filtros de entrada de e-mail'; |
| | | $labels['filtername'] = 'Nome do filtro'; |
| | | $labels['newfilter'] = 'Novo filtro'; |
| | | $labels['filteradd'] = 'Adicionar filtro'; |
| | | $labels['filterdel'] = 'Excluir filtro'; |
| | | $labels['moveup'] = 'Mover para cima'; |
| | | $labels['movedown'] = 'Mover para baixo'; |
| | | $labels['filterallof'] = 'casando todas as seguintes regras'; |
| | | $labels['filteranyof'] = 'casando qualquer das seguintes regras'; |
| | | $labels['filterany'] = 'todas as mensagens'; |
| | | $labels['filtercontains'] = 'contem'; |
| | | $labels['filternotcontains'] = 'não contem'; |
| | | $labels['filteris'] = 'é igual a'; |
| | | $labels['filterisnot'] = 'não é igual a'; |
| | | $labels['filterexists'] = 'existe'; |
| | | $labels['filternotexists'] = 'não existe'; |
| | | $labels['filterunder'] = 'inferior a'; |
| | | $labels['filterover'] = 'superior a'; |
| | | $labels['addrule'] = 'Adicionar regra'; |
| | | $labels['delrule'] = 'Excluir regra'; |
| | | $labels['messagemoveto'] = 'Mover mensagem para'; |
| | | $labels['messageredirect'] = 'Redirecionar mensagem para'; |
| | | $labels['messagereply'] = 'Responder com mensagem'; |
| | | $labels['messagedelete'] = 'Excluir mensagem'; |
| | | $labels['messagediscard'] = 'Descartar com mensagem'; |
| | | $labels['messagesrules'] = 'Para e-mails recebidos:'; |
| | | $labels['messagesactions'] = '...execute as seguintes ações:'; |
| | | $labels['add'] = 'Adicionar'; |
| | | $labels['del'] = 'Excluir'; |
| | | $labels['sender'] = 'Remetente'; |
| | | $labels['recipient'] = 'Destinatário'; |
| | | $labels['vacationaddresses'] = 'Lista adicional de e-mails de remetente (separado por vírgula):'; |
| | | $labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):'; |
| | | $labels['vacationreason'] = 'Corpo da mensagem (motivo de férias):'; |
| | | $labels['rulestop'] = 'Parar de avaliar regras'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Erro desconhecido de servidor'; |
| | | $messages['filterconnerror'] = 'Não foi possível conectar ao servidor managesieve'; |
| | | $messages['filterdeleteerror'] = 'Não foi possível excluir filtro. Occorreu um erro de servidor'; |
| | | $messages['filterdeleted'] = 'Filtro excluído com sucesso'; |
| | | $messages['filterdeleteconfirm'] = 'Deseja realmente excluir o filtro selecionado?'; |
| | | $messages['filtersaved'] = 'Filtro gravado com sucesso'; |
| | | $messages['filtersaveerror'] = 'Não foi possível gravar filtro. Occoreu um erro de servidor.'; |
| | | $messages['ruledeleteconfirm'] = 'Deseja realmente excluir a regra selecionada?'; |
| | | $messages['actiondeleteconfirm'] = 'Deseja realmente excluir a ação selecionada?'; |
| | | $messages['forbiddenchars'] = 'Caracteres não permitidos no campo'; |
| | | $messages['cannotbeempty'] = 'Campo não pode ficar em branco'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Filtros'; |
| | | $labels['managefilters'] = 'Gerir filtros de recepção de mails'; |
| | | $labels['filtername'] = 'Nome do filtro'; |
| | | $labels['newfilter'] = 'Novo filtro'; |
| | | $labels['filteradd'] = 'Adicionar filtro'; |
| | | $labels['filterdel'] = 'Eliminar filtro'; |
| | | $labels['moveup'] = 'Mover para cima'; |
| | | $labels['movedown'] = 'Mover para baixo'; |
| | | $labels['filterallof'] = 'corresponder a todas as seguintes regras'; |
| | | $labels['filteranyof'] = 'corresponder a qualquer das seguintes regras'; |
| | | $labels['filterany'] = 'todas as mensagens'; |
| | | $labels['filtercontains'] = 'contém'; |
| | | $labels['filternotcontains'] = 'não contém'; |
| | | $labels['filteris'] = 'é igual a'; |
| | | $labels['filterisnot'] = 'não é igual a'; |
| | | $labels['filterexists'] = 'existe'; |
| | | $labels['filternotexists'] = 'não existe'; |
| | | $labels['filterunder'] = 'inferior a'; |
| | | $labels['filterover'] = 'superior a'; |
| | | $labels['addrule'] = 'Adicionar regra'; |
| | | $labels['delrule'] = 'Eliminar regra'; |
| | | $labels['messagemoveto'] = 'Mover mensagem para'; |
| | | $labels['messageredirect'] = 'Redireccionar mensagem para'; |
| | | $labels['messagecopyto'] = 'Copiar mensagem para'; |
| | | $labels['messagesendcopy'] = 'Enviar cópia da mensagem para'; |
| | | $labels['messagereply'] = 'Responder com mensagem'; |
| | | $labels['messagedelete'] = 'Eliminar mensagem'; |
| | | $labels['messagediscard'] = 'Descartar com mensagem'; |
| | | $labels['messagesrules'] = 'Para mensagens recebidas:'; |
| | | $labels['messagesactions'] = '...executar as seguintes ações:'; |
| | | $labels['add'] = 'Adicionar'; |
| | | $labels['del'] = 'Eliminar'; |
| | | $labels['sender'] = 'Remetente'; |
| | | $labels['recipient'] = 'Destinatário'; |
| | | $labels['vacationaddresses'] = 'Lista complementar de destinatário de e-mails (separado por vírgula):'; |
| | | $labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):'; |
| | | $labels['vacationreason'] = 'Corpo da mensagem (motivo de férias):'; |
| | | $labels['rulestop'] = 'Parar execução de regras'; |
| | | $labels['filterset'] = 'Conjunto de filtros'; |
| | | $labels['filtersetadd'] = 'Adicionar conjunto de filtros'; |
| | | $labels['filtersetdel'] = 'Eliminar conjunto de filtros actual'; |
| | | $labels['filtersetact'] = 'Activar conjunto de filtros actual'; |
| | | $labels['filtersetdeact'] = 'Desactivar conjunto de filtros actual'; |
| | | $labels['filtersetget'] = 'Importar conjunto de filtros no formato de texto'; |
| | | $labels['filterdef'] = 'Definições para filtros'; |
| | | $labels['filtersetname'] = 'Nome conjunto de filtros'; |
| | | $labels['newfilterset'] = 'Novo conjunto de filtros'; |
| | | $labels['active'] = 'activo'; |
| | | $labels['none'] = 'nenhum'; |
| | | $labels['fromset'] = 'do conjunto'; |
| | | $labels['fromfile'] = 'do ficheiro'; |
| | | $labels['filterdisabled'] = 'Filtro inactivo'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Ocorreu um erro desconhecido no servidor.'; |
| | | $messages['filterconnerror'] = 'Não foi possível ligar ao servidor ManageSieve.'; |
| | | $messages['filterdeleteerror'] = 'Não foi possível eliminar o filtro. Ocorreu um erro no servidor.'; |
| | | $messages['filterdeleted'] = 'Filtro eliminado com sucesso.'; |
| | | $messages['filterdeleteconfirm'] = 'Deseja realmente eliminar o filtro seleccionado?'; |
| | | $messages['filtersaved'] = 'Filtro guardado com sucesso.'; |
| | | $messages['filtersaveerror'] = 'Não foi possível guardar o filtro. Occoreu um erro no servidor.'; |
| | | $messages['ruledeleteconfirm'] = 'Deseja realmente eliminar a regra seleccionada?'; |
| | | $messages['actiondeleteconfirm'] = 'Deseja realmente eliminar a acção seleccionada?'; |
| | | $messages['forbiddenchars'] = 'Caracteres não permitidos no campo'; |
| | | $messages['cannotbeempty'] = 'Campo não pode ficar em branco'; |
| | | $messages['setactivateerror'] = 'Não foi possível activar os filtros seleccionados. Occoreu um erro no servidor.'; |
| | | $messages['setdeactivateerror'] = 'Não foi possível desactivar os filtros seleccionados. Occoreu um erro no servidor.'; |
| | | $messages['setdeleteerror'] = 'Não foi possível eliminar os filtros seleccionados. Occoreu um erro no servidor.'; |
| | | $messages['setactivated'] = 'Filtros activados com sucesso.'; |
| | | $messages['setdeactivated'] = 'Filtros desactivados com sucesso.'; |
| | | $messages['setdeleted'] = 'Filtros eliminados com sucesso.'; |
| | | $messages['setdeleteconfirm'] = 'Tem a certeza que quer eliminar os filtros seleccionados?'; |
| | | $messages['setcreateerror'] = 'Não foi possível criar o filtro. Ocorreu um erro no servidor.'; |
| | | $messages['setcreated'] = 'Filtros criados com sucesso'; |
| | | $messages['emptyname'] = 'Não foi possível criar o filtro. Tem de indicar um nome para o filtro.'; |
| | | $messages['nametoolong'] = 'Não foi possível criar o filtro. O nome do filtro é demasiado grande.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | $labels['filters'] = 'Фильтры'; |
| | | $labels['managefilters'] = 'Управление фильтрами для входящей почты'; |
| | | $labels['filtername'] = 'Название фильтра'; |
| | | $labels['newfilter'] = 'Новый фильтр'; |
| | | $labels['filteradd'] = 'Добавить фильтр'; |
| | | $labels['filterdel'] = 'Удалить фильтр'; |
| | | $labels['moveup'] = 'Сдвинуть вверх'; |
| | | $labels['movedown'] = 'Сдвинуть вниз'; |
| | | $labels['filterallof'] = 'соответствует всем указанным правилам'; |
| | | $labels['filteranyof'] = 'соответствует любому из указанных правил'; |
| | | $labels['filterany'] = 'все сообщения'; |
| | | $labels['filtercontains'] = 'содержит'; |
| | | $labels['filternotcontains'] = 'не содержит'; |
| | | $labels['filteris'] = 'соответствует'; |
| | | $labels['filterisnot'] = 'не соответствует'; |
| | | $labels['filterexists'] = 'существует'; |
| | | $labels['filternotexists'] = 'не существует'; |
| | | $labels['filterunder'] = 'под'; |
| | | $labels['filterover'] = 'на'; |
| | | $labels['addrule'] = 'Добавить правило'; |
| | | $labels['delrule'] = 'Удалить правило'; |
| | | $labels['messagemoveto'] = 'Переместить сообщение в'; |
| | | $labels['messageredirect'] = 'Перенаправить сообщение на '; |
| | | $labels['messagereply'] = 'Ответить с сообщением'; |
| | | $labels['messagedelete'] = 'Удалить сообщение'; |
| | | $labels['messagediscard'] = 'Отбросить с сообщением'; |
| | | $labels['messagesrules'] = 'Для входящей почты:'; |
| | | $labels['messagesactions'] = '...выполнить следующие действия:'; |
| | | $labels['add'] = 'Добавить'; |
| | | $labels['del'] = 'Удалить'; |
| | | $labels['sender'] = 'Отправитель'; |
| | | $labels['recipient'] = 'Получатель'; |
| | | $labels['vacationaddresses'] = 'Список дополнительных адресов получателя (разделённых запятыми):'; |
| | | $labels['vacationdays'] = 'Как часто отправлять сообщения (в днях):'; |
| | | $labels['vacationreason'] = 'Текст сообщения (причина отсутствия):'; |
| | | $labels['rulestop'] = 'Закончить выполнение'; |
| | | $labels['filterset'] = 'Набор фильтров'; |
| | | $labels['filtersetadd'] = 'Добавить набор фильтров'; |
| | | $labels['filtersetdel'] = 'Удалить текущий набор фильтров'; |
| | | $labels['filtersetact'] = 'Активировать текущий набор фильтров'; |
| | | $labels['filtersetget'] = 'Скачать набор фильтров в виде текста'; |
| | | $labels['filterdef'] = 'Описание фильтра'; |
| | | $labels['filtersetname'] = 'Название набора фильтров'; |
| | | $labels['newfilterset'] = 'Новый набор фильтров'; |
| | | $labels['active'] = 'активный'; |
| | | $labels['none'] = 'пустой'; |
| | | $labels['fromset'] = 'из набора'; |
| | | $labels['fromfile'] = 'из файла'; |
| | | $labels['filterdisabled'] = 'Отключить фильтр'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Неизвестная ошибка сервера'; |
| | | $messages['filterconnerror'] = 'Невозможно подсоединится к серверу фильтров'; |
| | | $messages['filterdeleteerror'] = 'Невозможно удалить фильтр. Ошибка сервера'; |
| | | $messages['filterdeleted'] = 'Фильтр успешно удалён'; |
| | | $messages['filterdeleteconfirm'] = 'Вы действительно хотите удалить фильтр?'; |
| | | $messages['filtersaved'] = 'Фильтр успешно сохранён'; |
| | | $messages['filtersaveerror'] = 'Невозможно сохранить фильтр. Ошибка сервера'; |
| | | $messages['ruledeleteconfirm'] = 'Вы уверенны, что хотите удалить это правило?'; |
| | | $messages['actiondeleteconfirm'] = 'Вы уверенны, что хотите удалить это действие?'; |
| | | $messages['forbiddenchars'] = 'Недопустимые символы в поле'; |
| | | $messages['cannotbeempty'] = 'Поле не может быть пустым'; |
| | | $messages['setactivateerror'] = 'Невозможно активировать выбранный набор фильтров. Ошибка сервера'; |
| | | $messages['setdeleteerror'] = 'Невозможно удалить выбранный набор фильтров. Ошибка сервера'; |
| | | $messages['setactivated'] = 'Набор фильтров успешно активирован'; |
| | | $messages['setdeleted'] = 'Набор фильтров успешно удалён'; |
| | | $messages['setdeleteconfirm'] = 'Вы уверены в том, что хотите удалить выбранный набор фильтров?'; |
| | | $messages['setcreateerror'] = 'Невозможно создать набор фильтров. Ошибка сервера'; |
| | | $messages['setcreated'] = 'Набор фильтров успешно создан'; |
| | | $messages['emptyname'] = 'Невозможно создать набор фильтров. Название не задано'; |
| | | $messages['nametoolong'] = 'Невозможно создать набор фильтров. Название слишком длинное' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | * Slovak translation for roundcube/managesieve plugin |
| | | * |
| | | * @version 1.0 |
| | | * (c) 27.06.2010 by Juraj LUTTER <juraj@lutter.sk> |
| | | */ |
| | | |
| | | $labels['filters'] = 'Filtre'; |
| | | $labels['managefilters'] = 'Správa filtrov príchádzajúcej pošty'; |
| | | $labels['filtername'] = 'Názov filtra'; |
| | | $labels['newfilter'] = 'Nový filter'; |
| | | $labels['filteradd'] = 'Pridaj filter'; |
| | | $labels['filterdel'] = 'Zmaž filter'; |
| | | $labels['moveup'] = 'Presuň vyššie'; |
| | | $labels['movedown'] = 'Presuň nižšie'; |
| | | $labels['filterallof'] = 'vyhovujúcu VŠETKÝM nasledujúcim pravidlám'; |
| | | $labels['filteranyof'] = 'vyhovujúcu ĽUBOVOĽNÉMU z nasledujúcich pravidiel'; |
| | | $labels['filterany'] = 'všetky správy'; |
| | | $labels['filtercontains'] = 'obsahuje'; |
| | | $labels['filternotcontains'] = 'neobsahuje'; |
| | | $labels['filteris'] = 'je'; |
| | | $labels['filterisnot'] = 'nie je'; |
| | | $labels['filterexists'] = 'existuje'; |
| | | $labels['filternotexists'] = 'neexistuje'; |
| | | $labels['filterunder'] = 'pod'; |
| | | $labels['filterover'] = 'nad'; |
| | | $labels['addrule'] = 'Pridaj pravidlo'; |
| | | $labels['delrule'] = 'Zmaž pravidlo'; |
| | | $labels['messagemoveto'] = 'Presuň správu do'; |
| | | $labels['messageredirect'] = 'Presmeruj správu na'; |
| | | $labels['messagereply'] = 'Pošli automatickú odpoveď'; |
| | | $labels['messagedelete'] = 'Zmaž správu'; |
| | | $labels['messagediscard'] = 'Zmaž a pošli správu na'; |
| | | $labels['messagesrules'] = 'Pre prichádzajúcu poštu'; |
| | | $labels['messagesactions'] = 'vykonaj nasledovné akcie'; |
| | | $labels['add'] = 'Pridaj'; |
| | | $labels['del'] = 'Zmaž'; |
| | | $labels['sender'] = 'Odosielateľ'; |
| | | $labels['recipient'] = 'Adresát'; |
| | | $labels['vacationaddresses'] = 'Dodatoční príjemcovia správy (oddelení čiarkami):'; |
| | | $labels['vacationdays'] = 'Počet dní medzi odoslaním správy:'; |
| | | $labels['vacationreason'] = 'Dôvod neprítomnosti:'; |
| | | $labels['rulestop'] = 'Koniec pravidiel'; |
| | | $labels['filterset'] = 'Sada filtrov'; |
| | | $labels['filtersetadd'] = 'Pridaj sadu filtrov'; |
| | | $labels['filtersetdel'] = 'Zmaž túto sadu filtrov'; |
| | | $labels['filtersetact'] = 'Aktivuj túto sadu filtrov'; |
| | | $labels['filtersetdeact'] = 'Deaktivuj túto sadu filtrov'; |
| | | $labels['filtersetget'] = 'Stiahni definíciu filtrov v textovom súbore'; |
| | | $labels['filterdef'] = 'Definícia filtra'; |
| | | $labels['filtersetname'] = 'Názov sady filtrov'; |
| | | $labels['newfilterset'] = 'Nová sada filtrov'; |
| | | $labels['active'] = 'aktívna'; |
| | | $labels['none'] = 'žiadne'; |
| | | $labels['fromset'] = 'zo sady'; |
| | | $labels['fromfile'] = 'zo súboru'; |
| | | $labels['filterdisabled'] = 'Filter zakázaný'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Neznáma chyba serveru'; |
| | | $messages['filterconnerror'] = 'Nepodarilo sa pripojiť k managesieve serveru'; |
| | | $messages['filterdeleteerror'] = 'Nepodarilo sa zmazať filter, server ohlásil chybu'; |
| | | $messages['filterdeleted'] = 'Filter bol zmazaný'; |
| | | $messages['filtersaved'] = 'Filter bol uložený'; |
| | | $messages['filtersaveerror'] = 'Nepodarilo sa uložiť filter, server ohlásil chybu'; |
| | | $messages['filterdeleteconfirm'] = 'Naozaj si prajete zmazať tento filter?'; |
| | | $messages['ruledeleteconfirm'] = 'Naozaj si prajete zamzať toto pravidlo?'; |
| | | $messages['actiondeleteconfirm'] = 'Naozaj si prajete zmazať túto akciu?'; |
| | | $messages['forbiddenchars'] = 'Pole obsahuje nepovolené znaky'; |
| | | $messages['cannotbeempty'] = 'Pole nemôže byť prázdne'; |
| | | $messages['setactivateerror'] = 'Nepodarilo sa aktivovať zvolenú sadu filtrov, server ohlásil chybu'; |
| | | $messages['setdeactivateerror'] = 'Nepodarilo sa deaktivovať zvolenú sadu filtrov, server ohlásil chybu'; |
| | | $messages['setdeleteerror'] = 'Nepodarilo sa zmazať zvolenú sadu filtrov, server ohlásil chybu'; |
| | | $messages['setactivated'] = 'Sada filtrov bola aktivovaná'; |
| | | $messages['setdeactivated'] = 'Sada filtrov bola deaktivovaná'; |
| | | $messages['setdeleted'] = 'Sada filtrov bola zmazaná'; |
| | | $messages['setdeleteconfirm'] = 'Naozaj si prajete zmazať túto sadu filtrov?'; |
| | | $messages['setcreateerror'] = 'Nepodarilo sa vytvoriť sadu filtrov, server ohlásil chybu'; |
| | | $messages['setcreated'] = 'Sada filtrov bola vytvorená'; |
| | | $messages['emptyname'] = 'Názov sady filtrov nemôže byť prázdny'; |
| | | $messages['nametoolong'] = 'Názov sady filtrov je príliš dlhý' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = 'Pravila'; |
| | | $labels['managefilters'] = 'Uredi sporočilna pravila'; |
| | | $labels['filtername'] = 'Ime pravila'; |
| | | $labels['newfilter'] = 'Novo pravilo'; |
| | | $labels['filteradd'] = 'Dodaj pravilo'; |
| | | $labels['filterdel'] = 'Izbriši pravilo'; |
| | | $labels['moveup'] = 'Pomakni se više'; |
| | | $labels['movedown'] = 'Pomakni se niže'; |
| | | $labels['filterallof'] = 'izpolnjeni morajo biti vsi pogoji'; |
| | | $labels['filteranyof'] = 'izpolnjen mora biti vsaj eden od navedenih pogojev'; |
| | | $labels['filterany'] = 'pogoj velja za vsa sporočila'; |
| | | $labels['filtercontains'] = 'vsebuje'; |
| | | $labels['filternotcontains'] = 'ne vsebuje'; |
| | | $labels['filteris'] = 'je enak/a'; |
| | | $labels['filterisnot'] = 'ni enak/a'; |
| | | $labels['filterexists'] = 'obstaja'; |
| | | $labels['filternotexists'] = 'ne obstaja'; |
| | | $labels['filterunder'] = 'pod'; |
| | | $labels['filterover'] = 'nad'; |
| | | $labels['addrule'] = 'Dodaj pravilo'; |
| | | $labels['delrule'] = 'Izbriši pravilo'; |
| | | $labels['messagemoveto'] = 'Premakni sporočilo v'; |
| | | $labels['messageredirect'] = 'Preusmeri sporočilo v'; |
| | | $labels['messagereply'] = 'Odgovori s sporočilom'; |
| | | $labels['messagedelete'] = 'Izbriši sporočilo'; |
| | | $labels['messagediscard'] = 'Zavrži s sporočilom'; |
| | | $labels['messagesrules'] = 'Določi pravila za dohodno pošto:'; |
| | | $labels['messagesactions'] = '...izvrši naslednja dejanja:'; |
| | | $labels['add'] = 'Dodaj'; |
| | | $labels['del'] = 'Izbriši'; |
| | | $labels['sender'] = 'Pošiljatelj'; |
| | | $labels['recipient'] = 'Prejemnik'; |
| | | $labels['vacationaddresses'] = 'Dodaten seznam naslovov prejemnikov (ločenih z vejico):'; |
| | | $labels['vacationdays'] = 'Kako pogosto naj bodo sporočila poslana (v dnevih):'; |
| | | $labels['vacationreason'] = 'Vsebina sporočila (vzrok za odsotnost):'; |
| | | $labels['rulestop'] = 'Prekini z izvajanjem pravil'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Prišlo je do neznane napake.'; |
| | | $messages['filterconnerror'] = 'Povezave s strežnikom (managesieve) ni bilo mogoče vzpostaviti'; |
| | | $messages['filterdeleteerror'] = 'Pravila ni bilo mogoče izbrisati. Prišlo je do napake.'; |
| | | $messages['filterdeleted'] = 'Pravilo je bilo uspešno izbrisano.'; |
| | | $messages['filterdeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano pravilo?'; |
| | | $messages['filtersaved'] = 'Pravilo je bilo uspešno shranjeno'; |
| | | $messages['filtersaveerror'] = 'Pravilo ni bilo shranjeno. Prišlo je do napake.'; |
| | | $messages['ruledeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano pravilo?'; |
| | | $messages['actiondeleteconfirm'] = 'Ste prepričani, da želite izbrisati izbrano dejanje?'; |
| | | $messages['forbiddenchars'] = 'V polju so neveljavni znaki'; |
| | | $messages['cannotbeempty'] = 'Polje ne sme biti prazno'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Filter'; |
| | | $labels['managefilters'] = 'Administrera filter'; |
| | | $labels['filtername'] = 'Filternamn'; |
| | | $labels['newfilter'] = 'Nytt filter'; |
| | | $labels['filteradd'] = 'Lägg till filter'; |
| | | $labels['filterdel'] = 'Ta bort filter'; |
| | | $labels['moveup'] = 'Flytta upp filter'; |
| | | $labels['movedown'] = 'Flytta ner filter'; |
| | | $labels['filterallof'] = 'Filtrera på alla följande regler'; |
| | | $labels['filteranyof'] = 'Filtrera på någon av följande regler'; |
| | | $labels['filterany'] = 'Filtrera alla meddelanden'; |
| | | $labels['filtercontains'] = 'innehåller'; |
| | | $labels['filternotcontains'] = 'inte innehåller'; |
| | | $labels['filteris'] = 'är lika med'; |
| | | $labels['filterisnot'] = 'är inte lika med'; |
| | | $labels['filterexists'] = 'finns'; |
| | | $labels['filternotexists'] = 'inte finns'; |
| | | $labels['filterunder'] = 'under'; |
| | | $labels['filterover'] = 'över'; |
| | | $labels['addrule'] = 'Lägg till regel'; |
| | | $labels['delrule'] = 'Ta bort regel'; |
| | | $labels['messagemoveto'] = 'Flytta meddelande till'; |
| | | $labels['messageredirect'] = 'Ändra mottagare till'; |
| | | $labels['messagereply'] = 'Besvara meddelande'; |
| | | $labels['messagedelete'] = 'Ta bort meddelande'; |
| | | $labels['messagediscard'] = 'Avböj med felmeddelande'; |
| | | $labels['messagesrules'] = 'För inkommande meddelande'; |
| | | $labels['messagesactions'] = 'Utför följande åtgärd'; |
| | | $labels['add'] = 'Lägg till'; |
| | | $labels['del'] = 'Ta bort'; |
| | | $labels['sender'] = 'Avsändare'; |
| | | $labels['recipient'] = 'Mottagare'; |
| | | $labels['vacationaddresses'] = 'Ytterligare mottagaradresser (avdelade med kommatecken)'; |
| | | $labels['vacationdays'] = 'Antal dagar mellan auto-svar'; |
| | | $labels['vacationreason'] = 'Meddelande i auto-svar'; |
| | | $labels['rulestop'] = 'Avsluta filtrering'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Okänt serverfel'; |
| | | $messages['filterconnerror'] = 'Anslutning till serverns filtertjänst misslyckades'; |
| | | $messages['filterdeleteerror'] = 'Filtret kunde inte tas bort på grund av serverfel'; |
| | | $messages['filterdeleted'] = 'Filtret är borttaget'; |
| | | $messages['filterdeleteconfirm'] = 'Vill du ta bort det markerade filtret?'; |
| | | $messages['filtersaved'] = 'Filtret har sparats'; |
| | | $messages['filtersaveerror'] = 'Filtret kunde inte sparas på grund av serverfel'; |
| | | $messages['ruledeleteconfirm'] = 'Vill du ta bort filterregeln?'; |
| | | $messages['actiondeleteconfirm'] = 'Vill du ta bort filteråtgärden?'; |
| | | $messages['forbiddenchars'] = 'Otillåtet tecken i fältet'; |
| | | $messages['cannotbeempty'] = 'Fältet kan inte lämnas tomt'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['filters'] = 'Фільтри'; |
| | | $labels['managefilters'] = 'Керування фільтрами вхідної пошти'; |
| | | $labels['filtername'] = 'Назва фільтру'; |
| | | $labels['newfilter'] = 'Новий фільтр'; |
| | | $labels['filteradd'] = 'Додати фільтр'; |
| | | $labels['filterdel'] = 'Видалити фільтр'; |
| | | $labels['moveup'] = 'Пересунути вгору'; |
| | | $labels['movedown'] = 'Пересунути вниз'; |
| | | $labels['filterallof'] = 'задовольняє усім наступним умовам'; |
| | | $labels['filteranyof'] = 'задовольняє будь-якій з умов'; |
| | | $labels['filterany'] = 'всі повідомлення'; |
| | | $labels['filtercontains'] = 'містить'; |
| | | $labels['filternotcontains'] = 'не містить'; |
| | | $labels['filteris'] = 'ідентичний до'; |
| | | $labels['filterisnot'] = 'не ідентичний до'; |
| | | $labels['filterexists'] = 'існує'; |
| | | $labels['filternotexists'] = 'не існує'; |
| | | $labels['filterunder'] = 'менше, ніж'; |
| | | $labels['filterover'] = 'більше, ніж'; |
| | | $labels['addrule'] = 'Додати правило'; |
| | | $labels['delrule'] = 'Видалити правило'; |
| | | $labels['messagemoveto'] = 'Пересунути повідомлення до'; |
| | | $labels['messageredirect'] = 'Перенаправити повідомлення до'; |
| | | $labels['messagereply'] = 'Автовідповідач'; |
| | | $labels['messagedelete'] = 'Видалити повідомлення'; |
| | | $labels['messagediscard'] = 'Відхилити з повідомленням'; |
| | | $labels['messagesrules'] = 'Для вхідної пошти'; |
| | | $labels['messagesactions'] = '... виконати дію:'; |
| | | $labels['add'] = 'Додати'; |
| | | $labels['del'] = 'Видалити'; |
| | | $labels['sender'] = 'Відправник'; |
| | | $labels['recipient'] = 'Отримувач'; |
| | | $labels['vacationaddresses'] = 'Додатковий список адрес отримувачів (розділених комою)'; |
| | | $labels['vacationdays'] = 'Як часто повторювати (у днях):'; |
| | | $labels['vacationreason'] = 'Текст повідомлення:'; |
| | | $labels['rulestop'] = 'Зупинити перевірку правил'; |
| | | $labels['filterset'] = 'Набір фільтрів'; |
| | | $labels['filtersetadd'] = 'Додати набір фільтрів'; |
| | | $labels['filtersetdel'] = 'Видалити поточний набір'; |
| | | $labels['filtersetact'] = 'Активувати поточний набір'; |
| | | $labels['filtersetget'] = 'Зберегти набір у текстовому форматі'; |
| | | $labels['filterdef'] = 'Параметри фільтру'; |
| | | $labels['filtersetname'] = 'Назва набору фільтрів'; |
| | | $labels['newfilterset'] = 'Новий набір фільтрів'; |
| | | $labels['active'] = 'активний'; |
| | | $labels['none'] = 'нічого'; |
| | | $labels['fromset'] = 'з набору'; |
| | | $labels['fromfile'] = 'з файлу'; |
| | | $labels['filterdisabled'] = 'Фільтр вимкнено'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = 'Невідома помилка сервера'; |
| | | $messages['filterconnerror'] = 'Неможливо з\'єднатися з сервером'; |
| | | $messages['filterdeleteerror'] = 'Неможливо видалити фільтр. Помилка сервера'; |
| | | $messages['filterdeleted'] = 'Фільтр успішно видалено'; |
| | | $messages['filtersaved'] = 'Фільтр успішно збережено'; |
| | | $messages['filtersaveerror'] = 'Неможливо зберегти фільтр. Помилка сервера'; |
| | | $messages['filterdeleteconfirm'] = 'Ви дійсно хочете видалити обраний фільтр?'; |
| | | $messages['ruledeleteconfirm'] = 'Ви дійсно хочете видалити обране правило?'; |
| | | $messages['actiondeleteconfirm'] = 'Ви дійсно хочете видалити обрану дію?'; |
| | | $messages['forbiddenchars'] = 'Введено заборонений символ'; |
| | | $messages['cannotbeempty'] = 'Поле не може бути пустим'; |
| | | $messages['setactivateerror'] = 'Неможливо активувати обраний набір. Помилка сервера'; |
| | | $messages['setdeleteerror'] = 'Неможливо видалити обраний набір. Помилка сервера'; |
| | | $messages['setactivated'] = 'Набір фільтрів активовано успішно'; |
| | | $messages['setdeleted'] = 'Набір фільтрів видалено успішно'; |
| | | $messages['setdeleteconfirm'] = 'Ви впевнені, що хочете видалити обраний набір?'; |
| | | $messages['setcreateerror'] = 'Не вдалося створити набір. Помилка сервера'; |
| | | $messages['setcreated'] = 'Набір фільтрів створено успішно'; |
| | | $messages['emptyname'] = 'Не вдалося створити набір. Введіть назву набору'; |
| | | $messages['nametoolong'] = 'Не вдалося створити набір. Занадто довга назва' |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = '过滤器'; |
| | | $labels['managefilters'] = '管理邮件过滤器'; |
| | | $labels['filtername'] = '过滤器名称'; |
| | | $labels['newfilter'] = '新建过滤器'; |
| | | $labels['filteradd'] = '添加过滤器'; |
| | | $labels['filterdel'] = '删除过滤器'; |
| | | $labels['moveup'] = '上移'; |
| | | $labels['movedown'] = '下移'; |
| | | $labels['filterallof'] = '匹配所有规则'; |
| | | $labels['filteranyof'] = '匹配任意一条规则'; |
| | | $labels['filterany'] = '所有邮件'; |
| | | $labels['filtercontains'] = '包含'; |
| | | $labels['filternotcontains'] = '不包含'; |
| | | $labels['filteris'] = '等于'; |
| | | $labels['filterisnot'] = '不等于'; |
| | | $labels['filterexists'] = '存在'; |
| | | $labels['filternotexists'] = '不存在'; |
| | | $labels['filterunder'] = '小于'; |
| | | $labels['filterover'] = '大于'; |
| | | $labels['addrule'] = '添加规则'; |
| | | $labels['delrule'] = '删除规则'; |
| | | $labels['messagemoveto'] = '将邮件移动到'; |
| | | $labels['messageredirect'] = '将邮件转发到'; |
| | | $labels['messagereply'] = '回复以下信息'; |
| | | $labels['messagedelete'] = '删除邮件'; |
| | | $labels['messagediscard'] = '丢弃邮件并回复以下信息'; |
| | | $labels['messagesrules'] = '对收取的邮件应用规则:'; |
| | | $labels['messagesactions'] = '...执行以下动作:'; |
| | | $labels['add'] = '添加'; |
| | | $labels['del'] = '删除'; |
| | | $labels['sender'] = '发件人'; |
| | | $labels['recipient'] = '收件人'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = '未知的服务器错误'; |
| | | $messages['filterconnerror'] = '无法连接到 managesieve 服务器'; |
| | | $messages['filterdeleteerror'] = '无法删除过滤器。服务器错误'; |
| | | $messages['filterdeleted'] = '过滤器已成功删除'; |
| | | $messages['filterdeleteconfirm'] = '您确定要删除所选择的过滤器吗?'; |
| | | $messages['filtersaved'] = '过滤器已成功保存。'; |
| | | $messages['filtersaveerror'] = '无法保存过滤器。服务器错误'; |
| | | $messages['ruledeleteconfirm'] = '您确定要删除所选择的规则吗?'; |
| | | $messages['actiondeleteconfirm'] = '您确定要删除所选择的动作吗?'; |
| | | $messages['forbiddenchars'] = '内容中包含禁用的字符'; |
| | | $messages['cannotbeempty'] = '内容不能为空'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels['filters'] = '篩選器'; |
| | | $labels['managefilters'] = '設定篩選器'; |
| | | $labels['filtername'] = '篩選器名稱'; |
| | | $labels['newfilter'] = '建立新篩選器'; |
| | | $labels['filteradd'] = '增加篩選器'; |
| | | $labels['filterdel'] = '刪除篩選器'; |
| | | $labels['moveup'] = '上移'; |
| | | $labels['movedown'] = '下移'; |
| | | $labels['filterallof'] = '符合所有規則'; |
| | | $labels['filteranyof'] = '符合任一條規則'; |
| | | $labels['filterany'] = '所有信件'; |
| | | $labels['filtercontains'] = '包含'; |
| | | $labels['filternotcontains'] = '不包含'; |
| | | $labels['filteris'] = '等於'; |
| | | $labels['filterisnot'] = '不等於'; |
| | | $labels['filterexists'] = '存在'; |
| | | $labels['filternotexists'] = '不存在'; |
| | | $labels['filterunder'] = '小於'; |
| | | $labels['filterover'] = '大於'; |
| | | $labels['addrule'] = '新增規則'; |
| | | $labels['delrule'] = '刪除規則'; |
| | | $labels['messagemoveto'] = '將信件移至'; |
| | | $labels['messageredirect'] = '將信件轉寄至'; |
| | | $labels['messagereply'] = '以下列內容回覆'; |
| | | $labels['messagedelete'] = '刪除信件'; |
| | | $labels['messagediscard'] = '刪除信件並以下列內容回覆'; |
| | | $labels['messagesrules'] = '對新收到的信件:'; |
| | | $labels['messagesactions'] = '執行下列動作:'; |
| | | $labels['add'] = '新增'; |
| | | $labels['del'] = '刪除'; |
| | | $labels['sender'] = '寄件者'; |
| | | $labels['recipient'] = '收件者'; |
| | | $labels['vacationaddresses'] = '其他收件者(用半形逗號隔開):'; |
| | | $labels['vacationdays'] = '多久回覆一次(單位:天):'; |
| | | $labels['vacationreason'] = '信件內容(休假原因):'; |
| | | $labels['rulestop'] = '停止評估規則'; |
| | | $labels['filterset'] = '篩選器集合'; |
| | | $labels['filtersetadd'] = '加入篩選器集合'; |
| | | $labels['filtersetdel'] = '刪除目前的篩選器集合'; |
| | | $labels['filtersetact'] = '啟用目前的篩選器集合'; |
| | | $labels['filtersetdeact'] = '停用目前的篩選器集合'; |
| | | $labels['filtersetget'] = '以純文字下載篩選器集合'; |
| | | $labels['filterdef'] = '篩選器定義'; |
| | | $labels['filtersetname'] = '篩選器集合名稱'; |
| | | $labels['newfilterset'] = '建立篩選器集合'; |
| | | $labels['active'] = '啟用'; |
| | | $labels['none'] = '無'; |
| | | $labels['fromset'] = '從集合'; |
| | | $labels['fromfile'] = '重檔案'; |
| | | $labels['filterdisabled'] = '篩選器已停用'; |
| | | |
| | | $messages = array(); |
| | | $messages['filterunknownerror'] = '未知的伺服器錯誤'; |
| | | $messages['filterconnerror'] = '無法與伺服器連線'; |
| | | $messages['filterdeleteerror'] = '無法刪除篩選器。發生伺服器錯誤'; |
| | | $messages['filterdeleted'] = '成功刪除篩選器'; |
| | | $messages['filterconfirmdelete'] = '您確定要刪除選擇的篩選器嗎?'; |
| | | $messages['filtersaved'] = '成功儲存篩選器。'; |
| | | $messages['filtersaveerror'] = '無法儲存篩選器。發生伺服器錯誤'; |
| | | $messages['ruledeleteconfirm'] = '您確定要刪除選的規則嗎?'; |
| | | $messages['actiondeleteconfirm'] = '您確定要刪除選擇的動作嗎?'; |
| | | $messages['forbiddenchars'] = '內容包含禁用字元'; |
| | | $messages['cannotbeempty'] = '內容不能為空白'; |
| | | $messages['setactivateerror'] = '無法啟用選擇的篩選器集合。 伺服器發生錯誤'; |
| | | $messages['setdeactivateerror'] = '無法停用選擇的篩選器集合。 伺服器發生錯誤'; |
| | | $messages['setdeleteerror'] = '無法刪除選擇的篩選器集合。 伺服器發生錯誤'; |
| | | $messages['setactivated'] = '篩選器集合成功啟用'; |
| | | $messages['setdeactivated'] = '篩選器集合成功停用'; |
| | | $messages['setdeleted'] = '篩選器集合成功刪除'; |
| | | $messages['setdeleteconfirm'] = '你確定要刪除選擇的篩選器集合嗎?'; |
| | | $messages['setcreateerror'] = '無法建立篩選器集合。 伺服器發生錯誤'; |
| | | $messages['setcreated'] = '篩選器集合成功建立'; |
| | | $messages['emptyname'] = '無法建立篩選器集合。 集合名稱空白'; |
| | | $messages['nametoolong'] = '無法建立篩選器集合。 名稱太長' |
| | | |
| | | ?> |
New file |
| | |
| | | /* Sieve Filters (tab) */ |
| | | |
| | | if (window.rcmail) { |
| | | rcmail.addEventListener('init', function(evt) { |
| | | |
| | | var tab = $('<span>').attr('id', 'settingstabpluginmanagesieve').addClass('tablink'); |
| | | var button = $('<a>').attr('href', rcmail.env.comm_path+'&_action=plugin.managesieve') |
| | | .attr('title', rcmail.gettext('managesieve.managefilters')) |
| | | .html(rcmail.gettext('managesieve.filters')) |
| | | .bind('click', function(e){ return rcmail.command('plugin.managesieve', this) }) |
| | | .appendTo(tab); |
| | | |
| | | // add button and register commands |
| | | rcmail.add_element(tab, 'tabs'); |
| | | rcmail.register_command('plugin.managesieve', function() { rcmail.goto_url('plugin.managesieve') }, true); |
| | | rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() }, true); |
| | | rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() }, true); |
| | | rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() }, true); |
| | | rcmail.register_command('plugin.managesieve-up', function() { rcmail.managesieve_up() }, true); |
| | | rcmail.register_command('plugin.managesieve-down', function() { rcmail.managesieve_down() }, true); |
| | | rcmail.register_command('plugin.managesieve-set', function() { rcmail.managesieve_set() }, true); |
| | | rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() }, true); |
| | | rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() }, true); |
| | | rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() }, true); |
| | | rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() }, true); |
| | | |
| | | if (rcmail.env.action == 'plugin.managesieve') { |
| | | if (rcmail.gui_objects.sieveform) { |
| | | rcmail.enable_command('plugin.managesieve-save', true); |
| | | } |
| | | else { |
| | | rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', |
| | | 'plugin.managesieve-down', false); |
| | | rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror); |
| | | } |
| | | |
| | | if (rcmail.gui_objects.filterslist) { |
| | | var p = rcmail; |
| | | rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist, {multiselect:false, draggable:false, keyboard:false}); |
| | | rcmail.filters_list.addEventListener('select', function(o){ p.managesieve_select(o); }); |
| | | rcmail.filters_list.init(); |
| | | rcmail.filters_list.focus(); |
| | | |
| | | rcmail.enable_command('plugin.managesieve-set', true); |
| | | rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', rcmail.gui_objects.filtersetslist.length); |
| | | rcmail.enable_command('plugin.managesieve-setdel', rcmail.gui_objects.filtersetslist.length > 1); |
| | | |
| | | $('#'+rcmail.buttons['plugin.managesieve-setact'][0].id).attr('title', rcmail.gettext('managesieve.filterset' |
| | | + (rcmail.gui_objects.filtersetslist.value == rcmail.env.active_set ? 'deact' : 'act'))); |
| | | } |
| | | } |
| | | if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled) |
| | | $('#disabled').attr('checked', true); |
| | | }); |
| | | }; |
| | | |
| | | /*********************************************************/ |
| | | /********* Managesieve filters methods *********/ |
| | | /*********************************************************/ |
| | | |
| | | rcube_webmail.prototype.managesieve_add = function() |
| | | { |
| | | this.load_managesieveframe(); |
| | | this.filters_list.clear_selection(); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_del = function() |
| | | { |
| | | var id = this.filters_list.get_single_selection(); |
| | | if (confirm(this.get_label('managesieve.filterdeleteconfirm'))) |
| | | this.http_request('plugin.managesieve', |
| | | '_act=delete&_fid='+this.filters_list.rows[id].uid, true); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_up = function() |
| | | { |
| | | var id = this.filters_list.get_single_selection(); |
| | | this.http_request('plugin.managesieve', |
| | | '_act=up&_fid='+this.filters_list.rows[id].uid, true); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_down = function() |
| | | { |
| | | var id = this.filters_list.get_single_selection(); |
| | | this.http_request('plugin.managesieve', |
| | | '_act=down&_fid='+this.filters_list.rows[id].uid, true); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_rowid = function(id) |
| | | { |
| | | var i, rows = this.filters_list.rows; |
| | | |
| | | for (i=0; i<rows.length; i++) |
| | | if (rows[i] != null && rows[i].uid == id) |
| | | return i; |
| | | } |
| | | |
| | | rcube_webmail.prototype.managesieve_updatelist = function(action, name, id, disabled) |
| | | { |
| | | this.set_busy(true); |
| | | |
| | | switch (action) { |
| | | case 'delete': |
| | | this.filters_list.remove_row(this.managesieve_rowid(id)); |
| | | this.filters_list.clear_selection(); |
| | | this.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', 'plugin.managesieve-down', false); |
| | | this.show_contentframe(false); |
| | | |
| | | // re-numbering filters |
| | | var i, rows = this.filters_list.rows; |
| | | for (i=0; i<rows.length; i++) { |
| | | if (rows[i] != null && rows[i].uid > id) |
| | | rows[i].uid = rows[i].uid-1; |
| | | } |
| | | break; |
| | | |
| | | case 'down': |
| | | var from, fromstatus, status, rows = this.filters_list.rows; |
| | | |
| | | // we need only to replace filter names... |
| | | for (var i=0; i<rows.length; i++) { |
| | | if (rows[i]==null) { // removed row |
| | | continue; |
| | | } |
| | | else if (rows[i].uid == id) { |
| | | from = rows[i].obj; |
| | | fromstatus = $(from).hasClass('disabled'); |
| | | } |
| | | else if (rows[i].uid == id+1) { |
| | | name = rows[i].obj.cells[0].innerHTML; |
| | | status = $(rows[i].obj).hasClass('disabled'); |
| | | rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML; |
| | | from.cells[0].innerHTML = name; |
| | | $(from)[status?'addClass':'removeClass']('disabled'); |
| | | $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled'); |
| | | this.filters_list.highlight_row(i); |
| | | break; |
| | | } |
| | | } |
| | | // ... and disable/enable Down button |
| | | this.filters_listbuttons(); |
| | | break; |
| | | |
| | | case 'up': |
| | | var from, status, fromstatus, rows = this.filters_list.rows; |
| | | |
| | | // we need only to replace filter names... |
| | | for (var i=0; i<rows.length; i++) { |
| | | if (rows[i] == null) { // removed row |
| | | continue; |
| | | } |
| | | else if (rows[i].uid == id-1) { |
| | | from = rows[i].obj; |
| | | fromstatus = $(from).hasClass('disabled'); |
| | | this.filters_list.highlight_row(i); |
| | | } |
| | | else if (rows[i].uid == id) { |
| | | name = rows[i].obj.cells[0].innerHTML; |
| | | status = $(rows[i].obj).hasClass('disabled'); |
| | | rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML; |
| | | from.cells[0].innerHTML = name; |
| | | $(from)[status?'addClass':'removeClass']('disabled'); |
| | | $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled'); |
| | | break; |
| | | } |
| | | } |
| | | // ... and disable/enable Up button |
| | | this.filters_listbuttons(); |
| | | break; |
| | | |
| | | case 'update': |
| | | var rows = parent.rcmail.filters_list.rows; |
| | | for (var i=0; i<rows.length; i++) |
| | | if (rows[i] && rows[i].uid == id) { |
| | | rows[i].obj.cells[0].innerHTML = name; |
| | | if (disabled) |
| | | $(rows[i].obj).addClass('disabled'); |
| | | else |
| | | $(rows[i].obj).removeClass('disabled'); |
| | | break; |
| | | } |
| | | break; |
| | | |
| | | case 'add': |
| | | var row, new_row, td, list = parent.rcmail.filters_list; |
| | | |
| | | if (!list) |
| | | break; |
| | | |
| | | for (var i=0; i<list.rows.length; i++) |
| | | if (list.rows[i] != null && String(list.rows[i].obj.id).match(/^rcmrow/)) |
| | | row = list.rows[i].obj; |
| | | |
| | | if (row) { |
| | | new_row = parent.document.createElement('tr'); |
| | | new_row.id = 'rcmrow'+id; |
| | | td = parent.document.createElement('td'); |
| | | new_row.appendChild(td); |
| | | list.insert_row(new_row, false); |
| | | if (disabled) |
| | | $(new_row).addClass('disabled'); |
| | | if (row.cells[0].className) |
| | | td.className = row.cells[0].className; |
| | | |
| | | td.innerHTML = name; |
| | | list.highlight_row(id); |
| | | |
| | | parent.rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', true); |
| | | } |
| | | else // refresh whole page |
| | | parent.rcmail.goto_url('plugin.managesieve'); |
| | | break; |
| | | } |
| | | |
| | | this.set_busy(false); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_select = function(list) |
| | | { |
| | | var id = list.get_single_selection(); |
| | | if (id != null) |
| | | this.load_managesieveframe(list.rows[id].uid); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_save = function() |
| | | { |
| | | if (parent.rcmail && parent.rcmail.filters_list && this.gui_objects.sieveform.name != 'filtersetform') { |
| | | var id = parent.rcmail.filters_list.get_single_selection(); |
| | | if (id != null) |
| | | this.gui_objects.sieveform.elements['_fid'].value = parent.rcmail.filters_list.rows[id].uid; |
| | | } |
| | | this.gui_objects.sieveform.submit(); |
| | | }; |
| | | |
| | | // load filter frame |
| | | rcube_webmail.prototype.load_managesieveframe = function(id) |
| | | { |
| | | if (typeof(id) != 'undefined' && id != null) { |
| | | this.enable_command('plugin.managesieve-del', true); |
| | | this.filters_listbuttons(); |
| | | } |
| | | else |
| | | this.enable_command('plugin.managesieve-up', 'plugin.managesieve-down', 'plugin.managesieve-del', false); |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | this.set_busy(true, 'loading'); |
| | | target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_fid='+id; |
| | | } |
| | | }; |
| | | |
| | | // enable/disable Up/Down buttons |
| | | rcube_webmail.prototype.filters_listbuttons = function() |
| | | { |
| | | var id = this.filters_list.get_single_selection(), |
| | | rows = this.filters_list.rows; |
| | | |
| | | for (var i=0; i<rows.length; i++) { |
| | | if (rows[i] == null) { // removed row |
| | | } |
| | | else if (i == id) { |
| | | this.enable_command('plugin.managesieve-up', false); |
| | | break; |
| | | } |
| | | else { |
| | | this.enable_command('plugin.managesieve-up', true); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | for (var i=rows.length-1; i>0; i--) { |
| | | if (rows[i] == null) { // removed row |
| | | } |
| | | else if (i == id) { |
| | | this.enable_command('plugin.managesieve-down', false); |
| | | break; |
| | | } |
| | | else { |
| | | this.enable_command('plugin.managesieve-down', true); |
| | | break; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // operations on filters form |
| | | rcube_webmail.prototype.managesieve_ruleadd = function(id) |
| | | { |
| | | this.http_post('plugin.managesieve', '_act=ruleadd&_rid='+id); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_rulefill = function(content, id, after) |
| | | { |
| | | if (content != '') { |
| | | // create new element |
| | | var div = document.getElementById('rules'), |
| | | row = document.createElement('div'); |
| | | |
| | | this.managesieve_insertrow(div, row, after); |
| | | // fill row after inserting (for IE) |
| | | row.setAttribute('id', 'rulerow'+id); |
| | | row.className = 'rulerow'; |
| | | row.innerHTML = content; |
| | | |
| | | this.managesieve_formbuttons(div); |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_ruledel = function(id) |
| | | { |
| | | if (confirm(this.get_label('managesieve.ruledeleteconfirm'))) { |
| | | var row = document.getElementById('rulerow'+id); |
| | | row.parentNode.removeChild(row); |
| | | this.managesieve_formbuttons(document.getElementById('rules')); |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_actionadd = function(id) |
| | | { |
| | | this.http_post('plugin.managesieve', '_act=actionadd&_aid='+id); |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_actionfill = function(content, id, after) |
| | | { |
| | | if (content != '') { |
| | | var div = document.getElementById('actions'), |
| | | row = document.createElement('div'); |
| | | |
| | | this.managesieve_insertrow(div, row, after); |
| | | // fill row after inserting (for IE) |
| | | row.className = 'actionrow'; |
| | | row.setAttribute('id', 'actionrow'+id); |
| | | row.innerHTML = content; |
| | | |
| | | this.managesieve_formbuttons(div); |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_actiondel = function(id) |
| | | { |
| | | if (confirm(this.get_label('managesieve.actiondeleteconfirm'))) { |
| | | var row = document.getElementById('actionrow'+id); |
| | | row.parentNode.removeChild(row); |
| | | this.managesieve_formbuttons(document.getElementById('actions')); |
| | | } |
| | | }; |
| | | |
| | | // insert rule/action row in specified place on the list |
| | | rcube_webmail.prototype.managesieve_insertrow = function(div, row, after) |
| | | { |
| | | for (var i=0; i<div.childNodes.length; i++) { |
| | | if (div.childNodes[i].id == (div.id == 'rules' ? 'rulerow' : 'actionrow') + after) |
| | | break; |
| | | } |
| | | |
| | | if (div.childNodes[i+1]) |
| | | div.insertBefore(row, div.childNodes[i+1]); |
| | | else |
| | | div.appendChild(row); |
| | | }; |
| | | |
| | | // update Delete buttons status |
| | | rcube_webmail.prototype.managesieve_formbuttons = function(div) |
| | | { |
| | | var i, button, buttons = []; |
| | | |
| | | // count and get buttons |
| | | for (i=0; i<div.childNodes.length; i++) { |
| | | if (div.id == 'rules' && div.childNodes[i].id) { |
| | | if (/rulerow/.test(div.childNodes[i].id)) |
| | | buttons.push('ruledel' + div.childNodes[i].id.replace(/rulerow/, '')); |
| | | } |
| | | else if (div.childNodes[i].id) { |
| | | if (/actionrow/.test(div.childNodes[i].id)) |
| | | buttons.push( 'actiondel' + div.childNodes[i].id.replace(/actionrow/, '')); |
| | | } |
| | | } |
| | | |
| | | for (i=0; i<buttons.length; i++) { |
| | | button = document.getElementById(buttons[i]); |
| | | if (i>0 || buttons.length>1) { |
| | | $(button).removeClass('disabled'); |
| | | button.removeAttribute('disabled'); |
| | | } |
| | | else { |
| | | $(button).addClass('disabled'); |
| | | button.setAttribute('disabled', true); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // Set change |
| | | rcube_webmail.prototype.managesieve_set = function() |
| | | { |
| | | var script = $(this.gui_objects.filtersetslist).val(); |
| | | location.href = this.env.comm_path+'&_action=plugin.managesieve&_set='+script; |
| | | }; |
| | | |
| | | // Script download |
| | | rcube_webmail.prototype.managesieve_setget = function() |
| | | { |
| | | var script = $(this.gui_objects.filtersetslist).val(); |
| | | location.href = this.env.comm_path+'&_action=plugin.managesieve&_act=setget&_set='+script; |
| | | }; |
| | | |
| | | // Set activate |
| | | rcube_webmail.prototype.managesieve_setact = function() |
| | | { |
| | | if (!this.gui_objects.filtersetslist) |
| | | return false; |
| | | |
| | | var script = this.gui_objects.filtersetslist.value, |
| | | action = (script == rcmail.env.active_set ? 'deact' : 'setact'); |
| | | |
| | | this.http_post('plugin.managesieve', '_act='+action+'&_set='+script); |
| | | }; |
| | | |
| | | // Set activate flag in sets list after set activation |
| | | rcube_webmail.prototype.managesieve_reset = function() |
| | | { |
| | | if (!this.gui_objects.filtersetslist) |
| | | return false; |
| | | |
| | | var list = this.gui_objects.filtersetslist, |
| | | opts = list.getElementsByTagName('option'), |
| | | label = ' (' + this.get_label('managesieve.active') + ')', |
| | | regx = new RegExp(RegExp.escape(label)+'$'); |
| | | |
| | | for (var x=0; x<opts.length; x++) { |
| | | if (opts[x].value != rcmail.env.active_set && opts[x].innerHTML.match(regx)) |
| | | opts[x].innerHTML = opts[x].innerHTML.replace(regx, ''); |
| | | else if (opts[x].value == rcmail.env.active_set) |
| | | opts[x].innerHTML = opts[x].innerHTML + label; |
| | | } |
| | | |
| | | // change title of setact button |
| | | $('#'+rcmail.buttons['plugin.managesieve-setact'][0].id).attr('title', rcmail.gettext('managesieve.filterset' |
| | | + (list.value == rcmail.env.active_set ? 'deact' : 'act'))); |
| | | }; |
| | | |
| | | // Set delete |
| | | rcube_webmail.prototype.managesieve_setdel = function() |
| | | { |
| | | if (!this.gui_objects.filtersetslist) |
| | | return false; |
| | | |
| | | if (!confirm(this.get_label('managesieve.setdeleteconfirm'))) |
| | | return false; |
| | | |
| | | var script = this.gui_objects.filtersetslist.value; |
| | | this.http_post('plugin.managesieve', '_act=setdel&_set='+script); |
| | | }; |
| | | |
| | | // Set add |
| | | rcube_webmail.prototype.managesieve_setadd = function() |
| | | { |
| | | this.filters_list.clear_selection(); |
| | | this.enable_command('plugin.managesieve-up', 'plugin.managesieve-down', 'plugin.managesieve-del', false); |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | this.set_busy(true, 'loading'); |
| | | target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_newset=1'; |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.prototype.managesieve_reload = function(set) |
| | | { |
| | | this.env.reload_set = set; |
| | | window.setTimeout(function() { |
| | | location.href = rcmail.env.comm_path + '&_action=plugin.managesieve' |
| | | + (rcmail.env.reload_set ? '&_set=' + rcmail.env.reload_set : '') |
| | | }, 500); |
| | | }; |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Managesieve (Sieve Filters) |
| | | * |
| | | * Plugin that adds a possibility to manage Sieve filters in Thunderbird's style. |
| | | * It's clickable interface which operates on text scripts and communicates |
| | | * with server using managesieve protocol. Adds Filters tab in Settings. |
| | | * |
| | | * @version 2.9 |
| | | * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> |
| | | * |
| | | * Configuration (see config.inc.php.dist) |
| | | * |
| | | * $Id$ |
| | | */ |
| | | |
| | | class managesieve extends rcube_plugin |
| | | { |
| | | public $task = 'settings'; |
| | | |
| | | private $rc; |
| | | private $sieve; |
| | | private $errors; |
| | | private $form; |
| | | private $script = array(); |
| | | private $exts = array(); |
| | | private $headers = array( |
| | | 'subject' => 'Subject', |
| | | 'sender' => 'From', |
| | | 'recipient' => 'To', |
| | | ); |
| | | |
| | | |
| | | function init() |
| | | { |
| | | // add Tab label/title |
| | | $this->add_texts('localization/', array('filters','managefilters')); |
| | | |
| | | // register actions |
| | | $this->register_action('plugin.managesieve', array($this, 'managesieve_actions')); |
| | | $this->register_action('plugin.managesieve-save', array($this, 'managesieve_save')); |
| | | |
| | | // include main js script |
| | | $this->include_script('managesieve.js'); |
| | | } |
| | | |
| | | function managesieve_start() |
| | | { |
| | | $this->rc = rcmail::get_instance(); |
| | | $this->load_config(); |
| | | |
| | | // register UI objects |
| | | $this->rc->output->add_handlers(array( |
| | | 'filterslist' => array($this, 'filters_list'), |
| | | 'filtersetslist' => array($this, 'filtersets_list'), |
| | | 'filterframe' => array($this, 'filter_frame'), |
| | | 'filterform' => array($this, 'filter_form'), |
| | | 'filtersetform' => array($this, 'filterset_form'), |
| | | )); |
| | | |
| | | require_once($this->home . '/lib/Net/Sieve.php'); |
| | | require_once($this->home . '/lib/rcube_sieve.php'); |
| | | |
| | | $host = rcube_parse_host($this->rc->config->get('managesieve_host', 'localhost')); |
| | | $port = $this->rc->config->get('managesieve_port', 2000); |
| | | |
| | | // try to connect to managesieve server and to fetch the script |
| | | $this->sieve = new rcube_sieve($_SESSION['username'], |
| | | $this->rc->decrypt($_SESSION['password']), $host, $port, |
| | | $this->rc->config->get('managesieve_auth_type'), |
| | | $this->rc->config->get('managesieve_usetls', false), |
| | | $this->rc->config->get('managesieve_disabled_extensions'), |
| | | $this->rc->config->get('managesieve_debug', false) |
| | | ); |
| | | |
| | | if (!($error = $this->sieve->error())) { |
| | | |
| | | $list = $this->sieve->get_scripts(); |
| | | $active = $this->sieve->get_active(); |
| | | $_SESSION['managesieve_active'] = $active; |
| | | |
| | | if (!empty($_GET['_set'])) { |
| | | $script_name = get_input_value('_set', RCUBE_INPUT_GET); |
| | | } |
| | | else if (!empty($_SESSION['managesieve_current'])) { |
| | | $script_name = $_SESSION['managesieve_current']; |
| | | } |
| | | else { |
| | | // get active script |
| | | if ($active) { |
| | | $script_name = $active; |
| | | } |
| | | else if ($list) { |
| | | $script_name = $list[0]; |
| | | } |
| | | // create a new (initial) script |
| | | else { |
| | | // if script not exists build default script contents |
| | | $script_file = $this->rc->config->get('managesieve_default'); |
| | | $script_name = 'roundcube'; |
| | | if ($script_file && is_readable($script_file)) |
| | | $content = file_get_contents($script_file); |
| | | |
| | | // add script and set it active |
| | | if ($this->sieve->save_script($script_name, $content)) |
| | | if ($this->sieve->activate($script_name)) |
| | | $_SESSION['managesieve_active'] = $script_name; |
| | | } |
| | | } |
| | | |
| | | if ($script_name) |
| | | $this->sieve->load($script_name); |
| | | |
| | | $error = $this->sieve->error(); |
| | | } |
| | | |
| | | // finally set script objects |
| | | if ($error) { |
| | | switch ($error) { |
| | | case SIEVE_ERROR_CONNECTION: |
| | | case SIEVE_ERROR_LOGIN: |
| | | $this->rc->output->show_message('managesieve.filterconnerror', 'error'); |
| | | break; |
| | | default: |
| | | $this->rc->output->show_message('managesieve.filterunknownerror', 'error'); |
| | | break; |
| | | } |
| | | |
| | | raise_error(array('code' => 403, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Unable to connect to managesieve on $host:$port"), true, false); |
| | | |
| | | // to disable 'Add filter' button set env variable |
| | | $this->rc->output->set_env('filterconnerror', true); |
| | | $this->script = array(); |
| | | } |
| | | else { |
| | | $this->script = $this->sieve->script->as_array(); |
| | | $this->exts = $this->sieve->get_extensions(); |
| | | $this->rc->output->set_env('active_set', $_SESSION['managesieve_active']); |
| | | $_SESSION['managesieve_current'] = $this->sieve->current; |
| | | } |
| | | |
| | | return $error; |
| | | } |
| | | |
| | | function managesieve_actions() |
| | | { |
| | | // Init plugin and handle managesieve connection |
| | | $error = $this->managesieve_start(); |
| | | |
| | | // Handle user requests |
| | | if ($action = get_input_value('_act', RCUBE_INPUT_GPC)) { |
| | | $fid = (int) get_input_value('_fid', RCUBE_INPUT_GET); |
| | | |
| | | if ($action == 'up' && !$error) { |
| | | if ($fid && isset($this->script[$fid]) && isset($this->script[$fid-1])) { |
| | | if ($this->sieve->script->update_rule($fid, $this->script[$fid-1]) !== false |
| | | && $this->sieve->script->update_rule($fid-1, $this->script[$fid]) !== false) { |
| | | $result = $this->sieve->save(); |
| | | } |
| | | |
| | | if ($result) { |
| | | // $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); |
| | | $this->rc->output->command('managesieve_updatelist', 'up', '', $fid); |
| | | } else |
| | | $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); |
| | | } |
| | | } |
| | | else if ($action == 'down' && !$error) { |
| | | if (isset($this->script[$fid]) && isset($this->script[$fid+1])) { |
| | | if ($this->sieve->script->update_rule($fid, $this->script[$fid+1]) !== false |
| | | && $this->sieve->script->update_rule($fid+1, $this->script[$fid]) !== false) { |
| | | $result = $this->sieve->save(); |
| | | } |
| | | |
| | | if ($result === true) { |
| | | // $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); |
| | | $this->rc->output->command('managesieve_updatelist', 'down', '', $fid); |
| | | } else { |
| | | $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); |
| | | } |
| | | } |
| | | } |
| | | else if ($action == 'delete' && !$error) { |
| | | if (isset($this->script[$fid])) { |
| | | if ($this->sieve->script->delete_rule($fid)) |
| | | $result = $this->sieve->save(); |
| | | |
| | | if ($result === true) { |
| | | $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation'); |
| | | $this->rc->output->command('managesieve_updatelist', 'delete', '', $fid); |
| | | } else { |
| | | $this->rc->output->show_message('managesieve.filterdeleteerror', 'error'); |
| | | } |
| | | } |
| | | } |
| | | else if ($action == 'setact' && !$error) { |
| | | $script_name = get_input_value('_set', RCUBE_INPUT_GPC); |
| | | $result = $this->sieve->activate($script_name); |
| | | |
| | | if ($result === true) { |
| | | $this->rc->output->set_env('active_set', $script_name); |
| | | $this->rc->output->show_message('managesieve.setactivated', 'confirmation'); |
| | | $this->rc->output->command('managesieve_reset'); |
| | | $_SESSION['managesieve_active'] = $script_name; |
| | | } else { |
| | | $this->rc->output->show_message('managesieve.setactivateerror', 'error'); |
| | | } |
| | | } |
| | | else if ($action == 'deact' && !$error) { |
| | | $result = $this->sieve->deactivate(); |
| | | |
| | | if ($result === true) { |
| | | $this->rc->output->set_env('active_set', ''); |
| | | $this->rc->output->show_message('managesieve.setdeactivated', 'confirmation'); |
| | | $this->rc->output->command('managesieve_reset'); |
| | | $_SESSION['managesieve_active'] = ''; |
| | | } else { |
| | | $this->rc->output->show_message('managesieve.setdeactivateerror', 'error'); |
| | | } |
| | | } |
| | | else if ($action == 'setdel' && !$error) { |
| | | $script_name = get_input_value('_set', RCUBE_INPUT_GPC); |
| | | $result = $this->sieve->remove($script_name); |
| | | |
| | | if ($result === true) { |
| | | $this->rc->output->show_message('managesieve.setdeleted', 'confirmation'); |
| | | $this->rc->output->command('managesieve_reload'); |
| | | $this->rc->session->remove('managesieve_current'); |
| | | } else { |
| | | $this->rc->output->show_message('managesieve.setdeleteerror', 'error'); |
| | | } |
| | | } |
| | | else if ($action == 'setget') { |
| | | $script_name = get_input_value('_set', RCUBE_INPUT_GPC); |
| | | $script = $this->sieve->get_script($script_name); |
| | | |
| | | if (PEAR::isError($script)) |
| | | exit; |
| | | |
| | | $browser = new rcube_browser; |
| | | |
| | | // send download headers |
| | | header("Content-Type: application/octet-stream"); |
| | | header("Content-Length: ".strlen($script)); |
| | | |
| | | if ($browser->ie) |
| | | header("Content-Type: application/force-download"); |
| | | if ($browser->ie && $browser->ver < 7) |
| | | $filename = rawurlencode(abbreviate_string($script_name, 55)); |
| | | else if ($browser->ie) |
| | | $filename = rawurlencode($script_name); |
| | | else |
| | | $filename = addcslashes($script_name, '\\"'); |
| | | |
| | | header("Content-Disposition: attachment; filename=\"$filename.txt\""); |
| | | echo $script; |
| | | exit; |
| | | } |
| | | elseif ($action == 'ruleadd') { |
| | | $rid = get_input_value('_rid', RCUBE_INPUT_GPC); |
| | | $id = $this->genid(); |
| | | $content = $this->rule_div($fid, $id, false); |
| | | |
| | | $this->rc->output->command('managesieve_rulefill', $content, $id, $rid); |
| | | } |
| | | elseif ($action == 'actionadd') { |
| | | $aid = get_input_value('_aid', RCUBE_INPUT_GPC); |
| | | $id = $this->genid(); |
| | | $content = $this->action_div($fid, $id, false); |
| | | |
| | | $this->rc->output->command('managesieve_actionfill', $content, $id, $aid); |
| | | } |
| | | |
| | | $this->rc->output->send(); |
| | | } |
| | | |
| | | $this->managesieve_send(); |
| | | } |
| | | |
| | | function managesieve_save() |
| | | { |
| | | // Init plugin and handle managesieve connection |
| | | $error = $this->managesieve_start(); |
| | | |
| | | // filters set add action |
| | | if (!empty($_POST['_newset'])) { |
| | | $name = get_input_value('_name', RCUBE_INPUT_POST); |
| | | $copy = get_input_value('_copy', RCUBE_INPUT_POST); |
| | | $from = get_input_value('_from', RCUBE_INPUT_POST); |
| | | |
| | | if (!$name) |
| | | $error = 'managesieve.emptyname'; |
| | | else if (mb_strlen($name)>128) |
| | | $error = 'managesieve.nametoolong'; |
| | | else if ($from == 'file') { |
| | | // from file |
| | | if (is_uploaded_file($_FILES['_file']['tmp_name'])) { |
| | | $file = file_get_contents($_FILES['_file']['tmp_name']); |
| | | $file = preg_replace('/\r/', '', $file); |
| | | // for security don't save script directly |
| | | // check syntax before, like this... |
| | | $this->sieve->load_script($file); |
| | | if (!$this->sieve->save($name)) { |
| | | $error = 'managesieve.setcreateerror'; |
| | | } |
| | | } |
| | | else { // upload failed |
| | | $err = $_FILES['_file']['error']; |
| | | $error = true; |
| | | |
| | | if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { |
| | | $msg = rcube_label(array('name' => 'filesizeerror', |
| | | 'vars' => array('size' => |
| | | show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); |
| | | } |
| | | else { |
| | | $error = 'fileuploaderror'; |
| | | } |
| | | } |
| | | } |
| | | else if (!$this->sieve->copy($name, $from == 'set' ? $copy : '')) { |
| | | $error = 'managesieve.setcreateerror'; |
| | | } |
| | | |
| | | if (!$error) { |
| | | $this->rc->output->show_message('managesieve.setcreated', 'confirmation'); |
| | | $this->rc->output->command('parent.managesieve_reload', $name); |
| | | } else if ($msg) { |
| | | $this->rc->output->command('display_message', $msg, 'error'); |
| | | } else { |
| | | $this->rc->output->show_message($error, 'error'); |
| | | } |
| | | } |
| | | // filter add/edit action |
| | | else if (isset($_POST['_name'])) { |
| | | $name = trim(get_input_value('_name', RCUBE_INPUT_POST, true)); |
| | | $fid = trim(get_input_value('_fid', RCUBE_INPUT_POST)); |
| | | $join = trim(get_input_value('_join', RCUBE_INPUT_POST)); |
| | | |
| | | // and arrays |
| | | $headers = $_POST['_header']; |
| | | $cust_headers = $_POST['_custom_header']; |
| | | $ops = $_POST['_rule_op']; |
| | | $sizeops = $_POST['_rule_size_op']; |
| | | $sizeitems = $_POST['_rule_size_item']; |
| | | $sizetargets = $_POST['_rule_size_target']; |
| | | $targets = $_POST['_rule_target']; |
| | | $act_types = $_POST['_action_type']; |
| | | $mailboxes = $_POST['_action_mailbox']; |
| | | $act_targets = $_POST['_action_target']; |
| | | $area_targets = $_POST['_action_target_area']; |
| | | $reasons = $_POST['_action_reason']; |
| | | $addresses = $_POST['_action_addresses']; |
| | | $days = $_POST['_action_days']; |
| | | |
| | | // we need a "hack" for radiobuttons |
| | | foreach ($sizeitems as $item) |
| | | $items[] = $item; |
| | | |
| | | $this->form['disabled'] = $_POST['_disabled'] ? true : false; |
| | | $this->form['join'] = $join=='allof' ? true : false; |
| | | $this->form['name'] = $name; |
| | | $this->form['tests'] = array(); |
| | | $this->form['actions'] = array(); |
| | | |
| | | if ($name == '') |
| | | $this->errors['name'] = $this->gettext('cannotbeempty'); |
| | | else |
| | | foreach($this->script as $idx => $rule) |
| | | if($rule['name'] == $name && $idx != $fid) { |
| | | $this->errors['name'] = $this->gettext('ruleexist'); |
| | | break; |
| | | } |
| | | |
| | | $i = 0; |
| | | // rules |
| | | if ($join == 'any') { |
| | | $this->form['tests'][0]['test'] = 'true'; |
| | | } |
| | | else { |
| | | foreach($headers as $idx => $header) { |
| | | $header = $this->strip_value($header); |
| | | $target = $this->strip_value($targets[$idx], true); |
| | | $op = $this->strip_value($ops[$idx]); |
| | | |
| | | // normal header |
| | | if (in_array($header, $this->headers)) { |
| | | if(preg_match('/^not/', $op)) |
| | | $this->form['tests'][$i]['not'] = true; |
| | | $type = preg_replace('/^not/', '', $op); |
| | | |
| | | if ($type == 'exists') { |
| | | $this->form['tests'][$i]['test'] = 'exists'; |
| | | $this->form['tests'][$i]['arg'] = $header; |
| | | } |
| | | else { |
| | | $this->form['tests'][$i]['type'] = $type; |
| | | $this->form['tests'][$i]['test'] = 'header'; |
| | | $this->form['tests'][$i]['arg1'] = $header; |
| | | $this->form['tests'][$i]['arg2'] = $target; |
| | | |
| | | if ($target == '') |
| | | $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty'); |
| | | } |
| | | } |
| | | else |
| | | switch ($header) { |
| | | case 'size': |
| | | $sizeop = $this->strip_value($sizeops[$idx]); |
| | | $sizeitem = $this->strip_value($items[$idx]); |
| | | $sizetarget = $this->strip_value($sizetargets[$idx]); |
| | | |
| | | $this->form['tests'][$i]['test'] = 'size'; |
| | | $this->form['tests'][$i]['type'] = $sizeop; |
| | | $this->form['tests'][$i]['arg'] = $sizetarget.$sizeitem; |
| | | |
| | | if (!preg_match('/^[0-9]+(K|M|G)*$/i', $sizetarget)) |
| | | $this->errors['tests'][$i]['sizetarget'] = $this->gettext('wrongformat'); |
| | | break; |
| | | case '...': |
| | | $cust_header = $headers = $this->strip_value($cust_headers[$idx]); |
| | | |
| | | if(preg_match('/^not/', $op)) |
| | | $this->form['tests'][$i]['not'] = true; |
| | | $type = preg_replace('/^not/', '', $op); |
| | | |
| | | if ($cust_header == '') |
| | | $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty'); |
| | | else { |
| | | $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY); |
| | | |
| | | if (!count($headers)) |
| | | $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty'); |
| | | else { |
| | | foreach ($headers as $hr) |
| | | if (!preg_match('/^[a-z0-9-]+$/i', $hr)) |
| | | $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars'); |
| | | } |
| | | } |
| | | |
| | | if (empty($this->errors['tests'][$i]['header'])) |
| | | $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers; |
| | | |
| | | if ($type == 'exists') { |
| | | $this->form['tests'][$i]['test'] = 'exists'; |
| | | $this->form['tests'][$i]['arg'] = $cust_header; |
| | | } |
| | | else { |
| | | $this->form['tests'][$i]['test'] = 'header'; |
| | | $this->form['tests'][$i]['type'] = $type; |
| | | $this->form['tests'][$i]['arg1'] = $cust_header; |
| | | $this->form['tests'][$i]['arg2'] = $target; |
| | | |
| | | if ($target == '') |
| | | $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty'); |
| | | } |
| | | break; |
| | | } |
| | | $i++; |
| | | } |
| | | } |
| | | |
| | | $i = 0; |
| | | // actions |
| | | foreach($act_types as $idx => $type) { |
| | | $type = $this->strip_value($type); |
| | | $target = $this->strip_value($act_targets[$idx]); |
| | | |
| | | switch ($type) { |
| | | case 'fileinto': |
| | | case 'fileinto_copy': |
| | | $mailbox = $this->strip_value($mailboxes[$idx]); |
| | | $this->form['actions'][$i]['target'] = $mailbox; |
| | | if ($type == 'fileinto_copy') { |
| | | $type = 'fileinto'; |
| | | $this->form['actions'][$i]['copy'] = true; |
| | | } |
| | | break; |
| | | case 'reject': |
| | | case 'ereject': |
| | | $target = $this->strip_value($area_targets[$idx]); |
| | | $this->form['actions'][$i]['target'] = str_replace("\r\n", "\n", $target); |
| | | |
| | | // if ($target == '') |
| | | // $this->errors['actions'][$i]['targetarea'] = $this->gettext('cannotbeempty'); |
| | | break; |
| | | case 'redirect': |
| | | case 'redirect_copy': |
| | | $this->form['actions'][$i]['target'] = $target; |
| | | |
| | | if ($this->form['actions'][$i]['target'] == '') |
| | | $this->errors['actions'][$i]['target'] = $this->gettext('cannotbeempty'); |
| | | else if (!check_email($this->form['actions'][$i]['target'])) |
| | | $this->errors['actions'][$i]['target'] = $this->gettext('noemailwarning'); |
| | | |
| | | if ($type == 'redirect_copy') { |
| | | $type = 'redirect'; |
| | | $this->form['actions'][$i]['copy'] = true; |
| | | } |
| | | break; |
| | | case 'vacation': |
| | | $reason = $this->strip_value($reasons[$idx]); |
| | | $this->form['actions'][$i]['reason'] = str_replace("\r\n", "\n", $reason); |
| | | $this->form['actions'][$i]['days'] = $days[$idx]; |
| | | $this->form['actions'][$i]['addresses'] = explode(',', $addresses[$idx]); |
| | | // @TODO: vacation :subject, :mime, :from, :handle |
| | | |
| | | if ($this->form['actions'][$i]['addresses']) { |
| | | foreach($this->form['actions'][$i]['addresses'] as $aidx => $address) { |
| | | $address = trim($address); |
| | | if (!$address) |
| | | unset($this->form['actions'][$i]['addresses'][$aidx]); |
| | | else if(!check_email($address)) { |
| | | $this->errors['actions'][$i]['addresses'] = $this->gettext('noemailwarning'); |
| | | break; |
| | | } else |
| | | $this->form['actions'][$i]['addresses'][$aidx] = $address; |
| | | } |
| | | } |
| | | |
| | | if ($this->form['actions'][$i]['reason'] == '') |
| | | $this->errors['actions'][$i]['reason'] = $this->gettext('cannotbeempty'); |
| | | if ($this->form['actions'][$i]['days'] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i]['days'])) |
| | | $this->errors['actions'][$i]['days'] = $this->gettext('forbiddenchars'); |
| | | break; |
| | | } |
| | | |
| | | $this->form['actions'][$i]['type'] = $type; |
| | | $i++; |
| | | } |
| | | |
| | | if (!$this->errors) { |
| | | // zapis skryptu |
| | | if (!isset($this->script[$fid])) { |
| | | $fid = $this->sieve->script->add_rule($this->form); |
| | | $new = true; |
| | | } else |
| | | $fid = $this->sieve->script->update_rule($fid, $this->form); |
| | | |
| | | if ($fid !== false) |
| | | $save = $this->sieve->save(); |
| | | |
| | | if ($save && $fid !== false) { |
| | | $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); |
| | | $this->rc->output->add_script( |
| | | sprintf("rcmail.managesieve_updatelist('%s', '%s', %d, %d);", |
| | | isset($new) ? 'add' : 'update', Q($this->form['name']), |
| | | $fid, $this->form['disabled']), |
| | | 'foot'); |
| | | } |
| | | else { |
| | | $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); |
| | | // $this->rc->output->send(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | $this->managesieve_send(); |
| | | } |
| | | |
| | | private function managesieve_send() |
| | | { |
| | | // Handle form action |
| | | if (isset($_GET['_framed']) || isset($_POST['_framed'])) { |
| | | if (isset($_GET['_newset']) || isset($_POST['_newset'])) { |
| | | $this->rc->output->send('managesieve.setedit'); |
| | | } |
| | | else { |
| | | $this->rc->output->send('managesieve.filteredit'); |
| | | } |
| | | } else { |
| | | $this->rc->output->set_pagetitle($this->gettext('filters')); |
| | | $this->rc->output->send('managesieve.managesieve'); |
| | | } |
| | | } |
| | | |
| | | // return the filters list as HTML table |
| | | function filters_list($attrib) |
| | | { |
| | | // add id to message list table if not specified |
| | | if (!strlen($attrib['id'])) |
| | | $attrib['id'] = 'rcmfilterslist'; |
| | | |
| | | // define list of cols to be displayed |
| | | $a_show_cols = array('managesieve.filtername'); |
| | | |
| | | foreach($this->script as $idx => $filter) |
| | | $result[] = array( |
| | | 'managesieve.filtername' => $filter['name'], |
| | | 'id' => $idx, |
| | | 'class' => $filter['disabled'] ? 'disabled' : '', |
| | | ); |
| | | |
| | | // create XHTML table |
| | | $out = rcube_table_output($attrib, $result, $a_show_cols, 'id'); |
| | | |
| | | // set client env |
| | | $this->rc->output->add_gui_object('filterslist', $attrib['id']); |
| | | $this->rc->output->include_script('list.js'); |
| | | |
| | | // add some labels to client |
| | | $this->rc->output->add_label('managesieve.filterdeleteconfirm'); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | // return the filters list as <SELECT> |
| | | function filtersets_list($attrib) |
| | | { |
| | | // add id to message list table if not specified |
| | | if (!strlen($attrib['id'])) |
| | | $attrib['id'] = 'rcmfiltersetslist'; |
| | | |
| | | $list = $this->sieve->get_scripts(); |
| | | $active = $this->sieve->get_active(); |
| | | |
| | | $select = new html_select(array('name' => '_set', 'id' => $attrib['id'], |
| | | 'onchange' => 'rcmail.managesieve_set()')); |
| | | |
| | | if ($list) { |
| | | asort($list, SORT_LOCALE_STRING); |
| | | |
| | | foreach ($list as $set) |
| | | $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set); |
| | | } |
| | | |
| | | $out = $select->show($this->sieve->current); |
| | | |
| | | // set client env |
| | | $this->rc->output->add_gui_object('filtersetslist', $attrib['id']); |
| | | $this->rc->output->add_label( |
| | | 'managesieve.setdeleteconfirm', |
| | | 'managesieve.active', |
| | | 'managesieve.filtersetact', |
| | | 'managesieve.filtersetdeact' |
| | | ); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | function filter_frame($attrib) |
| | | { |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmfilterframe'; |
| | | |
| | | $attrib['name'] = $attrib['id']; |
| | | |
| | | $this->rc->output->set_env('contentframe', $attrib['name']); |
| | | $this->rc->output->set_env('blankpage', $attrib['src'] ? |
| | | $this->rc->output->abs_url($attrib['src']) : 'program/blank.gif'); |
| | | |
| | | return html::tag('iframe', $attrib); |
| | | } |
| | | |
| | | function filterset_form($attrib) |
| | | { |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmfiltersetform'; |
| | | |
| | | $out = '<form name="filtersetform" action="./" method="post" enctype="multipart/form-data">'."\n"; |
| | | |
| | | $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task)); |
| | | $hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save')); |
| | | $hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0))); |
| | | $hiddenfields->add(array('name' => '_newset', 'value' => 1)); |
| | | |
| | | $out .= $hiddenfields->show(); |
| | | |
| | | $name = get_input_value('_name', RCUBE_INPUT_POST); |
| | | $copy = get_input_value('_copy', RCUBE_INPUT_POST); |
| | | $selected = get_input_value('_from', RCUBE_INPUT_POST); |
| | | |
| | | $table = new html_table(array('cols' => 2)); |
| | | |
| | | // filter set name input |
| | | $input_name = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30, |
| | | 'class' => ($this->errors['name'] ? 'error' : ''))); |
| | | |
| | | $table->add('title', sprintf('<label for="%s"><b>%s:</b></label>', |
| | | '_name', Q($this->gettext('filtersetname')))); |
| | | $table->add(null, $input_name->show($name)); |
| | | |
| | | $from ='<div class="itemlist">'; |
| | | $from .= '<input type="radio" id="from_none" name="_from" value="none"' |
| | | .(!$selected || $selected=='none' ? ' checked="checked"' : '').'></input>'; |
| | | $from .= sprintf('<label for="%s">%s</label> ', 'from_none', Q($this->gettext('none'))); |
| | | |
| | | // filters set list |
| | | $list = $this->sieve->get_scripts(); |
| | | $active = $this->sieve->get_active(); |
| | | |
| | | $select = new html_select(array('name' => '_copy', 'id' => '_copy')); |
| | | |
| | | if (is_array($list)) { |
| | | asort($list, SORT_LOCALE_STRING); |
| | | |
| | | foreach ($list as $set) |
| | | $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set); |
| | | |
| | | $from .= '<br /><input type="radio" id="from_set" name="_from" value="set"' |
| | | .($selected=='set' ? ' checked="checked"' : '').'></input>'; |
| | | $from .= sprintf('<label for="%s">%s:</label> ', 'from_set', Q($this->gettext('fromset'))); |
| | | $from .= $select->show($copy); |
| | | } |
| | | |
| | | // script upload box |
| | | $upload = new html_inputfield(array('name' => '_file', 'id' => '_file', 'size' => 30, |
| | | 'type' => 'file', 'class' => ($this->errors['name'] ? 'error' : ''))); |
| | | |
| | | $from .= '<br /><input type="radio" id="from_file" name="_from" value="file"' |
| | | .($selected=='file' ? ' checked="checked"' : '').'></input>'; |
| | | $from .= sprintf('<label for="%s">%s:</label> ', 'from_file', Q($this->gettext('fromfile'))); |
| | | $from .= $upload->show(); |
| | | $from .= '</div>'; |
| | | |
| | | $table->add('title', '<label>'.$this->gettext('filters').':</label>'); |
| | | $table->add(null, $from); |
| | | |
| | | $out .= $table->show(); |
| | | |
| | | $this->rc->output->add_gui_object('sieveform', 'filtersetform'); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | function filter_form($attrib) |
| | | { |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmfilterform'; |
| | | |
| | | $fid = get_input_value('_fid', RCUBE_INPUT_GPC); |
| | | $scr = isset($this->form) ? $this->form : $this->script[$fid]; |
| | | |
| | | $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task)); |
| | | $hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save')); |
| | | $hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0))); |
| | | $hiddenfields->add(array('name' => '_fid', 'value' => $fid)); |
| | | |
| | | $out = '<form name="filterform" action="./" method="post">'."\n"; |
| | | $out .= $hiddenfields->show(); |
| | | |
| | | // 'any' flag |
| | | if (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not']) |
| | | $any = true; |
| | | |
| | | // filter name input |
| | | $field_id = '_name'; |
| | | $input_name = new html_inputfield(array('name' => '_name', 'id' => $field_id, 'size' => 30, |
| | | 'class' => ($this->errors['name'] ? 'error' : ''))); |
| | | |
| | | if (isset($scr)) |
| | | $input_name = $input_name->show($scr['name']); |
| | | else |
| | | $input_name = $input_name->show(); |
| | | |
| | | $out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s<br /><br />\n", |
| | | $field_id, Q($this->gettext('filtername')), $input_name); |
| | | |
| | | $out .= '<fieldset><legend>' . Q($this->gettext('messagesrules')) . "</legend>\n"; |
| | | |
| | | // any, allof, anyof radio buttons |
| | | $field_id = '_allof'; |
| | | $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'allof', |
| | | 'onclick' => 'rule_join_radio(\'allof\')', 'class' => 'radio')); |
| | | |
| | | if (isset($scr) && !$any) |
| | | $input_join = $input_join->show($scr['join'] ? 'allof' : ''); |
| | | else |
| | | $input_join = $input_join->show(); |
| | | |
| | | $out .= sprintf("%s<label for=\"%s\">%s</label> \n", |
| | | $input_join, $field_id, Q($this->gettext('filterallof'))); |
| | | |
| | | $field_id = '_anyof'; |
| | | $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'anyof', |
| | | 'onclick' => 'rule_join_radio(\'anyof\')', 'class' => 'radio')); |
| | | |
| | | if (isset($scr) && !$any) |
| | | $input_join = $input_join->show($scr['join'] ? '' : 'anyof'); |
| | | else |
| | | $input_join = $input_join->show('anyof'); // default |
| | | |
| | | $out .= sprintf("%s<label for=\"%s\">%s</label>\n", |
| | | $input_join, $field_id, Q($this->gettext('filteranyof'))); |
| | | |
| | | $field_id = '_any'; |
| | | $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'any', |
| | | 'onclick' => 'rule_join_radio(\'any\')', 'class' => 'radio')); |
| | | |
| | | $input_join = $input_join->show($any ? 'any' : ''); |
| | | |
| | | $out .= sprintf("%s<label for=\"%s\">%s</label>\n", |
| | | $input_join, $field_id, Q($this->gettext('filterany'))); |
| | | |
| | | $rows_num = isset($scr) ? sizeof($scr['tests']) : 1; |
| | | |
| | | $out .= '<div id="rules"'.($any ? ' style="display: none"' : '').'>'; |
| | | for ($x=0; $x<$rows_num; $x++) |
| | | $out .= $this->rule_div($fid, $x); |
| | | $out .= "</div>\n"; |
| | | |
| | | $out .= "</fieldset>\n"; |
| | | |
| | | // actions |
| | | $out .= '<fieldset><legend>' . Q($this->gettext('messagesactions')) . "</legend>\n"; |
| | | |
| | | $rows_num = isset($scr) ? sizeof($scr['actions']) : 1; |
| | | |
| | | $out .= '<div id="actions">'; |
| | | for ($x=0; $x<$rows_num; $x++) |
| | | $out .= $this->action_div($fid, $x); |
| | | $out .= "</div>\n"; |
| | | |
| | | $out .= "</fieldset>\n"; |
| | | |
| | | if ($scr['disabled']) { |
| | | $this->rc->output->set_env('rule_disabled', true); |
| | | } |
| | | $this->rc->output->add_label( |
| | | 'managesieve.ruledeleteconfirm', |
| | | 'managesieve.actiondeleteconfirm' |
| | | ); |
| | | $this->rc->output->add_gui_object('sieveform', 'filterform'); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | function rule_div($fid, $id, $div=true) |
| | | { |
| | | $rule = isset($this->form) ? $this->form['tests'][$id] : $this->script[$fid]['tests'][$id]; |
| | | $rows_num = isset($this->form) ? sizeof($this->form['tests']) : sizeof($this->script[$fid]['tests']); |
| | | |
| | | $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : ''; |
| | | |
| | | $out .= '<table><tr><td class="rowactions">'; |
| | | |
| | | // headers select |
| | | $select_header = new html_select(array('name' => "_header[]", 'id' => 'header'.$id, |
| | | 'onchange' => 'header_select(' .$id .')')); |
| | | foreach($this->headers as $name => $val) |
| | | $select_header->add(Q($this->gettext($name)), Q($val)); |
| | | $select_header->add(Q($this->gettext('size')), 'size'); |
| | | $select_header->add(Q($this->gettext('...')), '...'); |
| | | |
| | | // TODO: list arguments |
| | | |
| | | if ((isset($rule['test']) && $rule['test'] == 'header') |
| | | && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers)) |
| | | $out .= $select_header->show($rule['arg1']); |
| | | else if ((isset($rule['test']) && $rule['test'] == 'exists') |
| | | && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers)) |
| | | $out .= $select_header->show($rule['arg']); |
| | | else if (isset($rule['test']) && $rule['test'] == 'size') |
| | | $out .= $select_header->show('size'); |
| | | else if (isset($rule['test']) && $rule['test'] != 'true') |
| | | $out .= $select_header->show('...'); |
| | | else |
| | | $out .= $select_header->show(); |
| | | |
| | | $out .= '</td><td class="rowtargets">'; |
| | | |
| | | if ((isset($rule['test']) && $rule['test'] == 'header') |
| | | && (is_array($rule['arg1']) || !in_array($rule['arg1'], $this->headers))) |
| | | $custom = is_array($rule['arg1']) ? implode(', ', $rule['arg1']) : $rule['arg1']; |
| | | else if ((isset($rule['test']) && $rule['test'] == 'exists') |
| | | && (is_array($rule['arg']) || !in_array($rule['arg'], $this->headers))) |
| | | $custom = is_array($rule['arg']) ? implode(', ', $rule['arg']) : $rule['arg']; |
| | | |
| | | $out .= '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '"> |
| | | <input type="text" name="_custom_header[]" '. $this->error_class($id, 'test', 'header') |
| | | .' value="' .Q($custom). '" size="20" /> </div>' . "\n"; |
| | | |
| | | // matching type select (operator) |
| | | $select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id, |
| | | 'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'), |
| | | 'onchange' => 'rule_op_select('.$id.')')); |
| | | $select_op->add(Q($this->gettext('filtercontains')), 'contains'); |
| | | $select_op->add(Q($this->gettext('filternotcontains')), 'notcontains'); |
| | | $select_op->add(Q($this->gettext('filteris')), 'is'); |
| | | $select_op->add(Q($this->gettext('filterisnot')), 'notis'); |
| | | $select_op->add(Q($this->gettext('filterexists')), 'exists'); |
| | | $select_op->add(Q($this->gettext('filternotexists')), 'notexists'); |
| | | // $select_op->add(Q($this->gettext('filtermatches')), 'matches'); |
| | | // $select_op->add(Q($this->gettext('filternotmatches')), 'notmatches'); |
| | | |
| | | // target input (TODO: lists) |
| | | |
| | | if ($rule['test'] == 'header') { |
| | | $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['type']); |
| | | $target = $rule['arg2']; |
| | | } |
| | | else if ($rule['test'] == 'size') { |
| | | $out .= $select_op->show(); |
| | | if(preg_match('/^([0-9]+)(K|M|G)*$/', $rule['arg'], $matches)) { |
| | | $sizetarget = $matches[1]; |
| | | $sizeitem = $matches[2]; |
| | | } |
| | | } |
| | | else { |
| | | $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['test']); |
| | | $target = ''; |
| | | } |
| | | |
| | | $out .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '" |
| | | value="' .Q($target). '" size="20" ' . $this->error_class($id, 'test', 'target') |
| | | . ' style="display:' . ($rule['test']!='size' && $rule['test'] != 'exists' ? 'inline' : 'none') . '" />'."\n"; |
| | | |
| | | $select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id)); |
| | | $select_size_op->add(Q($this->gettext('filterunder')), 'under'); |
| | | $select_size_op->add(Q($this->gettext('filterover')), 'over'); |
| | | |
| | | $out .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">'; |
| | | $out .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : ''); |
| | | $out .= '<input type="text" name="_rule_size_target[]" value="'.$sizetarget.'" size="10" ' |
| | | . $this->error_class($id, 'test', 'sizetarget') .' /> |
| | | <input type="radio" name="_rule_size_item['.$id.']" value=""' |
| | | . (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('B').' |
| | | <input type="radio" name="_rule_size_item['.$id.']" value="K"' |
| | | . ($sizeitem=='K' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('KB').' |
| | | <input type="radio" name="_rule_size_item['.$id.']" value="M"' |
| | | . ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('MB').' |
| | | <input type="radio" name="_rule_size_item['.$id.']" value="G"' |
| | | . ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('GB'); |
| | | $out .= '</div>'; |
| | | $out .= '</td>'; |
| | | |
| | | // add/del buttons |
| | | $out .= '<td class="rowbuttons">'; |
| | | $out .= '<input type="button" id="ruleadd' . $id .'" value="'. Q($this->gettext('add')). '" |
| | | onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button" /> '; |
| | | $out .= '<input type="button" id="ruledel' . $id .'" value="'. Q($this->gettext('del')). '" |
| | | onclick="rcmail.managesieve_ruledel(' . $id .')" class="button' . ($rows_num<2 ? ' disabled' : '') .'"' |
| | | . ($rows_num<2 ? ' disabled="disabled"' : '') .' />'; |
| | | $out .= '</td></tr></table>'; |
| | | |
| | | $out .= $div ? "</div>\n" : ''; |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | function action_div($fid, $id, $div=true) |
| | | { |
| | | $action = isset($this->form) ? $this->form['actions'][$id] : $this->script[$fid]['actions'][$id]; |
| | | $rows_num = isset($this->form) ? sizeof($this->form['actions']) : sizeof($this->script[$fid]['actions']); |
| | | |
| | | $out = $div ? '<div class="actionrow" id="actionrow' .$id .'">'."\n" : ''; |
| | | |
| | | $out .= '<table><tr><td class="rowactions">'; |
| | | |
| | | // action select |
| | | $select_action = new html_select(array('name' => "_action_type[]", 'id' => 'action_type'.$id, |
| | | 'onchange' => 'action_type_select(' .$id .')')); |
| | | if (in_array('fileinto', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagemoveto')), 'fileinto'); |
| | | if (in_array('fileinto', $this->exts) && in_array('copy', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagecopyto')), 'fileinto_copy'); |
| | | $select_action->add(Q($this->gettext('messageredirect')), 'redirect'); |
| | | if (in_array('copy', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagesendcopy')), 'redirect_copy'); |
| | | if (in_array('reject', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagediscard')), 'reject'); |
| | | else if (in_array('ereject', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagediscard')), 'ereject'); |
| | | if (in_array('vacation', $this->exts)) |
| | | $select_action->add(Q($this->gettext('messagereply')), 'vacation'); |
| | | $select_action->add(Q($this->gettext('messagedelete')), 'discard'); |
| | | $select_action->add(Q($this->gettext('rulestop')), 'stop'); |
| | | |
| | | $select_type = $action['type']; |
| | | if (in_array($action['type'], array('fileinto', 'redirect')) && $action['copy']) { |
| | | $select_type .= '_copy'; |
| | | } |
| | | |
| | | $out .= $select_action->show($select_type); |
| | | $out .= '</td>'; |
| | | |
| | | // actions target inputs |
| | | $out .= '<td class="rowtargets">'; |
| | | // shared targets |
| | | $out .= '<input type="text" name="_action_target[]" id="action_target' .$id. '" ' |
| | | .'value="' .($action['type']=='redirect' ? Q($action['target'], 'strict', false) : ''). '" size="40" ' |
| | | .'style="display:' .($action['type']=='redirect' ? 'inline' : 'none') .'" ' |
| | | . $this->error_class($id, 'action', 'target') .' />'; |
| | | $out .= '<textarea name="_action_target_area[]" id="action_target_area' .$id. '" ' |
| | | .'rows="3" cols="40" '. $this->error_class($id, 'action', 'targetarea') |
| | | .'style="display:' .(in_array($action['type'], array('reject', 'ereject')) ? 'inline' : 'none') .'">' |
| | | . (in_array($action['type'], array('reject', 'ereject')) ? Q($action['target'], 'strict', false) : '') |
| | | . "</textarea>\n"; |
| | | |
| | | // vacation |
| | | $out .= '<div id="action_vacation' .$id.'" style="display:' .($action['type']=='vacation' ? 'inline' : 'none') .'">'; |
| | | $out .= '<span class="label">'. Q($this->gettext('vacationreason')) .'</span><br />' |
| | | .'<textarea name="_action_reason[]" id="action_reason' .$id. '" ' |
| | | .'rows="3" cols="40" '. $this->error_class($id, 'action', 'reason') . '>' |
| | | . Q($action['reason'], 'strict', false) . "</textarea>\n"; |
| | | $out .= '<br /><span class="label">' .Q($this->gettext('vacationaddresses')) . '</span><br />' |
| | | .'<input type="text" name="_action_addresses[]" ' |
| | | .'value="' . (is_array($action['addresses']) ? Q(implode(', ', $action['addresses']), 'strict', false) : $action['addresses']) . '" size="40" ' |
| | | . $this->error_class($id, 'action', 'addresses') .' />'; |
| | | $out .= '<br /><span class="label">' . Q($this->gettext('vacationdays')) . '</span><br />' |
| | | .'<input type="text" name="_action_days[]" ' |
| | | .'value="' .Q($action['days'], 'strict', false) . '" size="2" ' |
| | | . $this->error_class($id, 'action', 'days') .' />'; |
| | | $out .= '</div>'; |
| | | |
| | | // mailbox select |
| | | $out .= '<select id="action_mailbox' .$id. '" name="_action_mailbox[]" style="display:' |
| | | .(!isset($action) || $action['type']=='fileinto' ? 'inline' : 'none'). '">'; |
| | | |
| | | $this->rc->imap_connect(); |
| | | |
| | | $a_folders = $this->rc->imap->list_mailboxes(); |
| | | $delimiter = $this->rc->imap->get_hierarchy_delimiter(); |
| | | |
| | | // set mbox encoding |
| | | $mbox_encoding = $this->rc->config->get('managesieve_mbox_encoding', 'UTF7-IMAP'); |
| | | |
| | | if ($action['type'] == 'fileinto') |
| | | $mailbox = $action['target']; |
| | | else |
| | | $mailbox = ''; |
| | | |
| | | foreach ($a_folders as $folder) { |
| | | $utf7folder = $this->rc->imap->mod_mailbox($folder); |
| | | $names = explode($delimiter, rcube_charset_convert($folder, 'UTF7-IMAP')); |
| | | $name = $names[sizeof($names)-1]; |
| | | |
| | | if ($replace_delimiter = $this->rc->config->get('managesieve_replace_delimiter')) |
| | | $utf7folder = str_replace($delimiter, $replace_delimiter, $utf7folder); |
| | | |
| | | // convert to Sieve implementation encoding |
| | | $utf7folder = $this->mbox_encode($utf7folder, $mbox_encoding); |
| | | |
| | | if ($folder_class = rcmail_folder_classname($name)) |
| | | $foldername = $this->gettext($folder_class); |
| | | else |
| | | $foldername = $name; |
| | | |
| | | $out .= sprintf('<option value="%s"%s>%s%s</option>'."\n", |
| | | htmlspecialchars($utf7folder), |
| | | ($mailbox == $utf7folder ? ' selected="selected"' : ''), |
| | | str_repeat(' ', 4 * (sizeof($names)-1)), |
| | | Q(abbreviate_string($foldername, 40 - (2 * sizeof($names)-1)))); |
| | | } |
| | | $out .= '</select>'; |
| | | $out .= '</td>'; |
| | | |
| | | // add/del buttons |
| | | $out .= '<td class="rowbuttons">'; |
| | | $out .= '<input type="button" id="actionadd' . $id .'" value="'. Q($this->gettext('add')). '" |
| | | onclick="rcmail.managesieve_actionadd(' . $id .')" class="button" /> '; |
| | | $out .= '<input type="button" id="actiondel' . $id .'" value="'. Q($this->gettext('del')). '" |
| | | onclick="rcmail.managesieve_actiondel(' . $id .')" class="button' . ($rows_num<2 ? ' disabled' : '') .'"' |
| | | . ($rows_num<2 ? ' disabled="disabled"' : '') .' />'; |
| | | $out .= '</td>'; |
| | | |
| | | $out .= '</tr></table>'; |
| | | |
| | | $out .= $div ? "</div>\n" : ''; |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | private function genid() |
| | | { |
| | | $result = intval(rcube_timer()); |
| | | return $result; |
| | | } |
| | | |
| | | private function strip_value($str, $allow_html=false) |
| | | { |
| | | if (!$allow_html) |
| | | $str = strip_tags($str); |
| | | |
| | | return trim($str); |
| | | } |
| | | |
| | | private function error_class($id, $type, $target, $name_only=false) |
| | | { |
| | | // TODO: tooltips |
| | | if ($type == 'test' && isset($this->errors['tests'][$id][$target])) |
| | | return ($name_only ? 'error' : ' class="error"'); |
| | | else if ($type == 'action' && isset($this->errors['actions'][$id][$target])) |
| | | return ($name_only ? 'error' : ' class="error"'); |
| | | |
| | | return ''; |
| | | } |
| | | |
| | | private function mbox_encode($text, $encoding) |
| | | { |
| | | return rcube_charset_convert($text, 'UTF7-IMAP', $encoding); |
| | | } |
| | | } |
New file |
| | |
| | | /***** Roundcube|Filters styles *****/ |
| | | |
| | | |
| | | #filterslist |
| | | { |
| | | position: absolute; |
| | | left: 20px; |
| | | top: 120px; |
| | | bottom: 20px; |
| | | border: 1px solid #999999; |
| | | overflow: auto; |
| | | /* css hack for IE */ |
| | | height: expression((parseInt(document.documentElement.clientHeight)-140)+'px'); |
| | | } |
| | | |
| | | #filters-table |
| | | { |
| | | width: 100%; |
| | | table-layout: fixed; |
| | | /* css hack for IE */ |
| | | width: expression(document.getElementById('filterslist').clientWidth); |
| | | } |
| | | |
| | | #filters-table tbody td |
| | | { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | #filters-table tbody tr.disabled td |
| | | { |
| | | color: #999999; |
| | | } |
| | | |
| | | #filtersbuttons |
| | | { |
| | | position: absolute; |
| | | left: 20px; |
| | | top: 85px; |
| | | } |
| | | |
| | | #filtersetsbuttons |
| | | { |
| | | position: absolute; |
| | | left: 230px; |
| | | top: 85px; |
| | | } |
| | | |
| | | #filtersbuttons a, |
| | | #filtersetsbuttons a |
| | | { |
| | | display: block; |
| | | float: left; |
| | | } |
| | | |
| | | #filtersbuttons a.button, |
| | | #filtersbuttons a.buttonPas, |
| | | #filtersetsbuttons a.button, |
| | | #filtersetsbuttons a.buttonPas |
| | | { |
| | | display: block; |
| | | float: left; |
| | | width: 32px; |
| | | height: 32px; |
| | | padding: 0; |
| | | margin-right: 3px; |
| | | overflow: hidden; |
| | | background: url(managesieve_toolbar.png) 0 0 no-repeat transparent; |
| | | opacity: 0.99; /* this is needed to make buttons appear correctly in Chrome */ |
| | | } |
| | | |
| | | #filtersbuttons a.buttonPas, |
| | | #filtersetsbuttons a.buttonPas |
| | | { |
| | | filter: alpha(opacity=35); |
| | | opacity: 0.35; |
| | | } |
| | | |
| | | #filtersbuttons a.add { |
| | | background-position: 0px 0px; |
| | | } |
| | | |
| | | #filtersbuttons a.addsel { |
| | | background-position: 0 -32px; |
| | | } |
| | | |
| | | #filtersbuttons a.del { |
| | | background-position: -32px 0px; |
| | | } |
| | | |
| | | #filtersbuttons a.delsel { |
| | | background-position: -32px -32px; |
| | | } |
| | | |
| | | #filtersbuttons a.up { |
| | | background-position: -64px 0px; |
| | | } |
| | | |
| | | #filtersbuttons a.upsel { |
| | | background-position: -64px -32px; |
| | | } |
| | | |
| | | #filtersbuttons a.down { |
| | | background-position: -96px 0px; |
| | | } |
| | | |
| | | #filtersbuttons a.downsel { |
| | | background-position: -96px -32px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setadd { |
| | | background-position: -128px 0px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setaddsel { |
| | | background-position: -128px -32px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setdel { |
| | | background-position: -160px 0px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setdelsel { |
| | | background-position: -160px -32px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setset { |
| | | background-position: -192px 0px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setsetsel { |
| | | background-position: -192px -32px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setget { |
| | | background-position: -224px 0px; |
| | | } |
| | | |
| | | #filtersetsbuttons a.setgetsel { |
| | | background-position: -224px -32px; |
| | | } |
| | | |
| | | #filtersetselect |
| | | { |
| | | position: absolute; |
| | | left: 375px; |
| | | top: 90px; |
| | | } |
| | | |
| | | #filter-box |
| | | { |
| | | position: absolute; |
| | | top: 120px; |
| | | right: 20px; |
| | | bottom: 20px; |
| | | border: 1px solid #999999; |
| | | overflow: hidden; |
| | | /* css hack for IE */ |
| | | width: expression((parseInt(document.documentElement.clientWidth)-40-parseInt(document.getElementById('filterslist').offsetWidth))+'px'); |
| | | height: expression((parseInt(document.documentElement.clientHeight)-140)+'px'); |
| | | } |
| | | |
| | | #filter-frame |
| | | { |
| | | border: none; |
| | | } |
| | | |
| | | body.iframe |
| | | { |
| | | min-width: 740px; |
| | | width: expression(Math.max(740, document.documentElement.clientWidth)+'px'); |
| | | } |
| | | |
| | | #filter-form |
| | | { |
| | | min-width: 650px; |
| | | white-space: nowrap; |
| | | padding: 20px 10px 10px 10px; |
| | | } |
| | | |
| | | fieldset |
| | | { |
| | | background-color: white; |
| | | } |
| | | |
| | | legend, label |
| | | { |
| | | color: #666666; |
| | | } |
| | | |
| | | #rules, #actions |
| | | { |
| | | margin-top: 5px; |
| | | padding: 0; |
| | | border-collapse: collapse; |
| | | } |
| | | |
| | | div.rulerow, div.actionrow |
| | | { |
| | | width: auto; |
| | | padding: 2px; |
| | | white-space: nowrap; |
| | | border: 1px solid white; |
| | | } |
| | | |
| | | div.rulerow:hover, div.actionrow:hover |
| | | { |
| | | padding: 2px; |
| | | white-space: nowrap; |
| | | background: #F6F6F6; |
| | | border: 1px solid silver; |
| | | } |
| | | |
| | | div.rulerow table, div.actionrow table |
| | | { |
| | | padding: 0px; |
| | | width: 100%; |
| | | } |
| | | |
| | | td.rowbuttons |
| | | { |
| | | text-align: right; |
| | | white-space: nowrap; |
| | | width: 1%; |
| | | } |
| | | |
| | | td.rowactions |
| | | { |
| | | white-space: nowrap; |
| | | width: 1%; |
| | | } |
| | | |
| | | td.rowtargets |
| | | { |
| | | white-space: nowrap; |
| | | width: 98%; |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | input.disabled, input.disabled:hover |
| | | { |
| | | color: #999999; |
| | | } |
| | | |
| | | input.error, textarea.error |
| | | { |
| | | background-color: #FFFF88; |
| | | } |
| | | |
| | | input.box, |
| | | input.radio |
| | | { |
| | | border: 0; |
| | | } |
| | | |
| | | span.label |
| | | { |
| | | color: #666666; |
| | | font-size: 10px; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #footer |
| | | { |
| | | padding-top: 5px; |
| | | width: 100%; |
| | | } |
| | | |
| | | #footer .footerleft |
| | | { |
| | | padding-left: 2px; |
| | | white-space: nowrap; |
| | | float: left; |
| | | } |
| | | |
| | | #footer .footerright |
| | | { |
| | | padding-right: 2px; |
| | | white-space: nowrap; |
| | | text-align: right; |
| | | float: right; |
| | | } |
| | | |
| | | div .itemlist |
| | | { |
| | | line-height: 25px; |
| | | } |
| | | |
| | | div .itemlist input |
| | | { |
| | | vertical-align: middle; |
| | | } |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/managesieve.css" /> |
| | | </head> |
| | | <body class="iframe"> |
| | | |
| | | <script type="text/javascript"> |
| | | |
| | | function header_select(id) |
| | | { |
| | | var obj = document.getElementById('header'+id); |
| | | |
| | | if (obj.value == 'size') |
| | | { |
| | | document.getElementById('rule_size' + id).style.display = 'inline'; |
| | | document.getElementById('rule_op' + id).style.display = 'none'; |
| | | document.getElementById('rule_target' + id).style.display = 'none'; |
| | | document.getElementById('custom_header' + id).style.display = 'none'; |
| | | } |
| | | else |
| | | { |
| | | if (obj.value != '...') |
| | | document.getElementById('custom_header' + id).style.display = 'none'; |
| | | else |
| | | document.getElementById('custom_header' + id).style.display = 'inline'; |
| | | |
| | | document.getElementById('rule_size' + id).style.display = 'none'; |
| | | document.getElementById('rule_op' + id).style.display = 'inline'; |
| | | rule_op_select(id); |
| | | } |
| | | } |
| | | |
| | | function rule_op_select(id) |
| | | { |
| | | var obj = document.getElementById('rule_op'+id); |
| | | |
| | | if (obj.value == 'exists' || obj.value == 'notexists') |
| | | { |
| | | document.getElementById('rule_target' + id).style.display = 'none'; |
| | | } |
| | | else |
| | | { |
| | | document.getElementById('rule_target' + id).style.display = 'inline'; |
| | | } |
| | | } |
| | | |
| | | function action_type_select(id) |
| | | { |
| | | var obj = document.getElementById('action_type'+id); |
| | | |
| | | if (obj.value == 'fileinto' || obj.value == 'fileinto_copy') |
| | | { |
| | | document.getElementById('action_mailbox' + id).style.display = 'inline'; |
| | | document.getElementById('action_target' + id).style.display = 'none'; |
| | | document.getElementById('action_target_area' + id).style.display = 'none'; |
| | | document.getElementById('action_vacation' + id).style.display = 'none'; |
| | | } |
| | | else if (obj.value == 'redirect' || obj.value == 'redirect_copy') |
| | | { |
| | | document.getElementById('action_target' + id).style.display = 'inline'; |
| | | document.getElementById('action_mailbox' + id).style.display = 'none'; |
| | | document.getElementById('action_target_area' + id).style.display = 'none'; |
| | | document.getElementById('action_vacation' + id).style.display = 'none'; |
| | | } |
| | | else if (obj.value.match(/^reject|ereject$/)) |
| | | { |
| | | document.getElementById('action_target_area' + id).style.display = 'inline'; |
| | | document.getElementById('action_vacation' + id).style.display = 'none'; |
| | | document.getElementById('action_target' + id).style.display = 'none'; |
| | | document.getElementById('action_mailbox' + id).style.display = 'none'; |
| | | } |
| | | else if (obj.value == 'vacation') |
| | | { |
| | | document.getElementById('action_vacation' + id).style.display = 'inline'; |
| | | document.getElementById('action_target_area' + id).style.display = 'none'; |
| | | document.getElementById('action_target' + id).style.display = 'none'; |
| | | document.getElementById('action_mailbox' + id).style.display = 'none'; |
| | | } |
| | | else // discard, keep, stop |
| | | { |
| | | document.getElementById('action_target_area' + id).style.display = 'none'; |
| | | document.getElementById('action_vacation' + id).style.display = 'none'; |
| | | document.getElementById('action_target' + id).style.display = 'none'; |
| | | document.getElementById('action_mailbox' + id).style.display = 'none'; |
| | | } |
| | | } |
| | | |
| | | function rule_join_radio(value) |
| | | { |
| | | document.getElementById('rules').style.display = (value=='any' ? 'none' : 'block'); |
| | | } |
| | | </script> |
| | | |
| | | <div id="filter-title" class="boxtitle"><roundcube:label name="managesieve.filterdef" /></div> |
| | | |
| | | <div id="filter-form" class="boxcontent"> |
| | | <roundcube:object name="filterform" /> |
| | | |
| | | <div id="footer"> |
| | | <div class="footerleft"> |
| | | <roundcube:button command="plugin.managesieve-save" type="input" class="button mainaction" label="save" /> |
| | | </div> |
| | | <div class="footerright"> |
| | | <label for="disabled"><roundcube:label name="managesieve.filterdisabled" /></label> |
| | | <input type="checkbox" id="disabled" name="_disabled" value="1" /> |
| | | </div> |
| | | </div> |
| | | |
| | | </form> |
| | | </div> |
| | | |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/managesieve.css" /> |
| | | <script type="text/javascript" src="/functions.js"></script> |
| | | <script type="text/javascript" src="/splitter.js"></script> |
| | | |
| | | <style type="text/css"> |
| | | #filterslist { width: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter-5 : 210" />px; } |
| | | #filter-box { left: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220" />px; |
| | | <roundcube:exp expression="browser:ie ? ('width:expression((parseInt(this.parentNode.offsetWidth)-'.(!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220).')+\\'px\\');') : ''" /> |
| | | } |
| | | </style> |
| | | |
| | | </head> |
| | | <body> |
| | | |
| | | <roundcube:include file="/includes/taskbar.html" /> |
| | | <roundcube:include file="/includes/header.html" /> |
| | | <roundcube:include file="/includes/settingstabs.html" /> |
| | | |
| | | <div id="filtersbuttons"> |
| | | <roundcube:button command="plugin.managesieve-add" type="link" class="buttonPas add" classSel="button addsel" classAct="button add" title="managesieve.filteradd" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-del" type="link" class="buttonPas del" classSel="button delsel" classAct="button del" title="managesieve.filterdel" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-up" type="link" class="buttonPas up" classSel="button upsel" classAct="button up" title="managesieve.moveup" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-down" type="link" class="buttonPas down" classSel="button downsel" classAct="button down" title="managesieve.movedown" content=" " /> |
| | | </div> |
| | | |
| | | <div id="filtersetsbuttons"> |
| | | <roundcube:button command="plugin.managesieve-setadd" type="link" class="buttonPas setadd" classSel="button setaddsel" classAct="button setadd" title="managesieve.filtersetadd" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-setdel" type="link" class="buttonPas setdel" classSel="button setdelsel" classAct="button setdel" title="managesieve.filtersetdel" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-setact" type="link" class="buttonPas setset" classSel="button setsetsel" classAct="button setset" content=" " /> |
| | | <roundcube:button command="plugin.managesieve-setget" type="link" class="buttonPas setget" classSel="button setgetsel" classAct="button setget" title="managesieve.filtersetget" content=" " /> |
| | | </div> |
| | | <div id="filtersetselect"> |
| | | <roundcube:label name="managesieve.filterset" />: |
| | | <roundcube:object name="filtersetslist" id="filtersets-select" /> |
| | | </div> |
| | | |
| | | <div id="filterslist"> |
| | | <roundcube:object name="filterslist" id="filters-table" class="records-table" cellspacing="0" summary="Filters list" /> |
| | | </div> |
| | | <script type="text/javascript"> |
| | | var sieveviewsplit = new rcube_splitter({id:'sieveviewsplitter', p1: 'filterslist', p2: 'filter-box', orientation: 'v', relative: true, start: 215}); |
| | | rcmail.add_onload('sieveviewsplit.init()'); |
| | | </script> |
| | | <div id="filter-box"> |
| | | <roundcube:object name="filterframe" id="filter-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" /> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml"> |
| | | <head> |
| | | <title><roundcube:object name="pagetitle" /></title> |
| | | <roundcube:include file="/includes/links.html" /> |
| | | <link rel="stylesheet" type="text/css" href="/this/managesieve.css" /> |
| | | </head> |
| | | <body class="iframe"> |
| | | |
| | | <div id="filter-title" class="boxtitle"><roundcube:label name="managesieve.newfilterset" /></div> |
| | | |
| | | <div id="filter-form" class="boxcontent"> |
| | | <roundcube:object name="filtersetform" /> |
| | | |
| | | <p> |
| | | <roundcube:button command="plugin.managesieve-save" type="input" class="button mainaction" label="save" /> |
| | | </p> |
| | | |
| | | </form> |
| | | </div> |
| | | |
| | | |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | language/cs_CZ/labels.inc | |
| | | | | |
| | | | Language file of the Roundcube markasjunk plugin | |
| | | | Copyright (C) 2005-2009, Roundcube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Milan Kozak <hodza@hodza.net> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | @version $Id: labels.inc 2993 2009-09-26 18:32:07Z alec $ |
| | | |
| | | */ |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Označit jako Spam'; |
| | | $labels['reportedasjunk'] = 'Úspěšně nahlášeno jako Spam'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | // translation done by Ulli Heist - http://heist.hobby-site.org/ |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'als SPAM markieren'; |
| | | $labels['reportedasjunk'] = 'Erfolgreich als SPAM gemeldet'; |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Mark as Junk'; |
| | | $labels['reportedasjunk'] = 'Successfully reported as Junk'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Marcar como SPAM'; |
| | | $labels['reportedasjunk'] = 'Mensaje reportado como SPAM'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Marcar como SPAM'; |
| | | $labels['reportedasjunk'] = 'Mensaje informado como SPAM'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Märgista Rämpsuks'; |
| | | $labels['reportedasjunk'] = 'Edukalt Rämpsuks märgitud'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // EN-Revision: 3891 |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = '迷惑メールとして設定'; |
| | | $labels['reportedasjunk'] = '迷惑メールとして報告することに成功しました。'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Oznacz jako SPAM'; |
| | | $labels['reportedasjunk'] = 'Pomyślnie oznaczono jako SPAM'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Переместить в "СПАМ"'; |
| | | $labels['reportedasjunk'] = 'Перемещено в "СПАМ"'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = 'Märk som skräp'; |
| | | $labels['reportedasjunk'] = 'Framgångsrikt rapporterat som skräp'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['buttontitle'] = '標示為垃圾信'; |
| | | $labels['reportedasjunk'] = '成功回報垃圾信'; |
| | | |
| | | ?> |
New file |
| | |
| | | /* Mark-as-Junk plugin script */ |
| | | |
| | | function rcmail_markasjunk(prop) |
| | | { |
| | | if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length)) |
| | | return; |
| | | |
| | | var uids = rcmail.env.uid ? rcmail.env.uid : rcmail.message_list.get_selection().join(','); |
| | | |
| | | rcmail.set_busy(true, 'loading'); |
| | | rcmail.http_post('plugin.markasjunk', '_uid='+uids+'&_mbox='+urlencode(rcmail.env.mailbox), true); |
| | | } |
| | | |
| | | // callback for app-onload event |
| | | if (window.rcmail) { |
| | | rcmail.addEventListener('init', function(evt) { |
| | | |
| | | // register command (directly enable in message view mode) |
| | | rcmail.register_command('plugin.markasjunk', rcmail_markasjunk, rcmail.env.uid); |
| | | |
| | | // add event-listener to message list |
| | | if (rcmail.message_list) |
| | | rcmail.message_list.addEventListener('select', function(list){ |
| | | rcmail.enable_command('plugin.markasjunk', list.get_selection().length > 0); |
| | | }); |
| | | }) |
| | | } |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Mark as Junk |
| | | * |
| | | * Sample plugin that adds a new button to the mailbox toolbar |
| | | * to mark the selected messages as Junk and move them to the Junk folder |
| | | * |
| | | * @version @package_version@ |
| | | * @author Thomas Bruederli |
| | | */ |
| | | class markasjunk extends rcube_plugin |
| | | { |
| | | public $task = 'mail'; |
| | | |
| | | function init() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $this->register_action('plugin.markasjunk', array($this, 'request_action')); |
| | | |
| | | if ($rcmail->action == '' || $rcmail->action == 'show') { |
| | | $skin_path = $this->local_skin_path(); |
| | | $this->include_script('markasjunk.js'); |
| | | $this->add_texts('localization', true); |
| | | $this->add_button(array( |
| | | 'command' => 'plugin.markasjunk', |
| | | 'imagepas' => $skin_path.'/junk_pas.png', |
| | | 'imageact' => $skin_path.'/junk_act.png', |
| | | 'title' => 'markasjunk.buttontitle'), 'toolbar'); |
| | | } |
| | | } |
| | | |
| | | function request_action() |
| | | { |
| | | $this->add_texts('localization'); |
| | | |
| | | $GLOBALS['IMAP_FLAGS']['JUNK'] = 'Junk'; |
| | | $GLOBALS['IMAP_FLAGS']['NONJUNK'] = 'NonJunk'; |
| | | |
| | | $uids = get_input_value('_uid', RCUBE_INPUT_POST); |
| | | $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail->imap->unset_flag($uids, 'NONJUNK'); |
| | | $rcmail->imap->set_flag($uids, 'JUNK'); |
| | | |
| | | if (($junk_mbox = $rcmail->config->get('junk_mbox')) && $mbox != $junk_mbox) { |
| | | $rcmail->output->command('move_messages', $junk_mbox); |
| | | } |
| | | |
| | | $rcmail->output->command('display_message', $this->gettext('reportedasjunk'), 'confirmation'); |
| | | $rcmail->output->send(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 |
| | | http://pear.php.net/dtd/tasks-1.0.xsd |
| | | http://pear.php.net/dtd/package-2.0 |
| | | http://pear.php.net/dtd/package-2.0.xsd"> |
| | | <name>markasjunk</name> |
| | | <channel>pear.roundcube.net</channel> |
| | | <summary>Mark messages as Junk</summary> |
| | | <description>Adds a new button to the mailbox toolbar to mark the selected messages as Junk and move them to the configured Junk folder.</description> |
| | | <lead> |
| | | <name>Thomas Bruederli</name> |
| | | <user>thomasb</user> |
| | | <email>roundcube@gmail.com</email> |
| | | <active>yes</active> |
| | | </lead> |
| | | <date>2010-03-29</date> |
| | | <time>13:20:00</time> |
| | | <version> |
| | | <release>1.0</release> |
| | | <api>1.0</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license> |
| | | <notes>-</notes> |
| | | <contents> |
| | | <dir baseinstalldir="/" name="/"> |
| | | <file name="markasjunk.php" role="php"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <file name="markasjunk.js" role="data"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <file name="localization/en_US.inc" role="data"></file> |
| | | <file name="localization/cs_CZ.inc" role="data"></file> |
| | | <file name="localization/et_EE.inc" role="data"></file> |
| | | <file name="localization/pl_PL.inc" role="data"></file> |
| | | <file name="localization/ru_RU.inc" role="data"></file> |
| | | <file name="localization/sv_DE.inc" role="data"></file> |
| | | <file name="skins/default/junk_act.png" role="data"></file> |
| | | <file name="skins/default/junk_pas.png" role="data"></file> |
| | | </dir> |
| | | <!-- / --> |
| | | </contents> |
| | | <dependencies> |
| | | <required> |
| | | <php> |
| | | <min>5.2.1</min> |
| | | </php> |
| | | <pearinstaller> |
| | | <min>1.7.0</min> |
| | | </pearinstaller> |
| | | </required> |
| | | </dependencies> |
| | | <phprelease/> |
| | | </package> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Prosím doplňte své jméno a e-mail'; |
| | | $labels['identitydialoghint'] = 'Tento dialog se objeví pouze při prvním přihlášení.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Bitte vervollständigen Sie Ihre Absender-Informationen'; |
| | | $labels['identitydialoghint'] = 'Dieser Dialog erscheint nur einmal beim ersten Login.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Bitte vervollständigen Sie Ihre Absender-Informationen'; |
| | | $labels['identitydialoghint'] = 'Dieser Dialog erscheint nur einmal beim ersten Login.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Please complete your sender identity'; |
| | | $labels['identitydialoghint'] = 'This box only appears once at the first login.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Por favor completa tus datos'; |
| | | $labels['identitydialoghint'] = 'Este diálogo sólo aparece la primera vez que te conectas.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Palun täida oma saatja identiteet'; |
| | | $labels['identitydialoghint'] = 'See kast ilmub ainult esimesel sisselogimisel.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Per favore completa le informazioni riguardo la tua identità'; |
| | | $labels['identitydialoghint'] = 'Questa finestra comparirà una volta sola al primo accesso'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | // EN-Revision: 3891 |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = '送信者情報の入力を完了してください。'; |
| | | $labels['identitydialoghint'] = 'このボックスには最初のログイン時に一度だけ表示されます。'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Vul uw correcte identiteitgegevens in a.u.b.'; |
| | | $labels['identitydialoghint'] = 'Dit scherm verschijnt enkel bij uw eerste login.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Uzupełnij tożsamość nadawcy'; |
| | | $labels['identitydialoghint'] = 'To okno pojawia się tylko przy pierwszym logowaniu.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Пожалуйста, укажите Ваше имя.'; |
| | | $labels['identitydialoghint'] = 'Данное сообщение отображается только при первом входе.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Izberite identiteto za pošiljanje'; |
| | | $labels['identitydialoghint'] = 'To okno se prikaže le ob prvi prijavi v spletno pošto.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = 'Vänligen fyll i namn och avsändaradress under personliga inställningar'; |
| | | $labels['identitydialoghint'] = 'Informationen visas endast vid första inloggningen.'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | $labels = array(); |
| | | $labels['identitydialogtitle'] = '請完成您的身份資訊'; |
| | | $labels['identitydialoghint'] = '此視窗只會於第一次登入時出現。'; |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Present identities settings dialog to new users |
| | | * |
| | | * When a new user is created, this plugin checks the default identity |
| | | * and sets a session flag in case it is incomplete. An overlay box will appear |
| | | * on the screen until the user has reviewed/completed his identity. |
| | | * |
| | | * @version @package_version@ |
| | | * @author Thomas Bruederli |
| | | */ |
| | | class new_user_dialog extends rcube_plugin |
| | | { |
| | | public $task = 'login|mail'; |
| | | |
| | | function init() |
| | | { |
| | | $this->add_hook('identity_create', array($this, 'create_identity')); |
| | | $this->register_action('plugin.newusersave', array($this, 'save_data')); |
| | | |
| | | // register additional hooks if session flag is set |
| | | if ($_SESSION['plugin.newuserdialog']) { |
| | | $this->add_hook('render_page', array($this, 'render_page')); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check newly created identity at first login |
| | | */ |
| | | function create_identity($p) |
| | | { |
| | | // set session flag when a new user was created and the default identity seems to be incomplete |
| | | if ($p['login'] && !$p['complete']) |
| | | $_SESSION['plugin.newuserdialog'] = true; |
| | | } |
| | | |
| | | /** |
| | | * Callback function when HTML page is rendered |
| | | * We'll add an overlay box here. |
| | | */ |
| | | function render_page($p) |
| | | { |
| | | if ($_SESSION['plugin.newuserdialog'] && $p['template'] == 'mail') { |
| | | $this->add_texts('localization'); |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | $identity = $rcmail->user->get_identity(); |
| | | $identities_level = intval($rcmail->config->get('identities_level', 0)); |
| | | |
| | | // compose user-identity dialog |
| | | $table = new html_table(array('cols' => 2)); |
| | | |
| | | $table->add('title', $this->gettext('name')); |
| | | $table->add(null, html::tag('input', array( |
| | | 'type' => 'text', |
| | | 'name' => '_name', |
| | | 'value' => $identity['name'] |
| | | ))); |
| | | |
| | | $table->add('title', $this->gettext('email')); |
| | | $table->add(null, html::tag('input', array( |
| | | 'type' => 'text', |
| | | 'name' => '_email', |
| | | 'value' => $identity['email'], |
| | | 'disabled' => ($identities_level == 1 || $identities_level == 3) |
| | | ))); |
| | | |
| | | // add overlay input box to html page |
| | | $rcmail->output->add_footer(html::div(array('id' => 'newuseroverlay'), |
| | | html::tag('form', array( |
| | | 'action' => $rcmail->url('plugin.newusersave'), |
| | | 'method' => 'post'), |
| | | html::tag('h3', null, Q($this->gettext('identitydialogtitle'))) . |
| | | html::p('hint', Q($this->gettext('identitydialoghint'))) . |
| | | $table->show() . |
| | | html::p(array('class' => 'formbuttons'), |
| | | html::tag('input', array('type' => 'submit', |
| | | 'class' => 'button mainaction', 'value' => $this->gettext('save')))) |
| | | ) |
| | | )); |
| | | |
| | | // disable keyboard events for messages list (#1486726) |
| | | $rcmail->output->add_script( |
| | | "$(document).ready(function () { |
| | | rcmail.message_list.key_press = function(){}; |
| | | rcmail.message_list.key_down = function(){}; |
| | | });", 'foot'); |
| | | |
| | | $this->include_stylesheet('newuserdialog.css'); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handler for submitted form |
| | | * |
| | | * Check fields and save to default identity if valid. |
| | | * Afterwards the session flag is removed and we're done. |
| | | */ |
| | | function save_data() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $identity = $rcmail->user->get_identity(); |
| | | $identities_level = intval($rcmail->config->get('identities_level', 0)); |
| | | |
| | | $save_data = array( |
| | | 'name' => get_input_value('_name', RCUBE_INPUT_POST), |
| | | 'email' => get_input_value('_email', RCUBE_INPUT_POST), |
| | | ); |
| | | |
| | | // don't let the user alter the e-mail address if disabled by config |
| | | if ($identities_level == 1 || $identities_level == 3) |
| | | $save_data['email'] = $identity['email']; |
| | | |
| | | // save data if not empty |
| | | if (!empty($save_data['name']) && !empty($save_data['email'])) { |
| | | $rcmail->user->update_identity($identity['identity_id'], $save_data); |
| | | $rcmail->session->remove('plugin.newuserdialog'); |
| | | } |
| | | |
| | | $rcmail->output->redirect(''); |
| | | } |
| | | |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | /** Styles for the new-user-dialog overlay box */ |
| | | |
| | | #newuseroverlay { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | z-index: 10000; |
| | | background: rgba(0,0,0,0.5) !important; |
| | | background: #333; |
| | | |
| | | /** IE hacks */ |
| | | filter: alpha(opacity=90); |
| | | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; |
| | | width: expression(document.documentElement.clientWidth+'px'); |
| | | height: expression(document.documentElement.clientHeight+'px'); |
| | | } |
| | | |
| | | #newuseroverlay h3 { |
| | | color: #333; |
| | | font-size: normal; |
| | | margin-top: 0.5em; |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | #newuseroverlay p.hint { |
| | | margin-top: 0.5em; |
| | | font-style: italic; |
| | | } |
| | | |
| | | #newuseroverlay form { |
| | | width: 32em; |
| | | margin: 8em auto; |
| | | padding: 1em 2em; |
| | | background: #F6F6F6; |
| | | border: 2px solid #555; |
| | | border-radius: 6px; |
| | | -moz-border-radius: 6px; |
| | | -webkit-border-radius: 6px; |
| | | } |
| | | |
| | | #newuseroverlay table td.title |
| | | { |
| | | color: #666; |
| | | text-align: right; |
| | | padding-right: 1em; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #newuseroverlay table td input |
| | | { |
| | | width: 20em; |
| | | } |
| | | |
| | | #newuseroverlay .formbuttons { |
| | | margin-top: 1.5em; |
| | | text-align: center; |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 |
| | | http://pear.php.net/dtd/tasks-1.0.xsd |
| | | http://pear.php.net/dtd/package-2.0 |
| | | http://pear.php.net/dtd/package-2.0.xsd"> |
| | | <name>new_user_dialog</name> |
| | | <channel>pear.roundcube.net</channel> |
| | | <summary>Present identities settings dialog to new users</summary> |
| | | <description>When a new user is created, this plugin checks the default identity and sets a session flag in case it is incomplete. An overlay box will appear on the screen until the user has reviewed/completed his identity.</description> |
| | | <lead> |
| | | <name>Thomas Bruederli</name> |
| | | <user>thomasb</user> |
| | | <email>roundcube@gmail.com</email> |
| | | <active>yes</active> |
| | | </lead> |
| | | <date>2010-05-27</date> |
| | | <time>12:00:00</time> |
| | | <version> |
| | | <release>1.2</release> |
| | | <api>1.0</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license> |
| | | <notes> |
| | | - Add overlay box only to mail task main template |
| | | - Fix possible error on form submission (#1486103) |
| | | </notes> |
| | | <contents> |
| | | <dir baseinstalldir="/" name="/"> |
| | | <file name="new_user_dialog.php" role="php"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <file name="newuserdialog.css" role="data"> |
| | | <tasks:replace from="@name@" to="name" type="package-info"/> |
| | | <tasks:replace from="@package_version@" to="version" type="package-info"/> |
| | | </file> |
| | | <dir name="localization"> |
| | | <file name="en_US.inc" role="data" /> |
| | | <file name="cs_CZ.inc" role="data" /> |
| | | <file name="de_CH.inc" role="data" /> |
| | | <file name="de_DE.inc" role="data" /> |
| | | <file name="es_ES.inc" role="data" /> |
| | | <file name="et_EE.inc" role="data" /> |
| | | <file name="it_IT.inc" role="data" /> |
| | | <file name="pl_PL.inc" role="data" /> |
| | | <file name="ru_RU.inc" role="data" /> |
| | | <file name="sv_DE.inc" role="data" /> |
| | | <file name="zh_TW.inc" role="data" /> |
| | | </dir> |
| | | </dir> |
| | | <!-- / --> |
| | | </contents> |
| | | <dependencies> |
| | | <required> |
| | | <php> |
| | | <min>5.2.1</min> |
| | | </php> |
| | | <pearinstaller> |
| | | <min>1.7.0</min> |
| | | </pearinstaller> |
| | | </required> |
| | | </dependencies> |
| | | <phprelease/> |
| | | <changelog> |
| | | <release> |
| | | <date>2010-03-29</date> |
| | | <time>13:20:00</time> |
| | | <version> |
| | | <release>1.0</release> |
| | | <api>1.0</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license> |
| | | <notes></notes> |
| | | </release> |
| | | <release> |
| | | <date>2010-05-13</date> |
| | | <time>19:35:00</time> |
| | | <version> |
| | | <release>1.1</release> |
| | | <api>1.0</api> |
| | | </version> |
| | | <stability> |
| | | <release>stable</release> |
| | | <api>stable</api> |
| | | </stability> |
| | | <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license> |
| | | <notes> |
| | | - Fix space bar and backspace buttons not working (#1486726) |
| | | </notes> |
| | | </release> |
| | | </changelog> |
| | | </package> |
New file |
| | |
| | | <?php |
| | | /** |
| | | * New user identity |
| | | * |
| | | * Populates a new user's default identity from LDAP on their first visit. |
| | | * |
| | | * This plugin requires that a working public_ldap directory be configured. |
| | | * |
| | | * @version 1.0 |
| | | * @author Kris Steinhoff |
| | | * |
| | | * Example configuration: |
| | | * |
| | | * // The id of the address book to use to automatically set a new |
| | | * // user's full name in their new identity. (This should be an |
| | | * // string, which refers to the $rcmail_config['ldap_public'] array.) |
| | | * $rcmail_config['new_user_identity_addressbook'] = 'People'; |
| | | * |
| | | * // When automatically setting a new users's full name in their |
| | | * // new identity, match the user's login name against this field. |
| | | * $rcmail_config['new_user_identity_match'] = 'uid'; |
| | | */ |
| | | class new_user_identity extends rcube_plugin |
| | | { |
| | | public $task = 'login'; |
| | | |
| | | function init() |
| | | { |
| | | $this->add_hook('user_create', array($this, 'lookup_user_name')); |
| | | } |
| | | |
| | | function lookup_user_name($args) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | if ($addressbook = $rcmail->config->get('new_user_identity_addressbook')) { |
| | | $match = $rcmail->config->get('new_user_identity_match'); |
| | | $ldap = $rcmail->get_address_book($addressbook); |
| | | $ldap->prop['search_fields'] = array($match); |
| | | $results = $ldap->search($match, $args['user'], TRUE); |
| | | if (count($results->records) == 1) { |
| | | $args['user_name'] = $results->records[0]['name']; |
| | | if (!$args['user_email'] && strpos($results->records[0]['email'], '@')) { |
| | | $args['user_email'] = $results->records[0]['email']; |
| | | } |
| | | } |
| | | } |
| | | return $args; |
| | | } |
| | | } |
| | | ?> |
New file |
| | |
| | | ----------------------------------------------------------------------- |
| | | Password Plugin for Roundcube |
| | | ----------------------------------------------------------------------- |
| | | |
| | | Plugin that adds a possibility to change user password using many |
| | | methods (drivers) via Settings/Password tab. |
| | | |
| | | ----------------------------------------------------------------------- |
| | | This program is free software; you can redistribute it and/or modify |
| | | it under the terms of the GNU General Public License version 2 |
| | | as published by the Free Software Foundation. |
| | | |
| | | This program is distributed in the hope that it will be useful, |
| | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | | GNU General Public License for more details. |
| | | |
| | | You should have received a copy of the GNU General Public License along |
| | | with this program; if not, write to the Free Software Foundation, Inc., |
| | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| | | |
| | | @version @package_version@ |
| | | @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> |
| | | @author <see driver files for driver authors> |
| | | ----------------------------------------------------------------------- |
| | | |
| | | 1. Configuration |
| | | 2. Drivers |
| | | 2.1. Database (sql) |
| | | 2.2. Cyrus/SASL (sasl) |
| | | 2.3. Poppassd/Courierpassd (poppassd) |
| | | 2.4. LDAP (ldap) |
| | | 2.5. DirectAdmin Control Panel (directadmin) |
| | | 2.6. cPanel (cpanel) |
| | | 2.7. XIMSS/Communigate (ximms) |
| | | 2.8. Virtualmin (virtualmin) |
| | | 2.9. hMailServer (hmail) |
| | | 2.10. PAM (pam) |
| | | 2.11. Chpasswd (chpasswd) |
| | | 2.12. LDAP - no PEAR (ldap_simple) |
| | | 2.13. XMail (xmail) |
| | | 3. Driver API |
| | | |
| | | |
| | | 1. Configuration |
| | | ---------------- |
| | | |
| | | Copy config.inc.php.dist to config.inc.php and set the options as described |
| | | within the file. |
| | | |
| | | |
| | | 2. Drivers |
| | | ---------- |
| | | |
| | | Password plugin supports many password change mechanisms which are |
| | | handled by included drivers. Just pass driver name in 'password_driver' option. |
| | | |
| | | |
| | | 2.1. Database (sql) |
| | | ------------------- |
| | | |
| | | You can specify which database to connect by 'password_db_dsn' option and |
| | | what SQL query to execute by 'password_query'. See main.inc.php file for |
| | | more info. |
| | | |
| | | Example implementations of an update_passwd function: |
| | | |
| | | - This is for use with LMS (http://lms.org.pl) database and postgres: |
| | | |
| | | CREATE OR REPLACE FUNCTION update_passwd(hash text, account text) RETURNS integer AS $$ |
| | | DECLARE |
| | | res integer; |
| | | BEGIN |
| | | UPDATE passwd SET password = hash |
| | | WHERE login = split_part(account, '@', 1) |
| | | AND domainid = (SELECT id FROM domains WHERE name = split_part(account, '@', 2)) |
| | | RETURNING id INTO res; |
| | | RETURN res; |
| | | END; |
| | | $$ LANGUAGE plpgsql SECURITY DEFINER; |
| | | |
| | | - This is for use with a SELECT update_passwd(%o,%c,%u) query |
| | | Updates the password only when the old password matches the MD5 password |
| | | in the database |
| | | |
| | | CREATE FUNCTION update_password (oldpass text, cryptpass text, user text) RETURNS text |
| | | MODIFIES SQL DATA |
| | | BEGIN |
| | | DECLARE currentsalt varchar(20); |
| | | DECLARE error text; |
| | | SET error = 'incorrect current password'; |
| | | SELECT substring_index(substr(user.password,4),_latin1'$',1) INTO currentsalt FROM users WHERE username=user; |
| | | SELECT '' INTO error FROM users WHERE username=user AND password=ENCRYPT(oldpass,currentsalt); |
| | | UPDATE users SET password=cryptpass WHERE username=user AND password=ENCRYPT(oldpass,currentsalt); |
| | | RETURN error; |
| | | END |
| | | |
| | | Example SQL UPDATEs: |
| | | |
| | | - Plain text passwords: |
| | | UPDATE users SET password=%p WHERE username=%u AND password=%o AND domain=%h LIMIT 1 |
| | | |
| | | - Crypt text passwords: |
| | | UPDATE users SET password=%c WHERE username=%u LIMIT 1 |
| | | |
| | | - Use a MYSQL crypt function (*nix only) with random 8 character salt |
| | | UPDATE users SET password=ENCRYPT(%p,concat(_utf8'$1$',right(md5(rand()),8),_utf8'$')) WHERE username=%u LIMIT 1 |
| | | |
| | | - MD5 stored passwords: |
| | | UPDATE users SET password=MD5(%p) WHERE username=%u AND password=MD5(%o) LIMIT 1 |
| | | |
| | | |
| | | 2.2. Cyrus/SASL (sasl) |
| | | ---------------------- |
| | | |
| | | Cyrus SASL database authentication allows your Cyrus+Roundcube |
| | | installation to host mail users without requiring a Unix Shell account! |
| | | |
| | | This driver only covers the "sasldb" case when using Cyrus SASL. Kerberos |
| | | and PAM authentication mechanisms will require other techniques to enable |
| | | user password manipulations. |
| | | |
| | | Cyrus SASL includes a shell utility called "saslpasswd" for manipulating |
| | | user passwords in the "sasldb" database. This plugin attempts to use |
| | | this utility to perform password manipulations required by your webmail |
| | | users without any administrative interaction. Unfortunately, this |
| | | scheme requires that the "saslpasswd" utility be run as the "cyrus" |
| | | user - kind of a security problem since we have chosen to SUID a small |
| | | script which will allow this to happen. |
| | | |
| | | This driver is based on the Squirrelmail Change SASL Password Plugin. |
| | | See http://www.squirrelmail.org/plugin_view.php?id=107 for details. |
| | | |
| | | Installation: |
| | | |
| | | Change into the drivers directory. Edit the chgsaslpasswd.c file as is |
| | | documented within it. |
| | | |
| | | Compile the wrapper program: |
| | | gcc -o chgsaslpasswd chgsaslpasswd.c |
| | | |
| | | Chown the compiled chgsaslpasswd binary to the cyrus user and group |
| | | that your browser runs as, then chmod them to 4550. |
| | | |
| | | For example, if your cyrus user is 'cyrus' and the apache server group is |
| | | 'nobody' (I've been told Redhat runs Apache as user 'apache'): |
| | | |
| | | chown cyrus:nobody chgsaslpasswd |
| | | chmod 4550 chgsaslpasswd |
| | | |
| | | Stephen Carr has suggested users should try to run the scripts on a test |
| | | account as the cyrus user eg; |
| | | |
| | | su cyrus -c "./chgsaslpasswd -p test_account" |
| | | |
| | | This will allow you to make sure that the script will work for your setup. |
| | | Should the script not work, make sure that: |
| | | 1) the user the script runs as has access to the saslpasswd|saslpasswd2 |
| | | file and proper permissions |
| | | 2) make sure the user in the chgsaslpasswd.c file is set correctly. |
| | | This could save you some headaches if you are the paranoid type. |
| | | |
| | | |
| | | 2.3. Poppassd/Courierpassd (poppassd) |
| | | ------------------------------------- |
| | | |
| | | You can specify which host to connect to via 'password_pop_host' and |
| | | what port via 'password_pop_port'. See config.inc.php file for more info. |
| | | |
| | | |
| | | 2.4. LDAP (ldap) |
| | | ---------------- |
| | | |
| | | See config.inc.php file. Requires PEAR::Net_LDAP2 package. |
| | | |
| | | |
| | | 2.5. DirectAdmin Control Panel (directadmin) |
| | | -------------------------------------------- |
| | | |
| | | You can specify which host to connect to via 'password_directadmin_host' |
| | | and what port via 'password_direactadmin_port'. See config.inc.php file |
| | | for more info. |
| | | |
| | | |
| | | 2.6. cPanel (cpanel) |
| | | -------------------- |
| | | |
| | | You can specify parameters for HTTP connection to cPanel's admin |
| | | interface. See config.inc.php file for more info. |
| | | |
| | | |
| | | 2.7. XIMSS/Communigate (ximms) |
| | | ------------------------------ |
| | | |
| | | You can specify which host and port to connect to via 'password_ximss_host' |
| | | and 'password_ximss_port'. See config.inc.php file for more info. |
| | | |
| | | |
| | | 2.8. Virtualmin (virtualmin) |
| | | ---------------------------- |
| | | |
| | | As in sasl driver this one allows to change password using shell |
| | | utility called "virtualmin". See drivers/chgvirtualminpasswd.c for |
| | | installation instructions. |
| | | |
| | | |
| | | 2.9. hMailServer (hmail) |
| | | ------------------------ |
| | | |
| | | Requires PHP COM (Windows only). |
| | | |
| | | |
| | | 2.10. PAM (pam) |
| | | --------------- |
| | | |
| | | This driver is for changing passwords of shell users authenticated with PAM. |
| | | Requires PECL's PAM exitension to be installed (http://pecl.php.net/package/PAM). |
| | | |
| | | |
| | | 2.11. Chpasswd (chpasswd) |
| | | ------------------------- |
| | | |
| | | Driver that adds functionality to change the systems user password via |
| | | the 'chpasswd' command. See config.inc.php file. |
| | | |
| | | Attached wrapper script (chpass-wrapper.py) restricts password changes |
| | | to uids >= 1000 and can deny requests based on a blacklist. |
| | | |
| | | |
| | | 2.12. LDAP - no PEAR (ldap_simple) |
| | | ----------------------------------- |
| | | |
| | | It's rewritten ldap driver that doesn't require the Net_LDAP2 PEAR extension. |
| | | It uses directly PHP's ldap module functions instead (as Roundcube does). |
| | | |
| | | This driver is fully compatible with the ldap driver, but |
| | | does not require (or uses) the |
| | | $rcmail_config['password_ldap_force_replace'] variable. |
| | | Other advantages: |
| | | * Connects only once with the LDAP server when using the search user. |
| | | * Does not read the DN, but only replaces the password within (that is |
| | | why the 'force replace' is always used). |
| | | |
| | | |
| | | 2.13. XMail (xmail) |
| | | ----------------------------------- |
| | | |
| | | Driver for XMail (www.xmailserver.org). See config.inc.php file for configuration description. |
| | | |
| | | |
| | | 3. Driver API |
| | | ------------- |
| | | |
| | | Driver file (<driver_name>.php) must define 'password_save' function with |
| | | two arguments. First - current password, second - new password. Function |
| | | may return PASSWORD_SUCCESS on success or any of PASSWORD_CONNECT_ERROR, |
| | | PASSWORD_CRYPT_ERROR, PASSWORD_ERROR when driver was unable to change password. |
| | | See existing drivers in drivers/ directory for examples. |
New file |
| | |
| | | <?php |
| | | |
| | | // Password Plugin options |
| | | // ----------------------- |
| | | // A driver to use for password change. Default: "sql". |
| | | // See README file for list of supported driver names. |
| | | $rcmail_config['password_driver'] = 'sql'; |
| | | |
| | | // Determine whether current password is required to change password. |
| | | // Default: false. |
| | | $rcmail_config['password_confirm_current'] = true; |
| | | |
| | | // Require the new password to be a certain length. |
| | | // set to blank to allow passwords of any length |
| | | $rcmail_config['password_minimum_length'] = 0; |
| | | |
| | | // Require the new password to contain a letter and punctuation character |
| | | // Change to false to remove this check. |
| | | $rcmail_config['password_require_nonalpha'] = false; |
| | | |
| | | |
| | | // SQL Driver options |
| | | // ------------------ |
| | | // PEAR database DSN for performing the query. By default |
| | | // Roundcube DB settings are used. |
| | | $rcmail_config['password_db_dsn'] = ''; |
| | | |
| | | // The SQL query used to change the password. |
| | | // The query can contain the following macros that will be expanded as follows: |
| | | // %p is replaced with the plaintext new password |
| | | // %c is replaced with the crypt version of the new password, MD5 if available |
| | | // otherwise DES. |
| | | // %D is replaced with the dovecotpw-crypted version of the new password |
| | | // %o is replaced with the password before the change |
| | | // %n is replaced with the hashed version of the new password |
| | | // %q is replaced with the hashed password before the change |
| | | // %h is replaced with the imap host (from the session info) |
| | | // %u is replaced with the username (from the session info) |
| | | // %l is replaced with the local part of the username |
| | | // (in case the username is an email address) |
| | | // %d is replaced with the domain part of the username |
| | | // (in case the username is an email address) |
| | | // Escaping of macros is handled by this module. |
| | | // Default: "SELECT update_passwd(%c, %u)" |
| | | $rcmail_config['password_query'] = 'SELECT update_passwd(%c, %u)'; |
| | | |
| | | // Path for dovecotpw (if not in $PATH) |
| | | // $rcmail_config['password_dovecotpw'] = '/usr/local/sbin/dovecotpw'; |
| | | |
| | | // Dovecot method (dovecotpw -s 'method') |
| | | $rcmail_config['password_dovecotpw_method'] = 'CRAM-MD5'; |
| | | |
| | | // Enables use of password with crypt method prefix in %D, e.g. {MD5}$1$LUiMYWqx$fEkg/ggr/L6Mb2X7be4i1/ |
| | | $rcmail_config['password_dovecotpw_with_method'] = false; |
| | | |
| | | // Using a password hash for %n and %q variables. |
| | | // Determine which hashing algorithm should be used to generate |
| | | // the hashed new and current password for using them within the |
| | | // SQL query. Requires PHP's 'hash' extension. |
| | | $rcmail_config['password_hash_algorithm'] = 'sha1'; |
| | | |
| | | // You can also decide whether the hash should be provided |
| | | // as hex string or in base64 encoded format. |
| | | $rcmail_config['password_hash_base64'] = false; |
| | | |
| | | |
| | | // Poppassd Driver options |
| | | // ----------------------- |
| | | // The host which changes the password |
| | | $rcmail_config['password_pop_host'] = 'localhost'; |
| | | |
| | | // TCP port used for poppassd connections |
| | | $rcmail_config['password_pop_port'] = 106; |
| | | |
| | | |
| | | // SASL Driver options |
| | | // ------------------- |
| | | // Additional arguments for the saslpasswd2 call |
| | | $rcmail_config['password_saslpasswd_args'] = ''; |
| | | |
| | | |
| | | // LDAP and LDAP_SIMPLE Driver options |
| | | // ----------------------------------- |
| | | // LDAP server name to connect to. |
| | | // You can provide one or several hosts in an array in which case the hosts are tried from left to right. |
| | | // Exemple: array('ldap1.exemple.com', 'ldap2.exemple.com'); |
| | | // Default: 'localhost' |
| | | $rcmail_config['password_ldap_host'] = 'localhost'; |
| | | |
| | | // LDAP server port to connect to |
| | | // Default: '389' |
| | | $rcmail_config['password_ldap_port'] = '389'; |
| | | |
| | | // TLS is started after connecting |
| | | // Using TLS for password modification is recommanded. |
| | | // Default: false |
| | | $rcmail_config['password_ldap_starttls'] = false; |
| | | |
| | | // LDAP version |
| | | // Default: '3' |
| | | $rcmail_config['password_ldap_version'] = '3'; |
| | | |
| | | // LDAP base name (root directory) |
| | | // Exemple: 'dc=exemple,dc=com' |
| | | $rcmail_config['password_ldap_basedn'] = 'dc=exemple,dc=com'; |
| | | |
| | | // LDAP connection method |
| | | // There is two connection method for changing a user's LDAP password. |
| | | // 'user': use user credential (recommanded, require password_confirm_current=true) |
| | | // 'admin': use admin credential (this mode require password_ldap_adminDN and password_ldap_adminPW) |
| | | // Default: 'user' |
| | | $rcmail_config['password_ldap_method'] = 'user'; |
| | | |
| | | // LDAP Admin DN |
| | | // Used only in admin connection mode |
| | | // Default: null |
| | | $rcmail_config['password_ldap_adminDN'] = null; |
| | | |
| | | // LDAP Admin Password |
| | | // Used only in admin connection mode |
| | | // Default: null |
| | | $rcmail_config['password_ldap_adminPW'] = null; |
| | | |
| | | // LDAP user DN mask |
| | | // The user's DN is mandatory and as we only have his login, |
| | | // we need to re-create his DN using a mask |
| | | // '%login' will be replaced by the current roundcube user's login |
| | | // '%name' will be replaced by the current roundcube user's name part |
| | | // '%domain' will be replaced by the current roundcube user's domain part |
| | | // Exemple: 'uid=%login,ou=people,dc=exemple,dc=com' |
| | | $rcmail_config['password_ldap_userDN_mask'] = 'uid=%login,ou=people,dc=exemple,dc=com'; |
| | | |
| | | // LDAP search DN |
| | | // The DN roundcube should bind with to find out user's DN |
| | | // based on his login. Note that you should comment out the default |
| | | // password_ldap_userDN_mask setting for this to take effect. |
| | | // Use this if you cannot specify a general template for user DN with |
| | | // password_ldap_userDN_mask. You need to perform a search based on |
| | | // users login to find his DN instead. A common reason might be that |
| | | // your users are placed under different ou's like engineering or |
| | | // sales which cannot be derived from their login only. |
| | | $rcmail_config['password_ldap_searchDN'] = 'cn=roundcube,ou=services,dc=example,dc=com'; |
| | | |
| | | // LDAP search password |
| | | // If password_ldap_searchDN is set, the password to use for |
| | | // binding to search for user's DN. Note that you should comment out the default |
| | | // password_ldap_userDN_mask setting for this to take effect. |
| | | // Warning: Be sure to set approperiate permissions on this file so this password |
| | | // is only accesible to roundcube and don't forget to restrict roundcube's access to |
| | | // your directory as much as possible using ACLs. Should this password be compromised |
| | | // you want to minimize the damage. |
| | | $rcmail_config['password_ldap_searchPW'] = 'secret'; |
| | | |
| | | // LDAP search base |
| | | // If password_ldap_searchDN is set, the base to search in using the filter below. |
| | | // Note that you should comment out the default password_ldap_userDN_mask setting |
| | | // for this to take effect. |
| | | $rcmail_config['password_ldap_search_base'] = 'ou=people,dc=example,dc=com'; |
| | | |
| | | // LDAP search filter |
| | | // If password_ldap_searchDN is set, the filter to use when |
| | | // searching for user's DN. Note that you should comment out the default |
| | | // password_ldap_userDN_mask setting for this to take effect. |
| | | // '%login' will be replaced by the current roundcube user's login |
| | | // '%name' will be replaced by the current roundcube user's name part |
| | | // '%domain' will be replaced by the current roundcube user's domain part |
| | | // Example: '(uid=%login)' |
| | | // Example: '(&(objectClass=posixAccount)(uid=%login))' |
| | | $rcmail_config['password_ldap_search_filter'] = '(uid=%login)'; |
| | | |
| | | // LDAP password hash type |
| | | // Standard LDAP encryption type which must be one of: crypt, |
| | | // ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear. |
| | | // Please note that most encodage types require external libraries |
| | | // to be included in your PHP installation, see function hashPassword in drivers/ldap.php for more info. |
| | | // Default: 'crypt' |
| | | $rcmail_config['password_ldap_encodage'] = 'crypt'; |
| | | |
| | | // LDAP password attribute |
| | | // Name of the ldap's attribute used for storing user password |
| | | // Default: 'userPassword' |
| | | $rcmail_config['password_ldap_pwattr'] = 'userPassword'; |
| | | |
| | | // LDAP password force replace |
| | | // Force LDAP replace in cases where ACL allows only replace not read |
| | | // See http://pear.php.net/package/Net_LDAP2/docs/latest/Net_LDAP2/Net_LDAP2_Entry.html#methodreplace |
| | | // Default: true |
| | | $rcmail_config['password_ldap_force_replace'] = true; |
| | | |
| | | |
| | | // DirectAdmin Driver options |
| | | // -------------------------- |
| | | // The host which changes the password |
| | | // Use 'ssl://serverip' instead of 'tcp://serverip' when running DirectAdmin over SSL. |
| | | $rcmail_config['password_directadmin_host'] = 'tcp://localhost'; |
| | | |
| | | // TCP port used for DirectAdmin connections |
| | | $rcmail_config['password_directadmin_port'] = 2222; |
| | | |
| | | |
| | | // vpopmaild Driver options |
| | | // ----------------------- |
| | | // The host which changes the password |
| | | $rcmail_config['password_vpopmaild_host'] = 'localhost'; |
| | | |
| | | // TCP port used for vpopmaild connections |
| | | $rcmail_config['password_vpopmaild_port'] = 89; |
| | | |
| | | |
| | | // cPanel Driver options |
| | | // -------------------------- |
| | | // The cPanel Host name |
| | | $rcmail_config['password_cpanel_host'] = 'host.domain.com'; |
| | | |
| | | // The cPanel admin username |
| | | $rcmail_config['password_cpanel_username'] = 'username'; |
| | | |
| | | // The cPanel admin password |
| | | $rcmail_config['password_cpanel_password'] = 'password'; |
| | | |
| | | // The cPanel port to use |
| | | $rcmail_config['password_cpanel_port'] = 2082; |
| | | |
| | | // Using ssl for cPanel connections? |
| | | $rcmail_config['password_cpanel_ssl'] = true; |
| | | |
| | | // The cPanel theme in use |
| | | $rcmail_config['password_cpanel_theme'] = 'x'; |
| | | |
| | | |
| | | // XIMSS (Communigate server) Driver options |
| | | // ----------------------------------------- |
| | | // Host name of the Communigate server |
| | | $rcmail_config['password_ximss_host'] = 'mail.example.com'; |
| | | |
| | | // XIMSS port on Communigate server |
| | | $rcmail_config['password_ximss_port'] = 11024; |
| | | |
| | | |
| | | // chpasswd Driver options |
| | | // --------------------- |
| | | // Command to use |
| | | $rcmail_config['password_chpasswd_cmd'] = 'sudo /usr/sbin/chpasswd 2> /dev/null'; |
| | | |
| | | |
| | | // XMail Driver options |
| | | // --------------------- |
| | | $rcmail_config['xmail_host'] = 'localhost'; |
| | | $rcmail_config['xmail_user'] = 'YourXmailControlUser'; |
| | | $rcmail_config['xmail_pass'] = 'YourXmailControlPass'; |
| | | $rcmail_config['xmail_port'] = 6017; |
| | | |
New file |
| | |
| | | #include <stdio.h> |
| | | #include <unistd.h> |
| | | |
| | | // set the UID this script will run as (cyrus user) |
| | | #define UID 96 |
| | | // set the path to saslpasswd or saslpasswd2 |
| | | #define CMD "/usr/sbin/saslpasswd2" |
| | | |
| | | /* INSTALLING: |
| | | gcc -o chgsaslpasswd chgsaslpasswd.c |
| | | chown cyrus.apache chgsaslpasswd |
| | | strip chgsaslpasswd |
| | | chmod 4550 chgsaslpasswd |
| | | */ |
| | | |
| | | main(int argc, char *argv[]) |
| | | { |
| | | int rc,cc; |
| | | |
| | | cc = setuid(UID); |
| | | rc = execvp(CMD, argv); |
| | | if ((rc != 0) || (cc != 0)) |
| | | { |
| | | fprintf(stderr, "__ %s: failed %d %d\n", argv[0], rc, cc); |
| | | return 1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
New file |
| | |
| | | #include <stdio.h> |
| | | #include <unistd.h> |
| | | |
| | | // set the UID this script will run as (root user) |
| | | #define UID 0 |
| | | #define CMD "/usr/sbin/virtualmin" |
| | | |
| | | /* INSTALLING: |
| | | gcc -o chgvirtualminpasswd chgvirtualminpasswd.c |
| | | chown root.apache chgvirtualminpasswd |
| | | strip chgvirtualminpasswd |
| | | chmod 4550 chgvirtualminpasswd |
| | | */ |
| | | |
| | | main(int argc, char *argv[]) |
| | | { |
| | | int rc,cc; |
| | | |
| | | cc = setuid(UID); |
| | | rc = execvp(CMD, argv); |
| | | if ((rc != 0) || (cc != 0)) |
| | | { |
| | | fprintf(stderr, "__ %s: failed %d %d\n", argv[0], rc, cc); |
| | | return 1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
New file |
| | |
| | | #!/usr/bin/env python |
| | | |
| | | import sys |
| | | import pwd |
| | | import subprocess |
| | | |
| | | BLACKLIST = ( |
| | | # add blacklisted users here |
| | | #'user1', |
| | | ) |
| | | |
| | | try: |
| | | username, password = sys.stdin.readline().split(':', 1) |
| | | except ValueError, e: |
| | | sys.exit('Malformed input') |
| | | |
| | | try: |
| | | user = pwd.getpwnam(username) |
| | | except KeyError, e: |
| | | sys.exit('No such user: %s' % username) |
| | | |
| | | if user.pw_uid < 1000: |
| | | sys.exit('Changing the password for user id < 1000 is forbidden') |
| | | |
| | | if username in BLACKLIST: |
| | | sys.exit('Changing password for user %s is forbidden (user blacklisted)' % |
| | | username) |
| | | |
| | | handle = subprocess.Popen('/usr/sbin/chpasswd', stdin = subprocess.PIPE) |
| | | handle.communicate('%s:%s' % (username, password)) |
| | | |
| | | sys.exit(handle.returncode) |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * chpasswd Driver |
| | | * |
| | | * Driver that adds functionality to change the systems user password via |
| | | * the 'chpasswd' command. |
| | | * |
| | | * For installation instructions please read the README file. |
| | | * |
| | | * @version 1.0 |
| | | * @author Alex Cartwright <acartwright@mutinydesign.co.uk) |
| | | */ |
| | | |
| | | function password_save($currpass, $newpass) |
| | | { |
| | | $cmd = rcmail::get_instance()->config->get('password_chpasswd_cmd'); |
| | | $username = $_SESSION['username']; |
| | | |
| | | $handle = popen($cmd, "w"); |
| | | fwrite($handle, "$username:$newpass"); |
| | | |
| | | if (pclose($handle) == 0) { |
| | | return PASSWORD_SUCCESS; |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | 'code' => 600, |
| | | 'type' => 'php', |
| | | 'file' => __FILE__, |
| | | 'message' => "Password plugin: Unable to execute $cmd" |
| | | ), true, false); |
| | | } |
| | | |
| | | return PASSWORD_ERROR; |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * cPanel Password Driver |
| | | * |
| | | * Driver that adds functionality to change the users cPanel password. |
| | | * The cPanel PHP API code has been taken from: http://www.phpclasses.org/browse/package/3534.html |
| | | * |
| | | * This driver has been tested with Hostmonster hosting and seems to work fine. |
| | | |
| | | * |
| | | * @version 1.0 |
| | | * @author Fulvio Venturelli <fulvio@venturelli.org> |
| | | */ |
| | | |
| | | class HTTP |
| | | { |
| | | function HTTP($host, $username, $password, $port, $ssl, $theme) |
| | | { |
| | | $this->ssl = $ssl ? 'ssl://' : ''; |
| | | $this->username = $username; |
| | | $this->password = $password; |
| | | $this->theme = $theme; |
| | | $this->auth = base64_encode($username . ':' . $password); |
| | | $this->port = $port; |
| | | $this->host = $host; |
| | | $this->path = '/frontend/' . $theme . '/'; |
| | | } |
| | | |
| | | function getData($url, $data = '') |
| | | { |
| | | $url = $this->path . $url; |
| | | if(is_array($data)) |
| | | { |
| | | $url = $url . '?'; |
| | | foreach($data as $key=>$value) |
| | | { |
| | | $url .= urlencode($key) . '=' . urlencode($value) . '&'; |
| | | } |
| | | $url = substr($url, 0, -1); |
| | | } |
| | | $response = ''; |
| | | $fp = fsockopen($this->ssl . $this->host, $this->port); |
| | | if(!$fp) |
| | | { |
| | | return false; |
| | | } |
| | | $out = 'GET ' . $url . ' HTTP/1.0' . "\r\n"; |
| | | $out .= 'Authorization: Basic ' . $this->auth . "\r\n"; |
| | | $out .= 'Connection: Close' . "\r\n\r\n"; |
| | | fwrite($fp, $out); |
| | | while (!feof($fp)) |
| | | { |
| | | $response .= @fgets($fp); |
| | | } |
| | | fclose($fp); |
| | | return $response; |
| | | } |
| | | } |
| | | |
| | | |
| | | class emailAccount |
| | | { |
| | | function emailAccount($host, $username, $password, $port, $ssl, $theme, $address) |
| | | { |
| | | $this->HTTP = new HTTP($host, $username, $password, $port, $ssl, $theme); |
| | | if(strpos($address, '@')) |
| | | { |
| | | list($this->email, $this->domain) = explode('@', $address); |
| | | } |
| | | else |
| | | { |
| | | list($this->email, $this->domain) = array($address, ''); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * Change email account password |
| | | * |
| | | * Returns true on success or false on failure. |
| | | * @param string $password email account password |
| | | * @return bool |
| | | */ |
| | | function setPassword($password) |
| | | { |
| | | $data['email'] = $this->email; |
| | | $data['domain'] = $this->domain; |
| | | $data['password'] = $password; |
| | | $response = $this->HTTP->getData('mail/dopasswdpop.html', $data); |
| | | if(strpos($response, 'success') && !strpos($response, 'failure')) |
| | | { |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | function password_save($curpas, $newpass) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // Create a cPanel email object |
| | | $cPanel = new emailAccount($rcmail->config->get('password_cpanel_host'), |
| | | $rcmail->config->get('password_cpanel_username'), |
| | | $rcmail->config->get('password_cpanel_password'), |
| | | $rcmail->config->get('password_cpanel_port'), |
| | | $rcmail->config->get('password_cpanel_ssl'), |
| | | $rcmail->config->get('password_cpanel_theme'), |
| | | $_SESSION['username'] ); |
| | | |
| | | if ($cPanel->setPassword($newpass)){ |
| | | return PASSWORD_SUCCESS; |
| | | } |
| | | else |
| | | { |
| | | return PASSWORD_ERROR; |
| | | } |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * DirectAdmin Password Driver |
| | | * |
| | | * Driver to change passwords via DirectAdmin Control Panel |
| | | * |
| | | * @version 1.0 |
| | | * @author Victor Benincasa <vbenincasa@gmail.com> |
| | | * |
| | | */ |
| | | |
| | | |
| | | function password_save($curpass, $passwd){ |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | $Socket = new HTTPSocket; |
| | | |
| | | $da_user = $_SESSION['username']; |
| | | $da_curpass = $curpass; |
| | | $da_newpass = $passwd; |
| | | $da_host = $rcmail->config->get('password_directadmin_host'); |
| | | $da_port = $rcmail->config->get('password_directadmin_port'); |
| | | |
| | | $Socket->connect($da_host,$da_port); |
| | | $Socket->set_method('POST'); |
| | | $Socket->query('/CMD_CHANGE_EMAIL_PASSWORD', |
| | | array( |
| | | 'email' => $da_user, |
| | | 'oldpassword' => $da_curpass, |
| | | 'password1' => $da_newpass, |
| | | 'password2' => $da_newpass, |
| | | 'api' => '1' |
| | | )); |
| | | $response = $Socket->fetch_parsed_body(); |
| | | |
| | | //console("DA error response: $response[text] [$da_user]"); |
| | | |
| | | if($Socket->result_status_code <> 200) |
| | | return PASSWORD_CONNECT_ERROR; |
| | | elseif($response['error'] == 1){ //Error description: $response[text] |
| | | return PASSWORD_ERROR; |
| | | }else |
| | | return PASSWORD_SUCCESS; |
| | | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Socket communication class. |
| | | * |
| | | * Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need. |
| | | * |
| | | * Very, very basic usage: |
| | | * $Socket = new HTTPSocket; |
| | | * echo $Socket->get('http://user:pass@somesite.com/somedir/some.file?query=string&this=that'); |
| | | * |
| | | * @author Phi1 'l0rdphi1' Stier <l0rdphi1@liquenox.net> |
| | | * @package HTTPSocket |
| | | * @version 2.6 |
| | | */ |
| | | class HTTPSocket { |
| | | |
| | | var $version = '2.6'; |
| | | |
| | | /* all vars are private except $error, $query_cache, and $doFollowLocationHeader */ |
| | | |
| | | var $method = 'GET'; |
| | | |
| | | var $remote_host; |
| | | var $remote_port; |
| | | var $remote_uname; |
| | | var $remote_passwd; |
| | | |
| | | var $result; |
| | | var $result_header; |
| | | var $result_body; |
| | | var $result_status_code; |
| | | |
| | | var $lastTransferSpeed; |
| | | |
| | | var $bind_host; |
| | | |
| | | var $error = array(); |
| | | var $warn = array(); |
| | | var $query_cache = array(); |
| | | |
| | | var $doFollowLocationHeader = TRUE; |
| | | var $redirectURL; |
| | | |
| | | var $extra_headers = array(); |
| | | |
| | | /** |
| | | * Create server "connection". |
| | | * |
| | | */ |
| | | function connect($host, $port = '' ) |
| | | { |
| | | if (!is_numeric($port)) |
| | | { |
| | | $port = 80; |
| | | } |
| | | |
| | | $this->remote_host = $host; |
| | | $this->remote_port = $port; |
| | | } |
| | | |
| | | function bind( $ip = '' ) |
| | | { |
| | | if ( $ip == '' ) |
| | | { |
| | | $ip = $_SERVER['SERVER_ADDR']; |
| | | } |
| | | |
| | | $this->bind_host = $ip; |
| | | } |
| | | |
| | | /** |
| | | * Change the method being used to communicate. |
| | | * |
| | | * @param string|null request method. supports GET, POST, and HEAD. default is GET |
| | | */ |
| | | function set_method( $method = 'GET' ) |
| | | { |
| | | $this->method = strtoupper($method); |
| | | } |
| | | |
| | | /** |
| | | * Specify a username and password. |
| | | * |
| | | * @param string|null username. defualt is null |
| | | * @param string|null password. defualt is null |
| | | */ |
| | | function set_login( $uname = '', $passwd = '' ) |
| | | { |
| | | if ( strlen($uname) > 0 ) |
| | | { |
| | | $this->remote_uname = $uname; |
| | | } |
| | | |
| | | if ( strlen($passwd) > 0 ) |
| | | { |
| | | $this->remote_passwd = $passwd; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Query the server |
| | | * |
| | | * @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too. |
| | | * @param string|array query to pass to url |
| | | * @param int if connection KB/s drops below value here, will drop connection |
| | | */ |
| | | function query( $request, $content = '', $doSpeedCheck = 0 ) |
| | | { |
| | | $this->error = $this->warn = array(); |
| | | $this->result_status_code = NULL; |
| | | |
| | | // is our request a http:// ... ? |
| | | if (preg_match('!^http://!i',$request)) |
| | | { |
| | | $location = parse_url($request); |
| | | $this->connect($location['host'],$location['port']); |
| | | $this->set_login($location['user'],$location['pass']); |
| | | |
| | | $request = $location['path']; |
| | | $content = $location['query']; |
| | | |
| | | if ( strlen($request) < 1 ) |
| | | { |
| | | $request = '/'; |
| | | } |
| | | |
| | | } |
| | | |
| | | $array_headers = array( |
| | | 'User-Agent' => "HTTPSocket/$this->version", |
| | | 'Host' => ( $this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port" ), |
| | | 'Accept' => '*/*', |
| | | 'Connection' => 'Close' ); |
| | | |
| | | foreach ( $this->extra_headers as $key => $value ) |
| | | { |
| | | $array_headers[$key] = $value; |
| | | } |
| | | |
| | | $this->result = $this->result_header = $this->result_body = ''; |
| | | |
| | | // was content sent as an array? if so, turn it into a string |
| | | if (is_array($content)) |
| | | { |
| | | $pairs = array(); |
| | | |
| | | foreach ( $content as $key => $value ) |
| | | { |
| | | $pairs[] = "$key=".urlencode($value); |
| | | } |
| | | |
| | | $content = join('&',$pairs); |
| | | unset($pairs); |
| | | } |
| | | |
| | | $OK = TRUE; |
| | | |
| | | // instance connection |
| | | if ($this->bind_host) |
| | | { |
| | | $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); |
| | | socket_bind($socket,$this->bind_host); |
| | | |
| | | if (!@socket_connect($socket,$this->remote_host,$this->remote_port)) |
| | | { |
| | | $OK = FALSE; |
| | | } |
| | | |
| | | } |
| | | else |
| | | { |
| | | $socket = @fsockopen( $this->remote_host, $this->remote_port, $sock_errno, $sock_errstr, 10 ); |
| | | } |
| | | |
| | | if ( !$socket || !$OK ) |
| | | { |
| | | $this->error[] = "Can't create socket connection to $this->remote_host:$this->remote_port."; |
| | | return 0; |
| | | } |
| | | |
| | | // if we have a username and password, add the header |
| | | if ( isset($this->remote_uname) && isset($this->remote_passwd) ) |
| | | { |
| | | $array_headers['Authorization'] = 'Basic '.base64_encode("$this->remote_uname:$this->remote_passwd"); |
| | | } |
| | | |
| | | // for DA skins: if $this->remote_passwd is NULL, try to use the login key system |
| | | if ( isset($this->remote_uname) && $this->remote_passwd == NULL ) |
| | | { |
| | | $array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}"; |
| | | } |
| | | |
| | | // if method is POST, add content length & type headers |
| | | if ( $this->method == 'POST' ) |
| | | { |
| | | $array_headers['Content-type'] = 'application/x-www-form-urlencoded'; |
| | | $array_headers['Content-length'] = strlen($content); |
| | | } |
| | | // else method is GET or HEAD. we don't support anything else right now. |
| | | else |
| | | { |
| | | if ($content) |
| | | { |
| | | $request .= "?$content"; |
| | | } |
| | | } |
| | | |
| | | // prepare query |
| | | $query = "$this->method $request HTTP/1.0\r\n"; |
| | | foreach ( $array_headers as $key => $value ) |
| | | { |
| | | $query .= "$key: $value\r\n"; |
| | | } |
| | | $query .= "\r\n"; |
| | | |
| | | // if POST we need to append our content |
| | | if ( $this->method == 'POST' && $content ) |
| | | { |
| | | $query .= "$content\r\n\r\n"; |
| | | } |
| | | |
| | | // query connection |
| | | if ($this->bind_host) |
| | | { |
| | | socket_write($socket,$query); |
| | | |
| | | // now load results |
| | | while ( $out = socket_read($socket,2048) ) |
| | | { |
| | | $this->result .= $out; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | fwrite( $socket, $query, strlen($query) ); |
| | | |
| | | // now load results |
| | | $this->lastTransferSpeed = 0; |
| | | $status = socket_get_status($socket); |
| | | $startTime = time(); |
| | | $length = 0; |
| | | $prevSecond = 0; |
| | | while ( !feof($socket) && !$status['timed_out'] ) |
| | | { |
| | | $chunk = fgets($socket,1024); |
| | | $length += strlen($chunk); |
| | | $this->result .= $chunk; |
| | | |
| | | $elapsedTime = time() - $startTime; |
| | | |
| | | if ( $elapsedTime > 0 ) |
| | | { |
| | | $this->lastTransferSpeed = ($length/1024)/$elapsedTime; |
| | | } |
| | | |
| | | if ( $doSpeedCheck > 0 && $elapsedTime > 5 && $this->lastTransferSpeed < $doSpeedCheck ) |
| | | { |
| | | $this->warn[] = "kB/s for last 5 seconds is below 50 kB/s (~".( ($length/1024)/$elapsedTime )."), dropping connection..."; |
| | | $this->result_status_code = 503; |
| | | break; |
| | | } |
| | | |
| | | } |
| | | |
| | | if ( $this->lastTransferSpeed == 0 ) |
| | | { |
| | | $this->lastTransferSpeed = $length/1024; |
| | | } |
| | | |
| | | } |
| | | |
| | | list($this->result_header,$this->result_body) = split("\r\n\r\n",$this->result,2); |
| | | |
| | | if ($this->bind_host) |
| | | { |
| | | socket_close($socket); |
| | | } |
| | | else |
| | | { |
| | | fclose($socket); |
| | | } |
| | | |
| | | $this->query_cache[] = $query; |
| | | |
| | | |
| | | $headers = $this->fetch_header(); |
| | | |
| | | // what return status did we get? |
| | | if (!$this->result_status_code) |
| | | { |
| | | preg_match("#HTTP/1\.. (\d+)#",$headers[0],$matches); |
| | | $this->result_status_code = $matches[1]; |
| | | } |
| | | |
| | | // did we get the full file? |
| | | if ( !empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body) ) |
| | | { |
| | | $this->result_status_code = 206; |
| | | } |
| | | |
| | | // now, if we're being passed a location header, should we follow it? |
| | | if ($this->doFollowLocationHeader) |
| | | { |
| | | if ($headers['location']) |
| | | { |
| | | $this->redirectURL = $headers['location']; |
| | | $this->query($headers['location']); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | function getTransferSpeed() |
| | | { |
| | | return $this->lastTransferSpeed; |
| | | } |
| | | |
| | | /** |
| | | * The quick way to get a URL's content :) |
| | | * |
| | | * @param string URL |
| | | * @param boolean return as array? (like PHP's file() command) |
| | | * @return string result body |
| | | */ |
| | | function get($location, $asArray = FALSE ) |
| | | { |
| | | $this->query($location); |
| | | |
| | | if ( $this->get_status_code() == 200 ) |
| | | { |
| | | if ($asArray) |
| | | { |
| | | return split("\n",$this->fetch_body()); |
| | | } |
| | | |
| | | return $this->fetch_body(); |
| | | } |
| | | |
| | | return FALSE; |
| | | } |
| | | |
| | | /** |
| | | * Returns the last status code. |
| | | * 200 = OK; |
| | | * 403 = FORBIDDEN; |
| | | * etc. |
| | | * |
| | | * @return int status code |
| | | */ |
| | | function get_status_code() |
| | | { |
| | | return $this->result_status_code; |
| | | } |
| | | |
| | | /** |
| | | * Adds a header, sent with the next query. |
| | | * |
| | | * @param string header name |
| | | * @param string header value |
| | | */ |
| | | function add_header($key,$value) |
| | | { |
| | | $this->extra_headers[$key] = $value; |
| | | } |
| | | |
| | | /** |
| | | * Clears any extra headers. |
| | | * |
| | | */ |
| | | function clear_headers() |
| | | { |
| | | $this->extra_headers = array(); |
| | | } |
| | | |
| | | /** |
| | | * Return the result of a query. |
| | | * |
| | | * @return string result |
| | | */ |
| | | function fetch_result() |
| | | { |
| | | return $this->result; |
| | | } |
| | | |
| | | /** |
| | | * Return the header of result (stuff before body). |
| | | * |
| | | * @param string (optional) header to return |
| | | * @return array result header |
| | | */ |
| | | function fetch_header( $header = '' ) |
| | | { |
| | | $array_headers = split("\r\n",$this->result_header); |
| | | |
| | | $array_return = array( 0 => $array_headers[0] ); |
| | | unset($array_headers[0]); |
| | | |
| | | foreach ( $array_headers as $pair ) |
| | | { |
| | | list($key,$value) = split(": ",$pair,2); |
| | | $array_return[strtolower($key)] = $value; |
| | | } |
| | | |
| | | if ( $header != '' ) |
| | | { |
| | | return $array_return[strtolower($header)]; |
| | | } |
| | | |
| | | return $array_return; |
| | | } |
| | | |
| | | /** |
| | | * Return the body of result (stuff after header). |
| | | * |
| | | * @return string result body |
| | | */ |
| | | function fetch_body() |
| | | { |
| | | return $this->result_body; |
| | | } |
| | | |
| | | /** |
| | | * Return parsed body in array format. |
| | | * |
| | | * @return array result parsed |
| | | */ |
| | | function fetch_parsed_body() |
| | | { |
| | | parse_str($this->result_body,$x); |
| | | return $x; |
| | | } |
| | | |
| | | } |
| | | |
| | | ?> |
plugins/password/drivers/hmail.php
plugins/password/drivers/ldap.php
plugins/password/drivers/ldap_simple.php
plugins/password/drivers/pam.php
plugins/password/drivers/poppassd.php
plugins/password/drivers/sasl.php
plugins/password/drivers/sql.php
plugins/password/drivers/virtualmin.php
plugins/password/drivers/vpopmaild.php
plugins/password/drivers/ximss.php
plugins/password/drivers/xmail.php
plugins/password/localization/bg_BG.inc
plugins/password/localization/ca_ES.inc
plugins/password/localization/cs_CZ.inc
plugins/password/localization/da_DK.inc
plugins/password/localization/de_CH.inc
plugins/password/localization/de_DE.inc
plugins/password/localization/en_US.inc
plugins/password/localization/es_AR.inc
plugins/password/localization/es_ES.inc
plugins/password/localization/et_EE.inc
plugins/password/localization/fi_FI.inc
plugins/password/localization/fr_FR.inc
plugins/password/localization/hu_HU.inc
plugins/password/localization/it_IT.inc
plugins/password/localization/ja_JP.inc
plugins/password/localization/lt_LT.inc
plugins/password/localization/lv_LV.inc
plugins/password/localization/nl_NL.inc
plugins/password/localization/pl_PL.inc
plugins/password/localization/pt_BR.inc
plugins/password/localization/pt_PT.inc
plugins/password/localization/ru_RU.inc
plugins/password/localization/sl_SI.inc
plugins/password/localization/sv_SE.inc
plugins/password/localization/tr_TR.inc
plugins/password/localization/zh_TW.inc
plugins/password/package.xml
plugins/password/password.js
plugins/password/password.php
plugins/show_additional_headers/show_additional_headers.php
plugins/squirrelmail_usercopy/config.inc.php.dist
plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
plugins/subscriptions_option/localization/cs_CZ.inc
plugins/subscriptions_option/localization/de_CH.inc
plugins/subscriptions_option/localization/de_DE.inc
plugins/subscriptions_option/localization/en_US.inc
plugins/subscriptions_option/localization/es_ES.inc
plugins/subscriptions_option/localization/et_EE.inc
plugins/subscriptions_option/localization/ja_JP.inc
plugins/subscriptions_option/localization/pl_PL.inc
plugins/subscriptions_option/localization/ru_RU.inc
plugins/subscriptions_option/localization/sv_SE.inc
plugins/subscriptions_option/localization/zh_TW.inc
plugins/subscriptions_option/subscriptions_option.php
plugins/userinfo/localization/cs_CZ.inc
plugins/userinfo/localization/de_CH.inc
plugins/userinfo/localization/en_US.inc
plugins/userinfo/localization/es_ES.inc
plugins/userinfo/localization/et_EE.inc
plugins/userinfo/localization/ja_JP.inc
plugins/userinfo/localization/pl_PL.inc
plugins/userinfo/localization/pt_PT.inc
plugins/userinfo/localization/ru_RU.inc
plugins/userinfo/localization/sv_SE.inc
plugins/userinfo/localization/zh_TW.inc
plugins/userinfo/userinfo.js
plugins/userinfo/userinfo.php
plugins/vcard_attachments/localization/cs_CZ.inc
plugins/vcard_attachments/localization/de_CH.inc
plugins/vcard_attachments/localization/de_DE.inc
plugins/vcard_attachments/localization/en_US.inc
plugins/vcard_attachments/localization/es_ES.inc
plugins/vcard_attachments/localization/et_EE.inc
plugins/vcard_attachments/localization/ja_JP.inc
plugins/vcard_attachments/localization/pl_PL.inc
plugins/vcard_attachments/localization/ru_RU.inc
plugins/vcard_attachments/localization/sv_SE.inc
plugins/vcard_attachments/localization/zh_TW.inc
plugins/vcard_attachments/package.xml
plugins/vcard_attachments/vcard_add_contact.png
plugins/vcard_attachments/vcard_attachments.php
plugins/vcard_attachments/vcardattach.js
plugins/virtuser_file/virtuser_file.php
plugins/virtuser_query/virtuser_query.php |