summaryrefslogtreecommitdiff
path: root/cpan/lib/Mail/Address.pm
diff options
context:
space:
mode:
Diffstat (limited to 'cpan/lib/Mail/Address.pm')
-rw-r--r--cpan/lib/Mail/Address.pm536
1 files changed, 196 insertions, 340 deletions
diff --git a/cpan/lib/Mail/Address.pm b/cpan/lib/Mail/Address.pm
index 6fa1f1c9..13b2ff7d 100644
--- a/cpan/lib/Mail/Address.pm
+++ b/cpan/lib/Mail/Address.pm
@@ -1,27 +1,32 @@
-# Mail::Address.pm
-#
-# Copyright (c) 1995-2001 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
+# Copyrights 1995-2017 by [Mark Overmeer <perl@overmeer.net>].
+# For other contributors see ChangeLog.
+# See the manual pages for details on the licensing terms.
+# Pod stripped from pm file by OODoc 2.02.
package Mail::Address;
+use vars '$VERSION';
+$VERSION = '2.19';
+
use strict;
use Carp;
-use vars qw($VERSION);
-use locale;
-$VERSION = "1.52";
-sub Version { $VERSION }
+# use locale; removed in version 1.78, because it causes taint problems
-#
-# given a comment, attempt to extract a person's name
-#
+sub Version { our $VERSION }
+
+
+# given a comment, attempt to extract a person's name
sub _extract_name
-{
- local $_ = shift || '';
-
+{ # This function can be called as method as well
+ my $self = @_ && ref $_[0] ? shift : undef;
+
+ local $_ = shift
+ or return '';
+
+ # Using encodings, too hard. See Mail::Message::Field::Full.
+ return '' if m/\=\?.*?\?\=/;
+
# trim whitespace
s/^\s+//;
s/\s+$//;
@@ -30,391 +35,242 @@ sub _extract_name
# Disregard numeric names (e.g. 123456.1234@compuserve.com)
return "" if /^[\d ]+$/;
- # remove outermost parenthesis
- s/^\(|\)$//g;
-
- # remove outer quotation marks
- s/^"|"$//g;
-
- # remove embedded comments
- s/\(.*\)//g;
-
- # reverse "Last, First M." if applicable
- s/^([^\s]+) ?, ?(.*)$/$2 $1/;
+ s/^\((.*)\)$/$1/; # remove outermost parenthesis
+ s/^"(.*)"$/$1/; # remove outer quotation marks
+ s/\(.*?\)//g; # remove minimal embedded comments
+ s/\\//g; # remove all escapes
+ s/^"(.*)"$/$1/; # remove internal quotation marks
+ s/^([^\s]+) ?, ?(.*)$/$2 $1/; # reverse "Last, First M." if applicable
s/,.*//;
- # Set the case of the name to first char upper rest lower
- # Upcase first letter on name
- s/\b(\w+)/\L\u$1/igo;
-
- # Scottish names such as 'McLeod'
- s/\bMc(\w)/Mc\u$1/igo;
-
- # Irish names such as 'O'Malley, O'Reilly'
- s/\bo'(\w)/O'\u$1/igo;
-
- # Roman numerals, eg 'Level III Support'
- s/\b(x*(ix)?v*(iv)?i*)\b/\U$1/igo;
+ # Change casing only when the name contains only upper or only
+ # lower cased characters.
+ unless( m/[A-Z]/ && m/[a-z]/ )
+ { # Set the case of the name to first char upper rest lower
+ s/\b(\w+)/\L\u$1/igo; # Upcase first letter on name
+ s/\bMc(\w)/Mc\u$1/igo; # Scottish names such as 'McLeod'
+ s/\bo'(\w)/O'\u$1/igo; # Irish names such as 'O'Malley, O'Reilly'
+ s/\b(x*(ix)?v*(iv)?i*)\b/\U$1/igo; # Roman numerals, eg 'Level III Support'
+ }
# some cleanup
s/\[[^\]]*\]//g;
s/(^[\s'"]+|[\s'"]+$)//g;
s/\s{2,}/ /g;
- return $_;
+ $_;
}
-sub _tokenise {
- local($_) = join(',', @_);
- my(@words,$snippet,$field);
-
- s/\A\s+//;
- s/[\r\n]+/ /g;
+sub _tokenise
+{ local $_ = join ',', @_;
+ my (@words,$snippet,$field);
- while ($_ ne '')
- {
- $field = '';
- if( s/^\s*\(/(/ ) # (...)
- {
- my $depth = 0;
+ s/\A\s+//;
+ s/[\r\n]+/ /g;
+
+ while ($_ ne '')
+ { $field = '';
+ if(s/^\s*\(/(/ ) # (...)
+ { my $depth = 0;
PAREN: while(s/^(\(([^\(\)\\]|\\.)*)//)
- {
- $field .= $1;
- $depth++;
- while(s/^(([^\(\)\\]|\\.)*\)\s*)//)
- {
- $field .= $1;
- last PAREN unless --$depth;
- $field .= $1 if s/^(([^\(\)\\]|\\.)+)//;
+ { $field .= $1;
+ $depth++;
+ while(s/^(([^\(\)\\]|\\.)*\)\s*)//)
+ { $field .= $1;
+ last PAREN unless --$depth;
+ $field .= $1 if s/^(([^\(\)\\]|\\.)+)//;
+ }
+ }
+
+ carp "Unmatched () '$field' '$_'"
+ if $depth;
+
+ $field =~ s/\s+\Z//;
+ push @words, $field;
+
+ next;
}
- }
-
- carp "Unmatched () '$field' '$_'"
- if $depth;
- $field =~ s/\s+\Z//;
- push(@words, $field);
+ if( s/^("(?:[^"\\]+|\\.)*")\s*// # "..."
+ || s/^(\[(?:[^\]\\]+|\\.)*\])\s*// # [...]
+ || s/^([^\s()<>\@,;:\\".[\]]+)\s*//
+ || s/^([()<>\@,;:\\".[\]])\s*//
+ )
+ { push @words, $1;
+ next;
+ }
- next;
+ croak "Unrecognised line: $_";
}
- s/^("([^"\\]|\\.)*")\s*// # "..."
- || s/^(\[([^\]\\]|\\.)*\])\s*// # [...]
- || s/^([^\s\Q()<>\@,;:\\".[]\E]+)\s*//
- || s/^([\Q()<>\@,;:\\".[]\E])\s*//
- and do { push(@words, $1); next; };
-
- croak "Unrecognised line: $_";
- }
-
- push(@words, ",");
-
- \@words;
-}
-
-sub _find_next {
- my $idx = shift;
- my $tokens = shift;
- my $len = shift;
- while($idx < $len) {
- my $c = $tokens->[$idx];
- return $c if($c eq "," || $c eq "<");
- $idx++;
- }
- return "";
-}
-
-sub _complete {
- my $pkg = shift;
- my $phrase = shift;
- my $address = shift;
- my $comment = shift;
- my $o = undef;
-
- if(@{$phrase} || @{$comment} || @{$address}) {
- $o = $pkg->new(join(" ",@{$phrase}),
- join("", @{$address}),
- join(" ",@{$comment}));
- @{$phrase} = ();
- @{$address} = ();
- @{$comment} = ();
- }
-
- return $o;
+ push @words, ",";
+ \@words;
}
+sub _find_next
+{ my ($idx, $tokens, $len) = @_;
-sub new {
- my $pkg = shift;
- my $me = bless [@_], $pkg;
- return $me;
-}
-
+ while($idx < $len)
+ { my $c = $tokens->[$idx];
+ return $c if $c eq ',' || $c eq ';' || $c eq '<';
+ $idx++;
+ }
-sub parse {
- my $pkg = shift;
- my @line = grep { defined $_} @_;
- my $line = join '', @line;
-
- local $_;
-
- my @phrase = ();
- my @comment = ();
- my @address = ();
- my @objs = ();
- my $depth = 0;
- my $idx = 0;
- my $tokens = _tokenise(@line);
- my $len = @$tokens;
- my $next = _find_next($idx,$tokens,$len);
-
- for( ; $idx < $len ; $idx++) {
- $_ = $tokens->[$idx];
-
- if(substr($_,0,1) eq "(") {
- push(@comment,$_);
- }
- elsif($_ eq '<') {
- $depth++;
- }
- elsif($_ eq '>') {
- $depth-- if $depth;
- }
- elsif($_ eq ',') {
- warn "Unmatched '<>' in $line" if($depth);
- my $o = _complete($pkg,\@phrase, \@address, \@comment);
- push(@objs, $o) if(defined $o);
- $depth = 0;
- $next = _find_next($idx+1,$tokens,$len);
- }
- elsif($depth) {
- push(@address,$_);
- }
- elsif($next eq "<") {
- push(@phrase,$_);
- }
- elsif( /\A[\Q.\@:;\E]\Z/ || !@address || $address[-1] =~ /\A[\Q.\@:;\E]\Z/) {
- push(@address,$_);
- }
- else {
- warn "Unmatched '<>' in $line" if($depth);
- my $o = _complete($pkg,\@phrase, \@address, \@comment);
- push(@objs, $o) if(defined $o);
- $depth = 0;
- push(@address,$_);
- }
- }
- @objs;
+ "";
}
-sub set_or_get {
- my $me = shift;
- my $i = shift;
- my $val = $me->[$i];
+sub _complete
+{ my ($class, $phrase, $address, $comment) = @_;
- $me->[$i] = shift if(@_);
+ @$phrase || @$comment || @$address
+ or return undef;
- $val;
+ my $o = $class->new(join(" ",@$phrase), join("",@$address), join(" ",@$comment));
+ @$phrase = @$address = @$comment = ();
+ $o;
}
+#------------
-sub phrase { set_or_get(shift,0,@_) }
-sub address { set_or_get(shift,1,@_) }
-sub comment { set_or_get(shift,2,@_) }
-
-
-sub format {
- my @fmts = ();
- my $me;
-
- foreach $me (@_) {
- my($phrase,$addr,$comment) = @{$me};
- my @tmp = ();
-
- if(defined $phrase && length($phrase)) {
- push(@tmp, $phrase);
- push(@tmp, "<" . $addr . ">") if(defined $addr && length($addr));
- }
- else {
- push(@tmp, $addr) if(defined $addr && length($addr));
- }
- if(defined($comment) && $comment =~ /\S/) {
- $comment =~ s/^\s*\(?/(/;
- $comment =~ s/\)?\s*$/)/;
- }
- push(@tmp, $comment) if(defined $comment && length($comment));
- push(@fmts, join(" ", @tmp)) if(scalar(@tmp));
- }
-
- return join(", ", @fmts);
+sub new(@)
+{ my $class = shift;
+ bless [@_], $class;
}
-sub name
-{
- my $me = shift;
- my $phrase = $me->phrase;
- my $addr = $me->address;
-
- $phrase = $me->comment unless(defined($phrase) && length($phrase));
-
- my $name = _extract_name($phrase);
-
- # first.last@domain address
- if($name eq '' && $addr =~ /([^\%\.\@_]+([\._][^\%\.\@_]+)+)[\@\%]/o)
- {
- ($name = $1) =~ s/[\._]+/ /go;
- $name = _extract_name($name);
- }
-
- if($name eq '' && $addr =~ m#/g=#oi)
- # X400 style address
- {
- my ($f) = $addr =~ m#g=([^/]*)#oi;
- my ($l) = $addr =~ m#s=([^/]*)#io;
-
- $name = _extract_name($f . " " . $l);
- }
-
- return length($name) ? $name : undef;
-}
+sub parse(@)
+{ my $class = shift;
+ my @line = grep {defined} @_;
+ my $line = join '', @line;
+ my (@phrase, @comment, @address, @objs);
+ my ($depth, $idx) = (0, 0);
-sub host {
- my $me = shift;
- my $addr = $me->address;
- my $i = rindex($addr,'@');
+ my $tokens = _tokenise @line;
+ my $len = @$tokens;
+ my $next = _find_next $idx, $tokens, $len;
- my $host = ($i >= 0) ? substr($addr,$i+1) : undef;
+ local $_;
+ for(my $idx = 0; $idx < $len; $idx++)
+ { $_ = $tokens->[$idx];
- return $host;
+ if(substr($_,0,1) eq '(') { push @comment, $_ }
+ elsif($_ eq '<') { $depth++ }
+ elsif($_ eq '>') { $depth-- if $depth }
+ elsif($_ eq ',' || $_ eq ';')
+ { warn "Unmatched '<>' in $line" if $depth;
+ my $o = $class->_complete(\@phrase, \@address, \@comment);
+ push @objs, $o if defined $o;
+ $depth = 0;
+ $next = _find_next $idx+1, $tokens, $len;
+ }
+ elsif($depth) { push @address, $_ }
+ elsif($next eq '<') { push @phrase, $_ }
+ elsif( /^[.\@:;]$/ || !@address || $address[-1] =~ /^[.\@:;]$/ )
+ { push @address, $_ }
+ else
+ { warn "Unmatched '<>' in $line" if $depth;
+ my $o = $class->_complete(\@phrase, \@address, \@comment);
+ push @objs, $o if defined $o;
+ $depth = 0;
+ push @address, $_;
+ }
+ }
+ @objs;
}
+#------------
-sub user {
- my $me = shift;
- my $addr = $me->address;
- my $i = index($addr,'@');
+sub phrase { shift->set_or_get(0, @_) }
+sub address { shift->set_or_get(1, @_) }
+sub comment { shift->set_or_get(2, @_) }
- my $user = ($i >= 0) ? substr($addr,0,$i) : $addr;
+sub set_or_get($)
+{ my ($self, $i) = (shift, shift);
+ @_ or return $self->[$i];
- return $user;
+ my $val = $self->[$i];
+ $self->[$i] = shift if @_;
+ $val;
}
-sub path {
- return ();
-}
+my $atext = '[\-\w !#$%&\'*+/=?^`{|}~]';
+sub format
+{ my @addrs;
+ foreach (@_)
+ { my ($phrase, $email, $comment) = @$_;
+ my @addr;
-sub canon {
- my $me = shift;
- return ($me->host, $me->user, $me->path);
-}
+ if(defined $phrase && length $phrase)
+ { push @addr
+ , $phrase =~ /^(?:\s*$atext\s*)+$/o ? $phrase
+ : $phrase =~ /(?<!\\)"/ ? $phrase
+ : qq("$phrase");
-1;
-
-
-__END__
-
-=head1 NAME
+ push @addr, "<$email>"
+ if defined $email && length $email;
+ }
+ elsif(defined $email && length $email)
+ { push @addr, $email;
+ }
-Mail::Address - Parse mail addresses
+ if(defined $comment && $comment =~ /\S/)
+ { $comment =~ s/^\s*\(?/(/;
+ $comment =~ s/\)?\s*$/)/;
+ }
-=head1 SYNOPSIS
+ push @addr, $comment
+ if defined $comment && length $comment;
- use Mail::Address;
-
- my @addrs = Mail::Address->parse($line);
-
- foreach $addr (@addrs) {
- print $addr->format,"\n";
+ push @addrs, join(" ", @addr)
+ if @addr;
}
-=head1 DESCRIPTION
-
-C<Mail::Address> extracts and manipulates RFC822 compilant email
-addresses. As well as being able to create C<Mail::Address> objects
-in the normal manner, C<Mail::Address> can extract addresses from
-the To and Cc lines found in an email message.
-
-=head1 CONSTRUCTORS
-
-=over 4
-
-=item new( PHRASE, ADDRESS, [ COMMENT ])
-
- Mail::Address->new("Perl5 Porters", "perl5-porters@africa.nicoh.com");
-
-Create a new C<Mail::Address> object which represents an address with the
-elements given. In a message these 3 elements would be seen like:
-
- PHRASE <ADDRESS> (COMMENT)
- ADDRESS (COMMENT)
-
-=item parse( LINE )
-
- Mail::Address->parse($line);
-
-Parse the given line a return a list of extracted C<Mail::Address> objects.
-The line would normally be one taken from a To,Cc or Bcc line in a message
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item phrase ()
-
-Return the phrase part of the object.
-
-=item address ()
-
-Return the address part of the object.
-
-=item comment ()
-
-Return the comment part of the object
-
-=item format ()
-
-Return a string representing the address in a suitable form to be placed
-on a To,Cc or Bcc line of a message
-
-=item name ()
-
-Using the information contained within the object attempt to identify what
-the person or groups name is
-
-=item host ()
-
-Return the address excluding the user id and '@'
-
-=item user ()
+ join ", ", @addrs;
+}
-Return the address excluding the '@' and the mail domain
+#------------
-=item path ()
+sub name
+{ my $self = shift;
+ my $phrase = $self->phrase;
+ my $addr = $self->address;
-Unimplemented yet but should return the UUCP path for the message
+ $phrase = $self->comment
+ unless defined $phrase && length $phrase;
-=item canon ()
+ my $name = $self->_extract_name($phrase);
-Unimplemented yet but should return the UUCP canon for the message
+ # first.last@domain address
+ if($name eq '' && $addr =~ /([^\%\.\@_]+([\._][^\%\.\@_]+)+)[\@\%]/)
+ { ($name = $1) =~ s/[\._]+/ /g;
+ $name = _extract_name $name;
+ }
-=back
+ if($name eq '' && $addr =~ m#/g=#i) # X400 style address
+ { my ($f) = $addr =~ m#g=([^/]*)#i;
+ my ($l) = $addr =~ m#s=([^/]*)#i;
+ $name = _extract_name "$f $l";
+ }
-=head1 AUTHOR
+ length $name ? $name : undef;
+}
-Graham Barr. Maintained by Mark Overmeer <mailtools@overmeer.net>
-=head1 COPYRIGHT
+sub host
+{ my $addr = shift->address || '';
+ my $i = rindex $addr, '@';
+ $i >= 0 ? substr($addr, $i+1) : undef;
+}
-Copyright (c) 1995-2001 Graham Barr. All rights reserved. This program is free
-software; you can redistribute it and/or modify it under the same terms
-as Perl itself.
-=cut
+sub user
+{ my $addr = shift->address || '';
+ my $i = rindex $addr, '@';
+ $i >= 0 ? substr($addr,0,$i) : $addr;
+}
+1;