Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to get some headers from an email message using an imap module but it is getting complicated. I am trying to work with a hash reference in which the keys are the header field names and the values are references to arrays of values. This is what the documentation says. I managed to capture those headers in arrays, but there is always only one element where all the info is stored. It does not matter with 'From' or 'Subject', but with 'To' it is unwanted because there is often more then one of those.So iam trying to get to this array of arrays element I solved it with splitting the string and removing leading and trailing spaces, but it's not fantastic. Could someone have a look at my script and give me some advice on how to proceed? I'll show you what i got so far:

use strict; use warnings; use Mail::IMAPClient; my $imap = Mail::IMAPClient->new( Server => 'imap.gmail.com', User => 'jim@gmail.com', Password => '*******', Ssl => 1, Debug => 0, ); die "failed to instantiate $@." unless defined $imap; $imap->select('INBOX'); my $msgcount = $imap->message_count(); print "$msgcount number of measages in your mailbox\n\n"; my @messages = $imap->messages; pop(@messages); my $msgid = pop(@messages); my $text = $imap->bodypart_string($msgid,1); my $hashref = $imap->parse_headers( $msgid, "Subject","To","From"); my @sender = @{%$hashref{"From"}}; my @recs = @{%$hashref{"To"}}; my @subject = @{%$hashref{"Subject"}}; my @rec = split(/,/, $recs[0]); print "From: $sender[0]\n"; print "Subject: $subject[0]\n\n"; foreach(@rec){ $_=~ s/^\s+|\s+$//g; print "To: $_\n"; } print $text; $imap->logout();

Replies are listed 'Best First'.
Re: getting headers from essage
by LanX (Saint) on Dec 16, 2025 at 19:38 UTC
    this looks very very wrong

    @{%$hashref{"To"}};

    Could you please point me to the documentation explaining what you are expected to do here?

    FWIW:

    Dereferencing a hashref is either done with

    $hashref->{"To"}

    Or alternatively

    $$hashref{"To"}

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      If it's so wrong, then why is it working?</P

        A fluke.

        You're using a key-value hash slice, which is used to obtain a list of key-value pairs from a hash.

        $ perl -e' use v5.36; my %h = ( a => 1, b => 2, c => 3, d => 4 ); say for %h{qw( a b )}; ' a 1 b 2

        That's not possible in scalar context. In scalar context, it returns the value of the last key.

        $ perl -e' use v5.36; my %h = ( a => 1, b => 2, c => 3, d => 4 ); say scalar( %h{qw( a b )} ); ' 2

        In other words, it performs a simple hash lookup in scalar context. If you want to perform a hash lookup, it's much clearer to use a hash lookup instead of key-value hash slice.

        $ perl -e' use v5.36; my %h = ( a => 1, b => 2, c => 3, d => 4 ); say $h{ b }; ' 2

        In context, you used @{ %$hashref{ "To" } } aka @{ %{ $hashref }{ "To" } } aka @{ $hashref->%{ "To" } } aka $hashref->%{ "To" }->@* when it would have been clearer to use @{ $$hashref{ To } } aka @{ ${ $hashref }{ To } } aka @{ $hashref->{ To } } aka $hashref->{ To }->@*. There's already enough ways of doing this that it's easy to say it's wrong to add another level of complexity.


        Update: s/hash slice/key-value hash slice/g, as per LanX's reply.

Re: getting headers from essage
by ikegami (Patriarch) on Dec 16, 2025 at 20:41 UTC

    You are confused why you are getting all the recipients in one string instead of getting an array of recipients.

    That's because the function returns headers, not recipients.

    Getting only one To header is normal. A message may have zero or one To, but not more.[1] But that doesn't mean the message can only have one recipient. A To header can identify more than one recipient. If you want a list of the individual recipients, you will need to extract them from the To header yourself. IMAP provides you the headers you request, but it's up to you to parse their values to extract information from them.

    So everything is working as it should. And parsing the string received is the correct approach.

    There are three issues with your code:

    • You don't handle the lack of a Subject field or the lack of a To field.

    • You are parsing the header incorrectly. Your approach of splitting on commas would mishandle the following valid value for a To header:

      "Doe, John" <john.doe@example.org>, "Roe, Jane" <jane.roe@example.org>
    • As mentioned elsewhere, the odd use of key-value hash slices instead of hash lookups is misleading and unclear.


    1. From RFC 5322,

      +----------------+--------+------------+----------------------------+ | Field | Min | Max number | Notes | | | number | | | +----------------+--------+------------+----------------------------+ | trace | 0 | unlimited | Block prepended - see | | | | | 3.6.7 | | resent-date | 0* | unlimited* | One per block, required if | | | | | other resent fields are | | | | | present - see 3.6.6 | | resent-from | 0 | unlimited* | One per block - see 3.6.6 | | resent-sender | 0* | unlimited* | One per block, MUST occur | | | | | with multi-address | | | | | resent-from - see 3.6.6 | | resent-to | 0 | unlimited* | One per block - see 3.6.6 | | resent-cc | 0 | unlimited* | One per block - see 3.6.6 | | resent-bcc | 0 | unlimited* | One per block - see 3.6.6 | | resent-msg-id | 0 | unlimited* | One per block - see 3.6.6 | | orig-date | 1 | 1 | | | from | 1 | 1 | See sender and 3.6.2 | | sender | 0* | 1 | MUST occur with | | | | | multi-address from - see | | | | | 3.6.2 | | reply-to | 0 | 1 | | | to | 0 | 1 | | | cc | 0 | 1 | | | bcc | 0 | 1 | | | message-id | 0* | 1 | SHOULD be present - see | | | | | 3.6.4 | | in-reply-to | 0* | 1 | SHOULD occur in some | | | | | replies - see 3.6.4 | | references | 0* | 1 | SHOULD occur in some | | | | | replies - see 3.6.4 | | subject | 0 | 1 | | | comments | 0 | unlimited | | | keywords | 0 | unlimited | | | optional-field | 0 | unlimited | | +----------------+--------+------------+----------------------------+
Re: getting headers from essage
by LanX (Saint) on Dec 16, 2025 at 21:16 UTC
    <UPDATE

    NEVER MIND

    Didn't see ikegami's explanation before, but it makes sense!

    Tho I wouldn't be surprised if multiple "To:" headers were indeed allowed.

    UPDATE>

    > So iam trying to get to this array of arrays element I solved it with splitting the string and removing leading and trailing spaces, but it's not fantastic

    What you are describing is not in line with the documentation of parse_header()

    It returns a hash reference in which the keys are the header field names (without the colon) and the values are references to arrays of values.

    So please show us a dumps of

    $$hashref{To}

    and

    @{$$hashref{To}}

    in order to tell what's going on here.

    Maybe this module's documentation is faulty or the mail is malformed or ...

    Anyway using hashslices inside a deref is begging for trouble. Please change your style.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      It will have the form

      my $hashref = { From => [ 'Mark Smith <mark.smith@example.org>', ], To => [ join( ', ', '"Doe, John" <john.doe@example.org>', '"Roe, Jane" <jane.roe@example.org>', ), ], ... };
        Thanks, actually it can get much more complicated tho

        https://en.wikipedia.org/wiki/Email_address

        I think there should° be a module dedicated to parse a list of email addresses.

        Doing this manually smells like trying to parse HTML with a regex.

        UPDATE

        °) Yep, see Email::Address on CPAN (unexpected name ;-)

        my @addrs = Email::Address->parse( q[me@local, Casey <me@local>, "Casey" <me@local> (West)] );

        and from the description

        ACHTUNG! Email isn't easy (if even possible) to parse with a regex, at least if you're on a perl prior to 5.10.0. Providing regular expressions for use by other programs isn't a great idea,

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: getting headers from essage
by Anonymous Monk on Dec 17, 2025 at 12:16 UTC

    Ty for taking the time to help me. I have changed the code as you told and i have read some documentation on references. I looked into it years ago but i forgot most of it. When i got some results i assumed i was on the right track, but i clearly wasn't.