mirror of
https://github.com/imapsync/imapsync.git
synced 2025-06-12 23:44:52 +02:00
1.945
This commit is contained in:
parent
0797e48248
commit
275436c5a0
126 changed files with 25270 additions and 4380 deletions
576
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/BodyStructure.pm
Normal file
576
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/BodyStructure.pm
Normal file
|
@ -0,0 +1,576 @@
|
|||
use warnings;
|
||||
use strict;
|
||||
|
||||
package Mail::IMAPClient::BodyStructure;
|
||||
use Mail::IMAPClient::BodyStructure::Parse;
|
||||
|
||||
# BUG?: old code used name "HEAD" instead of "HEADER", change?
|
||||
my $HEAD = "HEAD";
|
||||
|
||||
# my has file scope, not limited to package!
|
||||
my $parser = Mail::IMAPClient::BodyStructure::Parse->new
|
||||
or die "Cannot parse rules: $@\n"
|
||||
. "Try remaking Mail::IMAPClient::BodyStructure::Parse.\n";
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $bodystructure = shift;
|
||||
|
||||
my $self = $parser->start($bodystructure)
|
||||
or return undef;
|
||||
|
||||
$self->{_prefix} = "";
|
||||
$self->{_id} = exists $self->{bodystructure} ? $HEAD : 1;
|
||||
$self->{_top} = 1;
|
||||
|
||||
bless $self, ref($class) || $class;
|
||||
}
|
||||
|
||||
sub _get_thingy {
|
||||
my $thingy = shift;
|
||||
my $object = shift || ( ref $thingy ? $thingy : undef );
|
||||
|
||||
unless ( $object && ref $object ) {
|
||||
warn $@ = "No argument passed to $thingy method.";
|
||||
return undef;
|
||||
}
|
||||
|
||||
unless ( UNIVERSAL::isa( $object, 'HASH' ) && exists $object->{$thingy} ) {
|
||||
my $a = $thingy =~ /^[aeiou]/i ? 'an' : 'a';
|
||||
my $has = ref $object eq 'HASH' ? join( ", ", keys %$object ) : '';
|
||||
warn $@ =
|
||||
ref($object)
|
||||
. " $object does not have $a $thingy. "
|
||||
. ( $has ? "It has $has" : '' );
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $value = $object->{$thingy};
|
||||
$value =~ s/\\ ( [\\\(\)"\x0d\x0a] )/$1/gx;
|
||||
$value =~ s/^"(.*)"$/$1/;
|
||||
$value;
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
no strict 'refs';
|
||||
foreach my $datum (
|
||||
qw/ bodytype bodysubtype bodyparms bodydisp bodyid bodydesc bodyenc
|
||||
bodysize bodylang envelopestruct textlines /
|
||||
)
|
||||
{
|
||||
*$datum = sub { _get_thingy( $datum, @_ ) };
|
||||
}
|
||||
}
|
||||
|
||||
sub parts {
|
||||
my $self = shift;
|
||||
return wantarray ? @{ $self->{PartsList} } : $self->{PartsList}
|
||||
if exists $self->{PartsList};
|
||||
|
||||
my @parts;
|
||||
$self->{PartsList} = \@parts;
|
||||
|
||||
# BUG?: should this default to ($HEAD, TEXT)
|
||||
unless ( exists $self->{bodystructure} ) {
|
||||
$self->{PartsIndex}{1} = $self;
|
||||
@parts = ( $HEAD, 1 );
|
||||
return wantarray ? @parts : \@parts;
|
||||
}
|
||||
|
||||
foreach my $p ( $self->bodystructure ) {
|
||||
my $id = $p->id;
|
||||
push @parts, $id;
|
||||
$self->{PartsIndex}{$id} = $p;
|
||||
my $type = uc $p->bodytype || '';
|
||||
|
||||
push @parts, "$id.$HEAD"
|
||||
if $type eq 'MESSAGE';
|
||||
}
|
||||
|
||||
wantarray ? @parts : \@parts;
|
||||
}
|
||||
|
||||
sub bodystructure {
|
||||
my $self = shift;
|
||||
my $partno = 0;
|
||||
my @parts;
|
||||
|
||||
if ( $self->{_top} ) {
|
||||
$self->{_id} ||= $HEAD;
|
||||
$self->{_prefix} ||= $HEAD;
|
||||
$partno = 0;
|
||||
foreach my $b ( @{ $self->{bodystructure} } ) {
|
||||
$b->{_id} = ++$partno;
|
||||
$b->{_prefix} = $partno;
|
||||
push @parts, $b, $b->bodystructure;
|
||||
}
|
||||
return wantarray ? @parts : \@parts;
|
||||
}
|
||||
|
||||
my $prefix = $self->{_prefix} || "";
|
||||
$prefix =~ s/\.?$/./;
|
||||
|
||||
foreach my $p ( @{ $self->{bodystructure} } ) {
|
||||
$partno++;
|
||||
|
||||
# BUG?: old code didn't add .TEXT sections, should we skip these?
|
||||
# - This code needs to be generalised (maybe it belongs in parts()?)
|
||||
# - Should every message should have HEAD (actually MIME) and TEXT?
|
||||
# at least dovecot and iplanet appear to allow this even for
|
||||
# non-multipart sections
|
||||
my $pno = $partno;
|
||||
my $stype = $self->{bodytype} || "";
|
||||
my $ptype = $p->{bodytype} || "";
|
||||
|
||||
# a message and the multipart inside of it "collapse together"
|
||||
if ( $partno == 1 and $stype eq 'MESSAGE' and $ptype eq 'MULTIPART' ) {
|
||||
$pno = "TEXT";
|
||||
$p->{_prefix} = "$prefix";
|
||||
}
|
||||
else {
|
||||
$p->{_prefix} = "$prefix$partno";
|
||||
}
|
||||
$p->{_id} ||= "$prefix$pno";
|
||||
|
||||
push @parts, $p, $p->{bodystructure} ? $p->bodystructure : ();
|
||||
}
|
||||
|
||||
wantarray ? @parts : \@parts;
|
||||
}
|
||||
|
||||
sub id {
|
||||
my $self = shift;
|
||||
return $self->{_id}
|
||||
if exists $self->{_id};
|
||||
|
||||
return $HEAD
|
||||
if $self->{_top};
|
||||
|
||||
# BUG?: can this be removed? ... seems wrong
|
||||
if ( $self->{bodytype} eq 'MULTIPART' ) {
|
||||
my $p = $self->{_id} || $self->{_prefix};
|
||||
$p =~ s/\.$//;
|
||||
return $p;
|
||||
}
|
||||
else {
|
||||
return $self->{_id} ||= 1;
|
||||
}
|
||||
}
|
||||
|
||||
package Mail::IMAPClient::BodyStructure::Part;
|
||||
our @ISA = qw/Mail::IMAPClient::BodyStructure/;
|
||||
|
||||
package Mail::IMAPClient::BodyStructure::Envelope;
|
||||
our @ISA = qw/Mail::IMAPClient::BodyStructure/;
|
||||
|
||||
sub new {
|
||||
my ( $class, $envelope ) = @_;
|
||||
$parser->envelope($envelope);
|
||||
}
|
||||
|
||||
sub parse_string {
|
||||
my ( $class, $envelope ) = @_;
|
||||
$envelope = "(" . $envelope . ")" unless ( $envelope =~ /^\(/ );
|
||||
$parser->envelopestruct($envelope);
|
||||
}
|
||||
|
||||
sub from_addresses { shift->_addresses( from => 1 ) }
|
||||
sub sender_addresses { shift->_addresses( sender => 1 ) }
|
||||
sub replyto_addresses { shift->_addresses( replyto => 1 ) }
|
||||
sub to_addresses { shift->_addresses( to => 0 ) }
|
||||
sub cc_addresses { shift->_addresses( cc => 0 ) }
|
||||
sub bcc_addresses { shift->_addresses( bcc => 0 ) }
|
||||
|
||||
sub _addresses($$$) {
|
||||
my ( $self, $name, $isSender ) = @_;
|
||||
ref $self->{$name} eq 'ARRAY'
|
||||
or return ();
|
||||
|
||||
my @list;
|
||||
foreach ( @{ $self->{$name} } ) {
|
||||
my $pn = $_->personalname;
|
||||
my $name = $pn && $pn ne 'NIL' ? "$pn " : '';
|
||||
push @list, $name . '<' . $_->mailboxname . '@' . $_->hostname . '>';
|
||||
}
|
||||
|
||||
wantarray ? @list
|
||||
: $isSender ? $list[0]
|
||||
: \@list;
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
no strict 'refs';
|
||||
for my $datum (
|
||||
qw(subject inreplyto from messageid bcc date
|
||||
replyto to sender cc)
|
||||
)
|
||||
{
|
||||
*$datum = sub { @_ > 1 ? $_[0]->{$datum} = $_[1] : $_[0]->{$datum} }
|
||||
}
|
||||
}
|
||||
|
||||
package Mail::IMAPClient::BodyStructure::Address;
|
||||
our @ISA = qw/Mail::IMAPClient::BodyStructure/;
|
||||
|
||||
for my $datum (qw(personalname mailboxname hostname sourcename)) {
|
||||
no strict 'refs';
|
||||
*$datum = sub { shift->{$datum}; };
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mail::IMAPClient::BodyStructure - parse fetched results
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Mail::IMAPClient;
|
||||
use Mail::IMAPClient::BodyStructure;
|
||||
|
||||
my $imap = Mail::IMAPClient->new(
|
||||
Server => $server, User => $login, Password => $pass
|
||||
);
|
||||
|
||||
$imap->select("INBOX") or die "Could not select INBOX: $@\n";
|
||||
|
||||
my @recent = $imap->search("recent") or die "No recent msgs in INBOX\n";
|
||||
|
||||
foreach my $id (@recent) {
|
||||
my $bsdat = $imap->fetch( $id, "bodystructure" );
|
||||
my $bso = Mail::IMAPClient::BodyStructure->new( join("", $imap->History) );
|
||||
my $mime = $bso->bodytype . "/" . $bso->bodysubtype;
|
||||
my $parts = map( "\n\t" . $_, $bso->parts );
|
||||
print "Msg $id (Content-type: $mime) contains these parts:$parts\n";
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This extension will parse the result of an IMAP FETCH BODYSTRUCTURE
|
||||
command into a perl data structure. It also provides helper methods
|
||||
to help pull information out of the data structure.
|
||||
|
||||
This module requires Parse::RecDescent.
|
||||
|
||||
=head1 Class Methods
|
||||
|
||||
The following class method is available:
|
||||
|
||||
=head2 new
|
||||
|
||||
This class method is the constructor method for instantiating new
|
||||
Mail::IMAPClient::BodyStructure objects. The B<new> method accepts
|
||||
one argument, a string containing a server response to a FETCH
|
||||
BODYSTRUCTURE directive.
|
||||
|
||||
The module B<Mail::IMAPClient> provides the B<get_bodystructure>
|
||||
convenience method to simplify use of this module when starting with
|
||||
just a messages sequence number or unique ID (UID).
|
||||
|
||||
=head1 Object Methods
|
||||
|
||||
The following object methods are available:
|
||||
|
||||
=head2 bodytype
|
||||
|
||||
The B<bodytype> object method requires no arguments. It returns the
|
||||
bodytype for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodysubtype
|
||||
|
||||
The B<bodysubtype> object method requires no arguments. It returns the
|
||||
bodysubtype for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodyparms
|
||||
|
||||
The B<bodyparms> object method requires no arguments. It returns the
|
||||
bodyparms for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodydisp
|
||||
|
||||
The B<bodydisp> object method requires no arguments. It returns the
|
||||
bodydisp for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodyid
|
||||
|
||||
The B<bodyid> object method requires no arguments. It returns the
|
||||
bodyid for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodydesc
|
||||
|
||||
The B<bodydesc> object method requires no arguments. It returns the
|
||||
bodydesc for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodyenc
|
||||
|
||||
The B<bodyenc> object method requires no arguments. It returns the
|
||||
bodyenc for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodysize
|
||||
|
||||
The B<bodysize> object method requires no arguments. It returns the
|
||||
bodysize for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodylang
|
||||
|
||||
The B<bodylang> object method requires no arguments. It returns the
|
||||
bodylang for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 bodystructure
|
||||
|
||||
The B<bodystructure> object method requires no arguments. It returns
|
||||
the bodystructure for the message whose structure is described by the
|
||||
calling B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 envelopestruct
|
||||
|
||||
The B<envelopestruct> object method requires no arguments. It returns
|
||||
a B<Mail::IMAPClient::BodyStructure::Envelope> object for the message
|
||||
from the calling B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head2 textlines
|
||||
|
||||
The B<textlines> object method requires no arguments. It returns the
|
||||
textlines for the message whose structure is described by the calling
|
||||
B<Mail::IMAPClient::Bodystructure> object.
|
||||
|
||||
=head1 Mail::IMAPClient::BodyStructure::Envelope
|
||||
|
||||
The IMAP standard specifies that output from the IMAP B<FETCH
|
||||
ENVELOPE> command will be an RFC2060 envelope structure. It further
|
||||
specifies that output from the B<FETCH BODYSTRUCTURE> command may also
|
||||
contain embedded envelope structures (if, for example, a message's
|
||||
subparts contain one or more included messages). Objects belonging to
|
||||
B<Mail::IMAPClient::BodyStructure::Envelope> are Perl representations
|
||||
of these envelope structures, which is to say the nested parenthetical
|
||||
lists of RFC2060 translated into a Perl datastructure.
|
||||
|
||||
Note that all of the fields relate to the specific part to which they
|
||||
belong. In other words, output from a FETCH nnnn ENVELOPE command
|
||||
(or, in B<Mail::IMAPClient>, C<$imap->fetch($msgid,"ENVELOPE")> or
|
||||
C<my $env = $imap->get_envelope($msgid)>) are for the message, but
|
||||
fields from within a bodystructure relate to the message subpart and
|
||||
not the parent message.
|
||||
|
||||
An envelope structure's B<Mail::IMAPClient::BodyStructure::Envelope>
|
||||
representation is a hash of thingies that looks like this:
|
||||
|
||||
{
|
||||
subject => "subject",
|
||||
inreplyto => "reference_message_id",
|
||||
from => [ addressStruct1 ],
|
||||
messageid => "message_id",
|
||||
bcc => [ addressStruct1, addressStruct2 ],
|
||||
date => "Tue, 09 Jul 2002 14:15:53 -0400",
|
||||
replyto => [ adressStruct1, addressStruct2 ],
|
||||
to => [ adressStruct1, addressStruct2 ],
|
||||
sender => [ adressStruct1 ],
|
||||
cc => [ adressStruct1, addressStruct2 ],
|
||||
}
|
||||
|
||||
The B<...::Envelope> object also has methods for accessing data in the
|
||||
structure. They are:
|
||||
|
||||
=over 4
|
||||
|
||||
=item date
|
||||
|
||||
Returns the date of the message.
|
||||
|
||||
=item inreplyto
|
||||
|
||||
Returns the message id of the message to which this message is a reply.
|
||||
|
||||
=item subject
|
||||
|
||||
Returns the subject of the message.
|
||||
|
||||
=item messageid
|
||||
|
||||
Returns the message id of the message.
|
||||
|
||||
=back
|
||||
|
||||
You can also use the following methods to get addressing information.
|
||||
Each of these methods returns an array of
|
||||
B<Mail::IMAPClient::BodyStructure::Address> objects, which are perl
|
||||
data structures representing RFC2060 address structures. Some of
|
||||
these arrays would naturally contain one element (such as B<from>,
|
||||
which normally contains a single "From:" address); others will often
|
||||
contain more than one address. However, because RFC2060 defines all
|
||||
of these as "lists of address structures", they are all translated
|
||||
into arrays of B<...::Address> objects.
|
||||
|
||||
See the section on B<Mail::IMAPClient::BodyStructure::Address>, below,
|
||||
for alternate (and preferred) ways of accessing these data.
|
||||
|
||||
The methods available are:
|
||||
|
||||
=over 4
|
||||
|
||||
=item bcc
|
||||
|
||||
Returns an array of blind cc'ed recipients' address structures.
|
||||
(Don't expect much in here unless the message was sent from the
|
||||
mailbox you're poking around in, by the way.)
|
||||
|
||||
=item cc
|
||||
|
||||
Returns an array of cc'ed recipients' address structures.
|
||||
|
||||
=item from
|
||||
|
||||
Returns an array of "From:" address structures--usually just one.
|
||||
|
||||
=item replyto
|
||||
|
||||
Returns an array of "Reply-to:" address structures. Once again there
|
||||
is usually just one address in the list.
|
||||
|
||||
=item sender
|
||||
|
||||
Returns an array of senders' address structures--usually just one and
|
||||
usually the same as B<from>.
|
||||
|
||||
=item to
|
||||
|
||||
Returns an array of recipients' address structures.
|
||||
|
||||
=back
|
||||
|
||||
Each of the methods that returns a list of address structures (i.e. a
|
||||
list of B<Mail::IMAPClient::BodyStructure::Address> arrays) also has
|
||||
an analogous method that will return a list of E-Mail addresses
|
||||
instead. The addresses are in the format C<personalname
|
||||
E<lt>mailboxname@hostnameE<gt>> (see the section on
|
||||
B<Mail::IMAPClient::BodyStructure::Address>, below) However, if the
|
||||
personal name is 'NIL' then it is omitted from the address.
|
||||
|
||||
These methods are:
|
||||
|
||||
=over 4
|
||||
|
||||
=item bcc_addresses
|
||||
|
||||
Returns a list (or an array reference if called in scalar context) of
|
||||
blind cc'ed recipients' email addresses. (Don't expect much in here
|
||||
unless the message was sent from the mailbox you're poking around in,
|
||||
by the way.)
|
||||
|
||||
=item cc_addresses
|
||||
|
||||
Returns a list of cc'ed recipients' email addresses. If called in a
|
||||
scalar context it returns a reference to an array of email addresses.
|
||||
|
||||
=item from_addresses
|
||||
|
||||
Returns a list of "From:" email addresses. If called in a scalar
|
||||
context it returns the first email address in the list. (It's usually
|
||||
a list of just one anyway.)
|
||||
|
||||
=item replyto_addresses
|
||||
|
||||
Returns a list of "Reply-to:" email addresses. If called in a scalar
|
||||
context it returns the first email address in the list.
|
||||
|
||||
=item sender_addresses
|
||||
|
||||
Returns a list of senders' email addresses. If called in a scalar
|
||||
context it returns the first email address in the list.
|
||||
|
||||
=item to_addresses
|
||||
|
||||
Returns a list of recipients' email addresses. If called in a scalar
|
||||
context it returns a reference to an array of email addresses.
|
||||
|
||||
=back
|
||||
|
||||
Note that context affects the behavior of all of the above methods.
|
||||
|
||||
Those fields that will commonly contain multiple entries (i.e. they
|
||||
are recipients) will return an array reference when called in scalar
|
||||
context. You can use this behavior to optimize performance.
|
||||
|
||||
Those fields that will commonly contain just one address (the
|
||||
sender's) will return the first (and usually only) address. You can
|
||||
use this behavior to optimize your development time.
|
||||
|
||||
=head1 Addresses and the Mail::IMAPClient::BodyStructure::Address
|
||||
|
||||
Several components of an envelope structure are address structures.
|
||||
They are each parsed into their own object,
|
||||
B<Mail::IMAPClient::BodyStructure::Address>, which looks like this:
|
||||
|
||||
{
|
||||
mailboxname => 'somebody.special',
|
||||
hostname => 'somplace.weird.com'
|
||||
personalname => 'Somebody Special
|
||||
sourceroute => 'NIL'
|
||||
}
|
||||
|
||||
RFC2060 specifies that each address component of a bodystructure is a
|
||||
list of address structures, so B<Mail::IMAPClient::BodyStructure>
|
||||
parses each of these into an array of
|
||||
B<Mail::IMAPClient::BodyStructure::Address> objects.
|
||||
|
||||
Each of these objects has the following methods available to it:
|
||||
|
||||
=over 4
|
||||
|
||||
=item mailboxname
|
||||
|
||||
Returns the "mailboxname" portion of the address, which is the part to
|
||||
the left of the '@' sign.
|
||||
|
||||
=item hostname
|
||||
|
||||
Returns the "hostname" portion of the address, which is the part to
|
||||
the right of the '@' sign.
|
||||
|
||||
=item personalname
|
||||
|
||||
Returns the "personalname" portion of the address, which is the part
|
||||
of the address that's treated like a comment.
|
||||
|
||||
=item sourceroute
|
||||
|
||||
Returns the "sourceroute" portion of the address, which is typically "NIL".
|
||||
|
||||
=back
|
||||
|
||||
Taken together, the parts of an address structure form an address that
|
||||
will look something like this:
|
||||
|
||||
C<personalname E<lt>mailboxname@hostnameE<gt>>
|
||||
|
||||
Note that because the B<Mail::IMAPClient::BodyStructure::Address>
|
||||
objects come in arrays, it's generally easier to use the methods
|
||||
available to B<Mail::IMAPClient::BodyStructure::Envelope> to obtain
|
||||
all of the addresses in a particular array in one operation. These
|
||||
methods are provided, however, in case you'd rather do things the hard
|
||||
way. (And also because the aforementioned methods from
|
||||
B<Mail::IMAPClient::BodyStructure::Envelope> need them anyway.)
|
||||
|
||||
=cut
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Original author: David J. Kernen; Reworked by: Mark Overmeer;
|
||||
Maintained by Phil Pearl.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
perl(1), Mail::IMAPClient, Parse::RecDescent, and RFC2060.
|
||||
|
||||
=cut
|
|
@ -0,0 +1,189 @@
|
|||
# Directives
|
||||
# ( none)
|
||||
# Start-up Actions
|
||||
|
||||
{
|
||||
my $mibs = "Mail::IMAPClient::BodyStructure";
|
||||
my $subpartCount = 0;
|
||||
my $partCount = 0;
|
||||
|
||||
sub take_optional_items($$@)
|
||||
{ my ($r, $items) = (shift, shift);
|
||||
foreach (@_)
|
||||
{ my $opt = $_ .'(?)';
|
||||
exists $items->{$opt} or next;
|
||||
$r->{$_} = UNIVERSAL::isa($items->{$opt}, 'ARRAY')
|
||||
? $items->{$opt}[0] : $items->{$opt};
|
||||
}
|
||||
}
|
||||
|
||||
sub merge_hash($$)
|
||||
{ my $to = shift;
|
||||
my $from = shift or return;
|
||||
while( my($k,$v) = each %$from) { $to->{$k} = $v }
|
||||
}
|
||||
}
|
||||
|
||||
# Atoms
|
||||
|
||||
TEXT: /^"TEXT"|^TEXT/i { $return = "TEXT" }
|
||||
PLAIN: /^"PLAIN"|^PLAIN/i { $return = "PLAIN" }
|
||||
HTML: /"HTML"|HTML/i { $return = "HTML" }
|
||||
MESSAGE: /^"MESSAGE"|^MESSAGE/i { $return = "MESSAGE"}
|
||||
RFC822: /^"RFC822"|^RFC822/i { $return = "RFC822" }
|
||||
NIL: /^NIL/i { $return = "NIL" }
|
||||
RFCNONCOMPLY: /^\(\)/i { $return = "NIL" }
|
||||
NUMBER: /^(\d+)/ { $return = $item[1] }
|
||||
|
||||
# Strings:
|
||||
|
||||
SINGLE_QUOTED_STRING: "'" /(?:\\['\\]|[^'])*/ "'" { $return = $item{__PATTERN1__} }
|
||||
DOUBLE_QUOTED_STRING: '"' /(?:\\["\\]|[^"])*/ '"' { $return = $item{__PATTERN1__} }
|
||||
|
||||
BARESTRING: ...!/^[)('"]/ /^(?!\(|\))(?:\\ |\S)+/
|
||||
{ $return = $item{__PATTERN1__} }
|
||||
|
||||
STRING: DOUBLE_QUOTED_STRING | SINGLE_QUOTED_STRING | BARESTRING
|
||||
|
||||
STRINGS: "(" STRING(s) ")" { $return = $item{'STRING(s)'} }
|
||||
|
||||
textlines: NIL | NUMBER
|
||||
|
||||
rfc822message: MESSAGE RFC822 { $return = "MESSAGE RFC822" }
|
||||
|
||||
bodysubtype: PLAIN | HTML | NIL | STRING
|
||||
|
||||
key: STRING
|
||||
value: NIL | NUMBER | STRING | KVPAIRS
|
||||
|
||||
kvpair: ...!")" key value
|
||||
{ $return = { $item{key} => $item{value} } }
|
||||
|
||||
KVPAIRS: "(" kvpair(s) ")"
|
||||
{ $return = { map { (%$_) } @{$item{'kvpair(s)'}} } }
|
||||
|
||||
bodytype: STRING
|
||||
bodyparms: NIL | KVPAIRS
|
||||
bodydisp: NIL | KVPAIRS
|
||||
bodyid: ...!/[()]/ NIL | STRING
|
||||
bodydesc: ...!/[()]/ NIL | STRING
|
||||
bodysize: ...!/[()]/ NIL | NUMBER
|
||||
bodyenc: NIL | STRING | KVPAIRS
|
||||
bodyMD5: NIL | STRING
|
||||
bodylang: NIL | STRING | STRINGS
|
||||
bodyextra: NIL | STRING | STRINGS
|
||||
bodyloc: NIL | STRING
|
||||
|
||||
personalname: NIL | STRING
|
||||
sourceroute: NIL | STRING
|
||||
mailboxname: NIL | STRING
|
||||
hostname: NIL | STRING
|
||||
|
||||
addressstruct: "(" personalname sourceroute mailboxname hostname ")"
|
||||
{ bless { personalname => $item{personalname}
|
||||
, sourceroute => $item{sourceroute}
|
||||
, mailboxname => $item{mailboxname}
|
||||
, hostname => $item{hostname}
|
||||
}, 'Mail::IMAPClient::BodyStructure::Address';
|
||||
}
|
||||
|
||||
subject: NIL | STRING
|
||||
inreplyto: NIL | STRING
|
||||
messageid: NIL | STRING
|
||||
date: NIL | STRING
|
||||
|
||||
ADDRESSES: NIL | RFCNONCOMPLY
|
||||
| "(" addressstruct(s) ")" { $return = $item{'addressstruct(s)'} }
|
||||
|
||||
cc: ADDRESSES
|
||||
bcc: ADDRESSES
|
||||
from: ADDRESSES
|
||||
replyto: ADDRESSES
|
||||
sender: ADDRESSES
|
||||
to: ADDRESSES
|
||||
|
||||
envelopestruct: "(" date subject from sender replyto to cc
|
||||
bcc inreplyto messageid ")"
|
||||
{ $return = bless {}, "Mail::IMAPClient::BodyStructure::Envelope";
|
||||
$return->{$_} = $item{$_}
|
||||
for qw/date subject from sender replyto to cc/
|
||||
, qw/bcc inreplyto messageid/;
|
||||
1;
|
||||
}
|
||||
|
||||
basicfields: bodysubtype bodyparms(?) bodyid(?)
|
||||
bodydesc(?) bodyenc(?) bodysize(?)
|
||||
{ $return = { bodysubtype => $item{bodysubtype} };
|
||||
take_optional_items($return, \%item,
|
||||
qw/bodyparms bodyid bodydesc bodyenc bodysize/);
|
||||
1;
|
||||
}
|
||||
|
||||
textmessage: TEXT <commit> basicfields textlines(?) bodyMD5(?)
|
||||
bodydisp(?) bodylang(?) bodyextra(?)
|
||||
{
|
||||
$return = $item{basicfields} || {};
|
||||
$return->{bodytype} = 'TEXT';
|
||||
take_optional_items($return, \%item
|
||||
, qw/textlines bodyMD5 bodydisp bodylang bodyextra/);
|
||||
1;
|
||||
}
|
||||
|
||||
othertypemessage: bodytype basicfields bodyMD5(?) bodydisp(?)
|
||||
bodylang(?) bodyextra(?)
|
||||
{ $return = { bodytype => $item{bodytype} };
|
||||
take_optional_items($return, \%item
|
||||
, qw/bodyMD5 bodydisp bodylang bodyextra/ );
|
||||
merge_hash($return, $item{basicfields});
|
||||
1;
|
||||
}
|
||||
|
||||
nestedmessage: rfc822message <commit> bodyparms bodyid bodydesc bodyenc
|
||||
# bodysize envelopestruct bodystructure textlines
|
||||
bodysize envelopestruct(?) bodystructure(?) textlines(?)
|
||||
bodyMD5(?) bodydisp(?) bodylang(?) bodyextra(?)
|
||||
{
|
||||
$return = {};
|
||||
$return->{$_} = $item{$_}
|
||||
for qw/bodyparms bodyid bodydesc bodyenc bodysize/;
|
||||
# envelopestruct bodystructure textlines/;
|
||||
|
||||
take_optional_items($return, \%item
|
||||
, qw/envelopestruct bodystructure textlines/
|
||||
, qw/bodyMD5 bodydisp bodylang bodyextra/);
|
||||
|
||||
merge_hash($return, $item{bodystructure}[0]);
|
||||
merge_hash($return, $item{basicfields});
|
||||
$return->{bodytype} = "MESSAGE" ;
|
||||
$return->{bodysubtype} = "RFC822" ;
|
||||
1;
|
||||
}
|
||||
|
||||
multipart: subpart(s) <commit> bodysubtype
|
||||
bodyparms(?) bodydisp(?) bodylang(?) bodyloc(?) bodyextra(?)
|
||||
<defer: $subpartCount = 0>
|
||||
{ $return =
|
||||
{ bodysubtype => $item{bodysubtype}
|
||||
, bodytype => 'MULTIPART'
|
||||
, bodystructure => $item{'subpart(s)'}
|
||||
};
|
||||
take_optional_items($return, \%item
|
||||
, qw/bodyparms bodydisp bodylang bodyloc bodyextra/);
|
||||
1;
|
||||
}
|
||||
|
||||
subpart: "(" part ")" {$return = $item{part}} <defer: ++$subpartCount;>
|
||||
|
||||
part: multipart { $return = bless $item{multipart}, $mibs }
|
||||
| textmessage { $return = bless $item{textmessage}, $mibs }
|
||||
| nestedmessage { $return = bless $item{nestedmessage}, $mibs }
|
||||
| othertypemessage { $return = bless $item{othertypemessage}, $mibs }
|
||||
|
||||
bodystructure: "(" part(s) ")"
|
||||
{ $return = $item{'part(s)'} }
|
||||
|
||||
start: /.*?\(.*?BODYSTRUCTURE \(/i part(1) /\).*\)\r?\n?/
|
||||
{ $return = $item{'part(1)'}[0] }
|
||||
|
||||
envelope: /.*?\(.*?ENVELOPE/ envelopestruct /.*\)/
|
||||
{ $return = $item{envelopestruct} }
|
17063
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/BodyStructure/Parse.pm
Normal file
17063
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/BodyStructure/Parse.pm
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,15 @@
|
|||
=head1 NAME
|
||||
|
||||
Mail::IMAPClient::BodyStructure::Parse - used internally by Mail::IMAPClient::BodyStructure
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used internally by L<Mail::IMAPClient::BodyStructure>
|
||||
and is generated using L<Parse::RecDescent>. It is not meant to be used
|
||||
directly by other scripts nor is there much point in debugging it.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
This module is used internally by L<Mail::IMAPClient::BodyStructure>
|
||||
and is not meant to be used or called directly from applications. So
|
||||
don't do that.
|
280
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/MessageSet.pm
Normal file
280
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/MessageSet.pm
Normal file
|
@ -0,0 +1,280 @@
|
|||
use warnings;
|
||||
use strict;
|
||||
|
||||
package Mail::IMAPClient::MessageSet;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mail::IMAPClient::MessageSet - ranges of message sequence numbers
|
||||
|
||||
=cut
|
||||
|
||||
use overload
|
||||
'""' => "str"
|
||||
, '.=' => sub {$_[0]->cat($_[1])}
|
||||
, '+=' => sub {$_[0]->cat($_[1])}
|
||||
, '-=' => sub {$_[0]->rem($_[1])}
|
||||
, '@{}' => "unfold"
|
||||
, fallback => 1;
|
||||
|
||||
sub new
|
||||
{ my $class = shift;
|
||||
my $range = $class->range(@_);
|
||||
bless \$range, $class;
|
||||
}
|
||||
|
||||
sub str { overload::StrVal( ${$_[0]} ) }
|
||||
|
||||
sub _unfold_range($)
|
||||
# { my $x = shift; return if $x =~ m/[^0-9,:]$/; $x =~ s/\:/../g; eval $x; }
|
||||
{ map { /(\d+)\s*\:\s*(\d+)/ ? ($1..$2) : $_ }
|
||||
split /\,/, shift;
|
||||
}
|
||||
|
||||
sub rem
|
||||
{ my $self = shift;
|
||||
my %delete = map { ($_ => 1) } map { _unfold_range $_ } @_;
|
||||
$$self = $self->range(grep {not $delete{$_}} $self->unfold);
|
||||
$self;
|
||||
}
|
||||
|
||||
sub cat
|
||||
{ my $self = shift;
|
||||
$$self = $self->range($$self, @_);
|
||||
$self;
|
||||
}
|
||||
|
||||
sub range
|
||||
{ my $self = shift;
|
||||
|
||||
my @msgs;
|
||||
foreach my $m (@_)
|
||||
{ defined $m && length $m
|
||||
or next;
|
||||
|
||||
foreach my $mm (ref $m eq 'ARRAY' ? @$m : $m)
|
||||
{ push @msgs, _unfold_range $mm;
|
||||
}
|
||||
}
|
||||
|
||||
@msgs
|
||||
or return undef;
|
||||
|
||||
@msgs = sort {$a <=> $b} @msgs;
|
||||
my $low = my $high = shift @msgs;
|
||||
|
||||
my @ranges;
|
||||
foreach my $m (@msgs)
|
||||
{ next if $m == $high; # double
|
||||
|
||||
if($m == $high + 1) { $high = $m }
|
||||
else
|
||||
{ push @ranges, $low == $high ? $low : "$low:$high";
|
||||
$low = $high = $m;
|
||||
}
|
||||
}
|
||||
|
||||
push @ranges, $low == $high ? $low : "$low:$high" ;
|
||||
join ",", @ranges;
|
||||
}
|
||||
|
||||
sub unfold
|
||||
{ my $self = shift;
|
||||
wantarray ? ( _unfold_range $$self ) : [ _unfold_range $$self ];
|
||||
}
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my @msgs = $imap->search("SUBJECT","Virus"); # returns 1,3,4,5,6,9,10
|
||||
my $msgset = Mail::IMAPClient::MessageSet->new(@msgs);
|
||||
print $msgset; # prints "1,3:6,9:10"
|
||||
|
||||
# add message 14 to the set:
|
||||
$msgset += 14;
|
||||
print $msgset; # prints "1,3:6,9:10,14"
|
||||
|
||||
# add messages 16,17,18,19, and 20 to the set:
|
||||
$msgset .= "16,17,18:20";
|
||||
print $msgset; # prints "1,3:6,9:10,14,16:20"
|
||||
|
||||
# Hey, I didn't really want message 17 in there; let's take it out:
|
||||
$msgset -= 17;
|
||||
print $msgset; # prints "1,3:6,9:10,14,16,18:20"
|
||||
|
||||
# Now let's iterate over each message:
|
||||
for my $msg (@$msgset)
|
||||
{ print "$msg\n"; # Prints: "1\n3\n4\n5\n6..16\n18\n19\n20\n"
|
||||
}
|
||||
print join("\n", @$msgset)."\n"; # same simpler
|
||||
local $" = "\n"; print "@$msgset\n"; # even more simple
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The B<Mail::IMAPClient::MessageSet> module is designed to make life easier
|
||||
for programmers who need to manipulate potentially large sets of IMAP
|
||||
message UID's or sequence numbers.
|
||||
|
||||
This module presents an object-oriented interface into handling your
|
||||
message sets. The object reference returned by the L<new> method is an
|
||||
overloaded reference to a scalar variable that contains the message set's
|
||||
compact RFC2060 representation. The object is overloaded so that using
|
||||
it like a string returns this compact message set representation. You
|
||||
can also add messages to the set (using either a '.=' operator or a '+='
|
||||
operator) or remove messages (with the '-=' operator). And if you use
|
||||
it as an array reference, it will humor you and act like one by calling
|
||||
L<unfold> for you.
|
||||
|
||||
RFC2060 specifies that multiple messages can be provided to certain IMAP
|
||||
commands by separating them with commas. For example, "1,2,3,4,5" would
|
||||
specify messages 1, 2, 3, 4, and (you guessed it!) 5. However, if you are
|
||||
performing an operation on lots of messages, this string can get quite long.
|
||||
So long that it may slow down your transaction, and perhaps even cause the
|
||||
server to reject it. So RFC2060 also permits you to specify a range of
|
||||
messages, so that messages 1, 2, 3, 4 and 5 can also be specified as
|
||||
"1:5".
|
||||
|
||||
This is where B<Mail::IMAPClient::MessageSet> comes in. It will convert
|
||||
your message set into the shortest correct syntax. This could potentially
|
||||
save you tons of network I/O, as in the case where you want to fetch the
|
||||
flags for all messages in a 10000 message folder, where the messages
|
||||
are all numbered sequentially. Delimited as commas, and making the
|
||||
best-case assumption that the first message is message "1", it would take
|
||||
48893 bytes to specify the whole message set using the comma-delimited
|
||||
method. To specify it as a range, it takes just seven bytes (1:10000).
|
||||
|
||||
Note that the L<Mail::IMAPClient> B<Range> method can be used as
|
||||
a short-cut to specifying C<Mail::IMAPClient::MessageSet-E<gt>new(@etc)>.)
|
||||
|
||||
=head1 CLASS METHODS
|
||||
|
||||
The only class method you need to worry about is B<new>. And if you create
|
||||
your B<Mail::IMAPClient::MessageSet> objects via L<Mail::IMAPClient>'s
|
||||
B<Range> method then you don't even need to worry about B<new>.
|
||||
|
||||
=head2 new
|
||||
|
||||
Example:
|
||||
|
||||
my $msgset = Mail::IMAPClient::MessageSet->new(@msgs);
|
||||
|
||||
The B<new> method requires at least one argument. That argument can be
|
||||
either a message, a comma-separated list of messages, a colon-separated
|
||||
range of messages, or a combination of comma-separated messages and
|
||||
colon-separated ranges. It can also be a reference to an array of messages,
|
||||
comma-separated message lists, and colon separated ranges.
|
||||
|
||||
If more then one argument is supplied to B<new>, then those arguments should
|
||||
be more message numbers, lists, and ranges (or references to arrays of them)
|
||||
just as in the first argument.
|
||||
|
||||
The message numbers passed to B<new> can really be any kind of number at
|
||||
all but to be useful in a L<Mail::IMAPClient> session they should be either
|
||||
message UID's (if your I<Uid> parameter is true) or message sequence numbers.
|
||||
|
||||
The B<new> method will return a reference to a B<Mail::IMAPClient::MessageSet>
|
||||
object. That object, when double quoted, will act just like a string whose
|
||||
value is the message set expressed in the shortest possible way, with the
|
||||
message numbers sorted in ascending order and with duplicates removed.
|
||||
|
||||
=head1 OBJECT METHODS
|
||||
|
||||
The only object method currently available to a B<Mail::IMAPClient::MessageSet>
|
||||
object is the L<unfold> method.
|
||||
|
||||
=head2 unfold
|
||||
|
||||
Example:
|
||||
|
||||
my $msgset = $imap->Range( $imap->messages ) ;
|
||||
my @all_messages = $msgset->unfold;
|
||||
|
||||
The B<unfold> method returns an array of messages that belong to the
|
||||
message set. If called in a scalar context it returns a reference to the
|
||||
array instead.
|
||||
|
||||
=head1 OVERRIDDEN OPERATIONS
|
||||
|
||||
B<Mail::IMAPClient::MessageSet> overrides a number of operators in order
|
||||
to make manipulating your message sets easier. The overridden operations are:
|
||||
|
||||
=head2 stringify
|
||||
|
||||
Attempts to stringify a B<Mail::IMAPClient::MessageSet> object will result in
|
||||
the compact message specification being returned, which is almost certainly
|
||||
what you will want.
|
||||
|
||||
=head2 Auto-increment
|
||||
|
||||
Attempts to autoincrement a B<Mail::IMAPClient::MessageSet> object will
|
||||
result in a message (or messages) being added to the object's message set.
|
||||
|
||||
Example:
|
||||
|
||||
$msgset += 34;
|
||||
# Message #34 is now in the message set
|
||||
|
||||
=head2 Concatenate
|
||||
|
||||
Attempts to concatenate to a B<Mail::IMAPClient::MessageSet> object will
|
||||
result in a message (or messages) being added to the object's message set.
|
||||
|
||||
Example:
|
||||
|
||||
$msgset .= "34,35,36,40:45";
|
||||
# Messages 34,35,36,40,41,42,43,44,and 45 are now in the message set
|
||||
|
||||
The C<.=> operator and the C<+=> operator can be used interchangeably, but
|
||||
as you can see by looking at the examples there are times when use of one
|
||||
has an aesthetic advantage over use of the other.
|
||||
|
||||
=head2 Autodecrement
|
||||
|
||||
Attempts to autodecrement a B<Mail::IMAPClient::MessageSet> object will
|
||||
result in a message being removed from the object's message set.
|
||||
|
||||
Examples:
|
||||
|
||||
$msgset -= 34;
|
||||
# Message #34 is no longer in the message set
|
||||
$msgset -= "1:10";
|
||||
# Messages 1 through 10 are no longer in the message set
|
||||
|
||||
If you attempt to remove a message that was not in the original message set
|
||||
then your resulting message set will be the same as the original, only more
|
||||
expensive. However, if you attempt to remove several messages from the message
|
||||
set and some of those messages were in the message set and some were not,
|
||||
the additional overhead of checking for the messages that were not there
|
||||
is negligible. In either case you get back the message set you want regardless
|
||||
of whether it was already like that or not.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
David J. Kernen
|
||||
The Kernen Consulting Group, Inc
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 1999, 2000, 2001, 2002 The Kernen Group, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of either:
|
||||
|
||||
=over 4
|
||||
|
||||
=item a) the "Artistic License" which comes with this Kit, or
|
||||
|
||||
=item b) the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 1, or (at your option) any later version.
|
||||
|
||||
=back
|
||||
|
||||
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 either the GNU
|
||||
General Public License or the Artistic License for more details. All your
|
||||
base are belong to us.
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
18
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.grammar
Normal file
18
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.grammar
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Atoms:
|
||||
|
||||
NUMBER: /\d+/
|
||||
|
||||
# Rules:
|
||||
|
||||
threadmember: NUMBER { $return = $item{NUMBER} ; } |
|
||||
thread { $return = $item{thread} ; }
|
||||
|
||||
thread: "(" threadmember(s) ")"
|
||||
{
|
||||
$return = $item{'threadmember(s)'}||undef;
|
||||
}
|
||||
|
||||
# Start:
|
||||
start: /^\* THREAD /i thread(s?) {
|
||||
$return=$item{'thread(s?)'}||undef;
|
||||
}
|
1039
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.pm
Normal file
1039
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.pm
Normal file
File diff suppressed because it is too large
Load diff
14
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.pod
Normal file
14
W/Mail-IMAPClient-3.42/lib/Mail/IMAPClient/Thread.pod
Normal file
|
@ -0,0 +1,14 @@
|
|||
=head1 NAME
|
||||
|
||||
Mail::IMAPClient::Thread - used internally by Mail::IMAPClient->thread
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used internally by L<Mail::IMAPClient> and is
|
||||
generated using L<Parse::RecDescent>. It is not meant to be used directly by
|
||||
other scripts nor is there much point in debugging it.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
This module is used internally by L<Mail::IMAPClient> and is not meant to
|
||||
be used or called directly from applications. So don't do that.
|
Loading…
Add table
Add a link
Reference in a new issue