3
votes

After hours of searching, reading, and clawing for an answer, I come here to beg for help:

I am trying to write a client to read my Gmail with the Perl module Mail::IMAPClient. Everything so far is functional, and it works great, but when I try to get the number of emails in my "INBOX" folder, it doesn't give the correct number:

# initialize the IMAP object
$imap = Mail::IMAPClient->new ( Server => 'imap.gmail.com',
                                User => $username,
                                Password => $password,
                                Port => 993,
                                Ssl => 1,
                                Uid => 1 )
or die "Could not connect to server, terminating...\n";

# find which folder to read from
print "Mailboxes: ".  join(", ", $imap->folders) . "\n";
print "Folder to use: ";
chomp (my $folder = <STDIN>);
$imap->select($folder) or die "select() failed, terminating...\n";

# get message IDs and number of messages
my @msgIDs = $imap->search("ALL");
print scalar(@msgIDs) . " message(s) found in mailbox.\n";

When reading from my "INBOX" folder, assigning @msgIDs to the commands:

$imap->search("ALL");
$imap->messages;
$imap->message_count;

All result in the same number being said to be the number of messages (1194, to be exact) in the inbox, when there are actually 1149. After the number of messages is printed, the program continues with asking the user how many "headers" of recent messages they want to see (this means that if I put in "5", I would see the "Subject" and "From" header fields from the five newest messages). This code comes right after the previously shown code:

# get number of messages to read (so the entire inbox isn't looked at)
print "Read how many? ";
chomp (my $read_num = <STDIN>);

# read the original number of headers requested
&read_more (scalar(@msgIDs), $read_num);

The &read_more() subroutine works with one or two arguments, but here's the two argument version:

if (@_ == 2) {
        # if an empty string was passed
        if ( $_[1] eq "" ) {
                $_[1] = 0;
        }

        # print $_[1] headers behind $_[0]
        foreach ( ($_[0]-$_[1])..($_[0]-1) ) {

                my $from = $imap->get_header ($_, "from");
                my $subject = $imap->get_header ($_, "subject");

                (printf "%d: %s\n", $_, $from);
                (printf "%d: %s\n\n", $_, $subject);
         }
}

So, if I called &read_more(1000, 5), I would see "Subject" and "From" header fields for message IDs 990-999. So when I call &read_more(scalar(@msgIDs), $read_num), I intend to see the header fields for the $read_num latest messages. Instead, I DO NOT see any header fields for my 9 latest messages, even though I am able to read them perfectly fine in the program (I'm not showing the code for that; it would complicate things). The number of messages found doesn't change. If I received one new message, then I wouldn't be able to see the 10 latest messages. The client is stuck at message ID 1193. I already configured my Gmail settings to allow IMAP.

Is this an error in my code, or is it a problem with my Gmail configuration, or something else?

2

2 Answers

2
votes

you consider @msgID as a sequence starting with 0 and ending with message_count-1. This don't need to be the case. For instance I have currently one message but the msgID of this single message is 6. Therefore you should use the msgIDs given by search and not assume just a simple sequence.

Edit: the code uses Uid =>1 in the constructor, so search returns UIDs and get_header expectets UIDs. Changing it to Uid => 0 makes it work with sequence numbers instead.

0
votes

Fixed it myself. Okay, this requires a bit of faulty logic (this program is for personal use, anyway, so that doesn't matter).
If a email doesn't have a "From" header field in its header (that is, if a email is found without an author), than it isn't an email. So, a subroutine that can count emails until an empty "From" header field is found would correctly count the number of emails in the Inbox:

# the only argument is the IMAP object, that allows communication
sub message_num {

        # there's always going to be at least this many messages, so guess here
        my @guess = $_[0]->search("ALL");

        # get the guess in scalar format so we can count with it
        my $i = scalar (@guess);

        # while the "from" headers are defined
        while ( defined ($_[0]->get_header ($i, "from")) ) {
                # count the messages past what the guess said
                $i++;
        }

        # return that count
        $i;
}

So, calling &message_num ($imap) in my code gives me a usable number of messages. It's incorrect, but it allows me to see all of my recent messages, which is what I wanted.