File Coverage

blib/lib/GMail/Checker.pm
Criterion Covered Total %
statement 31 283 10.9
branch 3 116 2.5
condition 2 9 22.2
subroutine 7 21 33.3
pod 10 16 62.5
total 53 445 11.9


line stmt bran cond sub pod time code
1             package GMail::Checker;
2              
3             # Perl interface for a gmail wrapper
4             # Allows you to check new mails, retrieving new mails and information about them
5              
6             # $Id: Checker.pm,v 1.04 2004/12/13 22:52:17 sacred Exp $
7              
8 1     1   5734 use strict;
  1         10  
  1         48  
9 1     1   7489 use IO::Socket::SSL;
  1         211578  
  1         11  
10 1     1   230 use Carp;
  1         7  
  1         70  
11 1     1   6 use vars qw($VERSION);
  1         2  
  1         4294  
12              
13             $VERSION = 1.04;
14              
15 0     0 0 0 sub version { sprintf("%f", $VERSION); }
16              
17             sub new {
18 1     1 1 20 my $self = {};
19 1         4 my $proto = shift;
20 1         2 my %params = @_;
21 1         2 $self->{SOCK} = undef;
22 1         2 $self->{NBMSG} = 0;
23 1         3 $self->{USERNAME} = '';
24 1         3 $self->{PASSWORD} = '';
25 1   33     7 my $class = ref($proto) || $proto;
26 1         2 bless($self,$class);
27 1 50 33     4 $self->login($params{"USERNAME"}, $params{"PASSWORD"}) if ((exists $params{"USERNAME"}) and (exists $params{"PASSWORD"}));
28 1         5 return $self;
29             }
30              
31             sub DESTROY {
32 1     1   636 my $self = shift;
33 1 50       93 if (defined $self->{SOCK}) { $self->{SOCK}->close(); }
  0         0  
34             }
35              
36             sub getsize { # Formatting the mail[box] size in a pretty manner
37 1     1 0 6 my $self = shift;
38 1         2 my $size = shift;
39 1         1 $size /= 1024;
40 1         2 my $unit = "Kbytes";
41 1 50       5 ($size, $unit) = ($size/1024, "Mbytes") if (($size / 1024) > 1) ;
42 1         4 return ($size, $unit);
43             }
44              
45             sub login {
46 0     0 0   my $self = shift;
47 0           my ($login, $passwd) = @_;
48 0 0         my $not = new IO::Socket::SSL "pop.gmail.com:995" or die IO::Socket::SSL->errstr();
49 0           $self->{SOCK} = $not;
50 0           my $line = <$not>; # Reading the welcome message
51 0           print $not "USER $login\r\n";
52 0           $line = <$not>;
53 0 0         if ($line !~ /^\+OK/) { print "Wrong username, please check your settings.\n"; $self->close(); } # We are not allowing USER on transaction state.
  0            
  0            
54 0           print $not "PASS $passwd\r\n";
55 0           $line = <$not>;
56 0 0         if ($line !~ /^\+OK/) { print "Wrong password, please check your settings.\n"; $self->close(); } # Same as above for PASS.
  0            
  0            
57 0           $self->{USERNAME} = $login;
58 0           $self->{PASSWORD} = $passwd;
59 0           return 1;
60             }
61              
62             sub get_msg_nb_size {
63 0     0 1   my $self = shift;
64 0 0         if (defined $self->{SOCK}) {
65 0           my $gsocket = $self->{SOCK};
66 0           print $gsocket "STAT\r\n";
67 0           my $gans = <$gsocket>;
68 0 0         unless ($gans !~ /^\+OK\s(\d+)\s(\d+)(\s.*)?\r\n/) {
69 0           return ($1,$2); # Sending the number of messages and the size of the mailbox
70             }
71 0           } else { croak "Operation failed, connection to server is not established.\n"; }
72             }
73              
74             sub get_pretty_nb_messages {
75 0     0 1   my $self = shift;
76 0           my %params = @_;
77 0 0         $params{"ALERT"} = "TOTAL_MSG" unless exists $params{"ALERT"}; # Making sure we have an alert type.
78 0 0         if (defined $self->{SOCK}) {
79 0           my $gsocket = $self->{SOCK};
80 0           print $gsocket "STAT\r\n";
81 0           my $gans = <$gsocket>;
82 0 0         unless ($gans !~ /^\+OK\s(\d+)\s(\d+)(\s.*)?\r\n/) {
83 0 0         if ($params{"ALERT"} eq "NEW_MSG_ONLY") {
84 0 0         if ($1 > $self->{NBMSG}) {
85 0           return sprintf "You have %d new messages.\n", $1 - $self->{NBMSG};
86             }
87             }
88 0           $self->{NBMSG} = $1;
89 0 0         return sprintf "You have $1 messages in your inbox (size %0.2f %s)\n", $self->getsize($2) unless $params{"ALERT"} eq "NEW_MSG_ONLY";
90             }
91 0           } else { croak "Operation failed, connection to server is not established.\n"; }
92             }
93              
94             sub get_msg_size {
95 0     0 1   my $self = shift;
96 0           my %params = @_;
97 0 0         if (defined $self->{SOCK}) {
98 0           my (@msg_size, $gans);
99 0           my $gsocket = $self->{SOCK};
100 0 0         if (exists $params{"MSG"}) {
101 0           print $gsocket "LIST " . $params{"MSG"} . "\r\n";
102 0           $gans = <$gsocket>;
103 0 0         if ($gans =~ /^\+OK\s(\d+)\s(\d+)/) {
104 0           ($msg_size[0]->{nb}, $msg_size[0]->{size}) = ($1, $2);
105 0           return @msg_size;
106 0           } else { print "No such message number.\r\n"; }
107             } else {
108 0           print $gsocket "LIST\r\n";
109 0           my $i = 0;
110 0           for ($gans = <$gsocket>; $gans ne ".\r\n"; $gans = <$gsocket>) {
111 0 0         if ($gans =~ /^(\d+)\s(\d+)/) {
112 0           ($msg_size[$i]->{nb}, $msg_size[$i]->{size}) = ($1, $2);
113 0           $i++;
114             }
115             }
116 0 0         ($msg_size[0]->{nb}, $msg_size[0]->{size}) = (-1,-1) if $i == 0; # Mailbox is empty
117 0           return @msg_size;
118             }
119             }
120             }
121              
122             sub parse_plain_msg {
123 0     0 0   my $self = shift;
124 0           my ($gsocket, $msgl) = ($self->{SOCK}, "");
125 0           for (my $gans = <$gsocket>; $gans ne ".\r\n"; $gans = <$gsocket>) {
126 0           $msgl .= $gans;
127             }
128 0           return $msgl;
129             }
130              
131             sub msg_to_file {
132 0     0 1   my $self = shift;
133 0           my $ind = shift;
134 0           my $gsocket = $self->{SOCK};
135 0           print $gsocket "RETR $ind\r\n";
136 0           my $gans = "";
137 0           my @uidl = $self->get_uidl(MSG => $ind);
138 0           open(MAILFILE, ">" . $uidl[0]->{hash});
139 0           while ($gans ne ".\r\n") {
140 0           $gans = <$gsocket>;
141 0           print MAILFILE $gans;
142             }
143 0           close(MAILFILE);
144             }
145              
146             sub parse_multipart_msg {
147 0     0 0   my $self = shift;
148 0           my ($gsocket, $msgl, $gans) = ($self->{SOCK}, "", "");
149 0           my %msgs = @_;
150 0           my @attachments = undef;
151 0           my ($content, $opt, $opttype, $encoding, $filename) = (undef, undef, undef, undef, undef);
152 0           my $boundary = $msgs{opt};
153 0           while ($gans !~ /^--$boundary/) { $gans = <$gsocket>; }
  0            
154            
155             # Retrieving the message body [inline text].
156 0           while ($gans ne "\r\n") {
157 0           $gans = <$gsocket>;
158 0 0         if ($gans =~ /^Content-Type: ([a-z0-9\/-]+);\s?(?:([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?)?\r\n/i) {
159 0           $content = $1;
160 0 0         if (!defined $2) {
161 0           $gans = <$gsocket>;
162 0           $gans =~ /\s+([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?\r\n/i;
163 0           $opt = $2;
164 0           $opttype = $1;
165 0           } else { $opt = $3; $opttype = $2; } # Content options (eg. name, charset)
  0            
166             }
167 0 0         if ($gans =~ /^Content-Transfer-Encoding: (7bit|8bit|binary|base64|quote-printable|ietf-token|x-token)\r\n/i) {
168 0           $encoding = $1;
169             }
170             }
171 0   0       do {
172 0           $gans = <$gsocket>;
173 0 0         $msgs{body} .= $gans unless $gans =~ /^--$boundary/;
174             } while (($gans ne ".\r\n") && ($gans !~ /^--$boundary/i));
175 0           $msgs{contentmsg} = $content;
176 0           $msgs{optmsg} = $opt;
177 0           $msgs{opttypemsg} = $opttype;
178 0           $msgs{encoding} = $encoding;
179            
180             # Retrieving attachements.
181              
182 0           for (my $i = -1; $gans ne ".\r\n";) {
183 0 0         if ($gans =~ /^--$boundary/) {
184 0           $i++;
185 0           ($content, $opt, $opttype, $encoding, $filename) = ("","","","","");
186 0           $gans = <$gsocket>;
187 0           while ($gans !~ /^(?:--$boundary|\.\r\n)/) {
188 0 0         if ($gans =~ /^Content-Type: ([a-z0-9\/-]+);\s?(?:([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?)?\r\n/i) {
189 0           $content = $1;
190 0 0         if (!defined $2) {
191 0           $gans = <$gsocket>;
192 0           $gans =~ /\s+([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?\r\n/i;
193 0           $opt = $2;
194 0           $opttype = $1;
195 0           } else { $opt = $3; $opttype = $2; } # Content options (eg. name, charset)
  0            
196             }
197 0 0         if ($gans =~ /^Content-Transfer-Encoding: (7bit|8bit|binary|base64|quote-printable|ietf-token|x-token)\r\n/i) {
198 0           $encoding = $1;
199             }
200 0 0         if ($gans =~ /^Content-Disposition: ([a-z]+);(?:\s+filename=\"?(\S+)\"?)?/) {
201 0 0         if ($1 eq "attachment") {
202 0 0         if (!defined $2) {
  0            
203 0           $gans = <$gsocket>;
204 0           ($attachments[$i]->{filename}) = $gans =~ /\s+filename=\"?(\S+)\"?/;
205             } else {$attachments[$i]->{filename} = $3; }
206 0           while ($gans ne "\r\n") {
207 0           $gans = <$gsocket>;
208 0 0         if ($gans =~ /^\s+$/) { next; }
  0            
209 0           $attachments[$i]->{body} .= $gans;
210             }
211 0           $attachments[$i]->{content} = $content;
212 0           $attachments[$i]->{opt} = $opt;
213 0           $attachments[$i]->{opttype} = $opttype;
214 0           $attachments[$i]->{encoding} = $encoding;
215 0           $gans = <$gsocket>;
216 0           last;
217             }
218             }
219 0           $gans = <$gsocket>;
220             }
221             }
222 0 0         if ($gans !~ /^(--$boundary|\.\r\n)/i) { $gans = <$gsocket>;}
  0            
223             }
224 0 0         if (@attachments != 0) { $msgs{attachment} = @attachments; }
  0            
225 0           return %msgs;
226             }
227              
228             sub parse_msg {
229 0     0 0   my $self = shift;
230 0           my $ind = shift;
231 0           my %msgs;
232 0           my $gsocket = $self->{SOCK};
233 0           print $gsocket "RETR $ind\r\n";
234 0           my ($msgl, $msgtype) = ("", 0);
235 0           my $gans = <$gsocket>;
236 0 0         if ($gans =~ /^\+OK\s/) {
237 0           do { # Getting the message headers
238 0           $gans = <$gsocket>;
239 0 0         $msgl .= $gans if $gans =~ /^([A-Z][a-zA-Z0-9-]+:\s+|\t)/;
240 0 0         if ($gans =~ /^Content-Type: ([a-z0-9\/-]+);\s?(?:([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?)?\r\n/i) {
241 0           $msgs{content} = $1;
242 0 0         if ($msgs{content} =~ /^multipart\/mixed/i) { $msgtype = 1; } # Mail content
  0            
243 0 0         if (!defined $2) {
244 0           $msgl .= $gans = <$gsocket>;
245 0           $gans =~ /\s+([a-z0-9-]+)=\"?([a-z0-9._=-]+)\"?\r\n/i;
246 0           $msgs{opt} = $2;
247 0           $msgs{opttype} = $1;
248 0           } else { $msgs{opt} = $3; $msgs{opttype} = $2; } # Content options (eg. name, charset)
  0            
249             }
250             # We need to know the encoding type
251 0 0         if ($gans =~ /^Content-Type-Encoding: (7bit|8bit|binary|base64|quote-printable|ietf-token|x-token)\r\n/i) { $msgs{encoding} = $1; }
  0            
252             } while ($gans ne "\r\n");
253 0           $msgs{headers} = $msgl;
254 0           $msgl = "";
255 0 0         if (!$msgtype) { $msgs{body} = $self->parse_plain_msg(); } else { %msgs = $self->parse_multipart_msg(%msgs); }
  0            
  0            
256 0           return %msgs;
257 0           } else { print "No such message number (" . $ind .").\r\n"; }
258             }
259              
260             sub get_msg {
261 0     0 1   my $self = shift;
262 0           my %params = @_;
263 0 0         if (defined $self->{SOCK}) {
264 0           my (@msgs, $gans);
265 0           my $gsocket = $self->{SOCK};
266 0 0         if (exists $params{"MSG"}) {
267 0           my %tmp = $self->parse_msg($params{"MSG"});
268 0           push(@msgs, \%tmp);
269 0 0         print $gsocket "DELE " . $params{"MSG"} . "\r\n" if exists $params{"DELETE"};
270             } else {
271 0           my $total = shift(@{ $self->get_msg_nb_size() } );
  0            
272 0           for (my $ind = 1; $ind < $total; $ind++) {
273 0           my %tmp = $self->parse_msg($ind);
274 0           push(@msgs, \%tmp);
275 0 0         print $gsocket "DELE $ind\r\n" if exists $params{"DELETE"};
276             }
277             }
278 0           return @msgs;
279             }
280             }
281              
282             sub get_msg_headers {
283 0     0 1   my $self = shift;
284 0           my %params = @_;
285 0 0         $params{"HEADERS"} = "MINIMAL" unless exists $params{"HEADERS"}; # Making sure we have headers type for retrieval.
286 0 0         my $headregx = ($params{"HEADERS"} eq "FULL") ? '^([A-Z][a-zA-Z0-9-]+:\s+|\t)' : '^(From|Subject|Date):\s+'; # Headers regexp
287 0           my @messages = [];
288 0 0         if (defined $self->{SOCK}) {
289 0           my $gsocket = $self->{SOCK};
290 0           my ($lastmsg, $gans) = (undef, undef);
291 0 0         if (!exists $params{"MSG"}) { # By default we get the last message's headers.
292 0           print $gsocket "STAT\r\n";
293 0           $gans = <$gsocket>;
294 0 0         if ($gans =~ /^\+OK\s(\d+)\s\d+(\s.*)?\r\n/) {
295 0           $lastmsg = $1;
296             }
297             } else { # Did we specify a message for which we want headers ?
298 0           $lastmsg = $params{"MSG"};
299             }
300 0           print $gsocket "TOP $lastmsg 1\r\n";
301 0           $gans = <$gsocket>;
302 0 0         if ($gans =~ /^\+OK/) {
303 0           do {
304 0           $gans = <$gsocket>;
305 0 0         push(@messages, $gans) if $gans =~ /$headregx/;
306             } while ($gans ne "\r\n");
307              
308 0           } else { print "No such message number.\r\n"; } # Duh! We received an -ERR
309 0           return @messages;
310 0           } else { croak "Operation failed, socket is not open.\n"; }
311             }
312              
313             sub get_uidl {
314 0     0 1   my $self = shift;
315 0           my %params = @_;
316 0 0         if (defined $self->{SOCK}) {
317 0           my (@uidls, $gans);
318 0           my $gsocket = $self->{SOCK};
319 0 0         if (exists $params{"MSG"}) {
320 0           print $gsocket "UIDL " . $params{"MSG"} . "\r\n";
321 0           $gans = <$gsocket>;
322 0 0         if ($gans =~ /^\+OK\s\d+\s<([\x21-\x7E]+)>\r\n/) { return $1; } else { print "No such message number (" . $params{"MSG"} .").\r\n"; return -1;}
  0            
  0            
  0            
323             } else {
324 0           print $gsocket "UIDL\r\n";
325 0           my $i = 0;
326 0           for ($gans = <$gsocket>; $gans ne ".\r\n"; $gans = <$gsocket>) {
327 0 0         if ($gans =~ /^(\d+)\s<([\x21-\x7E]+)>\r\n/) {
328 0           ($uidls[$i]->{nb}, $uidls[$i]->{hash}) = ($1, $2);
329 0           $i++;
330             }
331             }
332 0 0         ($uidls[0]->{nb}, $uidls[0]->{hash}) = (-1,-1) if $i == 0;
333 0           return @uidls;
334             }
335 0           } else { croak "Operation failed, socket is not open.\n"; }
336             }
337              
338             sub rset {
339 0     0 1   my $self = shift;
340 0 0         if (defined $self->{SOCK}) {
341 0           my $gsocket = $self->{SOCK};
342 0           print $gsocket "RSET\r\n";
343 0           my $gans = <$gsocket>;
344 0           return $gans;
345 0           } else { croak "Operation failed, socket is not open.\n"; }
346             }
347              
348             sub close {
349 0     0 1   my $self = shift;
350 0 0         if (defined $self->{SOCK}) {
351 0           my $gsocket = $self->{SOCK};
352 0           print $gsocket "QUIT\r\n"; # Sending a proper quit to the server so it can make an UPDATE in case DELE requests were sent.
353 0           $gsocket->close(SSL_ctx_free => 1); # Freeing the connection context
354 0           $self->{SOCK} = undef;
355 0           return 1;
356 0           } else { croak "Operation failed, socket is not open.\n"; }
357             }
358              
359              
360             __END__