2006-03-27 02:57:24 +02:00
# ===========================================================================
# Gpg.pm
# $Id$
#
# Object methods for gpg-ezmlm mailing lists
#
# Copyright (C) 2006, Lars Kruse, All Rights Reserved.
# Please send bug reports and comments to devel@sumpfralle.de
#
2006-04-18 22:54:28 +02:00
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2006-03-27 02:57:24 +02:00
#
2006-04-18 22:54:28 +02:00
# 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.
2006-03-27 02:57:24 +02:00
#
2006-04-18 22:54:28 +02:00
# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
2006-03-27 02:57:24 +02:00
#
# ==========================================================================
2006-04-19 01:52:16 +02:00
2006-03-27 02:57:24 +02:00
package Mail::Ezmlm::Gpg ;
use strict ;
2006-03-29 23:06:56 +02:00
use vars qw( $GPG_EZMLM_BASE $GPG_BIN $VERSION @ISA @EXPORT @EXPORT_OK ) ;
use vars qw( @GPG_LIST_OPTIONS ) ;
2006-03-27 02:57:24 +02:00
use Carp ;
2006-03-29 23:06:56 +02:00
use Crypt::GPG ;
2006-03-27 02:57:24 +02:00
require Exporter ;
@ ISA = qw( Exporter ) ;
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@ EXPORT = qw(
) ;
2006-04-19 01:52:16 +02:00
$ VERSION = '0.1' ;
2006-03-27 02:57:24 +02:00
require 5.005 ;
2006-04-19 01:52:16 +02:00
= head1 NAME
Mail::Ezmlm:: Gpg - Object Methods for encrypted Ezmlm Mailing Lists
= head1 SYNOPSIS
use Mail::Ezmlm::Gpg ;
$ list = new Mail::Ezmlm:: Gpg ( DIRNAME ) ;
The rest is a bit complicated for a Synopsis , see the description .
= head1 DESCRIPTION
Mail::Ezmlm:: Gpg is a Perl module that is designed to provide an object
interface to encrypted mailing lists based upon gpg - ezmlm .
See the ezmlm web page ( http: // www . synacklabs . net /projects/c rypt - ml / ) for
a this software .
= cut
2006-03-27 02:57:24 +02:00
# == Begin site dependant variables ==
2006-04-19 01:52:16 +02:00
$ GPG_EZMLM_BASE = '/usr/bin' ; # Autoinserted by Makefile.PL
$ GPG_BIN = '/usr/bin/gpg' ; # Autoinserted by Makefile.PL
2006-03-27 02:57:24 +02:00
# == check the ezmlm-make path ==
2006-04-18 22:54:28 +02:00
$ GPG_EZMLM_BASE = '/usr/local/bin/ezmlm'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
$ GPG_EZMLM_BASE = '/usr/local/bin/ezmlm-idx'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
$ GPG_EZMLM_BASE = '/usr/local/bin'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
$ GPG_EZMLM_BASE = '/usr/bin/ezmlm'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
$ GPG_EZMLM_BASE = '/usr/bin/ezmlm-idx'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
$ GPG_EZMLM_BASE = '/usr/bin'
unless ( - e "$GPG_EZMLM_BASE/gpg-ezmlm-manage.pl" ) ;
2006-03-27 02:57:24 +02:00
2006-03-29 23:06:56 +02:00
# == check the gpg path ==
2006-04-19 01:52:16 +02:00
$ GPG_BIN = '/usr/local/bin/gpg'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/usr/bin/gpg'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/bin/gpg'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/usr/local/bin/gpg2'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/usr/bin/gpg2'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/bin/gpg2'
unless ( - e "$GPG_BIN" ) ;
2006-04-18 22:54:28 +02:00
$ GPG_BIN = '/usr/local/bin/gpg'
unless ( - e "$GPG_BIN" ) ;
$ GPG_BIN = '/bin/gpg'
unless ( - e "$GPG_BIN" ) ;
2006-03-29 23:06:56 +02:00
2006-03-27 02:57:24 +02:00
# == clean up the path for taint checking ==
local $ ENV { 'PATH' } = $ GPG_EZMLM_BASE ;
2006-03-29 23:06:56 +02:00
# == define the available (supported) GPG_LIST_OPTIONS ==
@ GPG_LIST_OPTIONS = (
"RequireSub" ,
2006-04-10 15:29:08 +02:00
"requireSigs" ,
2006-03-29 23:06:56 +02:00
"NokeyNocrypt" ,
"signMessages" ,
"encryptToAll" ,
"VerifiedKeyReq" ,
"allowKeySubmission" ) ;
2006-04-19 01:52:16 +02:00
2006-03-27 02:57:24 +02:00
# == Initialiser - Returns a reference to the object ==
2006-04-19 01:52:16 +02:00
= head2 Setting up a new Ezmlm:: Gpg object:
use Mail::Ezmlm::Gpg ;
$ list = new Mail::Ezmlm:: Gpg ( '/home/user/lists/moolist' ) ;
new ( ) returns the value of thislist ( ) for success , undefined if there was a
problem .
= cut
2006-03-27 02:57:24 +02:00
sub new {
my ( $ class , $ list ) = @ _ ;
my $ self = { } ;
bless $ self , ref $ class || $ class || 'Mail::Ezmlm::Gpg' ;
2006-04-14 17:37:27 +02:00
$ list =~ m/^([\w\._\/-]*)$/ ;
$ list = $ 1 ;
2006-03-27 02:57:24 +02:00
$ self - > setlist ( $ list ) if ( defined ( $ list ) && $ list ) ;
return $ self ;
}
2006-04-11 12:33:35 +02:00
# == convert an existing list to gpg-ezmlm ==
2006-04-19 01:52:16 +02:00
= head2 Converting a plaintext mailing list to an encrypted list:
You have to create a normal list before you can convert it .
Use Mail:: Ezmlm to do this .
$ list - > convert_to_encrypted ( ) ;
= cut
2006-04-14 17:37:27 +02:00
sub convert_to_encrypted {
2006-04-11 12:33:35 +02:00
my ( $ self ) = @ _ ;
my $ list_dir = $ self - > { 'LIST_NAME' } ;
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'must define directory in convert_to_encrypted()' ) && return 0 )
unless ( defined ( $ list_dir ) ) ;
( $ self - > _seterror ( - 1 , 'directory does not exist: ' . $ list_dir ) && return 0 )
unless ( - d $ list_dir ) ;
2006-04-11 12:33:35 +02:00
my $ tlist = new Mail::Ezmlm:: Gpg ( $ list_dir ) ;
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'list is already encrypted: ' . $ list_dir ) && return 0 )
if ( $ tlist - > is_gpg ( ) ) ;
2006-04-11 12:33:35 +02:00
# retrieve location of dotqmail-files
my $ dot_loc ;
if ( - r "$list_dir/dot" ) {
open DOT , "<$list_dir/dot" ;
$ dot_loc = <DOT> ;
close DOC ;
2006-04-14 17:37:27 +02:00
} elsif ( - r "$list_dir/config" ) {
2006-04-11 12:33:35 +02:00
open CONFIG , "<$list_dir/config" ;
my @ lines = <CONFIG> ;
my $ one_line ;
foreach $ one_line ( @ lines ) {
$ dot_loc = $ 1 if ( $ one_line =~ m/^T:(.*)$/ ) ;
}
close CONFIG ;
2006-04-14 17:37:27 +02:00
} else {
$ self - > _seterror ( - 1 , 'list configuration file not found: ' . $ list_dir ) ;
return 0 ;
2006-04-11 12:33:35 +02:00
}
2006-04-14 17:37:27 +02:00
2006-04-11 12:33:35 +02:00
chomp ( $ dot_loc ) ;
2006-04-14 17:37:27 +02:00
$ dot_loc =~ m/^([\w\._\/-]*)$/ ;
$ dot_loc = $ 1 ;
2006-04-11 12:33:35 +02:00
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'dotqmail files not found: ' . $ dot_loc ) && return 0 )
unless ( ( $ dot_loc ne '' ) && ( - e $ dot_loc ) ) ;
2006-03-27 02:57:24 +02:00
2006-04-14 17:37:27 +02:00
system ( "$GPG_EZMLM_BASE/gpg-ezmlm-convert.pl" , "--quiet" , "--skip-keygen" , $ list_dir , $ dot_loc ) == 0
2006-04-18 22:54:28 +02:00
|| ( $ self - > _seterror ( $? ) && return undef ) ;
2006-04-14 17:37:27 +02:00
$ self - > _seterror ( undef ) ;
return $ self - > setlist ( $ list_dir ) ;
}
2006-03-27 02:57:24 +02:00
2006-04-14 17:37:27 +02:00
# == convert an encrypted list back to plaintext ==
2006-04-19 01:52:16 +02:00
= head2 Converting an encryted mailing list to a plaintext list:
$ list - > convert_to_plaintext ( ) ;
= cut
2006-04-14 17:37:27 +02:00
sub convert_to_plaintext {
my ( $ self ) = @ _ ;
my $ list_dir = $ self - > { 'LIST_NAME' } ;
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'must define directory in convert_to_plaintext()' ) && return 0 )
unless ( defined ( $ list_dir ) ) ;
( $ self - > _seterror ( - 1 , 'directory does not exist: ' . $ list_dir ) && return 0 )
unless ( - d $ list_dir ) ;
2006-04-14 17:37:27 +02:00
my $ tlist = new Mail::Ezmlm:: Gpg ( $ list_dir ) ;
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'list is not encrypted: ' . $ list_dir ) && return 0 )
unless ( $ tlist - > is_gpg ( ) ) ;
2006-04-14 17:37:27 +02:00
# retrieve location of dotqmail-files
my $ dot_loc ;
if ( - r "$list_dir/dot" ) {
open DOT , "<$list_dir/dot" ;
$ dot_loc = <DOT> ;
close DOC ;
} elsif ( - r "$list_dir/config.no-gpg" ) {
open CONFIG , "<$list_dir/config.no-gpg" ;
my @ lines = <CONFIG> ;
my $ one_line ;
foreach $ one_line ( @ lines ) {
$ dot_loc = $ 1 if ( $ one_line =~ m/^T:(.*)$/ ) ;
}
2006-04-11 12:33:35 +02:00
close CONFIG ;
2006-04-14 17:37:27 +02:00
} else {
$ self - > _seterror ( - 1 , 'list configuration file not found: ' . $ list_dir ) ;
return 0 ;
2006-04-11 12:33:35 +02:00
}
2006-04-14 17:37:27 +02:00
chomp ( $ dot_loc ) ;
$ dot_loc =~ m/^([\w\._\/-]*)$/ ;
$ dot_loc = $ 1 ;
2006-04-18 22:54:28 +02:00
( $ self - > _seterror ( - 1 , 'dotqmail files not found: ' . $ dot_loc ) && return 0 )
unless ( ( $ dot_loc ne '' ) && ( - e $ dot_loc ) ) ;
2006-04-11 12:33:35 +02:00
2006-04-14 17:37:27 +02:00
system ( "$GPG_EZMLM_BASE/gpg-ezmlm-convert.pl" , "--quiet" , "--revert" , $ list_dir , $ dot_loc ) == 0
2006-04-18 22:54:28 +02:00
|| ( $ self - > _seterror ( $? ) && return undef ) ;
2006-03-27 02:57:24 +02:00
$ self - > _seterror ( undef ) ;
2006-04-11 12:33:35 +02:00
return $ self - > setlist ( $ list_dir ) ;
2006-03-27 02:57:24 +02:00
}
# == Update the current list ==
2006-04-19 01:52:16 +02:00
= head2 Updating the configuration of the current list:
$ list - > update ( { 'allowKeySubmission' = > 1 } ) ;
= cut
2006-03-27 02:57:24 +02:00
sub update {
2006-04-10 16:26:25 +02:00
my ( $ self , % switches ) = @ _ ;
my % ok_switches ;
2006-03-27 02:57:24 +02:00
# check for important files: 'config'
( $ self - > _seterror ( - 1 , "$self->{'LIST_NAME'} does not appear to be a valid list in update()" ) && return 0 ) unless ( ( - e "$self->{'LIST_NAME'}/config" ) || ( - e "$self->{'LIST_NAME'}/flags" ) ) ;
2006-03-29 23:06:56 +02:00
# check if all supplied settings are supported
# btw we change the case (upper/lower) of the setting to the default one
my $ one_key ;
foreach $ one_key ( keys % switches ) {
my $ ok_key ;
foreach $ ok_key ( @ GPG_LIST_OPTIONS ) {
if ( $ ok_key =~ /^$one_key$/i ) {
$ ok_switches { $ ok_key } = $ switches { $ one_key } ;
delete $ switches { $ one_key } ;
}
}
}
# %switches should be empty now
if ( % switches ) {
foreach $ one_key ( keys % switches ) {
warn "unsupported setting: $one_key" ;
}
}
2006-04-11 12:33:35 +02:00
my $ errorstring ;
2006-03-29 23:06:56 +02:00
my $ config_file_old = "$self->{'LIST_NAME'}/config" ;
my $ config_file_new = "$self->{'LIST_NAME'}/config.new" ;
if ( open ( CONFIG_OLD , "<$config_file_old" ) ) {
if ( open ( CONFIG_NEW , ">$config_file_new" ) ) {
my ( $ in_line , $ one_opt , $ one_val , $ new_setting ) ;
while ( <CONFIG_OLD> ) {
$ in_line = $ _ ;
if ( % ok_switches ) {
2006-04-10 16:26:25 +02:00
my $ found = 0 ;
2006-03-29 23:06:56 +02:00
while ( ( $ one_opt , $ one_val ) = each ( % ok_switches ) ) {
# is this the right line (maybe commented out)?
if ( $ in_line =~ m/^#?\w*$one_opt/i ) {
print CONFIG_NEW "$one_opt " ;
print CONFIG_NEW ( $ one_val ) ? "yes" : "no" ;
print CONFIG_NEW "\n" ;
delete $ ok_switches { $ one_opt } ;
2006-04-10 16:26:25 +02:00
$ found = 1 ;
2006-03-29 23:06:56 +02:00
}
}
2006-04-10 16:26:25 +02:00
print CONFIG_NEW $ in_line if ( $ found == 0 ) ;
2006-03-29 23:06:56 +02:00
} else {
# just print the remaining config file if no other settings are left
print CONFIG_NEW $ in_line ;
}
}
# write the remaining settings to the end of the file
while ( ( $ one_opt , $ one_val ) = each ( % ok_switches ) ) {
print CONFIG_NEW "\n$one_opt " ;
print CONFIG_NEW ( $ one_val ) ? "yes" : "no" ;
print CONFIG_NEW "\n" ;
}
} else {
2006-04-11 12:33:35 +02:00
$ errorstring = "failed to write to temporary config file: $config_file_new" ;
2006-03-29 23:06:56 +02:00
$ self - > _seterror ( - 1 , $ errorstring ) ;
warn $ errorstring ;
close CONFIG_OLD ;
return ( 1 == 0 ) ;
}
close CONFIG_NEW ;
} else {
2006-04-11 12:33:35 +02:00
$ errorstring = "failed to read the config file: $config_file_old" ;
2006-03-29 23:06:56 +02:00
$ self - > _seterror ( - 1 , $ errorstring ) ;
warn $ errorstring ;
return ( 1 == 0 ) ;
}
close CONFIG_OLD ;
unless ( rename ( $ config_file_new , $ config_file_old ) ) {
2006-04-11 12:33:35 +02:00
$ errorstring = "failed to move new config file ($config_file_new) "
2006-03-29 23:06:56 +02:00
. "to original config file ($config_file_old)" ;
$ self - > _seterror ( - 1 , $ errorstring ) ;
warn $ errorstring ;
return ( 1 == 0 ) ;
}
2006-03-27 02:57:24 +02:00
$ self - > _seterror ( undef ) ;
2006-03-29 23:06:56 +02:00
return ( 0 == 0 ) ;
2006-03-27 02:57:24 +02:00
}
2006-03-29 23:06:56 +02:00
2006-03-27 02:57:24 +02:00
# == Get a list of options for the current list ==
2006-04-19 01:52:16 +02:00
= head2 Getting the current configuration of the current list:
$ list - > getconfig ;
getconfig ( ) returns a hash including all available settings
( undefined settings are returned with their default value ) .
= cut
2006-03-27 02:57:24 +02:00
sub getconfig {
my ( $ self ) = @ _ ;
my ( % options ) ;
2006-04-10 15:29:08 +02:00
# define defaults
$ options { signMessages } = 1 ;
$ options { NokeyNocrypt } = 0 ;
$ options { allowKeySubmission } = 1 ;
$ options { encryptToAll } = 0 ;
$ options { VerifiedKeyReq } = 0 ;
$ options { RequireSub } = 0 ;
$ options { requireSigs } = 0 ;
2006-03-27 02:57:24 +02:00
# Read the config file
if ( open ( CONFIG , "<$self->{'LIST_NAME'}/config" ) ) {
# 'config' contains the authorative information
while ( <CONFIG> ) {
2006-03-29 23:06:56 +02:00
if ( /^(\w+)\s(.*)$/ ) {
my $ optname = $ 1 ;
my $ optvalue = $ 2 ;
my $ one_opt ;
foreach $ one_opt ( @ GPG_LIST_OPTIONS ) {
if ( $ one_opt =~ m/^$optname$/i ) {
if ( $ optvalue =~ /^yes$/i ) {
$ options { $ one_opt } = 1 ;
} else {
$ options { $ one_opt } = 0 ;
}
}
}
}
2006-03-27 02:57:24 +02:00
}
close CONFIG ;
} else {
$ self - > _seterror ( - 1 , 'unable to read configuration file in getconfig()' && return undef ) ;
}
$ self - > _seterror ( undef ) ;
return % options ;
}
2006-03-29 23:06:56 +02:00
# == Return the directory of the current list ==
2006-04-19 01:52:16 +02:00
= head2 Determining which list we are currently altering:
$ whichlist = $ list - > thislist ;
print $ list - > thislist ;
= cut
2006-03-27 02:57:24 +02:00
sub thislist {
my ( $ self ) = shift ;
$ self - > _seterror ( undef ) ;
return $ self - > { 'LIST_NAME' } ;
}
2006-03-29 23:06:56 +02:00
2006-03-27 02:57:24 +02:00
# == Set the current mailing list ==
2006-04-19 01:52:16 +02:00
= head2 Changing which list the Mail::Ezmlm:: Gpg object points at:
$ list - > setlist ( '/home/user/lists/moolist' ) ;
= cut
2006-03-27 02:57:24 +02:00
sub setlist {
my ( $ self , $ list ) = @ _ ;
if ( $ list =~ m/^([\w\d\_\-\.\/]+)$/ ) {
$ list = $ 1 ;
if ( - e "$list/lock" ) {
2006-03-29 23:06:56 +02:00
$ self - > _seterror ( undef ) ;
2006-03-27 02:57:24 +02:00
return $ self - > { 'LIST_NAME' } = $ list ;
} else {
$ self - > _seterror ( - 1 , "$list does not appear to be a valid list in setlist()" ) ;
return undef ;
}
} else {
$ self - > _seterror ( - 1 , "$list contains tainted data in setlist()" ) ;
return undef ;
}
}
# == is the list encrypted? ==
2006-04-19 01:52:16 +02:00
= head2 Checking the state of a list:
To determine , if a list is encrypted or not , call is_gpg ( ) .
$ list - > is_gpg ( ) ;
= cut
2006-03-27 02:57:24 +02:00
sub is_gpg {
my ( $ self ) = @ _ ;
( $ self - > _seterror ( - 1 , 'must setlist() before is_gpg()' ) && return 0 ) unless ( defined ( $ self - > { 'LIST_NAME' } ) ) ;
$ self - > _seterror ( undef ) ;
return ( 0 == 1 ) unless ( - e "$self->{'LIST_NAME'}/config" ) ;
2006-03-29 23:06:56 +02:00
my $ content = $ self - > getpart ( "config" ) ;
# return false if we encounter the usual ezmlm-idx-v0.4-header
2006-03-27 02:57:24 +02:00
return ( 0 == 1 ) if ( $ content =~ /^F:/m ) ;
return ( 0 == 0 ) ;
}
# == retrieve file contents ==
2006-04-19 01:52:16 +02:00
= head2 Getting the content of file in a mailing list directory:
@ part = $ list - > getpart ( 'headeradd' ) ;
$ part = $ list - > getpart ( 'headeradd' ) ;
getpart ( ) can be used to retrieve the contents of various text files such as
headeradd , headerremove , mimeremove , etc .
= cut
2006-03-27 02:57:24 +02:00
sub getpart {
my ( $ self , $ part ) = @ _ ;
my ( @ contents , $ content ) ;
my $ filename = $ self - > { 'LIST_NAME' } . "/$part" ;
if ( open ( PART , "<$filename" ) ) {
while ( <PART> ) {
unless ( /^#/ ) {
chomp ( $ contents [ $# contents + + ] = $ _ ) ;
$ content . = $ _ ;
}
}
close PART ;
if ( wantarray ) {
return @ contents ;
} else {
return $ content ;
}
} ( $ self - > _seterror ( $? ) && return undef ) ;
}
2006-03-29 23:06:56 +02:00
2006-04-11 04:31:37 +02:00
# == export a key ==
2006-04-19 01:52:16 +02:00
= head2 Export a key:
You may export public keys of the keyring of a list .
The key can be identified by its id or other ( unique ) patterns ( like the
gnupg program ) .
$ list - > export_key ( $ key_id ) ;
$ list - > export_key ( $ email_address ) ;
The return value is a string containing the ascii armored key data .
= cut
2006-04-11 04:31:37 +02:00
sub export_key {
my ( $ self , $ keyid ) = @ _ ;
my $ gpg = $ self - > _get_gpg_object ( ) ;
my $ gpgoption = "--armor --export $keyid" ;
my $ gpgcommand = $ gpg - > gpgbin ( ) . " " . $ gpg - > gpgopts ( ) . " $gpgoption" ;
my $ output = `$gpgcommand 2>/dev/null` ;
if ( $ output ) {
return $ output ;
} else {
return undef ;
}
}
# == import a new key ==
2006-04-19 01:52:16 +02:00
= head2 Import a key:
You can import public or secret keys into the keyring of the list .
The key should be ascii armored .
$ list - > import_key ( $ ascii_armored_key_date ) ;
= cut
2006-04-11 04:31:37 +02:00
sub import_key {
2006-03-29 23:06:56 +02:00
my ( $ self , $ key ) = @ _ ;
my $ gpg = $ self - > _get_gpg_object ( ) ;
2006-04-11 04:31:37 +02:00
if ( $ gpg - > addkey ( $ key ) ) {
2006-03-29 23:06:56 +02:00
return ( 0 == 0 ) ;
} else {
return ( 1 == 0 ) ;
}
2006-03-27 02:57:24 +02:00
}
2006-04-11 04:31:37 +02:00
# == delete a key ==
2006-04-19 01:52:16 +02:00
= head2 Delete a key:
Remove a public key ( and the matching secret key if it exists ) from the keyring
of the list .
The argument is the id of the key or any other unique pattern .
$ list - > delete_key ( $ keyid ) ;
= cut
2006-04-11 04:31:37 +02:00
sub delete_key {
2006-03-29 23:06:56 +02:00
my ( $ self , $ keyid ) = @ _ ;
my $ gpg = $ self - > _get_gpg_object ( ) ;
2006-04-11 04:31:37 +02:00
my $ fprint = $ self - > _get_fingerprint ( $ keyid ) ;
return ( 1 == 0 ) unless ( defined ( $ fprint ) ) ;
my $ gpgoption = "--delete-secret-and-public-key $fprint" ;
my $ gpgcommand = $ gpg - > gpgbin ( ) . " " . $ gpg - > gpgopts ( ) . " $gpgoption" ;
if ( system ( $ gpgcommand ) ) {
2006-03-29 23:06:56 +02:00
return ( 1 == 0 ) ;
} else {
return ( 0 == 0 ) ;
}
2006-03-27 02:57:24 +02:00
}
2006-03-29 23:06:56 +02:00
# == generate new private key ==
2006-04-19 01:52:16 +02:00
= head2 Generate a new key:
$ list - > generate_key ( $ name , $ comment , $ email_address , $ keysize , $ expire ) ;
Refer to the documentation of gnupg for the format of the arguments .
= cut
2006-03-29 23:06:56 +02:00
sub generate_private_key {
2006-04-11 04:31:37 +02:00
my ( $ self , $ name , $ comment , $ email , $ keysize , $ expire ) = @ _ ;
2006-03-29 23:06:56 +02:00
my $ gpg = $ self - > _get_gpg_object ( ) ;
2006-04-11 04:31:37 +02:00
my $ gpgoption = "--gen-key" ;
my $ gpgcommand = $ gpg - > gpgbin ( ) . " " . $ gpg - > gpgopts ( ) . " $gpgoption" ;
my $ pid = open ( INPUT , "| $gpgcommand" ) ;
print INPUT "Key-Type: DSA\n" ;
print INPUT "Key-Length: 1024\n" ;
print INPUT "Subkey-Type: ELG-E\n" ;
print INPUT "Subkey-Length: $keysize\n" ;
print INPUT "Name-Real: $name\n" ;
print INPUT "Name-Comment: $comment\n" if ( $ comment ) ;
print INPUT "Name-Email: $email\n" ;
print INPUT "Expire-Date: $expire\n" ;
2006-04-18 22:54:28 +02:00
return close INPUT ;
2006-03-27 02:57:24 +02:00
}
2006-03-29 23:06:56 +02:00
# == get_public_keys ==
2006-04-19 01:52:16 +02:00
= head2 Getting public keys :
Return an array of key hashes each containing the following elements:
= over
= item *
name
= item *
email
= item *
id
= item *
expires
= back
$ list - > get_public_keys ( ) ;
$ list - > get_secret_keys ( ) ;
= cut
2006-03-29 23:06:56 +02:00
sub get_public_keys {
my ( $ self ) = @ _ ;
my @ keys = $ self - > _get_keys ( "pub" ) ;
2006-03-30 02:45:48 +02:00
return @ keys ;
2006-03-27 02:57:24 +02:00
}
2006-03-29 23:06:56 +02:00
# == get_private_keys ==
2006-04-19 02:45:18 +02:00
# see above for POD (get_public_keys)
2006-03-29 23:06:56 +02:00
sub get_secret_keys {
my ( $ self ) = @ _ ;
my @ keys = $ self - > _get_keys ( "sec" ) ;
2006-03-30 02:45:48 +02:00
return @ keys ;
2006-03-27 02:57:24 +02:00
}
2006-04-19 01:52:16 +02:00
2006-04-19 02:45:18 +02:00
# == check version of gpg-ezmlm ==
sub check_gpg_ezmlm_version {
my $ ret_value = system ( "'$GPG_EZMLM_BASE/gpg-ezmlm-convert.pl' --version &>/dev/null" ) ;
# for now we do not need a specific version of gpg-ezmlm - it just has to
# know the "--version" argument (available since gpg-ezmlm 0.3.4)
return ( $ ret_value == 0 ) ;
}
############ some internal functions ##############
2006-03-29 23:06:56 +02:00
# == internal function for creating a gpg object ==
sub _get_gpg_object () {
my ( $ self ) = @ _ ;
my $ gpg = new Crypt:: GPG ( ) ;
2006-04-11 04:31:37 +02:00
my $ dirname = $ self - > { 'LIST_NAME' } . '/.gnupg' ;
# fix spaces in filename
$ dirname =~ s/ /\\ /g ;
2006-03-29 23:06:56 +02:00
$ gpg - > gpgbin ( $ GPG_BIN ) ;
2006-04-11 04:31:37 +02:00
$ gpg - > gpgopts ( "--lock-multiple --no-tty --no-secmem-warning --batch --quiet --homedir $dirname" ) ;
2006-03-29 23:06:56 +02:00
return $ gpg ;
}
2006-03-27 02:57:24 +02:00
2006-03-29 23:06:56 +02:00
# == internal function to list keys ==
sub _get_keys () {
# type can be "pub" or "sec"
my ( $ self , $ keyType ) = @ _ ;
my $ gpg = $ self - > _get_gpg_object ( ) ;
my ( $ flag , $ gpgoption , @ keys , $ key ) ;
if ( $ keyType eq "pub" ) {
$ flag = "pub" ;
$ gpgoption = "--list-keys" ;
} elsif ( $ keyType eq "sec" ) {
$ flag = "sec" ;
$ gpgoption = "--list-secret-keys" ;
} else {
warn "wrong keyType: $keyType" ;
return undef ;
}
my $ gpgcommand = $ gpg - > gpgbin ( ) . " " . $ gpg - > gpgopts ( ) . " --with-colons $gpgoption" ;
my @ read_keys = grep /^$flag/ , `$gpgcommand` ;
foreach $ key ( @ read_keys ) {
my ( $ type , $ trust , $ size , $ algorithm , $ id , $ created ,
$ expires , $ u2 , $ ownertrust , $ uid ) = split ":" , $ key ;
# stupid way of "decoding" utf8 (at least it works for ":")
$ uid =~ s/\\x3a/:/g ;
2006-04-03 12:59:34 +02:00
$ uid =~ /^(.*) <([^<]*)>/ ;
my $ name = $ 1 ;
my $ email = $ 2 ;
push @ keys , { name = > $ name , email = > $ email , id = > $ id , expires = > $ expires } ;
2006-03-29 23:06:56 +02:00
}
return @ keys ;
2006-03-27 02:57:24 +02:00
}
2006-04-11 04:31:37 +02:00
# == internal function to retrieve the fingerprint of a key ==
sub _get_fingerprint ()
{
my ( $ self , $ key_id ) = @ _ ;
my $ gpg = $ self - > _get_gpg_object ( ) ;
$ key_id =~ /^([0-9A-Z]*)$/ ;
$ key_id = $ 1 ;
return undef unless ( $ key_id ) ;
my $ gpgoption = "--fingerprint $key_id" ;
my $ gpgcommand = $ gpg - > gpgbin ( ) . " " . $ gpg - > gpgopts ( ) . " --with-colons $gpgoption" ;
my @ fingerprints = grep /^fpr:/ , `$gpgcommand` ;
if ( @ fingerprints > 1 ) {
warn "[Mail::Ezmlm::Gpg] more than one key matched ($key_id)!" ;
return undef ;
}
return undef if ( @ fingerprints < 1 ) ;
my $ fpr = $ fingerprints [ 0 ] ;
$ fpr =~ /^fpr:*([0-9A-Z]*):*$/ ;
$ fpr = $ 1 ;
return undef unless $ 1 ;
return $ 1 ;
}
2006-03-27 02:57:24 +02:00
# == return an error message if appropriate ==
sub errmsg {
my ( $ self ) = @ _ ;
return $ self - > { 'ERRMSG' } ;
}
sub errno {
my ( $ self ) = @ _ ;
return $ self - > { 'ERRNO' } ;
}
# == Internal function to set the error to return ==
sub _seterror {
my ( $ self , $ no , $ mesg ) = @ _ ;
if ( defined ( $ no ) && $ no ) {
if ( $ no < 0 ) {
$ self - > { 'ERRNO' } = - 1 ;
$ self - > { 'ERRMSG' } = $ mesg || 'An undefined error occoured' ;
} else {
$ self - > { 'ERRNO' } = $ no / 256 ;
$ self - > { 'ERRMSG' } = $! || $ mesg || 'An undefined error occoured in a system() call' ;
}
} else {
$ self - > { 'ERRNO' } = 0 ;
$ self - > { 'ERRMSG' } = undef ;
}
return 1 ;
}
# == Internal function to test for valid email addresses ==
sub _checkaddress {
my ( $ self , $ address ) = @ _ ;
return 1 unless defined ( $ address ) ;
return 0 unless ( $ address =~ m/^(\S+\@\S+\.\S+)$/ ) ;
$ _ [ 1 ] = $ 1 ;
return 1 ;
}
1 ;
= head1 RETURN VALUES
All of the routines described above have return values . 0 or undefined are
used to indicate that an error of some form has occoured , while anything
> 0 ( including strings , etc ) are used to indicate success .
If an error is encountered , the functions
$ list - > errno ( ) ;
$ list - > errmsg ( ) ;
can be used to determine what the error was .
errno ( ) returns ; 0 or undef if there was no error .
- 1 for an error relating to this module .
> 0 exit value of the last system ( ) call .
errmsg ( ) returns a string containing a description of the error ( $! if it
was from a system ( ) call ) . If there is no error , it returns undef .
For those who are interested , in those sub routines that have to make system
calls to perform their function , an undefined value indicates that the
system call failed , while 0 indicates some other error . Things that you would
expect to return a string ( such as thislist ( ) ) return undefined to indicate
that they haven ' t a clue ... as opposed to the empty string which would mean
that they know about nothing : )
= head1 AUTHOR
Lars Kruse <devel@sumpfralle.de>
= head1 BUGS
There are no known bugs .
Please report bugs to the author or use the bug tracking system at
https: //s ystemausfall . org /trac/ ezmlm - web .
= head1 SEE ALSO
ezmlm ( 5 ) , ezmlm - make ( 2 ) , ezmlm - sub ( 1 ) ,
ezmlm - unsub ( 1 ) , ezmlm - list ( 1 ) , ezmlm - issub ( 1 )
2006-04-19 01:52:16 +02:00
https: //s ystemausfall . org /toolforge/ ezmlm - web /
http: // www . synacklabs . net /projects/c rypt - ml /
2006-03-27 02:57:24 +02:00
http: // www . ezmlm . org /
http: // www . qmail . org /
= cut