| 14 | rodolico | 1 | package Mail::IMAPClient::BodyStructure;
 | 
        
           |  |  | 2 | #$Id: BodyStructure.pm,v 1.3 2003/06/12 21:41:37 dkernen Exp $
 | 
        
           |  |  | 3 | #use Parse::RecDescent;
 | 
        
           |  |  | 4 | use Mail::IMAPClient;
 | 
        
           |  |  | 5 | use Mail::IMAPClient::BodyStructure::Parse;
 | 
        
           |  |  | 6 | use vars qw/$parser/;
 | 
        
           |  |  | 7 | use Exporter;
 | 
        
           |  |  | 8 | push @ISA, "Exporter";
 | 
        
           |  |  | 9 | push @EXPORT_OK , '$parser';
 | 
        
           |  |  | 10 |   | 
        
           |  |  | 11 | $Mail::IMAPClient::BodyStructure::VERSION = '0.0.2';
 | 
        
           |  |  | 12 | # Do it once more to show we mean it!
 | 
        
           |  |  | 13 | $Mail::IMAPClient::BodyStructure::VERSION = '0.0.2'; 
 | 
        
           |  |  | 14 |   | 
        
           |  |  | 15 | $parser = Mail::IMAPClient::BodyStructure::Parse->new()
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | 	or 	die 	"Cannot parse rules: $@\n"	.
 | 
        
           |  |  | 18 | 			"Try remaking Mail::IMAPClient::BodyStructure::Parse.\n" 
 | 
        
           |  |  | 19 | 	and 	return undef;
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 |   | 
        
           |  |  | 22 | sub new {
 | 
        
           |  |  | 23 | 	my $class = shift;
 | 
        
           |  |  | 24 | 	my $bodystructure = shift;
 | 
        
           |  |  | 25 | 	my $self 		= $parser->start($bodystructure) or return undef;
 | 
        
           |  |  | 26 | 	$self->{_prefix}	= "";
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | 	if ( exists $self->{bodystructure} ) {
 | 
        
           |  |  | 29 | 		$self->{_id}	= 'HEAD' ;
 | 
        
           |  |  | 30 | 	} else {
 | 
        
           |  |  | 31 | 		$self->{_id}	= 1;
 | 
        
           |  |  | 32 | 	}
 | 
        
           |  |  | 33 |   | 
        
           |  |  | 34 | 	$self->{_top}		= 1;
 | 
        
           |  |  | 35 |   | 
        
           |  |  | 36 | 	return bless($self ,ref($class)||$class);
 | 
        
           |  |  | 37 | }
 | 
        
           |  |  | 38 |   | 
        
           |  |  | 39 | sub _get_thingy {
 | 
        
           |  |  | 40 | 	my $thingy = shift;
 | 
        
           |  |  | 41 | 	my $object = shift||(ref($thingy)?$thingy:undef);
 | 
        
           |  |  | 42 | 	unless ( defined($object) and ref($object) ) {
 | 
        
           |  |  | 43 | 		$@ = "No argument passed to $thingy method." 	;
 | 
        
           |  |  | 44 | 		$^W and print STDERR "$@\n" ;
 | 
        
           |  |  | 45 | 		return undef;
 | 
        
           |  |  | 46 | 	}
 | 
        
           |  |  | 47 | 	unless ( 	"$object" =~ /HASH/ 
 | 
        
           |  |  | 48 | 		and 	exists($object->{$thingy}) 
 | 
        
           |  |  | 49 | 	) {
 | 
        
           |  |  | 50 | 		$@ = 	ref($object) 					.
 | 
        
           |  |  | 51 | 			" $object does not have " 			. 
 | 
        
           |  |  | 52 | 			( $thingy =~ /^[aeiou]/i ? "an " : "a " ) 	.
 | 
        
           |  |  | 53 | 			"${thingy}. " 					.
 | 
        
           |  |  | 54 | 			( ref($object) =~ /HASH/ ? "It has " . join(", ",keys(%$object)) : "") ; 
 | 
        
           |  |  | 55 | 		$^W and print STDERR "$@\n" ;
 | 
        
           |  |  | 56 | 		return undef;
 | 
        
           |  |  | 57 | 	}
 | 
        
           |  |  | 58 | 	return Unwrapped($object->{$thingy});
 | 
        
           |  |  | 59 | }
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 | BEGIN {
 | 
        
           |  |  | 62 |  foreach my $datum (qw/	bodytype bodysubtype 	bodyparms 	bodydisp bodyid
 | 
        
           |  |  | 63 | 			bodydesc bodyenc 	bodysize 	bodylang 
 | 
        
           |  |  | 64 | 			envelopestruct  	textlines
 | 
        
           |  |  | 65 | 		   /
 | 
        
           |  |  | 66 |  ) {
 | 
        
           |  |  | 67 |         no strict 'refs';
 | 
        
           |  |  | 68 |         *$datum = sub { _get_thingy($datum, @_); };
 | 
        
           |  |  | 69 |  }
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 | }
 | 
        
           |  |  | 72 |   | 
        
           |  |  | 73 | sub parts {
 | 
        
           |  |  | 74 | 	my $self = shift;
 | 
        
           |  |  | 75 |   | 
        
           |  |  | 76 |   | 
        
           |  |  | 77 | 	if ( exists $self->{PartsList} )  {
 | 
        
           |  |  | 78 | 		return wantarray ? @{$self->{PartsList}} : $self->{PartsList} ;
 | 
        
           |  |  | 79 | 	}
 | 
        
           |  |  | 80 |   | 
        
           |  |  | 81 | 	my @parts = ();
 | 
        
           |  |  | 82 | 	$self->{PartsList} = \@parts;
 | 
        
           |  |  | 83 |   | 
        
           |  |  | 84 | 	unless ( exists($self->{bodystructure}) ) {
 | 
        
           |  |  | 85 | 		$self->{PartsIndex}{1} = $self ;
 | 
        
           |  |  | 86 | 		@parts = ("HEAD",1);
 | 
        
           |  |  | 87 | 		return wantarray ? @parts : \@parts;
 | 
        
           |  |  | 88 | 	}
 | 
        
           |  |  | 89 | 	#@parts = ( 1 );
 | 
        
           |  |  | 90 | 	#} else {
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 | 	foreach my $p ($self->bodystructure()) {
 | 
        
           |  |  | 93 | 		push @parts, $p->id();
 | 
        
           |  |  | 94 | 		$self->{PartsIndex}{$p->id()} = $p ;
 | 
        
           |  |  | 95 | 		if ( uc($p->bodytype()||"") eq "MESSAGE" ) {
 | 
        
           |  |  | 96 | 			#print "Part $parts[-1] is a ",$p->bodytype,"\n";
 | 
        
           |  |  | 97 | 			push @parts,$parts[-1] . ".HEAD";
 | 
        
           |  |  | 98 | 		#} else {
 | 
        
           |  |  | 99 | 		#	print "Part $parts[-1] is a ",$p->bodytype,"\n";
 | 
        
           |  |  | 100 | 		}
 | 
        
           |  |  | 101 | 	}
 | 
        
           |  |  | 102 |   | 
        
           |  |  | 103 | 	#}
 | 
        
           |  |  | 104 |   | 
        
           |  |  | 105 | 	return wantarray ? @parts : \@parts;
 | 
        
           |  |  | 106 | }
 | 
        
           |  |  | 107 |   | 
        
           |  |  | 108 | sub oldbodystructure {
 | 
        
           |  |  | 109 | 	my $self = shift;
 | 
        
           |  |  | 110 | 	if ( exists $self->{_bodyparts} ) { 
 | 
        
           |  |  | 111 | 		return wantarray ? @{$self->{_bodyparts}} : $self->{_bodyparts} ;
 | 
        
           |  |  | 112 | 	}
 | 
        
           |  |  | 113 | 	my @bodyparts = ( $self );
 | 
        
           |  |  | 114 | 	$self->{_id} ||= "HEAD";	# aka "0"
 | 
        
           |  |  | 115 | 	my $count = 0;
 | 
        
           |  |  | 116 | 	#print STDERR "Analyzing a ",$self->bodytype, " part which I think is part number ",
 | 
        
           |  |  | 117 | 	#	$self->{_id},"\n";
 | 
        
           |  |  | 118 | 	my $dump = Data::Dumper->new( [ $self ] , [ 'bodystructure' ] );
 | 
        
           |  |  | 119 | 	$dump->Indent(1);
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 | 	foreach my $struct (@{$self->{bodystructure}}) {
 | 
        
           |  |  | 122 | 		$struct->{_prefix} ||= $self->{_prefix} . +$count . "." unless $struct->{_top};	
 | 
        
           |  |  | 123 | 		$struct->{_id} ||= $self->{_prefix} . $count unless $struct->{_top};
 | 
        
           |  |  | 124 | 		#if (
 | 
        
           |  |  | 125 | 		#	uc($struct->bodytype) eq 'MULTIPART' or 	
 | 
        
           |  |  | 126 | 		#	uc($struct->bodytype) eq 'MESSAGE'
 | 
        
           |  |  | 127 | 		#) {
 | 
        
           |  |  | 128 | 		#} else 	{
 | 
        
           |  |  | 129 | 		#}
 | 
        
           |  |  | 130 | 		push @bodyparts, $struct, 
 | 
        
           |  |  | 131 | 			ref($struct->{bodystructure}) ? $struct->bodystructure : () ;
 | 
        
           |  |  | 132 | 	}
 | 
        
           |  |  | 133 | 	$self->{_bodyparts} = \@bodyparts ;
 | 
        
           |  |  | 134 | 	return wantarray ? @bodyparts : $self->bodyparts ;
 | 
        
           |  |  | 135 | }
 | 
        
           |  |  | 136 |   | 
        
           |  |  | 137 | sub bodystructure {
 | 
        
           |  |  | 138 | 	my $self = shift;
 | 
        
           |  |  | 139 | 	my @parts = ();
 | 
        
           |  |  | 140 | 	my $partno = 0;
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |         my $prefix = $self->{_prefix} || "";
 | 
        
           |  |  | 143 |   | 
        
           |  |  | 144 | 	#print STDERR 	"Analyzing a ",($self->bodytype||"unknown ") , 
 | 
        
           |  |  | 145 | 		#	" part which I think is part number ",
 | 
        
           |  |  | 146 | 		#	$self->{_id},"\n";
 | 
        
           |  |  | 147 |   | 
        
           |  |  | 148 | 	my $bs = $self;
 | 
        
           |  |  | 149 |         $prefix = "$prefix." if ( $prefix and $prefix !~ /\.$/);
 | 
        
           |  |  | 150 |   | 
        
           |  |  | 151 | 	if ( $self->{_top} ) {
 | 
        
           |  |  | 152 | 		$self->{_id} ||= "HEAD";
 | 
        
           |  |  | 153 | 		$self->{_prefix} ||= "HEAD";
 | 
        
           |  |  | 154 | 		$partno = 0;
 | 
        
           |  |  | 155 | 		for (my $x = 0; $x < scalar(@{$self->{bodystructure}}) ; $x++) {
 | 
        
           |  |  | 156 | 			$self->{bodystructure}[$x]{_id} = ++$partno ;
 | 
        
           |  |  | 157 | 			$self->{bodystructure}[$x]{_prefix} = $partno ;
 | 
        
           |  |  | 158 | 			push @parts, $self->{bodystructure}[$x] , 
 | 
        
           |  |  | 159 | 				$self->{bodystructure}[$x]->bodystructure;
 | 
        
           |  |  | 160 | 		}
 | 
        
           |  |  | 161 |   | 
        
           |  |  | 162 |   | 
        
           |  |  | 163 | 	} else {
 | 
        
           |  |  | 164 | 	  $partno = 0;
 | 
        
           |  |  | 165 | 	  foreach my $p ( @{$self->{bodystructure}}  ) {
 | 
        
           |  |  | 166 | 		$partno++;
 | 
        
           |  |  | 167 | 		if (
 | 
        
           |  |  | 168 | 			! exists $p->{_prefix}  
 | 
        
           |  |  | 169 | 		) {
 | 
        
           |  |  | 170 | 			$p->{_prefix} = "$prefix$partno";
 | 
        
           |  |  | 171 | 		}
 | 
        
           |  |  | 172 | 		$p->{_prefix} = "$prefix$partno";
 | 
        
           |  |  | 173 | 		$p->{_id} ||= "$prefix$partno";
 | 
        
           |  |  | 174 | 		#my $bt = $p->bodytype;
 | 
        
           |  |  | 175 | 		#if ($bt eq 'MESSAGE') {
 | 
        
           |  |  | 176 | 			#$p->{_id} = $prefix . 
 | 
        
           |  |  | 177 | 			#$partno = 0;
 | 
        
           |  |  | 178 | 		#} 
 | 
        
           |  |  | 179 | 		push @parts, $p, $p->{bodystructure} ? $p->bodystructure : ();
 | 
        
           |  |  | 180 | 	  }
 | 
        
           |  |  | 181 | 	}
 | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 | 	return wantarray ? @parts : \@parts;
 | 
        
           |  |  | 184 | }
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 | sub id {
 | 
        
           |  |  | 187 | 	my $self = shift;
 | 
        
           |  |  | 188 |   | 
        
           |  |  | 189 | 	return $self->{_id} if exists $self->{_id};
 | 
        
           |  |  | 190 | 	return "HEAD" if $self->{_top};
 | 
        
           |  |  | 191 | 	#if ($self->bodytype eq 'MESSAGE') {
 | 
        
           |  |  | 192 | 	#	return 
 | 
        
           |  |  | 193 | 	#}
 | 
        
           |  |  | 194 |   | 
        
           |  |  | 195 | 	if ($self->{bodytype} eq 'MULTIPART') {
 | 
        
           |  |  | 196 | 		my $p = $self->{_id}||$self->{_prefix} ;
 | 
        
           |  |  | 197 | 		$p =~ s/\.$//;
 | 
        
           |  |  | 198 | 		return $p;
 | 
        
           |  |  | 199 | 	} else {
 | 
        
           |  |  | 200 | 		return $self->{_id} ||= 1;
 | 
        
           |  |  | 201 | 	}
 | 
        
           |  |  | 202 | }
 | 
        
           |  |  | 203 |   | 
        
           |  |  | 204 | sub Unwrapped {
 | 
        
           |  |  | 205 | 	my $unescape = Mail::IMAPClient::Unescape(@_);
 | 
        
           |  |  | 206 | 	$unescape =~ s/^"(.*)"$/$1/ if defined($unescape);
 | 
        
           |  |  | 207 | 	return $unescape;
 | 
        
           |  |  | 208 | }
 | 
        
           |  |  | 209 |   | 
        
           |  |  | 210 | package Mail::IMAPClient::BodyStructure::Part;
 | 
        
           |  |  | 211 | @ISA = qw/Mail::IMAPClient::BodyStructure/;
 | 
        
           |  |  | 212 |   | 
        
           |  |  | 213 |   | 
        
           |  |  | 214 | package Mail::IMAPClient::BodyStructure::Envelope;
 | 
        
           |  |  | 215 | @ISA = qw/Mail::IMAPClient::BodyStructure/;
 | 
        
           |  |  | 216 |   | 
        
           |  |  | 217 | sub new {
 | 
        
           |  |  | 218 | 	my $class = shift;
 | 
        
           |  |  | 219 | 	my $envelope = shift;
 | 
        
           |  |  | 220 | 	my $self 		= $Mail::IMAPClient::BodyStructure::parser->envelope($envelope);
 | 
        
           |  |  | 221 | 	return $self;
 | 
        
           |  |  | 222 | }
 | 
        
           |  |  | 223 |   | 
        
           |  |  | 224 |   | 
        
           |  |  | 225 | sub _do_accessor {
 | 
        
           |  |  | 226 |   my $datum = shift;
 | 
        
           |  |  | 227 |   if (scalar(@_) > 1) {
 | 
        
           |  |  | 228 |     return $_[0]->{$datum} = $_[1] ;
 | 
        
           |  |  | 229 |   } else {
 | 
        
           |  |  | 230 |     return $_[0]->{$datum};
 | 
        
           |  |  | 231 |   }
 | 
        
           |  |  | 232 | }
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 | # the following for loop sets up accessor methods for 
 | 
        
           |  |  | 235 | # the object's address attributes:
 | 
        
           |  |  | 236 |   | 
        
           |  |  | 237 | sub _mk_address_method {
 | 
        
           |  |  | 238 | 	my $datum = shift;
 | 
        
           |  |  | 239 | 	my $method1 = $datum . "_addresses" ;
 | 
        
           |  |  | 240 |         no strict 'refs';
 | 
        
           |  |  | 241 |         *$method1 = sub { 
 | 
        
           |  |  | 242 | 		my $self = shift;
 | 
        
           |  |  | 243 | 		return undef unless ref($self->{$datum}) eq 'ARRAY';
 | 
        
           |  |  | 244 | 		my @list = map {
 | 
        
           |  |  | 245 | 			my $pn = $_->personalname ; 
 | 
        
           |  |  | 246 | 			$pn = "" if $pn eq 'NIL' ;
 | 
        
           |  |  | 247 | 			( $pn ? "$pn " : "" ) 	. 
 | 
        
           |  |  | 248 | 			"<"			.
 | 
        
           |  |  | 249 | 			$_->mailboxname		.
 | 
        
           |  |  | 250 | 			'@'			.
 | 
        
           |  |  | 251 | 			$_->hostname		.
 | 
        
           |  |  | 252 | 			">" 
 | 
        
           |  |  | 253 | 		} 	@{$self->{$datum}} 	;
 | 
        
           |  |  | 254 | 		if ( $senderFields{$datum} ) {
 | 
        
           |  |  | 255 | 			return wantarray ? @list : $list[0] ;
 | 
        
           |  |  | 256 | 		} else {
 | 
        
           |  |  | 257 | 			return wantarray ? @list : \@list ;
 | 
        
           |  |  | 258 | 		}
 | 
        
           |  |  | 259 | 	};
 | 
        
           |  |  | 260 | }
 | 
        
           |  |  | 261 |   | 
        
           |  |  | 262 | BEGIN {
 | 
        
           |  |  | 263 |   | 
        
           |  |  | 264 |  for my $datum ( 
 | 
        
           |  |  | 265 | 	qw( subject inreplyto from messageid bcc date replyto to sender cc )
 | 
        
           |  |  | 266 |  ) {
 | 
        
           |  |  | 267 |         no strict 'refs';
 | 
        
           |  |  | 268 |         *$datum = sub { _do_accessor($datum, @_); };
 | 
        
           |  |  | 269 |  }
 | 
        
           |  |  | 270 |  my %senderFields = map { ($_ => 1) } qw/from sender replyto/ ;
 | 
        
           |  |  | 271 |  for my $datum ( 
 | 
        
           |  |  | 272 | 	qw( from bcc replyto to sender cc )
 | 
        
           |  |  | 273 |  ) {
 | 
        
           |  |  | 274 | 	_mk_address_method($datum);
 | 
        
           |  |  | 275 |  }
 | 
        
           |  |  | 276 | }
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |   | 
        
           |  |  | 279 | package Mail::IMAPClient::BodyStructure::Address;
 | 
        
           |  |  | 280 | @ISA = qw/Mail::IMAPClient::BodyStructure/;
 | 
        
           |  |  | 281 |   | 
        
           |  |  | 282 | for my $datum ( 
 | 
        
           |  |  | 283 | 	qw( personalname mailboxname hostname sourcename )
 | 
        
           |  |  | 284 |  ) {
 | 
        
           |  |  | 285 | 	no strict 'refs';
 | 
        
           |  |  | 286 | 	*$datum = sub { return $_[0]->{$datum}; };
 | 
        
           |  |  | 287 | }
 | 
        
           |  |  | 288 |   | 
        
           |  |  | 289 | 1;
 | 
        
           |  |  | 290 | __END__
 | 
        
           |  |  | 291 |   | 
        
           |  |  | 292 | =head1 NAME
 | 
        
           |  |  | 293 |   | 
        
           |  |  | 294 | Mail::IMAPClient::BodyStructure - Perl extension to Mail::IMAPClient to facilitate 
 | 
        
           |  |  | 295 | the parsing of server responses to the FETCH BODYSTRUCTURE and FETCH ENVELOPE
 | 
        
           |  |  | 296 | IMAP client commands.
 | 
        
           |  |  | 297 |   | 
        
           |  |  | 298 | =head1 SYNOPSIS
 | 
        
           |  |  | 299 |   | 
        
           |  |  | 300 |   use Mail::IMAPClient::BodyStructure;
 | 
        
           |  |  | 301 |   use Mail::IMAPClient;
 | 
        
           |  |  | 302 |   | 
        
           |  |  | 303 |   my $imap = Mail::IMAPClient->new(Server=>$serv,User=>$usr,Password=>$pwd);
 | 
        
           |  |  | 304 |   $imap->select("INBOX") or die "cannot select the inbox for $usr: $@\n";
 | 
        
           |  |  | 305 |   | 
        
           |  |  | 306 |   my @recent = $imap->search("recent");
 | 
        
           |  |  | 307 |   | 
        
           |  |  | 308 |   foreach my $new (@recent) {
 | 
        
           |  |  | 309 |   | 
        
           |  |  | 310 | 	my $struct = Mail::IMAPClient::BodyStructure->new(
 | 
        
           |  |  | 311 | 			$imap->fetch($new,"bodystructure")
 | 
        
           |  |  | 312 | 	);
 | 
        
           |  |  | 313 |   | 
        
           |  |  | 314 | 	print	"Msg $new (Content-type: ",$struct->bodytype,"/",$struct->bodysubtype,
 | 
        
           |  |  | 315 |         	") contains these parts:\n\t",join("\n\t",$struct->parts),"\n\n";
 | 
        
           |  |  | 316 |   | 
        
           |  |  | 317 |   | 
        
           |  |  | 318 |   }
 | 
        
           |  |  | 319 |   | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |   | 
        
           |  |  | 322 |   | 
        
           |  |  | 323 | =head1 DESCRIPTION
 | 
        
           |  |  | 324 |   | 
        
           |  |  | 325 | This extension will parse the result of an IMAP FETCH BODYSTRUCTURE command into a perl 
 | 
        
           |  |  | 326 | data structure. It also provides helper methods that will help you pull information out 
 | 
        
           |  |  | 327 | of the data structure.
 | 
        
           |  |  | 328 |   | 
        
           |  |  | 329 | Use of this extension requires Parse::RecDescent. If you don't have Parse::RecDescent 
 | 
        
           |  |  | 330 | then you must either get it or refrain from using this module.
 | 
        
           |  |  | 331 |   | 
        
           |  |  | 332 | =head2 EXPORT
 | 
        
           |  |  | 333 |   | 
        
           |  |  | 334 | Nothing is exported by default. C<$parser> is exported upon request. C<$parser> 
 | 
        
           |  |  | 335 | is the BodyStucture object's Parse::RecDescent object, which you'll probably 
 | 
        
           |  |  | 336 | only need for debugging purposes. 
 | 
        
           |  |  | 337 |   | 
        
           |  |  | 338 | =head1 Class Methods
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 | The following class method is available:
 | 
        
           |  |  | 341 |   | 
        
           |  |  | 342 | =head2 new
 | 
        
           |  |  | 343 |   | 
        
           |  |  | 344 | This class method is the constructor method for instantiating new 
 | 
        
           |  |  | 345 | Mail::IMAPClient::BodyStructure objects. The B<new> method accepts one argument, 
 | 
        
           |  |  | 346 | a string containing a server response to a FETCH BODYSTRUCTURE directive.  
 | 
        
           |  |  | 347 | Only one message's body structure should be described in this 
 | 
        
           |  |  | 348 | string, although that message may contain an arbitrary number of parts.
 | 
        
           |  |  | 349 |   | 
        
           |  |  | 350 | If you know the messages sequence number or unique ID (UID) but haven't got its 
 | 
        
           |  |  | 351 | body structure, and you want to get the body structure and parse it into a 
 | 
        
           |  |  | 352 | B<Mail::IMAPClient::BodyStructure> object, then you might as well save yourself 
 | 
        
           |  |  | 353 | some work and use B<Mail::IMAPClient>'s B<get_bodystructure> method, which 
 | 
        
           |  |  | 354 | accepts a message sequence number (or UID if I<Uid> is true) and returns a 
 | 
        
           |  |  | 355 | B<Mail::IMAPClient::BodyStructure> object. It's functionally equivalent to issuing the 
 | 
        
           |  |  | 356 | FETCH BODYSTRUCTURE IMAP client command and then passing the results to 
 | 
        
           |  |  | 357 | B<Mail::IMAPClient::BodyStructure>'s B<new> method but it does those things in one 
 | 
        
           |  |  | 358 | simple method call.
 | 
        
           |  |  | 359 |   | 
        
           |  |  | 360 | =head1 Object Methods
 | 
        
           |  |  | 361 |   | 
        
           |  |  | 362 | The following object methods are available:
 | 
        
           |  |  | 363 |   | 
        
           |  |  | 364 | =head2 bodytype
 | 
        
           |  |  | 365 |   | 
        
           |  |  | 366 | The B<bodytype> object method requires no arguments.  
 | 
        
           |  |  | 367 | It returns the bodytype for the message whose structure is described by the calling 
 | 
        
           |  |  | 368 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 369 |   | 
        
           |  |  | 370 | =cut
 | 
        
           |  |  | 371 |   | 
        
           |  |  | 372 | =head2 bodysubtype
 | 
        
           |  |  | 373 |   | 
        
           |  |  | 374 | The B<bodysubtype> object method requires no arguments.  
 | 
        
           |  |  | 375 | It returns the bodysubtype for the message whose structure is described by the calling 
 | 
        
           |  |  | 376 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 377 |   | 
        
           |  |  | 378 | =cut
 | 
        
           |  |  | 379 |   | 
        
           |  |  | 380 |   | 
        
           |  |  | 381 |   | 
        
           |  |  | 382 | =head2 bodyparms
 | 
        
           |  |  | 383 |   | 
        
           |  |  | 384 | The B<bodyparms> object method requires no arguments.  
 | 
        
           |  |  | 385 | It returns the bodyparms for the message whose structure is described by the calling 
 | 
        
           |  |  | 386 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 387 |   | 
        
           |  |  | 388 | =cut
 | 
        
           |  |  | 389 |   | 
        
           |  |  | 390 |   | 
        
           |  |  | 391 |   | 
        
           |  |  | 392 | =head2 bodydisp
 | 
        
           |  |  | 393 |   | 
        
           |  |  | 394 | The B<bodydisp> object method requires no arguments.  
 | 
        
           |  |  | 395 | It returns the bodydisp for the message whose structure is described by the calling 
 | 
        
           |  |  | 396 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 397 |   | 
        
           |  |  | 398 | =cut
 | 
        
           |  |  | 399 |   | 
        
           |  |  | 400 |   | 
        
           |  |  | 401 |   | 
        
           |  |  | 402 | =head2 bodyid
 | 
        
           |  |  | 403 |   | 
        
           |  |  | 404 | The B<bodyid> object method requires no arguments.  
 | 
        
           |  |  | 405 | It returns the bodyid for the message whose structure is described by the calling 
 | 
        
           |  |  | 406 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 407 |   | 
        
           |  |  | 408 | =cut
 | 
        
           |  |  | 409 |   | 
        
           |  |  | 410 |   | 
        
           |  |  | 411 |   | 
        
           |  |  | 412 | =head2 bodydesc
 | 
        
           |  |  | 413 |   | 
        
           |  |  | 414 | The B<bodydesc> object method requires no arguments.  
 | 
        
           |  |  | 415 | It returns the bodydesc for the message whose structure is described by the calling 
 | 
        
           |  |  | 416 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 417 |   | 
        
           |  |  | 418 | =cut
 | 
        
           |  |  | 419 |   | 
        
           |  |  | 420 |   | 
        
           |  |  | 421 |   | 
        
           |  |  | 422 | =head2 bodyenc
 | 
        
           |  |  | 423 |   | 
        
           |  |  | 424 | The B<bodyenc> object method requires no arguments.  
 | 
        
           |  |  | 425 | It returns the bodyenc for the message whose structure is described by the calling 
 | 
        
           |  |  | 426 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 427 |   | 
        
           |  |  | 428 | =cut
 | 
        
           |  |  | 429 |   | 
        
           |  |  | 430 |   | 
        
           |  |  | 431 |   | 
        
           |  |  | 432 | =head2 bodysize
 | 
        
           |  |  | 433 |   | 
        
           |  |  | 434 | The B<bodysize> object method requires no arguments.  
 | 
        
           |  |  | 435 | It returns the bodysize for the message whose structure is described by the calling 
 | 
        
           |  |  | 436 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 437 |   | 
        
           |  |  | 438 | =cut
 | 
        
           |  |  | 439 |   | 
        
           |  |  | 440 |   | 
        
           |  |  | 441 |   | 
        
           |  |  | 442 | =head2 bodylang
 | 
        
           |  |  | 443 |   | 
        
           |  |  | 444 | The B<bodylang> object method requires no arguments.  
 | 
        
           |  |  | 445 | It returns the bodylang for the message whose structure is described by the calling 
 | 
        
           |  |  | 446 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 447 |   | 
        
           |  |  | 448 | =cut
 | 
        
           |  |  | 449 |   | 
        
           |  |  | 450 | =head2 bodystructure
 | 
        
           |  |  | 451 |   | 
        
           |  |  | 452 | The B<bodystructure> object method requires no arguments.  
 | 
        
           |  |  | 453 | It returns the bodystructure for the message whose structure is described by the calling 
 | 
        
           |  |  | 454 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 455 |   | 
        
           |  |  | 456 | =cut
 | 
        
           |  |  | 457 |   | 
        
           |  |  | 458 |   | 
        
           |  |  | 459 |   | 
        
           |  |  | 460 | =head2 envelopestruct
 | 
        
           |  |  | 461 |   | 
        
           |  |  | 462 | The B<envelopestruct> object method requires no arguments.  
 | 
        
           |  |  | 463 | It returns the envelopestruct for the message whose structure is described by the 
 | 
        
           |  |  | 464 | calling B<Mail::IMAPClient::Bodystructure> object. This envelope structure is blessed
 | 
        
           |  |  | 465 | into the B<Mail::IMAPClient::BodyStructure::Envelope> subclass, which is explained more
 | 
        
           |  |  | 466 | fully below.
 | 
        
           |  |  | 467 |   | 
        
           |  |  | 468 | =cut
 | 
        
           |  |  | 469 |   | 
        
           |  |  | 470 |   | 
        
           |  |  | 471 | =head2 textlines
 | 
        
           |  |  | 472 |   | 
        
           |  |  | 473 | The B<textlines> object method requires no arguments.  
 | 
        
           |  |  | 474 | It returns the textlines for the message whose structure is described by the calling 
 | 
        
           |  |  | 475 | B<Mail::IMAPClient::Bodystructure> object.
 | 
        
           |  |  | 476 |   | 
        
           |  |  | 477 | =cut
 | 
        
           |  |  | 478 |   | 
        
           |  |  | 479 | =head1 Envelopes and the Mail::IMAPClient::BodyStructure::Envelope Subclass
 | 
        
           |  |  | 480 |   | 
        
           |  |  | 481 | The IMAP standard specifies that output from the IMAP B<FETCH ENVELOPE> command 
 | 
        
           |  |  | 482 | will be an RFC2060 envelope structure. It further specifies that output from the 
 | 
        
           |  |  | 483 | B<FETCH BODYSTRUCTURE> command may also contain embedded envelope structures (if, 
 | 
        
           |  |  | 484 | for example, a message's subparts contain one or more included messages). Objects
 | 
        
           |  |  | 485 | belonging to B<Mail::IMAPClient::BodyStructure::Envelope> are Perl representations
 | 
        
           |  |  | 486 | of these envelope structures, which is to say the nested parenthetical lists of 
 | 
        
           |  |  | 487 | RFC2060 translated into a Perl datastructure.
 | 
        
           |  |  | 488 |   | 
        
           |  |  | 489 | Note that all of the fields relate to the specific part to which they belong. In other
 | 
        
           |  |  | 490 | words, output from a FETCH nnnn ENVELOPE command (or, in B<Mail::IMAPClient>,
 | 
        
           |  |  | 491 | C<$imap->fetch($msgid,"ENVELOPE")> or C<my $env = $imap->get_envelope($msgid)>) are for
 | 
        
           |  |  | 492 | the message, but fields from within a bodystructure relate to the message subpart and 
 | 
        
           |  |  | 493 | not the parent message.
 | 
        
           |  |  | 494 |   | 
        
           |  |  | 495 | An envelope structure's B<Mail::IMAPClient::BodyStructure::Envelope> representation 
 | 
        
           |  |  | 496 | is a hash of thingies that looks like this:
 | 
        
           |  |  | 497 |   | 
        
           |  |  | 498 | {
 | 
        
           |  |  | 499 |                      subject => 	"subject",
 | 
        
           |  |  | 500 |                      inreplyto =>	"reference_message_id",
 | 
        
           |  |  | 501 |                      from => 		[ addressStruct1 ],
 | 
        
           |  |  | 502 |                      messageid => 	"message_id",
 | 
        
           |  |  | 503 |                      bcc => 		[ addressStruct1, addressStruct2 ],
 | 
        
           |  |  | 504 |                      date => 		"Tue, 09 Jul 2002 14:15:53 -0400",
 | 
        
           |  |  | 505 |                      replyto => 	[ adressStruct1, addressStruct2 ],
 | 
        
           |  |  | 506 |                      to => 		[ adressStruct1, addressStruct2 ],
 | 
        
           |  |  | 507 |                      sender => 		[ adressStruct1 ],
 | 
        
           |  |  | 508 |                      cc => 		[ adressStruct1, addressStruct2 ],
 | 
        
           |  |  | 509 | }
 | 
        
           |  |  | 510 |   | 
        
           |  |  | 511 | The B<...::Envelope> object also has methods for accessing data in the structure. They
 | 
        
           |  |  | 512 | are:
 | 
        
           |  |  | 513 |   | 
        
           |  |  | 514 | =over 4
 | 
        
           |  |  | 515 |   | 
        
           |  |  | 516 | =item date
 | 
        
           |  |  | 517 |   | 
        
           |  |  | 518 | Returns the date of the message.
 | 
        
           |  |  | 519 |   | 
        
           |  |  | 520 | =item inreplyto
 | 
        
           |  |  | 521 |   | 
        
           |  |  | 522 | Returns the message id of the message to which this message is a reply.
 | 
        
           |  |  | 523 |   | 
        
           |  |  | 524 | =item subject
 | 
        
           |  |  | 525 |   | 
        
           |  |  | 526 | Returns the subject of the message. 
 | 
        
           |  |  | 527 |   | 
        
           |  |  | 528 | =item messageid
 | 
        
           |  |  | 529 |   | 
        
           |  |  | 530 | Returns the message id of the message.
 | 
        
           |  |  | 531 |   | 
        
           |  |  | 532 | =back
 | 
        
           |  |  | 533 |   | 
        
           |  |  | 534 | You can also use the following methods to get addressing information. Each of these methods
 | 
        
           |  |  | 535 | returns an array of B<Mail::IMAPClient::BodyStructure::Address> objects, which are perl 
 | 
        
           |  |  | 536 | data structures representing RFC2060 address structures. Some of these arrays would naturally  
 | 
        
           |  |  | 537 | contain one element (such as B<from>, which normally contains a single "From:" address); others
 | 
        
           |  |  | 538 | will often contain more than one address. However, because RFC2060 defines all of these as "lists
 | 
        
           |  |  | 539 | of address structures", they are all translated into arrays of B<...::Address> objects. 
 | 
        
           |  |  | 540 |   | 
        
           |  |  | 541 | See the section on B<Mail::IMAPClient::BodyStructure::Address>", below, for alternate (and 
 | 
        
           |  |  | 542 | preferred) ways of accessing these data.
 | 
        
           |  |  | 543 |   | 
        
           |  |  | 544 | The methods available are:
 | 
        
           |  |  | 545 |   | 
        
           |  |  | 546 | =over 4
 | 
        
           |  |  | 547 |   | 
        
           |  |  | 548 | =item bcc
 | 
        
           |  |  | 549 |   | 
        
           |  |  | 550 | Returns an array of blind cc'ed recipients' address structures. (Don't expect much in here
 | 
        
           |  |  | 551 | unless the message was sent from the mailbox you're poking around in, by the way.)
 | 
        
           |  |  | 552 |   | 
        
           |  |  | 553 | =item cc
 | 
        
           |  |  | 554 |   | 
        
           |  |  | 555 | Returns an array of cc'ed recipients' address structures.
 | 
        
           |  |  | 556 |   | 
        
           |  |  | 557 | =item from
 | 
        
           |  |  | 558 |   | 
        
           |  |  | 559 | Returns an array of "From:" address structures--usually just one.
 | 
        
           |  |  | 560 |   | 
        
           |  |  | 561 | =item replyto
 | 
        
           |  |  | 562 |   | 
        
           |  |  | 563 | Returns an array of "Reply-to:" address structures. Once again there is usually
 | 
        
           |  |  | 564 | just one address in the list.
 | 
        
           |  |  | 565 |   | 
        
           |  |  | 566 | =item sender
 | 
        
           |  |  | 567 |   | 
        
           |  |  | 568 | Returns an array of senders' address structures--usually just one and usually the same
 | 
        
           |  |  | 569 | as B<from>.
 | 
        
           |  |  | 570 |   | 
        
           |  |  | 571 | =item to
 | 
        
           |  |  | 572 |   | 
        
           |  |  | 573 | Returns an array of recipients' address structures.
 | 
        
           |  |  | 574 |   | 
        
           |  |  | 575 | =back
 | 
        
           |  |  | 576 |   | 
        
           |  |  | 577 | Each of the methods that returns a list of address structures (i.e. a list of 
 | 
        
           |  |  | 578 | B<Mail::IMAPClient::BodyStructure::Address> arrays) also has an analagous method
 | 
        
           |  |  | 579 | that will return a list of E-Mail addresses instead. The addresses are in the  
 | 
        
           |  |  | 580 | format C<personalname E<lt>mailboxname@hostnameE<gt>> (see the section on 
 | 
        
           |  |  | 581 | B<Mail::IMAPClient::BodyStructure::Address>, below) However, if the personal name 
 | 
        
           |  |  | 582 | is 'NIL' then it is omitted from the address. 
 | 
        
           |  |  | 583 |   | 
        
           |  |  | 584 | These methods are:
 | 
        
           |  |  | 585 |   | 
        
           |  |  | 586 | =over 4
 | 
        
           |  |  | 587 |   | 
        
           |  |  | 588 | =item bcc_addresses
 | 
        
           |  |  | 589 |   | 
        
           |  |  | 590 | Returns a list (or an array reference if called in scalar context) of blind cc'ed 
 | 
        
           |  |  | 591 | recipients' email addresses. (Don't expect much in here unless the message was sent 
 | 
        
           |  |  | 592 | from the mailbox you're poking around in, by the way.)
 | 
        
           |  |  | 593 |   | 
        
           |  |  | 594 | =item cc_addresses
 | 
        
           |  |  | 595 |   | 
        
           |  |  | 596 | Returns a list of cc'ed recipients' email addresses. If called in a scalar 
 | 
        
           |  |  | 597 | context it returns a reference to an array of email addresses.
 | 
        
           |  |  | 598 |   | 
        
           |  |  | 599 | =item from_addresses
 | 
        
           |  |  | 600 |   | 
        
           |  |  | 601 | Returns a list of "From:" email addresses.  If called in a scalar context
 | 
        
           |  |  | 602 | it returns the first email address in the list. (It's usually a list of just
 | 
        
           |  |  | 603 | one anyway.)
 | 
        
           |  |  | 604 |   | 
        
           |  |  | 605 | =item replyto_addresses
 | 
        
           |  |  | 606 |   | 
        
           |  |  | 607 | Returns a list of "Reply-to:" email addresses.  If called in a scalar context
 | 
        
           |  |  | 608 | it returns the first email address in the list.
 | 
        
           |  |  | 609 |   | 
        
           |  |  | 610 | =item sender_addresses
 | 
        
           |  |  | 611 |   | 
        
           |  |  | 612 | Returns a list of senders' email addresses.  If called in a scalar context
 | 
        
           |  |  | 613 | it returns the first email address in the list.
 | 
        
           |  |  | 614 |   | 
        
           |  |  | 615 | =item to_addresses
 | 
        
           |  |  | 616 |   | 
        
           |  |  | 617 | Returns a list of recipients' email addresses.  If called in a scalar context
 | 
        
           |  |  | 618 | it returns a reference to an array of email addresses.
 | 
        
           |  |  | 619 |   | 
        
           |  |  | 620 | =back
 | 
        
           |  |  | 621 |   | 
        
           |  |  | 622 | Note that context affects the behavior of all of the above methods. 
 | 
        
           |  |  | 623 |   | 
        
           |  |  | 624 | Those fields that will commonly contain multiple entries (i.e. they are 
 | 
        
           |  |  | 625 | recipients) will return an array reference when called in scalar context. 
 | 
        
           |  |  | 626 | You can use this behavior to optimize performance.
 | 
        
           |  |  | 627 |   | 
        
           |  |  | 628 | Those fields that will commonly contain just one address (the sender's) will 
 | 
        
           |  |  | 629 | return the first (and usually only) address. You can use this behavior to 
 | 
        
           |  |  | 630 | optimize your development time.
 | 
        
           |  |  | 631 |   | 
        
           |  |  | 632 | =head1 Addresses and the Mail::IMAPClient::BodyStructure::Address
 | 
        
           |  |  | 633 |   | 
        
           |  |  | 634 | Several components of an envelope structure are address structures. They are each 
 | 
        
           |  |  | 635 | parsed into their own object, B<Mail::IMAPClient::BodyStructure::Address>, which 
 | 
        
           |  |  | 636 | looks like this:
 | 
        
           |  |  | 637 |   | 
        
           |  |  | 638 | 	  {
 | 
        
           |  |  | 639 |             mailboxname 	=> 'somebody.special',
 | 
        
           |  |  | 640 |             hostname 		=> 'somplace.weird.com',
 | 
        
           |  |  | 641 |             personalname 	=> 'Somebody Special
 | 
        
           |  |  | 642 |             sourceroute 	=> 'NIL'
 | 
        
           |  |  | 643 |           } 
 | 
        
           |  |  | 644 |   | 
        
           |  |  | 645 | RFC2060 specifies that each address component of a bodystructure is a list of 
 | 
        
           |  |  | 646 | address structures, so B<Mail::IMAPClient::BodyStructure> parses each of these into
 | 
        
           |  |  | 647 | an array of B<Mail::IMAPClient::BodyStructure::Address> objects.
 | 
        
           |  |  | 648 |   | 
        
           |  |  | 649 | Each of these objects has the following methods available to it:
 | 
        
           |  |  | 650 |   | 
        
           |  |  | 651 | =over 4
 | 
        
           |  |  | 652 |   | 
        
           |  |  | 653 | =item mailboxname
 | 
        
           |  |  | 654 |   | 
        
           |  |  | 655 | Returns the "mailboxname" portion of the address, which is the part to the left 
 | 
        
           |  |  | 656 | of the '@' sign.
 | 
        
           |  |  | 657 |   | 
        
           |  |  | 658 | =item hostname
 | 
        
           |  |  | 659 |   | 
        
           |  |  | 660 | Returns the "hostname" portion of the address, which is the part to the right of the
 | 
        
           |  |  | 661 | '@' sign. 
 | 
        
           |  |  | 662 |   | 
        
           |  |  | 663 | =item personalname
 | 
        
           |  |  | 664 |   | 
        
           |  |  | 665 | Returns the "personalname" portion of the address, which is the part of 
 | 
        
           |  |  | 666 | the address that's treated like a comment.
 | 
        
           |  |  | 667 |   | 
        
           |  |  | 668 | =item sourceroute
 | 
        
           |  |  | 669 |   | 
        
           |  |  | 670 | Returns the "sourceroute" portion of the address, which is typically "NIL".
 | 
        
           |  |  | 671 |   | 
        
           |  |  | 672 | =back
 | 
        
           |  |  | 673 |   | 
        
           |  |  | 674 | Taken together, the parts of an address structure form an address that will 
 | 
        
           |  |  | 675 | look something like this:
 | 
        
           |  |  | 676 |   | 
        
           |  |  | 677 | C<personalname E<lt>mailboxname@hostnameE<gt>>
 | 
        
           |  |  | 678 |   | 
        
           |  |  | 679 | Note that because the B<Mail::IMAPClient::BodyStructure::Address> objects come in 
 | 
        
           |  |  | 680 | arrays, it's generally easier to use the methods available to 
 | 
        
           |  |  | 681 | B<Mail::IMAPClient::BodyStructure::Envelope> to obtain all of the addresses in a 
 | 
        
           |  |  | 682 | particular array in one operation. These methods are provided, however, in case 
 | 
        
           |  |  | 683 | you'd rather do things the hard way. (And also because the aforementioned methods
 | 
        
           |  |  | 684 | from B<Mail::IMAPClient::BodyStructure::Envelope> need them anyway.)
 | 
        
           |  |  | 685 |   | 
        
           |  |  | 686 | =cut
 | 
        
           |  |  | 687 |   | 
        
           |  |  | 688 | =head1 AUTHOR
 | 
        
           |  |  | 689 |   | 
        
           |  |  | 690 | David J. Kernen
 | 
        
           |  |  | 691 |   | 
        
           |  |  | 692 | =head1 SEE ALSO
 | 
        
           |  |  | 693 |   | 
        
           |  |  | 694 | perl(1), Mail::IMAPClient, and RFC2060. See also Parse::RecDescent if you want
 | 
        
           |  |  | 695 | to understand the internals of this module.
 | 
        
           |  |  | 696 |   | 
        
           |  |  | 697 | =cut
 | 
        
           |  |  | 698 |   | 
        
           |  |  | 699 |   | 
        
           |  |  | 700 | # History: 
 | 
        
           |  |  | 701 | # $Log: BodyStructure.pm,v $
 | 
        
           |  |  | 702 | # Revision 1.3  2003/06/12 21:41:37  dkernen
 | 
        
           |  |  | 703 | # Cleaning up cvs repository
 | 
        
           |  |  | 704 | #
 | 
        
           |  |  | 705 | # Revision 1.1  2003/06/12 21:37:03  dkernen
 | 
        
           |  |  | 706 | #
 | 
        
           |  |  | 707 | # Preparing 2.2.8
 | 
        
           |  |  | 708 | # Added Files: COPYRIGHT
 | 
        
           |  |  | 709 | # Modified Files: Parse.grammar
 | 
        
           |  |  | 710 | # Added Files: Makefile.old
 | 
        
           |  |  | 711 | # 	Makefile.PL Todo sample.perldb
 | 
        
           |  |  | 712 | # 	BodyStructure.pm
 | 
        
           |  |  | 713 | #
 | 
        
           |  |  | 714 | # Revision 1.2  2002/09/26 17:56:14  dkernen
 | 
        
           |  |  | 715 | #
 | 
        
           |  |  | 716 | # Modified Files:
 | 
        
           |  |  | 717 | # BUG_REPORTS Changes IMAPClient.pm INSTALL_perl5.80 MANIFEST
 | 
        
           |  |  | 718 | # Makefile.PL for version 2.2.3. See the Changes file for details.
 | 
        
           |  |  | 719 | # Modified Files: BodyStructure.pm -- cosmetic changes to pod doc
 | 
        
           |  |  | 720 | #
 | 
        
           |  |  | 721 | # Revision 1.1  2002/08/30 20:58:51  dkernen
 | 
        
           |  |  | 722 | #
 | 
        
           |  |  | 723 | # In Mail::IMAPClient/IMAPClient, added files: BUG_REPORTS getGrammer runtest sample.perldb
 | 
        
           |  |  | 724 | # In Mail::IMAPClient/IMAPClient/BodyStructure, added files: BodyStructure.pm Makefile.PL debug.ksh runtest
 | 
        
           |  |  | 725 | #
 |