Compare commits

...

27 Commits

Author SHA1 Message Date
thomascube 63d6e6dfc3 Bump versions to 0.6 stable 13 years ago
thomascube c1e07e11f0 Updated localizations from mailing list submissions 13 years ago
alecpl 5f47eda94b - Applied some fixes from trunk up to r5280 13 years ago
alecpl 075e9d5ba2 - Applied fixes from trunk up to r5259 13 years ago
alecpl b578abb9f9 - Applied fixes from trunk up to r5232 13 years ago
thomascube 85993b24f8 Backport r5226 to release 0.6 branch 13 years ago
alecpl f593222ef5 - Applied fixes from trunk up to r5212 13 years ago
alecpl a7c51ac2d0 - Applied fixes from trunk up to r5202 13 years ago
alecpl d96dc639fc - Merge r5194 from trunk 13 years ago
thomascube 9fa938739d Localization updates 13 years ago
thomascube 491b655d74 Updated changelog 13 years ago
alecpl d30c3f5659 - jQuery-1.6.3 13 years ago
alecpl 636a536f42 - Fix typo 13 years ago
thomascube 853696b15a Backport changes from trunk up to r5164 13 years ago
alecpl b08caf5813 - Applied fixes from trunk up to r5150 13 years ago
alecpl fd6146b678 - Applied fixes from trunk up to r5126 13 years ago
thomascube 2c67d45f22 Backport r5124 to release branch 13 years ago
thomascube 33046ce911 Backport r5121 from trunk 13 years ago
thomascube fe91e2f31d Improve memcache connection and failure handling; add debug option to trace memcache activity for session storage 13 years ago
thomascube 81fd8797d8 Backport from trunk 13 years ago
alecpl 9f61af3518 - Backported r5093 13 years ago
thomascube d43d4bcf44 Backporting r5092, r5095, r5096 to release-0.6 branch 13 years ago
thomascube b6cd452bd3 Backport changes from r5084 to r5090 to release branch 13 years ago
thomascube b1cde954d1 Clone of r5078 13 years ago
thomascube 5375e84b75 Copying plugins into 0.6 release branch 13 years ago
thomascube af80aae480 Remove development stuff and update versions 13 years ago
thomascube a9d82447d0 Branching for 0.6 release 13 years ago

File diff suppressed because it is too large Load Diff

@ -29,6 +29,7 @@ REQUIREMENTS
- zend.ze1_compatibility_mode disabled
- suhosin.session.encrypt disabled
- mbstring.func_overload disabled
- magic_quotes_runtime disabled
* PHP compiled with OpenSSL to connect to IMAPS and to use the spell checker
* A MySQL (4.0.8 or newer), PostgreSQL, MSSQL database engine
or the SQLite extension for PHP

@ -1,14 +1,6 @@
Roundcube Webmail (http://roundcube.net)
ATTENTION
---------
This is just a snapshot of the current SVN repository and is NOT A STABLE
version of Roundcube. It's not recommended to replace an existing installation
of Roundcube with this version. Also using a separate database for this
installation is highly recommended.
Introduction:
-------------
Roundcube Webmail is a browser-based multilingual IMAP client with an

@ -1,98 +0,0 @@
#!/usr/bin/env php
<?php
/*
+-----------------------------------------------------------------------+
| bin/dumpschema.sh |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Dumps database schema in XML format using MDB2_Schema |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' );
require INSTALL_PATH.'program/include/clisetup.php';
/** callback function for schema dump **/
function print_schema($dump)
{
foreach ((array)$dump as $part)
echo $dump . "\n";
}
$config = new rcube_config();
// don't allow public access if not in devel_mode
if (!$config->get('devel_mode') && $_SERVER['REMOTE_ADDR']) {
header("HTTP/1.0 401 Access denied");
die("Access denied!");
}
$options = array(
'use_transactions' => false,
'log_line_break' => "\n",
'idxname_format' => '%s',
'debug' => false,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false,
);
$dsnw = $config->get('db_dsnw');
$dsn_array = MDB2::parseDSN($dsnw);
// set options for postgres databases
if ($dsn_array['phptype'] == 'pgsql') {
$options['disable_smart_seqname'] = true;
$options['seqname_format'] = '%s';
}
$schema =& MDB2_Schema::factory($dsnw, $options);
$schema->db->supported['transactions'] = false;
// send as text/xml when opened in browser
if ($_SERVER['REMOTE_ADDR'])
header('Content-Type: text/xml');
if (PEAR::isError($schema)) {
$error = $schema->getMessage() . ' ' . $schema->getUserInfo();
}
else {
$dump_config = array(
// 'output_mode' => 'file',
'output' => 'print_schema',
);
$definition = $schema->getDefinitionFromDatabase();
$definition['charset'] = 'utf8';
if (PEAR::isError($definition)) {
$error = $definition->getMessage() . ' ' . $definition->getUserInfo();
}
else {
$operation = $schema->dumpDatabase($definition, $dump_config, MDB2_SCHEMA_DUMP_STRUCTURE);
if (PEAR::isError($operation)) {
$error = $operation->getMessage() . ' ' . $operation->getUserInfo();
}
}
}
$schema->disconnect();
if ($error && !$_SERVER['REMOTE_ADDR'])
fputs(STDERR, $error);
?>

@ -1,25 +0,0 @@
#!/bin/sh
TITLE="Roundcube Classes"
PACKAGES="Core"
INSTALL_PATH="`dirname $0`/.."
PATH_PROJECT=$INSTALL_PATH/program/include
PATH_DOCS=$INSTALL_PATH/doc/phpdoc
BIN_PHPDOC="`/usr/bin/which phpdoc`"
if [ ! -x "$BIN_PHPDOC" ]
then
echo "phpdoc not found: $BIN_PHPDOC"
exit 1
fi
OUTPUTFORMAT=HTML
CONVERTER=frames
TEMPLATE=earthli
PRIVATE=off
# make documentation
$BIN_PHPDOC -d $PATH_PROJECT -t $PATH_DOCS -ti "$TITLE" -dn $PACKAGES \
-o $OUTPUTFORMAT:$CONVERTER:$TEMPLATE -pp $PRIVATE

@ -237,6 +237,10 @@ $rcmail_config['ip_check'] = false;
// check referer of incoming requests
$rcmail_config['referer_check'] = false;
// X-Frame-Options HTTP header value sent to prevent from Clickjacking.
// Possible values: sameorigin|deny. Set to false in order to disable sending them
$rcmail_config['x_frame_options'] = 'sameorigin';
// this key is used to encrypt the users imap password which is stored
// in the session record (and the client cookie if remember password is enabled).
// please provide a string of exactly 24 chars.
@ -326,6 +330,11 @@ $rcmail_config['dont_override'] = array();
// 3 - one identity with possibility to edit all params but not email address
$rcmail_config['identities_level'] = 0;
// Mimetypes supported by the browser.
// attachments of these types will open in a preview window
// either a comma-separated list or an array: 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/pdf'
$rcmail_config['client_mimetypes'] = null; # null == default
// mime magic database
$rcmail_config['mime_magic'] = '/usr/share/misc/magic';
@ -561,9 +570,11 @@ $rcmail_config['ldap_public']['Verisign'] = array(
// if the groups base_dn is empty, the contact base_dn is used for the groups as well
// -> in this case, assure that groups and contacts are separated due to the concernig filters!
'groups' => array(
'base_dn' => '',
'base_dn' => '',
'filter' => '(objectClass=groupOfNames)',
'object_classes' => array("top", "groupOfNames"),
// name of the member attribute, e.g. uniqueMember
'member_attr' => 'member',
),
);
*/

@ -2,7 +2,7 @@
/*
+-------------------------------------------------------------------------+
| Roundcube Webmail IMAP Client |
| Version 0.6-svn |
| Version 0.6 |
| |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| |
@ -155,7 +155,7 @@ else if ($RCMAIL->task != 'login' && $_SESSION['user_id'] && $RCMAIL->action !=
// not logged in -> show login page
if (empty($RCMAIL->user->ID)) {
// log session failures
if ($RCMAIL->task != 'login' && $RCMAIL->task != 'logout' && !$session_error && ($sess_id = $_COOKIE[ini_get('session.name')])) {
if (($task = get_input_value('_task', RCUBE_INPUT_GPC)) && !in_array($task, array('login','logout')) && !$session_error && ($sess_id = $_COOKIE[ini_get('session.name')])) {
$RCMAIL->session->log("Aborted session " . $sess_id . "; no valid session data found");
$session_error = true;
}

@ -1,5 +1,33 @@
<?php
/*
+-------------------------------------------------------------------------+
| Roundcube Webmail setup tool |
| Version 0.6 |
| |
| Copyright (C) 2009-2011, The Roundcube Dev Team |
| |
| 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: Thomas Bruederli <roundcube@gmail.com> |
+-------------------------------------------------------------------------+
$Id$
*/
ini_set('error_reporting', E_ALL&~E_NOTICE);
ini_set('display_errors', 1);

@ -5,7 +5,7 @@
| rcube_install.php |
| |
| This file is part of the Roundcube Webmail package |
| Copyright (C) 2008-2009, The Roundcube Dev Team |
| Copyright (C) 2008-2011, The Roundcube Dev Team |
| Licensed under the GNU Public License |
+-----------------------------------------------------------------------+
@ -95,14 +95,15 @@ class rcube_install
*/
function _load_config($suffix)
{
@include RCMAIL_CONFIG_DIR . '/main.inc' . $suffix;
if (is_array($rcmail_config)) {
$this->config += $rcmail_config;
if (is_readable($main_inc = RCMAIL_CONFIG_DIR . '/main.inc' . $suffix)) {
include($main_inc);
if (is_array($rcmail_config))
$this->config += $rcmail_config;
}
@include RCMAIL_CONFIG_DIR . '/db.inc'. $suffix;
if (is_array($rcmail_config)) {
$this->config += $rcmail_config;
if (is_readable($db_inc = RCMAIL_CONFIG_DIR . '/db.inc'. $suffix)) {
include($db_inc);
if (is_array($rcmail_config))
$this->config += $rcmail_config;
}
}

@ -2,9 +2,9 @@
/*
+-------------------------------------------------------------------------+
| Roundcube Webmail IMAP Client |
| Version 0.3-20090702 |
| Version 0.6 |
| |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 |
@ -64,3 +64,17 @@ function raise_error($p)
$rci = rcube_install::get_instance();
$rci->raise_error($p);
}
/**
* Local callback function for PEAR errors
*/
function rcube_pear_error($err)
{
raise_error(array(
'code' => $err->getCode(),
'message' => $err->getMessage(),
));
}
// set PEAR error handling (will also load the PEAR main class)
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error');

@ -0,0 +1,338 @@
/**
* ACL plugin script
*
* @version 0.6.1
* @author Aleksander Machniak <alec@alec.pl>
*/
if (window.rcmail) {
rcmail.addEventListener('init', function() {
if (rcmail.gui_objects.acltable) {
rcmail.acl_list_init();
// enable autocomplete on user input
if (rcmail.env.acl_users_source) {
rcmail.init_address_input_events($('#acluser'), {action:'settings/plugin.acl-autocomplete'});
// fix inserted value
rcmail.addEventListener('autocomplete_insert', function(e) {
if (e.field.id != 'acluser')
return;
var value = e.insert;
// get UID from the entry value
if (value.match(/\s*\(([^)]+)\)[, ]*$/))
value = RegExp.$1;
e.field.value = value;
});
}
}
rcmail.enable_command('acl-create', 'acl-save', 'acl-cancel', 'acl-mode-switch', true);
rcmail.enable_command('acl-delete', 'acl-edit', false);
});
}
// Display new-entry form
rcube_webmail.prototype.acl_create = function()
{
this.acl_init_form();
}
// Display ACL edit form
rcube_webmail.prototype.acl_edit = function()
{
// @TODO: multi-row edition
var id = this.acl_list.get_single_selection();
if (id)
this.acl_init_form(id);
}
// ACL entry delete
rcube_webmail.prototype.acl_delete = function()
{
var users = this.acl_get_usernames();
if (users && users.length && confirm(this.get_label('acl.deleteconfirm'))) {
this.http_request('settings/plugin.acl', '_act=delete&_user='+urlencode(users.join(','))
+ '&_mbox='+urlencode(this.env.mailbox),
this.set_busy(true, 'acl.deleting'));
}
}
// Save ACL data
rcube_webmail.prototype.acl_save = function()
{
var user = $('#acluser').val(), rights = '', type;
$(':checkbox', this.env.acl_advanced ? $('#advancedrights') : sim_ul = $('#simplerights')).map(function() {
if (this.checked)
rights += this.value;
});
if (type = $('input:checked[name=usertype]').val()) {
if (type != 'user')
user = type;
}
if (!user) {
alert(this.get_label('acl.nouser'));
return;
}
if (!rights) {
alert(this.get_label('acl.norights'));
return;
}
this.http_request('settings/plugin.acl', '_act=save'
+ '&_user='+urlencode(user)
+ '&_acl=' +rights
+ '&_mbox='+urlencode(this.env.mailbox)
+ (this.acl_id ? '&_old='+this.acl_id : ''),
this.set_busy(true, 'acl.saving'));
}
// Cancel/Hide form
rcube_webmail.prototype.acl_cancel = function()
{
this.ksearch_blur();
this.acl_form.hide();
}
// Update data after save (and hide form)
rcube_webmail.prototype.acl_update = function(o)
{
// delete old row
if (o.old)
this.acl_remove_row(o.old);
// make sure the same ID doesn't exist
else if (this.env.acl[o.id])
this.acl_remove_row(o.id);
// add new row
this.acl_add_row(o, true);
// hide autocomplete popup
this.ksearch_blur();
// hide form
this.acl_form.hide();
}
// Switch table display mode
rcube_webmail.prototype.acl_mode_switch = function(elem)
{
this.env.acl_advanced = !this.env.acl_advanced;
this.enable_command('acl-delete', 'acl-edit', false);
this.http_request('settings/plugin.acl', '_act=list'
+ '&_mode='+(this.env.acl_advanced ? 'advanced' : 'simple')
+ '&_mbox='+urlencode(this.env.mailbox),
this.set_busy(true, 'loading'));
}
// ACL table initialization
rcube_webmail.prototype.acl_list_init = function()
{
this.acl_list = new rcube_list_widget(this.gui_objects.acltable,
{multiselect:true, draggable:false, keyboard:true, toggleselect:true});
this.acl_list.addEventListener('select', function(o) { rcmail.acl_list_select(o); });
this.acl_list.addEventListener('dblclick', function(o) { rcmail.acl_list_dblclick(o); });
this.acl_list.addEventListener('keypress', function(o) { rcmail.acl_list_keypress(o); });
this.acl_list.init();
}
// ACL table row selection handler
rcube_webmail.prototype.acl_list_select = function(list)
{
rcmail.enable_command('acl-delete', list.selection.length > 0);
rcmail.enable_command('acl-edit', list.selection.length == 1);
list.focus();
}
// ACL table double-click handler
rcube_webmail.prototype.acl_list_dblclick = function(list)
{
this.acl_edit();
}
// ACL table keypress handler
rcube_webmail.prototype.acl_list_keypress = function(list)
{
if (list.key_pressed == list.ENTER_KEY)
this.command('acl-edit');
else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
if (!this.acl_form || !this.acl_form.is(':visible'))
this.command('acl-delete');
}
// Reloads ACL table
rcube_webmail.prototype.acl_list_update = function(html)
{
$(this.gui_objects.acltable).html(html);
this.acl_list_init();
}
// Returns names of users in selected rows
rcube_webmail.prototype.acl_get_usernames = function()
{
var users = [], n, len, cell, row,
list = this.acl_list,
selection = list.get_selection();
for (n=0, len=selection.length; n<len; n++) {
if (this.env.acl_specials.length && $.inArray(selection[n], this.env.acl_specials) >= 0) {
users.push(selection[n]);
}
else {
row = list.rows[selection[n]].obj;
cell = $('td.user', row);
if (cell.length == 1)
users.push(cell.text());
}
}
return users;
}
// Removes ACL table row
rcube_webmail.prototype.acl_remove_row = function(id)
{
this.acl_list.remove_row(id);
// we don't need it anymore (remove id conflict)
$('#rcmrow'+id).remove();
this.env.acl[id] = null;
}
// Adds ACL table row
rcube_webmail.prototype.acl_add_row = function(o, sel)
{
var n, len, ids = [], spec = [], id = o.id, list = this.acl_list,
items = this.env.acl_advanced ? [] : this.env.acl_items,
table = this.gui_objects.acltable,
row = $('thead > tr', table).clone();
// Update new row
$('td', row).map(function() {
var r, cl = this.className.replace(/^acl/, '');
if (items && items[cl])
cl = items[cl];
if (cl == 'user')
$(this).text(o.username);
else
$(this).addClass(rcmail.acl_class(o.acl, cl)).text('');
});
row.attr('id', 'rcmrow'+id);
row = row.get(0);
this.env.acl[id] = o.acl;
// sorting... (create an array of user identifiers, then sort it)
for (n in this.env.acl) {
if (this.env.acl[n]) {
if (this.env.acl_specials.length && $.inArray(n, this.env.acl_specials) >= 0)
spec.push(n);
else
ids.push(n);
}
}
ids.sort();
// specials on the top
ids = spec.concat(ids);
// find current id
for (n=0, len=ids.length; n<len; n++)
if (ids[n] == id)
break;
// add row
if (n && n < len) {
$('#rcmrow'+ids[n-1]).after(row);
list.init_row(row);
list.rowcount++;
}
else
list.insert_row(row);
if (sel)
list.select_row(o.id);
}
// Initializes and shows ACL create/edit form
rcube_webmail.prototype.acl_init_form = function(id)
{
var ul, row, val = '', type = 'user', li_elements, body = $('body'),
adv_ul = $('#advancedrights'), sim_ul = $('#simplerights'),
name_input = $('#acluser');
if (!this.acl_form) {
var fn = function () { $('input[value=user]').prop('checked', true); };
name_input.click(fn).keypress(fn);
}
this.acl_form = $('#aclform');
// Hide unused items
if (this.env.acl_advanced) {
adv_ul.show();
sim_ul.hide();
ul = adv_ul;
}
else {
sim_ul.show();
adv_ul.hide();
ul = sim_ul;
}
// initialize form fields
li_elements = $(':checkbox', ul);
li_elements.attr('checked', false);
if (id) {
row = this.acl_list.rows[id].obj;
li_elements.map(function() {
var val = this.value, td = $('td.'+this.id, row);
if (td && td.hasClass('enabled'))
this.checked = true;
});
if (!this.env.acl_specials.length || $.inArray(id, this.env.acl_specials) < 0)
val = $('td.user', row).text();
else
type = id;
}
name_input.val(val);
$('input[value='+type+']').prop('checked', true);
this.acl_id = id;
// position the form horizontally
var bw = body.width(), mw = this.acl_form.width();
if (bw >= mw)
this.acl_form.css({left: parseInt((bw - mw)/2)+'px'});
// display it
this.acl_form.show();
if (type == 'user')
name_input.focus();
}
// Returns class name according to ACL comparision result
rcube_webmail.prototype.acl_class = function(acl1, acl2)
{
var i, len, found = 0;
acl1 = String(acl1);
acl2 = String(acl2);
for (i=0, len=acl2.length; i<len; i++)
if (acl1.indexOf(acl2[i]) > -1)
found++;
if (found == len)
return 'enabled';
else if (found)
return 'partial';
return 'disabled';
}

@ -0,0 +1,696 @@
<?php
/**
* Folders Access Control Lists Management (RFC4314, RFC2086)
*
* @version 0.6.1
* @author Aleksander Machniak <alec@alec.pl>
*
*
* Copyright (C) 2011, Kolab Systems AG
*
* 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.
*/
class acl extends rcube_plugin
{
public $task = 'settings|addressbook|calendar';
private $rc;
private $supported = null;
private $mbox;
private $ldap;
private $specials = array('anyone', 'anonymous');
/**
* Plugin initialization
*/
function init()
{
$this->rc = rcmail::get_instance();
// Register hooks
$this->add_hook('folder_form', array($this, 'folder_form'));
// kolab_addressbook plugin
$this->add_hook('addressbook_form', array($this, 'folder_form'));
$this->add_hook('calendar_form_kolab', array($this, 'folder_form'));
// Plugin actions
$this->register_action('plugin.acl', array($this, 'acl_actions'));
$this->register_action('plugin.acl-autocomplete', array($this, 'acl_autocomplete'));
}
/**
* Handler for plugin actions (AJAX)
*/
function acl_actions()
{
$action = trim(get_input_value('_act', RCUBE_INPUT_GPC));
// Connect to IMAP
$this->rc->imap_init();
$this->rc->imap_connect();
// Load localization and configuration
$this->add_texts('localization/');
$this->load_config();
if ($action == 'save') {
$this->action_save();
}
else if ($action == 'delete') {
$this->action_delete();
}
else if ($action == 'list') {
$this->action_list();
}
// Only AJAX actions
$this->rc->output->send();
}
/**
* Handler for user login autocomplete request
*/
function acl_autocomplete()
{
$this->load_config();
$search = get_input_value('_search', RCUBE_INPUT_GPC, true);
$users = array();
if ($this->init_ldap()) {
$this->ldap->set_pagesize(15);
$result = $this->ldap->search('*', $search);
foreach ($result->records as $record) {
$user = $record['uid'];
if (is_array($user)) {
$user = array_filter($user);
$user = $user[0];
}
if ($user) {
if ($record['name'])
$user = $record['name'] . ' (' . $user . ')';
$users[] = $user;
}
}
}
sort($users, SORT_LOCALE_STRING);
$this->rc->output->command('ksearch_query_results', $users, $search);
$this->rc->output->send();
}
/**
* Handler for 'folder_form' hook
*
* @param array $args Hook arguments array (form data)
*
* @return array Hook arguments array
*/
function folder_form($args)
{
// Edited folder name (empty in create-folder mode)
$mbox_imap = $args['options']['name'];
if (!strlen($mbox_imap)) {
return $args;
}
/*
// Do nothing on protected folders (?)
if ($args['options']['protected']) {
return $args;
}
*/
// Namespace root
if ($args['options']['is_root']) {
return $args;
}
// Get MYRIGHTS
if (!($myrights = $args['options']['rights'])) {
return $args;
}
// Do nothing if no ACL support
if (!$this->rc->imap->get_capability('ACL')) {
return $args;
}
// Load localization and include scripts
$this->load_config();
$this->add_texts('localization/', array('deleteconfirm', 'norights',
'nouser', 'deleting', 'saving'));
$this->include_script('acl.js');
$this->rc->output->include_script('list.js');
$this->include_stylesheet($this->local_skin_path().'/acl.css');
// add Info fieldset if it doesn't exist
if (!isset($args['form']['props']['fieldsets']['info']))
$args['form']['props']['fieldsets']['info'] = array(
'name' => rcube_label('info'),
'content' => array());
// Display folder rights to 'Info' fieldset
$args['form']['props']['fieldsets']['info']['content']['myrights'] = array(
'label' => Q($this->gettext('myrights')),
'value' => $this->acl2text($myrights)
);
// Return if not folder admin
if (!in_array('a', $myrights)) {
return $args;
}
// The 'Sharing' tab
$this->mbox = $mbox_imap;
$this->rc->output->set_env('acl_users_source', (bool) $this->rc->config->get('acl_users_source'));
$this->rc->output->set_env('mailbox', $mbox_imap);
$this->rc->output->add_handlers(array(
'acltable' => array($this, 'templ_table'),
'acluser' => array($this, 'templ_user'),
'aclrights' => array($this, 'templ_rights'),
));
$args['form']['sharing'] = array(
'name' => Q($this->gettext('sharing')),
'content' => $this->rc->output->parse('acl.table', false, false),
);
return $args;
}
/**
* Creates ACL rights table
*
* @param array $attrib Template object attributes
*
* @return string HTML Content
*/
function templ_table($attrib)
{
if (empty($attrib['id']))
$attrib['id'] = 'acl-table';
$out = $this->list_rights($attrib);
$this->rc->output->add_gui_object('acltable', $attrib['id']);
return $out;
}
/**
* Creates ACL rights form (rights list part)
*
* @param array $attrib Template object attributes
*
* @return string HTML Content
*/
function templ_rights($attrib)
{
// Get supported rights
$supported = $this->rights_supported();
// depending on server capability either use 'te' or 'd' for deleting msgs
$deleteright = implode(array_intersect(str_split('ted'), $supported));
$out = '';
$ul = '';
$input = new html_checkbox();
// Advanced rights
$attrib['id'] = 'advancedrights';
foreach ($supported as $val) {
$id = "acl$val";
$ul .= html::tag('li', null,
$input->show('', array(
'name' => "acl[$val]", 'value' => $val, 'id' => $id))
. html::label(array('for' => $id, 'title' => $this->gettext('longacl'.$val)),
$this->gettext('acl'.$val)));
}
$out = html::tag('ul', $attrib, $ul, html::$common_attrib);
// Simple rights
$ul = '';
$attrib['id'] = 'simplerights';
$items = array(
'read' => 'lrs',
'write' => 'wi',
'delete' => $deleteright,
'other' => preg_replace('/[lrswi'.$deleteright.']/', '', implode($supported)),
);
foreach ($items as $key => $val) {
$id = "acl$key";
$ul .= html::tag('li', null,
$input->show('', array(
'name' => "acl[$val]", 'value' => $val, 'id' => $id))
. html::label(array('for' => $id, 'title' => $this->gettext('longacl'.$key)),
$this->gettext('acl'.$key)));
}
$out .= "\n" . html::tag('ul', $attrib, $ul, html::$common_attrib);
$this->rc->output->set_env('acl_items', $items);
return $out;
}
/**
* Creates ACL rights form (user part)
*
* @param array $attrib Template object attributes
*
* @return string HTML Content
*/
function templ_user($attrib)
{
// Create username input
$attrib['name'] = 'acluser';
$textfield = new html_inputfield($attrib);
$fields['user'] = html::label(array('for' => 'iduser'), $this->gettext('username'))
. ' ' . $textfield->show();
// Add special entries
if (!empty($this->specials)) {
foreach ($this->specials as $key) {
$fields[$key] = html::label(array('for' => 'id'.$key), $this->gettext($key));
}
}
$this->rc->output->set_env('acl_specials', $this->specials);
// Create list with radio buttons
if (count($fields) > 1) {
$ul = '';
$radio = new html_radiobutton(array('name' => 'usertype'));
foreach ($fields as $key => $val) {
$ul .= html::tag('li', null, $radio->show($key == 'user' ? 'user' : '',
array('value' => $key, 'id' => 'id'.$key))
. $val);
}
$out = html::tag('ul', array('id' => 'usertype'), $ul, html::$common_attrib);
}
// Display text input alone
else {
$out = $fields['user'];
}
return $out;
}
/**
* Creates ACL rights table
*
* @param array $attrib Template object attributes
*
* @return string HTML Content
*/
private function list_rights($attrib=array())
{
// Get ACL for the folder
$acl = $this->rc->imap->get_acl($this->mbox);
if (!is_array($acl)) {
$acl = array();
}
// Keep special entries (anyone/anonymous) on top of the list
if (!empty($this->specials) && !empty($acl)) {
foreach ($this->specials as $key) {
if (isset($acl[$key])) {
$acl_special[$key] = $acl[$key];
unset($acl[$key]);
}
}
}
// Sort the list by username
uksort($acl, 'strnatcasecmp');
if (!empty($acl_special)) {
$acl = array_merge($acl_special, $acl);
}
// Get supported rights and build column names
$supported = $this->rights_supported();
// depending on server capability either use 'te' or 'd' for deleting msgs
$deleteright = implode(array_intersect(str_split('ted'), $supported));
// Use advanced or simple (grouped) rights
$advanced = $this->rc->config->get('acl_advanced_mode');
if ($advanced) {
$items = array();
foreach ($supported as $sup) {
$items[$sup] = $sup;
}
}
else {
$items = array(
'read' => 'lrs',
'write' => 'wi',
'delete' => $deleteright,
'other' => preg_replace('/[lrswi'.$deleteright.']/', '', implode($supported)),
);
}
// Create the table
$attrib['noheader'] = true;
$table = new html_table($attrib);
// Create table header
$table->add_header('user', $this->gettext('identifier'));
foreach (array_keys($items) as $key) {
$table->add_header('acl'.$key, $this->gettext('shortacl'.$key));
}
$i = 1;
$js_table = array();
foreach ($acl as $user => $rights) {
if ($this->rc->imap->conn->user == $user) {
continue;
}
// filter out virtual rights (c or d) the server may return
$userrights = array_intersect($rights, $supported);
$userid = html_identifier($user);
if (!empty($this->specials) && in_array($user, $this->specials)) {
$user = $this->gettext($user);
}
$table->add_row(array('id' => 'rcmrow'.$userid));
$table->add('user', Q($user));
foreach ($items as $key => $right) {
$in = $this->acl_compare($userrights, $right);
switch ($in) {
case 2: $class = 'enabled'; break;
case 1: $class = 'partial'; break;
default: $class = 'disabled'; break;
}
$table->add('acl' . $key . ' ' . $class, '');
}
$js_table[$userid] = implode($userrights);
}
$this->rc->output->set_env('acl', $js_table);
$this->rc->output->set_env('acl_advanced', $advanced);
$out = $table->show();
return $out;
}
/**
* Handler for ACL update/create action
*/
private function action_save()
{
$mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); // UTF7-IMAP
$user = trim(get_input_value('_user', RCUBE_INPUT_GPC));
$acl = trim(get_input_value('_acl', RCUBE_INPUT_GPC));
$oldid = trim(get_input_value('_old', RCUBE_INPUT_GPC));
$acl = array_intersect(str_split($acl), $this->rights_supported());
if (!empty($this->specials) && in_array($user, $this->specials)) {
$username = $this->gettext($user);
}
else {
if (!strpos($user, '@') && ($realm = $this->get_realm())) {
$user .= '@' . rcube_idn_to_ascii(preg_replace('/^@/', '', $realm));
}
$username = $user;
}
if ($acl && $user && $user != $_SESSION['username'] && strlen($mbox)) {
$result = $this->rc->imap->set_acl($mbox, $user, $acl);
}
if ($result) {
$ret = array('id' => html_identifier($user),
'username' => $username, 'acl' => implode($acl), 'old' => $oldid);
$this->rc->output->command('acl_update', $ret);
$this->rc->output->show_message($oldid ? 'acl.updatesuccess' : 'acl.createsuccess', 'confirmation');
}
else {
$this->rc->output->show_message($oldid ? 'acl.updateerror' : 'acl.createerror', 'error');
}
}
/**
* Handler for ACL delete action
*/
private function action_delete()
{
$mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); //UTF7-IMAP
$user = trim(get_input_value('_user', RCUBE_INPUT_GPC));
$user = explode(',', $user);
foreach ($user as $u) {
if ($this->rc->imap->delete_acl($mbox, $u)) {
$this->rc->output->command('acl_remove_row', html_identifier($u));
}
else {
$error = true;
}
}
if (!$error) {
$this->rc->output->show_message('acl.deletesuccess', 'confirmation');
}
else {
$this->rc->output->show_message('acl.deleteerror', 'error');
}
}
/**
* Handler for ACL list update action (with display mode change)
*/
private function action_list()
{
if (in_array('acl_advanced_mode', (array)$this->rc->config->get('dont_override'))) {
return;
}
$this->mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true)); // UTF7-IMAP
$advanced = trim(get_input_value('_mode', RCUBE_INPUT_GPC));
$advanced = $advanced == 'advanced' ? true : false;
// Save state in user preferences
$this->rc->user->save_prefs(array('acl_advanced_mode' => $advanced));
$out = $this->list_rights();
$out = preg_replace(array('/^<table[^>]+>/', '/<\/table>$/'), '', $out);
$this->rc->output->command('acl_list_update', $out);
}
/**
* Creates <UL> list with descriptive access rights
*
* @param array $rights MYRIGHTS result
*
* @return string HTML content
*/
function acl2text($rights)
{
if (empty($rights)) {
return '';
}
$supported = $this->rights_supported();
$list = array();
$attrib = array(
'name' => 'rcmyrights',
'style' => 'margin:0; padding:0 15px;',
);
foreach ($supported as $right) {
if (in_array($right, $rights)) {
$list[] = html::tag('li', null, Q($this->gettext('acl' . $right)));
}
}
if (count($list) == count($supported))
return Q($this->gettext('aclfull'));
return html::tag('ul', $attrib, implode("\n", $list));
}
/**
* Compares two ACLs (according to supported rights)
*
* @param array $acl1 ACL rights array (or string)
* @param array $acl2 ACL rights array (or string)
*
* @param int Comparision result, 2 - full match, 1 - partial match, 0 - no match
*/
function acl_compare($acl1, $acl2)
{
if (!is_array($acl1)) $acl1 = str_split($acl1);
if (!is_array($acl2)) $acl2 = str_split($acl2);
$rights = $this->rights_supported();
$acl1 = array_intersect($acl1, $rights);
$acl2 = array_intersect($acl2, $rights);
$res = array_intersect($acl1, $acl2);
$cnt1 = count($res);
$cnt2 = count($acl2);
if ($cnt1 == $cnt2)
return 2;
else if ($cnt1)
return 1;
else
return 0;
}
/**
* Get list of supported access rights (according to RIGHTS capability)
*
* @return array List of supported access rights abbreviations
*/
function rights_supported()
{
if ($this->supported !== null) {
return $this->supported;
}
$capa = $this->rc->imap->get_capability('RIGHTS');
if (is_array($capa)) {
$rights = strtolower($capa[0]);
}
else {
$rights = 'cd';
}
return $this->supported = str_split('lrswi' . $rights . 'pa');
}
/**
* Username realm detection.
*
* @return string Username realm (domain)
*/
private function get_realm()
{
// When user enters a username without domain part, realm
// alows to add it to the username (and display correct username in the table)
if (isset($_SESSION['acl_username_realm'])) {
return $_SESSION['acl_username_realm'];
}
// find realm in username of logged user (?)
list($name, $domain) = explode('@', $_SESSION['username']);
// Use (always existent) ACL entry on the INBOX for the user to determine
// whether or not the user ID in ACL entries need to be qualified and how
// they would need to be qualified.
if (empty($domain)) {
$acl = $this->rc->imap->get_acl('INBOX');
if (is_array($acl)) {
$regexp = '/^' . preg_quote($_SESSION['username'], '/') . '@(.*)$/';
$regexp = '/^' . preg_quote('aleksander.machniak', '/') . '@(.*)$/';
foreach (array_keys($acl) as $name) {
if (preg_match($regexp, $name, $matches)) {
$domain = $matches[1];
break;
}
}
}
}
return $_SESSION['acl_username_realm'] = $domain;
}
/**
* Initializes autocomplete LDAP backend
*/
private function init_ldap()
{
if ($this->ldap)
return $this->ldap->ready;
// get LDAP config
$config = $this->rc->config->get('acl_users_source');
if (empty($config)) {
return false;
}
// not an array, use configured ldap_public source
if (!is_array($config)) {
$ldap_config = (array) $this->rc->config->get('ldap_public');
$config = $ldap_config[$config];
}
$uid_field = $this->rc->config->get('acl_users_field', 'mail');
$filter = $this->rc->config->get('acl_users_filter');
if (empty($uid_field) || empty($config)) {
return false;
}
// get name attribute
if (!empty($config['fieldmap'])) {
$name_field = $config['fieldmap']['name'];
}
// ... no fieldmap, use the old method
if (empty($name_field)) {
$name_field = $config['name_field'];
}
// add UID field to fieldmap, so it will be returned in a record with name
$config['fieldmap'] = array(
'name' => $name_field,
'uid' => $uid_field,
);
// search in UID and name fields
$config['search_fields'] = array_values($config['fieldmap']);
$config['required_fields'] = array($uid_field);
// set search filter
if ($filter)
$config['filter'] = $filter;
// disable vlv
$config['vlv'] = false;
// Initialize LDAP connection
$this->ldap = new rcube_ldap($config,
$this->rc->config->get('ldap_debug'),
$this->rc->config->mail_domain($_SESSION['imap_host']));
return $this->ldap->ready;
}
}

@ -0,0 +1,19 @@
<?php
// Default look of access rights table
// In advanced mode all access rights are displayed separately
// In simple mode access rights are grouped into four groups: read, write, delete, full
$rcmail_config['acl_advanced_mode'] = false;
// LDAP addressbook that would be searched for user names autocomplete.
// That should be an array refering to the $rcmail_config['ldap_public'] array key
// or complete addressbook configuration array.
$rcmail_config['acl_users_source'] = '';
// The LDAP attribute which will be used as ACL user identifier
$rcmail_config['acl_users_field'] = 'mail';
// The LDAP search filter will be &'d with search queries
$rcmail_config['acl_users_filter'] = '';
?>

@ -0,0 +1,83 @@
<?php
$labels['sharing'] = 'Freigabe';
$labels['myrights'] = 'Zugriffsrechte';
$labels['username'] = 'Benutzer:';
$labels['advanced'] = 'erweiterter Modus';
$labels['newuser'] = 'Eintrag hinzufügen';
$labels['actions'] = 'Zugriffsrechte Aktionen...';
$labels['anyone'] = 'Alle Benutzer (anyone)';
$labels['anonymous'] = 'Gäste (anonymous)';
$labels['identifier'] = 'Bezeichnung';
$labels['acll'] = 'Ordner sichtbar';
$labels['aclr'] = 'Nachrichten lesen';
$labels['acls'] = 'Lesestatus ändern';
$labels['aclw'] = 'Flags schreiben';
$labels['acli'] = 'Nachrichten Hinzufügen';
$labels['aclp'] = 'Nachrichten Senden an';
$labels['aclc'] = 'Unterordner erstellen';
$labels['aclk'] = 'Unterordner erstellen';
$labels['acld'] = 'Nachrichten als gelöscht markieren';
$labels['aclt'] = 'Nachrichten als gelöscht markieren';
$labels['acle'] = 'Nachrichten endgültig Löschen';
$labels['aclx'] = 'Ordner löschen';
$labels['acla'] = 'Zugriffsrechte Verwalten';
$labels['aclfull'] = 'Vollzugriff';
$labels['aclother'] = 'Andere';
$labels['aclread'] = 'Lesen';
$labels['aclwrite'] = 'Schreiben';
$labels['acldelete'] = 'Löschen';
$labels['shortacll'] = 'Sichtbar';
$labels['shortaclr'] = 'Lesen';
$labels['shortacls'] = 'Lesestatus';
$labels['shortaclw'] = 'Flags ändern';
$labels['shortacli'] = 'Hinzufügen';
$labels['shortaclp'] = 'Senden an';
$labels['shortaclc'] = 'Erstellen';
$labels['shortaclk'] = 'Erstellen';
$labels['shortacld'] = 'Löschen';
$labels['shortaclt'] = 'Löschen';
$labels['shortacle'] = 'endgültig löschen';
$labels['shortaclx'] = 'Ordner löschen';
$labels['shortacla'] = 'Verwalten';
$labels['shortaclother'] = 'Andere';
$labels['shortaclread'] = 'Lesen';
$labels['shortaclwrite'] = 'Schreiben';
$labels['shortacldelete'] = 'Löschen';
$labels['longacll'] = 'Der Ordner ist sichtbar und kann abonniert werden';
$labels['longaclr'] = 'Nachrichten im Ordner können gelesen werden';
$labels['longacls'] = 'Der Lesestatus von Nachrichten kann geändert werden';
$labels['longaclw'] = 'Alle Nachrichten-Flags und Schlüsselwörter außer "Gelesen" und "Gelöscht" können geändert werden';
$labels['longacli'] = 'Nachrichten können in diesen Ordner kopiert oder verschoben werden';
$labels['longaclp'] = 'Nachrichten können an diesen Ordner gesendet werden';
$labels['longaclc'] = 'Unterordner können in diesem Ordner erstellt oder umbenannt werden';
$labels['longaclk'] = 'Unterordner können in diesem Ordner erstellt oder umbenannt werden';
$labels['longacld'] = 'Der "gelöscht" Status von Nachrichten kann geändert werden';
$labels['longaclt'] = 'Der "gelöscht" Status von Nachrichten kann geändert werden';
$labels['longacle'] = 'Als "gelöscht" markiert Nachrichten können gelöscht werden.';
$labels['longaclx'] = 'Der Ordner kann gelöscht oder umbenannt werden';
$labels['longacla'] = 'Die Zugriffsrechte des Ordners können geändert werden';
$labels['longaclfull'] = 'Vollzugriff inklusive Ordner-Verwaltung';
$labels['longaclread'] = 'Der Ordnerinhalt kann gelesen werden';
$labels['longaclwrite'] = 'Nachrichten können markiert, an den Ordner gesendet und in den Ordner kopiert oder verschoben werden';
$labels['longacldelete'] = 'Nachrichten können gelöscht werden';
$messages['deleting'] = 'Zugriffsrechte werden entzogen...';
$messages['saving'] = 'Zugriffsrechte werden gewährt...';
$messages['updatesuccess'] = 'Zugriffsrechte erfolgreich geändert';
$messages['deletesuccess'] = 'Zugriffsrechte erfolgreich entzogen';
$messages['createsuccess'] = 'Zugriffsrechte erfolgreich gewährt';
$messages['updateerror'] = 'Zugriffsrechte konnten nicht geändert werden';
$messages['deleteerror'] = 'Zugriffsrechte konnten nicht entzogen werden';
$messages['createerror'] = 'Zugriffsrechte konnten nicht gewährt werden';
$messages['deleteconfirm'] = 'Sind Sie sicher, daß Sie die Zugriffsrechte den ausgewählten Benutzern entziehen möchten?';
$messages['norights'] = 'Es wurden keine Zugriffsrechte ausgewählt!';
$messages['nouser'] = 'Es wurde kein Benutzer ausgewählt!';
?>

@ -0,0 +1,83 @@
<?php
$labels['sharing'] = 'Sharing';
$labels['myrights'] = 'Access Rights';
$labels['username'] = 'User:';
$labels['advanced'] = 'advanced mode';
$labels['newuser'] = 'Add entry';
$labels['actions'] = 'Access right actions...';
$labels['anyone'] = 'All users (anyone)';
$labels['anonymous'] = 'Guests (anonymous)';
$labels['identifier'] = 'Identifier';
$labels['acll'] = 'Lookup';
$labels['aclr'] = 'Read messages';
$labels['acls'] = 'Keep Seen state';
$labels['aclw'] = 'Write flags';
$labels['acli'] = 'Insert (Copy into)';
$labels['aclp'] = 'Post';
$labels['aclc'] = 'Create subfolders';
$labels['aclk'] = 'Create subfolders';
$labels['acld'] = 'Delete messages';
$labels['aclt'] = 'Delete messages';
$labels['acle'] = 'Expunge';
$labels['aclx'] = 'Delete folder';
$labels['acla'] = 'Administer';
$labels['aclfull'] = 'Full control';
$labels['aclother'] = 'Other';
$labels['aclread'] = 'Read';
$labels['aclwrite'] = 'Write';
$labels['acldelete'] = 'Delete';
$labels['shortacll'] = 'Lookup';
$labels['shortaclr'] = 'Read';
$labels['shortacls'] = 'Keep';
$labels['shortaclw'] = 'Write';
$labels['shortacli'] = 'Insert';
$labels['shortaclp'] = 'Post';
$labels['shortaclc'] = 'Create';
$labels['shortaclk'] = 'Create';
$labels['shortacld'] = 'Delete';
$labels['shortaclt'] = 'Delete';
$labels['shortacle'] = 'Expunge';
$labels['shortaclx'] = 'Folder delete';
$labels['shortacla'] = 'Administer';
$labels['shortaclother'] = 'Other';
$labels['shortaclread'] = 'Read';
$labels['shortaclwrite'] = 'Write';
$labels['shortacldelete'] = 'Delete';
$labels['longacll'] = 'The folder is visible on lists and can be subscribed to';
$labels['longaclr'] = 'The folder can be opened for reading';
$labels['longacls'] = 'Messages Seen flag can be changed';
$labels['longaclw'] = 'Messages flags and keywords can be changed, except Seen and Deleted';
$labels['longacli'] = 'Messages can be written or copied to the folder';
$labels['longaclp'] = 'Messages can be posted to this folder';
$labels['longaclc'] = 'Folders can be created (or renamed) directly under this folder';
$labels['longaclk'] = 'Folders can be created (or renamed) directly under this folder';
$labels['longacld'] = 'Messages Delete flag can be changed';
$labels['longaclt'] = 'Messages Delete flag can be changed';
$labels['longacle'] = 'Messages can be expunged';
$labels['longaclx'] = 'The folder can be deleted or renamed';
$labels['longacla'] = 'The folder access rights can be changed';
$labels['longaclfull'] = 'Full control including folder administration';
$labels['longaclread'] = 'The folder can be opened for reading';
$labels['longaclwrite'] = 'Messages can be marked, written or copied to the folder';
$labels['longacldelete'] = 'Messages can be deleted';
$messages['deleting'] = 'Deleting access rights...';
$messages['saving'] = 'Saving access rights...';
$messages['updatesuccess'] = 'Successfully changed access rights';
$messages['deletesuccess'] = 'Successfully deleted access rights';
$messages['createsuccess'] = 'Successfully added access rights';
$messages['updateerror'] = 'Ubable to update access rights';
$messages['deleteerror'] = 'Unable to delete access rights';
$messages['createerror'] = 'Unable to add access rights';
$messages['deleteconfirm'] = 'Are you sure, you want to remove access rights of selected user(s)?';
$messages['norights'] = 'No rights has been specified!';
$messages['nouser'] = 'No username has been specified!';
?>

@ -0,0 +1,83 @@
<?php
$labels['sharing'] = 'Udostępnianie';
$labels['myrights'] = 'Prawa dostępu';
$labels['username'] = 'Użytkownik:';
$labels['advanced'] = 'tryb zaawansowany';
$labels['newuser'] = 'Dodaj rekord';
$labels['actions'] = 'Akcje na prawach...';
$labels['anyone'] = 'Wszyscy (anyone)';
$labels['anonymous'] = 'Goście (anonymous)';
$labels['identifier'] = 'Identyfikator';
$labels['acll'] = 'Podgląd (Lookup)';
$labels['aclr'] = 'Odczyt (Read)';
$labels['acls'] = 'Zmiana stanu wiadomości (Keep)';
$labels['aclw'] = 'Zmiana flag wiadomości (Write)';
$labels['acli'] = 'Dodawanie/Kopiowanie do (Insert)';
$labels['aclp'] = 'Wysyłanie (Post)';
$labels['aclc'] = 'Tworzenie podfolderów (Create)';
$labels['aclk'] = 'Tworzenie podfolderów (Create)';
$labels['acld'] = 'Usuwanie wiadomości (Delete)';
$labels['aclt'] = 'Usuwanie wiadomości (Delete)';
$labels['acle'] = 'Porządkowanie folderu (Expunge)';
$labels['aclx'] = 'Usuwanie folderu (Delete)';
$labels['acla'] = 'Administracja (Administer)';
$labels['aclfull'] = 'Wszystkie';
$labels['aclother'] = 'Inne';
$labels['aclread'] = 'Odczyt';
$labels['aclwrite'] = 'Zapis';
$labels['acldelete'] = 'Usuwanie';
$labels['shortacll'] = 'Podgląd';
$labels['shortaclr'] = 'Odczyt';
$labels['shortacls'] = 'Zmiana';
$labels['shortaclw'] = 'Zmiana flag';
$labels['shortacli'] = 'Dodawanie';
$labels['shortaclp'] = 'Wysyłanie';
$labels['shortaclc'] = 'Tworzenie';
$labels['shortaclk'] = 'Tworzenie';
$labels['shortacld'] = 'Usuwanie';
$labels['shortaclt'] = 'Usuwanie';
$labels['shortacle'] = 'Porządkowanie';
$labels['shortaclx'] = 'Usuwanie folderu';
$labels['shortacla'] = 'Administracja';
$labels['shortaclother'] = 'Pozostałe';
$labels['shortaclread'] = 'Odczyt';
$labels['shortaclwrite'] = 'Zapis';
$labels['shortacldelete'] = 'Usuwanie';
$labels['longacll'] = 'Pozwala na subskrybowanie folderu i powoduje, że jest on widoczny na liście';
$labels['longaclr'] = 'Pozwala na otwarcie folderu w trybie do odczytu';
$labels['longacls'] = 'Pozwala na zmienę stanu wiadomości';
$labels['longaclw'] = 'Pozwala zmieniać wszystkie flagi wiadomości, oprócz "Przeczytano" i "Usunięto"';
$labels['longacli'] = 'Pozwala zapisywać wiadomości i kopiować do folderu';
$labels['longaclp'] = 'Pozwala wysyłać wiadomości do folderu';
$labels['longaclc'] = 'Pozwala tworzyć (lub zmieniać nazwę) podfoldery';
$labels['longaclk'] = 'Pozwala tworzyć (lub zmieniać nazwę) podfoldery';
$labels['longacld'] = 'Pozwala zmianiać flagę "Usunięto" wiadomości';
$labels['longaclt'] = 'Pozwala zmianiać flagę "Usunięto" wiadomości';
$labels['longacle'] = 'Pozwala na usuwanie wiadomości oznaczonych do usunięcia';
$labels['longaclx'] = 'Pozwala na zmianę nazwy lub usunięcie folderu';
$labels['longacla'] = 'Pozwala na zmiane praw dostępu do folderu';
$labels['longaclfull'] = 'Pełna kontrola włącznie z administrowaniem folderem';
$labels['longaclread'] = 'Folder może być otwarty w trybie do odczytu';
$labels['longaclwrite'] = 'Wiadomości mogą być oznaczane, zapisywane i kopiowane do folderu';
$labels['longacldelete'] = 'Wiadomości mogą być usuwane';
$messages['deleting'] = 'Usuwanie praw dostępu...';
$messages['saving'] = 'Zapisywanie praw dostępu...';
$messages['updatesuccess'] = 'Pomyślnie zmieniono prawa dostępu';
$messages['deletesuccess'] = 'Pomyślnie usunięto prawa dostępu';
$messages['createsuccess'] = 'Pomyślnie dodano prawa dostępu';
$messages['updateerror'] = 'Nie udało się zmienić praw dostępu';
$messages['deleteerror'] = 'Nie udało się usunąć praw dostępu';
$messages['createerror'] = 'Nie udało się dodać praw dostępu';
$messages['deleteconfirm'] = 'Czy na pewno chcesz usunąć prawa wybranym użytkownikom?';
$messages['norights'] = 'Nie wybrano praw dostępu!';
$messages['nouser'] = 'Nie podano nazwy użytkownika!';
?>

@ -0,0 +1,94 @@
#aclmanager
{
position: relative;
border: 1px solid #999;
min-height: 302px;
}
#aclcontainer
{
overflow-x: auto;
}
#acltable
{
width: 100%;
border-collapse: collapse;
background-color: #F9F9F9;
}
#acltable td
{
width: 1%;
white-space: nowrap;
}
#acltable thead td
{
padding: 0 4px 0 2px;
}
#acltable tbody td
{
text-align: center;
padding: 2px;
border-bottom: 1px solid #999999;
cursor: default;
}
#acltable tbody td.user
{
width: 96%;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
#acltable tbody td.partial
{
background: url(images/partial.png) center no-repeat;
}
#acltable tbody td.enabled
{
background: url(images/enabled.png) center no-repeat;
}
#acltable tr.selected td
{
color: #FFFFFF;
background-color: #CC3333;
}
#acladvswitch
{
position: absolute;
right: 4px;
text-align: right;
line-height: 22px;
}
#acladvswitch input
{
vertical-align: middle;
}
#acladvswitch span
{
display: block;
}
#aclform
{
top: 80px;
width: 480px;
padding: 10px;
}
#aclform div
{
padding: 0;
text-align: center;
clear: both;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

@ -0,0 +1,54 @@
<!--[if lte IE 6]>
<style type="text/css">
#aclmanager { height: expression(Math.min(302, parseInt(document.documentElement.clientHeight))+'px'); }
</style>
<![endif]-->
<div id="aclmanager">
<div id="aclcontainer" class="boxlistcontent" style="top:0">
<roundcube:object name="acltable" id="acltable" class="records-table" />
</div>
<div class="boxfooter">
<roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="buttonPas addgroup" classAct="button addgroup" content=" " />
<roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="button groupactions" onclick="show_aclmenu(); return false" content=" " />
<roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" />
<div id="acladvswitch" class="pagenav">
<span><label for="acl-switch"><roundcube:label name="acl.advanced" /></label>
<input type="checkbox" id="acl-switch" onclick="rcmail.command('acl-mode-switch')"<roundcube:exp expression="config:acl_advanced_mode == true ? ' checked=checked' : ''" /> />
</span>
</div>
<roundcube:endif />
</div>
</div>
<div id="aclmenu" class="popupmenu">
<ul>
<li><roundcube:button command="acl-edit" label="edit" classAct="active" /></li>
<li><roundcube:button command="acl-delete" label="delete" classAct="active" /></li>
</ul>
</div>
<div id="aclform" class="popupmenu">
<fieldset class="thinbordered"><legend><roundcube:label name="acl.identifier" /></legend>
<roundcube:object name="acluser" class="toolbarmenu" id="acluser" size="35" />
</fieldset>
<fieldset class="thinbordered"><legend><roundcube:label name="acl.myrights" /></legend>
<roundcube:object name="aclrights" class="toolbarmenu" />
</fieldset>
<div>
<roundcube:button command="acl-cancel" type="input" class="button" label="cancel" />
<roundcube:button command="acl-save" type="input" class="button mainaction" label="save" />
</div>
</div>
<script type="text/javascript">
function show_aclmenu()
{
if (!rcmail_ui) {
rcube_init_mail_ui();
rcmail_ui.popups.aclmenu = {id:'aclmenu', above:1, obj: $('#aclmenu')};
}
rcmail_ui.show_popup('aclmenu');
}
</script>

@ -0,0 +1,43 @@
<?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;
}
}
?>

@ -0,0 +1,14 @@
<?php
// $rcmail_config['additional_message_headers']['X-Remote-Browser'] = $_SERVER['HTTP_USER_AGENT'];
// $rcmail_config['additional_message_headers']['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
// $rcmail_config['additional_message_headers']['X-RoundCube-Server'] = $_SERVER['SERVER_ADDR'];
// if( isset( $_SERVER['MACHINE_NAME'] )) {
// $rcmail_config['additional_message_headers']['X-RoundCube-Server'] .= ' (' . $_SERVER['MACHINE_NAME'] . ')';
// }
// To remove (e.g. X-Sender) message header use null value
// $rcmail_config['additional_message_headers']['X-Sender'] = null;
?>

@ -0,0 +1,47 @@
<?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>

@ -0,0 +1,34 @@
/*
* Archive plugin script
* @version @package_version@
*/
function rcmail_archive(prop)
{
if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length))
return;
if (rcmail.env.mailbox != rcmail.env.archive_folder)
rcmail.command('moveto', rcmail.env.archive_folder);
}
// 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 + ')');
})
}

@ -0,0 +1,124 @@
<?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();
// 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',
'width' => 32,
'height' => 32,
'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 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;
}
}
}

@ -0,0 +1,25 @@
<?php
/*
+-----------------------------------------------------------------------+
| language/cs_CZ/labels.inc |
| |
| Language file of the Roundcube archive plugin |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| 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';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Nachricht archivieren';
$labels['archived'] = 'Nachricht erfolgreich archiviert';
$labels['archivefolder'] = 'Archiv';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Nachricht archivieren';
$labels['archived'] = 'Nachricht erfolgreich archiviert';
$labels['archivefolder'] = 'Archiv';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Archive this message';
$labels['archived'] = 'Successfully archived';
$labels['archivefolder'] = 'Archive';
?>

@ -0,0 +1,10 @@
<?php
// MPBAUPGRADE
$labels = array();
$labels['buttontitle'] = 'Archivar este mensaje';
$labels['archived'] = 'Mensaje Archivado';
$labels['archivefolder'] = 'Archivo';
?>

@ -0,0 +1,10 @@
<?php
// MPBAUPGRADE
$labels = array();
$labels['buttontitle'] = 'Archivar este mensaje';
$labels['archived'] = 'Mensaje Archivado';
$labels['archivefolder'] = 'Archivo';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Arhiveeri see kiri';
$labels['archived'] = 'Edukalt arhiveeritud';
$labels['archivefolder'] = 'Arhiveeri';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Archiver ce message';
$labels['archived'] = 'Message archivé avec success';
$labels['archivefolder'] = 'Archive';
?>

@ -0,0 +1,10 @@
<?php
// MPBAUPGRADE
$labels = array();
$labels['buttontitle'] = 'Arquivar esta mensaxe';
$labels['archived'] = 'Aquivouse a mensaxe';
$labels['archivefolder'] = 'Arquivo';
?>

@ -0,0 +1,10 @@
<?php
// EN-Revision: 3891
$labels = array();
$labels['buttontitle'] = 'このメッセージのアーカイブ';
$labels['archived'] = 'アーカイブに成功しました。';
$labels['archivefolder'] = 'アーカイブ';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Archiveer dit bericht';
$labels['archived'] = 'Succesvol gearchiveerd';
$labels['archivefolder'] = 'Archief';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Przenieś do archiwum';
$labels['archived'] = 'Pomyślnie zarchiwizowano';
$labels['archivefolder'] = 'Archiwum';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Arquivar esta mensagem';
$labels['archived'] = 'Arquivada com sucesso';
$labels['archivefolder'] = 'Arquivo';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Переместить выбранное в архив';
$labels['archived'] = 'Перенесено в Архив';
$labels['archivefolder'] = 'Архив';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = 'Arkivera meddelande';
$labels['archived'] = 'Meddelandet är arkiverat';
$labels['archivefolder'] = 'Arkiv';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['buttontitle'] = '封存此信件';
$labels['archived'] = '已成功封存';
$labels['archivefolder'] = '封存';
?>

@ -0,0 +1,64 @@
<?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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,47 @@
<?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';
$args['cookiecheck'] = false;
$args['valid'] = true;
}
return $args;
}
function is_localhost()
{
return $_SERVER['REMOTE_ADDR'] == '::1' || $_SERVER['REMOTE_ADDR'] == '127.0.0.1';
}
}

@ -0,0 +1,168 @@
<?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($args)
{
$uname = $args['path'] ? $args['path'] : $args['name'];
return $this->cache_prefix . $args['group'] . md5(mktime() . $uname . $_SESSION['user_id']);
}
/**
* Save a newly uploaded attachment
*/
function upload($args)
{
$args['status'] = false;
$rcmail = rcmail::get_instance();
$key = $this->_key($args);
$data = file_get_contents($args['path']);
if ($data === false)
return $args;
$data = base64_encode($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;
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);
if ($args['path']) {
$args['data'] = file_get_contents($args['path']);
if ($args['data'] === false)
return $args;
}
$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)
{
$prefix = $this->cache_prefix . $args['group'];
$rcmail = rcmail::get_instance();
$rcmail->db->query(
"DELETE FROM ".get_table_name('cache')."
WHERE user_id=?
AND cache_key like '{$prefix}%'",
$_SESSION['user_id']);
}
}

@ -0,0 +1,146 @@
<?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();
}
}
?>

@ -0,0 +1,227 @@
<?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);
}
}
}
?>

@ -0,0 +1,77 @@
<?php
/**
* Display Emoticons
*
* Sample plugin to replace emoticons in plain text message body with real icons
*
* @version 1.3
* @author Thomas Bruederli
* @author Aleksander Machniak
* @website http://roundcube.net
*/
class emoticons extends rcube_plugin
{
public $task = 'mail';
function init()
{
$this->add_hook('message_part_after', array($this, 'replace'));
}
function replace($args)
{
// This is a lookbehind assertion which will exclude html entities
// E.g. situation when ";)" in "&quot;)" shouldn't be replaced by the icon
// It's so long because of assertion format restrictions
$entity = '(?<!&'
. '[a-zA-Z0-9]{2}' . '|' . '#[0-9]{2}' . '|'
. '[a-zA-Z0-9]{3}' . '|' . '#[0-9]{3}' . '|'
. '[a-zA-Z0-9]{4}' . '|' . '#[0-9]{4}' . '|'
. '[a-zA-Z0-9]{5}' . '|'
. '[a-zA-Z0-9]{6}' . '|'
. '[a-zA-Z0-9]{7}'
. ')';
// map of emoticon replacements
$map = array(
'/:\)/' => $this->img_tag('smiley-smile.gif', ':)' ),
'/:-\)/' => $this->img_tag('smiley-smile.gif', ':-)' ),
'/(?<!mailto):D/' => $this->img_tag('smiley-laughing.gif', ':D' ),
'/:-D/' => $this->img_tag('smiley-laughing.gif', ':-D' ),
'/:\(/' => $this->img_tag('smiley-frown.gif', ':(' ),
'/:-\(/' => $this->img_tag('smiley-frown.gif', ':-(' ),
'/'.$entity.';\)/' => $this->img_tag('smiley-wink.gif', ';)' ),
'/'.$entity.';-\)/' => $this->img_tag('smiley-wink.gif', ';-)' ),
'/8\)/' => $this->img_tag('smiley-cool.gif', '8)' ),
'/8-\)/' => $this->img_tag('smiley-cool.gif', '8-)' ),
'/(?<!mailto):O/i' => $this->img_tag('smiley-surprised.gif', ':O' ),
'/(?<!mailto):-O/i' => $this->img_tag('smiley-surprised.gif', ':-O' ),
'/(?<!mailto):P/i' => $this->img_tag('smiley-tongue-out.gif', ':P' ),
'/(?<!mailto):-P/i' => $this->img_tag('smiley-tongue-out.gif', ':-P' ),
'/(?<!mailto):@/i' => $this->img_tag('smiley-yell.gif', ':@' ),
'/(?<!mailto):-@/i' => $this->img_tag('smiley-yell.gif', ':-@' ),
'/O:\)/i' => $this->img_tag('smiley-innocent.gif', 'O:)' ),
'/O:-\)/i' => $this->img_tag('smiley-innocent.gif', 'O:-)' ),
'/(?<!mailto):$/' => $this->img_tag('smiley-embarassed.gif', ':$' ),
'/(?<!mailto):-$/' => $this->img_tag('smiley-embarassed.gif', ':-$' ),
'/(?<!mailto):\*/i' => $this->img_tag('smiley-kiss.gif', ':*' ),
'/(?<!mailto):-\*/i' => $this->img_tag('smiley-kiss.gif', ':-*' ),
'/(?<!mailto):S/i' => $this->img_tag('smiley-undecided.gif', ':S' ),
'/(?<!mailto):-S/i' => $this->img_tag('smiley-undecided.gif', ':-S' ),
);
if ($args['type'] == 'plain') {
$args['body'] = preg_replace(
array_keys($map), array_values($map), $args['body']);
}
return $args;
}
private function img_tag($ico, $title)
{
$path = './program/js/tiny_mce/plugins/emotions/img/';
return html::img(array('src' => $path.$ico, 'title' => $title));
}
}

@ -0,0 +1,35 @@
------------------------------------------------------------------
THIS IS NOT EVEN AN "ALPHA" STATE. USE ONLY FOR DEVELOPMENT!!!!!!!
------------------------------------------------------------------
WARNING: Don't use with gnupg-2.x!
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

@ -0,0 +1,14 @@
<?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;

@ -0,0 +1,206 @@
/* 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)},
lock = this.set_busy(true, 'searching');
// if (this.gui_objects.search_filter)
// addurl += '&_filter=' + this.gui_objects.search_filter.value;
this.env.current_page = 1;
this.enigma_loadframe();
this.enigma_clear_list();
this.http_post('plugin.enigma', params, lock);
}
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'},
lock = this.set_busy(true, 'loading');
this.env.current_page = page ? page : 1;
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, lock);
}
// 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)
{
var lock = this.set_busy(true, 'loading');
this.http_post('plugin.enigmaimport', '_uid='+this.env.uid+'&_mbox='
+urlencode(this.env.mailbox)+'&_part='+urlencode(mime_id), lock);
return false;
};

@ -0,0 +1,475 @@
<?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 displaying
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'));
}
// message composing
else if ($rcmail->action == 'compose') {
$this->load_ui();
$this->ui->init($section);
}
// message sending (and draft storing)
else if ($rcmail->action == 'sendmail') {
//$this->add_hook('outgoing_message_body', array($this, 'msg_encode'));
//$this->add_hook('outgoing_message_body', array($this, 'msg_sign'));
}
}
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);
}
}
}
/**
* 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 .= '&nbsp;' . 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'
);
}
}

@ -0,0 +1,2 @@
Order allow,deny
Deny from all

File diff suppressed because it is too large Load Diff

@ -0,0 +1,336 @@
<?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);
}
}
// }}}
}
?>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,473 @@
<?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;
}
// }}}
}
// }}}
?>

@ -0,0 +1,223 @@
<?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;
}
// }}}
}
// }}}
?>

@ -0,0 +1,428 @@
<?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;
}
// }}}
}
// }}}
?>

@ -0,0 +1,649 @@
<?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;
}
// }}}
}
// }}}
?>

@ -0,0 +1,373 @@
<?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;
}
// }}}
}
// }}}
?>

@ -0,0 +1,216 @@
<?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;
}
// }}}
}
?>

@ -0,0 +1,106 @@
<?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);
}

@ -0,0 +1,305 @@
<?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;
}
}

@ -0,0 +1,547 @@
<?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");
}
}

@ -0,0 +1,62 @@
<?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;
}
}

@ -0,0 +1,129 @@
<?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;
}
}

@ -0,0 +1,34 @@
<?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;
}

@ -0,0 +1,57 @@
<?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);
}
}

@ -0,0 +1,459 @@
<?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();
}
}
// Message composing UI
else if ($this->rc->action == 'compose') {
$this->compose_ui();
}
// 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;
}
private function compose_ui()
{
if (!is_array($_SESSION['compose']) || $_SESSION['compose']['id'] != get_input_value('_id', RCUBE_INPUT_GET))
return;
// Options menu button
// @TODO: make this work with non-default skins
$this->enigma->add_button(array(
'name' => 'enigmamenu',
'imagepas' => 'skins/default/enigma.png',
'imageact' => 'skins/default/enigma.png',
'onclick' => "rcmail_ui.show_popup('enigmamenu', true); return false",
'title' => 'securityoptions',
'domain' => 'enigma',
), 'toolbar');
// Options menu contents
$this->enigma->add_hook('render_page', array($this, 'compose_menu'));
}
function compose_menu($p)
{
$menu = new html_table(array('cols' => 2));
$chbox = new html_checkbox(array('value' => 1));
$menu->add(null, html::label(array('for' => 'enigmadefaultopt'),
Q($this->enigma->gettext('identdefault'))));
$menu->add(null, $chbox->show(1, array('name' => '_enigma_default', 'id' => 'enigmadefaultopt')));
$menu->add(null, html::label(array('for' => 'enigmasignopt'),
Q($this->enigma->gettext('signmsg'))));
$menu->add(null, $chbox->show(1, array('name' => '_enigma_sign', 'id' => 'enigmasignopt')));
$menu->add(null, html::label(array('for' => 'enigmacryptopt'),
Q($this->enigma->gettext('encryptmsg'))));
$menu->add(null, $chbox->show(1, array('name' => '_enigma_crypt', 'id' => 'enigmacryptopt')));
$menu = html::div(array('id' => 'enigmamenu', 'class' => 'popupmenu'),
$menu->show());
$p['content'] = preg_replace('/(<form name="form"[^>]+>)/i', '\\1'."\n$menu", $p['content']);
return $p;
}
}

@ -0,0 +1,31 @@
<?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;
}

@ -0,0 +1,53 @@
<?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';
$labels['securityoptions'] = 'Message security options...';
$labels['identdefault'] = 'Use settings of selected identity';
$labels['encryptmsg'] = 'Encrypt this message';
$labels['signmsg'] = 'Digitally sign this message';
$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.';
?>

@ -0,0 +1,55 @@
<?php
// EN-Revision: 4203
$labels = array();
$labels['enigmasettings'] = 'Enigma: 設定';
$labels['enigmacerts'] = 'Enigma: 証明書 (S/MIME)';
$labels['enigmakeys'] = 'Enigma: 鍵 (PGP)';
$labels['keysfromto'] = '鍵の一覧 $from $to (合計: $count )';
$labels['keyname'] = '名前';
$labels['keyid'] = '鍵 ID';
$labels['keyuserid'] = 'ユーザー ID';
$labels['keytype'] = '鍵の種類';
$labels['fingerprint'] = '指紋';
$labels['subkeys'] = 'Subkeys';
$labels['basicinfo'] = '基本情報';
$labels['userids'] = '追加のユーザー ID';
$labels['typepublickey'] = '公開鍵';
$labels['typekeypair'] = '鍵のペア';
$labels['keyattfound'] = 'このメールは PGP 鍵の添付があります。';
$labels['keyattimport'] = '鍵のインポート';
$labels['createkeys'] = '新しい鍵のペアを作成する';
$labels['importkeys'] = '鍵のインポート';
$labels['exportkeys'] = '鍵のエクスポート';
$labels['deletekeys'] = '鍵の削除';
$labels['keyactions'] = '鍵の操作...';
$labels['keydisable'] = '鍵を無効にする';
$labels['keyrevoke'] = '鍵を取り消す';
$labels['keysend'] = 'メッセージに公開鍵を含んで送信する';
$labels['keychpass'] = 'パスワードの変更';
$labels['securityoptions'] = 'メールのセキュリティ オプション...';
$labels['identdefault'] = '選択した識別子の設定を使う';
$labels['encryptmsg'] = 'このメールの暗号化';
$labels['signmsg'] = 'このメールのデジタル署名';
$messages = array();
$messages['sigvalid'] = '$sender からの署名を検証しました。';
$messages['siginvalid'] = '$sender からの署名が正しくありません。';
$messages['signokey'] = '署名は未検証です。公開鍵が見つかりません。鍵 ID: $keyid';
$messages['sigerror'] = '署名は未検証です。内部エラーです。';
$messages['decryptok'] = 'メールを復号しました。';
$messages['decrypterror'] = '復号に失敗しました。';
$messages['decryptnokey'] = '復号に失敗しました。秘密鍵が見つかりません。鍵 ID: $keyid.';
$messages['decryptbadpass'] = '復号に失敗しました。パスワードが正しくありません。';
$messages['nokeysfound'] = '鍵が見つかりません。';
$messages['keyopenerror'] = '鍵情報の取得に失敗しました! 内部エラーです。';
$messages['keylisterror'] = '鍵情報のリストに失敗しました! 内部エラーです。';
$messages['keysimportfailed'] = '鍵のインポートに失敗しました! 内部エラーです。';
$messages['keysimportsuccess'] = '鍵をインポートしました。インポート: $new, 未変更: $old';
$messages['keyconfirmdelete'] = '選択した鍵を本当に削除しますか?';
$messages['keyimporttext'] = '秘密鍵と公開鍵のインポート、または ASCII 形式の署名を無効にできます。';
?>

@ -0,0 +1,65 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/enigma/localization/ru_RU.inc |
| |
| Russian translation for roundcube/enigma plugin |
| Copyright (C) 2010 |
| Licensed under the GNU GPL |
| |
+-----------------------------------------------------------------------+
| Author: Sergey Dukachev <iam@dukess.ru> |
| Updates: |
+-----------------------------------------------------------------------+
@version 2010-12-23
*/
$labels = array();
$labels['enigmasettings'] = 'Enigma: Настройки';
$labels['enigmacerts'] = 'Enigma: Сертификаты (S/MIME)';
$labels['enigmakeys'] = 'Enigma: Ключи (PGP)';
$labels['keysfromto'] = 'Ключи от $from к $to в количестве $count';
$labels['keyname'] = 'Имя';
$labels['keyid'] = 'Идентификатор ключа';
$labels['keyuserid'] = 'Идентификатор пользователя';
$labels['keytype'] = 'Тип ключа';
$labels['fingerprint'] = 'Отпечаток (хэш) ключа';
$labels['subkeys'] = 'Подразделы';
$labels['basicinfo'] = 'Основные сведения';
$labels['userids'] = 'Дополнительные идентификаторы пользователя';
$labels['typepublickey'] = 'Открытый ключ';
$labels['typekeypair'] = 'пара ключей';
$labels['keyattfound'] = 'Это сообщение содержит один или несколько ключей PGP.';
$labels['keyattimport'] = 'Импортировать ключи';
$labels['createkeys'] = 'Создать новую пару ключей';
$labels['importkeys'] = 'Импортировать ключ(и)';
$labels['exportkeys'] = 'Экспортировать ключ(и)';
$labels['deletekeys'] = 'Удалить ключ(и)';
$labels['keyactions'] = 'Действия с ключами...';
$labels['keydisable'] = 'Отключить ключ';
$labels['keyrevoke'] = 'Отозвать ключ';
$labels['keysend'] = 'Отправить публичный ключ в собщении';
$labels['keychpass'] = 'Изменить пароль';
$messages = array();
$messages['sigvalid'] = 'Проверенная подпись у $sender.';
$messages['siginvalid'] = 'Неверная подпись у $sender.';
$messages['signokey'] = 'Непроверяемая подпись. Открытый ключ не найден. Идентификатор ключа: $keyid.';
$messages['sigerror'] = 'Непроверяемая подпись. Внутренняя ошибка.';
$messages['decryptok'] = 'Сообщение расшифровано.';
$messages['decrypterror'] = 'Расшифровка не удалась.';
$messages['decryptnokey'] = 'Расшифровка не удалась. Секретный ключ не найден. Идентификатор ключа: $keyid.';
$messages['decryptbadpass'] = 'Расшифровка не удалась. Неправильный пароль.';
$messages['nokeysfound'] = 'Ключи не найдены';
$messages['keyopenerror'] = 'Невозможно получить информацию о ключе! Внутренняя ошибка.';
$messages['keylisterror'] = 'Невозможно сделать список ключей! Внутренняя ошибка.';
$messages['keysimportfailed'] = 'Невозможно импортировать ключ(и)! Внутренняя ошибка.';
$messages['keysimportsuccess'] = 'Ключи успешно импортированы. Импортировано: $new, без изменений: $old.';
$messages['keyconfirmdelete'] = 'Вы точно хотите удалить выбранные ключи?';
$messages['keyimporttext'] = 'Вы можете импортировать открытые и секретные ключи или сообщения об отзыве ключей в формате ASCII-Armor.';
?>

@ -0,0 +1,182 @@
/*** 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,20 @@
<!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>

@ -0,0 +1,17 @@
<!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>

@ -0,0 +1,76 @@
<!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">&nbsp;</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>

@ -0,0 +1,50 @@
<?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';
private $abook_name = 'Static List';
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($this->abook_name);
$p['sources'][$this->abook_id] = array(
'id' => $this->abook_id,
'name' => $this->abook_name,
'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($this->abook_name);
}
return $p;
}
}

@ -0,0 +1,116 @@
<?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;
private $name;
public function __construct($name)
{
$this->ready = true;
$this->name = $name;
}
public function get_name()
{
return $this->name;
}
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, $nocount=false, $required=array())
{
// 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;
}
}

@ -0,0 +1,161 @@
<?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 = '?(?!login).*';
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;
$group = $args['group'];
$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'][$group][] = $tmpfname;
}
return $args;
}
/**
* Save an attachment from a non-upload source (draft or forward)
*/
function save($args)
{
$group = $args['group'];
$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'][$group][] = $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'])){
foreach ($_SESSION['plugins']['filesystem_attachments'] as $group => $files) {
if ($args['group'] && $args['group'] != $group)
continue;
foreach ((array)$files as $filename){
if(file_exists($filename)){
unlink($filename);
}
}
unset($_SESSION['plugins']['filesystem_attachments'][$group]);
}
}
return $args;
}
function file_id()
{
$userid = rcmail::get_instance()->user->ID;
list($usec, $sec) = explode(' ', microtime());
return preg_replace('/[^0-9]/', '', $userid . $sec . $usec);
}
}

@ -0,0 +1,5 @@
<?php
// Help content iframe source
// $rcmail_config['help_source'] = 'http://trac.roundcube.net/wiki';
$rcmail_config['help_source'] = '';

@ -0,0 +1,39 @@
<div id="helpabout">
<h3 align="center">Copyright &copy; 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>

@ -0,0 +1,387 @@
<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>

@ -0,0 +1,107 @@
<?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).*';
// we've got no ajax handlers
public $noajax = true;
// skip frames
public $noframe = true;
function init()
{
$rcmail = rcmail::get_instance();
$this->add_texts('localization/', false);
// register task
$this->register_task('help');
// register actions
$this->register_action('index', array($this, 'action'));
$this->register_action('about', array($this, 'action'));
$this->register_action('license', array($this, 'action'));
// add taskbar button
$this->add_button(array(
'name' => 'helptask',
'class' => 'button-help',
'label' => 'help.help',
'href' => './?_task=help',
'onclick' => sprintf("return %s.command('help')", JS_OBJECT_NAME)
), 'taskbar');
$rcmail->output->add_script(
JS_OBJECT_NAME . ".enable_command('help', true);\n" .
JS_OBJECT_NAME . ".help = function () { location.href = './?_task=help'; }",
'head');
$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 == 'about')
$rcmail->output->set_pagetitle($this->gettext('about'));
else if ($rcmail->action == 'license')
$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 == 'about') {
return @file_get_contents($this->home.'/content/about.html');
}
else if ($rcmail->action == 'license') {
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;
}
}

@ -0,0 +1,25 @@
<?php
/*
+-----------------------------------------------------------------------+
| language/cs_CZ/labels.inc |
| |
| Language file of the Roundcube help plugin |
| Copyright (C) 2005-2009, The Roundcube Dev Team |
| 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';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Hjælp';
$labels['about'] = 'Om';
$labels['license'] = 'Licens';
?>

@ -0,0 +1,8 @@
<?php
// translation done by Ulli Heist - http://heist.hobby-site.org/
$labels = array();
$labels['help'] = 'Hilfe';
$labels['about'] = '&Uuml;ber';
$labels['license'] = 'Lizenz';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Help';
$labels['about'] = 'About';
$labels['license'] = 'License';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Help';
$labels['about'] = 'About';
$labels['license'] = 'License';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Ayuda';
$labels['about'] = 'Acerca de';
$labels['license'] = 'Licencia';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Abi';
$labels['about'] = 'Roundcube info';
$labels['license'] = 'Litsents';
?>

@ -0,0 +1,8 @@
<?php
$labels = array();
$labels['help'] = 'Axuda';
$labels['about'] = 'Acerca de';
$labels['license'] = 'Licencia';
?>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save