Copying plugins into 0.6 release branch
@ -0,0 +1,338 @@
|
|||||||
|
/**
|
||||||
|
* ACL plugin script
|
||||||
|
*
|
||||||
|
* @version 0.5
|
||||||
|
* @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:'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('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('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('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,695 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folders Access Control Lists Management (RFC4314, RFC2086)
|
||||||
|
*
|
||||||
|
* @version 0.5
|
||||||
|
* @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';
|
||||||
|
|
||||||
|
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'));
|
||||||
|
// 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' => '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'] = '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: 100px;
|
||||||
|
width: 480px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aclform div
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
clear: both;
|
||||||
|
}
|
After Width: | Height: | Size: 674 B |
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>
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 977 B |
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 "")" 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 .= ' ' . 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
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
@ -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,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;
|
||||||
|
}
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
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"> </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 © 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'] = 'Ü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';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = 'Segítség';
|
||||||
|
$labels['about'] = 'Névjegy';
|
||||||
|
$labels['license'] = 'Licenc';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// EN-Revision: 3891
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = 'ヘルプ';
|
||||||
|
$labels['about'] = '紹介';
|
||||||
|
$labels['license'] = 'ライセンス';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = 'Pomoc';
|
||||||
|
$labels['about'] = 'O programie';
|
||||||
|
$labels['license'] = 'Licencja';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = 'Ajuda';
|
||||||
|
$labels['about'] = 'Sobre';
|
||||||
|
$labels['license'] = 'Licença';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = 'Hjälp';
|
||||||
|
$labels['about'] = 'Om';
|
||||||
|
$labels['license'] = 'Licens';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$labels['help'] = '說明';
|
||||||
|
$labels['about'] = '關於';
|
||||||
|
$labels['license'] = '許可證';
|
||||||
|
|
||||||
|
?>
|
@ -0,0 +1,29 @@
|
|||||||
|
/***** Roundcube|Mail Help task styles *****/
|
||||||
|
|
||||||
|
#taskbar a.button-help
|
||||||
|
{
|
||||||
|
background-image: url('help.gif');
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-box
|
||||||
|
{
|
||||||
|
overflow: auto;
|
||||||
|
background-color: #F2F2F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helplicense, #helpabout
|
||||||
|
{
|
||||||
|
width: 46em;
|
||||||
|
padding: 1em 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helplicense a, #helpabout a
|
||||||
|
{
|
||||||
|
color: #900;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpabout
|
||||||
|
{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
After Width: | Height: | Size: 898 B |
@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title><roundcube:object name="pagetitle" /></title>
|
||||||
|
<roundcube:include file="/includes/links.html" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/this/help.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/settings.css" />
|
||||||
|
<script type="text/javascript">
|
||||||
|
function help_init_settings_tabs()
|
||||||
|
{
|
||||||
|
var action, tab = '#helptabdefault';
|
||||||
|
if (window.rcmail && (action = rcmail.env.action)) {
|
||||||
|
tab = '#helptab' + (action ? action : 'default');
|
||||||
|
}
|
||||||
|
$(tab).addClass('tablink-selected');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<roundcube:include file="/includes/taskbar.html" />
|
||||||
|
<roundcube:include file="/includes/header.html" />
|
||||||
|
|
||||||
|
<div id="tabsbar">
|
||||||
|
<span id="helptabdefault" class="tablink"><roundcube:button name="helpdefault" href="?_task=help" type="link" label="help.help" title="help.help" /></span>
|
||||||
|
<span id="helptababout" class="tablink"><roundcube:button name="helpabout" href="?_task=help&_action=about" type="link" label="help.about" title="help.about" class="tablink" /></span>
|
||||||
|
<span id="helptablicense" class="tablink"><roundcube:button name="helplicense" href="?_task=help&_action=license" type="link" label="help.license" title="help.license" class="tablink" /></span>
|
||||||
|
<roundcube:container name="helptabs" id="helptabsbar" />
|
||||||
|
<script type="text/javascript"> if (window.rcmail) rcmail.add_onload(help_init_settings_tabs);</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mainscreen" class="box help-box">
|
||||||
|
<roundcube:object name="helpcontent" id="helpcontentframe" width="100%" height="100%" frameborder="0" src="/watermark.html" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Basic Authentication
|
||||||
|
*
|
||||||
|
* Make use of an existing HTTP authentication and perform login with the existing user credentials
|
||||||
|
*
|
||||||
|
* @version 1.2
|
||||||
|
* @author Thomas Bruederli
|
||||||
|
*/
|
||||||
|
class http_authentication extends rcube_plugin
|
||||||
|
{
|
||||||
|
public $task = 'login';
|
||||||
|
|
||||||
|
function init()
|
||||||
|
{
|
||||||
|
$this->add_hook('startup', array($this, 'startup'));
|
||||||
|
$this->add_hook('authenticate', array($this, 'authenticate'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup($args)
|
||||||
|
{
|
||||||
|
// change action to login
|
||||||
|
if (empty($args['action']) && empty($_SESSION['user_id'])
|
||||||
|
&& !empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW']))
|
||||||
|
$args['action'] = 'login';
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function authenticate($args)
|
||||||
|
{
|
||||||
|
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
|
||||||
|
$args['user'] = $_SERVER['PHP_AUTH_USER'];
|
||||||
|
$args['pass'] = $_SERVER['PHP_AUTH_PW'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$args['cookiecheck'] = false;
|
||||||
|
$args['valid'] = true;
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|