releasing/cbrtools/perl/Crypt/PGP.pm
author Bob Rosenberg <bob.rosenberg@nokia.com>
Mon, 20 Sep 2010 10:55:43 +0100
changeset 658 cab9da9b71bb
parent 602 3145852acc89
permissions -rw-r--r--
Test and fail for badly invalid unit elements when joining
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
602
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     1
# Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     2
# All rights reserved.
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     3
# This component and the accompanying materials are made available
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     4
# under the terms of the License "Eclipse Public License v1.0"
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     5
# which accompanies this distribution, and is available
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     6
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     7
# 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     8
# Initial Contributors:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
     9
# Nokia Corporation - initial contribution.
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    10
# 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    11
# Contributors:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    12
# 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    13
# Description:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    14
# 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    15
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    16
# Description:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    17
# Crypt::PGP.pm
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    18
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    19
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    20
package Crypt::PGP;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    21
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    22
use strict;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    23
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    24
use Crypt;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    25
use vars qw(@ISA);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    26
@ISA=("Crypt");
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    27
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    28
# Overidden methods from Crypt.pm
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    29
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    30
sub Initialize {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    31
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    32
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    33
  #check to see if the pgp executable exists
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    34
  grep {-x "$_/pgp.exe"} split /;/, $ENV{PATH}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    35
    or die "Error: The PGP executable \"pgp.exe\" does not exist in users path\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    36
  
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    37
  #call super class method
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    38
  $self->SUPER::Initialize();
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    39
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    40
  #check for existence of keyrings and keys
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    41
  $self->CheckKeyRings();
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    42
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    43
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    44
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    45
# Implemented abstract methods from Crypt.pm
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    46
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    47
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    48
sub DoEncrypt {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    49
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    50
  my $plainText = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    51
  my $cipherText = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    52
  my @recipientKeys = @{$_[0]};
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    53
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    54
  $self->CheckKeyRings();
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    55
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    56
  #build options list
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    57
  my @options = qw(+force +batchmode +verbose=2);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    58
  push @options, '-o '.$self->Quoted($cipherText);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    59
  if ($self->DefaultPath()) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    60
    push @options, '+PUBRING='.$self->Quoted($self->DefaultPath().'/pubring.pkr');
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    61
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    62
  my @command = '-e '.$self->Quoted($plainText);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    63
  push @command, @recipientKeys;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    64
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    65
  #do encryption
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    66
  open PGP, "pgp @options @command 2>NUL |" or die "Error: Encrypt command failed.\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    67
  my $unsignedKeyError;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    68
  while (my $line = <PGP>) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    69
    if ($self->{verbose} > 1) {print $line;}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    70
    if ($line =~ /skipping userid/i) { #check for unsigned key errors
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    71
      $unsignedKeyError = 1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    72
    }	
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    73
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    74
  close PGP;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    75
  if ($unsignedKeyError) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    76
    die "Error: Encryption failed. Public keys must be signed with the default signing key\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    77
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    78
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    79
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    80
sub DoDecrypt {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    81
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    82
  my $cipherText = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    83
  my $plainText = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    84
  my $passPhrase = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    85
  
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    86
  $self->CheckKeyRings();
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    87
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    88
  #build options list
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    89
  my @options =qw(+force +batchmode +verbose=2);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    90
  push @options, '-o '.$self->Quoted($plainText);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    91
  if ($self->DefaultPath()) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    92
    push @options, '+SECRING='.$self->Quoted($self->DefaultPath().'/secring.skr');
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    93
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    94
  push @options, '-z'.$self->Quoted($passPhrase);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    95
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    96
  my @command = ('-d '.$self->Quoted($cipherText));
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    97
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    98
  #do decryption
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
    99
  open PGP, "pgp @options @command 2>NUL |" or die "Error: Decrypt command failed.\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   100
  my ($errorCode, $exitCode);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   101
  while (my $line = <PGP>) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   102
    if ($self->{verbose} > 1) {print $line;}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   103
    if ($line =~ /error.*?-(\d+)/i) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   104
      $errorCode = $1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   105
    } 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   106
    elsif ($line =~ /exitcode.*?(\d+)/i) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   107
      $exitCode = $1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   108
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   109
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   110
  close PGP;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   111
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   112
  #handle specific decryption errors
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   113
  if (defined $errorCode) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   114
    if ($errorCode == 11477) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   115
      die "Error: Decryption of $cipherText failed. No decrypting key available. NO_SECKEY\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   116
    } 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   117
    elsif ($errorCode == 11489) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   118
      die "Error: Decryption of $cipherText failed. BAD_PASSPHRASE\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   119
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   120
  }	
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   121
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   122
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   123
sub GetPublicKeyList {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   124
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   125
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   126
  my @options = qw(+verbose=2);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   127
  if ($self->DefaultPath()) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   128
    push @options, '+PUBRING='.$self->Quoted($self->DefaultPath().'/pubring.pkr');
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   129
  } 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   130
  my @command = qw(-kv);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   131
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   132
  #list and extract keyids
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   133
  open PGP, "pgp @options @command 2>NUL |" or die "Error: List keys command failed.\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   134
  my @keys;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   135
  while (my $line = <PGP>) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   136
    if ($line =~ /(0x[0-9a-fA-F]{8})/i) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   137
      push @keys, $1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   138
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   139
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   140
  close PGP;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   141
  return \@keys;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   142
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   143
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   144
sub GetSecretKeyList {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   145
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   146
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   147
  my @options = qw(+verbose=2);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   148
  if ($self->DefaultPath()) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   149
    push @options, '+SECRING='.$self->Quoted($self->DefaultPath().'/secring.skr');
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   150
  } 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   151
  my @command = qw(-kv);
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   152
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   153
  #list and extract keyids
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   154
  open PGP, "pgp @options @command 2>NUL |" or die "Error: List keys command failed.\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   155
  my @keys;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   156
  while (my $line = <PGP>) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   157
    if ($self->{verbose} > 1) {print $line;}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   158
    if ($line =~ /(0x[0-9a-fA-F]{8})/i) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   159
      push @keys, $1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   160
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   161
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   162
  close PGP;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   163
  return \@keys;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   164
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   165
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   166
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   167
# Private
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   168
#
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   169
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   170
sub CheckKeyRings {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   171
  my $self = shift;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   172
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   173
  if ($self->DefaultPath) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   174
    unless (-e $self->DefaultPath.'/pubring.pkr') {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   175
      die "Error: PGP public keyring does not exist\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   176
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   177
    unless (-e $self->DefaultPath.'/secring.skr') {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   178
      die "Error: PGP secret keyring does not exist\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   179
    }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   180
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   181
  unless (@{$self->PublicKeyList}) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   182
    die "Error: PGP public keyring is empty\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   183
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   184
  unless (@{$self->SecretKeyList}) {
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   185
    die "Error: PGP secret keyring is empty\n";
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   186
  }
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   187
}
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   188
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   189
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   190
1;
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   191
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   192
__END__
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   193
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   194
=head1 NAME
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   195
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   196
Crypt::PGP.pm - A wrapper over Network Associates command line PGP tool
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   197
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   198
=head1 DESCRIPTION
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   199
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   200
C<Crypt::PGP> is inherited from the abstract base class C<Crypt>, implementing the abstract methods required for PGP encryption, decryption, etc... by calling NAI Inc. PGP command line tool (tested with version 6). For this module to work the PGP executable must have the name C<pgp.exe> and exist in one of the directories defined in the users path.
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   201
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   202
=head1 KNOWN BUGS
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   203
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   204
None
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   205
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   206
=head1 COPYRIGHT
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   207
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   208
 Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   209
 All rights reserved.
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   210
 This component and the accompanying materials are made available
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   211
 under the terms of the License "Eclipse Public License v1.0"
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   212
 which accompanies this distribution, and is available
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   213
 at the URL "http://www.eclipse.org/legal/epl-v10.html".
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   214
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   215
 Initial Contributors:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   216
 Nokia Corporation - initial contribution.
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   217
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   218
 Contributors:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   219
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   220
 Description:
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   221
 
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   222
3145852acc89 add releasing to new structure
jjkang
parents:
diff changeset
   223
=cut